Port Private Flavor Tenant python2 logic changes

Change-Id: Icb7aab24f15c7ec970330d84baf03a9c07f00897
This commit is contained in:
stewie925 2019-11-22 15:49:10 -08:00
parent b78d9f0f81
commit 2ccdfdf103
8 changed files with 181 additions and 37 deletions

View File

@ -59,20 +59,20 @@ class FlavorController(rest.RestController):
except ErrorStatus as exception:
LOG.log_exception("FlavorController - Failed to CreateFlavor", exception)
raise err_utils.get_error(request.transaction_id,
message=exception.message,
message=str(exception),
status_code=exception.status_code)
except ValueError as exception:
LOG.log_exception("FlavorController - Failed to CreateFlavor", exception)
raise err_utils.get_error(request.transaction_id,
status_code=400,
error_details=exception.message)
error_details=str(exception))
except Exception as exception:
LOG.log_exception("FlavorController - Failed to CreateFlavor", exception)
raise err_utils.get_error(request.transaction_id,
status_code=500,
error_details=exception.message)
error_details=str(exception))
@wsexpose(FlavorWrapper, str, body=FlavorWrapper, rest_content_types='json')
def put(self, flavor_id, flavors):
@ -94,7 +94,7 @@ class FlavorController(rest.RestController):
except ErrorStatus as exception:
LOG.log_exception("FlavorController - Failed to GetFlavorDetails", exception)
raise err_utils.get_error(request.transaction_id,
message=exception.message,
message=str(exception),
status_code=exception.status_code)
except Exception as exception:
@ -121,7 +121,7 @@ class FlavorController(rest.RestController):
except ErrorStatus as exception:
LOG.log_exception("FlavorController - Failed to GetFlavorlist", exception)
raise err_utils.get_error(request.transaction_id,
message=exception.message,
message=str(exception),
status_code=exception.status_code)
except Exception as exception:
@ -148,7 +148,7 @@ class FlavorController(rest.RestController):
except ErrorStatus as exception:
LOG.log_exception("FlavorController - Failed to delete flavor", exception)
raise err_utils.get_error(request.transaction_id,
message=exception.message,
message=str(exception),
status_code=exception.status_code)
except Exception as exception:

View File

@ -22,7 +22,6 @@ class TenantController(rest.RestController):
flavor_logic, utils = di.resolver.unpack(TenantController)
LOG.info("TenantController - add tenants: " + str(tenant_wrapper))
authentication.authorize(request, 'flavor:add_flavor_tenants')
try:
result = flavor_logic.add_tenants(flavor_id, tenant_wrapper, request.transaction_id)
@ -34,21 +33,20 @@ class TenantController(rest.RestController):
request.headers, flavor_id,
event_details=event_details)
return result
except ValueError as exception:
LOG.log_exception("TenantController - Failed to add tenants", exception)
LOG.log_exception("TenantController - Failed to add tenants", str(exception))
raise err_utils.get_error(request.transaction_id,
message=exception.message,
message=str(exception),
status_code=400)
except ErrorStatus as exception:
LOG.log_exception("TenantController - Failed to add tenants", exception)
LOG.log_exception("TenantController - Failed to add tenants", str(exception))
raise err_utils.get_error(request.transaction_id,
message=exception.message,
message=str(exception),
status_code=exception.status_code)
except Exception as exception:
LOG.log_exception("TenantController - Failed to add tenants", exception)
LOG.log_exception("TenantController - Failed to add tenants", str(exception))
raise err_utils.get_error(request.transaction_id,
status_code=500,
error_details=str(exception))
@ -72,13 +70,13 @@ class TenantController(rest.RestController):
event_details=event_details)
except ErrorStatus as exception:
LOG.log_exception("TenantController - Failed to delete tenant", exception)
LOG.log_exception("TenantController - Failed to delete tenant", str(exception))
raise err_utils.get_error(request.transaction_id,
message=exception.message,
message=str(exception),
status_code=exception.status_code)
except Exception as exception:
LOG.log_exception("TenantController - Failed to delete tenant", exception)
LOG.log_exception("TenantController - Failed to delete tenant", str(exception))
raise err_utils.get_error(request.transaction_id,
status_code=500,
error_details=str(exception))

View File

