def _create_from_image(self, context, volume, image_location, image_id, image_meta, image_service, **kwargs): LOG.debug( "Cloning %(volume_id)s from image %(image_id)s " " at location %(image_location)s.", { 'volume_id': volume.id, 'image_location': image_location, 'image_id': image_id }) # NOTE(e0ne): check for free space in image_conversion_dir before # image downloading. if (CONF.image_conversion_dir and not os.path.exists(CONF.image_conversion_dir)): os.makedirs(CONF.image_conversion_dir) image_utils.check_available_space(CONF.image_conversion_dir, image_meta['size'], image_id) virtual_size = image_meta.get('virtual_size') if virtual_size: virtual_size = image_utils.check_virtual_size( virtual_size, volume.size, image_id) # Create the volume from an image. # # First see if the driver can clone the image directly. # # NOTE (singn): two params need to be returned # dict containing provider_location for cloned volume # and clone status. # NOTE (lixiaoy1): Currently all images are raw data, we can't # use clone_image to copy data if new volume is encrypted. volume_is_encrypted = volume.encryption_key_id is not None cloned = False model_update = None if not volume_is_encrypted: model_update, cloned = self.driver.clone_image( context, volume, image_location, image_meta, image_service) # Try and clone the image if we have it set as a glance location. if not cloned and 'cinder' in CONF.allowed_direct_url_schemes: model_update, cloned = self._clone_image_volume( context, volume, image_location, image_meta) # Try and use the image cache, and download if not cached. if not cloned: model_update = self._create_from_image_cache_or_download( context, volume, image_location, image_id, image_meta, image_service) self._handle_bootable_volume_glance_meta(context, volume, image_id=image_id, image_meta=image_meta) return model_update
def _create_from_image_cache_or_download(self, context, volume, image_location, image_id, image_meta, image_service, update_cache=False): # NOTE(e0ne): check for free space in image_conversion_dir before # image downloading. # NOTE(mnaser): This check *only* happens if the backend is not able # to clone volumes and we have to resort to downloading # the image from Glance and uploading it. if CONF.image_conversion_dir: fileutils.ensure_tree(CONF.image_conversion_dir) try: image_utils.check_available_space(CONF.image_conversion_dir, image_meta['size'], image_id) except exception.ImageTooBig as err: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail=message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE, exception=err) # Try and use the image cache. should_create_cache_entry = False cloned = False model_update = None if self.image_volume_cache: internal_context = cinder_context.get_internal_tenant_context() if not internal_context: LOG.info('Unable to get Cinder internal context, will ' 'not use image-volume cache.') else: try: model_update, cloned = self._create_from_image_cache( context, internal_context, volume, image_id, image_meta) except exception.CinderException as e: LOG.warning( 'Failed to create volume from image-volume ' 'cache, image will be downloaded from Glance. ' 'Error: %(exception)s', {'exception': e}) # If an exception occurred when cloning the image-volume, # it may be the image-volume reached its snapshot limit. # Create another "fresh" cache entry. update_cache = True # Don't cache unless directed. if not cloned and update_cache: should_create_cache_entry = True # cleanup consistencygroup field in the volume, # because when creating cache entry, it will need # to update volume object. self._cleanup_cg_in_volume(volume) # Fall back to default behavior of creating volume, # download the image data and copy it into the volume. original_size = volume.size backend_name = volume_utils.extract_host(volume.service_topic_queue) try: if not cloned: try: with image_utils.TemporaryImages.fetch( image_service, context, image_id, backend_name) as tmp_image: if CONF.verify_glance_signatures != 'disabled': # Verify image signature via reading content from # temp image, and store the verification flag if # required. verified = \ image_utils.verify_glance_image_signature( context, image_service, image_id, tmp_image) self.db.volume_glance_metadata_bulk_create( context, volume.id, {'signature_verified': verified}) # Try to create the volume as the minimal size, # then we can extend once the image has been # downloaded. data = image_utils.qemu_img_info(tmp_image) virtual_size = image_utils.check_virtual_size( data.virtual_size, volume.size, image_id) if should_create_cache_entry: if virtual_size and virtual_size != original_size: volume.size = virtual_size volume.save() model_update = self._create_from_image_download( context, volume, image_location, image_meta, image_service) except exception.ImageTooBig as e: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail=message_field.Detail. NOT_ENOUGH_SPACE_FOR_IMAGE, exception=e) except exception.ImageSignatureVerificationException as err: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail=message_field.Detail. SIGNATURE_VERIFICATION_FAILED, exception=err) if should_create_cache_entry: # Update the newly created volume db entry before we clone it # for the image-volume creation. if model_update: volume.update(model_update) volume.save() self.manager._create_image_cache_volume_entry( internal_context, volume, image_id, image_meta) finally: # If we created the volume as the minimal size, extend it back to # what was originally requested. If an exception has occurred or # extending it back failed, we still need to put this back before # letting it be raised further up the stack. if volume.size != original_size: try: self.driver.extend_volume(volume, original_size) finally: volume.size = original_size volume.save() return model_update
def _create_from_image(self, context, volume, image_location, image_id, image_meta, image_service, **kwargs): LOG.debug("Cloning %(volume_id)s from image %(image_id)s " " at location %(image_location)s.", {'volume_id': volume.id, 'image_location': image_location, 'image_id': image_id}) # NOTE(e0ne): check for free space in image_conversion_dir before # image downloading. if (CONF.image_conversion_dir and not os.path.exists(CONF.image_conversion_dir)): os.makedirs(CONF.image_conversion_dir) try: image_utils.check_available_space(CONF.image_conversion_dir, image_meta['size'], image_id) except exception.ImageTooBig as err: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail=message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE, exception=err) virtual_size = image_meta.get('virtual_size') if virtual_size: virtual_size = image_utils.check_virtual_size(virtual_size, volume.size, image_id) # Create the volume from an image. # # First see if the driver can clone the image directly. # # NOTE (singn): two params need to be returned # dict containing provider_location for cloned volume # and clone status. # NOTE (lixiaoy1): Currently all images are raw data, we can't # use clone_image to copy data if new volume is encrypted. volume_is_encrypted = volume.encryption_key_id is not None cloned = False model_update = None if not volume_is_encrypted: model_update, cloned = self.driver.clone_image(context, volume, image_location, image_meta, image_service) # Try and clone the image if we have it set as a glance location. if not cloned and 'cinder' in CONF.allowed_direct_url_schemes: model_update, cloned = self._clone_image_volume(context, volume, image_location, image_meta) # Try and use the image cache, and download if not cached. if not cloned: model_update = self._create_from_image_cache_or_download( context, volume, image_location, image_id, image_meta, image_service) self._handle_bootable_volume_glance_meta(context, volume, image_id=image_id, image_meta=image_meta) return model_update
def _create_from_image(self, context, volume, image_location, image_id, image_meta, image_service, **kwargs): LOG.debug("Cloning %(volume_id)s from image %(image_id)s " " at location %(image_location)s.", {'volume_id': volume.id, 'image_location': image_location, 'image_id': image_id}) # NOTE(e0ne): check for free space in image_conversion_dir before # image downloading. if (CONF.image_conversion_dir and not os.path.exists(CONF.image_conversion_dir)): os.makedirs(CONF.image_conversion_dir) image_utils.check_available_space(CONF.image_conversion_dir, image_meta['size'], image_id) virtual_size = image_meta.get('virtual_size') if virtual_size: virtual_size = image_utils.check_virtual_size(virtual_size, volume.size, image_id) # Create the volume from an image. # # First see if the driver can clone the image directly. # # NOTE (singn): two params need to be returned # dict containing provider_location for cloned volume # and clone status. # NOTE (lixiaoy1): Currently all images are raw data, we can't # use clone_image to copy data if new volume is encrypted. volume_is_encrypted = volume.encryption_key_id is not None cloned = False model_update = None if not volume_is_encrypted: model_update, cloned = self.driver.clone_image(context, volume, image_location, image_meta, image_service) # Try and clone the image if we have it set as a glance location. if not cloned and 'cinder' in CONF.allowed_direct_url_schemes: model_update, cloned = self._clone_image_volume(context, volume, image_location, image_meta) # Try and use the image cache. should_create_cache_entry = False if self.image_volume_cache and not cloned: internal_context = cinder_context.get_internal_tenant_context() if not internal_context: LOG.info(_LI('Unable to get Cinder internal context, will ' 'not use image-volume cache.')) else: model_update, cloned = self._create_from_image_cache( context, internal_context, volume, image_id, image_meta ) # Don't cache encrypted volume. if not cloned and not volume_is_encrypted: should_create_cache_entry = True # Fall back to default behavior of creating volume, # download the image data and copy it into the volume. original_size = volume.size backend_name = volume_utils.extract_host(volume.service_topic_queue) try: if not cloned: with image_utils.TemporaryImages.fetch( image_service, context, image_id, backend_name) as tmp_image: # Try to create the volume as the minimal size, then we can # extend once the image has been downloaded. data = image_utils.qemu_img_info(tmp_image) virtual_size = image_utils.check_virtual_size( data.virtual_size, volume.size, image_id) if should_create_cache_entry: if virtual_size and virtual_size != original_size: volume.size = virtual_size volume.save() model_update = self._create_from_image_download( context, volume, image_location, image_id, image_service ) if should_create_cache_entry: # Update the newly created volume db entry before we clone it # for the image-volume creation. if model_update: volume.update(model_update) volume.save() self.manager._create_image_cache_volume_entry(internal_context, volume, image_id, image_meta) finally: # If we created the volume as the minimal size, extend it back to # what was originally requested. If an exception has occurred we # still need to put this back before letting it be raised further # up the stack. if volume.size != original_size: self.driver.extend_volume(volume, original_size) volume.size = original_size volume.save() self._handle_bootable_volume_glance_meta(context, volume, image_id=image_id, image_meta=image_meta) return model_update
def _create_from_image(self, context, volume, image_location, image_id, image_meta, image_service, **kwargs): LOG.debug("Cloning %(volume_id)s from image %(image_id)s " " at location %(image_location)s.", {'volume_id': volume.id, 'image_location': image_location, 'image_id': image_id}) # NOTE(e0ne): check for free space in image_conversion_dir before # image downloading. if (CONF.image_conversion_dir and not os.path.exists(CONF.image_conversion_dir)): os.makedirs(CONF.image_conversion_dir) try: # cinder should not check free space in conversion directory # if it's creating volume from image snapshot (Bug1683228). # If image disk format is other than raw, cinder should # convert it (this means free space check will be needed). if ('cinder' in CONF.allowed_direct_url_schemes and image_meta.get('disk_format') == 'raw'): LOG.debug("Creating volume from image snapshot. " "Skipping free space check on image " "convert path.") else: image_utils.check_available_space( CONF.image_conversion_dir, image_meta['size'], image_id) except exception.ImageTooBig as err: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail=message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE, exception=err) virtual_size = image_meta.get('virtual_size') if virtual_size: virtual_size = image_utils.check_virtual_size(virtual_size, volume.size, image_id) # Create the volume from an image. # # First see if the driver can clone the image directly. # # NOTE (singn): two params need to be returned # dict containing provider_location for cloned volume # and clone status. # NOTE (lixiaoy1): Currently all images are raw data, we can't # use clone_image to copy data if new volume is encrypted. volume_is_encrypted = volume.encryption_key_id is not None cloned = False model_update = None if not volume_is_encrypted: model_update, cloned = self.driver.clone_image(context, volume, image_location, image_meta, image_service) # Try and clone the image if we have it set as a glance location. if not cloned and 'cinder' in CONF.allowed_direct_url_schemes: model_update, cloned = self._clone_image_volume(context, volume, image_location, image_meta) # Try and use the image cache, and download if not cached. if not cloned: model_update = self._create_from_image_cache_or_download( context, volume, image_location, image_id, image_meta, image_service) self._handle_bootable_volume_glance_meta(context, volume, image_id=image_id, image_meta=image_meta) return model_update
def _create_from_image_cache_or_download(self, context, volume, image_location, image_id, image_meta, image_service): # NOTE(e0ne): check for free space in image_conversion_dir before # image downloading. # NOTE(mnaser): This check *only* happens if the backend is not able # to clone volumes and we have to resort to downloading # the image from Glance and uploading it. if (CONF.image_conversion_dir and not os.path.exists(CONF.image_conversion_dir)): os.makedirs(CONF.image_conversion_dir) try: image_utils.check_available_space(CONF.image_conversion_dir, image_meta['size'], image_id) except exception.ImageTooBig as err: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail=message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE, exception=err) # Try and use the image cache. should_create_cache_entry = False cloned = False model_update = None if self.image_volume_cache: internal_context = cinder_context.get_internal_tenant_context() if not internal_context: LOG.info('Unable to get Cinder internal context, will ' 'not use image-volume cache.') else: model_update, cloned = self._create_from_image_cache( context, internal_context, volume, image_id, image_meta) # Don't cache encrypted volume. if not cloned and not volume.encryption_key_id: should_create_cache_entry = True # cleanup consistencygroup field in the volume, # because when creating cache entry, it will need # to update volume object. self._cleanup_cg_in_volume(volume) # Fall back to default behavior of creating volume, # download the image data and copy it into the volume. original_size = volume.size backend_name = volume_utils.extract_host(volume.service_topic_queue) try: if not cloned: try: with image_utils.TemporaryImages.fetch( image_service, context, image_id, backend_name) as tmp_image: # Try to create the volume as the minimal size, # then we can extend once the image has been # downloaded. data = image_utils.qemu_img_info(tmp_image) virtual_size = image_utils.check_virtual_size( data.virtual_size, volume.size, image_id) if should_create_cache_entry: if virtual_size and virtual_size != original_size: volume.size = virtual_size volume.save() model_update = self._create_from_image_download( context, volume, image_location, image_meta, image_service) except exception.ImageTooBig as e: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail=message_field.Detail. NOT_ENOUGH_SPACE_FOR_IMAGE, exception=e) if should_create_cache_entry: # Update the newly created volume db entry before we clone it # for the image-volume creation. if model_update: volume.update(model_update) volume.save() self.manager._create_image_cache_volume_entry( internal_context, volume, image_id, image_meta) finally: # If we created the volume as the minimal size, extend it back to # what was originally requested. If an exception has occurred or # extending it back failed, we still need to put this back before # letting it be raised further up the stack. if volume.size != original_size: try: self.driver.extend_volume(volume, original_size) finally: volume.size = original_size volume.save() return model_update
def _create_from_image_cache_or_download(self, context, volume, image_location, image_id, image_meta, image_service, update_cache=False): # NOTE(e0ne): check for free space in image_conversion_dir before # image downloading. # NOTE(mnaser): This check *only* happens if the backend is not able # to clone volumes and we have to resort to downloading # the image from Glance and uploading it. if CONF.image_conversion_dir: fileutils.ensure_tree(CONF.image_conversion_dir) try: image_utils.check_available_space( CONF.image_conversion_dir, image_meta['size'], image_id) except exception.ImageTooBig as err: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail=message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE, exception=err) # Try and use the image cache. should_create_cache_entry = False cloned = False model_update = None if self.image_volume_cache: internal_context = cinder_context.get_internal_tenant_context() if not internal_context: LOG.info('Unable to get Cinder internal context, will ' 'not use image-volume cache.') else: try: model_update, cloned = self._create_from_image_cache( context, internal_context, volume, image_id, image_meta ) except exception.CinderException as e: LOG.warning('Failed to create volume from image-volume ' 'cache, image will be downloaded from Glance. ' 'Error: %(exception)s', {'exception': e}) # If an exception occurred when cloning the image-volume, # it may be the image-volume reached its snapshot limit. # Create another "fresh" cache entry. update_cache = True # Don't cache unless directed. if not cloned and update_cache: should_create_cache_entry = True # cleanup consistencygroup field in the volume, # because when creating cache entry, it will need # to update volume object. self._cleanup_cg_in_volume(volume) # Fall back to default behavior of creating volume, # download the image data and copy it into the volume. original_size = volume.size backend_name = volume_utils.extract_host(volume.service_topic_queue) try: if not cloned: try: with image_utils.TemporaryImages.fetch( image_service, context, image_id, backend_name) as tmp_image: if CONF.verify_glance_signatures != 'disabled': # Verify image signature via reading content from # temp image, and store the verification flag if # required. verified = \ image_utils.verify_glance_image_signature( context, image_service, image_id, tmp_image) self.db.volume_glance_metadata_bulk_create( context, volume.id, {'signature_verified': verified}) # Try to create the volume as the minimal size, # then we can extend once the image has been # downloaded. data = image_utils.qemu_img_info(tmp_image) virtual_size = image_utils.check_virtual_size( data.virtual_size, volume.size, image_id) if should_create_cache_entry: if virtual_size and virtual_size != original_size: volume.size = virtual_size volume.save() model_update = self._create_from_image_download( context, volume, image_location, image_meta, image_service ) except exception.ImageTooBig as e: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail= message_field.Detail.NOT_ENOUGH_SPACE_FOR_IMAGE, exception=e) except exception.ImageSignatureVerificationException as err: with excutils.save_and_reraise_exception(): self.message.create( context, message_field.Action.COPY_IMAGE_TO_VOLUME, resource_uuid=volume.id, detail= message_field.Detail.SIGNATURE_VERIFICATION_FAILED, exception=err) if should_create_cache_entry: # Update the newly created volume db entry before we clone it # for the image-volume creation. if model_update: volume.update(model_update) volume.save() self.manager._create_image_cache_volume_entry(internal_context, volume, image_id, image_meta) finally: # If we created the volume as the minimal size, extend it back to # what was originally requested. If an exception has occurred or # extending it back failed, we still need to put this back before # letting it be raised further up the stack. if volume.size != original_size: try: self.driver.extend_volume(volume, original_size) finally: volume.size = original_size volume.save() return model_update