297 lines
12 KiB
Python
Executable File
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
|