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.services.image_manager.ims.logic.error_base import \ ErrorStatus, 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 ErrorStatus( 409.2, "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): 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) if image_dict['regions'] or len(existing_region_names) > 0: 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 ErrorStatus(404, 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 ErrorStatus(405, "Cannot delete a 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) 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(405, "not allowed as aggregate status " "have to be Success (either the deletion " "failed on one of the regions or it is " "still in progress)") 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 ErrorStatus(404, '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) 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() 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 exp except Exception as exp: LOG.log_exception("ImageLogic - Failed to add regions", exp) datamanager.rollback() raise exp @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 ErrorStatus(404, '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) 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() 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 replace regions", exp) datamanager.rollback() raise exp except Exception as exp: LOG.log_exception("ImageLogic - Failed to repalce regions", exp) datamanager.rollback() raise exp @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 ErrorStatus(404, '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(400, protect_msg.format(image_uuid)) existing_region_names = sql_image.get_existing_region_names() sql_image.remove_region(region_name) # Get any exception created by previous actions against the database datamanager.flush() send_to_rds_if_needed(sql_image, existing_region_names, "put", transaction_id) if force_delete: datamanager.commit() else: datamanager.rollback() except ErrorStatus as exp: LOG.log_exception("ImageLogic - Failed to update image", 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 ErrorStatus(404, 'image with id: {0} not found'.format( image_uuid)) if sql_image.visibility != "shared": raise ErrorStatus(400, '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 ErrorStatus(409, "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 ErrorStatus(404, 'image {0} not found'.format(image_uuid)) if sql_image.visibility != "shared": raise ValueError('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 ErrorStatus(409, "Duplicate Customer for Image") LOG.log_exception("ImageLogic - Failed to add 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 ErrorStatus(404, 'image {0} not found'.format(image_uuid)) if sql_image.visibility != "shared": raise ValueError( "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) 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 exp 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) 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="put"): if action == "delete": set_regions_action(image_dict, "delete") elif action == "post": set_regions_action(image_dict, "create") else: # put for region in image_dict["regions"]: if region["name"] in existing_region_names: region["action"] = "modify" else: region["action"] = "create" # 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: image_dict["regions"].append( {"name": exist_region_name, "action": "delete"}) 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 ErrorStatus(404, 'Image with id: {0} not found'.format( image_uuid)) sql_image.enabled = int_enabled existing_region_names = sql_image.get_existing_region_names() datamanager.flush() # i want to get any exception created by this # insert method 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 exp except Exception as exp: LOG.log_exception( "ImageLogic - Failed to change image activation value", exp) datamanager.rollback() raise exp