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.Compute'
44 def __init__(self
, log
, nodetemplate
, metadata
=None):
45 super(ToscaCompute
, self
).__init
__(log
,
49 # List with associated port resources with this server
50 self
.assoc_port_resources
= []
51 self
._image
= None # Image to bring up the VDU
52 self
._image
_cksum
= None
53 self
._cloud
_init
= None # Cloud init file
64 return self
._cloud
_init
73 err_msg
= (_('VDU {0} already has a VNF {1} associated').
74 format(self
, self
._vnf
))
75 self
.log
.error(err_msg
)
76 raise ValidationError(message
=err_msg
)
79 def handle_properties(self
):
80 tosca_props
= self
.get_tosca_props()
81 self
.log
.debug(_("VDU {0} tosca properties: {1}").
82 format(self
.name
, tosca_props
))
84 for key
, value
in tosca_props
.items():
85 if key
== 'cloud_init':
86 vdu_props
['cloud-init'] = value
87 elif key
== 'cloud-init-file':
88 self
._cloud
_init
= "../cloud_init/{}".format(value
)
90 vdu_props
[key
] = value
92 if 'name' not in vdu_props
:
93 vdu_props
['name'] = self
.name
95 if 'id' not in vdu_props
:
96 vdu_props
['id'] = self
.id
98 if 'count' not in vdu_props
:
99 vdu_props
['count'] = 1
101 self
.log
.debug(_("VDU {0} properties: {1}").
102 format(self
.name
, vdu_props
))
103 self
.properties
= vdu_props
105 def handle_capabilities(self
):
107 def get_vm_flavor(specs
):
109 if 'num_cpus' in specs
:
110 vm_flavor
['vcpu-count'] = specs
['num_cpus']
112 vm_flavor
['vcpu-count'] = 1
114 if 'mem_size' in specs
:
115 vm_flavor
['memory-mb'] = (ScalarUnit_Size(specs
['mem_size']).
116 get_num_from_scalar_unit('MB'))
118 vm_flavor
['memory-mb'] = 512
120 if 'disk_size' in specs
:
121 vm_flavor
['storage-gb'] = (ScalarUnit_Size(specs
['disk_size']).
122 get_num_from_scalar_unit('GB'))
124 vm_flavor
['storage-gb'] = 4
128 tosca_caps
= self
.get_tosca_caps()
129 self
.log
.debug(_("VDU {0} tosca capabilites: {1}").
130 format(self
.name
, tosca_caps
))
132 if 'host' in tosca_caps
:
133 self
.properties
['vm-flavor'] = get_vm_flavor(tosca_caps
['host'])
134 self
.log
.debug(_("VDU {0} properties: {1}").
135 format(self
.name
, self
.properties
))
137 def handle_artifacts(self
):
138 if self
.artifacts
is None:
140 self
.log
.debug(_("VDU {0} tosca artifacts: {1}").
141 format(self
.name
, self
.artifacts
))
143 for key
in self
.artifacts
:
144 props
= self
.artifacts
[key
]
145 if isinstance(props
, dict):
147 for name
, value
in props
.items():
149 prefix
, type_
= value
.rsplit('.', 1)
151 details
['type'] = 'qcow2'
153 err_msg
= _("VDU {0}, Currently only QCOW2 images "
154 "are supported in artifacts ({1}:{2})"). \
155 format(self
.name
, key
, value
)
156 self
.log
.error(err_msg
)
157 raise ValidationError(message
=err_msg
)
159 details
['file'] = value
160 elif name
== 'image_checksum':
161 details
['image_checksum'] = value
163 self
.log
.warn(_("VDU {0}, unsuported attribute {1}").
164 format(self
.name
, name
))
168 arts
[key
] = self
.artifacts
[key
]
170 self
.log
.debug(_("VDU {0} artifacts: {1}").
171 format(self
.name
, arts
))
172 self
.artifacts
= arts
174 def handle_interfaces(self
):
175 # Currently, we support only create operation
176 operations_deploy_sequence
= ['create']
178 operations
= ManoResource
._get
_all
_operations
(self
.nodetemplate
)
180 # use the current ManoResource for the first operation in this order
181 # Currently we only support image in create operation
182 for operation
in operations
.values():
183 if operation
.name
in operations_deploy_sequence
:
184 self
.operations
[operation
.name
] = None
186 self
.operations
[operation
.name
] = operation
.implementation
187 for name
, details
in self
.artifacts
.items():
188 if name
== operation
.implementation
:
189 self
._image
= details
['file']
190 except KeyError as e
:
191 self
.log
.exception(e
)
194 def update_image_checksum(self
, in_file
):
195 # Create image checksum
196 # in_file is the TOSCA yaml file location
197 if self
._image
is None:
199 self
.log
.debug("Update image: {}".format(in_file
))
200 if os
.path
.exists(in_file
):
201 in_dir
= os
.path
.dirname(in_file
)
202 img_dir
= os
.path
.dirname(self
._image
)
203 abs_dir
= os
.path
.normpath(
204 os
.path
.join(in_dir
, img_dir
))
205 self
.log
.debug("Abs path: {}".format(abs_dir
))
206 if os
.path
.isdir(abs_dir
):
207 img_path
= os
.path
.join(abs_dir
,
208 os
.path
.basename(self
._image
))
209 self
.log
.debug(_("Image path: {0}").
211 if os
.path
.exists(img_path
):
212 # TODO (pjoseph): To be fixed when we can retrieve
213 # the VNF image in Launchpad.
214 # Check if the file is not size 0
215 # else it is a dummy file and to be ignored
216 if os
.path
.getsize(img_path
) != 0:
217 self
._image
_cksum
= ChecksumUtils
.get_md5(img_path
,
220 def get_mano_attribute(self
, attribute
, args
):
222 # Convert from a TOSCA attribute for a nodetemplate to a MANO
223 # attribute for the matching resource. Unless there is additional
224 # runtime support, this should be a one to one mapping.
226 # Note: We treat private and public IP addresses equally, but
227 # this will change in the future when TOSCA starts to support
228 # multiple private/public IP addresses.
229 self
.log
.debug(_('Converting TOSCA attribute for a nodetemplate to a MANO \
231 if attribute
== 'private_address' or \
232 attribute
== 'public_address':
233 attr
['get_attr'] = [self
.name
, 'networks', 'private', 0]
237 def _update_properties_for_model(self
):
239 self
.properties
['image'] = os
.path
.basename(self
._image
)
240 if self
._image
_cksum
:
241 self
.properties
['image-checksum'] = self
._image
_cksum
243 for key
in ToscaCompute
.IGNORE_PROPS
:
244 if key
in self
.properties
:
245 self
.properties
.pop(key
)
247 def generate_yang_submodel_gi(self
, vnfd
):
250 self
._update
_properties
_for
_model
()
251 props
= convert_keys_to_python(self
.properties
)
253 vnfd
.vdu
.add().from_dict(props
)
254 except Exception as e
:
255 err_msg
= _("{0} Exception vdu from dict {1}: {2}"). \
256 format(self
, props
, e
)
257 self
.log
.error(err_msg
)
260 def generate_yang_submodel(self
):
261 """Generate yang model for the VDU"""
262 self
.log
.debug(_("Generate YANG model for {0}").
265 self
._update
_properties
_for
_model
()
267 vdu
= self
.properties