7d095c1ee9b1724be7a6013b3f031b25b31058ae
[osm/SO.git] / common / python / rift / mano / yang_translator / rwmano / yang / yang_vdu.py
1 # Copyright 2016 RIFT.io Inc
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15
16 import os
17 import shutil
18 import tempfile
19
20 from copy import deepcopy
21
22 from rift.mano.yang_translator.common.exception import ValidationError
23 from rift.mano.yang_translator.common.utils import _
24 from rift.mano.yang_translator.rwmano.syntax.tosca_resource \
25 import ToscaResource
26
27 import rift.package.image
28
29 TARGET_CLASS_NAME = 'YangVdu'
30
31
32 class YangVdu(ToscaResource):
33 '''Class for RIFT.io YANG VDU descriptor translation to TOSCA type.'''
34
35 yangtype = 'vdu'
36
37 OTHER_KEYS = (VM_FLAVOR, CLOUD_INIT, IMAGE, IMAGE_CHKSUM,
38 VNFD_CP_REF, CP_TYPE, CLOUD_INIT_FILE,) = \
39 ('vm_flavor', 'cloud_init', 'image', 'image_checksum',
40 'vnfd_connection_point_ref', 'cp_type', 'cloud_init_file',)
41
42 TOSCA_MISC_KEYS = (VIRT_LINK, VIRT_BIND, VDU_INTF_NAME,
43 VDU_INTF_TYPE) = \
44 ('virtualLink', 'virtualBinding', 'vdu_intf_name',
45 'vdu_intf_type')
46
47 VM_FLAVOR_MAP = {
48 'vcpu_count': 'num_cpus',
49 'memory_mb': 'mem_size',
50 'storage_gb': 'disk_size',
51 }
52
53 VM_SIZE_UNITS_MAP = {
54 'vcpu_count': '',
55 'memory_mb': ' MB',
56 'storage_gb': ' GB',
57 }
58
59 def __init__(self,
60 log,
61 name,
62 type_,
63 yang):
64 super(YangVdu, self).__init__(log,
65 name,
66 type_,
67 yang)
68 self.yang = yang
69 self.props = {}
70 self.ext_cp = []
71 self.int_cp = []
72 self.image = None
73 self.cloud_init_file = None
74
75 def process_vdu(self):
76 self.log.debug(_("Process VDU desc {0}: {1}").format(self.name,
77 self.yang))
78
79 vdu_dic = deepcopy(self.yang)
80 vdu = {}
81
82 fields = [self.ID, self.COUNT, self.CLOUD_INIT,
83 self.IMAGE, self.IMAGE_CHKSUM, self.CLOUD_INIT_FILE,]
84 for key in fields:
85 if key in vdu_dic:
86 vdu[key] = vdu_dic.pop(key)
87
88 self.id = vdu[self.ID]
89
90 if self.VM_FLAVOR in vdu_dic:
91 vdu[self.HOST] = {}
92 for key, value in vdu_dic.pop(self.VM_FLAVOR).items():
93 vdu[self.HOST][self.VM_FLAVOR_MAP[key]] = "{}{}". \
94 format(value, self.VM_SIZE_UNITS_MAP[key])
95
96 if self.EXT_INTF in vdu_dic:
97 for ext_intf in vdu_dic.pop(self.EXT_INTF):
98 cp = {}
99 cp[self.NAME] = ext_intf.pop(self.VNFD_CP_REF)
100 cp[self.VDU_INTF_NAME] = ext_intf.pop(self.NAME)
101 cp[self.VDU_INTF_TYPE] = ext_intf[self.VIRT_INTF][self.TYPE_Y]
102 self.log.debug(_("{0}, External interface {1}: {2}").
103 format(self, cp, ext_intf))
104 self.ext_cp.append(cp)
105
106 self.remove_ignored_fields(vdu_dic)
107 if len(vdu_dic):
108 self.log.warn(_("{0}, Did not process the following in "
109 "VDU: {1}").
110 format(self, vdu_dic))
111
112 self.log.debug(_("{0} VDU: {1}").format(self, vdu))
113 self.props = vdu
114
115 def get_cp(self, name):
116 for cp in self.ext_cp:
117 if cp[self.NAME] == name:
118 return cp
119 return None
120
121 def has_cp(self, name):
122 if self.get_cp(name):
123 return True
124 return False
125
126 def set_cp_type(self, name, cp_type):
127 for idx, cp in enumerate(self.ext_cp):
128 if cp[self.NAME] == name:
129 cp[self.CP_TYPE] = cp_type
130 self.ext_cp[idx] = cp
131 self.log.debug(_("{0}, Updated CP: {1}").
132 format(self, self.ext_cp[idx]))
133 return
134
135 err_msg = (_("{0}, Did not find connection point {1}").
136 format(self, name))
137 self.log.error(err_msg)
138 raise ValidationError(message=err_msg)
139
140 def set_vld(self, name, vld_name):
141 cp = self.get_cp(name)
142 if cp:
143 cp[self.VLD] = vld_name
144 else:
145 err_msg = (_("{0}, Did not find connection point {1}").
146 format(self, name))
147 self.log.error(err_msg)
148 raise ValidationError(message=err_msg)
149
150 def get_name(self, vnf_name):
151 # Create a unique name incase multiple VNFs use same
152 # name for the vdu
153 return "{}_{}".format(vnf_name, self.name)
154
155 def generate_tosca_type(self, tosca):
156 self.log.debug(_("{0} Generate tosa types").
157 format(self, tosca))
158
159 # Add custom artifact type
160 if self.ARTIFACT_TYPES not in tosca:
161 tosca[self.ARTIFACT_TYPES] = {}
162 if self.T_ARTF_QCOW2 not in tosca[self.ARTIFACT_TYPES]:
163 tosca[self.ARTIFACT_TYPES][self.T_ARTF_QCOW2] = {
164 self.DERIVED_FROM: 'tosca.artifacts.Deployment.Image.VM.QCOW2',
165 self.IMAGE_CHKSUM:
166 {self.TYPE: self.STRING,
167 self.REQUIRED: self.NO},
168 }
169
170 if self.T_VDU1 not in tosca[self.NODE_TYPES]:
171 tosca[self.NODE_TYPES][self.T_VDU1] = {
172 self.DERIVED_FROM: 'tosca.nodes.nfv.VDU',
173 self.PROPERTIES: {
174 self.COUNT:
175 {self.TYPE: self.INTEGER,
176 self.DEFAULT: 1},
177 self.CLOUD_INIT:
178 {self.TYPE: self.STRING,
179 self.REQUIRED: self.NO,},
180 self.CLOUD_INIT_FILE:
181 {self.TYPE: self.STRING,
182 self.REQUIRED: self.NO,},
183 },
184 self.CAPABILITIES: {
185 self.VIRT_LINK: {
186 self.TYPE: 'tosca.capabilities.nfv.VirtualLinkable'
187 },
188 },
189 }
190
191 # Add CP type
192 if self.T_CP1 not in tosca[self.NODE_TYPES]:
193 tosca[self.NODE_TYPES][self.T_CP1] = {
194 self.DERIVED_FROM: 'tosca.nodes.nfv.CP',
195 self.PROPERTIES: {
196 self.NAME:
197 {self.TYPE: self.STRING,
198 self.DESC: 'Name of the connection point'},
199 self.CP_TYPE:
200 {self.TYPE: self.STRING,
201 self.DESC: 'Type of the connection point'},
202 self.VDU_INTF_NAME:
203 {self.TYPE: self.STRING,
204 self.DESC: 'Name of the interface on VDU'},
205 self.VDU_INTF_TYPE:
206 {self.TYPE: self.STRING,
207 self.DESC: 'Type of the interface on VDU'},
208 },
209 }
210
211 return tosca
212
213 def generate_vdu_template(self, tosca, vnf_name):
214 self.log.debug(_("{0} Generate tosca template for {2}").
215 format(self, tosca, vnf_name))
216
217 name = self.get_name(vnf_name)
218
219 node = {}
220 node[self.TYPE] = self.T_VDU1
221
222 if self.HOST in self.props:
223 node[self.CAPABILITIES] = {
224 self.HOST: {self.PROPERTIES: self.props.pop(self.HOST)}
225 }
226 else:
227 self.log.warn(_("{0}, Does not have host requirements defined").
228 format(self))
229
230 if self.IMAGE in self.props:
231 img_name = "{}_{}_vm_image".format(vnf_name, self.name)
232 image = "../{}/{}".format(self.IMAGE_DIR, self.props.pop(self.IMAGE))
233 self.image = image
234 node[self.ARTIFACTS] = {img_name: {
235 self.FILE: image,
236 self.TYPE: self.T_ARTF_QCOW2,
237 }}
238 if self.IMAGE_CHKSUM in self.props:
239 node[self.ARTIFACTS][img_name][self.IMAGE_CHKSUM] = \
240 self.props.pop(self.IMAGE_CHKSUM)
241 node[self.INTERFACES] = {'Standard': {
242 'create': img_name
243 }}
244
245 # Add cloud init script if available
246 if self.CLOUD_INIT_FILE in self.props:
247 self.cloud_init_file = self.props[self.CLOUD_INIT_FILE]
248
249 # Remove
250 self.props.pop(self.ID)
251 node[self.PROPERTIES] = self.props
252
253 self.log.debug(_("{0}, VDU node: {1}").format(self, node))
254 tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][name] = node
255
256 # Generate the connection point templates
257 for cp in self.ext_cp:
258 cpt = {self.TYPE: self.T_CP1}
259
260 cpt[self.REQUIREMENTS] = []
261 cpt[self.REQUIREMENTS].append({self.VIRT_BIND: {
262 self.NODE: self.get_name(vnf_name)
263 }})
264 if self.VLD in cp:
265 vld = cp.pop(self.VLD)
266 cpt[self.REQUIREMENTS].append({self.VIRT_LINK: {
267 self.NODE: vld
268 }})
269
270 cpt[self.PROPERTIES] = cp
271 cp_name = cp[self.NAME].replace('/', '_')
272
273 self.log.debug(_("{0}, CP node {1}: {2}").
274 format(self, cp_name, cpt))
275 tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][cp_name] = cpt
276
277 return tosca
278
279 def get_supporting_files(self):
280 files = []
281
282 if self.image is not None:
283 image_name = os.path.basename(self.image)
284
285 files.append({
286 self.TYPE: 'image',
287 self.NAME: image_name,
288 self.DEST: "{}/{}".format(self.IMAGE_DIR, image_name),
289 })
290
291 if self.cloud_init_file is not None:
292 files.append({
293 self.TYPE: 'cloud_init',
294 self.NAME: self.cloud_init_file,
295 self.DEST: "{}/{}".format(self.CLOUD_INIT, self.cloud_init_file)
296 })
297
298 self.log.debug(_("Supporting files for {} : {}").format(self, files))
299 if not len(files):
300 shutil.rmtree(out_dir)
301
302 return files