2 # Licensed under the Apache License, Version 2.0 (the "License"); you may
3 # not use this file except in compliance with the License. You may obtain
4 # a copy of the License at
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11 # License for the specific language governing permissions and limitations
14 # Copyright 2016 RIFT.io Inc
19 from rift
.mano
.tosca_translator
.common
.utils
import _
20 from rift
.mano
.tosca_translator
.common
.utils
import ChecksumUtils
21 from rift
.mano
.tosca_translator
.common
.utils
import convert_keys_to_python
22 from rift
.mano
.tosca_translator
.rwmano
.syntax
.mano_resource
import ManoResource
24 from toscaparser
.common
.exception
import ValidationError
25 from toscaparser
.elements
.scalarunit
import ScalarUnit_Size
27 # Name used to dynamically load appropriate map class.
28 TARGET_CLASS_NAME
= 'ToscaCompute'
31 class ToscaCompute(ManoResource
):
32 '''Translate TOSCA node type RIFT.io VDUs.'''
34 REQUIRED_PROPS
= ['name', 'id', 'image', 'count', 'vm-flavor']
42 toscatype
= 'tosca.nodes.nfv.VDU'
44 VALUE_TYPE_CONVERSION_MAP
= {
50 def __init__(self
, log
, nodetemplate
, metadata
=None):
51 super(ToscaCompute
, self
).__init
__(log
,
55 # List with associated port resources with this server
56 self
.assoc_port_resources
= []
57 self
._image
= None # Image to bring up the VDU
58 self
._image
_cksum
= None
59 self
._cloud
_init
= None # Cloud init file
63 self
._monitor
_param
= []
64 self
._mgmt
_interface
= {}
72 return self
._cloud
_init
81 err_msg
= (_('VDU {0} already has a VNF {1} associated').
82 format(self
, self
._vnf
))
83 self
.log
.error(err_msg
)
84 raise ValidationError(message
=err_msg
)
87 def handle_properties(self
):
88 tosca_props
= self
.get_tosca_props()
89 self
.log
.debug(_("VDU {0} tosca properties: {1}").
90 format(self
.name
, tosca_props
))
92 for key
, value
in tosca_props
.items():
93 if key
== 'cloud_init':
94 vdu_props
['cloud-init'] = value
95 elif key
== 'cloud-init-file':
96 self
._cloud
_init
= "../cloud_init/{}".format(value
)
98 vdu_props
[key
] = value
100 if 'name' not in vdu_props
:
101 vdu_props
['name'] = self
.name
103 if 'id' not in vdu_props
:
104 vdu_props
['id'] = self
.id
106 if 'count' not in vdu_props
:
107 vdu_props
['count'] = 1
109 self
.log
.debug(_("VDU {0} properties: {1}").
110 format(self
.name
, vdu_props
))
111 self
.properties
= vdu_props
113 def handle_capabilities(self
):
115 def get_mgmt_interface(specs
):
117 mgmt_intfce
['vdu-id'] = self
.id
118 if 'dashboard_params' in specs
:
119 mgmt_intfce
['dashboard-params'] = {'path':specs
['dashboard_params']['path'], 'port':specs
['dashboard_params']['port']}
121 mgmt_intfce
['port'] = specs
['port']
124 def get_monitor_param(specs
, monitor_id
):
126 monitor_param
['id'] = monitor_id
128 monitor_param
['name'] = specs
['name']
129 if 'json_query_method' in specs
:
130 monitor_param
['json_query_method'] = specs
['json_query_method'].upper()
131 if 'description' in specs
:
132 monitor_param
['description'] = specs
['description']
133 if 'url_path' in specs
:
134 monitor_param
['http-endpoint-ref'] = specs
['url_path']
135 if 'ui_data' in specs
:
136 if 'widget_type' in specs
['ui_data']:
137 monitor_param
['widget-type'] = specs
['ui_data']['widget_type'].upper()
138 if 'units' in specs
['ui_data']:
139 monitor_param
['units'] = specs
['ui_data']['units']
140 if 'group_tag' in specs
['ui_data']:
141 monitor_param
['group_tag'] = specs
['ui_data']['group_tag']
142 if 'constraints' in specs
:
143 if 'value_type' in specs
['constraints']:
144 monitor_param
['value-type'] = ToscaCompute
.VALUE_TYPE_CONVERSION_MAP
[specs
['constraints']['value_type']]
148 def get_vm_flavor(specs
):
150 if 'num_cpus' in specs
:
151 vm_flavor
['vcpu-count'] = specs
['num_cpus']
153 vm_flavor
['vcpu-count'] = 1
155 if 'mem_size' in specs
:
156 vm_flavor
['memory-mb'] = (ScalarUnit_Size(specs
['mem_size']).
157 get_num_from_scalar_unit('MB'))
159 vm_flavor
['memory-mb'] = 512
161 if 'disk_size' in specs
:
162 vm_flavor
['storage-gb'] = (ScalarUnit_Size(specs
['disk_size']).
163 get_num_from_scalar_unit('GB'))
165 vm_flavor
['storage-gb'] = 4
169 def get_host_epa(specs
):
171 if 'cpu_model' in specs
:
172 host_epa
["cpu-model"] = specs
['cpu_model'].upper()
173 if 'cpu_arch' in specs
:
174 host_epa
["cpu-arch"] = specs
['cpu_arch'].upper()
175 if 'cpu_vendor' in specs
:
176 host_epa
["cpu-vendor"] = specs
['cpu_vendor'].upper()
177 if 'cpu_socket_count' in specs
:
178 host_epa
["cpu-socket-count"] = specs
['cpu_socket_count']
179 if 'cpu_core_count' in specs
:
180 host_epa
["cpu-core-count"] = specs
['cpu_core_count']
181 if 'cpu_core_thread_count' in specs
:
182 host_epa
["cpu-core-thread-count"] = specs
['cpu_core_thread_count']
183 if 'om_cpu_model_string' in specs
:
184 host_epa
["om-cpu-model-string"] = specs
['om_cpu_model_string']
185 if 'cpu_feature' in specs
:
186 cpu_feature_prop
= []
187 for spec
in specs
['cpu_feature']:
188 cpu_feature_prop
.append({'feature':spec
.upper()})
189 host_epa
['cpu-feature'] = cpu_feature_prop
190 if 'om_cpu_feature' in specs
:
191 cpu_feature_prop
= []
192 for spec
in specs
['om_cpu_feature']:
193 cpu_feature_prop
.append({'feature':spec
})
194 host_epa
['om-cpu-feature'] = cpu_feature_prop
197 def get_vswitch_epa(specs
):
199 if 'ovs_acceleration' in specs
:
200 vswitch_epa
['ovs-acceleration'] = specs
['ovs_acceleration'].upper()
201 if 'ovs_offload' in specs
:
202 vswitch_epa
['ovs-offload'] = specs
['ovs_offload'].upper()
205 def get_hypervisor_epa(specs
):
208 hypervisor_epa
['type'] = specs
['type'].upper()
209 if 'version' in specs
:
210 hypervisor_epa
['version'] = specs
['version']
212 return hypervisor_epa
214 def get_guest_epa(specs
):
216 guest_epa
['numa-node-policy'] = {}
217 guest_epa
['numa-node-policy']['node'] = []
218 if 'mem_policy' in specs
:
219 guest_epa
['numa-node-policy']['mem-policy'] = specs
['mem_policy'].upper()
220 if 'node_cnt' in specs
:
221 guest_epa
['numa-node-policy']['node-cnt'] = specs
['node_cnt']
223 for node
in specs
['node']:
226 node_prop
['id'] = node
['id']
227 if 'mem_size' in node
:
228 if 'MiB' in node
['mem_size'] or 'MB' in node
['mem_size']:
229 node_prop
['memory-mb'] = int(node
['mem_size'].replace('MB',''))
231 err_msg
= "Specify mem_size of NUMA extension should be in MB"
232 raise ValidationError(message
=err_msg
)
233 if 'om_numa_type' in node
:
234 numa_type
= node
['om_numa_type']
235 if 'paired-threads' == numa_type
:
236 node_prop
['paired_threads'] = {}
237 node_prop
['paired_threads']['num_paired_threads'] = node
['paired_threads']['num_paired_threads']
238 elif 'threads' == numa_type
:
239 if 'num_threads' in node
:
240 node_prop
['num_threads'] = node
['num_threads']
241 elif 'cores' == numa_type
:
242 if 'num_cores' in node
:
243 node_prop
['num_cores'] = node
['num_cores']
245 err_msg
= "om_numa_type should be among cores, paired-threads or threads"
246 raise ValidationError(message
=err_msg
)
247 guest_epa
['numa-node-policy']['node'].append(node_prop
)
250 tosca_caps
= self
.get_tosca_caps()
251 self
.log
.debug(_("VDU {0} tosca capabilites: {1}").
252 format(self
.name
, tosca_caps
))
253 if 'nfv_compute' in tosca_caps
:
254 self
.properties
['vm-flavor'] = get_vm_flavor(tosca_caps
['nfv_compute'])
255 self
.log
.debug(_("VDU {0} properties: {1}").
256 format(self
.name
, self
.properties
))
257 if 'host_epa' in tosca_caps
:
258 self
.properties
['host-epa'] = get_host_epa(tosca_caps
['host_epa'])
259 if 'hypervisor_epa' in tosca_caps
:
260 self
.properties
['hypervisor-epa'] = get_hypervisor_epa(tosca_caps
['hypervisor_epa'])
261 if 'vswitch_epa' in tosca_caps
:
262 self
.properties
['vswitch-epa'] = get_vswitch_epa(tosca_caps
['vswitch_epa'])
263 if 'numa_extension' in tosca_caps
:
264 self
.properties
['guest-epa'] = get_guest_epa(tosca_caps
['numa_extension'])
265 if 'monitoring_param' in tosca_caps
:
266 self
._monitor
_param
.append(get_monitor_param(tosca_caps
['monitoring_param'], '1'))
267 if 'monitoring_param_1' in tosca_caps
:
268 self
._monitor
_param
.append(get_monitor_param(tosca_caps
['monitoring_param_1'], '2'))
269 if 'mgmt_interface' in tosca_caps
:
270 self
._mgmt
_interface
= get_mgmt_interface(tosca_caps
['mgmt_interface'])
272 def handle_artifacts(self
):
273 if self
.artifacts
is None:
275 self
.log
.debug(_("VDU {0} tosca artifacts: {1}").
276 format(self
.name
, self
.artifacts
))
278 for key
in self
.artifacts
:
279 props
= self
.artifacts
[key
]
280 if isinstance(props
, dict):
282 for name
, value
in props
.items():
283 if name
== 'type' and value
== 'tosca.artifacts.Deployment.Image.riftio.QCOW2':
284 prefix
, type_
= value
.rsplit('.', 1)
286 details
['type'] = 'qcow2'
287 self
.properties
['image'] = os
.path
.basename(props
['file'])
288 elif name
== 'type' and value
== 'tosca.artifacts.Deployment.riftio.cloud_init_file':
289 details
['cloud_init_file'] = os
.path
.basename(props
['file'])
290 self
.properties
['cloud_init_file'] = os
.path
.basename(props
['file'])
292 details
['file'] = value
293 elif name
== 'image_checksum':
294 self
.properties
['image_checksum'] = value
296 self
.log
.warn(_("VDU {0}, unsuported attribute {1}").
297 format(self
.name
, name
))
301 arts
[key
] = self
.artifacts
[key
]
303 self
.log
.debug(_("VDU {0} artifacts: {1}").
304 format(self
.name
, arts
))
305 self
.artifacts
= arts
307 def handle_interfaces(self
):
308 # Currently, we support only create operation
309 operations_deploy_sequence
= ['create']
311 operations
= ManoResource
._get
_all
_operations
(self
.nodetemplate
)
313 # use the current ManoResource for the first operation in this order
314 # Currently we only support image in create operation
315 for operation
in operations
.values():
316 if operation
.name
in operations_deploy_sequence
:
317 self
.operations
[operation
.name
] = None
319 self
.operations
[operation
.name
] = operation
.implementation
320 for name
, details
in self
.artifacts
.items():
321 if name
== operation
.implementation
:
322 self
._image
= details
['file']
323 except KeyError as e
:
324 self
.log
.exception(e
)
327 def update_image_checksum(self
, in_file
):
328 # Create image checksum
329 # in_file is the TOSCA yaml file location
330 if self
._image
is None:
332 self
.log
.debug("Update image: {}".format(in_file
))
333 if os
.path
.exists(in_file
):
334 in_dir
= os
.path
.dirname(in_file
)
335 img_dir
= os
.path
.dirname(self
._image
)
336 abs_dir
= os
.path
.normpath(
337 os
.path
.join(in_dir
, img_dir
))
338 self
.log
.debug("Abs path: {}".format(abs_dir
))
339 if os
.path
.isdir(abs_dir
):
340 img_path
= os
.path
.join(abs_dir
,
341 os
.path
.basename(self
._image
))
342 self
.log
.debug(_("Image path: {0}").
344 if os
.path
.exists(img_path
):
345 # TODO (pjoseph): To be fixed when we can retrieve
346 # the VNF image in Launchpad.
347 # Check if the file is not size 0
348 # else it is a dummy file and to be ignored
349 if os
.path
.getsize(img_path
) != 0:
350 self
._image
_cksum
= ChecksumUtils
.get_md5(img_path
,
353 def get_mano_attribute(self
, attribute
, args
):
355 # Convert from a TOSCA attribute for a nodetemplate to a MANO
356 # attribute for the matching resource. Unless there is additional
357 # runtime support, this should be a one to one mapping.
359 # Note: We treat private and public IP addresses equally, but
360 # this will change in the future when TOSCA starts to support
361 # multiple private/public IP addresses.
362 self
.log
.debug(_('Converting TOSCA attribute for a nodetemplate to a MANO \
364 if attribute
== 'private_address' or \
365 attribute
== 'public_address':
366 attr
['get_attr'] = [self
.name
, 'networks', 'private', 0]
370 def _update_properties_for_model(self
):
372 self
.properties
['image'] = os
.path
.basename(self
._image
)
373 if self
._image
_cksum
:
374 self
.properties
['image-checksum'] = self
._image
_cksum
376 for key
in ToscaCompute
.IGNORE_PROPS
:
377 if key
in self
.properties
:
378 self
.properties
.pop(key
)
380 def generate_yang_submodel_gi(self
, vnfd
):
383 self
._update
_properties
_for
_model
()
384 props
= convert_keys_to_python(self
.properties
)
386 for monitor_param
in self
._monitor
_param
:
387 monitor_props
= convert_keys_to_python(monitor_param
)
388 vnfd
.monitoring_param
.add().from_dict(monitor_props
)
390 if len(self
._mgmt
_interface
) > 0:
391 vnfd
.mgmt_interface
.from_dict(convert_keys_to_python(self
._mgmt
_interface
))
392 vnfd
.vdu
.add().from_dict(props
)
393 except Exception as e
:
394 err_msg
= _("{0} Exception vdu from dict {1}: {2}"). \
395 format(self
, props
, e
)
396 self
.log
.error(err_msg
)
399 def generate_yang_submodel(self
):
400 """Generate yang model for the VDU"""
401 self
.log
.debug(_("Generate YANG model for {0}").
404 self
._update
_properties
_for
_model
()
406 vdu
= self
.properties