Fix vfc-lcm parse ns package bugs
[vfc/nfvo/lcm.git] / lcm / pub / utils / toscaparser / basemodel.py
1 # Copyright 2017 ZTE Corporation.
2 #
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
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 import copy
16 import ftplib
17 import json
18 import logging
19 import os
20 import re
21 import shutil
22 import urllib
23
24 import paramiko
25 from toscaparser.functions import GetInput
26 from toscaparser.tosca_template import ToscaTemplate
27
28 from lcm.pub.utils.toscaparser.dataentityext import DataEntityExt
29
30 logger = logging.getLogger(__name__)
31
32
33 class BaseInfoModel(object):
34
35     def buildToscaTemplate(self, path, params):
36         file_name = None
37         try:
38             file_name = self._check_download_file(path)
39             valid_params = self._validate_input_params(file_name, params)
40             return self._create_tosca_template(file_name, valid_params)
41         finally:
42             if file_name is not None and file_name != path and os.path.exists(file_name):
43                 try:
44                     os.remove(file_name)
45                 except Exception as e:
46                     logger.error("Failed to parse package, error: %s", e.message)
47
48     def _validate_input_params(self, path, params):
49         valid_params = {}
50         if params and len(params) > 0:
51             tmp = self._create_tosca_template(path, None)
52             for key, value in params.items():
53                 if hasattr(tmp, 'inputs') and len(tmp.inputs) > 0:
54                     for input_def in tmp.inputs:
55                         if (input_def.name == key):
56                             valid_params[key] = DataEntityExt.validate_datatype(input_def.type, value)
57
58         return valid_params
59
60     def _create_tosca_template(self, file_name, valid_params):
61         tosca_tpl = None
62         try:
63             tosca_tpl = ToscaTemplate(path=file_name,
64                                       parsed_params=valid_params,
65                                       no_required_paras_check=True,
66                                       debug_mode=True)
67         except Exception as e:
68             print e.message
69         finally:
70             if tosca_tpl is not None and hasattr(tosca_tpl, "temp_dir") and os.path.exists(tosca_tpl.temp_dir):
71                 try:
72                     shutil.rmtree(tosca_tpl.temp_dir)
73                 except Exception, e:
74                     logger.error("Failed to create tosca template, error: %s", e.message)
75             print "-----------------------------"
76             print '\n'.join(['%s:%s' % item for item in tosca_tpl.__dict__.items()])
77             print "-----------------------------"
78             return tosca_tpl
79
80     def _check_download_file(self, path):
81         if (path.startswith("ftp") or path.startswith("sftp")):
82             return self.downloadFileFromFtpServer(path)
83         elif (path.startswith("http")):
84             return self.download_file_from_httpserver(path)
85         return path
86
87     def download_file_from_httpserver(self, path):
88         path = path.encode("utf-8")
89         tmps = str.split(path, '/')
90         localFileName = tmps[len(tmps) - 1]
91         urllib.urlretrieve(path, localFileName)
92         return localFileName
93
94     def downloadFileFromFtpServer(self, path):
95         path = path.encode("utf-8")
96         tmp = str.split(path, '://')
97         protocol = tmp[0]
98         tmp = str.split(tmp[1], ':')
99         if len(tmp) == 2:
100             userName = tmp[0]
101             tmp = str.split(tmp[1], '@')
102             userPwd = tmp[0]
103             index = tmp[1].index('/')
104             hostIp = tmp[1][0:index]
105             remoteFileName = tmp[1][index:len(tmp[1])]
106             if protocol.lower() == 'ftp':
107                 hostPort = 21
108             else:
109                 hostPort = 22
110
111         if len(tmp) == 3:
112             userName = tmp[0]
113             userPwd = str.split(tmp[1], '@')[0]
114             hostIp = str.split(tmp[1], '@')[1]
115             index = tmp[2].index('/')
116             hostPort = tmp[2][0:index]
117             remoteFileName = tmp[2][index:len(tmp[2])]
118
119         localFileName = str.split(remoteFileName, '/')
120         localFileName = localFileName[len(localFileName) - 1]
121
122         if protocol.lower() == 'sftp':
123             self.sftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName)
124         else:
125             self.ftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName)
126         return localFileName
127
128     def sftp_get(self, userName, userPwd, hostIp, hostPort, remoteFileName, localFileName):
129         # return
130         t = None
131         try:
132             t = paramiko.Transport(hostIp, int(hostPort))
133             t.connect(username=userName, password=userPwd)
134             sftp = paramiko.SFTPClient.from_transport(t)
135             sftp.get(remoteFileName, localFileName)
136         finally:
137             if t is not None:
138                 t.close()
139
140     def ftp_get(self, userName, userPwd, hostIp, hostPort, remoteFileName, localFileName):
141         f = None
142         try:
143             ftp = ftplib.FTP()
144             ftp.connect(hostIp, hostPort)
145             ftp.login(userName, userPwd)
146             f = open(localFileName, 'wb')
147             ftp.retrbinary('RETR ' + remoteFileName, f.write, 1024)
148             f.close()
149         finally:
150             if f is not None:
151                 f.close()
152
153     def buidMetadata(self, tosca):
154         if 'metadata' in tosca.tpl:
155             self.metadata = copy.deepcopy(tosca.tpl['metadata'])
156
157     def buildProperties(self, nodeTemplate, parsed_params):
158         properties = {}
159         isMappingParams = parsed_params and len(parsed_params) > 0
160         for k, item in nodeTemplate.get_properties().items():
161             properties[k] = item.value
162             if isinstance(item.value, GetInput):
163                 if item.value.result() and isMappingParams:
164                     properties[k] = DataEntityExt.validate_datatype(item.type, item.value.result())
165                 else:
166                     tmp = {}
167                     tmp[item.value.name] = item.value.input_name
168                     properties[k] = tmp
169         if 'attributes' in nodeTemplate.entity_tpl:
170             for k, item in nodeTemplate.entity_tpl['attributes'].items():
171                 properties[k] = str(item)
172         return properties
173
174     def verify_properties(self, props, inputs, parsed_params):
175         ret_props = {}
176         if (props and len(props) > 0):
177             for key, value in props.items():
178                 ret_props[key] = self._verify_value(value, inputs, parsed_params)
179                 #                 if isinstance(value, str):
180                 #                     ret_props[key] = self._verify_string(inputs, parsed_params, value);
181                 #                     continue
182                 #                 if isinstance(value, list):
183                 #                     ret_props[key] = map(lambda x: self._verify_dict(inputs, parsed_params, x), value)
184                 #                     continue
185                 #                 if isinstance(value, dict):
186                 #                     ret_props[key] = self._verify_map(inputs, parsed_params, value)
187                 #                     continue
188                 #                 ret_props[key] = value
189         return ret_props
190
191     def build_requirements(self, node_template):
192         rets = []
193         for req in node_template.requirements:
194             for req_name, req_value in req.items():
195                 if (isinstance(req_value, dict)):
196                     if ('node' in req_value and req_value['node'] not in node_template.templates):
197                         continue  # No target requirement for aria parser, not add to result.
198                 rets.append({req_name: req_value})
199         return rets
200
201     def buildCapabilities(self, nodeTemplate, inputs, ret):
202         capabilities = json.dumps(nodeTemplate.entity_tpl.get('capabilities', None))
203         match = re.findall(r'\{"get_input":\s*"([\w|\-]+)"\}', capabilities)
204         for m in match:
205             aa = [input_def for input_def in inputs if m == input_def.name][0]
206             capabilities = re.sub(r'\{"get_input":\s*"([\w|\-]+)"\}', json.dumps(aa.default), capabilities, 1)
207         if capabilities != 'null':
208             ret['capabilities'] = json.loads(capabilities)
209
210     def buildArtifacts(self, nodeTemplate, inputs, ret):
211         artifacts = json.dumps(nodeTemplate.entity_tpl.get('artifacts', None))
212         match = re.findall(r'\{"get_input":\s*"([\w|\-]+)"\}', artifacts)
213         for m in match:
214             aa = [input_def for input_def in inputs if m == input_def.name][0]
215             artifacts = re.sub(r'\{"get_input":\s*"([\w|\-]+)"\}', json.dumps(aa.default), artifacts, 1)
216         if artifacts != 'null':
217             ret['artifacts'] = json.loads(artifacts)
218
219     def build_interfaces(self, node_template):
220         if 'interfaces' in node_template.entity_tpl:
221             return node_template.entity_tpl['interfaces']
222         return None
223
224     def isVnf(self, node):
225         # return node['nodeType'].upper().find('.VNF.') >= 0 or node['nodeType'].upper().endswith('.VNF')
226         return node['nodeType'].upper().find('.VF.') >= 0 or node['nodeType'].upper().endswith('.VF')
227
228     def isPnf(self, node):
229         return node['nodeType'].upper().find('.PNF.') >= 0 or node['nodeType'].upper().endswith('.PNF')
230
231     def isCp(self, node):
232         return node['nodeType'].upper().find('.CP.') >= 0 or node['nodeType'].upper().endswith('.CP')
233
234     def isVl(self, node):
235         isvl = node['nodeType'].upper().find('.VIRTUALLINK.') >= 0 or node['nodeType'].upper().find('.VL.') >= 0
236         isvl = isvl or node['nodeType'].upper().endswith('.VIRTUALLINK') or node['nodeType'].upper().endswith('.VL')
237         return isvl
238
239     def isService(self, node):
240         return node['nodeType'].upper().find('.SERVICE.') >= 0 or node['nodeType'].upper().endswith('.SERVICE')
241
242     def get_requirement_node_name(self, req_value):
243         return self.get_prop_from_obj(req_value, 'node')
244
245     def get_prop_from_obj(self, obj, prop):
246         if isinstance(obj, str):
247             return obj
248         if (isinstance(obj, dict) and prop in obj):
249             return obj[prop]
250         return None
251
252     def getNodeDependencys(self, node):
253         return self.getRequirementByName(node, 'dependency')
254
255     def getVirtualLinks(self, node):
256         return self.getRequirementByName(node, 'virtualLink')
257
258     def getVirtualbindings(self, node):
259         return self.getRequirementByName(node, 'virtualbinding')
260
261     def getRequirementByName(self, node, requirementName):
262         requirements = []
263         if 'requirements' in node:
264             for item in node['requirements']:
265                 for key, value in item.items():
266                     if key == requirementName:
267                         requirements.append(value)
268         return requirements
269
270     def get_networks(self, node):
271         rets = []
272         if 'requirements' in node:
273             for item in node['requirements']:
274                 for key, value in item.items():
275                     if key.upper().find('VIRTUALLINK') >= 0:
276                         rets.append({"key_name": key, "vl_id": self.get_requirement_node_name(value)})
277         return rets
278
279     def _verify_value(self, value, inputs, parsed_params):
280         if isinstance(value, str):
281             return self._verify_string(inputs, parsed_params, value)
282         if isinstance(value, list) or isinstance(value, dict):
283             return self._verify_object(value, inputs, parsed_params)
284         return value
285
286     def _verify_object(self, value, inputs, parsed_params):
287         s = self._verify_string(inputs, parsed_params, json.dumps(value))
288         return json.loads(s)
289
290     def _get_input_name(self, getInput):
291         input_name = getInput.split(':')[1]
292         input_name = input_name.strip()
293         return input_name.replace('"', '').replace('}', '')
294
295     def _verify_string(self, inputs, parsed_params, value):
296         getInputs = re.findall(r'{"get_input": "[a-zA-Z_0-9]+"}', value)
297         for getInput in getInputs:
298             input_name = self._get_input_name(getInput)
299             if parsed_params and input_name in parsed_params:
300                 value = value.replace(getInput, json.dumps(parsed_params[input_name]))
301             else:
302                 for input_def in inputs:
303                     if input_def.default and input_name == input_def.name:
304                         value = value.replace(getInput, json.dumps(input_def.default))
305         return value
306
307     def get_node_vl_id(self, node):
308         vl_ids = map(lambda x: self.get_requirement_node_name(x), self.getVirtualLinks(node))
309         if len(vl_ids) > 0:
310             return vl_ids[0]
311         return ""
312
313     def get_node_by_name(self, node_templates, name):
314         for node in node_templates:
315             if node['name'] == name:
316                 return node
317         return None
318
319     def get_all_nested_ns(self, nodes):
320         nss = []
321         for node in nodes:
322             if self.is_nested_ns(node):
323                 ns = {}
324                 ns['ns_id'] = node['name']
325                 ns['description'] = node['description']
326                 ns['properties'] = node['properties']
327                 ns['networks'] = self.get_networks(node)
328
329                 nss.append(ns)
330         return nss
331
332     def is_nested_ns(self, node):
333         return node['nodeType'].upper().find('.NS.') >= 0 or node['nodeType'].upper().endswith('.NS')
334
335     def isVdu(self, node):
336         return node['nodeType'].upper().find('.VDU.') >= 0 or node['nodeType'].upper().endswith('.VDU')
337
338     def getCapabilityByName(self, node, capabilityName):
339         if 'capabilities' in node and capabilityName in node['capabilities']:
340             return node['capabilities'][capabilityName]
341         return None
342
343     def get_node_vdu_id(self, node):
344         vdu_ids = map(lambda x: self.get_requirement_node_name(x), self.getVirtualbindings(node))
345         if len(vdu_ids) > 0:
346             return vdu_ids[0]
347         return ""