import time from orm.common.orm_common.injector import injector 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 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 Exception as exp: LOG.log_exception("ImageLogic - Failed to CreateImage", exp) datamanager.rollback() 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 to send to RDS - sending to RDS Proxy ") rds_proxy.send_image(image_dict, transaction_id, http_action) else: LOG.debug("Group with no regions - wasn't send to RDS Proxy " + str( sql_image)) else: LOG.debug("Image with no regions - wasn't send 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('rds_proxy') @di.dependsOn('data_manager') def delete_image_by_uuid(image_uuid, transaction_id): rds_proxy, 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: raise NotFoundError(message="Image '{}' not found".format(image_uuid)) image_existing_region_names = sql_image.get_existing_region_names() if len(image_existing_region_names) > 0: # Do not delete a flavor that still has some regions raise ErrorStatus(405, "Cannot delete a image with regions. " "Please delete the regions first and then " "delete the image. ") # Get status from RDS image_status = rds_proxy.get_status(sql_image.id, False) status_resp = None if image_status.status_code == 200: status_resp = image_status.json()['status'] LOG.debug('RDS returned status: {}'.format(status_resp)) elif image_status.status_code == 404: status_resp = 'Success' else: # fail to get status from rds raise ErrorStatus(500, "fail to get status for this resource " "deleting image not allowed ") if status_resp != '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() 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, on_success_by_rds, 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 on_success_by_rds and not sql_image: return 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) datamanager.flush() # Get any exception created by # previous actions against the database if on_success_by_rds: datamanager.commit() else: 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 == "public": raise ErrorStatus(400, 'Cannot add Customers to public 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 exp.message or 'Duplicate entry' in exp.message: 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 == "public": raise ValueError('Cannot add Customers to public 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 exp.message or 'Duplicate entry' in exp.message: 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 trying to delete the only one Customer then return value error if sql_image.visibility == "public": raise ValueError("Image {} is public, no customers".format(image_uuid)) if len(sql_image.customers) == 1 and \ sql_image.customers[0].customer_id == customer_id: raise ValueError('Private Image must have at least one Customer - ' 'You are trying to delete the only one Customer') 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 @di.dependsOn('data_manager') @di.dependsOn('rds_proxy') 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, rds_proxy = 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)) image_wrapper = ImageWrapper.from_db_model(sql_image) # 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) # Get the status from RDS image_status = rds_proxy.get_status(image_wrapper.image.id, False) if image_status.status_code == 404: # image not on rds resource table - applicable to images created with no region assigned image_wrapper.image.status = 'no regions' elif image_status.status_code == 200: image_status = image_status.json() if image_wrapper.image.regions: image_wrapper.image.status = image_status['status'] else: image_wrapper.image.status = 'no regions' # update status for all regions for result_regions in image_wrapper.image.regions: for status_region in image_status['regions']: if result_regions.name == status_region['region']: result_regions.status = status_region['status'] if status_region['error_msg']: result_regions.set_error_message(status_region['error_msg']) # status codes not falling under 404 (not found) or 200 (success) else: raise ErrorStatus(500, "unsuccessful GET - failed to get status for this resource") 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') @di.dependsOn('rds_proxy') def get_image_list_by_params(visibility, region, Customer): DataManager, rds_proxy = 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 = ','.join(str("\'" + sql_image.id + "\'") for sql_image in sql_images if sql_image and sql_image.id) resource_status_dict = image_record.get_images_status_by_uuids(uuids) for sql_image in sql_images: image = ImageSummary.from_db_model(sql_image) if sql_image.id: status = resource_status_dict.get(sql_image.id) image.status = not status and 'no regions' or status response.images.append(image) 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