Chi Lo aa15e29fb3 Refactor catching exception at services controller level
Change-Id: I17ba7b9f1142b9bc52e3facc0813cf962ecf87c0
2020-12-10 10:25:45 -08:00

297 lines
12 KiB
Python
Executable File

"""create resource moudle."""
import logging
import threading
import time
from orm.common.orm_common.utils.error_base import ConflictError, ErrorStatus
from orm.services.flavor_manager.fms_rest.data.sql_alchemy.data_manager \
import DataManager
from orm.services.resource_distributor.rds.ordupdate.ord_notifier \
import notify_ord, NoTokenError
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)
from orm.services.resource_distributor.rds.services.model.resource_input \
import ResourceData as InputData
from orm.services.resource_distributor.rds.utils import utils, uuid_utils
from pecan import conf, request
LOG = logging.getLogger(__name__)
def _get_inputs_from_resource_type(jsondata,
resource_type,
external_transaction_id,
operation="create"):
if resource_type == "customer" or resource_type == "group":
input_data = InputData(resource_id=jsondata['uuid'],
resource_type=resource_type,
operation=operation,
targets=jsondata['regions'],
model=jsondata,
external_transaction_id=external_transaction_id)
elif resource_type == "flavor" or resource_type == "image":
input_data = InputData(resource_id=jsondata['id'],
resource_type=resource_type,
operation=operation,
targets=jsondata['regions'],
model=jsondata,
external_transaction_id=external_transaction_id)
else:
raise ErrorStatus("no support for resource %s" % resource_type)
return input_data
def _region_valid(region):
if 'rms_status' in region and region[
'rms_status'] not in conf.allow_region_statuses:
return False
return True
def _create_or_update_resource_status(input_data, target, error_msg='',
status="Submitted"):
# check rms region status
if not _region_valid(target):
error_msg = "Not sent to ord as status equal to " + \
target['rms_status']
raise ErrorStatus(error_msg)
LOG.debug("save status as %s" % status)
data_to_save = dict(
timestamp=int(time.time() * 1000),
region=target['name'],
resource_id=input_data.resource_id,
status=status,
transaction_id=input_data.transaction_id,
error_code='',
error_msg=error_msg,
resource_operation=target['action'],
resource_type=input_data.resource_type,
ord_notifier_id='')
regionResourceIdStatus.add_status(data_to_save)
LOG.debug("status %s saved" % status)
def _set_all_statuses_to_error(input_data, message=None):
targets = input_data.targets
for target in targets:
_create_or_update_resource_status(input_data=input_data, target=target,
error_msg=message or 'system error',
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_template_data(input_data):
"""create data.
: build yaml string
:param jsondata: full json in request body
:param resource_type: eg... Customer
:return: return list of dictionaries with yaml string
"""
jsondata = input_data.model
targetslist = []
targets = input_data.targets
for target in targets:
# save start status to submitted for each region
_create_or_update_resource_status(input_data, target)
if not _region_valid(target):
continue
if target['action'] == "delete":
yamldata = "delete"
elif input_data.resource_type == "customer":
yamldata = yaml_customer_builder.yamlbuilder(jsondata, target)
elif input_data.resource_type == "group":
yamldata = yaml_group_builder.yamlbuilder(jsondata, target)
elif input_data.resource_type == "flavor":
# save off the original "input_data.model['tenants']" value
tenants_list = input_data.model['tenants']
if input_data.model['visibility'] == 'private':
ok_tenants_list = []
ok_tenants = {}
# skip tenant validation if tenants list is empty
if tenants_list:
valid_tenants_list = get_valid_tenants(
# input_data.model['tenants'], target['name'])
tenants_list, 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'] = ''
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)
# restore the original "input_data.model['tenants']" value
input_data.model['tenants'] = tenants_list
elif input_data.resource_type == "image":
yamldata = yaml_image_builder.yamlbuilder(jsondata, target)
targetslist.append({"region_id": target['name'],
"resource_type": input_data.resource_type,
"resource_name": input_data.resource_id,
"template_data": yamldata,
"operation": target['action']})
return targetslist
def send_data_to_ord(tracking_id, transaction_id, resource_list,
application_id, user_id, headers={}):
LOG.info("Prepping data to send to ord ...")
# This method is called also in case exception raised.
# Notification to ords will not be sent but status db and audit
# will be updated.
for resource in resource_list:
try:
resource_stack_name = 's_{}'.format(resource["resource_name"])
notify_ord(transaction_id,
tracking_id,
resource["resource_type"],
0, # This is the resource-template-version
resource_stack_name, # This is the stack name
resource["resource_name"], # This is the resource_id
resource["operation"],
resource["region_id"],
resource["template_data"],
application_id, # application_id is not available
user_id, # user_id is not available
"NA", # external_id is not available
headers)
except NoTokenError as e:
LOG.error("Failed to get token for resource id {} "
"region {}".format(resource["resource_name"],
resource["region_id"]))
except Exception as e:
LOG.error("Error in updating ORD! Error: {}".format(
str(e)))
def _save_resource_to_ord(tracking_id, transaction_id,
resource_list, application_id, user_id,
headers={}):
thread = threading.Thread(target=send_data_to_ord,
args=(tracking_id,
transaction_id,
resource_list,
application_id,
user_id,
headers))
thread.start()
def _submit_template_data(uuid, tranid, targetslist):
application_id = request.headers[
'X-RANGER-Client'] if 'X-RANGER-Client' in request.headers else \
'NA'
user_id = request.headers[
'X-RANGER-Requester'] if 'X-RANGER-Requester' in request.headers else \
''
headers = {}
headers['X-Auth-Region'] = request.headers[
'X-Auth-Region'] if 'X-Auth-Region' in \
request.headers else ''
headers['X-Auth-Token'] = request.headers[
'X-Auth-Token'] if 'X-Auth-Token' in \
request.headers else ''
_save_resource_to_ord(uuid,
tranid,
targetslist,
application_id,
user_id,
headers)
def _check_resource_status(input_data):
status = conf.block_by_status
# check if any of the region is blocked by "Submitted" state
regions_by_resource = \
regionResourceIdStatus.get_regions_by_status_resource_id(
status,
input_data.resource_id,
input_data.resource_type)
# if any not ready return 409
if regions_by_resource is not None and regions_by_resource.regions:
regions_in_error = [reg.region for reg in regions_by_resource.regions]
msg = "Previous operation still in %s state for regions: %s " % (
status, regions_in_error)
raise ConflictError(msg)
def _generate_resource_data(input_data):
"""create resource."""
LOG.debug("build yaml file for %s id: %s" % (
input_data.resource_type, input_data.resource_id))
targetslist = _create_template_data(input_data)
LOG.debug("submit yaml to ranger-agent...")
_submit_template_data(input_data.resource_id,
input_data.transaction_id,
targetslist)
# User domain is only used in the case that a customer template is being
# generated, as certain builds of heat require user_domain in order to
# validate roles
def main(jsondata, external_transaction_id, resource_type, operation):
"""main function handle resource operation."""
LOG.info("got %s for %s resource" % (operation, resource_type))
try:
input_data = _get_inputs_from_resource_type(
jsondata=jsondata,
resource_type=resource_type,
operation=operation,
external_transaction_id=external_transaction_id
)
LOG.debug("iterate through the regions see if none in submitted")
_check_resource_status(input_data)
LOG.debug("get uuid from uuid generator")
input_data.transaction_id = uuid_utils.get_random_uuid()
LOG.debug("uuid ={}".format(input_data.transaction_id))
# add regions status from rms (to check if it down)
input_data.targets = utils.add_rms_status_to_regions(
input_data.targets, input_data.resource_type)
_generate_resource_data(input_data)
except (ConflictError, ErrorStatus) as exp:
LOG.error(str(exp))
raise
except Exception as e:
_set_all_statuses_to_error(input_data)
LOG.error("deleting failed, Error: {}".format(str(e)))
raise
return input_data.resource_id