update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / common / python / rift / mano / yang_translator / rwmano / yang / yang_vnfd.py
1 # Copyright 2016 RIFT.io Inc
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
16 from copy import deepcopy
17
18 from rift.mano.yang_translator.common.exception import ValidationError
19 from rift.mano.yang_translator.common.utils import _
20 from rift.mano.yang_translator.rwmano.syntax.tosca_resource \
21 import ToscaResource
22 from rift.mano.yang_translator.rwmano.yang.yang_vdu import YangVdu
23
24 TARGET_CLASS_NAME = 'YangVnfd'
25
26
27 class YangVnfd(ToscaResource):
28 '''Class for RIFT.io YANG VNF descriptor translation to TOSCA type.'''
29
30 yangtype = 'vnfd'
31
32 CONFIG_TYPES = ['script', 'netconf', 'rest', 'juju']
33
34 OTHER_KEYS = (MGMT_INTF, HTTP_EP, MON_PARAM) = \
35 ('mgmt_interface', 'http_endpoint', 'monitoring_param')
36 vnf_prefix_type = 'tosca.nodes.nfv.riftio.'
37
38 VALUE_TYPE_CONVERSION_MAP = {
39 'INTEGER' : 'integer',
40 'INT' : 'integer',
41 'STRING' : 'string',
42 'DECIMAL' : 'float',
43 'INTEGER': 'INTEGER',
44 'DECIMAL' : 'float'
45
46 }
47
48 def __init__(self,
49 log,
50 name,
51 type_,
52 yang):
53 super(YangVnfd, self).__init__(log,
54 name,
55 type_,
56 yang)
57 self.props = {}
58 self.vdus = []
59 self.mgmt_intf = {}
60 self.mon_param = []
61 self.http_ep = []
62 self.vnf_configuration = None
63 self.monitor_param = {}
64 self.monitor_param_1 = {}
65 self.vnf_type = None
66 self.tosca = None
67 self.script_files = []
68 self.service_function_type = None
69
70 def handle_yang(self):
71 self.log.debug(_("Process VNFD desc {0}: {1}").format(self.name,
72 self.yang))
73
74 def process_vnf_config(conf):
75 vnf_conf = {}
76 config = {}
77
78 init_primitive_config = {}
79 if 'config_template' in conf:
80 config['config_template'] = conf['config_template']
81 if 'config_attributes' in conf:
82 if 'config_delay' in conf['config_attributes']:
83 config['config_delay'] = conf['config_attributes']['config_delay']
84 if 'config_priority' in conf['config_attributes']:
85 config['config_priority'] = conf['config_attributes']['config_priority']
86 if 'config_type' in conf:
87 config['config_type'] = conf['config_type']
88 if 'script' in conf:
89 config['config_details'] = conf['script']
90 for conf_type in self.CONFIG_TYPES:
91 if conf_type in conf:
92 config['config_type'] = conf_type
93 if len(config) > 0:
94 vnf_conf['config'] = config
95
96 if 'initial_config_primitive' in conf:
97 init_config_prims = []
98 for init_conf_prim in conf['initial_config_primitive']:
99 init_conf = {}
100 if 'name' in init_conf_prim:
101 init_conf['name'] = init_conf_prim['name']
102 if 'seq' in init_conf_prim:
103 init_conf['seq'] = init_conf_prim['seq']
104 if 'user_defined_script' in init_conf_prim:
105 init_conf['user_defined_script'] = init_conf_prim['user_defined_script']
106 self.script_files.append(init_conf_prim['user_defined_script'])
107 if 'parameter' in init_conf_prim:
108 init_conf['parameter'] = []
109 for parameter in init_conf_prim['parameter']:
110 init_conf['parameter'].append({parameter['name']: parameter['value']})
111 init_config_prims.append(init_conf)
112 vnf_conf['initial_config'] = init_config_prims
113
114 self.vnf_configuration = vnf_conf
115
116 def process_mgmt_intf(intf):
117 if len(self.mgmt_intf) > 0:
118 err_msg(_("{0}, Already processed another mgmt intf {1}, "
119 "got another {2}").
120 format(self, self.msmg_intf, intf))
121 self.log.error(err_msg)
122 raise ValidationError(message=err_msg)
123
124 self.mgmt_intf['protocol'] = 'tcp'
125
126 if self.PORT in intf:
127 self.mgmt_intf[self.PORT] = intf.pop(self.PORT)
128 self.props[self.PORT] = self.mgmt_intf[self.PORT]
129
130 if 'vdu_id' in intf:
131 for vdu in self.vdus:
132 if intf['vdu_id'] == vdu.id:
133 self.mgmt_intf[self.VDU] = vdu.get_name(self.name)
134 intf.pop('vdu_id')
135 break
136
137 if self.DASHBOARD_PARAMS in intf:
138 self.mgmt_intf[self.DASHBOARD_PARAMS] = \
139 intf.pop(self.DASHBOARD_PARAMS)
140
141 if len(intf):
142 self.log.warn(_("{0}, Did not process all in mgmt "
143 "interface {1}").
144 format(self, intf))
145 self.log.debug(_("{0}, Management interface: {1}").
146 format(self, self.mgmt_intf))
147
148 def process_http_ep(eps):
149 self.log.debug("{}, HTTP EP: {}".format(self, eps))
150 for ep in eps:
151 http_ep = {'protocol': 'http'} # Required for TOSCA
152 http_ep[self.PATH] = ep.pop(self.PATH)
153 http_ep[self.PORT] = ep.pop(self.PORT)
154 if self.POLL_INTVL in http_ep:
155 http_ep[self.POLL_INTVL] = ep.pop(self.POLL_INTVL_SECS)
156 if len(ep):
157 self.log.warn(_("{0}, Did not process the following for "
158 "http ep {1}").format(self, ep))
159 self.log.debug(_("{0}, http endpoint: {1}").format(self, http_ep))
160 self.http_ep.append(http_ep)
161
162 def process_mon_param(params):
163 for param in params:
164 monp = {}
165 fields = [self.NAME, self.ID, 'value_type', 'units', 'group_tag',
166 'json_query_method', 'http_endpoint_ref', 'widget_type',
167 self.DESC]
168 mon_param = {}
169 ui_param = {}
170 if 'name' in param:
171 mon_param['name'] = param['name']
172 if 'description' in param:
173 mon_param['description'] = param['description']
174 if 'polling_interval' in param:
175 mon_param['polling_interval'] = param['polling_interval']
176 if 'http_endpoint_ref' in param:
177 mon_param['url_path'] = param['http_endpoint_ref']
178 if 'json_query_method' in param:
179 mon_param['json_query_method'] = param['json_query_method'].lower()
180 #if 'value_type' in param:
181 # mon_param['constraints'] = {}
182 # mon_param['constraints']['value_type'] = YangVnfd.VALUE_TYPE_CONVERSION_MAP[param['value_type'].upper()]
183 if 'group_tag' in param:
184 ui_param['group_tag'] = param['group_tag']
185 if 'widget_type' in param:
186 ui_param['widget_type'] = param['widget_type'].lower()
187 if 'units' in param:
188 ui_param['units'] = param['units']
189 mon_param['ui_data'] = ui_param
190
191 self.mon_param.append(mon_param)
192
193 if len(param):
194 self.log.warn(_("{0}, Did not process the following for "
195 "monitporing-param {1}").
196 format(self, param))
197 self.log.debug(_("{0}, Monitoring param: {1}").format(self, monp))
198 #self.mon_param.append(monp)
199
200 def process_cp(cps):
201 for cp_dic in cps:
202 self.log.debug("{}, CP: {}".format(self, cp_dic))
203 name = cp_dic.pop(self.NAME)
204 for vdu in self.vdus:
205 if vdu.has_cp(name):
206 vdu.set_cp_type(name, cp_dic.pop(self.TYPE_Y))
207 break
208 if len(cp_dic):
209 self.log.warn(_("{0}, Did not process the following for "
210 "connection-point {1}: {2}").
211 format(self, name, cp_dic))
212
213 def process_service_type(dic):
214 self.service_function_type = dic['service_function_type']
215
216 ENDPOINTS_MAP = {
217 self.MGMT_INTF: process_mgmt_intf,
218 self.HTTP_EP: process_http_ep,
219 self.MON_PARAM: process_mon_param,
220 'connection_point': process_cp
221 }
222 dic = deepcopy(self.yang)
223 try:
224 for key in self.REQUIRED_FIELDS:
225 if key in dic:
226 self.props[key] = dic.pop(key)
227
228 self.id = self.props[self.ID]
229
230 # Process VDUs before CPs so as to update the CP struct in VDU
231 # when we process CP later
232 if self.VDU in dic:
233 for vdu_dic in dic.pop(self.VDU):
234 vdu = YangVdu(self.log, vdu_dic.pop(self.NAME),
235 self.VDU, vdu_dic)
236 vdu.process_vdu()
237 self.vdus.append(vdu)
238 for key in ENDPOINTS_MAP.keys():
239 if key in dic:
240 ENDPOINTS_MAP[key](dic.pop(key))
241 if self.VNF_CONFIG in dic:
242 process_vnf_config(dic.pop(self.VNF_CONFIG))
243
244 if 'service_function_type' in dic:
245 process_service_type(dic)
246
247 self.remove_ignored_fields(dic)
248 if len(dic):
249 self.log.warn(_("{0}, Did not process the following for "
250 "VNFD: {1}").
251 format(self, dic))
252 self.log.debug(_("{0}, VNFD: {1}").format(self, self.props))
253 except Exception as e:
254 err_msg = _("Exception processing VNFD {0} : {1}"). \
255 format(self.name, e)
256 self.log.error(err_msg)
257 raise ValidationError(message=err_msg)
258
259 def update_cp_vld(self, cp_name, vld_name):
260 for vdu in self.vdus:
261 cp = vdu.get_cp(cp_name)
262 if cp:
263 vdu.set_vld(cp_name, vld_name)
264 break
265 def _generate_vnf_type(self, tosca):
266 name = self.name.replace("_","")
267 name = name.split('_', 1)[0]
268 self.vnf_type = "{0}{1}{2}".format(self.vnf_prefix_type, name, 'VNF')
269 if self.NODE_TYPES not in tosca and self.vnf_type:
270 tosca[self.NODE_TYPES] = {}
271 tosca[self.NODE_TYPES][self.vnf_type] = {
272 self.DERIVED_FROM : self.T_VNF1
273 }
274
275 def generate_tosca_template(self, tosca):
276 self.tosca = tosca
277 tosca['tosca_definitions_version'] = 'tosca_simple_profile_for_nfv_1_0'
278 tosca[self.IMPORT] = []
279 tosca[self.IMPORT].append("riftiotypes.yaml")
280 tosca[self.DESC] = self.props[self.DESC]
281 tosca[self.METADATA] = {
282 'ID': self.name,
283 self.VENDOR: self.props[self.VENDOR],
284 self.VERSION: self.props[self.VERSION],
285 }
286 if self.name:
287 self._generate_vnf_type(tosca);
288
289
290 tosca[self.TOPOLOGY_TMPL] = {}
291 tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL] = {}
292 tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING] = {}
293 tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'] = self.vnf_type
294
295 for vdu in self.vdus:
296 vdu.generate_vdu_template(tosca, self.name)
297 if 'vdu' in self.mgmt_intf and self.mgmt_intf['vdu'] == vdu.get_name(self.name): #TEST
298 mgmt_interface = {}
299 mgmt_interface[self.PROPERTIES] = self.mgmt_intf
300 self.mgmt_intf.pop('vdu')
301 caps = []
302 tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES]['mgmt_interface'] = mgmt_interface #TEST
303 if len(self.mon_param) > 0:
304 mon_param = {}
305 mon_param = {}
306 mon_param['properties'] = self.mon_param[0]
307 tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES]['monitoring_param'] = mon_param #TEST
308 if len(self.mon_param) > 1:
309 for idx in range(1, len(self.mon_param)):
310 monitor_param_name = "monitoring_param_{}".format(idx)
311 mon_param = {}
312 mon_param = {}
313 mon_param['properties'] = self.mon_param[idx]
314 tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES][monitor_param_name] = mon_param
315
316 node = {}
317 node[self.TYPE] = self.T_VNF1
318
319 # Remove fields not required in TOSCA
320 self.props.pop(self.DESC)
321
322 # Update index to the member-vnf-index
323
324 # For now I am putting index as 1. This needs to be revisted
325 self.props[self.ID] = 1
326 node[self.PROPERTIES] = self.props
327
328 caps = {}
329 if len(self.mgmt_intf):
330 caps[self.MGMT_INTF] = {
331 self.PROPERTIES: self.mgmt_intf
332 }
333
334 if len(self.http_ep):
335 caps[self.HTTP_EP] = {
336 self.PROPERTIES: self.http_ep[0]
337 }
338 if len(self.http_ep) > 1:
339 self.log.warn(_("{0}: Currently only one HTTP endpoint "
340 "supported: {1}").
341 format(self, self.http_ep))
342
343 if len(self.mon_param):
344 count = 0
345 for monp in self.mon_param:
346 name = "{}_{}".format(self.MON_PARAM, count)
347 caps[name] = {self.PROPERTIES: monp}
348 count += 1
349
350 node[self.CAPABILITIES] = caps
351
352 if len(self.vdus):
353 reqs = []
354 for vdu in self.vdus:
355 reqs.append({'vdus': {self.NODE: vdu.get_name(self.name)}})
356
357 node[self.REQUIREMENTS] = reqs
358 else:
359 self.log.warn(_("{0}, Did not find any VDUS with this VNF").
360 format(self))
361
362 self.log.debug(_("{0}, VNF node: {1}").format(self, node))
363
364 #tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][self.name] = node
365 self.get_vnf_configuration_policy(tosca)
366
367 return tosca
368
369 def generate_vld_link(self, virtualLink, conn_point):
370 if self.REQUIREMENTS not in self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]:
371 self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING] = {}
372 self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'] = self.vnf_type
373 #self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'] = []
374 #self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'].\
375 #append(['node_type', self.vnf_type])
376 self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.REQUIREMENTS] = []
377
378 for vdu in self.vdus:
379 if conn_point in vdu.cp_name_to_cp_node:
380 conn_point_node_name = vdu.cp_name_to_cp_node[conn_point]
381 if {virtualLink : "[{0}, {1}]".format(conn_point_node_name, "virtualLink")} not in \
382 self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.REQUIREMENTS]:
383 self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.REQUIREMENTS].\
384 append({virtualLink : "[{0}, {1}]".format(conn_point_node_name, "virtualLink")})
385
386 if self.REQUIREMENTS not in self.tosca[self.NODE_TYPES][self.vnf_type]:
387 self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS] = []
388 if {virtualLink : {"type": "tosca.nodes.nfv.VL"}} not in self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS]:
389 self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS].append({virtualLink : {
390 "type": "tosca.nodes.nfv.VL"}})
391
392 def generate_forwarder_sub_mapping(self, sub_link):
393 if self.CAPABILITIES not in self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]:
394 self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.CAPABILITIES] = {}
395 self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.CAPABILITIES]
396
397 self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.CAPABILITIES][sub_link[1]] = \
398 "[{}, forwarder]".format(sub_link[2])
399
400 def generate_sfc_link(self, sfs_conn_point_name):
401 for vdu in self.vdus:
402 if sfs_conn_point_name in vdu.cp_name_to_cp_node:
403 conn_point_node_name = vdu.cp_name_to_cp_node[sfs_conn_point_name]
404 if conn_point_node_name in self.tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL]:
405 if self.CAPABILITIES not in self.tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL]:
406 self.tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][conn_point_node_name][self.CAPABILITIES] = {}
407 self.tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][conn_point_node_name][self.CAPABILITIES]['sfc'] = {self.PROPERTIES: {}}
408 self.tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][conn_point_node_name] \
409 [self.CAPABILITIES]['sfc'][self.PROPERTIES]['sfc_type'] = 'sf'
410
411 if self.service_function_type:
412 self.tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][conn_point_node_name] \
413 [self.CAPABILITIES]['sfc'][self.PROPERTIES]['sf_type'] = self.service_function_type
414
415 def generate_tosca(self):
416 tosca = {}
417 return tosca
418
419 def get_vnf_configuration_policy(self, tosca):
420 if self.vnf_configuration:
421 if self.POLICIES in tosca:
422 tosca[self.TOPOLOGY_TMPL][self.POLICIES]['configuration'] ={
423 'type' : self.T_VNF_CONFIG,
424 self.PROPERTIES: self.vnf_configuration
425 }
426 else:
427 tosca[self.TOPOLOGY_TMPL][self.POLICIES] = []
428 # This is bad hack. TOSCA Openstack does not return policies without target
429 if len(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL]) > 0:
430 node_name = list(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL].keys())[0]
431 tosca[self.TOPOLOGY_TMPL][self.POLICIES].append({'configuration' :{
432 'type' : self.T_VNF_CONFIG,
433 self.PROPERTIES: self.vnf_configuration,
434 self.TARGETS : "[{0}]".format(node_name)
435 }})
436
437 def get_supporting_files(self):
438 files = []
439 for file in self.script_files:
440 files.append({
441 self.TYPE: 'script',
442 self.NAME: file,
443 self.DEST: "{}/{}".format(self.SCRIPT_DIR, file),
444 })
445
446
447 for vdu in self.vdus:
448 vdu_files = vdu.get_supporting_files()
449 for vdu_file in vdu_files:
450 files.append(vdu_file)
451
452 return files