1 # Copyright 2016 RIFT.io Inc
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
20 from copy
import deepcopy
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 \
27 import rift
.package
.image
29 TARGET_CLASS_NAME
= 'YangVdu'
32 class YangVdu(ToscaResource
):
33 '''Class for RIFT.io YANG VDU descriptor translation to TOSCA type.'''
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',)
42 TOSCA_MISC_KEYS
= (VIRT_LINK
, VIRT_BIND
, VDU_INTF_NAME
,
44 ('virtualLink', 'virtualBinding', 'vdu_intf_name',
48 'vcpu_count': 'num_cpus',
49 'memory_mb': 'mem_size',
50 'storage_gb': 'disk_size',
62 'SIZE_2MB': 'size_2MB',
63 'SIZE_1GB': 'size_1GB',
64 'PREFER_LARGE': 'prefer_huge'
73 super(YangVdu
, self
).__init
__(log
,
82 self
.cloud_init_file
= None
84 self
.vswitch_epa
= None
85 self
.hypervisor_epa
= None
87 self
.cp_name_to_cp_node
= {}
88 self
.pinning_epa_prop
= {}
89 self
.mem_page_guest_epa
= None
90 self
.conn_point_to_conection_node
= {}
92 def process_vdu(self
):
93 self
.log
.debug(_("Process VDU desc {0}: {1}").format(self
.name
,
96 vdu_dic
= deepcopy(self
.yang
)
99 fields
= [self
.ID
, self
.COUNT
, self
.CLOUD_INIT
,
100 self
.IMAGE
, self
.IMAGE_CHKSUM
, self
.CLOUD_INIT_FILE
,]
103 vdu
[key
] = vdu_dic
.pop(key
)
105 self
.id = vdu
[self
.ID
]
107 if self
.VM_FLAVOR
in vdu_dic
:
108 vdu
[self
.NFV_COMPUTE
] = {}
109 for key
, value
in vdu_dic
.pop(self
.VM_FLAVOR
).items():
110 vdu
[self
.NFV_COMPUTE
][self
.VM_FLAVOR_MAP
[key
]] = "{}{}". \
111 format(value
, self
.VM_SIZE_UNITS_MAP
[key
])
113 if self
.EXT_INTF
in vdu_dic
:
114 for ext_intf
in vdu_dic
.pop(self
.EXT_INTF
):
116 cp
[self
.NAME
] = ext_intf
.pop(self
.VNFD_CP_REF
)
117 cp
[self
.VDU_INTF_NAME
] = ext_intf
.pop(self
.NAME
)
118 cp
[self
.VDU_INTF_TYPE
] = ext_intf
[self
.VIRT_INTF
][self
.TYPE_Y
]
119 self
.log
.debug(_("{0}, External interface {1}: {2}").
120 format(self
, cp
, ext_intf
))
121 self
.ext_cp
.append(cp
)
123 if self
.HOST_EPA
in vdu_dic
:
124 host_epa
= vdu_dic
.pop(self
.HOST_EPA
)
126 self
.host_epa
= host_epa
128 if 'cpu_model' in host_epa:
129 host_epa_prop['cpu_model'] = host_epa['cpu_model'].lower()
130 if 'cpu_arch' in host_epa:
131 host_epa_prop['cpu_arch'] = host_epa['cpu_arch'].lower()
132 if 'cpu_vendor' in host_epa:
133 host_epa_prop['cpu_vendor'] = host_epa['cpu_vendor'].lower()
134 if 'cpu_socket_count' in host_epa:
135 host_epa_prop['cpu_socket_count'] = host_epa['cpu_socket_count']
136 if 'cpu_core_count' in host_epa:
137 host_epa_prop['cpu_core_count'] = host_epa['cpu_core_count']
138 if 'cpu_core_thread_count' in host_epa:
139 host_epa_prop['cpu_core_thread_count'] = host_epa['cpu_core_thread_count']
140 if 'om_cpu_model_string' in host_epa:
141 host_epa_prop['om_cpu_model_string'] = host_epa['om_cpu_model_string']
142 if 'cpu_feature' in host_epa:
143 host_epa_prop['cpu_feature'] = []
144 for cpu_feature in host_epa['cpu_feature']:
145 cpu_feature_prop = {}
146 cpu_feature_prop['feature'] = cpu_feature['feature'].lower()
147 host_epa_prop['cpu_feature'] .append(cpu_feature_prop)
149 if 'om_cpu_feature' in host_epa:
150 host_epa_prop['om_cpu_feature'] = []
151 for cpu_feature in host_epa['om_cpu_feature']:
152 om_cpu_feature_prop = {}
153 om_cpu_feature_prop['feature'] = cpu_feature
154 host_epa_prop['om_cpu_feature'].append(om_cpu_feature_prop)
155 self.host_epa = host_epa
157 # We might have to re write this piece of code, there are mismatch in
158 # enum names. Its all capital in RIFT yang and TOSCA
159 if self
.VSWITCH_EPA
in vdu_dic
:
160 vswitch_epa
= vdu_dic
.pop(self
.VSWITCH_EPA
)
161 self
.vswitch_epa
= vswitch_epa
162 if self
.HYPERVISOR_EPA
in vdu_dic
:
163 hypervisor_epa
= vdu_dic
.pop(self
.HYPERVISOR_EPA
)
164 hypervisor_epa_prop
= {}
166 if 'type_yang' in hypervisor_epa
:
167 hypervisor_epa_prop
['type'] = hypervisor_epa
['type_yang']
168 if 'version' in hypervisor_epa
:
169 hypervisor_epa_prop
['version'] = str(hypervisor_epa
['version'])
171 hypervisor_epa_prop
['version'] = '1'
172 self
.hypervisor_epa
= hypervisor_epa_prop
174 if self
.GUEST_EPA
in vdu_dic
:
175 guest_epa
= vdu_dic
[self
.GUEST_EPA
]
178 # This is a hack. I have to rewrite this. I have got this quick to working
179 # 'ANY' check should be added in riftio common file. Its not working for some reason. Will fix.
181 if 'cpu_pinning_policy' in guest_epa
and guest_epa
['cpu_pinning_policy'] != 'ANY':
182 self
.pinning_epa_prop
['cpu_affinity'] = guest_epa
['cpu_pinning_policy'].lower()
183 if 'cpu_thread_pinning_policy' in guest_epa
:
184 self
.pinning_epa_prop
['thread_allocation'] = guest_epa
['cpu_thread_pinning_policy'].lower()
185 if 'mempage_size' in guest_epa
:
186 self
.mem_page_guest_epa
= self
.TOSCA_MEM_SIZE
[guest_epa
['mempage_size']]
188 if 'numa_node_policy' in guest_epa
:
189 num_node_policy
= guest_epa
['numa_node_policy']
190 if 'node_cnt' in num_node_policy
:
191 guest_epa_prop
['node_cnt'] = num_node_policy
['node_cnt']
192 if 'mem_policy' in num_node_policy
:
193 guest_epa_prop
['mem_policy'] = num_node_policy
['mem_policy']
194 if 'node' in num_node_policy
:
196 for node
in num_node_policy
['node']:
199 node_prop
['id'] = node
['id']
202 for vcp
in node
['vcpu']:
205 node_prop
['vcpus'] = vc
206 if 'memory_mb' in node
:
207 node_prop
['mem_size'] = "{} MB".format(node
['memory_mb'])
208 # om_numa_type generation
210 if 'num_cores' in node
:
211 node_prop
['om_numa_type'] = 'num_cores'
212 node_prop
['num_cores'] = node
['num_cores']
213 elif 'paired_threads' in node
:
214 node_prop
['om_numa_type'] = 'paired-threads'
215 node_prop
['paired_threads'] = node
['paired_threads']
216 elif 'threads]' in node
:
217 node_prop
['om_numa_type'] = 'threads]'
218 node_prop
['num_thread]'] = node
['threads]']
220 nodes
.append(node_prop
)
221 guest_epa_prop
['node'] = nodes
223 self
.guest_epa
= guest_epa_prop
225 self
.remove_ignored_fields(vdu_dic
)
227 for cp
in self
.ext_cp
:
228 cp_name
= cp
[self
.NAME
].replace('/', '_')
229 self
.conn_point_to_conection_node
[cp
[self
.NAME
]] = cp_name
233 self
.log
.warn(_("{0}, Did not process the following in "
235 format(self
, vdu_dic
))
237 self
.log
.debug(_("{0} VDU: {1}").format(self
, vdu
))
240 def get_cp(self
, name
):
241 for cp
in self
.ext_cp
:
242 if cp
[self
.NAME
] == name
:
246 def has_cp(self
, name
):
247 if self
.get_cp(name
):
251 def set_cp_type(self
, name
, cp_type
):
252 for idx
, cp
in enumerate(self
.ext_cp
):
253 if cp
[self
.NAME
] == name
:
254 cp
[self
.CP_TYPE
] = cp_type
255 self
.ext_cp
[idx
] = cp
256 self
.log
.debug(_("{0}, Updated CP: {1}").
257 format(self
, self
.ext_cp
[idx
]))
260 err_msg
= (_("{0}, Did not find connection point {1}").
262 self
.log
.error(err_msg
)
263 raise ValidationError(message
=err_msg
)
265 def set_vld(self
, name
, vld_name
):
266 cp
= self
.get_cp(name
)
268 cp
[self
.VLD
] = vld_name
270 err_msg
= (_("{0}, Did not find connection point {1}").
272 self
.log
.error(err_msg
)
273 raise ValidationError(message
=err_msg
)
275 def get_name(self
, vnf_name
):
276 # Create a unique name incase multiple VNFs use same
278 return "{}_{}".format(vnf_name
, self
.name
)
281 def generate_tosca_type(self
, tosca
):
282 self
.log
.debug(_("{0} Generate tosa types").
285 # Add custom artifact type
286 if self
.ARTIFACT_TYPES
not in tosca
:
287 tosca
[self
.ARTIFACT_TYPES
] = {}
288 if self
.T_ARTF_QCOW2
not in tosca
[self
.ARTIFACT_TYPES
]:
289 tosca
[self
.ARTIFACT_TYPES
][self
.T_ARTF_QCOW2
] = {
290 self
.DERIVED_FROM
: 'tosca.artifacts.Deployment.Image.VM.QCOW2',
292 {self
.TYPE
: self
.STRING
,
293 self
.REQUIRED
: self
.NO
},
296 if self
.T_VDU1
not in tosca
[self
.NODE_TYPES
]:
297 tosca
[self
.NODE_TYPES
][self
.T_VDU1
] = {
298 self
.DERIVED_FROM
: 'tosca.nodes.nfv.VDU',
301 {self
.TYPE
: self
.INTEGER
,
304 {self
.TYPE
: self
.STRING
,
305 self
.REQUIRED
: self
.NO
,},
306 self
.CLOUD_INIT_FILE
:
307 {self
.TYPE
: self
.STRING
,
308 self
.REQUIRED
: self
.NO
,},
312 self
.TYPE
: 'tosca.capabilities.nfv.VirtualLinkable'
318 if self
.T_CP1
not in tosca
[self
.NODE_TYPES
]:
319 tosca
[self
.NODE_TYPES
][self
.T_CP1
] = {
320 self
.DERIVED_FROM
: 'tosca.nodes.nfv.CP',
323 {self
.TYPE
: self
.STRING
,
324 self
.DESC
: 'Name of the connection point'},
326 {self
.TYPE
: self
.STRING
,
327 self
.DESC
: 'Type of the connection point'},
329 {self
.TYPE
: self
.STRING
,
330 self
.DESC
: 'Name of the interface on VDU'},
332 {self
.TYPE
: self
.STRING
,
333 self
.DESC
: 'Type of the interface on VDU'},
339 def generate_vdu_template(self
, tosca
, vnf_name
):
340 self
.log
.debug(_("{0} Generate tosca template for {2}").
341 format(self
, tosca
, vnf_name
))
343 name
= self
.get_name(vnf_name
)
346 node
[self
.TYPE
] = self
.T_VDU1
347 node
[self
.CAPABILITIES
] = {}
349 if self
.NFV_COMPUTE
in self
.props
:
350 node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
] = {self
.PROPERTIES
: self
.props
.pop(self
.NFV_COMPUTE
)}
352 self
.log
.warn(_("{0}, Does not have host requirements defined").
355 node
[self
.CAPABILITIES
][self
.HOST_EPA
] = {
356 self
.PROPERTIES
: self
.host_epa
359 node
[self
.CAPABILITIES
][self
.VSWITCH_EPA
] = {
360 self
.PROPERTIES
: self
.vswitch_epa
362 if self
.hypervisor_epa
:
363 node
[self
.CAPABILITIES
][self
.HYPERVISOR_EPA
] = {
364 self
.PROPERTIES
: self
.hypervisor_epa
367 node
[self
.CAPABILITIES
]['numa_extension'] = {
368 self
.PROPERTIES
: self
.guest_epa
370 if len(self
.pinning_epa_prop
) > 0:
371 if node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
] and node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
][self
.PROPERTIES
]:
372 node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
][self
.PROPERTIES
]['cpu_allocation'] = self
.pinning_epa_prop
373 if self
.mem_page_guest_epa
:
374 if node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
] and node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
][self
.PROPERTIES
]:
375 node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
][self
.PROPERTIES
]['mem_page_size'] = self
.mem_page_guest_epa
377 if self
.IMAGE
in self
.props
:
378 img_name
= "{}_{}_vm_image".format(vnf_name
, self
.name
)
379 image
= "../{}/{}".format(self
.IMAGE_DIR
, self
.props
.pop(self
.IMAGE
))
381 node
[self
.ARTIFACTS
] = {img_name
: {
383 self
.TYPE
: self
.T_ARTF_QCOW2
,
385 if self
.IMAGE_CHKSUM
in self
.props
:
386 node
[self
.ARTIFACTS
][img_name
][self
.IMAGE_CHKSUM
] = \
387 self
.props
.pop(self
.IMAGE_CHKSUM
)
388 node
[self
.INTERFACES
] = {'Standard': {
391 # Add cloud init script if available
392 if self
.CLOUD_INIT_FILE
in self
.props
:
393 cloud_name
= "{}_{}_cloud_init".format(vnf_name
, self
.name
)
394 self
.cloud_init_file
= self
.props
[self
.CLOUD_INIT_FILE
]
395 cloud_init_file
= "../{}/{}".format(self
.CLOUD_INIT_DIR
, self
.props
.pop(self
.CLOUD_INIT_FILE
))
396 if self
.ARTIFACTS
in node
:
397 node
[self
.ARTIFACTS
][cloud_name
] = {
398 self
.FILE
: cloud_init_file
,
399 self
.TYPE
: self
.T_ARTF_CLOUD_INIT
,
402 node
[self
.ARTIFACTS
] = {
404 self
.FILE
: cloud_init_file
,
405 self
.TYPE
: self
.T_ARTF_CLOUD_INIT
,
409 self
.props
.pop(self
.ID
)
410 node
[self
.PROPERTIES
] = self
.props
412 self
.log
.debug(_("{0}, VDU node: {1}").format(self
, node
))
413 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][name
] = node
415 # Generate the connection point templates
416 for cp
in self
.ext_cp
:
417 cpt
= {self
.TYPE
: self
.T_CP1
}
419 cpt
[self
.REQUIREMENTS
] = []
420 cpt
[self
.REQUIREMENTS
].append({self
.VIRT_BIND
: {
421 self
.NODE
: self
.get_name(vnf_name
)
424 vld
= cp
.pop(self
.VLD
)
425 cpt
[self
.REQUIREMENTS
].append({self
.VIRT_LINK
: {
429 cpt
[self
.PROPERTIES
] = cp
430 cp_name
= cp
[self
.NAME
].replace('/', '_')
431 self
.cp_name_to_cp_node
[cp
[self
.NAME
]] = cp_name
433 self
.log
.debug(_("{0}, CP node {1}: {2}").
434 format(self
, cp_name
, cpt
))
435 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][cp_name
] = cpt
439 def get_supporting_files(self
):
442 if self
.image
is not None:
443 image_name
= os
.path
.basename(self
.image
)
447 self
.NAME
: image_name
,
448 self
.DEST
: "{}/{}".format(self
.IMAGE_DIR
, image_name
),
451 if self
.cloud_init_file
is not None:
453 self
.TYPE
: 'cloud_init',
454 self
.NAME
: self
.cloud_init_file
,
455 self
.DEST
: "{}/{}".format(self
.CLOUD_INIT
, self
.cloud_init_file
)
458 self
.log
.debug(_("Supporting files for {} : {}").format(self
, files
))