aboutsummaryrefslogtreecommitdiffstats
path: root/catalog/pub/utils/toscaparser/basemodel.py
diff options
context:
space:
mode:
Diffstat (limited to 'catalog/pub/utils/toscaparser/basemodel.py')
-rw-r--r--catalog/pub/utils/toscaparser/basemodel.py534
1 files changed, 534 insertions, 0 deletions
diff --git a/catalog/pub/utils/toscaparser/basemodel.py b/catalog/pub/utils/toscaparser/basemodel.py
new file mode 100644
index 0000000..6ed26aa
--- /dev/null
+++ b/catalog/pub/utils/toscaparser/basemodel.py
@@ -0,0 +1,534 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import ftplib
+import json
+import logging
+import os
+import re
+import shutil
+import urllib
+
+# import paramiko
+from toscaparser.tosca_template import ToscaTemplate
+from toscaparser.properties import Property
+from toscaparser.functions import Function, Concat, GetInput, get_function, function_mappings
+from catalog.pub.utils.toscaparser.graph import Graph
+
+from catalog.pub.utils.toscaparser.dataentityext import DataEntityExt
+
+logger = logging.getLogger(__name__)
+
+METADATA = "metadata"
+PROPERTIES = "properties"
+DESCRIPTION = "description"
+REQUIREMENTS = "requirements"
+INTERFACES = "interfaces"
+TOPOLOGY_TEMPLATE = "topology_template"
+INPUTS = "inputs"
+CAPABILITIES = "capabilities"
+ATTRIBUTES = "attributes"
+ARTIFACTS = "artifacts"
+DERIVED_FROM = "derived_from"
+
+NODE_NAME = "name"
+NODE_TYPE = "nodeType"
+NODE_ROOT = "tosca.nodes.Root"
+GROUP_TYPE = "groupType"
+GROUPS_ROOT = "tosca.groups.Root"
+
+
+class BaseInfoModel(object):
+
+ def __init__(self, path=None, params=None, tosca=None):
+ if tosca:
+ _tosca = tosca
+ else:
+ _tosca = self.buildToscaTemplate(path, params)
+ self.description = getattr(_tosca, "description", "")
+ self.parseModel(_tosca)
+
+ def parseModel(self, tosca):
+ pass
+
+ def buildInputs(self, tosca):
+ topo = tosca.tpl.get(TOPOLOGY_TEMPLATE, None)
+ return topo.get(INPUTS, {}) if topo else {}
+
+ def buildToscaTemplate(self, path, params):
+ file_name = None
+ try:
+ file_name = self._check_download_file(path)
+ valid_params = self._validate_input_params(file_name, params)
+ return self._create_tosca_template(file_name, valid_params)
+ finally:
+ if file_name is not None and file_name != path and os.path.exists(file_name):
+ try:
+ os.remove(file_name)
+ except Exception as e:
+ logger.error("Failed to parse package, error: %s", e.args[0])
+
+ def _validate_input_params(self, path, params):
+ valid_params = {}
+ inputs = {}
+ if isinstance(params, list):
+ for param in params:
+ key = param.get('key', 'undefined')
+ value = param.get('value', 'undefined')
+ inputs[key] = value
+ params = inputs
+
+ if params:
+ tmp = self._create_tosca_template(path, None)
+ if isinstance(params, dict):
+ for key, value in list(params.items()):
+ if hasattr(tmp, 'inputs') and len(tmp.inputs) > 0:
+ for input_def in tmp.inputs:
+ if (input_def.name == key):
+ valid_params[key] = DataEntityExt.validate_datatype(input_def.type, value)
+ return valid_params
+
+ def _create_tosca_template(self, file_name, valid_params):
+ tosca_tpl = None
+ try:
+ tosca_tpl = ToscaTemplate(path=file_name,
+ parsed_params=valid_params,
+ no_required_paras_check=True,
+ debug_mode=True)
+ except Exception as e:
+ print(e.args[0])
+ finally:
+ if tosca_tpl is not None and hasattr(tosca_tpl, "temp_dir") and os.path.exists(tosca_tpl.temp_dir):
+ try:
+ shutil.rmtree(tosca_tpl.temp_dir)
+ except Exception as e:
+ logger.error("Failed to create tosca template, error: %s", e.args[0])
+ print("-----------------------------")
+ print('\n'.join(['%s:%s' % item for item in list(tosca_tpl.__dict__.items())]))
+ print("-----------------------------")
+ return tosca_tpl
+
+ def _check_download_file(self, path):
+ if (path.startswith("ftp") or path.startswith("sftp")):
+ return self.downloadFileFromFtpServer(path)
+ elif (path.startswith("http")):
+ return self.download_file_from_httpserver(path)
+ return path
+
+ def download_file_from_httpserver(self, path):
+ path = path.encode("utf-8")
+ tmps = str.split(path, '/')
+ localFileName = tmps[len(tmps) - 1]
+ urllib.request.urlretrieve(path, localFileName)
+ return localFileName
+
+ def downloadFileFromFtpServer(self, path):
+ path = path.encode("utf-8")
+ tmp = str.split(path, '://')
+ protocol = tmp[0]
+ tmp = str.split(tmp[1], ':')
+ if len(tmp) == 2:
+ userName = tmp[0]
+ tmp = str.split(tmp[1], '@')
+ userPwd = tmp[0]
+ index = tmp[1].index('/')
+ hostIp = tmp[1][0:index]
+ remoteFileName = tmp[1][index:len(tmp[1])]
+ if protocol.lower() == 'ftp':
+ hostPort = 21
+ else:
+ hostPort = 22
+
+ if len(tmp) == 3:
+ userName = tmp[0]
+ userPwd = str.split(tmp[1], '@')[0]
+ hostIp = str.split(tmp[1], '@')[1]
+ index = tmp[2].index('/')
+ hostPort = tmp[2][0:index]
+ remoteFileName = tmp[2][index:len(tmp[2])]
+
+ localFileName = str.split(remoteFileName, '/')
+ localFileName = localFileName[len(localFileName) - 1]
+
+ if protocol.lower() == 'sftp':
+ self.sftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName)
+ else:
+ self.ftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName)
+ return localFileName
+
+ # def sftp_get(self, userName, userPwd, hostIp, hostPort, remoteFileName, localFileName):
+ # # return
+ # t = None
+ # try:
+ # t = paramiko.Transport(hostIp, int(hostPort))
+ # t.connect(username=userName, password=userPwd)
+ # sftp = paramiko.SFTPClient.from_transport(t)
+ # sftp.get(remoteFileName, localFileName)
+ # finally:
+ # if t is not None:
+ # t.close()
+
+ def ftp_get(self, userName, userPwd, hostIp, hostPort, remoteFileName, localFileName):
+ f = None
+ try:
+ ftp = ftplib.FTP()
+ ftp.connect(hostIp, hostPort)
+ ftp.login(userName, userPwd)
+ f = open(localFileName, 'wb')
+ ftp.retrbinary('RETR ' + remoteFileName, f.write, 1024)
+ f.close()
+ finally:
+ if f is not None:
+ f.close()
+
+ def buildMetadata(self, tosca):
+ return tosca.tpl.get(METADATA, {}) if tosca else {}
+
+ def buildNode(self, nodeTemplate, tosca):
+ inputs = tosca.inputs
+ parsed_params = tosca.parsed_params
+ ret = {}
+ ret[NODE_NAME] = nodeTemplate.name
+ ret[NODE_TYPE] = nodeTemplate.type
+ if DESCRIPTION in nodeTemplate.entity_tpl:
+ ret[DESCRIPTION] = nodeTemplate.entity_tpl[DESCRIPTION]
+ else:
+ ret[DESCRIPTION] = ''
+ if METADATA in nodeTemplate.entity_tpl:
+ ret[METADATA] = nodeTemplate.entity_tpl[METADATA]
+ else:
+ ret[METADATA] = ''
+ props = self.buildProperties_ex(nodeTemplate, tosca.topology_template)
+ ret[PROPERTIES] = self.verify_properties(props, inputs, parsed_params)
+ ret[REQUIREMENTS] = self.build_requirements(nodeTemplate)
+ self.buildCapabilities(nodeTemplate, inputs, ret)
+ self.buildArtifacts(nodeTemplate, inputs, ret)
+ interfaces = self.build_interfaces(nodeTemplate)
+ if interfaces:
+ ret[INTERFACES] = interfaces
+ return ret
+
+ def buildProperties(self, nodeTemplate, parsed_params):
+ properties = {}
+ isMappingParams = parsed_params and len(parsed_params) > 0
+ for k, item in list(nodeTemplate.get_properties().items()):
+ properties[k] = item.value
+ if isinstance(item.value, GetInput):
+ if item.value.result() and isMappingParams:
+ properties[k] = DataEntityExt.validate_datatype(item.type, item.value.result())
+ else:
+ tmp = {}
+ tmp[item.value.name] = item.value.input_name
+ properties[k] = tmp
+ if ATTRIBUTES in nodeTemplate.entity_tpl:
+ for k, item in list(nodeTemplate.entity_tpl[ATTRIBUTES].items()):
+ properties[k] = str(item)
+ return properties
+
+ def buildProperties_ex(self, nodeTemplate, topology_template, properties=None):
+ if properties is None:
+ properties = nodeTemplate.get_properties()
+ _properties = {}
+ if isinstance(properties, dict):
+ for name, prop in list(properties.items()):
+ if isinstance(prop, Property):
+ if isinstance(prop.value, Function):
+ if isinstance(prop.value, Concat): # support one layer inner function.
+ value_str = ''
+ for arg in prop.value.args:
+ if isinstance(arg, str):
+ value_str += arg
+ elif isinstance(arg, dict):
+ raw_func = {}
+ for k, v in list(arg.items()):
+ func_args = []
+ func_args.append(v)
+ raw_func[k] = func_args
+ func = get_function(topology_template, nodeTemplate, raw_func)
+ value_str += str(func.result())
+ _properties[name] = value_str
+ else:
+ _properties[name] = prop.value.result()
+ elif isinstance(prop.value, dict) or isinstance(prop.value, list):
+ _properties[name] = self.buildProperties_ex(nodeTemplate, topology_template, prop.value)
+ elif prop.type == 'string':
+ _properties[name] = prop.value
+ else:
+ _properties[name] = json.dumps(prop.value)
+ elif isinstance(prop, dict):
+ _properties[name] = self.buildProperties_ex(nodeTemplate, topology_template, prop)
+ elif isinstance(prop, list):
+ _properties[name] = self.buildProperties_ex(nodeTemplate, topology_template, prop)
+ elif name in function_mappings:
+ raw_func = {}
+ func_args = []
+ func_args.append(prop)
+ raw_func[name] = func_args
+ if name == 'CONCAT':
+ value_str = ''
+ for arg in prop:
+ if isinstance(arg, str):
+ value_str += arg
+ elif isinstance(arg, dict):
+ raw_func = {}
+ for k, v in list(arg.items()):
+ func_args = []
+ func_args.append(v)
+ raw_func[k] = func_args
+ value_str += str(
+ get_function(topology_template, nodeTemplate, raw_func).result())
+ value = value_str
+ else:
+ return get_function(topology_template, nodeTemplate, raw_func).result()
+ else:
+ _properties[name] = prop
+ elif isinstance(properties, list):
+ value = []
+ for para in properties:
+ if isinstance(para, dict) or isinstance(para, list):
+ value.append(self.buildProperties_ex(nodeTemplate, topology_template, para))
+ else:
+ value.append(para)
+ return value
+ return _properties
+
+ def verify_properties(self, props, inputs, parsed_params):
+ ret_props = {}
+ if (props and len(props) > 0):
+ for key, value in list(props.items()):
+ ret_props[key] = self._verify_value(value, inputs, parsed_params)
+ # if isinstance(value, str):
+ # ret_props[key] = self._verify_string(inputs, parsed_params, value);
+ # continue
+ # if isinstance(value, list):
+ # ret_props[key] = map(lambda x: self._verify_dict(inputs, parsed_params, x), value)
+ # continue
+ # if isinstance(value, dict):
+ # ret_props[key] = self._verify_map(inputs, parsed_params, value)
+ # continue
+ # ret_props[key] = value
+ return ret_props
+
+ def build_requirements(self, node_template):
+ rets = []
+ for req in node_template.requirements:
+ for req_name, req_value in list(req.items()):
+ if (isinstance(req_value, dict)):
+ if ('node' in req_value and req_value['node'] not in node_template.templates):
+ continue # No target requirement for aria parser, not add to result.
+ rets.append({req_name: req_value})
+ return rets
+
+ def buildCapabilities(self, nodeTemplate, inputs, ret):
+ capabilities = json.dumps(nodeTemplate.entity_tpl.get(CAPABILITIES, None))
+ match = re.findall(r'\{"get_input":\s*"([\w|\-]+)"\}', capabilities)
+ for m in match:
+ aa = [input_def for input_def in inputs if m == input_def.name][0]
+ capabilities = re.sub(r'\{"get_input":\s*"([\w|\-]+)"\}', json.dumps(aa.default), capabilities, 1)
+ if capabilities != 'null':
+ ret[CAPABILITIES] = json.loads(capabilities)
+
+ def buildArtifacts(self, nodeTemplate, inputs, ret):
+ artifacts = json.dumps(nodeTemplate.entity_tpl.get('artifacts', None))
+ match = re.findall(r'\{"get_input":\s*"([\w|\-]+)"\}', artifacts)
+ for m in match:
+ aa = [input_def for input_def in inputs if m == input_def.name][0]
+ artifacts = re.sub(r'\{"get_input":\s*"([\w|\-]+)"\}', json.dumps(aa.default), artifacts, 1)
+ if artifacts != 'null':
+ ret[ARTIFACTS] = json.loads(artifacts)
+
+ def build_interfaces(self, node_template):
+ if INTERFACES in node_template.entity_tpl:
+ return node_template.entity_tpl[INTERFACES]
+ return None
+
+ def isNodeTypeX(self, node, nodeTypes, x):
+ node_type = node[NODE_TYPE]
+ while node_type != x:
+ node_type_derived = node_type
+ node_type = nodeTypes[node_type][DERIVED_FROM]
+ if node_type == NODE_ROOT or node_type == node_type_derived:
+ return False
+ return True
+
+ def get_requirement_node_name(self, req_value):
+ return self.get_prop_from_obj(req_value, 'node')
+
+ def getRequirementByNodeName(self, nodeTemplates, storage_name, prop):
+ for node in nodeTemplates:
+ if node[NODE_NAME] == storage_name:
+ if prop in node:
+ return node[prop]
+
+ def get_prop_from_obj(self, obj, prop):
+ if isinstance(obj, str):
+ return obj
+ if (isinstance(obj, dict) and prop in obj):
+ return obj[prop]
+ return None
+
+ def getNodeDependencys(self, node):
+ return self.getRequirementByName(node, 'dependency')
+
+ def getRequirementByName(self, node, requirementName):
+ requirements = []
+ if REQUIREMENTS in node:
+ for item in node[REQUIREMENTS]:
+ for key, value in list(item.items()):
+ if key == requirementName:
+ requirements.append(value)
+ return requirements
+
+ def _verify_value(self, value, inputs, parsed_params):
+ if value == '{}':
+ return ''
+ if isinstance(value, str):
+ return self._verify_string(inputs, parsed_params, value)
+ if isinstance(value, list) or isinstance(value, dict):
+ return self._verify_object(value, inputs, parsed_params)
+ return value
+
+ def _verify_object(self, value, inputs, parsed_params):
+ s = self._verify_string(inputs, parsed_params, json.dumps(value))
+ return json.loads(s)
+
+ def _get_input_name(self, getInput):
+ input_name = getInput.split(':')[1]
+ input_name = input_name.strip()
+ return input_name.replace('"', '').replace('}', '')
+
+ def _verify_string(self, inputs, parsed_params, value):
+ getInputs = re.findall(r'{"get_input": "[a-zA-Z_0-9]+"}', value)
+ for getInput in getInputs:
+ input_name = self._get_input_name(getInput)
+ if parsed_params and input_name in parsed_params:
+ value = value.replace(getInput, json.dumps(parsed_params[input_name]))
+ else:
+ for input_def in inputs:
+ if input_def.default and input_name == input_def.name:
+ value = value.replace(getInput, json.dumps(input_def.default))
+ return value
+
+ def get_node_by_name(self, node_templates, name):
+ for node in node_templates:
+ if node[NODE_NAME] == name:
+ return node
+ return None
+
+ def getCapabilityByName(self, node, capabilityName):
+ if CAPABILITIES in node and capabilityName in node[CAPABILITIES]:
+ return node[CAPABILITIES][capabilityName]
+ return None
+
+ def get_base_path(self, tosca):
+ fpath, fname = os.path.split(tosca.path)
+ return fpath
+
+ def build_artifacts(self, node):
+ rets = []
+ if ARTIFACTS in node and len(node[ARTIFACTS]) > 0:
+ artifacts = node[ARTIFACTS]
+ for name, value in list(artifacts.items()):
+ ret = {}
+ ret['artifact_name'] = name
+ ret['file'] = value
+ if isinstance(value, dict):
+ ret.update(value)
+ rets.append(ret)
+ else:
+ # TODO It is workaround for SDC-1900.
+ logger.error("VCPE specific code")
+ ret = {}
+ ret['artifact_name'] = "sw_image"
+ ret['file'] = "ubuntu_16.04"
+ ret['type'] = "tosca.artifacts.nfv.SwImage"
+ rets.append(ret)
+
+ return rets
+
+ def get_node_by_req(self, node_templates, req):
+ req_node_name = self.get_requirement_node_name(req)
+ return self.get_node_by_name(node_templates, req_node_name)
+
+ def isGroupTypeX(self, group, groupTypes, x):
+ group_type = group[GROUP_TYPE]
+ while group_type != x:
+ group_type_derived = group_type
+ group_type = groupTypes[group_type][DERIVED_FROM]
+ if group_type == GROUPS_ROOT or group_type == group_type_derived:
+ return False
+ return True
+
+ def setTargetValues(self, dict_target, target_keys, dict_source, source_keys):
+ i = 0
+ for item in source_keys:
+ dict_target[target_keys[i]] = dict_source.get(item, "")
+ i += 1
+ return dict_target
+
+ def get_deploy_graph(self, tosca, relations):
+ nodes = tosca.graph.nodetemplates
+ graph = Graph()
+ for node in nodes:
+ self._build_deploy_path(node, [], graph, relations)
+ return graph.to_dict()
+
+ def _build_deploy_path(self, node, node_parent, graph, relations):
+ graph.add_node(node.name, node_parent)
+ type_require_set = {}
+ type_requires = node.type_definition.requirements
+ for type_require in type_requires:
+ type_require_set.update(type_require)
+ for requirement in node.requirements:
+ for k in list(requirement.keys()):
+ if type_require_set[k].get('relationship', None) in relations[0] or type_require_set[k].get('capability', None) in relations[0]:
+ if isinstance(requirement[k], dict):
+ next_node = requirement[k].get('node', None)
+ else:
+ next_node = requirement[k]
+ graph.add_node(next_node, [node.name])
+ if type_require_set[k].get('relationship', None) in relations[1]:
+ if isinstance(requirement[k], dict):
+ next_node = requirement[k].get('node', None)
+ else:
+ next_node = requirement[k]
+ graph.add_node(next_node, [node.name])
+
+ def get_substitution_mappings(self, tosca):
+ node = {
+ 'properties': {},
+ 'requirements': {},
+ 'capabilities': {},
+ 'metadata': {}
+ }
+ metadata = None
+ substitution_mappings = tosca.tpl['topology_template'].get('substitution_mappings', None)
+ if substitution_mappings:
+ nodeType = substitution_mappings['node_type']
+ logger.debug("nodeType %s", nodeType)
+ if "type" not in node or node['type'] == "":
+ node['type'] = nodeType
+ node['properties'] = substitution_mappings.get('properties', {})
+ node['requirements'] = substitution_mappings.get('requirements', {})
+ node['capabilities'] = substitution_mappings.get('capabilities', {})
+ metadata = substitution_mappings.get('metadata', {})
+
+ if "node_types" in tosca.tpl:
+ node_types = tosca.tpl['node_types'].get(nodeType, None)
+ derivedFrom = node_types.get('derived_from', "")
+ node['type'] = derivedFrom
+ node['properties'] = node_types.get('properties', {})
+
+ node['metadata'] = metadata if metadata and metadata != {} else self.buildMetadata(tosca)
+ return node