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 if key
== 'cloud_init':
105 vdu_props
['cloud-init'] = value
106 elif key
== 'cloud-init-file':
107 self
._cloud
_init
= "../cloud_init/{}".format(value
)
109 vdu_props
[key
] = value
111 if 'name' not in vdu_props
:
112 vdu_props
['name'] = self
.name
114 if 'id' not in vdu_props
:
115 vdu_props
['id'] = self
.id
117 if 'count' not in vdu_props
:
118 vdu_props
['count'] = 1
120 self
.log
.debug(_("VDU {0} properties: {1}").
121 format(self
.name
, vdu_props
))
122 self
.properties
= vdu_props
124 def handle_capabilities(self
):
126 def get_mgmt_interface(specs
):
128 mgmt_intfce
['vdu-id'] = self
.id
129 if 'dashboard_params' in specs
:
130 mgmt_intfce
['dashboard-params'] = {'path':specs
['dashboard_params']['path'], 'port':specs
['dashboard_params']['port']}
132 mgmt_intfce
['port'] = specs
['port']
135 def get_monitor_param(specs
, monitor_id
):
137 monitor_param
['id'] = monitor_id
139 monitor_param
['name'] = specs
['name']
140 if 'json_query_method' in specs
:
141 monitor_param
['json_query_method'] = specs
['json_query_method'].upper()
142 if 'description' in specs
:
143 monitor_param
['description'] = specs
['description']
144 if 'url_path' in specs
:
145 monitor_param
['http-endpoint-ref'] = specs
['url_path']
146 if 'ui_data' in specs
:
147 if 'widget_type' in specs
['ui_data']:
148 monitor_param
['widget-type'] = specs
['ui_data']['widget_type'].upper()
149 if 'units' in specs
['ui_data']:
150 monitor_param
['units'] = specs
['ui_data']['units']
151 if 'group_tag' in specs
['ui_data']:
152 monitor_param
['group_tag'] = specs
['ui_data']['group_tag']
153 if 'constraints' in specs
:
154 if 'value_type' in specs
['constraints']:
155 monitor_param
['value-type'] = ToscaCompute
.VALUE_TYPE_CONVERSION_MAP
[specs
['constraints']['value_type']]
159 def get_vm_flavor(specs
):
161 if 'num_cpus' in specs
:
162 vm_flavor
['vcpu-count'] = specs
['num_cpus']
164 vm_flavor
['vcpu-count'] = 1
166 if 'mem_size' in specs
:
167 vm_flavor
['memory-mb'] = (ScalarUnit_Size(specs
['mem_size']).
168 get_num_from_scalar_unit('MB'))
170 vm_flavor
['memory-mb'] = 512
172 if 'disk_size' in specs
:
173 vm_flavor
['storage-gb'] = (ScalarUnit_Size(specs
['disk_size']).
174 get_num_from_scalar_unit('GB'))
176 vm_flavor
['storage-gb'] = 4
180 def get_host_epa(specs
):
182 if 'cpu_model' in specs
:
183 host_epa
["cpu-model"] = specs
['cpu_model'].upper()
184 if 'cpu_arch' in specs
:
185 host_epa
["cpu-arch"] = specs
['cpu_arch'].upper()
186 if 'cpu_vendor' in specs
:
187 host_epa
["cpu-vendor"] = specs
['cpu_vendor'].upper()
188 if 'cpu_socket_count' in specs
:
189 host_epa
["cpu-socket-count"] = specs
['cpu_socket_count']
190 if 'cpu_core_count' in specs
:
191 host_epa
["cpu-core-count"] = specs
['cpu_core_count']
192 if 'cpu_core_thread_count' in specs
:
193 host_epa
["cpu-core-thread-count"] = specs
['cpu_core_thread_count']
194 if 'om_cpu_model_string' in specs
:
195 host_epa
["om-cpu-model-string"] = specs
['om_cpu_model_string']
196 if 'cpu_feature' in specs
:
197 cpu_feature_prop
= []
198 for spec
in specs
['cpu_feature']:
199 cpu_feature_prop
.append({'feature':spec
.upper()})
200 host_epa
['cpu-feature'] = cpu_feature_prop
201 if 'om_cpu_feature' in specs
:
202 cpu_feature_prop
= []
203 for spec
in specs
['om_cpu_feature']:
204 cpu_feature_prop
.append({'feature':spec
})
205 host_epa
['om-cpu-feature'] = cpu_feature_prop
208 def get_vswitch_epa(specs
):
210 if 'ovs_acceleration' in specs
:
211 vswitch_epa
['ovs-acceleration'] = specs
['ovs_acceleration'].upper()
212 if 'ovs_offload' in specs
:
213 vswitch_epa
['ovs-offload'] = specs
['ovs_offload'].upper()
216 def get_hypervisor_epa(specs
):
219 hypervisor_epa
['type'] = specs
['type'].upper()
220 if 'version' in specs
:
221 hypervisor_epa
['version'] = str(specs
['version'])
223 return hypervisor_epa
225 def get_guest_epa(specs
, nfv_comput_specs
):
227 guest_epa
['numa-node-policy'] = {}
228 guest_epa
['numa-node-policy']['node'] = []
229 if 'mem_policy' in specs
:
230 guest_epa
['numa-node-policy']['mem-policy'] = specs
['mem_policy'].upper()
231 if 'node_cnt' in specs
:
232 guest_epa
['numa-node-policy']['node-cnt'] = specs
['node_cnt']
234 for node
in specs
['node']:
237 node_prop
['id'] = node
['id']
238 if 'mem_size' in node
:
239 if 'MiB' in node
['mem_size'] or 'MB' in node
['mem_size']:
240 node_prop
['memory-mb'] = int(node
['mem_size'].replace('MB',''))
242 err_msg
= "Specify mem_size of NUMA extension should be in MB"
243 raise ValidationError(message
=err_msg
)
246 for vcpu
in node
['vcpus']:
247 vcpu_lis
.append({'id': vcpu
})
248 node_prop
['vcpu'] = vcpu_lis
249 if 'om_numa_type' in node
:
250 numa_type
= node
['om_numa_type']
251 if 'paired-threads' == numa_type
:
252 node_prop
['paired_threads'] = {}
253 node_prop
['paired_threads']['num_paired_threads'] = node
['paired_threads']['num_paired_threads']
254 elif 'threads' == numa_type
:
255 if 'num_threads' in node
:
256 node_prop
['num_threads'] = node
['num_threads']
257 elif 'cores' == numa_type
:
258 if 'num_cores' in node
:
259 node_prop
['num_cores'] = node
['num_cores']
261 err_msg
= "om_numa_type should be among cores, paired-threads or threads"
262 raise ValidationError(message
=err_msg
)
263 guest_epa
['numa-node-policy']['node'].append(node_prop
)
265 if 'mem_page_size' in nfv_comput_specs
:
266 guest_epa
['mempage-size'] = self
.TOSCA_MEM_SIZE
[nfv_comput_specs
['mem_page_size']]
267 if 'cpu_allocation' in nfv_comput_specs
:
268 if 'cpu_affinity' in nfv_comput_specs
['cpu_allocation']:
269 guest_epa
['cpu-pinning-policy'] = nfv_comput_specs
['cpu_allocation']['cpu_affinity'].upper()
270 guest_epa
['trusted-execution'] = False
271 if 'thread_allocation' in nfv_comput_specs
['cpu_allocation']:
272 guest_epa
['cpu-thread-pinning-policy'] = nfv_comput_specs
['cpu_allocation']['thread_allocation'].upper()
276 tosca_caps
= self
.get_tosca_caps()
277 self
.log
.debug(_("VDU {0} tosca capabilites: {1}").
278 format(self
.name
, tosca_caps
))
279 if 'nfv_compute' in tosca_caps
:
280 self
.properties
['vm-flavor'] = get_vm_flavor(tosca_caps
['nfv_compute'])
281 self
.log
.debug(_("VDU {0} properties: {1}").
282 format(self
.name
, self
.properties
))
283 if 'host_epa' in tosca_caps
:
284 self
.properties
['host-epa'] = get_host_epa(tosca_caps
['host_epa'])
285 if 'hypervisor_epa' in tosca_caps
:
286 self
.properties
['hypervisor-epa'] = get_hypervisor_epa(tosca_caps
['hypervisor_epa'])
287 if 'vswitch_epa' in tosca_caps
:
288 self
.properties
['vswitch-epa'] = get_vswitch_epa(tosca_caps
['vswitch_epa'])
289 if 'numa_extension' in tosca_caps
:
290 self
.properties
['guest-epa'] = get_guest_epa(tosca_caps
['numa_extension'], tosca_caps
['nfv_compute'])
291 if 'monitoring_param' in tosca_caps
:
292 self
._monitor
_param
.append(get_monitor_param(tosca_caps
['monitoring_param'], '1'))
293 if 'monitoring_param_1' in tosca_caps
:
294 self
._monitor
_param
.append(get_monitor_param(tosca_caps
['monitoring_param_1'], '2'))
295 if 'mgmt_interface' in tosca_caps
:
296 self
._mgmt
_interface
= get_mgmt_interface(tosca_caps
['mgmt_interface'])
297 if len(self
._mgmt
_interface
) > 0:
299 if 'dashboard-params' in self
._mgmt
_interface
:
300 if 'path' in self
._mgmt
_interface
['dashboard-params']:
301 prop
['path'] = self
._mgmt
_interface
['dashboard-params']['path']
302 if 'port' in self
._mgmt
_interface
['dashboard-params']:
303 prop
['port'] = self
._mgmt
_interface
['dashboard-params']['port']
304 self
._http
_endpoint
= prop
308 def handle_artifacts(self
):
309 if self
.artifacts
is None:
311 self
.log
.debug(_("VDU {0} tosca artifacts: {1}").
312 format(self
.name
, self
.artifacts
))
314 for key
in self
.artifacts
:
315 props
= self
.artifacts
[key
]
316 if isinstance(props
, dict):
318 for name
, value
in props
.items():
319 if name
== 'type' and value
== 'tosca.artifacts.Deployment.Image.riftio.QCOW2':
320 prefix
, type_
= value
.rsplit('.', 1)
322 details
['type'] = 'qcow2'
323 self
._image
= props
['file']
324 self
.properties
['image'] = os
.path
.basename(props
['file'])
325 elif name
== 'type' and value
== 'tosca.artifacts.Deployment.riftio.cloud_init_file':
326 details
['cloud_init_file'] = os
.path
.basename(props
['file'])
327 self
._cloud
_init
= props
['file']
328 self
.properties
['cloud_init_file'] = os
.path
.basename(props
['file'])
330 details
['file'] = value
331 elif name
== 'image_checksum':
332 self
.properties
['image_checksum'] = value
334 self
.log
.warn(_("VDU {0}, unsuported attribute {1}").
335 format(self
.name
, name
))
339 arts
[key
] = self
.artifacts
[key
]
341 self
.log
.debug(_("VDU {0} artifacts: {1}").
342 format(self
.name
, arts
))
343 self
.artifacts
= arts
345 def handle_interfaces(self
):
346 # Currently, we support only create operation
347 operations_deploy_sequence
= ['create']
349 operations
= ManoResource
._get
_all
_operations
(self
.nodetemplate
)
351 # use the current ManoResource for the first operation in this order
352 # Currently we only support image in create operation
353 for operation
in operations
.values():
354 if operation
.name
in operations_deploy_sequence
:
355 self
.operations
[operation
.name
] = None
357 self
.operations
[operation
.name
] = operation
.implementation
358 for name
, details
in self
.artifacts
.items():
359 if name
== operation
.implementation
:
360 self
._image
= details
['file']
361 except KeyError as e
:
362 self
.log
.exception(e
)
365 def update_image_checksum(self
, in_file
):
367 # Create image checksum
368 # in_file is the TOSCA yaml file location
369 if self
._image
is None:
371 self
.log
.debug("Update image: {}".format(in_file
))
372 if os
.path
.exists(in_file
):
373 in_dir
= os
.path
.dirname(in_file
)
374 img_dir
= os
.path
.dirname(self
._image
)
375 abs_dir
= os
.path
.normpath(
376 os
.path
.join(in_dir
, img_dir
))
377 self
.log
.debug("Abs path: {}".format(abs_dir
))
378 if os
.path
.isdir(abs_dir
):
379 img_path
= os
.path
.join(abs_dir
,
380 os
.path
.basename(self
._image
))
381 self
.log
.debug(_("Image path: {0}").
383 if os
.path
.exists(img_path
):
384 # TODO (pjoseph): To be fixed when we can retrieve
385 # the VNF image in Launchpad.
386 # Check if the file is not size 0
387 # else it is a dummy file and to be ignored
388 if os
.path
.getsize(img_path
) != 0:
389 self
._image
_cksum
= ChecksumUtils
.get_md5(img_path
,
392 def get_mano_attribute(self
, attribute
, args
):
394 # Convert from a TOSCA attribute for a nodetemplate to a MANO
395 # attribute for the matching resource. Unless there is additional
396 # runtime support, this should be a one to one mapping.
398 # Note: We treat private and public IP addresses equally, but
399 # this will change in the future when TOSCA starts to support
400 # multiple private/public IP addresses.
401 self
.log
.debug(_('Converting TOSCA attribute for a nodetemplate to a MANO \
403 if attribute
== 'private_address' or \
404 attribute
== 'public_address':
405 attr
['get_attr'] = [self
.name
, 'networks', 'private', 0]
409 def _update_properties_for_model(self
):
411 self
.properties
['image'] = os
.path
.basename(self
._image
)
412 if self
._image
_cksum
:
413 self
.properties
['image-checksum'] = self
._image
_cksum
415 for key
in ToscaCompute
.IGNORE_PROPS
:
416 if key
in self
.properties
:
417 self
.properties
.pop(key
)
419 def generate_yang_submodel_gi(self
, vnfd
):
422 self
._update
_properties
_for
_model
()
423 props
= convert_keys_to_python(self
.properties
)
425 for monitor_param
in self
._monitor
_param
:
426 monitor_props
= convert_keys_to_python(monitor_param
)
427 vnfd
.monitoring_param
.add().from_dict(monitor_props
)
429 if len(self
._mgmt
_interface
) > 0:
430 vnfd
.mgmt_interface
.from_dict(convert_keys_to_python(self
._mgmt
_interface
))
431 if self
._http
_endpoint
:
432 vnfd
.http_endpoint
.add().from_dict(convert_keys_to_python(self
._http
_endpoint
))
433 vnfd
.vdu
.add().from_dict(props
)
434 except Exception as e
:
435 err_msg
= _("{0} Exception vdu from dict {1}: {2}"). \
436 format(self
, props
, e
)
437 self
.log
.error(err_msg
)
440 def generate_yang_submodel(self
):
441 """Generate yang model for the VDU"""
442 self
.log
.debug(_("Generate YANG model for {0}").
445 self
._update
_properties
_for
_model
()
447 vdu
= self
.properties