1 # Copyright 2016 RIFT.io Inc
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.
16 from copy
import deepcopy
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 \
22 from rift
.mano
.yang_translator
.rwmano
.yang
.yang_vdu
import YangVdu
24 TARGET_CLASS_NAME
= 'YangVnfd'
27 class YangVnfd(ToscaResource
):
28 '''Class for RIFT.io YANG VNF descriptor translation to TOSCA type.'''
32 CONFIG_TYPES
= ['script', 'netconf', 'rest', 'juju']
34 OTHER_KEYS
= (MGMT_INTF
, HTTP_EP
, MON_PARAM
) = \
35 ('mgmt_interface', 'http_endpoint', 'monitoring_param')
36 vnf_prefix_type
= 'tosca.nodes.nfv.riftio.'
38 VALUE_TYPE_CONVERSION_MAP
= {
39 'INTEGER' : 'integer',
53 super(YangVnfd
, self
).__init
__(log
,
62 self
.vnf_configuration
= None
63 self
.monitor_param
= {}
64 self
.monitor_param_1
= {}
67 self
.script_files
= []
68 self
.service_function_type
= None
70 def handle_yang(self
):
71 self
.log
.debug(_("Process VNFD desc {0}: {1}").format(self
.name
,
74 def process_vnf_config(conf
):
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']
89 config
['config_details'] = conf
['script']
90 for conf_type
in self
.CONFIG_TYPES
:
92 config
['config_type'] = conf_type
94 vnf_conf
['config'] = config
96 if 'initial_config_primitive' in conf
:
97 init_config_prims
= []
98 for init_conf_prim
in conf
['initial_config_primitive']:
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
114 self
.vnf_configuration
= vnf_conf
116 def process_mgmt_intf(intf
):
117 if len(self
.mgmt_intf
) > 0:
118 err_msg(_("{0}, Already processed another mgmt intf {1}, "
120 format(self
, self
.msmg_intf
, intf
))
121 self
.log
.error(err_msg
)
122 raise ValidationError(message
=err_msg
)
124 self
.mgmt_intf
['protocol'] = 'tcp'
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
]
131 for vdu
in self
.vdus
:
132 if intf
['vdu_id'] == vdu
.id:
133 self
.mgmt_intf
[self
.VDU
] = vdu
.get_name(self
.name
)
137 if self
.DASHBOARD_PARAMS
in intf
:
138 self
.mgmt_intf
[self
.DASHBOARD_PARAMS
] = \
139 intf
.pop(self
.DASHBOARD_PARAMS
)
142 self
.log
.warn(_("{0}, Did not process all in mgmt "
145 self
.log
.debug(_("{0}, Management interface: {1}").
146 format(self
, self
.mgmt_intf
))
148 def process_http_ep(eps
):
149 self
.log
.debug("{}, HTTP EP: {}".format(self
, 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
)
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
)
162 def process_mon_param(params
):
165 fields
= [self
.NAME
, self
.ID
, 'value_type', 'units', 'group_tag',
166 'json_query_method', 'http_endpoint_ref', 'widget_type',
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()
188 ui_param
['units'] = param
['units']
189 mon_param
['ui_data'] = ui_param
191 self
.mon_param
.append(mon_param
)
194 self
.log
.warn(_("{0}, Did not process the following for "
195 "monitporing-param {1}").
197 self
.log
.debug(_("{0}, Monitoring param: {1}").format(self
, monp
))
198 #self.mon_param.append(monp)
202 self
.log
.debug("{}, CP: {}".format(self
, cp_dic
))
203 name
= cp_dic
.pop(self
.NAME
)
204 for vdu
in self
.vdus
:
206 vdu
.set_cp_type(name
, cp_dic
.pop(self
.TYPE_Y
))
209 self
.log
.warn(_("{0}, Did not process the following for "
210 "connection-point {1}: {2}").
211 format(self
, name
, cp_dic
))
213 def process_service_type(dic
):
214 self
.service_function_type
= dic
['service_function_type']
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
222 dic
= deepcopy(self
.yang
)
224 for key
in self
.REQUIRED_FIELDS
:
226 self
.props
[key
] = dic
.pop(key
)
228 self
.id = self
.props
[self
.ID
]
230 # Process VDUs before CPs so as to update the CP struct in VDU
231 # when we process CP later
233 for vdu_dic
in dic
.pop(self
.VDU
):
234 vdu
= YangVdu(self
.log
, vdu_dic
.pop(self
.NAME
),
237 self
.vdus
.append(vdu
)
238 for key
in ENDPOINTS_MAP
.keys():
240 ENDPOINTS_MAP
[key
](dic
.pop(key
))
241 if self
.VNF_CONFIG
in dic
:
242 process_vnf_config(dic
.pop(self
.VNF_CONFIG
))
244 if 'service_function_type' in dic
:
245 process_service_type(dic
)
247 self
.remove_ignored_fields(dic
)
249 self
.log
.warn(_("{0}, Did not process the following for "
252 self
.log
.debug(_("{0}, VNFD: {1}").format(self
, self
.props
))
253 except Exception as e
:
254 err_msg
= _("Exception processing VNFD {0} : {1}"). \
256 self
.log
.error(err_msg
)
257 raise ValidationError(message
=err_msg
)
259 def update_cp_vld(self
, cp_name
, vld_name
):
260 for vdu
in self
.vdus
:
261 cp
= vdu
.get_cp(cp_name
)
263 vdu
.set_vld(cp_name
, vld_name
)
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
275 def generate_tosca_template(self
, 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
] = {
283 self
.VENDOR
: self
.props
[self
.VENDOR
],
284 self
.VERSION
: self
.props
[self
.VERSION
],
287 self
._generate
_vnf
_type
(tosca
);
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
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
299 mgmt_interface
[self
.PROPERTIES
] = self
.mgmt_intf
300 self
.mgmt_intf
.pop('vdu')
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:
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
)
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
317 node
[self
.TYPE
] = self
.T_VNF1
319 # Remove fields not required in TOSCA
320 self
.props
.pop(self
.DESC
)
322 # Update index to the member-vnf-index
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
329 if len(self
.mgmt_intf
):
330 caps
[self
.MGMT_INTF
] = {
331 self
.PROPERTIES
: self
.mgmt_intf
334 if len(self
.http_ep
):
335 caps
[self
.HTTP_EP
] = {
336 self
.PROPERTIES
: self
.http_ep
[0]
338 if len(self
.http_ep
) > 1:
339 self
.log
.warn(_("{0}: Currently only one HTTP endpoint "
341 format(self
, self
.http_ep
))
343 if len(self
.mon_param
):
345 for monp
in self
.mon_param
:
346 name
= "{}_{}".format(self
.MON_PARAM
, count
)
347 caps
[name
] = {self
.PROPERTIES
: monp
}
350 node
[self
.CAPABILITIES
] = caps
354 for vdu
in self
.vdus
:
355 reqs
.append({'vdus': {self
.NODE
: vdu
.get_name(self
.name
)}})
357 node
[self
.REQUIREMENTS
] = reqs
359 self
.log
.warn(_("{0}, Did not find any VDUS with this VNF").
362 self
.log
.debug(_("{0}, VNF node: {1}").format(self
, node
))
364 #tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][self.name] = node
365 self
.get_vnf_configuration_policy(tosca
)
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
] = []
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")})
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"}})
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
]
397 self
.tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
][self
.CAPABILITIES
][sub_link
[1]] = \
398 "[{}, forwarder]".format(sub_link
[2])
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'
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
415 def generate_tosca(self
):
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
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
)
437 def get_supporting_files(self
):
439 for file in self
.script_files
:
443 self
.DEST
: "{}/{}".format(self
.SCRIPT_DIR
, file),
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
)