
Instead of broadcasting service request to all regions, IMS service will now send request to only affected regions. Un-affected regions will not receive request to perform a no-op. Change-Id: I4008739676ce8ccd71f72bca57fb33dd05f001bc
621 lines
22 KiB
Python
Executable File
621 lines
22 KiB
Python
Executable File
import time
|
|
|
|
from orm.common.orm_common.injector import injector
|
|
from orm.common.orm_common.utils import utils
|
|
from orm.services.image_manager.ims.logger import get_logger
|
|
from orm.common.orm_common.utils.error_base import \
|
|
ConflictError, ErrorStatus, NotAllowedError, NotFoundError
|
|
from orm.services.image_manager.ims.persistency.sql_alchemy.db_models import \
|
|
ImageCustomer, ImageRegion
|
|
from orm.services.image_manager.ims.persistency.wsme.models import (
|
|
ImageSummary, ImageSummaryResponse, ImageWrapper, RegionWrapper)
|
|
from orm.services.image_manager.ims.utils import utils as ImsUtils
|
|
import oslo_db
|
|
|
|
LOG = get_logger(__name__)
|
|
|
|
di = injector.get_di()
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def create_image(image_wrapper, image_uuid, transaction_id):
|
|
DataManager = di.resolver.unpack(create_image)
|
|
datamanager = DataManager()
|
|
image_wrapper.image.id = image_uuid
|
|
|
|
image_wrapper.image.created_at = str(int(time.time()))
|
|
image_wrapper.image.updated_at = image_wrapper.image.created_at
|
|
|
|
try:
|
|
image_wrapper.handle_region_group()
|
|
image_wrapper.validate_model()
|
|
sql_image = image_wrapper.to_db_model()
|
|
|
|
image_rec = datamanager.get_record('image')
|
|
|
|
datamanager.begin_transaction()
|
|
image_rec.insert(sql_image)
|
|
datamanager.flush() # i want to get any exception created by this
|
|
# insert
|
|
existing_region_names = []
|
|
send_to_rds_if_needed(sql_image, existing_region_names, "post",
|
|
transaction_id)
|
|
|
|
datamanager.commit()
|
|
|
|
ret_image = get_image_by_uuid(image_uuid)
|
|
return ret_image
|
|
|
|
except oslo_db.exception.DBDuplicateEntry as exception:
|
|
utils.delete_uuid(image_uuid)
|
|
raise ConflictError("Image name '{}' already exists".format(
|
|
image_wrapper.image.name))
|
|
|
|
except Exception as exp:
|
|
LOG.log_exception("ImageLogic - Failed to CreateImage", exp)
|
|
datamanager.rollback()
|
|
utils.delete_uuid(image_uuid)
|
|
raise
|
|
|
|
|
|
@di.dependsOn('rds_proxy')
|
|
def send_to_rds_if_needed(sql_image,
|
|
existing_region_names,
|
|
http_action,
|
|
transaction_id,
|
|
optimized=False):
|
|
rds_proxy = di.resolver.unpack(send_to_rds_if_needed)
|
|
if (sql_image.regions and len(sql_image.regions) > 0) or len(
|
|
existing_region_names) > 0:
|
|
image_dict = sql_image.get_proxy_dict()
|
|
update_region_actions(
|
|
image_dict, existing_region_names, http_action, optimized)
|
|
|
|
if image_dict['regions']:
|
|
LOG.debug("Image is valid, sending to RDS Proxy ")
|
|
rds_proxy.send_image(image_dict, transaction_id, http_action)
|
|
else:
|
|
LOG.debug("Group with no regions, not sending to RDS Proxy " + str(
|
|
sql_image))
|
|
else:
|
|
LOG.debug("Image with no regions, not sending to RDS Proxy " + str(
|
|
sql_image))
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def update_image(image_wrapper, image_uuid, transaction_id, http_action="put"):
|
|
DataManager = di.resolver.unpack(update_image)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_wrapper.validate_model('update')
|
|
new_image = image_wrapper.to_db_model()
|
|
new_image.id = image_uuid
|
|
|
|
image_rec = datamanager.get_record('image')
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
|
|
if sql_image is None:
|
|
raise NotFoundError(
|
|
status_code=404,
|
|
message="Image {0} does not exist for update".format(
|
|
image_uuid))
|
|
|
|
image_wrapper.validate_update(sql_image, new_image)
|
|
|
|
datamanager.begin_transaction()
|
|
|
|
new_image.owner = sql_image.owner
|
|
existing_regions = sql_image.get_existing_region_names()
|
|
new_image.created_at = int(sql_image.created_at)
|
|
new_image.updated_at = int(time.time())
|
|
# result = image_rec.delete_image_by_id(image_uuid)
|
|
datamanager.get_session().delete(sql_image)
|
|
# del sql_image
|
|
image_rec.insert(new_image)
|
|
datamanager.flush()
|
|
|
|
send_to_rds_if_needed(new_image, existing_regions, http_action,
|
|
transaction_id)
|
|
|
|
datamanager.commit()
|
|
|
|
ret_image = get_image_by_uuid(image_uuid)
|
|
|
|
return ret_image
|
|
|
|
except Exception as exp:
|
|
datamanager.rollback()
|
|
LOG.log_exception("ImageLogic - Failed to update image", exp)
|
|
raise
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def delete_image_by_uuid(image_uuid, transaction_id):
|
|
DataManager = di.resolver.unpack(delete_image_by_uuid)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
datamanager.begin_transaction()
|
|
image_rec = datamanager.get_record('image')
|
|
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
if sql_image is None:
|
|
message_not_found = "Image '{}' not found".format(image_uuid)
|
|
raise NotFoundError(message_not_found)
|
|
|
|
image_existing_region_names = sql_image.get_existing_region_names()
|
|
if len(image_existing_region_names) > 0:
|
|
# Do not delete an image that still has region(s)
|
|
raise NotAllowedError("Cannot delete an image with regions. "
|
|
"Please delete the regions first and then "
|
|
"delete the image.")
|
|
|
|
# Get status from resource status table
|
|
uuid = [sql_image.id]
|
|
resource_status_dict = utils.get_resource_status_from_db(
|
|
datamanager.get_session(), uuid, True)
|
|
status_model = resource_status_dict.get(sql_image.id)
|
|
|
|
if status_model:
|
|
status = status_model.status
|
|
LOG.debug('Status from resource_status table: {}'.format(status))
|
|
else:
|
|
# Image not found in table, that means it never had any
|
|
# regions. So it is OK to delete it
|
|
status = 'Success'
|
|
LOG.debug('Resource not found in table, so it is OK to delete')
|
|
|
|
if status != 'Success':
|
|
raise ErrorStatus("not allowed as aggregate status "
|
|
"have to be Success (either the deletion "
|
|
"failed on one of the regions or it is "
|
|
"still in progress)",
|
|
405)
|
|
|
|
image_rec.delete_image_by_id(image_uuid)
|
|
datamanager.flush() # i want to get any exception created by this
|
|
|
|
# delete
|
|
datamanager.commit()
|
|
|
|
# after successful image delete, delete image id from uuids table
|
|
utils.delete_uuid(image_uuid)
|
|
|
|
except Exception as exp:
|
|
LOG.log_exception("ImageLogic - Failed to delete image", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def add_regions(image_uuid, regions, transaction_id):
|
|
DataManager = di.resolver.unpack(add_regions)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_rec = datamanager.get_record('image')
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
if not sql_image:
|
|
raise NotFoundError('image with id: {0} not found'.format(
|
|
image_uuid))
|
|
|
|
existing_region_names = sql_image.get_existing_region_names()
|
|
|
|
for region in regions.regions:
|
|
db_region = ImageRegion(region_name=region.name,
|
|
region_type=region.type)
|
|
sql_image.add_region(db_region)
|
|
|
|
# Flush to get any exception, if any, created by
|
|
# previous actions against the database
|
|
datamanager.flush()
|
|
|
|
send_to_rds_if_needed(sql_image, existing_region_names, "put",
|
|
transaction_id, optimized=True)
|
|
|
|
datamanager.commit()
|
|
|
|
image_wrapper = get_image_by_uuid(image_uuid)
|
|
ret = RegionWrapper(regions=image_wrapper.image.regions)
|
|
return ret
|
|
|
|
except ErrorStatus as exp:
|
|
LOG.log_exception("ImageLogic - Failed to add regions", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
except Exception as exp:
|
|
LOG.log_exception("ImageLogic - Failed to add regions", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def replace_regions(image_uuid, regions, transaction_id):
|
|
DataManager = di.resolver.unpack(replace_regions)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_rec = datamanager.get_record('image')
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
if not sql_image:
|
|
raise NotFoundError('image with id: {0} not found'.format(
|
|
image_uuid))
|
|
|
|
existing_region_names = sql_image.get_existing_region_names()
|
|
|
|
sql_image.remove_all_regions()
|
|
datamanager.flush()
|
|
|
|
for region in regions.regions:
|
|
db_region = ImageRegion(region_name=region.name,
|
|
region_type=region.type)
|
|
sql_image.add_region(db_region)
|
|
|
|
# Flush to get any exception, if any, created by
|
|
# previous actions against the database
|
|
datamanager.flush()
|
|
|
|
send_to_rds_if_needed(sql_image, existing_region_names, "put",
|
|
transaction_id, optimized=True)
|
|
|
|
datamanager.commit()
|
|
|
|
image_wrapper = get_image_by_uuid(image_uuid)
|
|
ret = RegionWrapper(regions=image_wrapper.image.regions)
|
|
return ret
|
|
|
|
except ErrorStatus as exp:
|
|
LOG.error("ImageLogic - Failed to replace regions", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
except Exception as exp:
|
|
LOG.error("ImageLogic - Failed to repalce regions", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def delete_region(image_uuid, region_name, transaction_id, force_delete):
|
|
DataManager = di.resolver.unpack(delete_region)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_rec = datamanager.get_record('image')
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
if not sql_image:
|
|
raise NotFoundError('image with id: {0} not found'.format(
|
|
image_uuid))
|
|
# do not allow delete_region for protected images
|
|
if sql_image.protected:
|
|
protect_msg = "Protected image {} cannot be deleted. Please " \
|
|
"update image with protected=false and try again"
|
|
raise ErrorStatus(protect_msg.format(image_uuid), 400)
|
|
|
|
existing_region_names = sql_image.get_existing_region_names()
|
|
sql_image.remove_region(region_name)
|
|
|
|
# Flush to get any exception, if any, created by
|
|
# previous actions against the database
|
|
datamanager.flush()
|
|
|
|
send_to_rds_if_needed(sql_image, existing_region_names, "put",
|
|
transaction_id, optimized=True)
|
|
if force_delete:
|
|
datamanager.commit()
|
|
else:
|
|
datamanager.rollback()
|
|
|
|
except ErrorStatus as exp:
|
|
LOG.log_exception("ImageLogic - Failed to delete region", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
except Exception as exp:
|
|
LOG.log_exception("ImageLogic - Failed to delete region", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
finally:
|
|
datamanager.close()
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def add_customers(image_uuid, customers, transaction_id):
|
|
DataManager = di.resolver.unpack(add_customers)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_rec = datamanager.get_record('image')
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
if not sql_image:
|
|
raise NotFoundError('image with id: {0} not found'.format(
|
|
image_uuid))
|
|
|
|
if sql_image.visibility != "shared":
|
|
raise ErrorStatus('Customer can only be added to shared image.')
|
|
|
|
existing_region_names = sql_image.get_existing_region_names()
|
|
|
|
for user in customers.customers:
|
|
db_Customer = ImageCustomer(customer_id=user)
|
|
sql_image.add_customer(db_Customer)
|
|
|
|
datamanager.flush() # i want to get any exception created by
|
|
# previous actions against the database
|
|
send_to_rds_if_needed(sql_image, existing_region_names, "put",
|
|
transaction_id)
|
|
datamanager.commit()
|
|
|
|
ret_image = get_image_by_uuid(image_uuid)
|
|
return ret_image
|
|
|
|
except Exception as exp:
|
|
if 'conflicts with persistent instance' in str(exp) or \
|
|
'Duplicate entry' in str(exp):
|
|
raise ConflictError("Duplicate Customer for Image")
|
|
LOG.log_exception("ImageLogic - Failed to add Customers", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def replace_customers(image_uuid, customers, transaction_id):
|
|
DataManager = di.resolver.unpack(replace_customers)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_rec = datamanager.get_record('image')
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
if not sql_image:
|
|
raise NotFoundError('image {0} not found'.format(image_uuid))
|
|
|
|
if sql_image.visibility != "shared":
|
|
raise ErrorStatus('Customer can only be replaced with shared Image')
|
|
|
|
existing_region_names = sql_image.get_existing_region_names()
|
|
sql_image.remove_all_customers()
|
|
datamanager.flush()
|
|
|
|
for cust in customers.customers:
|
|
db_Customer = ImageCustomer(customer_id=cust)
|
|
sql_image.add_customer(db_Customer)
|
|
|
|
datamanager.flush() # get exception created by previous db actions
|
|
send_to_rds_if_needed(sql_image, existing_region_names, "put",
|
|
transaction_id)
|
|
datamanager.commit()
|
|
|
|
ret_image = get_image_by_uuid(image_uuid)
|
|
return ret_image
|
|
|
|
except Exception as exp:
|
|
if 'conflicts with persistent instance' in str(exp) or \
|
|
'Duplicate entry' in str(exp):
|
|
raise ConflictError("Duplicate customer for image")
|
|
LOG.log_exception("ImageLogic - Failed to replace customers", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def delete_customer(image_uuid, customer_id, transaction_id):
|
|
DataManager = di.resolver.unpack(delete_customer)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_rec = datamanager.get_record('image')
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
if not sql_image:
|
|
raise NotFoundError('image {0} not found'.format(image_uuid))
|
|
|
|
if sql_image.visibility != "shared":
|
|
raise ErrorStatus(
|
|
"Customer can only be deleted from shared image {}".format(
|
|
image_uuid))
|
|
|
|
existing_region_names = sql_image.get_existing_region_names()
|
|
sql_image.remove_customer(customer_id)
|
|
|
|
datamanager.flush() # i want to get any exception created by
|
|
# previous actions against the database
|
|
send_to_rds_if_needed(sql_image, existing_region_names, "put",
|
|
transaction_id)
|
|
datamanager.commit()
|
|
|
|
except Exception as exp:
|
|
LOG.log_exception("ImageLogic - Failed to delete Customer", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
|
|
|
|
def set_resource_status(sql_image, status_model):
|
|
image_wrapper = ImageWrapper.from_db_model(sql_image)
|
|
image_wrapper.image.status = 'no regions'
|
|
|
|
if status_model and status_model.regions:
|
|
for region in image_wrapper.image.regions:
|
|
for status in status_model.regions:
|
|
if status.region == region.name:
|
|
region.status = status.status
|
|
if status.error_msg:
|
|
region.set_error_message(status.error_msg)
|
|
|
|
image_wrapper.image.status = status_model.status
|
|
|
|
return image_wrapper
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def get_image_by_uuid(image_uuid, query_by_id_or_name=False):
|
|
"""This function includes an optional boolean parameter
|
|
query_by_id_or_name. If query_by_id_or_name evaluates to true, IMS logic
|
|
will fetch the image record whose "image_uuid" parameter value matches
|
|
either the image id or name value. Otherwise, it defaults to query by
|
|
image ID value only.
|
|
"""
|
|
DataManager = di.resolver.unpack(get_image_by_uuid)
|
|
datamanager = DataManager()
|
|
|
|
LOG.debug("Get image by uuid : {}".format(image_uuid))
|
|
try:
|
|
|
|
datamanager.begin_transaction()
|
|
image_rec = datamanager.get_record('image')
|
|
|
|
# Only the get_image API will pass the optional parameter and
|
|
# set it to true
|
|
if query_by_id_or_name:
|
|
sql_image = image_rec.get_image(image_uuid)
|
|
else:
|
|
# all other image APIs will NOT pass the optional parameter
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
|
|
if not sql_image:
|
|
raise NotFoundError(
|
|
status_code=404,
|
|
message="Image {0} not found ".format(image_uuid))
|
|
|
|
# Get the status from resource table
|
|
uuid = [sql_image.id]
|
|
resource_status_dict = utils.get_resource_status_from_db(
|
|
datamanager.get_session(), uuid, True)
|
|
status_model = resource_status_dict.get(sql_image.id)
|
|
|
|
image_wrapper = set_resource_status(sql_image, status_model)
|
|
|
|
# get image own link
|
|
image_wrapper.image.links, image_wrapper.image.self_link = \
|
|
ImsUtils.get_server_links(image_uuid)
|
|
|
|
# convert time stamp format to human readable time
|
|
image_wrapper.image.created_at = ImsUtils.convert_time_human(
|
|
image_wrapper.image.created_at)
|
|
image_wrapper.image.updated_at = ImsUtils.convert_time_human(
|
|
image_wrapper.image.updated_at)
|
|
|
|
except NotFoundError as exp:
|
|
datamanager.rollback()
|
|
LOG.log_exception("ImageLogic - Failed to update image", exp)
|
|
raise
|
|
|
|
except Exception as exp:
|
|
datamanager.rollback()
|
|
LOG.log_exception("ImageLogic - Failed to delete Customer", exp)
|
|
raise
|
|
|
|
return image_wrapper
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def get_image_list_by_params(visibility, region, Customer):
|
|
DataManager = di.resolver.unpack(get_image_list_by_params)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_record = datamanager.get_record('image')
|
|
sql_images = image_record.get_images_by_criteria(visibility=visibility,
|
|
region=region,
|
|
Customer=Customer)
|
|
|
|
response = ImageSummaryResponse()
|
|
if sql_images:
|
|
uuids = [sql_image.id for sql_image in sql_images]
|
|
resource_status_dict = utils.get_resource_status_from_db(
|
|
datamanager.get_session(), uuids, True)
|
|
|
|
for sql_image in sql_images:
|
|
status_model = resource_status_dict.get(sql_image.id)
|
|
wsme_image = set_resource_status(sql_image, status_model)
|
|
image_summary = ImageSummary.from_wsme(wsme_image)
|
|
response.images.append(image_summary)
|
|
|
|
return response
|
|
|
|
except ErrorStatus as exp:
|
|
LOG.log_exception("ImageLogic - Failed to get list", exp)
|
|
raise
|
|
except Exception as exp:
|
|
LOG.log_exception("ImageLogic - Failed to get list", exp)
|
|
raise
|
|
|
|
|
|
def update_region_actions(image_dict, existing_region_names, action,
|
|
optimized):
|
|
if action == "delete":
|
|
set_regions_action(image_dict, "delete")
|
|
elif action == "post":
|
|
set_regions_action(image_dict, "create")
|
|
else: # put
|
|
requested_regions = []
|
|
for region in image_dict["regions"]:
|
|
if region["name"] in existing_region_names:
|
|
region["action"] = "modify"
|
|
if not optimized:
|
|
requested_regions.append(region)
|
|
else:
|
|
region["action"] = "create"
|
|
requested_regions.append(region)
|
|
|
|
# add deleted regions
|
|
for exist_region_name in existing_region_names:
|
|
if region_name_exist_in_regions(exist_region_name,
|
|
image_dict["regions"]):
|
|
continue
|
|
else:
|
|
requested_regions.append({"name": exist_region_name,
|
|
"action": "delete"})
|
|
|
|
image_dict["regions"] = requested_regions
|
|
|
|
|
|
def region_name_exist_in_regions(region_name, regions):
|
|
for region in regions:
|
|
if region["name"] == region_name:
|
|
return True
|
|
return False
|
|
|
|
|
|
def set_regions_action(image_dict, action):
|
|
for region in image_dict["regions"]:
|
|
region["action"] = action
|
|
|
|
|
|
@di.dependsOn('data_manager')
|
|
def enable_image(image_uuid, int_enabled, transaction_id):
|
|
DataManager = di.resolver.unpack(enable_image)
|
|
datamanager = DataManager()
|
|
|
|
try:
|
|
image_rec = datamanager.get_record('image')
|
|
sql_image = image_rec.get_image_by_id(image_uuid)
|
|
if not sql_image:
|
|
raise NotFoundError('Image with id: {0} not found'.format(
|
|
image_uuid))
|
|
|
|
sql_image.enabled = int_enabled
|
|
existing_region_names = sql_image.get_existing_region_names()
|
|
|
|
# Flush to see if there is any exception
|
|
datamanager.flush()
|
|
|
|
send_to_rds_if_needed(sql_image, existing_region_names, "put",
|
|
transaction_id)
|
|
|
|
datamanager.commit()
|
|
|
|
ret_image = get_image_by_uuid(image_uuid)
|
|
return ret_image
|
|
|
|
except ErrorStatus as exp:
|
|
LOG.log_exception(
|
|
"ImageLogic - Failed to change image activation value", exp)
|
|
datamanager.rollback()
|
|
raise
|
|
except Exception as exp:
|
|
LOG.log_exception(
|
|
"ImageLogic - Failed to change image activation value", exp)
|
|
datamanager.rollback()
|
|
raise
|