예제 #1
0
 def test_cinder_internal_context_missing_all(self, mock_conf):
     project_id = None
     user_id = None
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     self.assertIsNone(ctx)
예제 #2
0
    def _prepare_image_cache_entry(self, context, volume,
                                   image_location, image_id,
                                   image_meta, image_service):
        internal_context = cinder_context.get_internal_tenant_context()
        if not internal_context:
            return None, False

        cache_entry = self.image_volume_cache.get_entry(internal_context,
                                                        volume,
                                                        image_id,
                                                        image_meta)

        # If the entry is in the cache then return ASAP in order to minimize
        # the scope of the lock. If it isn't in the cache then do the work
        # that adds it. The work is done inside the locked region to ensure
        # only one cache entry is created.
        if cache_entry:
            LOG.debug('Found cache entry for image = '
                      '%(image_id)s on host %(host)s.',
                      {'image_id': image_id, 'host': volume.host})
            return None, False
        else:
            LOG.debug('Preparing cache entry for image = '
                      '%(image_id)s on host %(host)s.',
                      {'image_id': image_id, 'host': volume.host})
            model_update = self._create_from_image_cache_or_download(
                context,
                volume,
                image_location,
                image_id,
                image_meta,
                image_service,
                update_cache=True)
            return model_update, True
예제 #3
0
 def test_cinder_internal_context_missing_user(self, mock_conf):
     project_id = 'ec729e9946bc43c39ece6dfa7de70eea'
     user_id = None
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     self.assertIsNone(ctx)
예제 #4
0
 def test_cinder_internal_context_missing_project(self, mock_conf):
     project_id = None
     user_id = 'c466a48309794261b64a4f02cfcc3d64'
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     self.assertIsNone(ctx)
예제 #5
0
 def test_cinder_internal_context_missing_all(self, mock_conf):
     project_id = None
     user_id = None
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     self.assertIsNone(ctx)
예제 #6
0
 def test_cinder_internal_context_missing_project(self, mock_conf):
     project_id = None
     user_id = 'c466a48309794261b64a4f02cfcc3d64'
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     self.assertIsNone(ctx)
예제 #7
0
 def test_cinder_internal_context_missing_user(self, mock_conf):
     project_id = 'ec729e9946bc43c39ece6dfa7de70eea'
     user_id = None
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     self.assertIsNone(ctx)
예제 #8
0
 def test_cinder_internal_context(self, mock_conf):
     project_id = 'ec729e9946bc43c39ece6dfa7de70eea'
     user_id = 'c466a48309794261b64a4f02cfcc3d64'
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     self.assertEqual(user_id, ctx.user_id)
     self.assertEqual(project_id, ctx.project_id)
예제 #9
0
 def test_cinder_internal_context(self, mock_conf):
     project_id = 'ec729e9946bc43c39ece6dfa7de70eea'
     user_id = 'c466a48309794261b64a4f02cfcc3d64'
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     self.assertEqual(user_id, ctx.user_id)
     self.assertEqual(project_id, ctx.project_id)
예제 #10
0
 def test_cinder_internal_context(self, project_id, user_id, mock_conf):
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     if project_id is None or user_id is None:
         self.assertIsNone(ctx)
     else:
         self.assertEqual(user_id, ctx.user_id)
         self.assertEqual(project_id, ctx.project_id)
예제 #11
0
 def test_cinder_internal_context(self, project_id, user_id, mock_conf):
     mock_conf.cinder_internal_tenant_project_id = project_id
     mock_conf.cinder_internal_tenant_user_id = user_id
     ctx = context.get_internal_tenant_context()
     if project_id is None or user_id is None:
         self.assertIsNone(ctx)
     else:
         self.assertEqual(user_id, ctx.user_id)
         self.assertEqual(project_id, ctx.project_id)
예제 #12
0
    def _prepare_image_cache_entry(self, context, volume, image_location,
                                   image_id, image_meta, image_service):
        internal_context = cinder_context.get_internal_tenant_context()
        if not internal_context:
            return None, False

        cache_entry = self.image_volume_cache.get_entry(
            internal_context, volume, image_id, image_meta)

        # If the entry is in the cache then return ASAP in order to minimize
        # the scope of the lock. If it isn't in the cache then do the work
        # that adds it. The work is done inside the locked region to ensure
        # only one cache entry is created.
        if cache_entry:
            LOG.debug(
                'Found cache entry for image = '
                '%(image_id)s on host %(host)s.', {
                    'image_id': image_id,
                    'host': volume.host
                })
            return None, False
        else:
            LOG.debug(
                'Preparing cache entry for image = '
                '%(image_id)s on host %(host)s.', {
                    'image_id': image_id,
                    'host': volume.host
                })
            model_update = self._create_from_image_cache_or_download(
                context,
                volume,
                image_location,
                image_id,
                image_meta,
                image_service,
                update_cache=True)
            return model_update, True
