4e421d1f39a088d14821de5bee73123d977e1ba7
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
19 from rift
.mano
.yang_translator
.common
.exception
import ValidationError
20 from rift
.mano
.yang_translator
.common
.utils
import _
21 from rift
.mano
.yang_translator
.rwmano
.syntax
.tosca_resource \
23 from rift
.mano
.yang_translator
.rwmano
.yang
.yang_vld
import YangVld
25 TARGET_CLASS_NAME
= 'YangNsd'
28 class YangNsd(ToscaResource
):
29 '''Class for RIFT.io YANG NS descriptor translation to TOSCA type.'''
33 OTHER_FIELDS
= (SCALE_GRP
, CONF_PRIM
,
34 USER_DEF_SCRIPT
, SCALE_ACT
,
35 TRIGGER
, NS_CONF_PRIM_REF
,
36 CONST_VNFD
, VNFD_MEMBERS
,
37 MIN_INST_COUNT
, MAX_INST_COUNT
,
38 INPUT_PARAM_XPATH
, CONFIG_ACTIONS
,
40 ('scaling_group_descriptor', 'service_primitive',
41 'user_defined_script', 'scaling_config_action',
42 'trigger', 'ns_config_primitive_name_ref',
43 'constituent_vnfd', 'vnfd_member',
44 'min_instance_count', 'max_instance_count',
45 'input_parameter_xpath', 'config_actions',
46 'initial_config_primitive',)
54 super(YangNsd
, self
).__init
__(log
,
65 self
.placement_groups
= []
66 self
.vnf_id_to_vnf_map
= {}
67 self
.vnfd_files
= vnfd_files
68 self
.vld_to_vnf_map
= {}
69 self
.vnf_to_vld_map
= {}
70 self
._vnf
_vld
_conn
_point
_map
= {}
72 def handle_yang(self
, vnfds
):
73 self
.log
.debug(_("Process NSD desc {0}: {1}").
74 format(self
.name
, self
.yang
))
76 def process_input_param(param
):
77 if self
.XPATH
in param
:
78 val
= param
.pop(self
.XPATH
)
79 # Strip namesapce, catalog and nsd part
82 self
.map_yang_name_to_tosca(
83 val
.replace('/nsd:nsd-catalog/nsd:nsd/nsd:', ''))})
85 self
.log
.warn(_("{0}, Did not process the following for "
86 "input param {1}: {2}").
87 format(self
, self
.inputs
, param
))
88 self
.log
.debug(_("{0}, inputs: {1}").format(self
, self
.inputs
[-1]))
90 def process_const_vnfd(cvnfd
):
91 # Get the matching VNFD
92 vnfd_id
= cvnfd
.pop(self
.VNFD_ID_REF
)
94 if vnfd
.type == self
.VNFD
and vnfd
.id == vnfd_id
:
95 self
.vnf_id_to_vnf_map
[vnfd_id
] = vnfd
.name
96 self
.vnfds
[cvnfd
.pop(self
.MEM_VNF_INDEX
)] = vnfd
97 if self
.START_BY_DFLT
in cvnfd
:
98 vnfd
.props
[self
.START_BY_DFLT
] = \
99 cvnfd
.pop(self
.START_BY_DFLT
)
103 self
.log
.warn(_("{0}, Did not process the following for "
104 "constituent vnfd {1}: {2}").
105 format(self
, vnfd_id
, cvnfd
))
106 self
.log
.debug(_("{0}, VNFD: {1}").format(self
, self
.vnfds
))
108 def process_scale_grp(dic
):
110 self
.log
.debug(_("{0}, scale group: {1}").format(self
, dic
))
111 fields
= [self
.NAME
, self
.MIN_INST_COUNT
, self
.MAX_INST_COUNT
]
114 sg
[key
] = dic
.pop(key
)
117 for vnfd_memb
in dic
.pop(self
.VNFD_MEMBERS
):
118 vnfd_idx
= vnfd_memb
[self
.MEM_VNF_INDEX_REF
]
119 if vnfd_idx
in self
.vnfds
:
120 membs
[self
.vnfds
[vnfd_idx
].name
] = \
121 vnfd_memb
[self
.COUNT
]
122 sg
['vnfd_members'] = membs
125 if self
.SCALE_ACT
in dic
:
126 for sg_act
in dic
.pop(self
.SCALE_ACT
):
127 # Validate the primitive
128 prim
= sg_act
.pop(self
.NS_CONF_PRIM_REF
)
129 for cprim
in self
.conf_prims
:
130 if cprim
[self
.NAME
] == prim
:
131 trigs
[sg_act
.pop(self
.TRIGGER
)] = prim
134 err_msg
= (_("{0}, Did not find config-primitive {1}").
136 self
.log
.error(err_msg
)
137 raise ValidationError(message
=err_msg
)
138 sg
[self
.CONFIG_ACTIONS
] = trigs
141 self
.log
.warn(_("{0}, Did not process all fields for {1}").
143 self
.log
.debug(_("{0}, Scale group {1}").format(self
, sg
))
144 self
.scale_grps
.append(sg
)
146 def process_initial_config(dic
):
148 self
.log
.debug(_("{0}, initial config: {1}").format(self
, dic
))
149 for key
in [self
.NAME
, self
.SEQ
, self
.USER_DEF_SCRIPT
]:
151 icp
[key
] = dic
.pop(key
)
154 if self
.PARAM
in dic
:
155 for p
in dic
.pop(self
.PARAM
):
156 if (self
.NAME
in p
and
158 params
[p
[self
.NAME
]] = p
[self
.VALUE
]
160 # TODO (pjoseph): Need to add support to read the
161 # config file and get the value from that
162 self
.log
.warn(_("{0}, Got parameter without value: {1}").
165 icp
[self
.PARAM
] = params
168 self
.log
.warn(_("{0}, Did not process all fields for {1}").
170 self
.log
.debug(_("{0}, Initial config {1}").format(self
, icp
))
171 self
.initial_cfg
.append({self
.PROPERTIES
: icp
})
173 def process_vld(vld
, dic
):
176 ip_profile_vld
= None
178 if 'ip_profile_ref' in vld
:
179 ip_profile_name
= vld
['ip_profile_ref']
180 if 'ip_profiles' in dic
:
181 for ip_prof
in dic
['ip_profiles']:
182 if ip_profile_name
== ip_prof
['name']:
183 ip_profile_vld
= ip_prof
185 vld_name
= vld
['name']
186 if 'description' in vld
:
187 vld_conf
['description'] = vld
['description']
189 vld_conf
['vendor'] = vld
['vendor']
191 if 'ip_profile_params' in ip_profile_vld
:
192 ip_param
= ip_profile_vld
['ip_profile_params']
193 if 'gateway_address' in ip_param
:
194 vld_conf
['gateway_ip'] = ip_param
['gateway_address']
195 if 'subnet_address' in ip_param
:
196 vld_conf
['cidr'] = ip_param
['subnet_address']
197 if 'ip_version' in ip_param
:
198 vld_conf
['ip_version'] = ip_param
['ip_version'].replace('ipv','')
201 vld_prop
= {vld_name
:
204 self
.PROPERTIES
: vld_conf
206 self
.vlds
[vld_name
] = { 'type': self
.T_ELAN
,
207 self
.PROPERTIES
: vld_conf
210 self
.vld_to_vnf_map
[vld_name
] = []
211 if 'vnfd_connection_point_ref' in vld
:
212 for vnfd_ref
in vld
['vnfd_connection_point_ref']:
213 vnf_name
= self
.vnf_id_to_vnf_map
[vnfd_ref
['vnfd_id_ref']]
214 if vnf_name
in self
.vnf_to_vld_map
:
215 self
.vnf_to_vld_map
[vnf_name
].append(vld_name
)
216 self
._vnf
_vld
_conn
_point
_map
[vnf_name
].\
217 append((vld_name
,vnfd_ref
['vnfd_connection_point_ref']))
219 self
.vnf_to_vld_map
[vnf_name
] = []
220 self
._vnf
_vld
_conn
_point
_map
[vnf_name
] = []
221 self
.vnf_to_vld_map
[vnf_name
].append(vld_name
)
222 self
._vnf
_vld
_conn
_point
_map
[vnf_name
].\
223 append((vld_name
,vnfd_ref
['vnfd_connection_point_ref']))
225 def process_placement_group(placement_groups
):
226 for i
in range(0, len(placement_groups
)):
227 placement_group
= placement_groups
[i
]
228 pg_name
= "placement_{0}".format(i
)
231 if 'name' in placement_group
:
232 pg_config
['name'] = placement_group
['name']
233 if 'requirement' in placement_group
:
234 pg_config
['requirement'] = placement_group
['requirement']
235 if 'strategy' in placement_group
:
236 pg_config
['strategy'] = placement_group
['strategy']
237 if 'member_vnfd' in placement_group
:
238 for member_vnfd
in placement_group
['member_vnfd']:
239 targets
.append(self
.vnf_id_to_vnf_map
[member_vnfd
['vnfd_id_ref']])
240 placement
= { pg_name
: {
241 'type': self
.T_PLACEMENT
,
242 self
.PROPERTIES
: pg_config
,
243 self
.TARGETS
: str(targets
)
246 self
.placement_groups
.append(placement
)
248 dic
= deepcopy(self
.yang
)
250 for key
in self
.REQUIRED_FIELDS
:
251 self
.props
[key
] = dic
.pop(key
)
253 self
.id = self
.props
[self
.ID
]
255 # Process constituent VNFDs
256 if self
.CONST_VNFD
in dic
:
257 for cvnfd
in dic
.pop(self
.CONST_VNFD
):
258 process_const_vnfd(cvnfd
)
262 for vld_dic
in dic
.pop(self
.VLD
):
263 process_vld(vld_dic
, dic
)
264 #self.vlds.append(vld)
266 # Process config primitives
267 if self
.CONF_PRIM
in dic
:
268 for cprim
in dic
.pop(self
.CONF_PRIM
):
269 conf_prim
= {self
.NAME
: cprim
.pop(self
.NAME
)}
270 if self
.USER_DEF_SCRIPT
in cprim
:
271 conf_prim
[self
.USER_DEF_SCRIPT
] = \
272 cprim
.pop(self
.USER_DEF_SCRIPT
)
273 self
.conf_prims
.append(conf_prim
)
275 err_msg
= (_("{0}, Only user defined script supported "
276 "in config-primitive for now {}: {}").
277 format(self
, conf_prim
, cprim
))
278 self
.log
.error(err_msg
)
279 raise ValidationError(message
=err_msg
)
281 # Process scaling group
282 if self
.SCALE_GRP
in dic
:
283 for sg_dic
in dic
.pop(self
.SCALE_GRP
):
284 process_scale_grp(sg_dic
)
286 # Process initial config primitives
287 if self
.INITIAL_CFG
in dic
:
288 for icp_dic
in dic
.pop(self
.INITIAL_CFG
):
289 process_initial_config(icp_dic
)
291 # Process the input params
292 if self
.INPUT_PARAM_XPATH
in dic
:
293 for param
in dic
.pop(self
.INPUT_PARAM_XPATH
):
294 process_input_param(param
)
296 if 'placement_groups' in dic
:
297 process_placement_group(dic
['placement_groups'])
300 self
.remove_ignored_fields(dic
)
302 self
.log
.warn(_("{0}, Did not process the following for "
304 format(self
, self
.props
, dic
))
305 self
.log
.debug(_("{0}, NSD: {1}").format(self
, self
.props
))
306 except Exception as e
:
307 err_msg
= _("Exception processing NSD {0} : {1}"). \
309 self
.log
.error(err_msg
)
310 self
.log
.exception(e
)
311 raise ValidationError(message
=err_msg
)
313 def generate_tosca_type(self
):
315 self
.log
.debug(_("{0} Generate tosa types").
319 #tosca[self.DATA_TYPES] = {}
320 #tosca[self.NODE_TYPES] = {}
322 for idx
, vnfd
in self
.vnfds
.items():
323 tosca
= vnfd
.generate_tosca_type(tosca
)
325 for vld
in self
.vlds
:
326 tosca
= vld
.generate_tosca_type(tosca
)
328 # Generate type for config primitives
329 if self
.GROUP_TYPES
not in tosca
:
330 tosca
[self
.GROUP_TYPES
] = {}
331 if self
.T_CONF_PRIM
not in tosca
[self
.GROUP_TYPES
]:
332 tosca
[self
.GROUP_TYPES
][self
.T_CONF_PRIM
] = {
333 self
.DERIVED_FROM
: 'tosca.policies.Root',
335 'primitive': self
.MAP
338 # Generate type for scaling group
339 if self
.POLICY_TYPES
not in tosca
:
340 tosca
[self
.POLICY_TYPES
] = {}
341 if self
.T_SCALE_GRP
not in tosca
[self
.POLICY_TYPES
]:
342 tosca
[self
.POLICY_TYPES
][self
.T_SCALE_GRP
] = {
343 self
.DERIVED_FROM
: 'tosca.policies.Root',
346 {self
.TYPE
: self
.STRING
},
348 {self
.TYPE
: self
.INTEGER
},
350 {self
.TYPE
: self
.INTEGER
},
352 {self
.TYPE
: self
.MAP
},
354 {self
.TYPE
: self
.MAP
}
357 if self
.T_INITIAL_CFG
not in tosca
[self
.POLICY_TYPES
]:
358 tosca
[self
.POLICY_TYPES
][self
.T_INITIAL_CFG
] = {
359 self
.DERIVED_FROM
: 'tosca.policies.Root',
362 {self
.TYPE
: self
.STRING
},
364 {self
.TYPE
: self
.INTEGER
},
365 self
.USER_DEF_SCRIPT
:
366 {self
.TYPE
: self
.STRING
},
368 {self
.TYPE
: self
.MAP
},
373 def generate_tosca_template(self
, tosca
):
374 self
.log
.debug(_("{0}, Generate tosca template").
376 # Add the standard entries
377 tosca
['tosca_definitions_version'] = \
378 'tosca_simple_profile_for_nfv_1_0'
379 tosca
[self
.DESC
] = self
.props
[self
.DESC
]
380 tosca
[self
.METADATA
] = {
382 self
.VENDOR
: self
.props
[self
.VENDOR
],
383 self
.VERSION
: self
.props
[self
.VERSION
],
385 if len(self
.vnfd_files
) > 0:
386 tosca
[self
.IMPORT
] = []
388 for vnfd_file
in self
.vnfd_files
:
389 tosca
[self
.IMPORT
].append('"{0}.yaml"'.format(vnfd_file
))
391 tosca
[self
.TOPOLOGY_TMPL
] = {}
396 if self.INPUTS not in tosca[self.TOPOLOGY_TMPL]:
397 tosca[self.TOPOLOGY_TMPL][self.INPUTS] = {}
398 for inp in self.inputs:
399 entry = {inp[self.NAME]: {self.TYPE: self.STRING,
401 'Translated from YANG'}}
402 tosca[self.TOPOLOGY_TMPL][self.INPUTS] = entry
404 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
] = {}
406 # Add the VNFDs and VLDs
407 for idx
, vnfd
in self
.vnfds
.items():
408 #vnfd.generate_vnf_template(tosca, idx)
410 'type' : vnfd
.vnf_type
,
413 self
.VENDOR
: self
.props
[self
.VENDOR
],
414 self
.VERSION
: self
.props
[self
.VERSION
]
417 if vnfd
.name
in self
.vnf_to_vld_map
:
418 vld_list
= self
.vnf_to_vld_map
[vnfd
.name
]
419 node
[self
.REQUIREMENTS
] = []
420 for vld_idx
in range(0, len(vld_list
)):
421 vld_link_name
= "{0}{1}".format("virtualLink", vld_idx
+ 1)
423 vld_prop
[vld_link_name
] = vld_list
[vld_idx
]
424 node
[self
.REQUIREMENTS
].append(vld_prop
)
425 if vnfd
.name
in self
._vnf
_vld
_conn
_point
_map
:
426 vnf_vld_list
= self
._vnf
_vld
_conn
_point
_map
[vnfd
.name
]
427 for vnf_vld
in vnf_vld_list
:
428 vnfd
.generate_vld_link(vld_link_name
, vnf_vld
[1])
431 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][vnfd
.name
] = node
433 for vld_node_name
in self
.vlds
:
434 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][vld_node_name
] = self
.vlds
[vld_node_name
]
436 # add the config primitives
437 if len(self
.conf_prims
):
438 if self
.GROUPS
not in tosca
[self
.TOPOLOGY_TMPL
]:
439 tosca
[self
.TOPOLOGY_TMPL
][self
.GROUPS
] = {}
442 self
.TYPE
: self
.T_CONF_PRIM
444 conf_prims
[self
.MEMBERS
] = [vnfd
.name
for vnfd
in
447 for confp
in self
.conf_prims
:
448 prims
[confp
[self
.NAME
]] = {
449 self
.USER_DEF_SCRIPT
: confp
[self
.USER_DEF_SCRIPT
]
451 conf_prims
[self
.PROPERTIES
] = {
452 self
.PRIMITIVES
: prims
455 tosca
[self
.TOPOLOGY_TMPL
][self
.GROUPS
][self
.CONF_PRIM
] = conf_prims
458 # Add the scale group
459 if len(self
.scale_grps
):
460 if self
.POLICIES
not in tosca
[self
.TOPOLOGY_TMPL
]:
461 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
] = []
463 for sg
in self
.scale_grps
:
465 self
.TYPE
: self
.T_SCALE_GRP
,
468 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
].append({
472 # Add initial configs
473 if len(self
.initial_cfg
):
474 if self
.POLICIES
not in tosca
[self
.TOPOLOGY_TMPL
]:
475 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
] = []
477 for icp
in self
.initial_cfg
:
478 if len(tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
]) > 0:
479 node_name
= list(tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
].keys())[0]
481 self
.TYPE
: self
.T_INITIAL_CFG
,
482 self
.TARGETS
: "[{0}]".format(node_name
)
485 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
].append({
486 self
.INITIAL_CFG
: icpt
489 if len(self
.placement_groups
) > 0:
490 if self
.POLICIES
not in tosca
[self
.TOPOLOGY_TMPL
]:
491 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
] = []
493 for placment_group
in self
.placement_groups
:
494 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
].append(placment_group
)
498 def get_supporting_files(self
):
500 # Get the config files for initial config
501 for icp
in self
.initial_cfg
:
502 if 'properties' in icp
:
503 if 'user_defined_script' in icp
['properties']:
504 script
= os
.path
.basename(icp
['properties']['user_defined_script'])
508 self
.DEST
: "{}/{}".format(self
.SCRIPT_DIR
, script
),
511 # TODO (pjoseph): Add support for config scripts,
514 self
.log
.debug(_("{0}, supporting files: {1}").format(self
, files
))