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
= []
60 def handle_yang(self
):
61 self
.log
.debug(_("Process VNFD desc {0}: {1}").format(self
.name
,
64 def process_vnf_config(conf
):
68 init_primitive_config
= {}
69 if 'config_template' in conf
:
70 config
['config_template'] = conf
['config_template']
71 if 'config_attributes' in conf
:
72 if 'config_delay' in conf
['config_attributes']:
73 config
['config_delay'] = conf
['config_attributes']['config_delay']
74 if 'config_priority' in conf
['config_attributes']:
75 config
['config_priority'] = conf
['config_attributes']['config_priority']
76 if 'config_type' in conf
:
77 config
['config_type'] = conf
['config_type']
79 config
['config_details'] = conf
['script']
80 for conf_type
in self
.CONFIG_TYPES
:
82 config
['config_type'] = conf_type
84 vnf_conf
['config'] = config
86 if 'initial_config_primitive' in conf
:
87 init_config_prims
= []
88 for init_conf_prim
in conf
['initial_config_primitive']:
90 if 'name' in init_conf_prim
:
91 init_conf
['name'] = init_conf_prim
['name']
92 if 'seq' in init_conf_prim
:
93 init_conf
['seq'] = init_conf_prim
['seq']
94 if 'user_defined_script' in init_conf_prim
:
95 init_conf
['user_defined_script'] = init_conf_prim
['user_defined_script']
96 self
.script_files
.append(init_conf_prim
['user_defined_script'])
97 if 'parameter' in init_conf_prim
:
98 init_conf
['parameter'] = []
99 for parameter
in init_conf_prim
['parameter']:
100 init_conf
['parameter'].append({parameter
['name']: parameter
['value']})
101 init_config_prims
.append(init_conf
)
102 vnf_conf
['initial_config_primitive'] = init_config_prims
104 self
.vnf_configuration
= vnf_conf
106 def process_mgmt_intf(intf
):
107 if len(self
.mgmt_intf
) > 0:
108 err_msg(_("{0}, Already processed another mgmt intf {1}, "
110 format(self
, self
.msmg_intf
, intf
))
111 self
.log
.error(err_msg
)
112 raise ValidationError(message
=err_msg
)
114 self
.mgmt_intf
['protocol'] = 'tcp'
116 if self
.PORT
in intf
:
117 self
.mgmt_intf
[self
.PORT
] = intf
.pop(self
.PORT
)
118 self
.props
[self
.PORT
] = self
.mgmt_intf
[self
.PORT
]
121 for vdu
in self
.vdus
:
122 if intf
['vdu_id'] == vdu
.id:
123 self
.mgmt_intf
[self
.VDU
] = vdu
.get_name(self
.name
)
127 if self
.DASHBOARD_PARAMS
in intf
:
128 self
.mgmt_intf
[self
.DASHBOARD_PARAMS
] = \
129 intf
.pop(self
.DASHBOARD_PARAMS
)
132 self
.log
.warn(_("{0}, Did not process all in mgmt "
135 self
.log
.debug(_("{0}, Management interface: {1}").
136 format(self
, self
.mgmt_intf
))
138 def process_http_ep(eps
):
139 self
.log
.debug("{}, HTTP EP: {}".format(self
, eps
))
141 http_ep
= {'protocol': 'http'} # Required for TOSCA
142 http_ep
[self
.PATH
] = ep
.pop(self
.PATH
)
143 http_ep
[self
.PORT
] = ep
.pop(self
.PORT
)
144 if self
.POLL_INTVL
in http_ep
:
145 http_ep
[self
.POLL_INTVL
] = ep
.pop(self
.POLL_INTVL_SECS
)
147 self
.log
.warn(_("{0}, Did not process the following for "
148 "http ep {1}").format(self
, ep
))
149 self
.log
.debug(_("{0}, http endpoint: {1}").format(self
, http_ep
))
150 self
.http_ep
.append(http_ep
)
152 def process_mon_param(params
):
155 fields
= [self
.NAME
, self
.ID
, 'value_type', 'units', 'group_tag',
156 'json_query_method', 'http_endpoint_ref', 'widget_type',
161 mon_param
['name'] = param
['name']
162 if 'description' in param
:
163 mon_param
['description'] = param
['description']
164 if 'polling_interval' in param
:
165 mon_param
['polling_interval'] = param
['polling_interval']
166 if 'http_endpoint_ref' in param
:
167 mon_param
['url_path'] = param
['http_endpoint_ref']
168 if 'json_query_method' in param
:
169 mon_param
['json_query_method'] = param
['json_query_method'].lower()
170 if 'group_tag' in param
:
171 ui_param
['group_tag'] = param
['group_tag']
172 if 'widget_type' in param
:
173 ui_param
['widget_type'] = param
['widget_type'].lower()
175 ui_param
['units'] = param
['units']
176 mon_param
['ui_data'] = ui_param
178 self
.mon_param
.append(mon_param
)
181 self
.log
.warn(_("{0}, Did not process the following for "
182 "monitporing-param {1}").
184 self
.log
.debug(_("{0}, Monitoring param: {1}").format(self
, monp
))
185 #self.mon_param.append(monp)
189 self
.log
.debug("{}, CP: {}".format(self
, cp_dic
))
190 name
= cp_dic
.pop(self
.NAME
)
191 for vdu
in self
.vdus
:
193 vdu
.set_cp_type(name
, cp_dic
.pop(self
.TYPE_Y
))
196 self
.log
.warn(_("{0}, Did not process the following for "
197 "connection-point {1}: {2}").
198 format(self
, name
, cp_dic
))
201 self
.MGMT_INTF
: process_mgmt_intf
,
202 self
.HTTP_EP
: process_http_ep
,
203 self
.MON_PARAM
: process_mon_param
,
204 'connection_point': process_cp
206 dic
= deepcopy(self
.yang
)
208 for key
in self
.REQUIRED_FIELDS
:
209 self
.props
[key
] = dic
.pop(key
)
211 self
.id = self
.props
[self
.ID
]
213 # Process VDUs before CPs so as to update the CP struct in VDU
214 # when we process CP later
216 for vdu_dic
in dic
.pop(self
.VDU
):
217 vdu
= YangVdu(self
.log
, vdu_dic
.pop(self
.NAME
),
220 self
.vdus
.append(vdu
)
221 for key
in ENDPOINTS_MAP
.keys():
223 ENDPOINTS_MAP
[key
](dic
.pop(key
))
224 if self
.VNF_CONFIG
in dic
:
225 process_vnf_config(dic
.pop(self
.VNF_CONFIG
))
227 self
.remove_ignored_fields(dic
)
229 self
.log
.warn(_("{0}, Did not process the following for "
232 self
.log
.debug(_("{0}, VNFD: {1}").format(self
, self
.props
))
233 except Exception as e
:
234 err_msg
= _("Exception processing VNFD {0} : {1}"). \
236 self
.log
.error(err_msg
)
237 raise ValidationError(message
=err_msg
)
239 def update_cp_vld(self
, cp_name
, vld_name
):
240 for vdu
in self
.vdus
:
241 cp
= vdu
.get_cp(cp_name
)
243 vdu
.set_vld(cp_name
, vld_name
)
245 def _generate_vnf_type(self
, tosca
):
246 name
= self
.name
.split('_', 1)[0]
247 self
.vnf_type
= "{0}{1}{2}".format(self
.vnf_prefix_type
, name
, 'VNF')
248 if self
.NODE_TYPES
not in tosca
and self
.vnf_type
:
249 tosca
[self
.NODE_TYPES
] = {}
250 tosca
[self
.NODE_TYPES
][self
.vnf_type
] = {
251 self
.DERIVED_FROM
: self
.T_VNF1
254 def generate_tosca_template(self
, tosca
):
256 tosca
['tosca_definitions_version'] = 'tosca_simple_profile_for_nfv_1_0'
257 tosca
[self
.IMPORT
] = []
258 tosca
[self
.IMPORT
].append("riftiotypes.yaml")
259 tosca
[self
.DESC
] = self
.props
[self
.DESC
]
260 tosca
[self
.METADATA
] = {
262 self
.VENDOR
: self
.props
[self
.VENDOR
],
263 self
.VERSION
: self
.props
[self
.VERSION
],
266 self
._generate
_vnf
_type
(tosca
);
269 tosca
[self
.TOPOLOGY_TMPL
] = {}
270 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
] = {}
271 tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
] = {}
272 tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
]['node_type'] = self
.vnf_type
274 for vdu
in self
.vdus
:
275 vdu
.generate_vdu_template(tosca
, self
.name
)
276 if 'vdu' in self
.mgmt_intf
and self
.mgmt_intf
['vdu'] == vdu
.get_name(self
.name
): #TEST
278 mgmt_interface
[self
.PROPERTIES
] = self
.mgmt_intf
279 self
.mgmt_intf
.pop('vdu')
281 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][vdu
.get_name(self
.name
)][self
.CAPABILITIES
]['mgmt_interface'] = mgmt_interface
#TEST
282 if len(self
.mon_param
) > 0:
285 mon_param
['properties'] = self
.mon_param
[0]
286 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][vdu
.get_name(self
.name
)][self
.CAPABILITIES
]['monitoring_param'] = mon_param
#TEST
287 if len(self
.mon_param
) == 2:
290 mon_param
['properties'] = self
.mon_param
[1]
291 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][vdu
.get_name(self
.name
)][self
.CAPABILITIES
]['monitoring_param_1'] = mon_param
294 node
[self
.TYPE
] = self
.T_VNF1
296 # Remove fields not required in TOSCA
297 self
.props
.pop(self
.DESC
)
299 # Update index to the member-vnf-index
301 # For now I am putting index as 1. This needs to be revisted
302 self
.props
[self
.ID
] = 1
303 node
[self
.PROPERTIES
] = self
.props
306 if len(self
.mgmt_intf
):
307 caps
[self
.MGMT_INTF
] = {
308 self
.PROPERTIES
: self
.mgmt_intf
311 if len(self
.http_ep
):
312 caps
[self
.HTTP_EP
] = {
313 self
.PROPERTIES
: self
.http_ep
[0]
315 if len(self
.http_ep
) > 1:
316 self
.log
.warn(_("{0}: Currently only one HTTP endpoint "
318 format(self
, self
.http_ep
))
320 if len(self
.mon_param
):
322 for monp
in self
.mon_param
:
323 name
= "{}_{}".format(self
.MON_PARAM
, count
)
324 caps
[name
] = {self
.PROPERTIES
: monp
}
327 node
[self
.CAPABILITIES
] = caps
331 for vdu
in self
.vdus
:
332 reqs
.append({'vdus': {self
.NODE
: vdu
.get_name(self
.name
)}})
334 node
[self
.REQUIREMENTS
] = reqs
336 self
.log
.warn(_("{0}, Did not find any VDUS with this VNF").
339 self
.log
.debug(_("{0}, VNF node: {1}").format(self
, node
))
341 #tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][self.name] = node
342 self
.get_vnf_configuration_policy(tosca
)
346 def generate_vld_link(self
, virtualLink
, conn_point
):
347 if self
.REQUIREMENTS
not in self
.tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
]:
348 self
.tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
] = {}
349 self
.tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
]['node_type'] = self
.vnf_type
350 #self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'] = []
351 #self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'].\
352 #append(['node_type', self.vnf_type])
353 self
.tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
][self
.REQUIREMENTS
] = []
355 for vdu
in self
.vdus
:
356 if conn_point
in vdu
.cp_name_to_cp_node
:
357 conn_point_node_name
= vdu
.cp_name_to_cp_node
[conn_point
]
358 self
.tosca
[self
.TOPOLOGY_TMPL
][self
.SUBSTITUTION_MAPPING
][self
.REQUIREMENTS
].\
359 append({virtualLink
: "[{0}, {1}]".format(conn_point_node_name
, "virtualLink")})
361 if self
.REQUIREMENTS
not in self
.tosca
[self
.NODE_TYPES
][self
.vnf_type
]:
362 self
.tosca
[self
.NODE_TYPES
][self
.vnf_type
][self
.REQUIREMENTS
] = []
363 self
.tosca
[self
.NODE_TYPES
][self
.vnf_type
][self
.REQUIREMENTS
].append({virtualLink
: {
364 "type": "tosca.nodes.nfv.VL"}})
367 def generate_tosca(self
):
371 def get_vnf_configuration_policy(self
, tosca
):
372 if self
.vnf_configuration
:
373 if self
.POLICIES
in tosca
:
374 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
]['configuration'] ={
375 'type' : self
.T_VNF_CONFIG
,
376 self
.PROPERTIES
: self
.vnf_configuration
379 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
] = []
380 # This is bad hack. TOSCA Openstack does not return policies without target
381 if len(tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
]) > 0:
382 node_name
= list(tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
].keys())[0]
383 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
].append({'configuration' :{
384 'type' : self
.T_VNF_CONFIG
,
385 self
.PROPERTIES
: self
.vnf_configuration
,
386 self
.TARGETS
: "[{0}]".format(node_name
)
389 def get_supporting_files(self
):
391 for file in self
.script_files
:
395 self
.DEST
: "{}/{}".format(self
.SCRIPT_DIR
, file),
399 for vdu
in self
.vdus
:
400 vdu_files
= vdu
.get_supporting_files()
401 for vdu_file
in vdu_files
:
402 files
.append(vdu_file
)