From 3176e57da097f2c238b134f18b1a4af00e105546 Mon Sep 17 00:00:00 2001 From: Bin Yang Date: Mon, 1 Apr 2019 09:46:31 +0000 Subject: Update capacity check API Query the AZinfo from the cache Relay on the background thread of AZcap Auditing Change-Id: I064dbc22c71ef25683145ef1c96274ce6ac74c3b Issue-ID: MULTICLOUD-542 Signed-off-by: Bin Yang --- share/newton_base/resource/capacity.py | 183 +++++++++++----------- share/starlingx_base/registration/registration.py | 6 +- share/starlingx_base/resource/__init__.py | 10 ++ share/starlingx_base/resource/capacity.py | 97 ++++++++++++ 4 files changed, 204 insertions(+), 92 deletions(-) create mode 100644 share/starlingx_base/resource/__init__.py create mode 100644 share/starlingx_base/resource/capacity.py (limited to 'share') diff --git a/share/newton_base/resource/capacity.py b/share/newton_base/resource/capacity.py index 1a08b9a5..c2fee361 100644 --- a/share/newton_base/resource/capacity.py +++ b/share/newton_base/resource/capacity.py @@ -35,112 +35,113 @@ class CapacityCheck(APIView): self._logger.info("vimid, data> %s, %s" % (vimid, request.data)) self._logger.debug("META> %s" % request.META) - hasEnoughResource = False try: - resource_demand = request.data - - tenant_name = None - vim = VimDriverUtils.get_vim_info(vimid) - sess = VimDriverUtils.get_session(vim, tenant_name) - - # get token: - cloud_owner, regionid = extsys.decode_vim_id(vimid) - interface = 'public' - service = {'service_type': 'compute', - 'interface': interface, - 'region_name': vim['openstack_region_id'] - if vim.get('openstack_region_id') - else vim['cloud_region_id'] - } - - # get limit for this tenant - req_resouce = "/limits" - self._logger.info("check limits> URI:%s" % req_resouce) - resp = sess.get(req_resouce, endpoint_filter=service) - self._logger.info("check limits> status:%s" % resp.status_code) - content = resp.json() - compute_limits = content['limits']['absolute'] - self._logger.debug("check limits> resp data:%s" % content) - - # get total resource of this cloud region - try: - req_resouce = "/os-hypervisors/statistics" - self._logger.info("check os-hypervisors statistics> URI:%s" % req_resouce) - resp = sess.get(req_resouce, endpoint_filter=service) - self._logger.info("check os-hypervisors statistics> status:%s" % resp.status_code) - content = resp.json() - hypervisor_statistics = content['hypervisor_statistics'] - self._logger.debug("check os-hypervisors statistics> resp data:%s" % content) - except HttpError as e: - if e.http_status == status.HTTP_403_FORBIDDEN: - # Due to non administrator account cannot get hypervisor data, - # so construct enough resource data - conVCPUS = int(resource_demand['vCPU']) - conFreeRamMB = int(resource_demand['Memory']) - conFreeDiskGB = int(resource_demand['Storage']) - self._logger.info("Non administator forbidden to access hypervisor statistics data") - hypervisor_statistics = {'vcpus_used': 0, - 'vcpus': conVCPUS, - 'free_ram_mb': conFreeRamMB, - 'free_disk_gb': conFreeDiskGB} - else: - # non forbiden exeption will be redirected - raise e - - # get storage limit for this tenant - service['service_type'] = 'volumev2' - req_resouce = "/limits" - self._logger.info("check volumev2 limits> URI:%s" % req_resouce) - resp = sess.get(req_resouce, endpoint_filter=service) - self._logger.info("check volumev2> status:%s" % resp.status_code) - content = resp.json() - storage_limits = content['limits']['absolute'] - self._logger.debug("check volumev2> resp data:%s" % content) - - # compute actual available resource for this tenant - remainVCPU = compute_limits['maxTotalCores'] - compute_limits['totalCoresUsed'] - remainHypervisorVCPU = hypervisor_statistics['vcpus'] - hypervisor_statistics['vcpus_used'] - - if (remainVCPU > remainHypervisorVCPU): - remainVCPU = remainHypervisorVCPU - - remainMEM = compute_limits['maxTotalRAMSize'] - compute_limits['totalRAMUsed'] - remainHypervisorMEM = hypervisor_statistics['free_ram_mb'] - if remainMEM > remainHypervisorMEM: - remainMEM = remainHypervisorMEM - - remainStorage = storage_limits['maxTotalVolumeGigabytes'] - storage_limits['totalGigabytesUsed'] - remainHypervisorStorage = hypervisor_statistics['free_disk_gb'] - if (remainStorage > remainHypervisorStorage): - remainStorage = remainHypervisorStorage - - # compare resource demanded with available - if (int(resource_demand['vCPU']) > remainVCPU): - hasEnoughResource = False - elif (int(resource_demand['Memory']) > remainMEM): - hasEnoughResource = False - elif (int(resource_demand['Storage']) > remainStorage): - hasEnoughResource = False - else: - hasEnoughResource = True - + hasEnoughResource = self.get_tenant_cap_info(vimid, request.data) self._logger.info("RESP with data> result:%s" % hasEnoughResource) return Response(data={'result': hasEnoughResource}, status=status.HTTP_200_OK) except VimDriverNewtonException as e: self._logger.error("Plugin exception> status:%s,error:%s" % (e.status_code, e.content)) - return Response(data={'result': hasEnoughResource, + return Response(data={'result': False, 'error': e.content}, status=e.status_code) except HttpError as e: self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json())) resp = e.response.json() - resp.update({'result': hasEnoughResource}) + resp.update({'result': False}) return Response(data=e.response.json(), status=e.http_status) except Exception as e: self._logger.error(traceback.format_exc()) - return Response(data={'result': hasEnoughResource, 'error': str(e)}, + return Response(data={'result': False, 'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + def get_tenant_cap_info(self, vimid, resource_demand): + hasEnoughResource = False + tenant_name = None + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenant_name) + + # get token: + # cloud_owner, regionid = extsys.decode_vim_id(vimid) + interface = 'public' + service = {'service_type': 'compute', + 'interface': interface, + 'region_name': vim['openstack_region_id'] + if vim.get('openstack_region_id') + else vim['cloud_region_id'] + } + + # get limit for this tenant + req_resouce = "/limits" + self._logger.info("check limits> URI:%s" % req_resouce) + resp = sess.get(req_resouce, endpoint_filter=service) + self._logger.info("check limits> status:%s" % resp.status_code) + content = resp.json() + compute_limits = content['limits']['absolute'] + self._logger.debug("check limits> resp data:%s" % content) + + # get total resource of this cloud region + try: + req_resouce = "/os-hypervisors/statistics" + self._logger.info("check os-hypervisors statistics> URI:%s" % req_resouce) + resp = sess.get(req_resouce, endpoint_filter=service) + self._logger.info("check os-hypervisors statistics> status:%s" % resp.status_code) + content = resp.json() + hypervisor_statistics = content['hypervisor_statistics'] + self._logger.debug("check os-hypervisors statistics> resp data:%s" % content) + except HttpError as e: + if e.http_status == status.HTTP_403_FORBIDDEN: + # Due to non administrator account cannot get hypervisor data, + # so construct enough resource data + conVCPUS = int(resource_demand['vCPU']) + conFreeRamMB = int(resource_demand['Memory']) + conFreeDiskGB = int(resource_demand['Storage']) + self._logger.info("Non administator forbidden to access hypervisor statistics data") + hypervisor_statistics = {'vcpus_used': 0, + 'vcpus': conVCPUS, + 'free_ram_mb': conFreeRamMB, + 'free_disk_gb': conFreeDiskGB} + else: + # non forbiden exeption will be redirected + raise e + + # get storage limit for this tenant + service['service_type'] = 'volumev2' + req_resouce = "/limits" + self._logger.info("check volumev2 limits> URI:%s" % req_resouce) + resp = sess.get(req_resouce, endpoint_filter=service) + self._logger.info("check volumev2> status:%s" % resp.status_code) + content = resp.json() + storage_limits = content['limits']['absolute'] + self._logger.debug("check volumev2> resp data:%s" % content) + + # compute actual available resource for this tenant + remainVCPU = compute_limits['maxTotalCores'] - compute_limits['totalCoresUsed'] + remainHypervisorVCPU = hypervisor_statistics['vcpus'] - hypervisor_statistics['vcpus_used'] + + if (remainVCPU > remainHypervisorVCPU): + remainVCPU = remainHypervisorVCPU + + remainMEM = compute_limits['maxTotalRAMSize'] - compute_limits['totalRAMUsed'] + remainHypervisorMEM = hypervisor_statistics['free_ram_mb'] + if remainMEM > remainHypervisorMEM: + remainMEM = remainHypervisorMEM + + remainStorage = storage_limits['maxTotalVolumeGigabytes'] - storage_limits['totalGigabytesUsed'] + remainHypervisorStorage = hypervisor_statistics['free_disk_gb'] + if (remainStorage > remainHypervisorStorage): + remainStorage = remainHypervisorStorage + + # compare resource demanded with available + if (int(resource_demand['vCPU']) > remainVCPU): + hasEnoughResource = False + elif (int(resource_demand['Memory']) > remainMEM): + hasEnoughResource = False + elif (int(resource_demand['Storage']) > remainStorage): + hasEnoughResource = False + else: + hasEnoughResource = True + + return hasEnoughResource class APIv1CapacityCheck(CapacityCheck): def __init__(self): diff --git a/share/starlingx_base/registration/registration.py b/share/starlingx_base/registration/registration.py index 7a85b29f..59074568 100644 --- a/share/starlingx_base/registration/registration.py +++ b/share/starlingx_base/registration/registration.py @@ -373,7 +373,8 @@ class InfraResourceAuditor(newton_registration.RegistryHelper): continue hypervisors_dict[h["hypervisor_hostname"]] = h - az_pserver_info = {} + vimAzCacheKey = "cap_azlist_" + vimid + vimAzList = [] # cloud_owner, cloud_region_id = extsys.decode_vim_id(vimid) for az in self._get_list_resources( "/os-availability-zone/detail", "compute", session, @@ -393,6 +394,8 @@ class InfraResourceAuditor(newton_registration.RegistryHelper): if azName == 'internal': continue + vimAzList.append(azName) + # get list of host names pservers_info = [k for (k, v) in az['hosts'].items()] # set the association between az and pservers @@ -456,6 +459,7 @@ class InfraResourceAuditor(newton_registration.RegistryHelper): # update the cache cache.set(azCapCacheKey, json.dumps(azCapInfoCache), 3600 * 24) + cache.set(vimAzCacheKey, vimAzList, 3600 * 24) except Exception as e: self._logger.error("azcap_audit raise exception: %s" % e) pass diff --git a/share/starlingx_base/resource/__init__.py b/share/starlingx_base/resource/__init__.py new file mode 100644 index 00000000..825091ff --- /dev/null +++ b/share/starlingx_base/resource/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017-2019 Wind River Systems, Inc. +# +# 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. diff --git a/share/starlingx_base/resource/capacity.py b/share/starlingx_base/resource/capacity.py new file mode 100644 index 00000000..fc926d00 --- /dev/null +++ b/share/starlingx_base/resource/capacity.py @@ -0,0 +1,97 @@ +# Copyright (c) 2017-2019 Wind River Systems, Inc. +# +# 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. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import traceback +import json + +from django.core.cache import cache + +from newton_base.resource import capacity as newton_capacity +from common.exceptions import VimDriverNewtonException +from newton_base.util import VimDriverUtils + +from keystoneauth1.exceptions import HttpError +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView +from common.msapi import extsys + +logger = logging.getLogger(__name__) + + +class CapacityCheck(newton_capacity.CapacityCheck): + def __init__(self): + super(CapacityCheck, self).__init__() + self._logger = logger + + def post(self, request, vimid=""): + self._logger.info("vimid, data> %s, %s" % (vimid, request.data)) + self._logger.debug("META> %s" % request.META) + + try: + hasEnoughResource = self.get_tenant_cap_info(vimid, request.data) + azCapInfo = self.get_az_cap_info(vimid) + self._logger.info("RESP with data> result:%s" % hasEnoughResource) + return Response(data={'result': hasEnoughResource, 'AZs': azCapInfo}, status=status.HTTP_200_OK) + except Exception as e: + self._logger.error(traceback.format_exc()) + return Response(data={'result': False, 'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def get_az_cap_info(self, vimid): + azCapInfo = [] + viminfo = VimDriverUtils.get_vim_info(vimid) + if not viminfo: + self._logger.warn("azcap_audit no valid vimid: %s" % vimid) + return + + session = VimDriverUtils.get_session( + viminfo, + tenant_name=viminfo['tenant'] + ) + try: + # get list of AZ + vimAzCacheKey = "cap_azlist_" + vimid + vimAzListCacheStr = cache.get(vimAzCacheKey) + vimAzListCache = json.loads(vimAzListCacheStr) if vimAzListCacheStr else [] + for azName in vimAzListCache: + azCapCacheKey = "cap_" + vimid + "_" + azName + azCapInfoCacheStr = cache.get(azCapCacheKey) + azCapInfoCache = json.loads(azCapInfoCacheStr) if azCapInfoCacheStr else None + + azCapInfo["availability-zone-name"] = azName + azCapInfo["vCPUAvail"] = azCapInfoCache.get("vcpus", 0) + azCapInfoCache.get("vcpus_used", 0) + azCapInfo["vCPUTotal"] = azCapInfoCache.get("vcpus", 0) + azCapInfo["MemoryAvail"] = azCapInfoCache.get("vcpus", 0) + azCapInfo["MemoryTotal"] = azCapInfoCache.get("vcpus", 0) + azCapInfo["StorageAvail"] = azCapInfoCache.get("vcpus", 0) + azCapInfo["StorageTotal"] = azCapInfoCache.get("vcpus", 0) + + return azCapInfo + except Exception as e: + return azCapInfo + pass + +class APIv1CapacityCheck(CapacityCheck): + def __init__(self): + super(APIv1CapacityCheck, self).__init__() + # self._logger = logger + + def post(self, request, cloud_owner="", cloud_region_id=""): + self._logger.info("vimid, data> %s,%s, %s" % (cloud_owner, cloud_region_id, request.data)) + self._logger.debug("META> %s" % request.META) + + vimid = extsys.encode_vim_id(cloud_owner, cloud_region_id) + return super(APIv1CapacityCheck, self).post(request, vimid) -- cgit 1.2.3-korg