@ -1,7 +1,8 @@
import logging
# from orm.services.flavor_manager.fms_rest.logic.error_base import DuplicateEntityError
from orm.services.flavor_manager.fms_rest.data.sql_alchemy.flavor.flavor_record import FlavorRecord
from orm.services.flavor_manager.fms_rest.data.sql_alchemy.flavor.\
flavor_record import FlavorRecord
from oslo_db.sqlalchemy.enginefacade import LegacyEngineFacade
from pecan import conf
@ -86,3 +87,27 @@ class DataManager(object):
self.flavor_record = FlavorRecord(self.session)
return self.flavor_record
return None
def get_valid_tenant_region_list(self, requested_tenants,
requested_regions):
datamanager = DataManager()
# convert tenant and region lists to sql-friendly list format
tenants_list = str(tuple(requested_tenants)).rstrip(',)') + ')'
regions_list = str(tuple(requested_regions)).rstrip(',)') + ')'
# --- use sql query for processing time considerations
# get valid tenants list for the region(s) by checking
# tenant/region status in resource_status table if
# status shows 'Success' and operation not 'delete'
sql_query = '''select a.uuid, b.name from customer a,
cms_region b, customer_region c, resource_status d
where c.customer_id = a.id and c.region_id = b.id
and d.resource_id = a.uuid and d.region = b.name
and ((d.status = 'Success' and d.operation != 'delete')
or (d.status = 'Error' and d.operation = 'modify'))
and a.uuid in %s and b.name in %s'''
results = datamanager.session.connection().execute(sql_query % (tenants_list, regions_list)).fetchall()
return results

View File

