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
91 def process_vdu(self
):
92 self
.log
.debug(_("Process VDU desc {0}: {1}").format(self
.name
,
95 vdu_dic
= deepcopy(self
.yang
)
98 fields
= [self
.ID
, self
.COUNT
, self
.CLOUD_INIT
,
99 self
.IMAGE
, self
.IMAGE_CHKSUM
, self
.CLOUD_INIT_FILE
,]
102 vdu
[key
] = vdu_dic
.pop(key
)
104 self
.id = vdu
[self
.ID
]
106 if self
.VM_FLAVOR
in vdu_dic
:
107 vdu
[self
.NFV_COMPUTE
] = {}
108 for key
, value
in vdu_dic
.pop(self
.VM_FLAVOR
).items():
109 vdu
[self
.NFV_COMPUTE
][self
.VM_FLAVOR_MAP
[key
]] = "{}{}". \
110 format(value
, self
.VM_SIZE_UNITS_MAP
[key
])
112 if self
.EXT_INTF
in vdu_dic
:
113 for ext_intf
in vdu_dic
.pop(self
.EXT_INTF
):
115 cp
[self
.NAME
] = ext_intf
.pop(self
.VNFD_CP_REF
)
116 cp
[self
.VDU_INTF_NAME
] = ext_intf
.pop(self
.NAME
)
117 cp
[self
.VDU_INTF_TYPE
] = ext_intf
[self
.VIRT_INTF
][self
.TYPE_Y
]
118 self
.log
.debug(_("{0}, External interface {1}: {2}").
119 format(self
, cp
, ext_intf
))
120 self
.ext_cp
.append(cp
)
122 if self
.HOST_EPA
in vdu_dic
:
123 host_epa
= vdu_dic
.pop(self
.HOST_EPA
)
125 self
.host_epa
= host_epa
127 if 'cpu_model' in host_epa:
128 host_epa_prop['cpu_model'] = host_epa['cpu_model'].lower()
129 if 'cpu_arch' in host_epa:
130 host_epa_prop['cpu_arch'] = host_epa['cpu_arch'].lower()
131 if 'cpu_vendor' in host_epa:
132 host_epa_prop['cpu_vendor'] = host_epa['cpu_vendor'].lower()
133 if 'cpu_socket_count' in host_epa:
134 host_epa_prop['cpu_socket_count'] = host_epa['cpu_socket_count']
135 if 'cpu_core_count' in host_epa:
136 host_epa_prop['cpu_core_count'] = host_epa['cpu_core_count']
137 if 'cpu_core_thread_count' in host_epa:
138 host_epa_prop['cpu_core_thread_count'] = host_epa['cpu_core_thread_count']
139 if 'om_cpu_model_string' in host_epa:
140 host_epa_prop['om_cpu_model_string'] = host_epa['om_cpu_model_string']
141 if 'cpu_feature' in host_epa:
142 host_epa_prop['cpu_feature'] = []
143 for cpu_feature in host_epa['cpu_feature']:
144 cpu_feature_prop = {}
145 cpu_feature_prop['feature'] = cpu_feature['feature'].lower()
146 host_epa_prop['cpu_feature'] .append(cpu_feature_prop)
148 if 'om_cpu_feature' in host_epa:
149 host_epa_prop['om_cpu_feature'] = []
150 for cpu_feature in host_epa['om_cpu_feature']:
151 om_cpu_feature_prop = {}
152 om_cpu_feature_prop['feature'] = cpu_feature
153 host_epa_prop['om_cpu_feature'].append(om_cpu_feature_prop)
154 self.host_epa = host_epa
156 # We might have to re write this piece of code, there are mismatch in
157 # enum names. Its all capital in RIFT yang and TOSCA
158 if self
.VSWITCH_EPA
in vdu_dic
:
159 vswitch_epa
= vdu_dic
.pop(self
.VSWITCH_EPA
)
160 self
.vswitch_epa
= vswitch_epa
161 if self
.HYPERVISOR_EPA
in vdu_dic
:
162 hypervisor_epa
= vdu_dic
.pop(self
.HYPERVISOR_EPA
)
163 hypervisor_epa_prop
= {}
165 if 'type_yang' in hypervisor_epa
:
166 hypervisor_epa_prop
['type'] = hypervisor_epa
['type_yang']
167 if 'version' in hypervisor_epa
:
168 hypervisor_epa_prop
['version'] = str(hypervisor_epa
['version'])
170 hypervisor_epa_prop
['version'] = '1'
171 self
.hypervisor_epa
= hypervisor_epa_prop
173 if self
.GUEST_EPA
in vdu_dic
:
174 guest_epa
= vdu_dic
[self
.GUEST_EPA
]
177 # This is a hack. I have to rewrite this. I have got this quick to working
178 # 'ANY' check should be added in riftio common file. Its not working for some reason. Will fix.
180 if 'cpu_pinning_policy' in guest_epa
and guest_epa
['cpu_pinning_policy'] != 'ANY':
181 self
.pinning_epa_prop
['cpu_affinity'] = guest_epa
['cpu_pinning_policy'].lower()
182 if 'cpu_thread_pinning_policy' in guest_epa
:
183 self
.pinning_epa_prop
['thread_allocation'] = guest_epa
['cpu_thread_pinning_policy'].lower()
184 if 'mempage_size' in guest_epa
:
185 self
.mem_page_guest_epa
= self
.TOSCA_MEM_SIZE
[guest_epa
['mempage_size']]
187 if 'numa_node_policy' in guest_epa
:
188 num_node_policy
= guest_epa
['numa_node_policy']
189 if 'node_cnt' in num_node_policy
:
190 guest_epa_prop
['node_cnt'] = num_node_policy
['node_cnt']
191 if 'mem_policy' in num_node_policy
:
192 guest_epa_prop
['mem_policy'] = num_node_policy
['mem_policy']
193 if 'node' in num_node_policy
:
195 for node
in num_node_policy
['node']:
198 node_prop
['id'] = node
['id']
201 for vcp
in node
['vcpu']:
204 node_prop
['vcpus'] = vc
205 if 'memory_mb' in node
:
206 node_prop
['mem_size'] = "{} MB".format(node
['memory_mb'])
207 # om_numa_type generation
209 if 'num_cores' in node
:
210 node_prop
['om_numa_type'] = 'num_cores'
211 node_prop
['num_cores'] = node
['num_cores']
212 elif 'paired_threads' in node
:
213 node_prop
['om_numa_type'] = 'paired-threads'
214 node_prop
['paired_threads'] = node
['paired_threads']
215 elif 'threads]' in node
:
216 node_prop
['om_numa_type'] = 'threads]'
217 node_prop
['num_thread]'] = node
['threads]']
219 nodes
.append(node_prop
)
220 guest_epa_prop
['node'] = nodes
222 self
.guest_epa
= guest_epa_prop
224 self
.remove_ignored_fields(vdu_dic
)
227 self
.log
.warn(_("{0}, Did not process the following in "
229 format(self
, vdu_dic
))
231 self
.log
.debug(_("{0} VDU: {1}").format(self
, vdu
))
234 def get_cp(self
, name
):
235 for cp
in self
.ext_cp
:
236 if cp
[self
.NAME
] == name
:
240 def has_cp(self
, name
):
241 if self
.get_cp(name
):
245 def set_cp_type(self
, name
, cp_type
):
246 for idx
, cp
in enumerate(self
.ext_cp
):
247 if cp
[self
.NAME
] == name
:
248 cp
[self
.CP_TYPE
] = cp_type
249 self
.ext_cp
[idx
] = cp
250 self
.log
.debug(_("{0}, Updated CP: {1}").
251 format(self
, self
.ext_cp
[idx
]))
254 err_msg
= (_("{0}, Did not find connection point {1}").
256 self
.log
.error(err_msg
)
257 raise ValidationError(message
=err_msg
)
259 def set_vld(self
, name
, vld_name
):
260 cp
= self
.get_cp(name
)
262 cp
[self
.VLD
] = vld_name
264 err_msg
= (_("{0}, Did not find connection point {1}").
266 self
.log
.error(err_msg
)
267 raise ValidationError(message
=err_msg
)
269 def get_name(self
, vnf_name
):
270 # Create a unique name incase multiple VNFs use same
272 return "{}_{}".format(vnf_name
, self
.name
)
275 def generate_tosca_type(self
, tosca
):
276 self
.log
.debug(_("{0} Generate tosa types").
279 # Add custom artifact type
280 if self
.ARTIFACT_TYPES
not in tosca
:
281 tosca
[self
.ARTIFACT_TYPES
] = {}
282 if self
.T_ARTF_QCOW2
not in tosca
[self
.ARTIFACT_TYPES
]:
283 tosca
[self
.ARTIFACT_TYPES
][self
.T_ARTF_QCOW2
] = {
284 self
.DERIVED_FROM
: 'tosca.artifacts.Deployment.Image.VM.QCOW2',
286 {self
.TYPE
: self
.STRING
,
287 self
.REQUIRED
: self
.NO
},
290 if self
.T_VDU1
not in tosca
[self
.NODE_TYPES
]:
291 tosca
[self
.NODE_TYPES
][self
.T_VDU1
] = {
292 self
.DERIVED_FROM
: 'tosca.nodes.nfv.VDU',
295 {self
.TYPE
: self
.INTEGER
,
298 {self
.TYPE
: self
.STRING
,
299 self
.REQUIRED
: self
.NO
,},
300 self
.CLOUD_INIT_FILE
:
301 {self
.TYPE
: self
.STRING
,
302 self
.REQUIRED
: self
.NO
,},
306 self
.TYPE
: 'tosca.capabilities.nfv.VirtualLinkable'
312 if self
.T_CP1
not in tosca
[self
.NODE_TYPES
]:
313 tosca
[self
.NODE_TYPES
][self
.T_CP1
] = {
314 self
.DERIVED_FROM
: 'tosca.nodes.nfv.CP',
317 {self
.TYPE
: self
.STRING
,
318 self
.DESC
: 'Name of the connection point'},
320 {self
.TYPE
: self
.STRING
,
321 self
.DESC
: 'Type of the connection point'},
323 {self
.TYPE
: self
.STRING
,
324 self
.DESC
: 'Name of the interface on VDU'},
326 {self
.TYPE
: self
.STRING
,
327 self
.DESC
: 'Type of the interface on VDU'},
333 def generate_vdu_template(self
, tosca
, vnf_name
):
334 self
.log
.debug(_("{0} Generate tosca template for {2}").
335 format(self
, tosca
, vnf_name
))
337 name
= self
.get_name(vnf_name
)
340 node
[self
.TYPE
] = self
.T_VDU1
341 node
[self
.CAPABILITIES
] = {}
343 if self
.NFV_COMPUTE
in self
.props
:
344 node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
] = {self
.PROPERTIES
: self
.props
.pop(self
.NFV_COMPUTE
)}
346 self
.log
.warn(_("{0}, Does not have host requirements defined").
349 node
[self
.CAPABILITIES
][self
.HOST_EPA
] = {
350 self
.PROPERTIES
: self
.host_epa
353 node
[self
.CAPABILITIES
][self
.VSWITCH_EPA
] = {
354 self
.PROPERTIES
: self
.vswitch_epa
356 if self
.hypervisor_epa
:
357 node
[self
.CAPABILITIES
][self
.HYPERVISOR_EPA
] = {
358 self
.PROPERTIES
: self
.hypervisor_epa
361 node
[self
.CAPABILITIES
]['numa_extension'] = {
362 self
.PROPERTIES
: self
.guest_epa
364 if len(self
.pinning_epa_prop
) > 0:
365 if node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
] and node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
][self
.PROPERTIES
]:
366 node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
][self
.PROPERTIES
]['cpu_allocation'] = self
.pinning_epa_prop
367 if self
.mem_page_guest_epa
:
368 if node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
] and node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
][self
.PROPERTIES
]:
369 node
[self
.CAPABILITIES
][self
.NFV_COMPUTE
][self
.PROPERTIES
]['mem_page_size'] = self
.mem_page_guest_epa
371 if self
.IMAGE
in self
.props
:
372 img_name
= "{}_{}_vm_image".format(vnf_name
, self
.name
)
373 image
= "../{}/{}".format(self
.IMAGE_DIR
, self
.props
.pop(self
.IMAGE
))
375 node
[self
.ARTIFACTS
] = {img_name
: {
377 self
.TYPE
: self
.T_ARTF_QCOW2
,
379 if self
.IMAGE_CHKSUM
in self
.props
:
380 node
[self
.ARTIFACTS
][img_name
][self
.IMAGE_CHKSUM
] = \
381 self
.props
.pop(self
.IMAGE_CHKSUM
)
382 node
[self
.INTERFACES
] = {'Standard': {
385 # Add cloud init script if available
386 if self
.CLOUD_INIT_FILE
in self
.props
:
387 cloud_name
= "{}_{}_cloud_init".format(vnf_name
, self
.name
)
388 self
.cloud_init_file
= self
.props
[self
.CLOUD_INIT_FILE
]
389 cloud_init_file
= "../{}/{}".format(self
.CLOUD_INIT_DIR
, self
.props
.pop(self
.CLOUD_INIT_FILE
))
390 if self
.ARTIFACTS
in node
:
391 node
[self
.ARTIFACTS
][cloud_name
] = {
392 self
.FILE
: cloud_init_file
,
393 self
.TYPE
: self
.T_ARTF_CLOUD_INIT
,
396 node
[self
.ARTIFACTS
] = {
398 self
.FILE
: cloud_init_file
,
399 self
.TYPE
: self
.T_ARTF_CLOUD_INIT
,
403 self
.props
.pop(self
.ID
)
404 node
[self
.PROPERTIES
] = self
.props
406 self
.log
.debug(_("{0}, VDU node: {1}").format(self
, node
))
407 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][name
] = node
409 # Generate the connection point templates
410 for cp
in self
.ext_cp
:
411 cpt
= {self
.TYPE
: self
.T_CP1
}
413 cpt
[self
.REQUIREMENTS
] = []
414 cpt
[self
.REQUIREMENTS
].append({self
.VIRT_BIND
: {
415 self
.NODE
: self
.get_name(vnf_name
)
418 vld
= cp
.pop(self
.VLD
)
419 cpt
[self
.REQUIREMENTS
].append({self
.VIRT_LINK
: {
423 cpt
[self
.PROPERTIES
] = cp
424 cp_name
= cp
[self
.NAME
].replace('/', '_')
425 self
.cp_name_to_cp_node
[cp
[self
.NAME
]] = cp_name
427 self
.log
.debug(_("{0}, CP node {1}: {2}").
428 format(self
, cp_name
, cpt
))
429 tosca
[self
.TOPOLOGY_TMPL
][self
.NODE_TMPL
][cp_name
] = cpt
433 def get_supporting_files(self
):
436 if self
.image
is not None:
437 image_name
= os
.path
.basename(self
.image
)
441 self
.NAME
: image_name
,
442 self
.DEST
: "{}/{}".format(self
.IMAGE_DIR
, image_name
),
445 if self
.cloud_init_file
is not None:
447 self
.TYPE
: 'cloud_init',
448 self
.NAME
: self
.cloud_init_file
,
449 self
.DEST
: "{}/{}".format(self
.CLOUD_INIT
, self
.cloud_init_file
)
452 self
.log
.debug(_("Supporting files for {} : {}").format(self
, files
))