3e529671a103e732429d95d3f70f73539e578472
[osm/SO.git] / common / python / rift / mano / tosca_translator / rwmano / tosca / tosca_nfv_vnf.py
1 #
2 # Copyright 2016 RIFT.io Inc
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16
17
18 from rift.mano.tosca_translator.common.utils import _
19 from rift.mano.tosca_translator.common.utils import convert_keys_to_python
20 from rift.mano.tosca_translator.rwmano.syntax.mano_resource import ManoResource
21
22 from toscaparser.common.exception import ValidationError
23
24 try:
25 import gi
26 gi.require_version('RwVnfdYang', '1.0')
27
28 from gi.repository import RwVnfdYang
29 except ImportError:
30 pass
31 except ValueError:
32 pass
33
34
35 # Name used to dynamically load appropriate map class.
36 TARGET_CLASS_NAME = 'ToscaNfvVnf'
37
38
39 class ToscaNfvVnf(ManoResource):
40 '''Translate TOSCA node type tosca.nodes.nfv.vnf.'''
41
42 toscatype = 'tosca.nodes.nfv.VNF'
43
44 REQUIRED_PROPS = ['name', 'short-name', 'id', 'short-name', 'description',
45 'mgmt-interface']
46 OPTIONAL_PROPS = ['version', 'vendor', 'http-endpoint', 'monitoring-param',
47 'connection-point']
48 IGNORE_PROPS = ['port']
49 TOSCA_CAPS = ['mgmt_interface', 'http_endpoint', 'monitoring_param_0',
50 'monitoring_param_1', 'connection_point']
51
52 def __init__(self, log, nodetemplate, metadata=None):
53 super(ToscaNfvVnf, self).__init__(log,
54 nodetemplate,
55 type_="vnfd",
56 metadata=metadata)
57 self._const_vnfd = {}
58 self._vnf_config = {}
59 self._vdus = []
60
61 def map_tosca_name_to_mano(self, name):
62 new_name = super().map_tosca_name_to_mano(name)
63 if new_name.startswith('monitoring-param'):
64 new_name = 'monitoring-param'
65 if new_name == 'polling-interval':
66 new_name = 'polling_interval_secs'
67 return new_name
68
69 def handle_properties(self):
70 tosca_props = self.get_tosca_props()
71 self.log.debug(_("VNF {0} with tosca properties: {1}").
72 format(self.name, tosca_props))
73
74 def get_vnf_config(config):
75 vnf_config = {}
76 for key, value in config.items():
77 new_key = self.map_tosca_name_to_mano(key)
78 if isinstance(value, dict):
79 sub_config = {}
80 for subkey, subvalue in value.items():
81 sub_config[self.map_tosca_name_to_mano(subkey)] = \
82 subvalue
83 vnf_config[new_key] = sub_config
84 else:
85 vnf_config[new_key] = value
86
87 if vnf_config['config-type'] != 'script':
88 err_msg = _("{}, Only script config supported "
89 "for now: {}"). \
90 format(self, vnf_config['config-type'])
91 self.log.error(err_msg)
92 raise ValidationError(message=err_msg)
93
94 # Replace config-details with actual name (config-type)
95 if ('config-type' in vnf_config and
96 'config-details' in vnf_config):
97 vnf_config[vnf_config['config-type']] = \
98 vnf_config.pop('config-details')
99 vnf_config.pop('config-type')
100
101 # Update config-delay and confgig-priortiy to correct struct
102 vnf_config['config-attributes'] = {}
103 if 'config-delay' in vnf_config:
104 vnf_config['config-attributes']['config-delay'] = \
105 vnf_config.pop('config-delay')
106 else:
107 vnf_config['config-attributes']['config-delay'] = 0
108 if 'config-priority' in vnf_config:
109 vnf_config['config-attributes']['config-priority'] = \
110 vnf_config.pop('config-priority')
111 return vnf_config
112
113 vnf_props = {}
114 for key, value in tosca_props.items():
115 if key == 'id':
116 self._const_vnfd['member-vnf-index'] = int(value)
117 self._const_vnfd['vnfd-id-ref'] = self.id
118 elif key == 'vnf_configuration':
119 self._vnf_config = get_vnf_config(value)
120 else:
121 vnf_props[key] = value
122
123 if 'name' not in vnf_props:
124 vnf_props['name'] = self.name
125
126 if 'short-name' not in vnf_props:
127 vnf_props['short-name'] = self.name
128
129 if 'id' not in vnf_props:
130 vnf_props['id'] = self.id
131
132 if 'vendor' not in vnf_props:
133 vnf_props['vendor'] = self.vendor
134
135 if 'description' not in vnf_props:
136 vnf_props['description'] = self.description
137
138 if 'start_by_default' in vnf_props:
139 self._const_vnfd['start-by-default'] = \
140 vnf_props.pop('start_by_default')
141
142 self.log.debug(_("VNF {0} with constituent vnf: {1}").
143 format(self.name, self._const_vnfd))
144 self.log.debug(_("VNF {0} with properties: {1}").
145 format(self.name, vnf_props))
146 self.properties = vnf_props
147
148 def handle_capabilities(self):
149 tosca_caps = self.get_tosca_caps()
150 self.log.debug(_("VDU {0} tosca capabilites: {1}").
151 format(self.name, tosca_caps))
152
153 def get_props(props):
154 properties = {}
155 for key in props.keys():
156 value = props[key]
157 if isinstance(value, dict):
158 if 'get_property' in value:
159 val = self.get_property(value['get_property'])
160 value = val
161 properties[self.map_tosca_name_to_mano(key)] = value
162 return properties
163
164 for key, value in tosca_caps.items():
165 if key in ToscaNfvVnf.TOSCA_CAPS:
166 new_key = self.map_tosca_name_to_mano(key)
167 props = get_props(value)
168 if 'id' in props:
169 props['id'] = str(props['id'])
170 if 'protocol' in props:
171 props.pop('protocol')
172
173 # There is only one instance of mgmt interface, but others
174 # are a list
175 if key == 'mgmt_interface':
176 self.properties[new_key] = props
177 elif key == 'http_endpoint':
178 if new_key not in self.properties:
179 self.properties[new_key] = []
180 self.properties[new_key].append(props)
181 else:
182 if new_key not in self.properties:
183 self.properties[new_key] = []
184 self.properties[new_key].append(props)
185
186 self.log.debug(_("VDU {0} properties: {1}").
187 format(self.name, self.properties))
188
189 def handle_requirements(self, nodes):
190 tosca_reqs = self.get_tosca_reqs()
191 self.log.debug("VNF {0} requirements: {1}".
192 format(self.name, tosca_reqs))
193
194 try:
195 for req in tosca_reqs:
196 if 'vdus' in req:
197 target = req['vdus']['target']
198 node = self.get_node_with_name(target, nodes)
199 if node:
200 self._vdus.append(node)
201 node._vnf = self
202 # Add the VDU id to mgmt-intf
203 if 'mgmt-interface' in self.properties:
204 self.properties['mgmt-interface']['vdu-id'] = \
205 node.id
206 if 'vdu' in self.properties['mgmt-interface']:
207 # Older yang
208 self.properties['mgmt-interface'].pop('vdu')
209 else:
210 err_msg = _("VNF {0}, VDU {1} specified not found"). \
211 format(self.name, target)
212 self.log.error(err_msg)
213 raise ValidationError(message=err_msg)
214
215 except Exception as e:
216 err_msg = _("Exception getting VDUs for VNF {0}: {1}"). \
217 format(self.name, e)
218 self.log.error(err_msg)
219 raise e
220
221 self.log.debug(_("VNF {0} properties: {1}").
222 format(self.name, self.properties))
223
224 def generate_yang_model_gi(self, nsd, vnfds):
225 vnfd_cat = RwVnfdYang.YangData_Vnfd_VnfdCatalog()
226 vnfd = vnfd_cat.vnfd.add()
227 props = convert_keys_to_python(self.properties)
228 try:
229 vnfd.from_dict(props)
230 except Exception as e:
231 err_msg = _("{0} Exception updating vnfd from dict {1}: {2}"). \
232 format(self, props, e)
233 self.log.error(err_msg)
234 raise e
235 vnfds.append(vnfd_cat)
236
237 # Update the VDU properties
238 for vdu in self._vdus:
239 vdu.generate_yang_submodel_gi(vnfd)
240
241 # Update constituent vnfd in nsd
242 try:
243 props = convert_keys_to_python(self._const_vnfd)
244 nsd.constituent_vnfd.add().from_dict(props)
245 except Exception as e:
246 err_msg = _("{0} Exception constituent vnfd from dict {1}: {2}"). \
247 format(self, props, e)
248 self.log.error(err_msg)
249 raise e
250
251 # Update the vnf configuration info in mgmt_interface
252 props = convert_keys_to_python(self._vnf_config)
253 try:
254 vnfd.vnf_configuration.from_dict(props)
255 except Exception as e:
256 err_msg = _("{0} Exception vnfd mgmt intf from dict {1}: {2}"). \
257 format(self, props, e)
258 self.log.error(err_msg)
259 raise e
260
261 def generate_yang_model(self, nsd, vnfds, use_gi=False):
262 """Generate yang model for the node"""
263 self.log.debug(_("Generate YANG model for {0}").
264 format(self))
265
266 for key in ToscaNfvVnf.IGNORE_PROPS:
267 if key in self.properties:
268 self.properties.pop(key)
269
270 if use_gi:
271 return self.generate_yang_model_gi(nsd, vnfds)
272
273 vnfd = {}
274 vnfd.update(self.properties)
275 # Update vnf configuration on mgmt interface
276 vnfd['mgmt-interface']['vnf-configuration'] = self._vnf_config
277
278 # Update the VDU properties
279 vnfd['vdu'] = []
280 for vdu in self._vdus:
281 vnfd['vdu'].append(vdu.generate_yang_submodel())
282
283 vnfds.append(vnfd)
284
285 # Update constituent vnfd in nsd
286 if 'constituent-vnfd' not in nsd:
287 nsd['constituent-vnfd'] = []
288 nsd['constituent-vnfd'].append(self._const_vnfd)
289
290 def get_member_vnf_index(self):
291 return self._const_vnfd['member-vnf-index']
292
293 def get_supporting_files(self, files, desc_id=None):
294 files[self.id] = []
295 for vdu in self._vdus:
296 if vdu.image:
297 files[self.id].append({
298 'type': 'image',
299 'name': vdu.image,
300 },)
301 if vdu.cloud_init:
302 files[self.id].append({
303 'type': 'cloud_init',
304 'name': vdu.cloud_init,
305 },)