491bd86db4ee23b52ca16473cd3f94609fdd44f5
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',)
53 super(YangNsd
, self
).__init
__(log
,
65 def handle_yang(self
, vnfds
):
66 self
.log
.debug(_("Process NSD desc {0}: {1}").
67 format(self
.name
, self
.yang
))
69 def process_input_param(param
):
70 if self
.XPATH
in param
:
71 val
= param
.pop(self
.XPATH
)
72 # Strip namesapce, catalog and nsd part
75 self
.map_yang_name_to_tosca(
76 val
.replace('/nsd:nsd-catalog/nsd:nsd/nsd:', ''))})
78 self
.log
.warn(_("{0}, Did not process the following for "
79 "input param {1}: {2}").
80 format(self
, self
.inputs
, param
))
81 self
.log
.debug(_("{0}, inputs: {1}").format(self
, self
.inputs
[-1]))
83 def process_const_vnfd(cvnfd
):
84 # Get the matching VNFD
85 vnfd_id
= cvnfd
.pop(self
.VNFD_ID_REF
)
87 if vnfd
.type == self
.VNFD
and vnfd
.id == vnfd_id
:
88 self
.vnfds
[cvnfd
.pop(self
.MEM_VNF_INDEX
)] = vnfd
89 if self
.START_BY_DFLT
in cvnfd
:
90 vnfd
.props
[self
.START_BY_DFLT
] = \
91 cvnfd
.pop(self
.START_BY_DFLT
)
95 self
.log
.warn(_("{0}, Did not process the following for "
96 "constituent vnfd {1}: {2}").
97 format(self
, vnfd_id
, cvnfd
))
98 self
.log
.debug(_("{0}, VNFD: {1}").format(self
, self
.vnfds
))
100 def process_scale_grp(dic
):
102 self
.log
.debug(_("{0}, scale group: {1}").format(self
, dic
))
103 fields
= [self
.NAME
, self
.MIN_INST_COUNT
, self
.MAX_INST_COUNT
]
106 sg
[key
] = dic
.pop(key
)
109 for vnfd_memb
in dic
.pop(self
.VNFD_MEMBERS
):
110 vnfd_idx
= vnfd_memb
[self
.MEM_VNF_INDEX_REF
]
111 if vnfd_idx
in self
.vnfds
:
112 membs
[self
.vnfds
[vnfd_idx
].name
] = \
113 vnfd_memb
[self
.COUNT
]
114 sg
['vnfd_members'] = membs
117 if self
.SCALE_ACT
in dic
:
118 for sg_act
in dic
.pop(self
.SCALE_ACT
):
119 # Validate the primitive
120 prim
= sg_act
.pop(self
.NS_CONF_PRIM_REF
)
121 for cprim
in self
.conf_prims
:
122 if cprim
[self
.NAME
] == prim
:
123 trigs
[sg_act
.pop(self
.TRIGGER
)] = prim
126 err_msg
= (_("{0}, Did not find config-primitive {1}").
128 self
.log
.error(err_msg
)
129 raise ValidationError(message
=err_msg
)
130 sg
[self
.CONFIG_ACTIONS
] = trigs
133 self
.log
.warn(_("{0}, Did not process all fields for {1}").
135 self
.log
.debug(_("{0}, Scale group {1}").format(self
, sg
))
136 self
.scale_grps
.append(sg
)
138 def process_initial_config(dic
):
140 self
.log
.debug(_("{0}, initial config: {1}").format(self
, dic
))
141 for key
in [self
.NAME
, self
.SEQ
, self
.USER_DEF_SCRIPT
]:
143 icp
[key
] = dic
.pop(key
)
146 if self
.PARAM
in dic
:
147 for p
in dic
.pop(self
.PARAM
):
148 if (self
.NAME
in p
and
150 params
[p
[self
.NAME
]] = p
[self
.VALUE
]
152 # TODO (pjoseph): Need to add support to read the
153 # config file and get the value from that
154 self
.log
.warn(_("{0}, Got parameter without value: {1}").
157 icp
[self
.PARAM
] = params
160 self
.log
.warn(_("{0}, Did not process all fields for {1}").
162 self
.log
.debug(_("{0}, Initial config {1}").format(self
, icp
))
163 self
.initial_cfg
.append(icp
)
165 dic
= deepcopy(self
.yang
)
167 for key
in self
.REQUIRED_FIELDS
:
168 self
.props
[key
] = dic
.pop(key
)
170 self
.id = self
.props
[self
.ID
]
172 # Process constituent VNFDs
173 if self
.CONST_VNFD
in dic
:
174 for cvnfd
in dic
.pop(self
.CONST_VNFD
):
175 process_const_vnfd(cvnfd
)
179 for vld_dic
in dic
.pop(self
.VLD
):
180 vld
= YangVld(self
.log
, vld_dic
.pop(self
.NAME
),
182 vld
.process_vld(self
.vnfds
)
183 self
.vlds
.append(vld
)
185 # Process config primitives
186 if self
.CONF_PRIM
in dic
:
187 for cprim
in dic
.pop(self
.CONF_PRIM
):
188 conf_prim
= {self
.NAME
: cprim
.pop(self
.NAME
)}
189 if self
.USER_DEF_SCRIPT
in cprim
:
190 conf_prim
[self
.USER_DEF_SCRIPT
] = \
191 cprim
.pop(self
.USER_DEF_SCRIPT
)
192 self
.conf_prims
.append(conf_prim
)
194 err_msg
= (_("{0}, Only user defined script supported "
195 "in config-primitive for now {}: {}").
196 format(self
, conf_prim
, cprim
))
197 self
.log
.error(err_msg
)
198 raise ValidationError(message
=err_msg
)
200 # Process scaling group
201 if self
.SCALE_GRP
in dic
:
202 for sg_dic
in dic
.pop(self
.SCALE_GRP
):
203 process_scale_grp(sg_dic
)
205 # Process initial config primitives
206 if self
.INITIAL_CFG
in dic
:
207 for icp_dic
in dic
.pop(self
.INITIAL_CFG
):
208 process_initial_config(icp_dic
)
210 # Process the input params
211 if self
.INPUT_PARAM_XPATH
in dic
:
212 for param
in dic
.pop(self
.INPUT_PARAM_XPATH
):
213 process_input_param(param
)
215 self
.remove_ignored_fields(dic
)
217 self
.log
.warn(_("{0}, Did not process the following for "
219 format(self
, self
.props
, dic
))
220 self
.log
.debug(_("{0}, NSD: {1}").format(self
, self
.props
))
221 except Exception as e
:
222 err_msg
= _("Exception processing NSD {0} : {1}"). \
224 self
.log
.error(err_msg
)
225 self
.log
.exception(e
)
226 raise ValidationError(message
=err_msg
)
228 def generate_tosca_type(self
):
229 self
.log
.debug(_("{0} Generate tosa types").
233 tosca
[self
.DATA_TYPES
] = {}
234 tosca
[self
.NODE_TYPES
] = {}
236 for idx
, vnfd
in self
.vnfds
.items():
237 tosca
= vnfd
.generate_tosca_type(tosca
)
239 for vld
in self
.vlds
:
240 tosca
= vld
.generate_tosca_type(tosca
)
242 # Generate type for config primitives
243 if self
.GROUP_TYPES
not in tosca
:
244 tosca
[self
.GROUP_TYPES
] = {}
245 if self
.T_CONF_PRIM
not in tosca
[self
.GROUP_TYPES
]:
246 tosca
[self
.GROUP_TYPES
][self
.T_CONF_PRIM
] = {
247 self
.DERIVED_FROM
: 'tosca.policies.Root',
249 'primitive': self
.MAP
252 # Generate type for scaling group
253 if self
.POLICY_TYPES
not in tosca
:
254 tosca
[self
.POLICY_TYPES
] = {}
255 if self
.T_SCALE_GRP
not in tosca
[self
.POLICY_TYPES
]:
256 tosca
[self
.POLICY_TYPES
][self
.T_SCALE_GRP
] = {
257 self
.DERIVED_FROM
: 'tosca.policies.Root',
260 {self
.TYPE
: self
.STRING
},
262 {self
.TYPE
: self
.INTEGER
},
264 {self
.TYPE
: self
.INTEGER
},
266 {self
.TYPE
: self
.MAP
},
268 {self
.TYPE
: self
.MAP
}
271 if self
.T_INITIAL_CFG
not in tosca
[self
.POLICY_TYPES
]:
272 tosca
[self
.POLICY_TYPES
][self
.T_INITIAL_CFG
] = {
273 self
.DERIVED_FROM
: 'tosca.policies.Root',
276 {self
.TYPE
: self
.STRING
},
278 {self
.TYPE
: self
.INTEGER
},
279 self
.USER_DEF_SCRIPT
:
280 {self
.TYPE
: self
.STRING
},
282 {self
.TYPE
: self
.MAP
},
287 def generate_tosca_template(self
, tosca
):
288 self
.log
.debug(_("{0}, Generate tosca template").
291 # Add the standard entries
292 tosca
['tosca_definitions_version'] = \
293 'tosca_simple_profile_for_nfv_1_0_0'
294 tosca
[self
.DESC
] = self
.props
[self
.DESC
]
295 tosca
[self
.METADATA
] = {
297 self
.VENDOR
: self
.props
[self
.VENDOR
],
298 self
.VERSION
: self
.props
[self
.VERSION
],
301 tosca
[self
.TOPOLOGY_TMPL
] = {}
305 if self
.INPUTS
not in tosca
[self
.TOPOLOGY_TMPL
]:
306 tosca
[self
.TOPOLOGY_TMPL
][self
.INPUTS
] = {}
307 for inp
in self
.inputs
:
308 entry
= {inp
[self
.NAME
]: {self
.TYPE
: self
.STRING
,
310 'Translated from YANG'}}
311 tosca
[self
.TOPOLOGY_TMPL
][self
.INPUTS
] = entry
313 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
] = {}
315 # Add the VNFDs and VLDs
316 for idx
, vnfd
in self
.vnfds
.items():
317 vnfd
.generate_vnf_template(tosca
, idx
)
319 for vld
in self
.vlds
:
320 vld
.generate_tosca_template(tosca
)
322 # add the config primitives
323 if len(self
.conf_prims
):
324 if self
.GROUPS
not in tosca
[self
.TOPOLOGY_TMPL
]:
325 tosca
[self
.TOPOLOGY_TMPL
][self
.GROUPS
] = {}
328 self
.TYPE
: self
.T_CONF_PRIM
330 conf_prims
[self
.MEMBERS
] = [vnfd
.name
for vnfd
in
333 for confp
in self
.conf_prims
:
334 prims
[confp
[self
.NAME
]] = {
335 self
.USER_DEF_SCRIPT
: confp
[self
.USER_DEF_SCRIPT
]
337 conf_prims
[self
.PROPERTIES
] = {
338 self
.PRIMITIVES
: prims
341 tosca
[self
.TOPOLOGY_TMPL
][self
.GROUPS
][self
.CONF_PRIM
] = conf_prims
344 # Add the scale group
345 if len(self
.scale_grps
):
346 if self
.POLICIES
not in tosca
[self
.TOPOLOGY_TMPL
]:
347 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
] = []
349 for sg
in self
.scale_grps
:
351 self
.TYPE
: self
.T_SCALE_GRP
,
354 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
].append({
358 # Add initial configs
359 if len(self
.initial_cfg
):
360 if self
.POLICIES
not in tosca
[self
.TOPOLOGY_TMPL
]:
361 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
] = []
363 for icp
in self
.initial_cfg
:
365 self
.TYPE
: self
.T_INITIAL_CFG
,
368 tosca
[self
.TOPOLOGY_TMPL
][self
.POLICIES
].append({
369 self
.INITIAL_CFG
: icpt
374 def get_supporting_files(self
):
377 for vnfd
in self
.vnfds
.values():
378 f
= vnfd
.get_supporting_files()
382 # Get the config files for initial config
383 for icp
in self
.initial_cfg
:
384 if self
.USER_DEF_SCRIPT
in icp
:
385 script
= os
.path
.basename(icp
[self
.USER_DEF_SCRIPT
])
389 self
.DEST
: "{}/{}".format(self
.SCRIPT_DIR
, script
),
392 # TODO (pjoseph): Add support for config scripts,
395 self
.log
.debug(_("{0}, supporting files: {1}").format(self
, files
))