Merge "RMS direct access to database for region resources"

This commit is contained in:
Zuul 2020-08-20 18:24:29 +00:00 committed by Gerrit Code Review
commit 88b95a0dcb
5 changed files with 94 additions and 90 deletions

View File

@ -15,10 +15,8 @@ from orm.services.region_manager.rms.services import error_base
from orm.services.region_manager.rms.services import services as RegionService from orm.services.region_manager.rms.services import services as RegionService
from orm.services.region_manager.rms.utils import authentication from orm.services.region_manager.rms.utils import authentication
from pecan import conf as pecan_conf
from pecan import request from pecan import request
from pecan import rest from pecan import rest
import requests
import wsme import wsme
from wsme import types as wtypes from wsme import types as wtypes
from wsmeext.pecan import wsexpose from wsmeext.pecan import wsexpose
@ -190,45 +188,6 @@ class RegionsController(rest.RestController):
metadata = RegionMetadataController() metadata = RegionMetadataController()
status = RegionStatusController() status = RegionStatusController()
def has_no_resources(self, region_id):
# function to check if any resource (flavor, customer, or image) is
# assigned to the region_id
try:
resources = {
'flavors': [pecan_conf.api.fms_server.base,
pecan_conf.api.fms_server.flavors],
'customers': [pecan_conf.api.cms_server.base,
pecan_conf.api.cms_server.customers],
'images': [pecan_conf.api.ims_server.base,
pecan_conf.api.ims_server.images]
}
keystone_ep = authentication.get_keystone_ep(
request.headers['X-Auth-Region'])
headers = {'Keystone-Endpoint': keystone_ep,
'X-Auth-Region': request.headers['X-Auth-Region'],
'X-Auth-Token': request.headers['X-Auth-Token']}
for resource in resources:
resource_get_url = '%s%s/?region=%s' % (
resources[resource][0],
resources[resource][1], region_id)
resp = requests.get(resource_get_url,
headers=headers,
verify=pecan_conf.verify)
resp_dict = resp.json()
if resp_dict[resource]:
return False
return True
except Exception as e:
raise err_utils.get_error(request.transaction_id,
status_code=401,
message=str(e))
@wsexpose(Regions, str, str, [str], str, str, str, str, str, str, str, str, @wsexpose(Regions, str, str, [str], str, str, str, str, str, str, str, str,
str, str, str, str, status_code=200, rest_content_types='json') str, str, str, str, status_code=200, rest_content_types='json')
def get_all(self, type=None, status=None, metadata=None, def get_all(self, type=None, status=None, metadata=None,
@ -263,8 +222,9 @@ class RegionsController(rest.RestController):
'ranger_agent_version': ranger_agent_version, 'clli': clli, 'ranger_agent_version': ranger_agent_version, 'clli': clli,
'regionname': regionname, 'osversion': osversion, 'regionname': regionname, 'osversion': osversion,
'location_type': location_type, 'state': state, 'location_type': location_type, 'state': state,
'domain_name': domain_name, 'country': country, 'city': city, 'domain_name': domain_name, 'country': country,
'street': street, 'zip': zip, 'vlcp_name': vlcp_name} 'city': city, 'street': street, 'zip': zip,
'vlcp_name': vlcp_name}
logger.debug("Parameters: {}".format(str(url_args))) logger.debug("Parameters: {}".format(str(url_args)))
try: try:
@ -358,46 +318,41 @@ class RegionsController(rest.RestController):
@wsexpose(None, str, rest_content_types='json', status_code=204) @wsexpose(None, str, rest_content_types='json', status_code=204)
def delete(self, region_id): def delete(self, region_id):
utils.set_utils_conf(pecan_conf) logger.info("Delete Region")
# currently ORM resource types are 'flavor', 'customer', and 'image' authentication.authorize(request, 'region:delete')
proceed_to_delete = self.has_no_resources(region_id) try:
if proceed_to_delete:
logger.info("Delete Region")
authentication.authorize(request, 'region:delete')
try:
logger.debug("delete region {}".format(region_id)) logger.debug("delete region {}".format(region_id))
RegionService.delete_region(region_id) RegionService.delete_region(region_id)
logger.debug("region deleted") logger.debug("region deleted")
event_details = 'Region {} deleted'.format(region_id) event_details = 'Region {} deleted'.format(region_id)
utils.audit_trail('delete region', request.transaction_id, utils.audit_trail('delete region', request.transaction_id,
request.headers, region_id, request.headers, region_id,
event_details=event_details) event_details=event_details)
# issue NotFoundError for "Delete Region" when group_id not found # issue NotFoundError for "Delete Region" when group_id not found
# which is returned by RegionService.delete_region function # which is returned by RegionService.delete_region function
except error_base.NotFoundError as exp: except error_base.NotFoundError as exp:
logger.error("RegionsController - Region not found") logger.error("RegionsController - Region not found")
raise err_utils.get_error( raise err_utils.get_error(
request.transaction_id, request.transaction_id,
message="Cannot delete - " + exp.message, message="Cannot delete - " + exp.message,
status_code=exp.status_code) status_code=exp.status_code)
except Exception as exp:
logger.exception(
"error in deleting region .. reason:- {}".format(str(exp)))
raise err_utils.get_error(request.transaction_id,
status_code=500,
message=str(exp))
return
else:
region_resources_exist_msg = "Region {} cannot be deleted as " \
"resources are assigned.".format(region_id)
except error_base.ConflictError as exp:
logger.error("Region with resources cannot be deleted")
raise err_utils.get_error(request.transaction_id, raise err_utils.get_error(request.transaction_id,
status_code=400, status_code=400,
message=region_resources_exist_msg) message=exp.message)
except Exception as exp:
logger.exception(
"Error in deleting region .. reason:- {}".format(str(exp)))
raise err_utils.get_error(request.transaction_id,
status_code=500,
message=str(exp))
return
@wsexpose(RegionsData, str, body=RegionsData, status_code=201, @wsexpose(RegionsData, str, body=RegionsData, status_code=201,
rest_content_types='json') rest_content_types='json')

