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