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
= {
54 'size_2MB': 'SIZE_2MB',
55 'size_1GB': 'SIZE_1GB',
56 'prefer_huge': 'PREFER_LARGE'
60 def __init__(self
, log
, nodetemplate
, metadata
=None):
61 super(ToscaCompute
, self
).__init
__(log
,
65 # List with associated port resources with this server
66 self
.assoc_port_resources
= []
67 self
._image
= None # Image to bring up the VDU
68 self
._image
_cksum
= None
69 self
._cloud
_init
= None # Cloud init file
73 self
._monitor
_param
= []
74 self
._mgmt
_interface
= {}
75 self
._http
_endpoint
= None
83 return self
._cloud
_init
92 err_msg
= (_('VDU {0} already has a VNF {1} associated').
93 format(self
, self
._vnf
))
94 self
.log
.error(err_msg
)
95 raise ValidationError(message
=err_msg
)
98 def handle_properties(self
):
99 tosca_props
= self
.get_tosca_props()
100 self
.log
.debug(_("VDU {0} tosca properties: {1}").
101 format(self
.name
, tosca_props
))
103 for key
, value
in tosca_props
.items():
104 vdu_props
[key
] = value
106 if 'name' not in vdu_props
:
107 vdu_props
['name'] = self
.name
109 if 'id' not in vdu_props
:
110 vdu_props
['id'] = self
.id
112 if 'count' not in vdu_props
:
113 vdu_props
['count'] = 1
115 self
.log
.debug(_("VDU {0} properties: {1}").
116 format(self
.name
, vdu_props
))
117 self
.properties
= vdu_props
119 def handle_capabilities(self
):
121 def get_mgmt_interface(specs
):
123 mgmt_intfce
['vdu-id'] = self
.id
124 if 'dashboard_params' in specs
:
125 mgmt_intfce
['dashboard-params'] = {'path':specs
['dashboard_params']['path'], 'port':specs
['dashboard_params']['port']}
127 mgmt_intfce
['port'] = specs
['port']
130 def get_monitor_param(specs
, monitor_id
):
132 monitor_param
['id'] = monitor_id
134 monitor_param
['name'] = specs
['name']
135 if 'json_query_method' in specs
:
136 monitor_param
['json_query_method'] = specs
['json_query_method'].upper()
137 if 'description' in specs
:
138 monitor_param
['description'] = specs
['description']
139 if 'url_path' in specs
:
140 monitor_param
['http-endpoint-ref'] = specs
['url_path']
141 if 'ui_data' in specs
:
142 if 'widget_type' in specs
['ui_data']:
143 monitor_param
['widget-type'] = specs
['ui_data']['widget_type'].upper()
144 if 'units' in specs
['ui_data']:
145 monitor_param
['units'] = specs
['ui_data']['units']
146 if 'group_tag' in specs
['ui_data']:
147 monitor_param
['group_tag'] = specs
['ui_data']['group_tag']
148 if 'constraints' in specs
:
149 if 'value_type' in specs
['constraints']:
150 monitor_param
['value-type'] = ToscaCompute
.VALUE_TYPE_CONVERSION_MAP
[specs
['constraints']['value_type']]
154 def get_vm_flavor(specs
):
156 if 'num_cpus' in specs
:
157 vm_flavor
['vcpu-count'] = specs
['num_cpus']
159 vm_flavor
['vcpu-count'] = 1
161 if 'mem_size' in specs
:
162 vm_flavor
['memory-mb'] = (ScalarUnit_Size(specs
['mem_size']).
163 get_num_from_scalar_unit('MB'))
165 vm_flavor
['memory-mb'] = 512
167 if 'disk_size' in specs
:
168 vm_flavor
['storage-gb'] = (ScalarUnit_Size(specs
['disk_size']).
169 get_num_from_scalar_unit('GB'))
171 vm_flavor
['storage-gb'] = 4
175 def get_host_epa(specs
):
177 if 'cpu_model' in specs
:
178 host_epa
["cpu-model"] = specs
['cpu_model'].upper()
179 if 'cpu_arch' in specs
:
180 host_epa
["cpu-arch"] = specs
['cpu_arch'].upper()
181 if 'cpu_vendor' in specs
:
182 host_epa
["cpu-vendor"] = specs
['cpu_vendor'].upper()
183 if 'cpu_socket_count' in specs
:
184 host_epa
["cpu-socket-count"] = specs
['cpu_socket_count']
185 if 'cpu_core_count' in specs
:
186 host_epa
["cpu-core-count"] = specs
['cpu_core_count']
187 if 'cpu_core_thread_count' in specs
:
188 host_epa
["cpu-core-thread-count"] = specs
['cpu_core_thread_count']
189 if 'om_cpu_model_string' in specs
:
190 host_epa
["om-cpu-model-string"] = specs
['om_cpu_model_string']
191 if 'cpu_feature' in specs
:
192 cpu_feature_prop
= []
193 for spec
in specs
['cpu_feature']:
194 cpu_feature_prop
.append({'feature':spec
.upper()})
195 host_epa
['cpu-feature'] = cpu_feature_prop
196 if 'om_cpu_feature' in specs
:
197 cpu_feature_prop
= []
198 for spec
in specs
['om_cpu_feature']:
199 cpu_feature_prop
.append({'feature':spec
})
200 host_epa
['om-cpu-feature'] = cpu_feature_prop
203 def get_vswitch_epa(specs
):
205 if 'ovs_acceleration' in specs
:
206 vswitch_epa
['ovs-acceleration'] = specs
['ovs_acceleration'].upper()
207 if 'ovs_offload' in specs
:
208 vswitch_epa
['ovs-offload'] = specs
['ovs_offload'].upper()
211 def get_hypervisor_epa(specs
):
214 hypervisor_epa
['type'] = specs
['type'].upper()
215 if 'version' in specs
:
216 hypervisor_epa
['version'] = str(specs
['version'])
218 return hypervisor_epa
220 def get_guest_epa(specs
, nfv_comput_specs
):
222 guest_epa
['numa-node-policy'] = {}
223 guest_epa
['numa-node-policy']['node'] = []
224 if 'mem_policy' in specs
:
225 guest_epa
['numa-node-policy']['mem-policy'] = specs
['mem_policy'].upper()
226 if 'node_cnt' in specs
:
227 guest_epa
['numa-node-policy']['node-cnt'] = specs
['node_cnt']
229 for node
in specs
['node']:
232 node_prop
['id'] = node
['id']
233 if 'mem_size' in node
:
234 if 'MiB' in node
['mem_size'] or 'MB' in node
['mem_size']:
235 node_prop
['memory-mb'] = int(node
['mem_size'].replace('MB',''))
237 err_msg
= "Specify mem_size of NUMA extension should be in MB"
238 raise ValidationError(message
=err_msg
)
241 for vcpu
in node
['vcpus']:
242 vcpu_lis
.append({'id': vcpu
})
243 node_prop
['vcpu'] = vcpu_lis
244 if 'om_numa_type' in node
:
245 numa_type
= node
['om_numa_type']
246 if 'paired-threads' == numa_type
:
247 node_prop
['paired_threads'] = {}
248 node_prop
['paired_threads']['num_paired_threads'] = node
['paired_threads']['num_paired_threads']
249 elif 'threads' == numa_type
:
250 if 'num_threads' in node
:
251 node_prop
['num_threads'] = node
['num_threads']
252 elif 'cores' == numa_type
:
253 if 'num_cores' in node
:
254 node_prop
['num_cores'] = node
['num_cores']
256 err_msg
= "om_numa_type should be among cores, paired-threads or threads"
257 raise ValidationError(message
=err_msg
)
258 guest_epa
['numa-node-policy']['node'].append(node_prop
)
260 if 'mem_page_size' in nfv_comput_specs
:
261 guest_epa
['mempage-size'] = self
.TOSCA_MEM_SIZE
[nfv_comput_specs
['mem_page_size']]
262 if 'cpu_allocation' in nfv_comput_specs
:
263 if 'cpu_affinity' in nfv_comput_specs
['cpu_allocation']:
264 guest_epa
['cpu-pinning-policy'] = nfv_comput_specs
['cpu_allocation']['cpu_affinity'].upper()
265 guest_epa
['trusted-execution'] = False
266 if 'thread_allocation' in nfv_comput_specs
['cpu_allocation']:
267 guest_epa
['cpu-thread-pinning-policy'] = nfv_comput_specs
['cpu_allocation']['thread_allocation'].upper()
271 tosca_caps
= self
.get_tosca_caps()
272 self
.log
.debug(_("VDU {0} tosca capabilites: {1}").
273 format(self
.name
, tosca_caps
))
274 if 'nfv_compute' in tosca_caps
:
275 self
.properties
['vm-flavor'] = get_vm_flavor(tosca_caps
['nfv_compute'])
276 self
.log
.debug(_("VDU {0} properties: {1}").
277 format(self
.name
, self
.properties
))
278 if 'host_epa' in tosca_caps
:
279 self
.properties
['host-epa'] = get_host_epa(tosca_caps
['host_epa'])
280 if 'hypervisor_epa' in tosca_caps
:
281 self
.properties
['hypervisor-epa'] = get_hypervisor_epa(tosca_caps
['hypervisor_epa'])
282 if 'vswitch_epa' in tosca_caps
:
283 self
.properties
['vswitch-epa'] = get_vswitch_epa(tosca_caps
['vswitch_epa'])
284 if 'numa_extension' in tosca_caps
:
285 self
.properties
['guest-epa'] = get_guest_epa(tosca_caps
['numa_extension'], tosca_caps
['nfv_compute'])
286 if 'monitoring_param' in tosca_caps
:
287 self
._monitor
_param
.append(get_monitor_param(tosca_caps
['monitoring_param'], '1'))
288 if 'mgmt_interface' in tosca_caps
:
289 self
._mgmt
_interface
= get_mgmt_interface(tosca_caps
['mgmt_interface'])
290 if len(self
._mgmt
_interface
) > 0:
292 if 'dashboard-params' in self
._mgmt
_interface
:
293 if 'path' in self
._mgmt
_interface
['dashboard-params']:
294 prop
['path'] = self
._mgmt
_interface
['dashboard-params']['path']
295 if 'port' in self
._mgmt
_interface
['dashboard-params']:
296 prop
['port'] = self
._mgmt
_interface
['dashboard-params']['port']
297 self
._http
_endpoint
= prop
300 monitoring_param_name
= 'monitoring_param_1'
302 if monitoring_param_name
in tosca_caps
:
303 self
._monitor
_param
.append(get_monitor_param(tosca_caps
[monitoring_param_name
], str(mon_idx
)))
305 monitoring_param_name
= 'monitoring_param_{}'.format(mon_idx
)
309 # THis is a quick hack to remove monitor params without name
310 for mon_param
in list(self
._monitor
_param
):
311 if 'name' not in mon_param
:
312 self
._monitor
_param
.remove(mon_param
)
314 def handle_artifacts(self
):
315 if self
.artifacts
is None:
317 self
.log
.debug(_("VDU {0} tosca artifacts: {1}").
318 format(self
.name
, self
.artifacts
))
320 for key
in self
.artifacts
:
321 props
= self
.artifacts
[key
]
322 if isinstance(props
, dict):
324 for name
, value
in props
.items():
325 if name
== 'type' and value
== 'tosca.artifacts.Deployment.Image.riftio.QCOW2':
326 prefix
, type_
= value
.rsplit('.', 1)
328 details
['type'] = 'qcow2'
329 self
._image
= props
['file']
330 self
.properties
['image'] = os
.path
.basename(props
['file'])
331 elif name
== 'type' and value
== 'tosca.artifacts.Deployment.riftio.cloud_init_file':
332 details
['cloud_init_file'] = os
.path
.basename(props
['file'])
333 self
._cloud
_init
= props
['file']
334 self
.properties
['cloud_init_file'] = os
.path
.basename(props
['file'])
336 details
['file'] = value
337 elif name
== 'image_checksum':
338 self
.properties
['image_checksum'] = value
340 self
.log
.warn(_("VDU {0}, unsuported attribute {1}").
341 format(self
.name
, name
))
345 arts
[key
] = self
.artifacts
[key
]
347 self
.log
.debug(_("VDU {0} artifacts: {1}").
348 format(self
.name
, arts
))
349 self
.artifacts
= arts
351 def handle_interfaces(self
):
352 # Currently, we support the following:
353 operations_deploy_sequence
= ['create', 'configure']
355 operations
= ManoResource
._get
_all
_operations
(self
.nodetemplate
)
357 # use the current ManoResource for the first operation in this order
358 # Currently we only support image in create operation
359 for operation
in operations
.values():
360 if operation
.name
in operations_deploy_sequence
:
361 self
.operations
[operation
.name
] = None
363 self
.operations
[operation
.name
] = operation
.implementation
364 for name
, details
in self
.artifacts
.items():
365 if name
== operation
.implementation
:
366 if operation
.name
== 'create':
367 self
._image
= details
['file']
368 elif operation
.name
== 'configure':
369 self
._cloud
_init
= details
['file']
371 except KeyError as e
:
372 self
.log
.exception(e
)
375 def update_image_checksum(self
, in_file
):
377 # Create image checksum
378 # in_file is the TOSCA yaml file location
379 if self
._image
is None:
381 self
.log
.debug("Update image: {}".format(in_file
))
382 if os
.path
.exists(in_file
):
383 in_dir
= os
.path
.dirname(in_file
)
384 img_dir
= os
.path
.dirname(self
._image
)
385 abs_dir
= os
.path
.normpath(
386 os
.path
.join(in_dir
, img_dir
))
387 self
.log
.debug("Abs path: {}".format(abs_dir
))
388 if os
.path
.isdir(abs_dir
):
389 img_path
= os
.path
.join(abs_dir
,
390 os
.path
.basename(self
._image
))
391 self
.log
.debug(_("Image path: {0}").
393 if os
.path
.exists(img_path
):
394 # TODO (pjoseph): To be fixed when we can retrieve
395 # the VNF image in Launchpad.
396 # Check if the file is not size 0
397 # else it is a dummy file and to be ignored
398 if os
.path
.getsize(img_path
) != 0:
399 self
._image
_cksum
= ChecksumUtils
.get_md5(img_path
,
402 def get_mano_attribute(self
, attribute
, args
):
404 # Convert from a TOSCA attribute for a nodetemplate to a MANO
405 # attribute for the matching resource. Unless there is additional
406 # runtime support, this should be a one to one mapping.
408 # Note: We treat private and public IP addresses equally, but
409 # this will change in the future when TOSCA starts to support
410 # multiple private/public IP addresses.
411 self
.log
.debug(_('Converting TOSCA attribute for a nodetemplate to a MANO \
413 if attribute
== 'private_address' or \
414 attribute
== 'public_address':
415 attr
['get_attr'] = [self
.name
, 'networks', 'private', 0]
419 def _update_properties_for_model(self
):
421 self
.properties
['image'] = os
.path
.basename(self
._image
)
422 if self
._image
_cksum
:
423 self
.properties
['image-checksum'] = self
._image
_cksum
426 self
.properties
['cloud-init-file'] = os
.path
.basename(self
._cloud
_init
)
428 for key
in ToscaCompute
.IGNORE_PROPS
:
429 if key
in self
.properties
:
430 self
.properties
.pop(key
)
432 def generate_yang_submodel_gi(self
, vnfd
):
435 self
._update
_properties
_for
_model
()
436 props
= convert_keys_to_python(self
.properties
)
438 for monitor_param
in self
._monitor
_param
:
439 monitor_props
= convert_keys_to_python(monitor_param
)
440 vnfd
.monitoring_param
.add().from_dict(monitor_props
)
442 if len(self
._mgmt
_interface
) > 0:
443 vnfd
.mgmt_interface
.from_dict(convert_keys_to_python(self
._mgmt
_interface
))
444 if self
._http
_endpoint
:
445 vnfd
.http_endpoint
.add().from_dict(convert_keys_to_python(self
._http
_endpoint
))
446 vnfd
.vdu
.add().from_dict(props
)
447 except Exception as e
:
448 err_msg
= _("{0} Exception vdu from dict {1}: {2}"). \
449 format(self
, props
, e
)
450 self
.log
.error(err_msg
)
453 def generate_yang_submodel(self
):
454 """Generate yang model for the VDU"""
455 self
.log
.debug(_("Generate YANG model for {0}").
458 self
._update
_properties
_for
_model
()
460 vdu
= self
.properties