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.'
44 super(YangVnfd
, self
).__init
__(log
,
53 self
.vnf_configuration
= None
54 self
.monitor_param
= {}
55 self
.monitor_param_1
= {}
58 self
.script_files
= []
59 self
.service_function_type
= None
61 def handle_yang(self
):
62 self
.log
.debug(_("Process VNFD desc {0}: {1}").format(self
.name
,
65 def process_vnf_config(conf
):
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']
80 config
['config_details'] = conf
['script']
81 for conf_type
in self
.CONFIG_TYPES
:
83 config
['config_type'] = conf_type
85 vnf_conf
['config'] = config
87 if 'initial_config_primitive' in conf
:
88 init_config_prims
= []
89 for init_conf_prim
in conf
['initial_config_primitive']:
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
105 self
.vnf_configuration
= vnf_conf
107 def process_mgmt_intf(intf
):
108 if len(self
.mgmt_intf
) > 0:
109 err_msg(_("{0}, Already processed another mgmt intf {1}, "
111 format(self
, self
.msmg_intf
, intf
))
112 self
.log
.error(err_msg
)
113 raise ValidationError(message
=err_msg
)
115 self
.mgmt_intf
['protocol'] = 'tcp'
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
]
122 for vdu
in self
.vdus
:
123 if intf
['vdu_id'] == vdu
.id:
124 self
.mgmt_intf
[self
.VDU
] = vdu
.get_name(self
.name
)
128 if self
.DASHBOARD_PARAMS
in intf
:
129 self
.mgmt_intf
[self
.DASHBOARD_PARAMS
] = \
130 intf
.pop(self
.DASHBOARD_PARAMS
)
133 self
.log
.warn(_("{0}, Did not process all in mgmt "
136 self
.log
.debug(_("{0}, Management interface: {1}").
137 format(self
, self
.mgmt_intf
))
139 def process_http_ep(eps
):
140 self
.log
.debug("{}, HTTP EP: {}".format(self
, 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
)
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
)
153 def process_mon_param(params
):
156 fields
= [self
.NAME
, self
.ID
, 'value_type', 'units', 'group_tag',
157 'json_query_method', 'http_endpoint_ref', 'widget_type',
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()
176 ui_param
['units'] = param
['units']
177 mon_param
['ui_data'] = ui_param
179 self
.mon_param
.append(mon_param
)
182 self
.log
.warn(_("{0}, Did not process the following for "
183 "monitporing-param {1}").
185 self
.log
.debug(_("{0}, Monitoring param: {1}").format(self
, monp
))
186 #self.mon_param.append(monp)
190 self
.log
.debug("{}, CP: {}".format(self
, cp_dic
))
191 name
= cp_dic
.pop(self
.NAME
)
192 for vdu
in self
.vdus
:
194 vdu
.set_cp_type(name
, cp_dic
.pop(self
.TYPE_Y
))
197 self
.log
.warn(_("{0}, Did not process the following for "
198 "connection-point {1}: {2}").
199 format(self
, name
, cp_dic
))
201 def process_service_type(dic
):
202 self
.service_function_type
= dic
['service_function_type']
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
210 dic
= deepcopy(self
.yang
)
212 for key
in self
.REQUIRED_FIELDS
:
213 self
.props
[key
] = dic
.pop(key
)
215 self
.id = self
.props
[self
.ID
]
217 # Process VDUs before CPs so as to update the CP struct in VDU
218 # when we process CP later
220 for vdu_dic
in dic
.pop(self
.VDU
):
221 vdu
= YangVdu(self
.log
, vdu_dic
.pop(self
.NAME
),
224 self
.vdus
.append(vdu
)
225 for key
in ENDPOINTS_MAP
.keys():
227 ENDPOINTS_MAP
[key
](dic
.pop(key
))
228 if self
.VNF_CONFIG
in dic
:
229 process_vnf_config(dic
.pop(self
.VNF_CONFIG
))
231 if 'service_function_type' in dic
:
232 process_service_type(dic
)
234 self
.remove_ignored_fields(dic
)
236 self
.log
.warn(_("{0}, Did not process the following for "
239 self
.log
.debug(_("{0}, VNFD: {1}").format(self
, self
.props
))
240 except Exception as e
:
241 err_msg
= _("Exception processing VNFD {0} : {1}"). \
243 self
.log
.error(err_msg
)
244 raise ValidationError(message
=err_msg
)
246 def update_cp_vld(self
, cp_name
, vld_name
):
247 for vdu
in self
.vdus
:
248 cp
= vdu
.get_cp(cp_name
)
250 vdu
.set_vld(cp_name
, vld_name
)
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
262 def generate_tosca_template(self
, 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
] = {
270 self
.VENDOR
: self
.props
[self
.VENDOR
],
271 self
.VERSION
: self
.props
[self
.VERSION
],
274 self
._generate
_vnf
_type
(tosca
);
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
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
286 mgmt_interface
[self
.PROPERTIES
] = self
.mgmt_intf
287 self
.mgmt_intf
.pop('vdu')
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:
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:
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
302 node
[self
.TYPE
] = self
.T_VNF1
304 # Remove fields not required in TOSCA
305 self
.props
.pop(self
.DESC
)
307 # Update index to the member-vnf-index
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
314 if len(self
.mgmt_intf
):
315 caps
[self
.MGMT_INTF
] = {
316 self
.PROPERTIES
: self
.mgmt_intf
319 if len(self
.http_ep
):
320 caps
[self
.HTTP_EP
] = {
321 self
.PROPERTIES
: self
.http_ep
[0]
323 if len(self
.http_ep
) > 1:
324 self
.log
.warn(_("{0}: Currently only one HTTP endpoint "
326 format(self
, self
.http_ep
))
328 if len(self
.mon_param
):
330 for monp
in self
.mon_param
:
331 name
= "{}_{}".format(self
.MON_PARAM
, count
)
332 caps
[name
] = {self
.PROPERTIES
: monp
}
335 node
[self
.CAPABILITIES
] = caps
339 for vdu
in self
.vdus
:
340 reqs
.append({'vdus': {self
.NODE
: vdu
.get_name(self
.name
)}})
342 node
[self
.REQUIREMENTS
] = reqs
344 self
.log
.warn(_("{0}, Did not find any VDUS with this VNF").
347 self
.log
.debug(_("{0}, VNF node: {1}").format(self
, node
))
349 #tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][self.name] = node
350 self
.get_vnf_configuration_policy(tosca
)
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
] = []
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")})
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
]
378 self
.tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
][self
.CAPABILITIES
][sub_link
[1]] = \
379 "[{}, forwarder]".format(sub_link
[2])
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'
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
396 def generate_tosca(self
):
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
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
)
418 def get_supporting_files(self
):
420 for file in self
.script_files
:
424 self
.DEST
: "{}/{}".format(self
.SCRIPT_DIR
, file),
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
)