View File

@ -77,6 +77,7 @@ def delete_region(region_id):
:return: :return:
""" """
LOG.debug("logic:- delete region {}".format(region_id)) LOG.debug("logic:- delete region {}".format(region_id))
try: try:
db = data_manager_factory.get_data_manager() db = data_manager_factory.get_data_manager()
# logic to allow 'delete_region' to issue NotFoundError when region_id is non-existent # logic to allow 'delete_region' to issue NotFoundError when region_id is non-existent
@ -84,6 +85,13 @@ def delete_region(region_id):
if not region: if not region:
raise error_base.NotFoundError(message="Region '{}' not found".format(region_id)) raise error_base.NotFoundError(message="Region '{}' not found".format(region_id))
# Region with resources cannnot be deleted
resource_exist = db.query_region_resources(region_id)
if resource_exist:
region_resources_exist_msg = "Region {} cannot be deleted as " \
"resources are assigned.".format(region_id)
raise error_base.ConflictError(message=region_resources_exist_msg)
db.delete_region(region_id) db.delete_region(region_id)
LOG.debug("region deleted") LOG.debug("region deleted")

View File

@ -1,11 +1,19 @@
import datetime import datetime
import logging import logging
from orm.services.customer_manager.cms_rest.data.sql_alchemy.models import (
Customer, CustomerRegion)
from orm.services.customer_manager.cms_rest.data.sql_alchemy.models import \
Region as CmsRegion
from orm.services.flavor_manager.fms_rest.data.sql_alchemy.db_models import (
Flavor, FlavorRegion)
from orm.services.image_manager.ims.persistency.sql_alchemy.db_models import (
Image, ImageRegion)
from orm.services.region_manager.rms.model import model as PythonModels from orm.services.region_manager.rms.model import model as PythonModels
from orm.services.region_manager.rms.services import error_base from orm.services.region_manager.rms.services import error_base
from orm.services.region_manager.rms.storage.base_data_manager import (BaseDataManager, from orm.services.region_manager.rms.storage.base_data_manager import (
DuplicateEntryError, BaseDataManager, DuplicateEntryError, EntityNotFound)
EntityNotFound)
import oslo_db import oslo_db
from .data_models import (Group, GroupRegion, Region, RegionEndPoint, from .data_models import (Group, GroupRegion, Region, RegionEndPoint,
@ -177,6 +185,43 @@ class DataManager(BaseDataManager):
logger.exception("fail to update region {}".format(str(exp))) logger.exception("fail to update region {}".format(str(exp)))
raise raise
def query_region_resources(self, region_id):
try:
resource_exist = False
session = self._engine_facade.get_session()
with session.begin():
# query customer resource for the region
query = session.query(Customer)
query = query.join(CustomerRegion).filter(
CustomerRegion.customer_id == Customer.id)
query = query.join(CmsRegion).filter(
CmsRegion.id == CustomerRegion.region_id,
CmsRegion.type == 'single', CmsRegion.name == region_id)
if query.first() is not None:
resource_exist = True
else:
# query flavor resource for the region
query = session.query(Flavor)
query = query.join(FlavorRegion).filter(
FlavorRegion.flavor_internal_id == Flavor.internal_id,
FlavorRegion.region_name == region_id)
if query.first() is not None:
resource_exist = True
else:
# query image resource for the region
query = session.query(Image)
query = query.join(ImageRegion).filter(
ImageRegion.image_id == Image.id,
ImageRegion.region_name == region_id)
if query.first() is not None:
resource_exist = True
return resource_exist
except Exception as exp:
logger.exception(
"fail to verify if region {} has resources".format(region_id))
raise
def delete_region(self, region_id): def delete_region(self, region_id):
# delete a region from `region` table and also the region's # delete a region from `region` table and also the region's
# entries from `region_meta_data` and `region_end_points` tables # entries from `region_meta_data` and `region_end_points` tables

View File

@ -257,10 +257,9 @@ class TestAddRegion(FunctionalTest):
@patch.object(regions, 'request') @patch.object(regions, 'request')
@patch.object(regions, 'err_utils') @patch.object(regions, 'err_utils')
@patch.object(regions.RegionService, 'delete_region') @patch.object(regions.RegionService, 'delete_region')
@patch.object(regions.RegionsController, 'has_no_resources', return_value=True)
@patch.object(regions.authentication, 'authorize', return_value=True) @patch.object(regions.authentication, 'authorize', return_value=True)
def test_delete_region_error(self, mock_auth, mock_delete_logic, def test_delete_region_error(self, mock_auth, mock_delete_logic,
mock_get_error, mock_request, mock_controller): mock_get_error, mock_request):
mock_get_error.get_error = self.get_error mock_get_error.get_error = self.get_error
mock_request.transaction_id = "555" mock_request.transaction_id = "555"
mock_delete_logic.side_effect = regions.error_base.ErrorStatus(message="unknown error", status_code=500) mock_delete_logic.side_effect = regions.error_base.ErrorStatus(message="unknown error", status_code=500)

View File

@ -51,6 +51,9 @@ class db(object):
def get_region_by_id_or_name(self, id_name): def get_region_by_id_or_name(self, id_name):
return id_name return id_name
def query_region_resources(self, id_name):
return False
def add_region(self, **kw): def add_region(self, **kw):
if self.exp: if self.exp:
raise Exception("any") raise Exception("any")
@ -313,22 +316,16 @@ class TestServices(FunctionalTest):
message = exp.message message = exp.message
self.assertEqual(message, "id not found") self.assertEqual(message, "id not found")
@patch.object(services, 'get_region_by_id_or_name',
return_value={"a": "b"})
@patch.object(services.data_manager_factory, 'get_data_manager', @patch.object(services.data_manager_factory, 'get_data_manager',
return_value=db(exp=True)) return_value=db(exp=True))
def test_delete_region_error(self, mock_db_get_group, def test_delete_region_error(self, mock_db_get_group):
mock_get_region_id_name):
try: try:
result = services.delete_region(self._to_wsme_from_input(full_region)) result = services.delete_region(self._to_wsme_from_input(full_region))
except Exception as exp: except Exception as exp:
message = str(exp) message = str(exp)
self.assertEqual(message, "not deleted") self.assertEqual(message, "not deleted")
@patch.object(services, 'get_region_by_id_or_name',
return_value={"a": "b"})
@patch.object(services.data_manager_factory, 'get_data_manager', @patch.object(services.data_manager_factory, 'get_data_manager',
return_value=db()) return_value=db())
def test_delete_region_success(self, mock_db_get_group, def test_delete_region_success(self, mock_db_get_group):
mock_get_region_id_name):
result = services.delete_region(self._to_wsme_from_input(full_region)) result = services.delete_region(self._to_wsme_from_input(full_region))