Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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