1 # Copyright (c) 2018 Amdocs
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.
21 from azure.pub.config.config import AAI_SCHEMA_VERSION
22 from azure.pub.config.config import AAI_SERVICE_URL
23 from azure.pub.config.config import AAI_USERNAME
24 from azure.pub.config.config import AAI_PASSWORD
25 from azure.pub.config.config import MSB_SERVICE_IP, MSB_SERVICE_PORT
27 from azure.pub.exceptions import VimDriverAzureException
29 rest_no_auth, rest_oneway_auth, rest_bothway_auth = 0, 1, 2
30 HTTP_200_OK, HTTP_201_CREATED = '200', '201'
31 HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED = '204', '202'
32 status_ok_list = [HTTP_200_OK, HTTP_201_CREATED,
33 HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED]
34 HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN = '404', '403'
35 HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUEST = '401', '400'
37 logger = logging.getLogger(__name__)
40 def call_req(base_url, user, passwd, auth_type, resource, method, content='',
42 callid = str(uuid.uuid1())
43 # logger.debug("[%s]call_req('%s','%s','%s',%s,'%s','%s','%s')" % (
44 # callid, base_url, user, passwd, auth_type, resource, method, content))
51 full_url = combine_url(base_url, resource)
54 headers['content-type'] = 'application/json'
57 headers['Authorization'] = 'Basic ' + \
58 ('%s:%s' % (user, passwd)).encode("base64")
60 for retry_times in range(3):
63 disable_ssl_certificate_validation=(
64 auth_type == rest_no_auth))
65 http.follow_all_redirects = True
67 logger.debug("request=%s" % full_url)
68 resp, resp_content = http.request(
69 full_url, method=method.upper(), body=content,
71 resp_status = resp['status']
72 resp_body = resp_content.decode('UTF-8')
74 if resp_status in status_ok_list:
75 ret = [0, resp_body, resp_status, resp]
77 ret = [1, resp_body, resp_status, resp]
79 except Exception as ex:
80 if 'httplib.ResponseNotReady' in str(sys.exc_info()):
81 logger.error(traceback.format_exc())
82 ret = [1, "Unable to connect to %s" % full_url,
86 except urllib2.URLError as err:
87 ret = [2, str(err), resp_status, resp]
88 except Exception as ex:
89 logger.error(traceback.format_exc())
90 logger.error("[%s]ret=%s" % (callid, str(sys.exc_info())))
91 res_info = str(sys.exc_info())
92 if 'httplib.ResponseNotReady' in res_info:
93 res_info = ("The URL[%s] request failed or is not responding." %
95 ret = [3, res_info, resp_status, resp]
96 # logger.debug("[%s]ret=%s" % (callid, str(ret)))
100 def req_by_msb(resource, method, content=''):
101 base_url = "http://%s:%s/" % (MSB_SERVICE_IP, MSB_SERVICE_PORT)
102 return call_req(base_url, "", "", rest_no_auth, resource, method, content)
105 def combine_url(base_url, resource):
107 if base_url.endswith('/') and resource.startswith('/'):
108 full_url = base_url[:-1] + resource
109 elif base_url.endswith('/') and not resource.startswith('/'):
110 full_url = base_url + resource
111 elif not base_url.endswith('/') and resource.startswith('/'):
112 full_url = base_url + resource
114 full_url = base_url + '/' + resource
118 def get_res_from_aai(resource, content=''):
120 'X-FromAppId': 'MultiCloud',
121 'X-TransactionId': '9001',
122 'content-type': 'application/json',
123 'accept': 'application/json'
125 base_url = "%s/%s" % (AAI_SERVICE_URL, AAI_SCHEMA_VERSION)
126 return call_req(base_url, AAI_USERNAME, AAI_PASSWORD, rest_no_auth,
127 resource, "GET", content, headers)
130 class AAIClient(object):
131 def __init__(self, cloud_owner, cloud_region):
132 self.base_url = "%s/%s" % (AAI_SERVICE_URL, AAI_SCHEMA_VERSION)
133 self.username = AAI_USERNAME
134 self.password = AAI_PASSWORD
135 self.default_headers = {
136 'X-FromAppId': 'multicloud-openstack-vmware',
137 'X-TransactionId': '9004',
138 'content-type': 'application/json',
139 'accept': 'application/json'
141 self.cloud_owner = cloud_owner
142 self.cloud_region = cloud_region
143 self._vim_info = None
145 def get_vim(self, get_all=False):
146 resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
147 "/%s/%s" % (self.cloud_owner, self.cloud_region))
149 resource = "%s?depth=all" % resource
150 resp = call_req(self.base_url, self.username, self.password,
151 rest_no_auth, resource, "GET",
152 headers=self.default_headers)
154 raise VimDriverAzureException(
156 content="Failed to query VIM with id (%s_%s) from extsys." % (
157 self.cloud_owner, self.cloud_region))
158 return json.loads(resp[1])
160 def delete_vim(self):
161 resp = self.get_vim(get_all=True)
162 logger.debug('Delete tenants')
163 self._del_tenants(resp)
164 logger.debug('Delete images')
165 self._del_images(resp)
166 logger.debug('Delete flavors')
167 self._del_flavors(resp)
168 logger.debug('Delete networks')
169 self._del_networks(resp)
170 logger.debug('Delete availability zones')
172 logger.debug('Delete cloud region')
173 resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
174 "/%s/%s?resource-version=%s" %
175 (self.cloud_owner, self.cloud_region,
176 resp['resource-version']))
177 resp = call_req(self.base_url, self.username, self.password,
178 rest_no_auth, resource, "DELETE",
179 headers=self.default_headers)
181 raise VimDriverAzureException(
183 content="Failed to delete cloud %s_%s: %s." % (
184 self.cloud_owner, self.cloud_region, resp[1]))
186 def update_vim(self, content):
187 # update identity url
188 self.update_identity_url()
190 self.add_tenants(content)
192 self.add_images(content)
194 self.add_flavors(content)
196 self.add_networks(content)
198 self.add_pservers(content)
200 def update_identity_url(self):
202 vim['identity-url'] = ("http://%s/api/multicloud/v0/%s_%s/identity/"
203 "v3" % (MSB_SERVICE_IP, self.cloud_owner,
205 resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
206 "/%s/%s" % (self.cloud_owner, self.cloud_region))
207 logger.debug("Updating identity url %s" % vim)
208 call_req(self.base_url, self.username, self.password,
209 rest_no_auth, resource, "PUT",
210 content=json.dumps(vim),
211 headers=self.default_headers)
213 def add_tenants(self, content):
214 for tenant in content['tenants']:
215 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
216 "%s/%s/tenants/tenant/%s" % (
217 self.cloud_owner, self.cloud_region, tenant['id']))
218 body = {'tenant-name': tenant['name']}
219 logger.debug("Adding tenants to cloud region")
220 call_req(self.base_url, self.username, self.password,
221 rest_no_auth, resource, "PUT",
222 content=json.dumps(body),
223 headers=self.default_headers)
225 def add_flavors(self, content):
226 for flavor in content['flavors']:
227 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
228 "%s/%s/flavors/flavor/%s" % (
229 self.cloud_owner, self.cloud_region, flavor['id']))
231 'flavor-name': flavor['name'],
232 'flavor-vcpus': flavor['vcpus'],
233 'flavor-ram': flavor['ram'],
234 'flavor-disk': flavor['disk'],
235 'flavor-ephemeral': flavor['ephemeral'],
236 'flavor-swap': flavor['swap'],
237 'flavor-is-public': flavor['is_public'],
238 'flavor-selflink': flavor['links'][0]['href'],
239 'flavor-disabled': flavor['is_disabled']
242 if flavor['name'].startswith("onap."):
243 hpa_capabilities = self._get_hpa_capabilities(
245 body['hpa-capabilities'] = {
246 'hpa-capability': hpa_capabilities}
248 logger.debug("Adding flavors to cloud region")
249 call_req(self.base_url, self.username, self.password,
250 rest_no_auth, resource, "PUT",
251 content=json.dumps(body),
252 headers=self.default_headers)
254 def add_images(self, content):
255 for image in content['images']:
256 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
257 "%s/%s/images/image/%s" % (
258 self.cloud_owner, self.cloud_region, image['id']))
259 split_image_name = image['name'].split("-")
260 os_distro = split_image_name[0]
261 os_version = split_image_name[1] if \
262 len(split_image_name) > 1 else ""
264 'image-name': image['name'],
265 # 'image-architecture': image[''],
266 'image-os-distro': os_distro,
267 'image-os-version': os_version,
268 # 'application': image[''],
269 # 'application-vendor': image[''],
270 # 'application-version': image[''],
271 # TODO replace this with image proxy endpoint
272 'image-selflink': "",
274 logger.debug("Adding images to cloud region")
275 call_req(self.base_url, self.username, self.password,
276 rest_no_auth, resource, "PUT",
277 content=json.dumps(body),
278 headers=self.default_headers)
280 def add_networks(self, content):
281 for network in content['networks']:
282 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
283 "%s/%s/oam-networks/oam-network/%s" % (
284 self.cloud_owner, self.cloud_region,
287 'network-uuid': network['id'],
288 'network-name': network['name'],
289 'cvlan-tag': network['segmentationId'] or 0,
291 logger.debug("Adding networks to cloud region")
292 call_req(self.base_url, self.username, self.password,
293 rest_no_auth, resource, "PUT",
294 content=json.dumps(body),
295 headers=self.default_headers)
297 def add_pservers(self, content):
298 for hypervisor in content['hypervisors']:
299 resource = ("/cloud-infrastructure/pservers/pserver/%s" % (
303 'number-of-cpus': hypervisor['vcpus'],
304 'disk-in-gigabytes': hypervisor['local_disk_size'],
305 'ram-in-megabytes': hypervisor['memory_size'],
311 'ipv4-oam-address': hypervisor['host_ip'],
313 # 'ipaddress-v4-loopback-0'
314 # 'ipaddress-v6-loopback-0'
319 'pserver-id': hypervisor['id'],
320 # 'internet-topology'
322 logger.debug("Adding pservers")
323 call_req(self.base_url, self.username, self.password,
324 rest_no_auth, resource, "PUT",
325 content=json.dumps(body),
326 headers=self.default_headers)
327 # update relationship
328 resource = ("/cloud-infrastructure/pservers/pserver/%s/"
329 "relationship-list/relationship" %
331 related_link = ("%s/cloud-infrastructure/cloud-regions/"
332 "cloud-region/%s/%s" % (
333 self.base_url, self.cloud_owner,
336 'related-to': 'cloud-region',
337 'related-link': related_link,
338 'relationship-data': [
340 'relationship-key': 'cloud-region.cloud-owner',
341 'relationship-value': self.cloud_owner
344 'relationship-key': 'cloud-region.cloud-region-id',
345 'relationship-value': self.cloud_region
349 logger.debug("Connecting pservers and cloud region")
350 call_req(self.base_url, self.username, self.password,
351 rest_no_auth, resource, "PUT",
352 content=json.dumps(body),
353 headers=self.default_headers)
355 def _del_tenants(self, rsp):
356 tenants = rsp.get("tenants", [])
359 for tenant in tenants["tenant"]:
360 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
361 "%s/%s/tenants/tenant/%s?resource-version=%s" % (
362 self.cloud_owner, self.cloud_region,
363 tenant['tenant-id'], tenant['resource-version']))
364 resp = call_req(self.base_url, self.username, self.password,
365 rest_no_auth, resource, "DELETE",
366 headers=self.default_headers)
368 raise VimDriverAzureException(
370 content="Failed to delete tenant %s: %s." % (
371 tenant['tenant-id'], resp[1]))
373 def _del_hpa(self, flavor):
374 hpas = flavor.get("hpa-capabilities", {}).get("hpa-capability", [])
377 "/cloud-infrastructure/cloud-regions/cloud-region/"
378 "%s/%s/flavors/flavor/%s/hpa-capabilities/hpa-capability/%s"
379 "?resource-version=%s" % (
380 self.cloud_owner, self.cloud_region,
381 flavor['flavor-id'], hpa['hpa-capability-id'],
382 hpa['resource-version']))
383 resp = call_req(self.base_url, self.username, self.password,
384 rest_no_auth, resource, "DELETE",
385 headers=self.default_headers)
387 raise VimDriverAzureException(
389 content="Failed to delete flavor %s on hpa %s: %s." % (
390 flavor['flavor-id'], hpa['hpa-capability-id'],
393 def _del_flavors(self, rsp):
394 flavors = rsp.get("flavors", [])
397 for flavor in flavors["flavor"]:
398 self._del_hpa(flavor)
399 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
400 "%s/%s/flavors/flavor/%s?resource-version=%s" % (
401 self.cloud_owner, self.cloud_region,
402 flavor['flavor-id'], flavor['resource-version']))
403 resp = call_req(self.base_url, self.username, self.password,
404 rest_no_auth, resource, "DELETE",
405 headers=self.default_headers)
407 raise VimDriverAzureException(
409 content="Failed to delete flavor %s: %s." % (
410 flavor['flavor-id'], resp[1]))
412 def _del_images(self, rsp):
413 tenants = rsp.get("images", [])
416 for tenant in tenants["image"]:
417 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
418 "%s/%s/images/image/%s?resource-version=%s" % (
419 self.cloud_owner, self.cloud_region,
420 tenant['image-id'], tenant['resource-version']))
421 resp = call_req(self.base_url, self.username, self.password,
422 rest_no_auth, resource, "DELETE",
423 headers=self.default_headers)
425 raise VimDriverAzureException(
427 content="Failed to delete image %s: %s." % (
428 tenant['image-id'], resp[1]))
430 def _del_networks(self, rsp):
431 networks = rsp.get("oam-networks", [])
434 for network in networks["oam-network"]:
435 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
436 "%s/%s/oam-networks/oam-network/%s?"
437 "resource-version=%s" % (
438 self.cloud_owner, self.cloud_region,
439 network['network-uuid'],
440 network['resource-version']))
441 resp = call_req(self.base_url, self.username, self.password,
442 rest_no_auth, resource, "DELETE",
443 headers=self.default_headers)
445 raise VimDriverAzureException(
447 content="Failed to delete network %s: %s." % (
448 network['network-uuid'], resp[1]))
450 def _del_azs(self, rsp):
451 azs = rsp.get("availability-zones", [])
454 for az in azs["availability-zone"]:
455 resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
456 "%s/%s/availability-zones/availability-zone/%s?"
457 "resource-version=%s" % (
458 self.cloud_owner, self.cloud_region,
459 az['availability-zone-name'],
460 az['resource-version']))
461 resp = call_req(self.base_url, self.username, self.password,
462 rest_no_auth, resource, "DELETE",
463 headers=self.default_headers)
465 raise VimDriverAzureException(
467 content="Failed to delete availability zone %s: %s." % (
468 az['availability-zone-name'], resp[1]))
470 def _get_hpa_capabilities(self, flavor):
474 caps_dict = self._get_hpa_basic_capabilities(flavor)
475 if len(caps_dict) > 0:
476 logger.debug("basic_capabilities_info: %s" % caps_dict)
477 hpa_caps.append(caps_dict)
479 # cpupining capabilities
480 caps_dict = self._get_cpupinning_capabilities(flavor['extra_specs'])
481 if len(caps_dict) > 0:
482 logger.debug("cpupining_capabilities_info: %s" % caps_dict)
483 hpa_caps.append(caps_dict)
485 # cputopology capabilities
486 caps_dict = self._get_cputopology_capabilities(flavor['extra_specs'])
487 if len(caps_dict) > 0:
488 logger.debug("cputopology_capabilities_info: %s" % caps_dict)
489 hpa_caps.append(caps_dict)
491 # hugepages capabilities
492 caps_dict = self._get_hugepages_capabilities(flavor['extra_specs'])
493 if len(caps_dict) > 0:
494 logger.debug("hugepages_capabilities_info: %s" % caps_dict)
495 hpa_caps.append(caps_dict)
498 caps_dict = self._get_numa_capabilities(flavor['extra_specs'])
499 if len(caps_dict) > 0:
500 logger.debug("numa_capabilities_info: %s" % caps_dict)
501 hpa_caps.append(caps_dict)
503 # storage capabilities
504 caps_dict = self._get_storage_capabilities(flavor)
505 if len(caps_dict) > 0:
506 logger.debug("storage_capabilities_info: %s" % caps_dict)
507 hpa_caps.append(caps_dict)
509 # CPU instruction set extension capabilities
510 caps_dict = self._get_instruction_set_capabilities(
511 flavor['extra_specs'])
512 if len(caps_dict) > 0:
513 logger.debug("instruction_set_capabilities_info: %s" % caps_dict)
514 hpa_caps.append(caps_dict)
516 # PCI passthrough capabilities
517 caps_dict = self._get_pci_passthrough_capabilities(
518 flavor['extra_specs'])
519 if len(caps_dict) > 0:
520 logger.debug("pci_passthrough_capabilities_info: %s" % caps_dict)
521 hpa_caps.append(caps_dict)
523 # ovsdpdk capabilities
524 caps_dict = self._get_ovsdpdk_capabilities()
525 if len(caps_dict) > 0:
526 logger.debug("ovsdpdk_capabilities_info: %s" % caps_dict)
527 hpa_caps.append(caps_dict)
531 def _get_hpa_basic_capabilities(self, flavor):
532 basic_capability = {}
533 feature_uuid = uuid.uuid4()
535 basic_capability['hpa-capability-id'] = str(feature_uuid)
536 basic_capability['hpa-feature'] = 'basicCapabilities'
537 basic_capability['architecture'] = 'generic'
538 basic_capability['hpa-version'] = 'v1'
540 basic_capability['hpa-feature-attributes'] = []
541 basic_capability['hpa-feature-attributes'].append({
542 'hpa-attribute-key': 'numVirtualCpu',
543 'hpa-attribute-value': json.dumps(
544 {'value': str(flavor['vcpus'])})})
545 basic_capability['hpa-feature-attributes'].append({
546 'hpa-attribute-key': 'virtualMemSize',
547 'hpa-attribute-value': json.dumps({'value': str(
548 flavor['ram']), 'unit': 'MB'})})
550 return basic_capability
552 def _get_cpupinning_capabilities(self, extra_specs):
553 cpupining_capability = {}
554 feature_uuid = uuid.uuid4()
556 if (extra_specs.get('hw:cpu_policy') or
557 extra_specs.get('hw:cpu_thread_policy')):
558 cpupining_capability['hpa-capability-id'] = str(feature_uuid)
559 cpupining_capability['hpa-feature'] = 'cpuPinning'
560 cpupining_capability['architecture'] = 'generic'
561 cpupining_capability['hpa-version'] = 'v1'
563 cpupining_capability['hpa-feature-attributes'] = []
564 if extra_specs.get('hw:cpu_thread_policy'):
565 cpupining_capability['hpa-feature-attributes'].append({
566 'hpa-attribute-key': 'logicalCpuThreadPinningPolicy',
567 'hpa-attribute-value': json.dumps({'value': str(
568 extra_specs['hw:cpu_thread_policy'])})})
569 if extra_specs.get('hw:cpu_policy'):
570 cpupining_capability['hpa-feature-attributes'].append({
571 'hpa-attribute-key': 'logicalCpuPinningPolicy',
572 'hpa-attribute-value': json.dumps({'value': str(
573 extra_specs['hw:cpu_policy'])})})
575 return cpupining_capability
577 def _get_cputopology_capabilities(self, extra_specs):
578 cputopology_capability = {}
579 feature_uuid = uuid.uuid4()
581 if (extra_specs.get('hw:cpu_sockets') or
582 extra_specs.get('hw:cpu_cores') or
583 extra_specs.get('hw:cpu_threads')):
584 cputopology_capability['hpa-capability-id'] = str(feature_uuid)
585 cputopology_capability['hpa-feature'] = 'cpuTopology'
586 cputopology_capability['architecture'] = 'generic'
587 cputopology_capability['hpa-version'] = 'v1'
589 cputopology_capability['hpa-feature-attributes'] = []
590 if extra_specs.get('hw:cpu_sockets'):
591 cputopology_capability['hpa-feature-attributes'].append({
592 'hpa-attribute-key': 'numCpuSockets',
593 'hpa-attribute-value': json.dumps({'value': str(
594 extra_specs['hw:cpu_sockets'])})})
595 if extra_specs.get('hw:cpu_cores'):
596 cputopology_capability['hpa-feature-attributes'].append({
597 'hpa-attribute-key': 'numCpuCores',
598 'hpa-attribute-value': json.dumps({'value': str(
599 extra_specs['hw:cpu_cores'])})})
600 if extra_specs.get('hw:cpu_threads'):
601 cputopology_capability['hpa-feature-attributes'].append({
602 'hpa-attribute-key': 'numCpuThreads',
603 'hpa-attribute-value': json.dumps({'value': str(
604 extra_specs['hw:cpu_threads'])})})
606 return cputopology_capability
608 def _get_hugepages_capabilities(self, extra_specs):
609 hugepages_capability = {}
610 feature_uuid = uuid.uuid4()
612 if extra_specs.get('hw:mem_page_size'):
613 hugepages_capability['hpa-capability-id'] = str(feature_uuid)
614 hugepages_capability['hpa-feature'] = 'hugePages'
615 hugepages_capability['architecture'] = 'generic'
616 hugepages_capability['hpa-version'] = 'v1'
618 hugepages_capability['hpa-feature-attributes'] = []
619 if extra_specs['hw:mem_page_size'] == 'large':
620 hugepages_capability['hpa-feature-attributes'].append({
621 'hpa-attribute-key': 'memoryPageSize',
622 'hpa-attribute-value': json.dumps(
623 {'value': '2', 'unit': 'MB'})})
624 elif extra_specs['hw:mem_page_size'] == 'small':
625 hugepages_capability['hpa-feature-attributes'].append({
626 'hpa-attribute-key': 'memoryPageSize',
627 'hpa-attribute-value': json.dumps(
628 {'value': '4', 'unit': 'KB'})})
629 elif extra_specs['hw:mem_page_size'] == 'any':
630 logger.info("Currently HPA feature memoryPageSize "
631 "did not support 'any' page!!")
633 hugepages_capability['hpa-feature-attributes'].append({
634 'hpa-attribute-key': 'memoryPageSize',
635 'hpa-attribute-value': json.dumps({'value': str(
636 extra_specs['hw:mem_page_size']), 'unit': 'KB'})
639 return hugepages_capability
641 def _get_numa_capabilities(self, extra_specs):
643 feature_uuid = uuid.uuid4()
645 if extra_specs.get('hw:numa_nodes'):
646 numa_capability['hpa-capability-id'] = str(feature_uuid)
647 numa_capability['hpa-feature'] = 'numa'
648 numa_capability['architecture'] = 'generic'
649 numa_capability['hpa-version'] = 'v1'
651 numa_capability['hpa-feature-attributes'] = []
652 numa_capability['hpa-feature-attributes'].append({
653 'hpa-attribute-key': 'numaNodes',
654 'hpa-attribute-value': json.dumps({'value': str(
655 extra_specs['hw:numa_nodes'])})
658 for num in range(0, int(extra_specs['hw:numa_nodes'])):
659 numa_cpu_node = "hw:numa_cpus.%s" % num
660 numa_mem_node = "hw:numa_mem.%s" % num
661 numacpu_key = "numaCpu-%s" % num
662 numamem_key = "numaMem-%s" % num
664 if (extra_specs.get(numa_cpu_node) and
665 extra_specs.get(numa_mem_node)):
666 numa_capability['hpa-feature-attributes'].append({
667 'hpa-attribute-key': numacpu_key,
668 'hpa-attribute-value': json.dumps({'value': str(
669 extra_specs[numa_cpu_node])})
671 numa_capability['hpa-feature-attributes'].append({
672 'hpa-attribute-key': numamem_key,
673 'hpa-attribute-value': json.dumps({'value': str(
674 extra_specs[numa_mem_node]), 'unit': 'MB'})
677 return numa_capability
679 def _get_storage_capabilities(self, flavor):
680 storage_capability = {}
681 feature_uuid = uuid.uuid4()
683 storage_capability['hpa-capability-id'] = str(feature_uuid)
684 storage_capability['hpa-feature'] = 'localStorage'
685 storage_capability['architecture'] = 'generic'
686 storage_capability['hpa-version'] = 'v1'
688 storage_capability['hpa-feature-attributes'] = []
689 storage_capability['hpa-feature-attributes'].append({
690 'hpa-attribute-key': 'diskSize',
691 'hpa-attribute-value': json.dumps({'value': str(
692 flavor['disk']), 'unit': 'GB'})
694 storage_capability['hpa-feature-attributes'].append({
695 'hpa-attribute-key': 'swapMemSize',
696 'hpa-attribute-value': json.dumps({'value': str(
697 flavor.get('swap', 0)), 'unit': 'MB'})
699 storage_capability['hpa-feature-attributes'].append({
700 'hpa-attribute-key': 'ephemeralDiskSize',
701 'hpa-attribute-value': json.dumps({'value': str(
702 flavor.get('OS-FLV-EXT-DATA:ephemeral', 0)), 'unit': 'GB'})
704 return storage_capability
706 def _get_instruction_set_capabilities(self, extra_specs):
707 instruction_capability = {}
708 feature_uuid = uuid.uuid4()
710 if extra_specs.get('hw:capabilities:cpu_info:features'):
711 instruction_capability['hpa-capability-id'] = str(feature_uuid)
712 instruction_capability['hpa-feature'] = 'instructionSetExtensions'
713 instruction_capability['architecture'] = 'Intel64'
714 instruction_capability['hpa-version'] = 'v1'
716 instruction_capability['hpa-feature-attributes'] = []
717 instruction_capability['hpa-feature-attributes'].append({
718 'hpa-attribute-key': 'instructionSetExtensions',
719 'hpa-attribute-value': json.dumps(
720 {'value': extra_specs[
721 'hw:capabilities:cpu_info:features']})
723 return instruction_capability
725 def _get_pci_passthrough_capabilities(self, extra_specs):
726 instruction_capability = {}
727 feature_uuid = uuid.uuid4()
729 if extra_specs.get('pci_passthrough:alias'):
730 value1 = extra_specs['pci_passthrough:alias'].split(':')
731 value2 = value1[0].split('-')
733 instruction_capability['hpa-capability-id'] = str(feature_uuid)
734 instruction_capability['hpa-feature'] = 'pciePassthrough'
735 instruction_capability['architecture'] = str(value2[2])
736 instruction_capability['hpa-version'] = 'v1'
738 instruction_capability['hpa-feature-attributes'] = []
739 instruction_capability['hpa-feature-attributes'].append({
740 'hpa-attribute-key': 'pciCount',
741 'hpa-attribute-value': json.dumps({'value': value1[1]})
743 instruction_capability['hpa-feature-attributes'].append({
744 'hpa-attribute-key': 'pciVendorId',
745 'hpa-attribute-value': json.dumps({'value': value2[3]})
747 instruction_capability['hpa-feature-attributes'].append({
748 'hpa-attribute-key': 'pciDeviceId',
749 'hpa-attribute-value': json.dumps({'value': value2[4]})
752 return instruction_capability
754 def _get_ovsdpdk_capabilities(self):
755 ovsdpdk_capability = {}
756 feature_uuid = uuid.uuid4()
758 if not self._vim_info:
759 self._vim_info = self.get_vim(get_all=True)
760 cloud_extra_info_str = self._vim_info.get('cloud-extra-info')
761 if not isinstance(cloud_extra_info_str, dict):
763 cloud_extra_info_str = json.loads(cloud_extra_info_str)
764 except Exception as ex:
765 logger.error("Can not convert cloud extra info %s %s" % (
766 str(ex), cloud_extra_info_str))
768 if cloud_extra_info_str:
769 cloud_dpdk_info = cloud_extra_info_str.get("ovsDpdk")
771 ovsdpdk_capability['hpa-capability-id'] = str(feature_uuid)
772 ovsdpdk_capability['hpa-feature'] = 'ovsDpdk'
773 ovsdpdk_capability['architecture'] = 'Intel64'
774 ovsdpdk_capability['hpa-version'] = 'v1'
776 ovsdpdk_capability['hpa-feature-attributes'] = []
777 ovsdpdk_capability['hpa-feature-attributes'].append({
778 'hpa-attribute-key': str(cloud_dpdk_info.get("libname")),
779 'hpa-attribute-value': json.dumps(
780 {'value': cloud_dpdk_info.get("libversion")})
782 return ovsdpdk_capability