예제 #13
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
예제 #14
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
예제 #15
0
    def _create_from_image_cache_or_download(self, context, volume,
                                             image_location, image_id,
                                             image_meta, image_service):
        # 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
예제 #16
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
예제 #17
0
    def _create_from_image_cache_or_download(self, context, volume,
                                             image_location, image_id,
                                             image_meta, image_service):
        # 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
예제 #18
0
    def _create_from_image_cache_or_download(self, context, volume,
                                             image_location, image_id,
                                             image_meta, image_service):
        # 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

        # 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_meta,
                        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 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
예제 #19
0
    def _create_from_image(self, context, volume_ref, 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_ref["id"], "image_location": image_location, "image_id": image_id},
        )

        virtual_size = image_meta.get("virtual_size")
        if virtual_size:
            virtual_size = image_utils.check_virtual_size(virtual_size, volume_ref.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.
        model_update, cloned = self.driver.clone_image(context, volume_ref, 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_ref, 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_ref, image_id, image_meta
                )
                if not cloned:
                    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_ref["size"]
        try:
            if not cloned:
                with image_utils.TemporaryImages.fetch(image_service, context, image_id) 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_ref.size, image_id)

                    if should_create_cache_entry:
                        if virtual_size and virtual_size != original_size:
                            volume_ref.size = virtual_size
                            volume_ref.save()

                    model_update = self._create_from_image_download(
                        context, volume_ref, 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_ref.update(model_update)
                    volume_ref.save()
                self.manager._create_image_cache_volume_entry(internal_context, volume_ref, 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_ref.size != original_size:
                self.driver.extend_volume(volume_ref, original_size)
                volume_ref.size = original_size
                volume_ref.save()

        self._handle_bootable_volume_glance_meta(context, volume_ref.id, image_id=image_id, image_meta=image_meta)
        return model_update
예제 #20
0
    def _create_from_image(self, context, volume_ref,
                           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_ref['id'],
                   'image_location': image_location, 'image_id': 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.
        model_update, cloned = self.driver.clone_image(context,
                                                       volume_ref,
                                                       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_ref,
                                                            image_location,
                                                            image_meta)

        # Try and use the image cache.
        should_create_cache_entry = False
        internal_context = cinder_context.get_internal_tenant_context()
        if not internal_context:
            LOG.warning(_LW('Unable to get Cinder internal context, will '
                            'not use image-volume cache.'))

        if not cloned and internal_context and self.image_volume_cache:
            model_update, cloned = self._create_from_image_cache(
                context,
                internal_context,
                volume_ref,
                image_id,
                image_meta
            )
            if not cloned:
                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_ref['size']
        try:
            if not cloned:
                with image_utils.TemporaryImages.fetch(
                        image_service, context, image_id) as tmp_image:
                    # Try to create the volume as the minimal size, then we can
                    # extend once the image has been downloaded.
                    if should_create_cache_entry:
                        data = image_utils.qemu_img_info(tmp_image)

                        virtual_size = int(
                            math.ceil(float(data.virtual_size) / units.Gi))

                        if virtual_size > volume_ref.size:
                            params = {'image_size': virtual_size,
                                      'volume_size': volume_ref.size}
                            reason = _("Image virtual size is %(image_size)dGB"
                                       " and doesn't fit in a volume of size"
                                       " %(volume_size)dGB.") % params
                            raise exception.ImageUnacceptable(
                                image_id=image_id, reason=reason)

                        if virtual_size and virtual_size != original_size:
                            updates = {'size': virtual_size}
                            volume_ref = self.db.volume_update(
                                context,
                                volume_ref['id'],
                                updates
                            )

                    model_update = self._create_from_image_download(
                        context,
                        volume_ref,
                        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_ref = self.db.volume_update(context,
                                                       volume_ref['id'],
                                                       model_update)
                self.manager._create_image_cache_volume_entry(internal_context,
                                                              volume_ref,
                                                              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_ref['size'] != original_size:
                self.driver.extend_volume(volume_ref, original_size)
                updates = {'size': original_size}
                self.db.volume_update(context, volume_ref['id'], updates)

        self._handle_bootable_volume_glance_meta(context, volume_ref['id'],
                                                 image_id=image_id,
                                                 image_meta=image_meta)
        return model_update