1 # Copyright 2017 ZTE Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
24 from toscaparser.tosca_template import ToscaTemplate
25 from toscaparser.properties import Property
26 from toscaparser.functions import Function, Concat, GetInput, get_function, function_mappings
27 from catalog.pub.utils.toscaparser.graph import Graph
29 from catalog.pub.utils.toscaparser.dataentityext import DataEntityExt
31 logger = logging.getLogger(__name__)
34 PROPERTIES = "properties"
35 DESCRIPTION = "description"
36 REQUIREMENTS = "requirements"
37 INTERFACES = "interfaces"
38 TOPOLOGY_TEMPLATE = "topology_template"
40 CAPABILITIES = "capabilities"
41 ATTRIBUTES = "attributes"
42 ARTIFACTS = "artifacts"
43 DERIVED_FROM = "derived_from"
46 NODE_TYPE = "nodeType"
47 NODE_ROOT = "tosca.nodes.Root"
48 GROUP_TYPE = "groupType"
49 GROUPS_ROOT = "tosca.groups.Root"
52 class BaseInfoModel(object):
54 def __init__(self, path, params):
55 tosca = self.buildToscaTemplate(path, params)
56 self.parseModel(tosca)
58 def parseModel(self, tosca):
61 def buildInputs(self, tosca):
62 topo = tosca.tpl.get(TOPOLOGY_TEMPLATE, None)
63 return topo.get(INPUTS, {}) if topo else {}
65 def buildToscaTemplate(self, path, params):
68 file_name = self._check_download_file(path)
69 valid_params = self._validate_input_params(file_name, params)
70 return self._create_tosca_template(file_name, valid_params)
72 if file_name is not None and file_name != path and os.path.exists(file_name):
75 except Exception as e:
76 logger.error("Failed to parse package, error: %s", e.message)
78 def _validate_input_params(self, path, params):
81 if isinstance(params, list):
83 key = param.get('key', 'undefined')
84 value = param.get('value', 'undefined')
88 if params and len(params) > 0:
89 tmp = self._create_tosca_template(path, None)
90 if isinstance(params, dict):
91 for key, value in params.items():
92 if hasattr(tmp, 'inputs') and len(tmp.inputs) > 0:
93 for input_def in tmp.inputs:
94 if (input_def.name == key):
95 valid_params[key] = DataEntityExt.validate_datatype(input_def.type, value)
98 def _create_tosca_template(self, file_name, valid_params):
101 tosca_tpl = ToscaTemplate(path=file_name,
102 parsed_params=valid_params,
103 no_required_paras_check=True,
105 except Exception as e:
108 if tosca_tpl is not None and hasattr(tosca_tpl, "temp_dir") and os.path.exists(tosca_tpl.temp_dir):
110 shutil.rmtree(tosca_tpl.temp_dir)
111 except Exception as e:
112 logger.error("Failed to create tosca template, error: %s", e.message)
113 print "-----------------------------"
114 print '\n'.join(['%s:%s' % item for item in tosca_tpl.__dict__.items()])
115 print "-----------------------------"
118 def _check_download_file(self, path):
119 if (path.startswith("ftp") or path.startswith("sftp")):
120 return self.downloadFileFromFtpServer(path)
121 elif (path.startswith("http")):
122 return self.download_file_from_httpserver(path)
125 def download_file_from_httpserver(self, path):
126 path = path.encode("utf-8")
127 tmps = str.split(path, '/')
128 localFileName = tmps[len(tmps) - 1]
129 urllib.urlretrieve(path, localFileName)
132 def downloadFileFromFtpServer(self, path):
133 path = path.encode("utf-8")
134 tmp = str.split(path, '://')
136 tmp = str.split(tmp[1], ':')
139 tmp = str.split(tmp[1], '@')
141 index = tmp[1].index('/')
142 hostIp = tmp[1][0:index]
143 remoteFileName = tmp[1][index:len(tmp[1])]
144 if protocol.lower() == 'ftp':
151 userPwd = str.split(tmp[1], '@')[0]
152 hostIp = str.split(tmp[1], '@')[1]
153 index = tmp[2].index('/')
154 hostPort = tmp[2][0:index]
155 remoteFileName = tmp[2][index:len(tmp[2])]
157 localFileName = str.split(remoteFileName, '/')
158 localFileName = localFileName[len(localFileName) - 1]
160 if protocol.lower() == 'sftp':
161 self.sftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName)
163 self.ftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName)
166 def sftp_get(self, userName, userPwd, hostIp, hostPort, remoteFileName, localFileName):
170 t = paramiko.Transport(hostIp, int(hostPort))
171 t.connect(username=userName, password=userPwd)
172 sftp = paramiko.SFTPClient.from_transport(t)
173 sftp.get(remoteFileName, localFileName)
178 def ftp_get(self, userName, userPwd, hostIp, hostPort, remoteFileName, localFileName):
182 ftp.connect(hostIp, hostPort)
183 ftp.login(userName, userPwd)
184 f = open(localFileName, 'wb')
185 ftp.retrbinary('RETR ' + remoteFileName, f.write, 1024)
191 def buildMetadata(self, tosca):
192 return tosca.tpl.get(METADATA, {}) if tosca else {}
194 def buildNode(self, nodeTemplate, tosca):
195 inputs = tosca.inputs
196 parsed_params = tosca.parsed_params
198 ret[NODE_NAME] = nodeTemplate.name
199 ret[NODE_TYPE] = nodeTemplate.type
200 if DESCRIPTION in nodeTemplate.entity_tpl:
201 ret[DESCRIPTION] = nodeTemplate.entity_tpl[DESCRIPTION]
203 ret[DESCRIPTION] = ''
204 if METADATA in nodeTemplate.entity_tpl:
205 ret[METADATA] = nodeTemplate.entity_tpl[METADATA]
208 props = self.buildProperties_ex(nodeTemplate, tosca.topology_template)
209 ret[PROPERTIES] = self.verify_properties(props, inputs, parsed_params)
210 ret[REQUIREMENTS] = self.build_requirements(nodeTemplate)
211 self.buildCapabilities(nodeTemplate, inputs, ret)
212 self.buildArtifacts(nodeTemplate, inputs, ret)
213 interfaces = self.build_interfaces(nodeTemplate)
215 ret[INTERFACES] = interfaces
218 def buildProperties(self, nodeTemplate, parsed_params):
220 isMappingParams = parsed_params and len(parsed_params) > 0
221 for k, item in nodeTemplate.get_properties().items():
222 properties[k] = item.value
223 if isinstance(item.value, GetInput):
224 if item.value.result() and isMappingParams:
225 properties[k] = DataEntityExt.validate_datatype(item.type, item.value.result())
228 tmp[item.value.name] = item.value.input_name
230 if ATTRIBUTES in nodeTemplate.entity_tpl:
231 for k, item in nodeTemplate.entity_tpl[ATTRIBUTES].items():
232 properties[k] = str(item)
235 def buildProperties_ex(self, nodeTemplate, topology_template, properties=None):
236 if properties is None:
237 properties = nodeTemplate.get_properties()
239 if isinstance(properties, dict):
240 for name, prop in properties.items():
241 if isinstance(prop, Property):
242 if isinstance(prop.value, Function):
243 if isinstance(prop.value, Concat): # support one layer inner function.
245 for arg in prop.value.args:
246 if isinstance(arg, str):
248 elif isinstance(arg, dict):
250 for k, v in arg.items():
253 raw_func[k] = func_args
254 func = get_function(topology_template, nodeTemplate, raw_func)
255 value_str += str(func.result())
256 _properties[name] = value_str
258 _properties[name] = prop.value.result()
259 elif isinstance(prop.value, dict) or isinstance(prop.value, list):
260 _properties[name] = self.buildProperties_ex(nodeTemplate, topology_template, prop.value)
261 elif prop.type == 'string':
262 _properties[name] = prop.value
264 _properties[name] = json.dumps(prop.value)
265 elif isinstance(prop, dict):
266 _properties[name] = self.buildProperties_ex(nodeTemplate, topology_template, prop)
267 elif isinstance(prop, list):
268 _properties[name] = self.buildProperties_ex(nodeTemplate, topology_template, prop)
269 elif name in function_mappings:
272 func_args.append(prop)
273 raw_func[name] = func_args
277 if isinstance(arg, str):
279 elif isinstance(arg, dict):
281 for k, v in arg.items():
284 raw_func[k] = func_args
286 get_function(topology_template, nodeTemplate, raw_func).result())
289 return get_function(topology_template, nodeTemplate, raw_func).result()
291 _properties[name] = prop
292 elif isinstance(properties, list):
294 for para in properties:
295 if isinstance(para, dict) or isinstance(para, list):
296 value.append(self.buildProperties_ex(nodeTemplate, topology_template, para))
302 def verify_properties(self, props, inputs, parsed_params):
304 if (props and len(props) > 0):
305 for key, value in props.items():
306 ret_props[key] = self._verify_value(value, inputs, parsed_params)
307 # if isinstance(value, str):
308 # ret_props[key] = self._verify_string(inputs, parsed_params, value);
310 # if isinstance(value, list):
311 # ret_props[key] = map(lambda x: self._verify_dict(inputs, parsed_params, x), value)
313 # if isinstance(value, dict):
314 # ret_props[key] = self._verify_map(inputs, parsed_params, value)
316 # ret_props[key] = value
319 def build_requirements(self, node_template):
321 for req in node_template.requirements:
322 for req_name, req_value in req.items():
323 if (isinstance(req_value, dict)):
324 if ('node' in req_value and req_value['node'] not in node_template.templates):
325 continue # No target requirement for aria parser, not add to result.
326 rets.append({req_name: req_value})
329 def buildCapabilities(self, nodeTemplate, inputs, ret):
330 capabilities = json.dumps(nodeTemplate.entity_tpl.get(CAPABILITIES, None))
331 match = re.findall(r'\{"get_input":\s*"([\w|\-]+)"\}', capabilities)
333 aa = [input_def for input_def in inputs if m == input_def.name][0]
334 capabilities = re.sub(r'\{"get_input":\s*"([\w|\-]+)"\}', json.dumps(aa.default), capabilities, 1)
335 if capabilities != 'null':
336 ret[CAPABILITIES] = json.loads(capabilities)
338 def buildArtifacts(self, nodeTemplate, inputs, ret):
339 artifacts = json.dumps(nodeTemplate.entity_tpl.get('artifacts', None))
340 match = re.findall(r'\{"get_input":\s*"([\w|\-]+)"\}', artifacts)
342 aa = [input_def for input_def in inputs if m == input_def.name][0]
343 artifacts = re.sub(r'\{"get_input":\s*"([\w|\-]+)"\}', json.dumps(aa.default), artifacts, 1)
344 if artifacts != 'null':
345 ret[ARTIFACTS] = json.loads(artifacts)
347 def build_interfaces(self, node_template):
348 if INTERFACES in node_template.entity_tpl:
349 return node_template.entity_tpl[INTERFACES]
352 def isNodeTypeX(self, node, nodeTypes, x):
353 node_type = node[NODE_TYPE]
354 while node_type != x:
355 node_type_derived = node_type
356 node_type = nodeTypes[node_type][DERIVED_FROM]
357 if node_type == NODE_ROOT or node_type == node_type_derived:
361 def get_requirement_node_name(self, req_value):
362 return self.get_prop_from_obj(req_value, 'node')
364 def getRequirementByNodeName(self, nodeTemplates, storage_name, prop):
365 for node in nodeTemplates:
366 if node[NODE_NAME] == storage_name:
370 def get_prop_from_obj(self, obj, prop):
371 if isinstance(obj, str):
373 if (isinstance(obj, dict) and prop in obj):
377 def getNodeDependencys(self, node):
378 return self.getRequirementByName(node, 'dependency')
380 def getRequirementByName(self, node, requirementName):
382 if REQUIREMENTS in node:
383 for item in node[REQUIREMENTS]:
384 for key, value in item.items():
385 if key == requirementName:
386 requirements.append(value)
389 def _verify_value(self, value, inputs, parsed_params):
392 if isinstance(value, str):
393 return self._verify_string(inputs, parsed_params, value)
394 if isinstance(value, list) or isinstance(value, dict):
395 return self._verify_object(value, inputs, parsed_params)
398 def _verify_object(self, value, inputs, parsed_params):
399 s = self._verify_string(inputs, parsed_params, json.dumps(value))
402 def _get_input_name(self, getInput):
403 input_name = getInput.split(':')[1]
404 input_name = input_name.strip()
405 return input_name.replace('"', '').replace('}', '')
407 def _verify_string(self, inputs, parsed_params, value):
408 getInputs = re.findall(r'{"get_input": "[a-zA-Z_0-9]+"}', value)
409 for getInput in getInputs:
410 input_name = self._get_input_name(getInput)
411 if parsed_params and input_name in parsed_params:
412 value = value.replace(getInput, json.dumps(parsed_params[input_name]))
414 for input_def in inputs:
415 if input_def.default and input_name == input_def.name:
416 value = value.replace(getInput, json.dumps(input_def.default))
419 def get_node_by_name(self, node_templates, name):
420 for node in node_templates:
421 if node[NODE_NAME] == name:
425 def getCapabilityByName(self, node, capabilityName):
426 if CAPABILITIES in node and capabilityName in node[CAPABILITIES]:
427 return node[CAPABILITIES][capabilityName]
430 def get_base_path(self, tosca):
431 fpath, fname = os.path.split(tosca.path)
434 def build_artifacts(self, node):
436 if ARTIFACTS in node and len(node[ARTIFACTS]) > 0:
437 artifacts = node[ARTIFACTS]
438 for name, value in artifacts.items():
440 ret['artifact_name'] = name
442 if isinstance(value, dict):
447 def get_node_by_req(self, node_templates, req):
448 req_node_name = self.get_requirement_node_name(req)
449 return self.get_node_by_name(node_templates, req_node_name)
451 def isGroupTypeX(self, group, groupTypes, x):
452 group_type = group[GROUP_TYPE]
453 while group_type != x:
454 group_type_derived = group_type
455 group_type = groupTypes[group_type][DERIVED_FROM]
456 if group_type == GROUPS_ROOT or group_type == group_type_derived:
460 def setTargetValues(self, dict_target, target_keys, dict_source, source_keys):
462 for item in source_keys:
463 dict_target[target_keys[i]] = dict_source.get(item, "")
467 def get_deploy_graph(self, tosca, relations):
468 nodes = tosca.graph.nodetemplates
471 self._build_deploy_path(node, [], graph, relations)
472 return graph.to_dict()
474 def _build_deploy_path(self, node, node_parent, graph, relations):
475 graph.add_node(node.name, node_parent)
476 type_require_set = {}
477 type_requires = node.type_definition.requirements
478 for type_require in type_requires:
479 type_require_set.update(type_require)
480 for requirement in node.requirements:
481 for k in requirement.keys():
482 if type_require_set[k].get('relationship', None) in relations[0] or type_require_set[k].get('capability', None) in relations[0]:
483 if isinstance(requirement[k], dict):
484 next_node = requirement[k].get('node', None)
486 next_node = requirement[k]
487 graph.add_node(next_node, [node.name])
488 if type_require_set[k].get('relationship', None) in relations[1]:
489 if isinstance(requirement[k], dict):
490 next_node = requirement[k].get('node', None)
492 next_node = requirement[k]
493 graph.add_node(next_node, [node.name])