2b244d7ac411e1431a8ed0363d139cbc2c9e7a2d
[osm/SO.git] / common / python / rift / mano / tosca_translator / rwmano / tosca / tosca_compute.py
1 #
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
5 #
6 # http://www.apache.org/licenses/LICENSE-2.0
7 #
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
12 # under the License.
13 #
14 # Copyright 2016 RIFT.io Inc
15
16
17 import os
18
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
23
24 from toscaparser.common.exception import ValidationError
25 from toscaparser.elements.scalarunit import ScalarUnit_Size
26
27 # Name used to dynamically load appropriate map class.
28 TARGET_CLASS_NAME = 'ToscaCompute'
29
30
31 class ToscaCompute(ManoResource):
32 '''Translate TOSCA node type RIFT.io VDUs.'''
33
34 REQUIRED_PROPS = ['name', 'id', 'image', 'count', 'vm-flavor']
35 OPTIONAL_PROPS = [
36 'external-interface',
37 'image-checksum',
38 'cloud-init',
39 'cloud-init-file',]
40 IGNORE_PROPS = []
41
42 toscatype = 'tosca.nodes.Compute'
43
44 def __init__(self, log, nodetemplate, metadata=None):
45 super(ToscaCompute, self).__init__(log,
46 nodetemplate,
47 type_='vdu',
48 metadata=metadata)
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
54 self._vnf = None
55 self._yang = None
56 self._id = self.name
57
58 @property
59 def image(self):
60 return self._image
61
62 @property
63 def cloud_init(self):
64 return self._cloud_init
65
66 @property
67 def vnf(self):
68 return self._vnf
69
70 @vnf.setter
71 def vnf(self, vnf):
72 if self._vnf:
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)
77 self._vnf = vnf
78
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))
83 vdu_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)
89 else:
90 vdu_props[key] = value
91
92 if 'name' not in vdu_props:
93 vdu_props['name'] = self.name
94
95 if 'id' not in vdu_props:
96 vdu_props['id'] = self.id
97
98 if 'count' not in vdu_props:
99 vdu_props['count'] = 1
100
101 self.log.debug(_("VDU {0} properties: {1}").
102 format(self.name, vdu_props))
103 self.properties = vdu_props
104
105 def handle_capabilities(self):
106
107 def get_vm_flavor(specs):
108 vm_flavor = {}
109 if 'num_cpus' in specs:
110 vm_flavor['vcpu-count'] = specs['num_cpus']
111 else:
112 vm_flavor['vcpu-count'] = 1
113
114 if 'mem_size' in specs:
115 vm_flavor['memory-mb'] = (ScalarUnit_Size(specs['mem_size']).
116 get_num_from_scalar_unit('MB'))
117 else:
118 vm_flavor['memory-mb'] = 512
119
120 if 'disk_size' in specs:
121 vm_flavor['storage-gb'] = (ScalarUnit_Size(specs['disk_size']).
122 get_num_from_scalar_unit('GB'))
123 else:
124 vm_flavor['storage-gb'] = 4
125
126 return vm_flavor
127
128 tosca_caps = self.get_tosca_caps()
129 self.log.debug(_("VDU {0} tosca capabilites: {1}").
130 format(self.name, tosca_caps))
131
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))
136
137 def handle_artifacts(self):
138 if self.artifacts is None:
139 return
140 self.log.debug(_("VDU {0} tosca artifacts: {1}").
141 format(self.name, self.artifacts))
142 arts = {}
143 for key in self.artifacts:
144 props = self.artifacts[key]
145 if isinstance(props, dict):
146 details = {}
147 for name, value in props.items():
148 if name == 'type':
149 prefix, type_ = value.rsplit('.', 1)
150 if type_ == 'QCOW2':
151 details['type'] = 'qcow2'
152 else:
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)
158 elif name == 'file':
159 details['file'] = value
160 elif name == 'image_checksum':
161 details['image_checksum'] = value
162 else:
163 self.log.warn(_("VDU {0}, unsuported attribute {1}").
164 format(self.name, name))
165 if len(details):
166 arts[key] = details
167 else:
168 arts[key] = self.artifacts[key]
169
170 self.log.debug(_("VDU {0} artifacts: {1}").
171 format(self.name, arts))
172 self.artifacts = arts
173
174 def handle_interfaces(self):
175 # Currently, we support only create operation
176 operations_deploy_sequence = ['create']
177
178 operations = ManoResource._get_all_operations(self.nodetemplate)
179
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
185 try:
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)
192 return None
193
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:
198 return
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}").
210 format(img_path))
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,
218 log=self.log)
219
220 def get_mano_attribute(self, attribute, args):
221 attr = {}
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.
225
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 \
230 attriute.'))
231 if attribute == 'private_address' or \
232 attribute == 'public_address':
233 attr['get_attr'] = [self.name, 'networks', 'private', 0]
234
235 return attr
236
237 def _update_properties_for_model(self):
238 if self._image:
239 self.properties['image'] = os.path.basename(self._image)
240 if self._image_cksum:
241 self.properties['image-checksum'] = self._image_cksum
242
243 for key in ToscaCompute.IGNORE_PROPS:
244 if key in self.properties:
245 self.properties.pop(key)
246
247 def generate_yang_submodel_gi(self, vnfd):
248 if vnfd is None:
249 return None
250 self._update_properties_for_model()
251 props = convert_keys_to_python(self.properties)
252 try:
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)
258 raise e
259
260 def generate_yang_submodel(self):
261 """Generate yang model for the VDU"""
262 self.log.debug(_("Generate YANG model for {0}").
263 format(self))
264
265 self._update_properties_for_model()
266
267 vdu = self.properties
268
269 return vdu