Adoption of base framework code for azure plugin
[multicloud/azure.git] / azure / azure / pub / utils / restcall.py
diff --git a/azure/azure/pub/utils/restcall.py b/azure/azure/pub/utils/restcall.py
new file mode 100644 (file)
index 0000000..4b28098
--- /dev/null
@@ -0,0 +1,782 @@
+# Copyright (c) 2018 Amdocs
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+
+import sys
+import traceback
+import logging
+import urllib2
+import uuid
+import httplib2
+import json
+
+from azure.pub.config.config import AAI_SCHEMA_VERSION
+from azure.pub.config.config import AAI_SERVICE_URL
+from azure.pub.config.config import AAI_USERNAME
+from azure.pub.config.config import AAI_PASSWORD
+from azure.pub.config.config import MSB_SERVICE_IP, MSB_SERVICE_PORT
+
+from azure.pub.exceptions import VimDriverAzureException
+
+rest_no_auth, rest_oneway_auth, rest_bothway_auth = 0, 1, 2
+HTTP_200_OK, HTTP_201_CREATED = '200', '201'
+HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED = '204', '202'
+status_ok_list = [HTTP_200_OK, HTTP_201_CREATED,
+                  HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED]
+HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN = '404', '403'
+HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUEST = '401', '400'
+
+logger = logging.getLogger(__name__)
+
+
+def call_req(base_url, user, passwd, auth_type, resource, method, content='',
+             headers=None):
+    callid = str(uuid.uuid1())
+#    logger.debug("[%s]call_req('%s','%s','%s',%s,'%s','%s','%s')" % (
+#        callid, base_url, user, passwd, auth_type, resource, method, content))
+    ret = None
+    resp_status = ''
+    resp = ""
+    full_url = ""
+
+    try:
+        full_url = combine_url(base_url, resource)
+        if headers is None:
+            headers = {}
+            headers['content-type'] = 'application/json'
+
+        if user:
+            headers['Authorization'] = 'Basic ' + \
+                ('%s:%s' % (user, passwd)).encode("base64")
+        ca_certs = None
+        for retry_times in range(3):
+            http = httplib2.Http(
+                ca_certs=ca_certs,
+                disable_ssl_certificate_validation=(
+                    auth_type == rest_no_auth))
+            http.follow_all_redirects = True
+            try:
+                logger.debug("request=%s" % full_url)
+                resp, resp_content = http.request(
+                    full_url, method=method.upper(), body=content,
+                    headers=headers)
+                resp_status = resp['status']
+                resp_body = resp_content.decode('UTF-8')
+
+                if resp_status in status_ok_list:
+                    ret = [0, resp_body, resp_status, resp]
+                else:
+                    ret = [1, resp_body, resp_status, resp]
+                break
+            except Exception as ex:
+                if 'httplib.ResponseNotReady' in str(sys.exc_info()):
+                    logger.error(traceback.format_exc())
+                    ret = [1, "Unable to connect to %s" % full_url,
+                           resp_status, resp]
+                    continue
+                raise ex
+    except urllib2.URLError as err:
+        ret = [2, str(err), resp_status, resp]
+    except Exception as ex:
+        logger.error(traceback.format_exc())
+        logger.error("[%s]ret=%s" % (callid, str(sys.exc_info())))
+        res_info = str(sys.exc_info())
+        if 'httplib.ResponseNotReady' in res_info:
+            res_info = ("The URL[%s] request failed or is not responding." %
+                        full_url)
+        ret = [3, res_info, resp_status, resp]
+#    logger.debug("[%s]ret=%s" % (callid, str(ret)))
+    return ret
+
+
+def req_by_msb(resource, method, content=''):
+    base_url = "http://%s:%s/" % (MSB_SERVICE_IP, MSB_SERVICE_PORT)
+    return call_req(base_url, "", "", rest_no_auth, resource, method, content)
+
+
+def combine_url(base_url, resource):
+    full_url = None
+    if base_url.endswith('/') and resource.startswith('/'):
+        full_url = base_url[:-1] + resource
+    elif base_url.endswith('/') and not resource.startswith('/'):
+        full_url = base_url + resource
+    elif not base_url.endswith('/') and resource.startswith('/'):
+        full_url = base_url + resource
+    else:
+        full_url = base_url + '/' + resource
+    return full_url
+
+
+def get_res_from_aai(resource, content=''):
+    headers = {
+        'X-FromAppId': 'MultiCloud',
+        'X-TransactionId': '9001',
+        'content-type': 'application/json',
+        'accept': 'application/json'
+    }
+    base_url = "%s/%s" % (AAI_SERVICE_URL, AAI_SCHEMA_VERSION)
+    return call_req(base_url, AAI_USERNAME, AAI_PASSWORD, rest_no_auth,
+                    resource, "GET", content, headers)
+
+
+class AAIClient(object):
+    def __init__(self, cloud_owner, cloud_region):
+        self.base_url = "%s/%s" % (AAI_SERVICE_URL, AAI_SCHEMA_VERSION)
+        self.username = AAI_USERNAME
+        self.password = AAI_PASSWORD
+        self.default_headers = {
+            'X-FromAppId': 'multicloud-openstack-vmware',
+            'X-TransactionId': '9004',
+            'content-type': 'application/json',
+            'accept': 'application/json'
+        }
+        self.cloud_owner = cloud_owner
+        self.cloud_region = cloud_region
+        self._vim_info = None
+
+    def get_vim(self, get_all=False):
+        resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
+                    "/%s/%s" % (self.cloud_owner, self.cloud_region))
+        if get_all:
+            resource = "%s?depth=all" % resource
+        resp = call_req(self.base_url, self.username, self.password,
+                        rest_no_auth, resource, "GET",
+                        headers=self.default_headers)
+        if resp[0] != 0:
+            raise VimDriverAzureException(
+                status_code=404,
+                content="Failed to query VIM with id (%s_%s) from extsys." % (
+                    self.cloud_owner, self.cloud_region))
+        return json.loads(resp[1])
+
+    def delete_vim(self):
+        resp = self.get_vim(get_all=True)
+        logger.debug('Delete tenants')
+        self._del_tenants(resp)
+        logger.debug('Delete images')
+        self._del_images(resp)
+        logger.debug('Delete flavors')
+        self._del_flavors(resp)
+        logger.debug('Delete networks')
+        self._del_networks(resp)
+        logger.debug('Delete availability zones')
+        self._del_azs(resp)
+        logger.debug('Delete cloud region')
+        resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
+                    "/%s/%s?resource-version=%s" %
+                    (self.cloud_owner, self.cloud_region,
+                     resp['resource-version']))
+        resp = call_req(self.base_url, self.username, self.password,
+                        rest_no_auth, resource, "DELETE",
+                        headers=self.default_headers)
+        if resp[0] != 0:
+            raise VimDriverAzureException(
+                status_code=400,
+                content="Failed to delete cloud %s_%s: %s." % (
+                    self.cloud_owner, self.cloud_region, resp[1]))
+
+    def update_vim(self, content):
+        # update identity url
+        self.update_identity_url()
+        # update tenants
+        self.add_tenants(content)
+        # update flavors
+        self.add_images(content)
+        # update images
+        self.add_flavors(content)
+        # update networks
+        self.add_networks(content)
+        # update pservers
+        self.add_pservers(content)
+
+    def update_identity_url(self):
+        vim = self.get_vim()
+        vim['identity-url'] = ("http://%s/api/multicloud/v0/%s_%s/identity/"
+                               "v3" % (MSB_SERVICE_IP, self.cloud_owner,
+                                       self.cloud_region))
+        resource = ("/cloud-infrastructure/cloud-regions/cloud-region"
+                    "/%s/%s" % (self.cloud_owner, self.cloud_region))
+        logger.debug("Updating identity url %s" % vim)
+        call_req(self.base_url, self.username, self.password,
+                 rest_no_auth, resource, "PUT",
+                 content=json.dumps(vim),
+                 headers=self.default_headers)
+
+    def add_tenants(self, content):
+        for tenant in content['tenants']:
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/tenants/tenant/%s" % (
+                            self.cloud_owner, self.cloud_region, tenant['id']))
+            body = {'tenant-name': tenant['name']}
+            logger.debug("Adding tenants to cloud region")
+            call_req(self.base_url, self.username, self.password,
+                     rest_no_auth, resource, "PUT",
+                     content=json.dumps(body),
+                     headers=self.default_headers)
+
+    def add_flavors(self, content):
+        for flavor in content['flavors']:
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/flavors/flavor/%s" % (
+                            self.cloud_owner, self.cloud_region, flavor['id']))
+            body = {
+                'flavor-name': flavor['name'],
+                'flavor-vcpus': flavor['vcpus'],
+                'flavor-ram': flavor['ram'],
+                'flavor-disk': flavor['disk'],
+                'flavor-ephemeral': flavor['ephemeral'],
+                'flavor-swap': flavor['swap'],
+                'flavor-is-public': flavor['is_public'],
+                'flavor-selflink': flavor['links'][0]['href'],
+                'flavor-disabled': flavor['is_disabled']
+            }
+            # Handle extra specs
+            if flavor['name'].startswith("onap."):
+                hpa_capabilities = self._get_hpa_capabilities(
+                    flavor)
+                body['hpa-capabilities'] = {
+                    'hpa-capability': hpa_capabilities}
+
+            logger.debug("Adding flavors to cloud region")
+            call_req(self.base_url, self.username, self.password,
+                     rest_no_auth, resource, "PUT",
+                     content=json.dumps(body),
+                     headers=self.default_headers)
+
+    def add_images(self, content):
+        for image in content['images']:
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/images/image/%s" % (
+                            self.cloud_owner, self.cloud_region, image['id']))
+            split_image_name = image['name'].split("-")
+            os_distro = split_image_name[0]
+            os_version = split_image_name[1] if \
+                len(split_image_name) > 1 else ""
+            body = {
+                'image-name': image['name'],
+                # 'image-architecture': image[''],
+                'image-os-distro': os_distro,
+                'image-os-version': os_version,
+                # 'application': image[''],
+                # 'application-vendor': image[''],
+                # 'application-version': image[''],
+                # TODO replace this with image proxy endpoint
+                'image-selflink': "",
+            }
+            logger.debug("Adding images to cloud region")
+            call_req(self.base_url, self.username, self.password,
+                     rest_no_auth, resource, "PUT",
+                     content=json.dumps(body),
+                     headers=self.default_headers)
+
+    def add_networks(self, content):
+        for network in content['networks']:
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/oam-networks/oam-network/%s" % (
+                            self.cloud_owner, self.cloud_region,
+                            network['id']))
+            body = {
+                'network-uuid': network['id'],
+                'network-name': network['name'],
+                'cvlan-tag': network['segmentationId'] or 0,
+            }
+            logger.debug("Adding networks to cloud region")
+            call_req(self.base_url, self.username, self.password,
+                     rest_no_auth, resource, "PUT",
+                     content=json.dumps(body),
+                     headers=self.default_headers)
+
+    def add_pservers(self, content):
+        for hypervisor in content['hypervisors']:
+            resource = ("/cloud-infrastructure/pservers/pserver/%s" % (
+                hypervisor['name']))
+            body = {
+                # 'ptnii-equip-name'
+                'number-of-cpus': hypervisor['vcpus'],
+                'disk-in-gigabytes': hypervisor['local_disk_size'],
+                'ram-in-megabytes': hypervisor['memory_size'],
+                # 'equip-type'
+                # 'equip-vendor'
+                # 'equip-model'
+                # 'fqdn'
+                # 'pserver-selflink'
+                'ipv4-oam-address': hypervisor['host_ip'],
+                # 'serial-number'
+                # 'ipaddress-v4-loopback-0'
+                # 'ipaddress-v6-loopback-0'
+                # 'ipaddress-v4-aim'
+                # 'ipaddress-v6-aim'
+                # 'ipaddress-v6-oam'
+                # 'inv-status'
+                'pserver-id': hypervisor['id'],
+                # 'internet-topology'
+            }
+            logger.debug("Adding pservers")
+            call_req(self.base_url, self.username, self.password,
+                     rest_no_auth, resource, "PUT",
+                     content=json.dumps(body),
+                     headers=self.default_headers)
+            # update relationship
+            resource = ("/cloud-infrastructure/pservers/pserver/%s/"
+                        "relationship-list/relationship" %
+                        hypervisor['name'])
+            related_link = ("%s/cloud-infrastructure/cloud-regions/"
+                            "cloud-region/%s/%s" % (
+                                self.base_url, self.cloud_owner,
+                                self.cloud_region))
+            body = {
+                'related-to': 'cloud-region',
+                'related-link': related_link,
+                'relationship-data': [
+                    {
+                        'relationship-key': 'cloud-region.cloud-owner',
+                        'relationship-value': self.cloud_owner
+                    },
+                    {
+                        'relationship-key': 'cloud-region.cloud-region-id',
+                        'relationship-value': self.cloud_region
+                    }
+                ]
+            }
+            logger.debug("Connecting pservers and cloud region")
+            call_req(self.base_url, self.username, self.password,
+                     rest_no_auth, resource, "PUT",
+                     content=json.dumps(body),
+                     headers=self.default_headers)
+
+    def _del_tenants(self, rsp):
+        tenants = rsp.get("tenants", [])
+        if not tenants:
+            return
+        for tenant in tenants["tenant"]:
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/tenants/tenant/%s?resource-version=%s" % (
+                            self.cloud_owner, self.cloud_region,
+                            tenant['tenant-id'], tenant['resource-version']))
+            resp = call_req(self.base_url, self.username, self.password,
+                            rest_no_auth, resource, "DELETE",
+                            headers=self.default_headers)
+            if resp[0] != 0:
+                raise VimDriverAzureException(
+                    status_code=400,
+                    content="Failed to delete tenant %s: %s." % (
+                        tenant['tenant-id'], resp[1]))
+
+    def _del_hpa(self, flavor):
+        hpas = flavor.get("hpa-capabilities", {}).get("hpa-capability", [])
+        for hpa in hpas:
+            resource = (
+                "/cloud-infrastructure/cloud-regions/cloud-region/"
+                "%s/%s/flavors/flavor/%s/hpa-capabilities/hpa-capability/%s"
+                "?resource-version=%s" % (
+                    self.cloud_owner, self.cloud_region,
+                    flavor['flavor-id'], hpa['hpa-capability-id'],
+                    hpa['resource-version']))
+            resp = call_req(self.base_url, self.username, self.password,
+                            rest_no_auth, resource, "DELETE",
+                            headers=self.default_headers)
+            if resp[0] != 0:
+                raise VimDriverAzureException(
+                    status_code=400,
+                    content="Failed to delete flavor %s on hpa %s: %s." % (
+                        flavor['flavor-id'], hpa['hpa-capability-id'],
+                        resp[1]))
+
+    def _del_flavors(self, rsp):
+        flavors = rsp.get("flavors", [])
+        if not flavors:
+            return
+        for flavor in flavors["flavor"]:
+            self._del_hpa(flavor)
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/flavors/flavor/%s?resource-version=%s" % (
+                            self.cloud_owner, self.cloud_region,
+                            flavor['flavor-id'], flavor['resource-version']))
+            resp = call_req(self.base_url, self.username, self.password,
+                            rest_no_auth, resource, "DELETE",
+                            headers=self.default_headers)
+            if resp[0] != 0:
+                raise VimDriverAzureException(
+                    status_code=400,
+                    content="Failed to delete flavor %s: %s." % (
+                        flavor['flavor-id'], resp[1]))
+
+    def _del_images(self, rsp):
+        tenants = rsp.get("images", [])
+        if not tenants:
+            return
+        for tenant in tenants["image"]:
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/images/image/%s?resource-version=%s" % (
+                            self.cloud_owner, self.cloud_region,
+                            tenant['image-id'], tenant['resource-version']))
+            resp = call_req(self.base_url, self.username, self.password,
+                            rest_no_auth, resource, "DELETE",
+                            headers=self.default_headers)
+            if resp[0] != 0:
+                raise VimDriverAzureException(
+                    status_code=400,
+                    content="Failed to delete image %s: %s." % (
+                        tenant['image-id'], resp[1]))
+
+    def _del_networks(self, rsp):
+        networks = rsp.get("oam-networks", [])
+        if not networks:
+            return
+        for network in networks["oam-network"]:
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/oam-networks/oam-network/%s?"
+                        "resource-version=%s" % (
+                            self.cloud_owner, self.cloud_region,
+                            network['network-uuid'],
+                            network['resource-version']))
+            resp = call_req(self.base_url, self.username, self.password,
+                            rest_no_auth, resource, "DELETE",
+                            headers=self.default_headers)
+            if resp[0] != 0:
+                raise VimDriverAzureException(
+                    status_code=400,
+                    content="Failed to delete network %s: %s." % (
+                        network['network-uuid'], resp[1]))
+
+    def _del_azs(self, rsp):
+        azs = rsp.get("availability-zones", [])
+        if not azs:
+            return
+        for az in azs["availability-zone"]:
+            resource = ("/cloud-infrastructure/cloud-regions/cloud-region/"
+                        "%s/%s/availability-zones/availability-zone/%s?"
+                        "resource-version=%s" % (
+                            self.cloud_owner, self.cloud_region,
+                            az['availability-zone-name'],
+                            az['resource-version']))
+            resp = call_req(self.base_url, self.username, self.password,
+                            rest_no_auth, resource, "DELETE",
+                            headers=self.default_headers)
+            if resp[0] != 0:
+                raise VimDriverAzureException(
+                    status_code=400,
+                    content="Failed to delete availability zone %s: %s." % (
+                        az['availability-zone-name'], resp[1]))
+
+    def _get_hpa_capabilities(self, flavor):
+        hpa_caps = []
+
+        # Basic capabilties
+        caps_dict = self._get_hpa_basic_capabilities(flavor)
+        if len(caps_dict) > 0:
+            logger.debug("basic_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        # cpupining capabilities
+        caps_dict = self._get_cpupinning_capabilities(flavor['extra_specs'])
+        if len(caps_dict) > 0:
+            logger.debug("cpupining_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        # cputopology capabilities
+        caps_dict = self._get_cputopology_capabilities(flavor['extra_specs'])
+        if len(caps_dict) > 0:
+            logger.debug("cputopology_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        # hugepages capabilities
+        caps_dict = self._get_hugepages_capabilities(flavor['extra_specs'])
+        if len(caps_dict) > 0:
+            logger.debug("hugepages_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        # numa capabilities
+        caps_dict = self._get_numa_capabilities(flavor['extra_specs'])
+        if len(caps_dict) > 0:
+            logger.debug("numa_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        # storage capabilities
+        caps_dict = self._get_storage_capabilities(flavor)
+        if len(caps_dict) > 0:
+            logger.debug("storage_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        # CPU instruction set extension capabilities
+        caps_dict = self._get_instruction_set_capabilities(
+            flavor['extra_specs'])
+        if len(caps_dict) > 0:
+            logger.debug("instruction_set_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        # PCI passthrough capabilities
+        caps_dict = self._get_pci_passthrough_capabilities(
+            flavor['extra_specs'])
+        if len(caps_dict) > 0:
+            logger.debug("pci_passthrough_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        # ovsdpdk capabilities
+        caps_dict = self._get_ovsdpdk_capabilities()
+        if len(caps_dict) > 0:
+            logger.debug("ovsdpdk_capabilities_info: %s" % caps_dict)
+            hpa_caps.append(caps_dict)
+
+        return hpa_caps
+
+    def _get_hpa_basic_capabilities(self, flavor):
+        basic_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        basic_capability['hpa-capability-id'] = str(feature_uuid)
+        basic_capability['hpa-feature'] = 'basicCapabilities'
+        basic_capability['architecture'] = 'generic'
+        basic_capability['hpa-version'] = 'v1'
+
+        basic_capability['hpa-feature-attributes'] = []
+        basic_capability['hpa-feature-attributes'].append({
+            'hpa-attribute-key': 'numVirtualCpu',
+            'hpa-attribute-value': json.dumps(
+                {'value': str(flavor['vcpus'])})})
+        basic_capability['hpa-feature-attributes'].append({
+            'hpa-attribute-key': 'virtualMemSize',
+            'hpa-attribute-value': json.dumps({'value': str(
+                flavor['ram']), 'unit': 'MB'})})
+
+        return basic_capability
+
+    def _get_cpupinning_capabilities(self, extra_specs):
+        cpupining_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        if (extra_specs.get('hw:cpu_policy') or
+                extra_specs.get('hw:cpu_thread_policy')):
+            cpupining_capability['hpa-capability-id'] = str(feature_uuid)
+            cpupining_capability['hpa-feature'] = 'cpuPinning'
+            cpupining_capability['architecture'] = 'generic'
+            cpupining_capability['hpa-version'] = 'v1'
+
+            cpupining_capability['hpa-feature-attributes'] = []
+            if extra_specs.get('hw:cpu_thread_policy'):
+                cpupining_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': 'logicalCpuThreadPinningPolicy',
+                    'hpa-attribute-value': json.dumps({'value': str(
+                        extra_specs['hw:cpu_thread_policy'])})})
+            if extra_specs.get('hw:cpu_policy'):
+                cpupining_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': 'logicalCpuPinningPolicy',
+                    'hpa-attribute-value': json.dumps({'value': str(
+                        extra_specs['hw:cpu_policy'])})})
+
+        return cpupining_capability
+
+    def _get_cputopology_capabilities(self, extra_specs):
+        cputopology_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        if (extra_specs.get('hw:cpu_sockets') or
+                extra_specs.get('hw:cpu_cores') or
+                extra_specs.get('hw:cpu_threads')):
+            cputopology_capability['hpa-capability-id'] = str(feature_uuid)
+            cputopology_capability['hpa-feature'] = 'cpuTopology'
+            cputopology_capability['architecture'] = 'generic'
+            cputopology_capability['hpa-version'] = 'v1'
+
+            cputopology_capability['hpa-feature-attributes'] = []
+            if extra_specs.get('hw:cpu_sockets'):
+                cputopology_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': 'numCpuSockets',
+                    'hpa-attribute-value': json.dumps({'value': str(
+                        extra_specs['hw:cpu_sockets'])})})
+            if extra_specs.get('hw:cpu_cores'):
+                cputopology_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': 'numCpuCores',
+                    'hpa-attribute-value': json.dumps({'value': str(
+                        extra_specs['hw:cpu_cores'])})})
+            if extra_specs.get('hw:cpu_threads'):
+                cputopology_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': 'numCpuThreads',
+                    'hpa-attribute-value': json.dumps({'value': str(
+                        extra_specs['hw:cpu_threads'])})})
+
+        return cputopology_capability
+
+    def _get_hugepages_capabilities(self, extra_specs):
+        hugepages_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        if extra_specs.get('hw:mem_page_size'):
+            hugepages_capability['hpa-capability-id'] = str(feature_uuid)
+            hugepages_capability['hpa-feature'] = 'hugePages'
+            hugepages_capability['architecture'] = 'generic'
+            hugepages_capability['hpa-version'] = 'v1'
+
+            hugepages_capability['hpa-feature-attributes'] = []
+            if extra_specs['hw:mem_page_size'] == 'large':
+                hugepages_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': 'memoryPageSize',
+                    'hpa-attribute-value': json.dumps(
+                        {'value': '2', 'unit': 'MB'})})
+            elif extra_specs['hw:mem_page_size'] == 'small':
+                hugepages_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': 'memoryPageSize',
+                    'hpa-attribute-value': json.dumps(
+                        {'value': '4', 'unit': 'KB'})})
+            elif extra_specs['hw:mem_page_size'] == 'any':
+                logger.info("Currently HPA feature memoryPageSize "
+                            "did not support 'any' page!!")
+            else:
+                hugepages_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': 'memoryPageSize',
+                    'hpa-attribute-value': json.dumps({'value': str(
+                        extra_specs['hw:mem_page_size']), 'unit': 'KB'})
+                    })
+
+        return hugepages_capability
+
+    def _get_numa_capabilities(self, extra_specs):
+        numa_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        if extra_specs.get('hw:numa_nodes'):
+            numa_capability['hpa-capability-id'] = str(feature_uuid)
+            numa_capability['hpa-feature'] = 'numa'
+            numa_capability['architecture'] = 'generic'
+            numa_capability['hpa-version'] = 'v1'
+
+            numa_capability['hpa-feature-attributes'] = []
+            numa_capability['hpa-feature-attributes'].append({
+                'hpa-attribute-key': 'numaNodes',
+                'hpa-attribute-value': json.dumps({'value': str(
+                    extra_specs['hw:numa_nodes'])})
+                })
+
+            for num in range(0, int(extra_specs['hw:numa_nodes'])):
+                numa_cpu_node = "hw:numa_cpus.%s" % num
+                numa_mem_node = "hw:numa_mem.%s" % num
+                numacpu_key = "numaCpu-%s" % num
+                numamem_key = "numaMem-%s" % num
+
+                if (extra_specs.get(numa_cpu_node) and
+                        extra_specs.get(numa_mem_node)):
+                    numa_capability['hpa-feature-attributes'].append({
+                        'hpa-attribute-key': numacpu_key,
+                        'hpa-attribute-value': json.dumps({'value': str(
+                            extra_specs[numa_cpu_node])})
+                        })
+                    numa_capability['hpa-feature-attributes'].append({
+                        'hpa-attribute-key': numamem_key,
+                        'hpa-attribute-value': json.dumps({'value': str(
+                            extra_specs[numa_mem_node]), 'unit': 'MB'})
+                        })
+
+        return numa_capability
+
+    def _get_storage_capabilities(self, flavor):
+        storage_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        storage_capability['hpa-capability-id'] = str(feature_uuid)
+        storage_capability['hpa-feature'] = 'localStorage'
+        storage_capability['architecture'] = 'generic'
+        storage_capability['hpa-version'] = 'v1'
+
+        storage_capability['hpa-feature-attributes'] = []
+        storage_capability['hpa-feature-attributes'].append({
+            'hpa-attribute-key': 'diskSize',
+            'hpa-attribute-value': json.dumps({'value': str(
+                flavor['disk']), 'unit': 'GB'})
+            })
+        storage_capability['hpa-feature-attributes'].append({
+            'hpa-attribute-key': 'swapMemSize',
+            'hpa-attribute-value': json.dumps({'value': str(
+                flavor.get('swap', 0)), 'unit': 'MB'})
+            })
+        storage_capability['hpa-feature-attributes'].append({
+            'hpa-attribute-key': 'ephemeralDiskSize',
+            'hpa-attribute-value': json.dumps({'value': str(
+                flavor.get('OS-FLV-EXT-DATA:ephemeral', 0)), 'unit': 'GB'})
+            })
+        return storage_capability
+
+    def _get_instruction_set_capabilities(self, extra_specs):
+        instruction_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        if extra_specs.get('hw:capabilities:cpu_info:features'):
+            instruction_capability['hpa-capability-id'] = str(feature_uuid)
+            instruction_capability['hpa-feature'] = 'instructionSetExtensions'
+            instruction_capability['architecture'] = 'Intel64'
+            instruction_capability['hpa-version'] = 'v1'
+
+            instruction_capability['hpa-feature-attributes'] = []
+            instruction_capability['hpa-feature-attributes'].append({
+                'hpa-attribute-key': 'instructionSetExtensions',
+                'hpa-attribute-value': json.dumps(
+                    {'value': extra_specs[
+                        'hw:capabilities:cpu_info:features']})
+                })
+        return instruction_capability
+
+    def _get_pci_passthrough_capabilities(self, extra_specs):
+        instruction_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        if extra_specs.get('pci_passthrough:alias'):
+            value1 = extra_specs['pci_passthrough:alias'].split(':')
+            value2 = value1[0].split('-')
+
+            instruction_capability['hpa-capability-id'] = str(feature_uuid)
+            instruction_capability['hpa-feature'] = 'pciePassthrough'
+            instruction_capability['architecture'] = str(value2[2])
+            instruction_capability['hpa-version'] = 'v1'
+
+            instruction_capability['hpa-feature-attributes'] = []
+            instruction_capability['hpa-feature-attributes'].append({
+                'hpa-attribute-key': 'pciCount',
+                'hpa-attribute-value': json.dumps({'value': value1[1]})
+                })
+            instruction_capability['hpa-feature-attributes'].append({
+                'hpa-attribute-key': 'pciVendorId',
+                'hpa-attribute-value': json.dumps({'value': value2[3]})
+                })
+            instruction_capability['hpa-feature-attributes'].append({
+                'hpa-attribute-key': 'pciDeviceId',
+                'hpa-attribute-value': json.dumps({'value': value2[4]})
+                })
+
+        return instruction_capability
+
+    def _get_ovsdpdk_capabilities(self):
+        ovsdpdk_capability = {}
+        feature_uuid = uuid.uuid4()
+
+        if not self._vim_info:
+            self._vim_info = self.get_vim(get_all=True)
+        cloud_extra_info_str = self._vim_info.get('cloud-extra-info')
+        if not isinstance(cloud_extra_info_str, dict):
+            try:
+                cloud_extra_info_str = json.loads(cloud_extra_info_str)
+            except Exception as ex:
+                logger.error("Can not convert cloud extra info %s %s" % (
+                             str(ex), cloud_extra_info_str))
+                return {}
+        if cloud_extra_info_str:
+            cloud_dpdk_info = cloud_extra_info_str.get("ovsDpdk")
+            if cloud_dpdk_info:
+                ovsdpdk_capability['hpa-capability-id'] = str(feature_uuid)
+                ovsdpdk_capability['hpa-feature'] = 'ovsDpdk'
+                ovsdpdk_capability['architecture'] = 'Intel64'
+                ovsdpdk_capability['hpa-version'] = 'v1'
+
+                ovsdpdk_capability['hpa-feature-attributes'] = []
+                ovsdpdk_capability['hpa-feature-attributes'].append({
+                    'hpa-attribute-key': str(cloud_dpdk_info.get("libname")),
+                    'hpa-attribute-value': json.dumps(
+                        {'value': cloud_dpdk_info.get("libversion")})
+                    })
+        return ovsdpdk_capability