@ -334,8 +334,6 @@ class Flavor(Base, FMSBaseModel):
if self.visibility == "public" and len(self.flavor_tenants) > 0:
raise ValueError(
"tenants should not be specified for a public flavor")
elif self.visibility == "private" and len(self.flavor_tenants) == 0:
raise ValueError("Tenants must be specified for a private flavor")
elif self.visibility not in ["private", "public"]:
raise ValueError(
"Flavor visibility can be 'public' or 'private',"

View File

@ -18,6 +18,35 @@ LOG = get_logger(__name__)
di = injector.get_di()
def validate_tenants_regions_list(requested_tenants, requested_regions,
action, datamanager):
regions_list, tenants_list = [], []
results = datamanager.get_valid_tenant_region_list(requested_tenants,
requested_regions)
valid_tenants_list, valid_regions_list = [], []
# the first element in the results tuple is the tenant
# prep result_tenant_list from result_rows and remove NoneTypes from list
result_tenant_list = [x[0] for x in results]
result_tenant_list = filter(None, result_tenant_list)
# lastly clean up valid_tenants_list list by removing duplicate items
valid_tenants_list = list(dict.fromkeys(result_tenant_list))
# second element in the results tuple is the region - compile the region
# data into valid_regions_list and eliminate duplicates from the list
valid_regions_list = [x[1] for x in results]
valid_regions_list = list(dict.fromkeys(valid_regions_list))
# update the tenants list with validated tenants from query results
# and will be reflected in the create/get flavor as well as
# add/ tenant responses
requested_tenants = valid_tenants_list
requested_regions = valid_regions_list
return requested_tenants, requested_regions
@di.dependsOn('data_manager')
def create_flavor(flavor, flavor_uuid, transaction_id):
DataManager = di.resolver.unpack(create_flavor)
@ -31,6 +60,22 @@ def create_flavor(flavor, flavor_uuid, transaction_id):
flavor.flavor.name = calculate_name(flavor)
LOG.debug("Flavor name is [{}] ".format(flavor.flavor.name))
flavor_regions = []
for region in flavor.flavor.regions:
flavor_regions.append(region.name)
# validate which tenants from the original tenants list to
# be assigned to the private flavor; if no valid tenants
# found, the valid_tenants_list will return empty list
if flavor.flavor.visibility == 'private':
valid_tenants_list, valid_regions_list = \
validate_tenants_regions_list(flavor.flavor.tenants,
# flavor.flavor.regions,
flavor_regions,
'create', datamanager)
# replace the original tenant list in the private flavor
# with the valid_tenants_list
flavor.flavor.tenants = valid_tenants_list
sql_flavor = flavor.to_db_model()
@ -217,6 +262,7 @@ def add_regions(flavor_uuid, regions, transaction_id):
existing_region_names = sql_flavor.get_existing_region_names()
flvr_tenant_list, flvr_region_list = [], []
for region in regions.regions:
if region.name == '' or region.name.isspace():
raise ErrorStatus(400, 'Cannot add region with an empty name')
@ -227,10 +273,25 @@ def add_regions(flavor_uuid, regions, transaction_id):
db_region = FlavorRegion(region_name=region.name,
region_type='single')
sql_flavor.add_region(db_region)
flvr_region_list.append(region.name)
# get any exception created by previous actions against the database
datamanager.flush()
# private flavor new logic
# validate tenants assigned to the flavor
for tenant in sql_flavor.flavor_tenants:
flvr_tenant_list.append(tenant.tenant_id)
valid_tenants_list, valid_regions_list = \
validate_tenants_regions_list(flvr_tenant_list,
flvr_region_list,
'create', datamanager)
# update tenant list
for tenant in flvr_tenant_list:
if tenant not in valid_tenants_list:
sql_flavor.remove_tenant(tenant)
send_to_rds_if_needed(
sql_flavor, existing_region_names, "put", transaction_id)
@ -298,6 +359,8 @@ def add_tenants(flavor_uuid, tenants, transaction_id):
DataManager = di.resolver.unpack(add_tenants)
datamanager = DataManager()
valid_tenants_list, valid_regions_list = [], []
try:
flavor_rec = datamanager.get_record('flavor')
sql_flavor = flavor_rec.get_flavor_by_id(flavor_uuid)
@ -309,6 +372,21 @@ def add_tenants(flavor_uuid, tenants, transaction_id):
raise ErrorStatus(405, 'Cannot add tenant to a public flavor')
existing_region_names = sql_flavor.get_existing_region_names()
existing_region_list = []
for x in existing_region_names:
existing_region_list.append(x)
if tenants.tenants:
valid_tenants_list, valid_regions_list = \
validate_tenants_regions_list(tenants.tenants,
existing_region_list,
'create', datamanager)
# replace tenants.tenants with only the valid tenants
tenants.tenants = valid_tenants_list
# issue error message if tenant list is empty
if not tenants.tenants:
raise ValueError("At least one valid tenant must be provided")
for tenant in tenants.tenants:
if not isinstance(tenant, str):
@ -357,11 +435,6 @@ def delete_tenant(flavor_uuid, tenant_id, transaction_id):
raise ValueError("{} is a public flavor, delete tenant"
" action is not relevant".format(flavor_uuid))
if len(sql_flavor.flavor_tenants) == 1 \
and sql_flavor.flavor_tenants[0].tenant_id == tenant_id:
raise ValueError(
'Private flavor must have at least one tenant')
existing_region_names = sql_flavor.get_existing_region_names()
sql_flavor.remove_tenant(tenant_id)
# get any exception created by previous actions against the database

View File

@ -2,6 +2,8 @@
import logging
import time
from orm.services.flavor_manager.fms_rest.data.sql_alchemy.data_manager \
import DataManager
from orm.services.resource_distributor.rds.services import region_resource_id_status as regionResourceIdStatus
from orm.services.resource_distributor.rds.services import (yaml_customer_builder, yaml_flavor_builder,
yaml_group_builder, yaml_image_builder)
@ -79,6 +81,27 @@ def _set_all_statuses_to_error(input_data, message=None):
status="Error")
def get_valid_tenants(tenants, region):
# identify valid tenants for a particular region and save in
# valid_tenants_list; if no tenants validated return empty list
requested_tenants, valid_tenants_list = [], []
for tenant in tenants:
requested_tenants.append(tenant['tenant_id'])
requested_region = [region]
datamanager = DataManager()
customer_region = datamanager.get_record('customer_region')
results = datamanager.get_valid_tenant_region_list(
requested_tenants, requested_region)
for x in results:
valid_tenants_list.append(x[0])
# prep result_tenant_list from result_rows and remove NoneTypes from list
valid_tenants_list = filter(None, valid_tenants_list)
return valid_tenants_list
def _create_data_to_sot(input_data):
"""create data.
@ -102,7 +125,29 @@ def _create_data_to_sot(input_data):
elif input_data.resource_type == "group":
yamldata = yaml_group_builder.yamlbuilder(jsondata, target)
elif input_data.resource_type == "flavor":
if input_data.model['visibility'] == 'private':
ok_tenants_list = []
ok_tenants = {}
# skip tenant validation if tenants list is empty
if input_data.model['tenants']:
valid_tenants_list = get_valid_tenants(
input_data.model['tenants'], target['name'])
for tenant in valid_tenants_list:
ok_tenants['tenant_id'] = tenant
ok_tenants_list.append(ok_tenants.copy())
# Note: If ok_tenant_list is empty, just create heat template
# for private flavor with empty tenant list
if not ok_tenants_list:
ok_tenants['tenant_id'] = None
ok_tenants_list.append(ok_tenants.copy())
jsondata['tenants'] = ok_tenants_list
# now issue yamldata for flavor either public or private
yamldata = yaml_flavor_builder.yamlbuilder(jsondata, target)
elif input_data.resource_type == "image":
yamldata = yaml_image_builder.yamlbuilder(jsondata, target)
targetslist.append({"region_id": target['name'],

View File

@ -52,7 +52,8 @@ def yamlbuilder(alldata, region):
extra_specs[key] = value
# Handle tenants
for tenant in alldata['tenants']:
tenants.append(tenant['tenant_id'])
if tenant['tenant_id']:
tenants.append(tenant['tenant_id'])
# Generate the output
resources['resources'] = {}

View File

@ -52,29 +52,43 @@ class TestFlavorLogic(FunctionalTest):
self.assertRaises(flavor_logic.ErrorStatus, flavor_logic.create_flavor,
flavor, 'uuid', 'transaction')
@patch.object(flavor_logic, 'validate_tenants_regions_list')
@patch.object(flavor_logic, 'FlavorWrapper')
def test_create_flavor_success(self, mock_flavorwrapper):
def test_create_flavor_success(self, mock_flavorwrapper, mock_validate):
mock_flavorwrapper.from_db_model.return_value = get_flavor_mock()
# Flavor is public - test success
flavor = get_flavor_mock()
flavor.flavor.visibility = 'public'
global error
error = 31
injector.override_injected_dependency(
('rds_proxy', get_rds_proxy_mock()))
res_flavor = flavor_logic.create_flavor(get_flavor_mock(), 'uuid',
res_flavor = flavor_logic.create_flavor(flavor, 'uuid',
'transaction')
mock_validate.assert_not_called()
self.assertEqual(res_flavor.flavor.profile, 'N1')
self.assertEqual(res_flavor.flavor.ram, '1024')
self.assertEqual(res_flavor.flavor.vcpus, '1')
self.assertEqual(res_flavor.flavor.series, cfg.CONF.fms.flavor_series[0])
self.assertEqual(res_flavor.flavor.id, 'g')
# Test that flavor validate model works by passing bad swap value
flavor = get_flavor_mock()
flavor.flavor.swap = '1024000'
self.assertRaises(flavor_logic.ErrorStatus, flavor_logic.create_flavor,
flavor, 'uuid', 'transaction')
# Flavor is private - test success
flavor.flavor.validate_model = MagicMock()
flavor.flavor.visibility = 'private'
flavor.flavor.tenants = ['1234']
flavor.flavor.regions = [Region(name='test_region')]
mock_validate.return_value = [['1234'], ['rgn1']]
res_flavor = flavor_logic.create_flavor(flavor, 'uuid',
'transaction')
mock_validate.assert_called()
self.assertEqual(res_flavor.flavor.profile, 'N1')
self.assertEqual(res_flavor.flavor.ram, '1024')
self.assertEqual(res_flavor.flavor.vcpus, '1')
@ -582,16 +596,6 @@ class TestFlavorLogic(FunctionalTest):
'tenant_id',
'transaction')
global FLAVOR_MOCK
tenant = MagicMock()
tenant.tenant_id = 'tenant_id'
FLAVOR_MOCK.flavor_tenants = [tenant]
error = 6
self.assertRaises(ValueError,
flavor_logic.delete_tenant, 'uuid',
'tenant_id',
'transaction')
@patch.object(flavor_logic, 'send_to_rds_if_needed')
@patch.object(flavor_logic, 'get_flavor_by_uuid')
@patch.object(models, 'request')