Example #1
0
    def copy_volume_to_image(self, context, volume_id, image_meta):
        """Uploads the specified volume to Glance.

        image_meta is a dictionary containing the following keys:
        'id', 'container_format', 'disk_format'

        """
        payload = {"volume_id": volume_id, "image_id": image_meta["id"]}
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the volume status updated.
            utils.require_driver_initialized(self.driver)

            volume = self.db.volume_get(context, volume_id)
            self.driver.ensure_export(context.elevated(), volume)
            image_service, image_id = glance.get_remote_image_service(context, image_meta["id"])
            self.driver.copy_volume_to_image(context, volume, image_service, image_meta)
            LOG.debug(
                _("Uploaded volume %(volume_id)s to " "image (%(image_id)s) successfully"),
                {"volume_id": volume_id, "image_id": image_id},
            )
        except Exception as error:
            with excutils.save_and_reraise_exception():
                payload["message"] = unicode(error)
        finally:
            if volume["instance_uuid"] is None and volume["attached_host"] is None:
                self.db.volume_update(context, volume_id, {"status": "available"})
            else:
                self.db.volume_update(context, volume_id, {"status": "in-use"})
Example #2
0
    def delete_backup(self, context, backup_id):
        """Delete volume backup from configured backup service."""
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized as err:
            with excutils.save_and_reraise_exception():
                self.db.backup_update(context, backup_id, {
                    'status': 'error',
                    'fail_reason': unicode(err)
                })

        LOG.info(_('Delete backup started, backup: %s.'), backup_id)
        backup = self.db.backup_get(context, backup_id)
        self.db.backup_update(context, backup_id, {'host': self.host})

        expected_status = 'deleting'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = _('Delete_backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                        'expected_status': expected_status,
                        'actual_status': actual_status,
                    }
            self.db.backup_update(context, backup_id, {
                'status': 'error',
                'fail_reason': err
            })
            raise exception.InvalidBackup(reason=err)

        backup_service = self._map_service_to_driver(backup['service'])
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Delete backup aborted, the backup service currently'
                        ' configured [%(configured_service)s] is not the'
                        ' backup service that was used to create this'
                        ' backup [%(backup_service)s].') % {
                            'configured_service': configured_service,
                            'backup_service': backup_service,
                        }
                self.db.backup_update(context, backup_id, {'status': 'error'})
                raise exception.InvalidBackup(reason=err)

            try:
                backup_service = self.service.get_backup_driver(context)
                backup_service.delete(backup)
            except Exception as err:
                with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id, {
                        'status': 'error',
                        'fail_reason': unicode(err)
                    })

        context = context.elevated()
        self.db.backup_destroy(context, backup_id)
        LOG.info(_('Delete backup finished, backup %s deleted.'), backup_id)
Example #3
0
    def detach_volume(self, context, volume_id):
        """Updates db to show volume is detached."""
        # TODO(vish): refactor this into a more general "unreserve"
        # TODO(sleepsonthefloor): Is this 'elevated' appropriate?

        volume = self.db.volume_get(context, volume_id)
        self._notify_about_volume_usage(context, volume, "detach.start")
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the volume status updated.
            utils.require_driver_initialized(self.driver)

            self.driver.detach_volume(context, volume)
        except Exception:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id, {"status": "error_detaching"})

        self.db.volume_detached(context.elevated(), volume_id)
        self.db.volume_admin_metadata_delete(context.elevated(), volume_id, "attached_mode")

        # Check for https://bugs.launchpad.net/cinder/+bug/1065702
        volume = self.db.volume_get(context, volume_id)
        if volume["provider_location"] and volume["name"] not in volume["provider_location"]:
            self.driver.ensure_export(context, volume)
        self._notify_about_volume_usage(context, volume, "detach.end")
Example #4
0
        def do_attach():
            # check the volume status before attaching
            volume = self.db.volume_get(context, volume_id)
            volume_metadata = self.db.volume_admin_metadata_get(context.elevated(), volume_id)
            if volume["status"] == "attaching":
                if volume["instance_uuid"] and volume["instance_uuid"] != instance_uuid:
                    msg = _("being attached by another instance")
                    raise exception.InvalidVolume(reason=msg)
                if volume["attached_host"] and volume["attached_host"] != host_name:
                    msg = _("being attached by another host")
                    raise exception.InvalidVolume(reason=msg)
                if volume_metadata.get("attached_mode") and volume_metadata.get("attached_mode") != mode:
                    msg = _("being attached by different mode")
                    raise exception.InvalidVolume(reason=msg)
            elif volume["status"] != "available":
                msg = _("status must be available or attaching")
                raise exception.InvalidVolume(reason=msg)

            # TODO(jdg): attach_time column is currently varchar
            # we should update this to a date-time object
            # also consider adding detach_time?
            self._notify_about_volume_usage(context, volume, "attach.start")
            self.db.volume_update(
                context,
                volume_id,
                {
                    "instance_uuid": instance_uuid,
                    "attached_host": host_name,
                    "status": "attaching",
                    "attach_time": timeutils.strtime(),
                },
            )
            self.db.volume_admin_metadata_update(context.elevated(), volume_id, {"attached_mode": mode}, False)

            if instance_uuid and not uuidutils.is_uuid_like(instance_uuid):
                self.db.volume_update(context, volume_id, {"status": "error_attaching"})
                raise exception.InvalidUUID(uuid=instance_uuid)

            host_name_sanitized = utils.sanitize_hostname(host_name) if host_name else None

            volume = self.db.volume_get(context, volume_id)

            if volume_metadata.get("readonly") == "True" and mode != "ro":
                self.db.volume_update(context, volume_id, {"status": "error_attaching"})
                raise exception.InvalidVolumeAttachMode(mode=mode, volume_id=volume_id)
            try:
                # NOTE(flaper87): Verify the driver is enabled
                # before going forward. The exception will be caught
                # and the volume status updated.
                utils.require_driver_initialized(self.driver)

                self.driver.attach_volume(context, volume, instance_uuid, host_name_sanitized, mountpoint)
            except Exception:
                with excutils.save_and_reraise_exception():
                    self.db.volume_update(context, volume_id, {"status": "error_attaching"})

            volume = self.db.volume_attached(
                context.elevated(), volume_id, instance_uuid, host_name_sanitized, mountpoint
            )
            self._notify_about_volume_usage(context, volume, "attach.end")
Example #5
0
    def delete_backup(self, context, backup_id):
        """Delete volume backup from configured backup service."""
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized as err:
            with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id,
                                          {'status': 'error',
                                           'fail_reason':
                                           unicode(err)})

        LOG.info(_('Delete backup started, backup: %s.'), backup_id)
        backup = self.db.backup_get(context, backup_id)
        self.db.backup_update(context, backup_id, {'host': self.host})

        expected_status = 'deleting'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = _('Delete_backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                'expected_status': expected_status,
                'actual_status': actual_status,
            }
            self.db.backup_update(context, backup_id, {'status': 'error',
                                                       'fail_reason': err})
            raise exception.InvalidBackup(reason=err)

        backup_service = self._map_service_to_driver(backup['service'])
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Delete backup aborted, the backup service currently'
                        ' configured [%(configured_service)s] is not the'
                        ' backup service that was used to create this'
                        ' backup [%(backup_service)s].') % {
                    'configured_service': configured_service,
                    'backup_service': backup_service,
                }
                self.db.backup_update(context, backup_id,
                                      {'status': 'error'})
                raise exception.InvalidBackup(reason=err)

            try:
                backup_service = self.service.get_backup_driver(context)
                backup_service.delete(backup)
            except Exception as err:
                with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id,
                                          {'status': 'error',
                                           'fail_reason':
                                           unicode(err)})

        context = context.elevated()
        self.db.backup_destroy(context, backup_id)
        LOG.info(_('Delete backup finished, backup %s deleted.'), backup_id)
Example #6
0
    def extend_volume(self, context, volume_id, new_size):
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the volume status updated.
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id, {"status": "error_extending"})

        volume = self.db.volume_get(context, volume_id)
        size_increase = (int(new_size)) - volume["size"]

        try:
            reservations = QUOTAS.reserve(context, gigabytes=+size_increase)
        except exception.OverQuota as exc:
            self.db.volume_update(context, volume["id"], {"status": "error_extending"})
            overs = exc.kwargs["overs"]
            usages = exc.kwargs["usages"]
            quotas = exc.kwargs["quotas"]

            def _consumed(name):
                return usages[name]["reserved"] + usages[name]["in_use"]

            if "gigabytes" in overs:
                msg = _(
                    "Quota exceeded for %(s_pid)s, "
                    "tried to extend volume by "
                    "%(s_size)sG, (%(d_consumed)dG of %(d_quota)dG "
                    "already consumed)"
                )
                LOG.error(
                    msg
                    % {
                        "s_pid": context.project_id,
                        "s_size": size_increase,
                        "d_consumed": _consumed("gigabytes"),
                        "d_quota": quotas["gigabytes"],
                    }
                )
            return

        self._notify_about_volume_usage(context, volume, "resize.start")
        try:
            LOG.info(_("volume %s: extending"), volume["id"])
            self.driver.extend_volume(volume, new_size)
            LOG.info(_("volume %s: extended successfully"), volume["id"])
        except Exception:
            LOG.exception(_("volume %s: Error trying to extend volume"), volume_id)
            try:
                self.db.volume_update(context, volume["id"], {"status": "error_extending"})
            finally:
                QUOTAS.rollback(context, reservations)
                return

        QUOTAS.commit(context, reservations)
        self.db.volume_update(context, volume["id"], {"size": int(new_size), "status": "available"})
        self.stats["allocated_capacity_gb"] += size_increase
        self._notify_about_volume_usage(context, volume, "resize.end", extra_usage_info={"size": int(new_size)})
Example #7
0
    def test_require_driver_intialized(self):
        driver = mock.Mock()
        driver.initialized = True
        utils.require_driver_initialized(driver)

        driver.initialized = False
        self.assertRaises(exception.DriverNotInitialized,
                          utils.require_driver_initialized, driver)
Example #8
0
    def test_require_driver_intialized(self):
        driver = mock.Mock()
        driver.initialized = True
        utils.require_driver_initialized(driver)

        driver.initialized = False
        self.assertRaises(exception.DriverNotInitialized,
                          utils.require_driver_initialized,
                          driver)
Example #9
0
    def create_backup(self, context, backup):
        """Create volume backups using configured backup service."""
        volume_id = backup.volume_id
        volume = self.db.volume_get(context, volume_id)
        LOG.info(
            _LI("Create backup started, backup: %(backup_id)s " "volume: %(volume_id)s."),
            {"backup_id": backup.id, "volume_id": volume_id},
        )

        self._notify_about_backup_usage(context, backup, "create.start")
        volume_host = volume_utils.extract_host(volume["host"], "backend")
        backend = self._get_volume_backend(host=volume_host)

        backup.host = self.host
        backup.service = self.driver_name
        backup.save()

        expected_status = "backing-up"
        actual_status = volume["status"]
        if actual_status != expected_status:
            err = _(
                "Create backup aborted, expected volume status " "%(expected_status)s but got %(actual_status)s."
            ) % {"expected_status": expected_status, "actual_status": actual_status}
            self._update_backup_error(backup, context, err)
            raise exception.InvalidVolume(reason=err)

        expected_status = "creating"
        actual_status = backup.status
        if actual_status != expected_status:
            err = _(
                "Create backup aborted, expected backup status " "%(expected_status)s but got %(actual_status)s."
            ) % {"expected_status": expected_status, "actual_status": actual_status}
            self._update_backup_error(backup, context, err)
            backup.save()
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self.driver)

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).backup_volume(context, backup, backup_service)
        except Exception as err:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id, {"status": "available"})
                self._update_backup_error(backup, context, six.text_type(err))

        self.db.volume_update(context, volume_id, {"status": "available"})
        backup.status = "available"
        backup.size = volume["size"]
        backup.availability_zone = self.az
        backup.save()
        LOG.info(_LI("Create backup finished. backup: %s."), backup.id)
        self._notify_about_backup_usage(context, backup, "create.end")
Example #10
0
    def accept_transfer(self, context, volume_id, new_user, new_project):
        # NOTE(flaper87): Verify the driver is enabled
        # before going forward. The exception will be caught
        # and the volume status updated.
        utils.require_driver_initialized(self.driver)

        # NOTE(jdg): need elevated context as we haven't "given" the vol
        # yet
        volume_ref = self.db.volume_get(context.elevated(), volume_id)
        self.driver.accept_transfer(context, volume_ref, new_user, new_project)
    def export_record(self, context, backup_id):
        """Export all volume backup metadata details to allow clean import.

        Export backup metadata so it could be re-imported into the database
        without any prerequisite in the backup database.

        :param context: running context
        :param backup_id: backup id to export
        :returns: backup_record - a description of how to import the backup
        :returns: contains 'backup_url' - how to import the backup, and
        :returns: 'backup_service' describing the needed driver.
        :raises: InvalidBackup
        """
        LOG.info(_('Export record started, backup: %s.'), backup_id)

        backup = self.db.backup_get(context, backup_id)

        expected_status = 'available'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = (_('Export backup aborted, expected backup status '
                     '%(expected_status)s but got %(actual_status)s.') % {
                         'expected_status': expected_status,
                         'actual_status': actual_status
                     })
            raise exception.InvalidBackup(reason=err)

        backup_record = {}
        backup_record['backup_service'] = backup['service']
        backup_service = self._map_service_to_driver(backup['service'])
        configured_service = self.driver_name
        if backup_service != configured_service:
            err = (_('Export record aborted, the backup service currently'
                     ' configured [%(configured_service)s] is not the'
                     ' backup service that was used to create this'
                     ' backup [%(backup_service)s].') % {
                         'configured_service': configured_service,
                         'backup_service': backup_service
                     })
            raise exception.InvalidBackup(reason=err)

        # Call driver to create backup description string
        try:
            utils.require_driver_initialized(self.driver)
            backup_service = self.service.get_backup_driver(context)
            backup_url = backup_service.export_record(backup)
            backup_record['backup_url'] = backup_url
        except Exception as err:
            msg = unicode(err)
            raise exception.InvalidBackup(reason=msg)

        LOG.info(_('Export record finished, backup %s exported.'), backup_id)
        return backup_record
Example #12
0
    def delete_snapshot(self, context, snapshot_id):
        """Deletes and unexports snapshot."""
        caller_context = context
        context = context.elevated()
        snapshot_ref = self.db.snapshot_get(context, snapshot_id)
        project_id = snapshot_ref["project_id"]

        LOG.info(_("snapshot %s: deleting"), snapshot_ref["id"])
        self._notify_about_snapshot_usage(context, snapshot_ref, "delete.start")

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the snapshot status updated.
            utils.require_driver_initialized(self.driver)

            LOG.debug(_("snapshot %s: deleting"), snapshot_ref["id"])

            # Pass context so that drivers that want to use it, can,
            # but it is not a requirement for all drivers.
            snapshot_ref["context"] = caller_context

            self.driver.delete_snapshot(snapshot_ref)
        except exception.SnapshotIsBusy:
            LOG.error(_("Cannot delete snapshot %s: snapshot is busy"), snapshot_ref["id"])
            self.db.snapshot_update(context, snapshot_ref["id"], {"status": "available"})
            return True
        except Exception:
            with excutils.save_and_reraise_exception():
                self.db.snapshot_update(context, snapshot_ref["id"], {"status": "error_deleting"})

        # Get reservations
        try:
            if CONF.no_snapshot_gb_quota:
                reserve_opts = {"snapshots": -1}
            else:
                reserve_opts = {"snapshots": -1, "gigabytes": -snapshot_ref["volume_size"]}
            volume_ref = self.db.volume_get(context, snapshot_ref["volume_id"])
            QUOTAS.add_volume_type_opts(context, reserve_opts, volume_ref.get("volume_type_id"))
            reservations = QUOTAS.reserve(context, project_id=project_id, **reserve_opts)
        except Exception:
            reservations = None
            LOG.exception(_("Failed to update usages deleting snapshot"))
        self.db.volume_glance_metadata_delete_by_snapshot(context, snapshot_id)
        self.db.snapshot_destroy(context, snapshot_id)
        LOG.info(_("snapshot %s: deleted successfully"), snapshot_ref["id"])
        self._notify_about_snapshot_usage(context, snapshot_ref, "delete.end")

        # Commit the reservations
        if reservations:
            QUOTAS.commit(context, reservations, project_id=project_id)
        return True
Example #13
0
    def migrate_volume(self, ctxt, volume_id, host, force_host_copy=False, new_type_id=None):
        """Migrate the volume to the specified host (called on source host)."""
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the migration status updated.
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(ctxt, volume_id, {"migration_status": "error"})

        volume_ref = self.db.volume_get(ctxt, volume_id)
        model_update = None
        moved = False

        status_update = None
        if volume_ref["status"] == "retyping":
            status_update = {"status": self._get_original_status(volume_ref)}

        self.db.volume_update(ctxt, volume_ref["id"], {"migration_status": "migrating"})
        if not force_host_copy and new_type_id is None:
            try:
                LOG.debug(_("volume %s: calling driver migrate_volume"), volume_ref["id"])
                moved, model_update = self.driver.migrate_volume(ctxt, volume_ref, host)
                if moved:
                    updates = {"host": host["host"], "migration_status": None}
                    if status_update:
                        updates.update(status_update)
                    if model_update:
                        updates.update(model_update)
                    volume_ref = self.db.volume_update(ctxt, volume_ref["id"], updates)
            except Exception:
                with excutils.save_and_reraise_exception():
                    updates = {"migration_status": None}
                    if status_update:
                        updates.update(status_update)
                    model_update = self.driver.create_export(ctxt, volume_ref)
                    if model_update:
                        updates.update(model_update)
                    self.db.volume_update(ctxt, volume_ref["id"], updates)
        if not moved:
            try:
                self._migrate_volume_generic(ctxt, volume_ref, host, new_type_id)
            except Exception:
                with excutils.save_and_reraise_exception():
                    updates = {"migration_status": None}
                    if status_update:
                        updates.update(status_update)
                    model_update = self.driver.create_export(ctxt, volume_ref)
                    if model_update:
                        updates.update(model_update)
                    self.db.volume_update(ctxt, volume_ref["id"], updates)
Example #14
0
    def create_backup(self, context, backup_id):
        """Create volume backups using configured backup service."""
        backup = self.db.backup_get(context, backup_id)
        volume_id = backup["volume_id"]
        volume = self.db.volume_get(context, volume_id)
        LOG.info(
            _("Create backup started, backup: %(backup_id)s " "volume: %(volume_id)s.")
            % {"backup_id": backup_id, "volume_id": volume_id}
        )
        backend = self._get_volume_backend(host=volume["host"])

        self.db.backup_update(context, backup_id, {"host": self.host, "service": self.driver_name})

        expected_status = "backing-up"
        actual_status = volume["status"]
        if actual_status != expected_status:
            err = _(
                "Create backup aborted, expected volume status " "%(expected_status)s but got %(actual_status)s."
            ) % {"expected_status": expected_status, "actual_status": actual_status}
            self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": err})
            raise exception.InvalidVolume(reason=err)

        expected_status = "creating"
        actual_status = backup["status"]
        if actual_status != expected_status:
            err = _(
                "Create backup aborted, expected backup status " "%(expected_status)s but got %(actual_status)s."
            ) % {"expected_status": expected_status, "actual_status": actual_status}
            self.db.volume_update(context, volume_id, {"status": "available"})
            self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": err})
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self.driver)

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).backup_volume(context, backup, backup_service)
        except Exception as err:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id, {"status": "available"})
                self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": unicode(err)})

        self.db.volume_update(context, volume_id, {"status": "available"})
        self.db.backup_update(
            context, backup_id, {"status": "available", "size": volume["size"], "availability_zone": self.az}
        )
        LOG.info(_("Create backup finished. backup: %s."), backup_id)
Example #15
0
    def export_record(self, context, backup_id):
        """Export all volume backup metadata details to allow clean import.

        Export backup metadata so it could be re-imported into the database
        without any prerequisite in the backup database.

        :param context: running context
        :param backup_id: backup id to export
        :returns: backup_record - a description of how to import the backup
        :returns: contains 'backup_url' - how to import the backup, and
        :returns: 'backup_service' describing the needed driver.
        :raises: InvalidBackup
        """
        LOG.info(_('Export record started, backup: %s.'), backup_id)

        backup = self.db.backup_get(context, backup_id)

        expected_status = 'available'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = (_('Export backup aborted, expected backup status '
                     '%(expected_status)s but got %(actual_status)s.') %
                   {'expected_status': expected_status,
                    'actual_status': actual_status})
            raise exception.InvalidBackup(reason=err)

        backup_record = {}
        backup_record['backup_service'] = backup['service']
        backup_service = self._map_service_to_driver(backup['service'])
        configured_service = self.driver_name
        if backup_service != configured_service:
            err = (_('Export record aborted, the backup service currently'
                     ' configured [%(configured_service)s] is not the'
                     ' backup service that was used to create this'
                     ' backup [%(backup_service)s].') %
                   {'configured_service': configured_service,
                    'backup_service': backup_service})
            raise exception.InvalidBackup(reason=err)

        # Call driver to create backup description string
        try:
            utils.require_driver_initialized(self.driver)
            backup_service = self.service.get_backup_driver(context)
            backup_url = backup_service.export_record(backup)
            backup_record['backup_url'] = backup_url
        except Exception as err:
            msg = unicode(err)
            raise exception.InvalidBackup(reason=msg)

        LOG.info(_('Export record finished, backup %s exported.'), backup_id)
        return backup_record
Example #16
0
    def migrate_volume_completion(self, ctxt, volume_id, new_volume_id, error=False):
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the migration status updated.
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(ctxt, volume_id, {"migration_status": "error"})

        msg = _("migrate_volume_completion: completing migration for " "volume %(vol1)s (temporary volume %(vol2)s")
        LOG.debug(msg % {"vol1": volume_id, "vol2": new_volume_id})
        volume = self.db.volume_get(ctxt, volume_id)
        new_volume = self.db.volume_get(ctxt, new_volume_id)
        rpcapi = volume_rpcapi.VolumeAPI()

        status_update = None
        if volume["status"] == "retyping":
            status_update = {"status": self._get_original_status(volume)}

        if error:
            msg = _(
                "migrate_volume_completion is cleaning up an error " "for volume %(vol1)s (temporary volume %(vol2)s"
            )
            LOG.info(msg % {"vol1": volume["id"], "vol2": new_volume["id"]})
            new_volume["migration_status"] = None
            rpcapi.delete_volume(ctxt, new_volume)
            updates = {"migration_status": None}
            if status_update:
                updates.update(status_update)
            self.db.volume_update(ctxt, volume_id, updates)
            return volume_id

        self.db.volume_update(ctxt, volume_id, {"migration_status": "completing"})

        # Delete the source volume (if it fails, don't fail the migration)
        try:
            self.delete_volume(ctxt, volume_id)
        except Exception as ex:
            msg = _("Failed to delete migration source vol %(vol)s: %(err)s")
            LOG.error(msg % {"vol": volume_id, "err": ex})

        self.db.finish_volume_migration(ctxt, volume_id, new_volume_id)
        self.db.volume_destroy(ctxt, new_volume_id)
        updates = {"migration_status": None}
        if status_update:
            updates.update(status_update)
        self.db.volume_update(ctxt, volume_id, updates)
        return volume["id"]
Example #17
0
    def create_snapshot(self, context, volume_id, snapshot_id):
        """Creates and exports the snapshot."""
        caller_context = context
        context = context.elevated()
        snapshot_ref = self.db.snapshot_get(context, snapshot_id)
        LOG.info(_("snapshot %s: creating"), snapshot_ref["id"])

        self._notify_about_snapshot_usage(context, snapshot_ref, "create.start")

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the snapshot status updated.
            utils.require_driver_initialized(self.driver)

            LOG.debug(_("snapshot %(snap_id)s: creating"), {"snap_id": snapshot_ref["id"]})

            # Pass context so that drivers that want to use it, can,
            # but it is not a requirement for all drivers.
            snapshot_ref["context"] = caller_context

            model_update = self.driver.create_snapshot(snapshot_ref)
            if model_update:
                self.db.snapshot_update(context, snapshot_ref["id"], model_update)

        except Exception:
            with excutils.save_and_reraise_exception():
                self.db.snapshot_update(context, snapshot_ref["id"], {"status": "error"})

        self.db.snapshot_update(context, snapshot_ref["id"], {"status": "available", "progress": "100%"})

        vol_ref = self.db.volume_get(context, volume_id)
        if vol_ref.bootable:
            try:
                self.db.volume_glance_metadata_copy_to_snapshot(context, snapshot_ref["id"], volume_id)
            except exception.CinderException as ex:
                LOG.exception(
                    _(
                        "Failed updating %(snapshot_id)s"
                        " metadata using the provided volumes"
                        " %(volume_id)s metadata"
                    )
                    % {"volume_id": volume_id, "snapshot_id": snapshot_id}
                )
                raise exception.MetadataCopyFailure(reason=ex)
        LOG.info(_("snapshot %s: created successfully"), snapshot_ref["id"])
        self._notify_about_snapshot_usage(context, snapshot_ref, "create.end")
        return snapshot_id
Example #18
0
    def export_record(self, context, backup):
        """Export all volume backup metadata details to allow clean import.

        Export backup metadata so it could be re-imported into the database
        without any prerequisite in the backup database.

        :param context: running context
        :param backup: backup object to export
        :returns: backup_record - a description of how to import the backup
        :returns: contains 'backup_url' - how to import the backup, and
        :returns: 'backup_service' describing the needed driver.
        :raises: InvalidBackup
        """
        LOG.info(_LI("Export record started, backup: %s."), backup.id)

        expected_status = "available"
        actual_status = backup.status
        if actual_status != expected_status:
            err = _(
                "Export backup aborted, expected backup status " "%(expected_status)s but got %(actual_status)s."
            ) % {"expected_status": expected_status, "actual_status": actual_status}
            raise exception.InvalidBackup(reason=err)

        backup_record = {}
        backup_record["backup_service"] = backup.service
        backup_service = self._map_service_to_driver(backup.service)
        configured_service = self.driver_name
        if backup_service != configured_service:
            err = _(
                "Export record aborted, the backup service currently"
                " configured [%(configured_service)s] is not the"
                " backup service that was used to create this"
                " backup [%(backup_service)s]."
            ) % {"configured_service": configured_service, "backup_service": backup_service}
            raise exception.InvalidBackup(reason=err)

        # Call driver to create backup description string
        try:
            utils.require_driver_initialized(self.driver)
            backup_service = self.service.get_backup_driver(context)
            backup_url = backup_service.export_record(backup)
            backup_record["backup_url"] = backup_url
        except Exception as err:
            msg = six.text_type(err)
            raise exception.InvalidBackup(reason=msg)

        LOG.info(_LI("Export record finished, backup %s exported."), backup.id)
        return backup_record
Example #19
0
    def terminate_connection(self, context, volume_id, connector, force=False):
        """Cleanup connection from host represented by connector.

        The format of connector is the same as for initialize_connection.
        """
        # NOTE(flaper87): Verify the driver is enabled
        # before going forward. The exception will be caught
        # and the volume status updated.
        utils.require_driver_initialized(self.driver)

        volume_ref = self.db.volume_get(context, volume_id)
        try:
            self.driver.terminate_connection(volume_ref, connector, force=force)
        except Exception as err:
            err_msg = _("Unable to terminate volume connection: %(err)s") % {"err": str(err)}
            LOG.error(err_msg)
            raise exception.VolumeBackendAPIException(data=err_msg)
Example #20
0
    def copy_volume_to_image(self, context, volume_id, image_meta):
        """Uploads the specified volume to Glance.

        image_meta is a dictionary containing the following keys:
        'id', 'container_format', 'disk_format'

        """
        payload = {'volume_id': volume_id, 'image_id': image_meta['id']}
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the volume status updated.
            utils.require_driver_initialized(self.driver)

            volume = self.db.volume_get(context, volume_id)

            volume_type_id = volume.get('volume_type_id', None)
            if self._is_share_volume(volume_type_id):
                self.db.volume_update(context,
                                      volume_id,
                                      {'status': 'error'})
                msg = _("Volume is share volume")
                raise exception.InvalidVolume(reason=msg)

            self.driver.ensure_export(context.elevated(), volume)
            image_service, image_id = \
                glance.get_remote_image_service(context, image_meta['id'])
            self.driver.copy_volume_to_image(context, volume, image_service,
                                             image_meta)
            LOG.debug(_("Uploaded volume %(volume_id)s to "
                        "image (%(image_id)s) successfully"),
                      {'volume_id': volume_id, 'image_id': image_id})
        except Exception as error:
            with excutils.save_and_reraise_exception():
                payload['message'] = unicode(error)
        finally:
            if not volume['volume_attachment']:
                self.db.volume_update(context, volume_id,
                                      {'status': 'available'})
            else:
                self.db.volume_update(context, volume_id,
                                      {'status': 'in-use'})
    def reset_status(self, context, backup_id, status):
        """Reset volume backup status.

        :param context: running context
        :param backup_id: The backup id for reset status operation
        :param status: The status to be set
        :raises: InvalidBackup
        :raises: BackupVerifyUnsupportedDriver
        :raises: AttributeError
        """
        LOG.info(
            _('Reset backup status started, backup_id: '
              '%(backup_id)s, status: %(status)s.'), {
                  'backup_id': backup_id,
                  'status': status
              })
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized:
            with excutils.save_and_reraise_exception():
                LOG.exception(_("Backup driver has not been initialized"))

        backup = self.db.backup_get(context, backup_id)
        backup_service = self._map_service_to_driver(backup['service'])
        LOG.info(_('Backup service: %s.'), backup_service)
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Reset backup status aborted, the backup service'
                        ' currently configured [%(configured_service)s] '
                        'is not the backup service that was used to create'
                        ' this backup [%(backup_service)s].') % \
                    {'configured_service': configured_service,
                     'backup_service': backup_service}
                raise exception.InvalidBackup(reason=err)
            # Verify backup
            try:
                # check whether the backup is ok or not
                if status == 'available' and backup['status'] != 'restoring':
                    # check whether we could verify the backup is ok or not
                    if isinstance(backup_service,
                                  driver.BackupDriverWithVerify):
                        backup_service.verify(backup_id)
                        self.db.backup_update(context, backup_id,
                                              {'status': status})
                    # driver does not support verify function
                    else:
                        msg = (_('Backup service %(configured_service)s '
                                 'does not support verify. Backup id'
                                 ' %(id)s is not verified. '
                                 'Skipping verify.') % {
                                     'configured_service': self.driver_name,
                                     'id': backup_id
                                 })
                        raise exception.BackupVerifyUnsupportedDriver(
                            reason=msg)
                # reset status to error or from restoring to available
                else:
                    if (status == 'error'
                            or (status == 'available'
                                and backup['status'] == 'restoring')):
                        self.db.backup_update(context, backup_id,
                                              {'status': status})
            except exception.InvalidBackup:
                with excutils.save_and_reraise_exception():
                    msg = (_("Backup id %(id)s is not invalid. "
                             "Skipping reset.") % {
                                 'id': backup_id
                             })
                    LOG.error(msg)
            except exception.BackupVerifyUnsupportedDriver:
                with excutils.save_and_reraise_exception():
                    msg = (_('Backup service %(configured_service)s '
                             'does not support verify. Backup id'
                             ' %(id)s is not verified. '
                             'Skipping verify.') % {
                                 'configured_service': self.driver_name,
                                 'id': backup_id
                             })
                    LOG.error(msg)
            except AttributeError:
                msg = (_('Backup service %(service)s does not support '
                         'verify. Backup id %(id)s is not verified. '
                         'Skipping reset.') % {
                             'service': self.driver_name,
                             'id': backup_id
                         })
                LOG.error(msg)
                raise exception.BackupVerifyUnsupportedDriver(reason=msg)

            # send notification to ceilometer
            notifier_info = {'id': backup_id, 'update': {'status': status}}
            notifier = rpc.get_notifier('backupStatusUpdate')
            notifier.info(context, "backups" + '.reset_status.end',
                          notifier_info)
Example #22
0
    def restore_backup(self, context, backup_id, volume_id):
        """Restore volume backups from configured backup service."""
        LOG.info(_('Restore backup started, backup: %(backup_id)s '
                   'volume: %(volume_id)s.') %
                 {'backup_id': backup_id, 'volume_id': volume_id})

        backup = self.db.backup_get(context, backup_id)
        volume = self.db.volume_get(context, volume_id)
        backend = self._get_volume_backend(host=volume['host'])

        self.db.backup_update(context, backup_id, {'host': self.host})

        expected_status = 'restoring-backup'
        actual_status = volume['status']
        if actual_status != expected_status:
            err = (_('Restore backup aborted, expected volume status '
                     '%(expected_status)s but got %(actual_status)s.') %
                   {'expected_status': expected_status,
                    'actual_status': actual_status})
            self.db.backup_update(context, backup_id, {'status': 'available'})
            raise exception.InvalidVolume(reason=err)

        expected_status = 'restoring'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = (_('Restore backup aborted: expected backup status '
                     '%(expected_status)s but got %(actual_status)s.') %
                   {'expected_status': expected_status,
                    'actual_status': actual_status})
            self.db.backup_update(context, backup_id, {'status': 'error',
                                                       'fail_reason': err})
            self.db.volume_update(context, volume_id, {'status': 'error'})
            raise exception.InvalidBackup(reason=err)

        if volume['size'] > backup['size']:
            LOG.info(_('Volume: %(vol_id)s, size: %(vol_size)d is '
                       'larger than backup: %(backup_id)s, '
                       'size: %(backup_size)d, continuing with restore.'),
                     {'vol_id': volume['id'],
                      'vol_size': volume['size'],
                      'backup_id': backup['id'],
                      'backup_size': backup['size']})

        backup_service = self._map_service_to_driver(backup['service'])
        configured_service = self.driver_name
        if backup_service != configured_service:
            err = _('Restore backup aborted, the backup service currently'
                    ' configured [%(configured_service)s] is not the'
                    ' backup service that was used to create this'
                    ' backup [%(backup_service)s].') % {
                'configured_service': configured_service,
                'backup_service': backup_service,
            }
            self.db.backup_update(context, backup_id, {'status': 'available'})
            self.db.volume_update(context, volume_id, {'status': 'error'})
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self.driver)

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).restore_backup(context, backup,
                                                     volume,
                                                     backup_service)
        except Exception:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id,
                                      {'status': 'error_restoring'})
                self.db.backup_update(context, backup_id,
                                      {'status': 'available'})

        self.db.volume_update(context, volume_id, {'status': 'available'})
        self.db.backup_update(context, backup_id, {'status': 'available'})
        LOG.info(_('Restore backup finished, backup %(backup_id)s restored'
                   ' to volume %(volume_id)s.') %
                 {'backup_id': backup_id, 'volume_id': volume_id})
Example #23
0
    def create_snapshot(self, context, volume_id, snapshot_id):
        """Creates and exports the snapshot."""
        caller_context = context
        context = context.elevated()
        snapshot_ref = self.db.snapshot_get(context, snapshot_id)
        LOG.info(_("snapshot %s: creating"), snapshot_ref['id'])

        vol_ref = self.db.volume_get(context, volume_id)
        volume_type_id = vol_ref.get('volume_type_id', None)
        if self._is_share_volume(volume_type_id):
            msg = _("Volume is share volume")
            self.db.snapshot_update(context,
                                    snapshot_ref['id'],
                                    {'status': 'error'})
            raise exception.InvalidVolume(reason=msg)

        self._notify_about_snapshot_usage(
            context, snapshot_ref, "create.start")

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the snapshot status updated.
            utils.require_driver_initialized(self.driver)

            LOG.debug(_("snapshot %(snap_id)s: creating"),
                      {'snap_id': snapshot_ref['id']})

            # Pass context so that drivers that want to use it, can,
            # but it is not a requirement for all drivers.
            snapshot_ref['context'] = caller_context

            model_update = self.driver.create_snapshot(snapshot_ref)
            if model_update:
                self.db.snapshot_update(context, snapshot_ref['id'],
                                        model_update)

        except Exception:
            with excutils.save_and_reraise_exception():
                self.db.snapshot_update(context,
                                        snapshot_ref['id'],
                                        {'status': 'error'})

        self.db.snapshot_update(context,
                                snapshot_ref['id'], {'status': 'available',
                                                     'progress': '100%'})

        if vol_ref.bootable:
            try:
                self.db.volume_glance_metadata_copy_to_snapshot(
                    context, snapshot_ref['id'], volume_id)
            except exception.CinderException as ex:
                LOG.exception(_("Failed updating %(snapshot_id)s"
                                " metadata using the provided volumes"
                                " %(volume_id)s metadata") %
                              {'volume_id': volume_id,
                               'snapshot_id': snapshot_id})
                raise exception.MetadataCopyFailure(reason=ex)
        LOG.info(_("snapshot %s: created successfully"), snapshot_ref['id'])
        self._notify_about_snapshot_usage(context, snapshot_ref, "create.end")
        return snapshot_id
Example #24
0
    def reset_status(self, context, backup, status):
        """Reset volume backup status.

        :param context: running context
        :param backup: The backup object for reset status operation
        :param status: The status to be set
        :raises: InvalidBackup
        :raises: BackupVerifyUnsupportedDriver
        :raises: AttributeError
        """
        LOG.info(_LI('Reset backup status started, backup_id: '
                     '%(backup_id)s, status: %(status)s.'),
                 {'backup_id': backup.id,
                  'status': status})
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized:
            with excutils.save_and_reraise_exception():
                LOG.exception(_LE("Backup driver has not been initialized"))

        backup_service = self._map_service_to_driver(backup.service)
        LOG.info(_LI('Backup service: %s.'), backup_service)
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Reset backup status aborted, the backup service'
                        ' currently configured [%(configured_service)s] '
                        'is not the backup service that was used to create'
                        ' this backup [%(backup_service)s].') % \
                    {'configured_service': configured_service,
                     'backup_service': backup_service}
                raise exception.InvalidBackup(reason=err)
            # Verify backup
            try:
                # check whether the backup is ok or not
                if status == 'available' and backup['status'] != 'restoring':
                    # check whether we could verify the backup is ok or not
                    if isinstance(backup_service,
                                  driver.BackupDriverWithVerify):
                        backup_service.verify(backup.id)
                        backup.status = status
                        backup.save()
                    # driver does not support verify function
                    else:
                        msg = (_('Backup service %(configured_service)s '
                                 'does not support verify. Backup id'
                                 ' %(id)s is not verified. '
                                 'Skipping verify.') %
                               {'configured_service': self.driver_name,
                                'id': backup.id})
                        raise exception.BackupVerifyUnsupportedDriver(
                            reason=msg)
                # reset status to error or from restoring to available
                else:
                    if (status == 'error' or
                        (status == 'available' and
                            backup.status == 'restoring')):
                        backup.status = status
                        backup.save()
            except exception.InvalidBackup:
                with excutils.save_and_reraise_exception():
                    LOG.error(_LE("Backup id %s is not invalid. "
                                  "Skipping reset."), backup.id)
            except exception.BackupVerifyUnsupportedDriver:
                with excutils.save_and_reraise_exception():
                    LOG.error(_LE('Backup service %(configured_service)s '
                                  'does not support verify. Backup id '
                                  '%(id)s is not verified. '
                                  'Skipping verify.'),
                              {'configured_service': self.driver_name,
                               'id': backup.id})
            except AttributeError:
                msg = (_('Backup service %(service)s does not support '
                         'verify. Backup id %(id)s is not verified. '
                         'Skipping reset.') %
                       {'service': self.driver_name,
                        'id': backup.id})
                LOG.error(msg)
                raise exception.BackupVerifyUnsupportedDriver(
                    reason=msg)

            # send notification to ceilometer
            notifier_info = {'id': backup.id, 'update': {'status': status}}
            notifier = rpc.get_notifier('backupStatusUpdate')
            notifier.info(context, "backups.reset_status.end",
                          notifier_info)
Example #25
0
    def create_backup(self, context, backup_id):
        """Create volume backups using configured backup service."""
        backup = self.db.backup_get(context, backup_id)
        volume_id = backup['volume_id']
        volume = self.db.volume_get(context, volume_id)
        LOG.info(_('Create backup started, backup: %(backup_id)s '
                   'volume: %(volume_id)s.') %
                 {'backup_id': backup_id, 'volume_id': volume_id})
        backend = self._get_volume_backend(host=volume['host'])

        self.db.backup_update(context, backup_id, {'host': self.host,
                                                   'service':
                                                   self.driver_name})

        expected_status = 'backing-up'
        actual_status = volume['status']
        if actual_status != expected_status:
            err = _('Create backup aborted, expected volume status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                'expected_status': expected_status,
                'actual_status': actual_status,
            }
            self.db.backup_update(context, backup_id, {'status': 'error',
                                                       'fail_reason': err})
            raise exception.InvalidVolume(reason=err)

        expected_status = 'creating'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = _('Create backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                'expected_status': expected_status,
                'actual_status': actual_status,
            }
            self.db.volume_update(context, volume_id, {'status': 'available'})
            self.db.backup_update(context, backup_id, {'status': 'error',
                                                       'fail_reason': err})
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self.driver)

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).backup_volume(context, backup,
                                                    backup_service)
        except Exception as err:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id,
                                      {'status': 'available'})
                self.db.backup_update(context, backup_id,
                                      {'status': 'error',
                                       'fail_reason': unicode(err)})

        self.db.volume_update(context, volume_id, {'status': 'available'})
        self.db.backup_update(context, backup_id, {'status': 'available',
                                                   'size': volume['size'],
                                                   'availability_zone':
                                                   self.az})
        LOG.info(_('Create backup finished. backup: %s.'), backup_id)
Example #26
0
    def create_backup(self, context, backup):
        """Create volume backups using configured backup service."""
        volume_id = backup.volume_id
        volume = self.db.volume_get(context, volume_id)
        LOG.info(
            _LI('Create backup started, backup: %(backup_id)s '
                'volume: %(volume_id)s.'), {
                    'backup_id': backup.id,
                    'volume_id': volume_id
                })

        self._notify_about_backup_usage(context, backup, "create.start")
        volume_host = volume_utils.extract_host(volume['host'], 'backend')
        backend = self._get_volume_backend(host=volume_host)

        backup.host = self.host
        backup.service = self.driver_name
        backup.save()

        expected_status = 'backing-up'
        actual_status = volume['status']
        if actual_status != expected_status:
            err = _('Create backup aborted, expected volume status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                        'expected_status': expected_status,
                        'actual_status': actual_status,
                    }
            self._update_backup_error(backup, context, err)
            raise exception.InvalidVolume(reason=err)

        expected_status = 'creating'
        actual_status = backup.status
        if actual_status != expected_status:
            err = _('Create backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                        'expected_status': expected_status,
                        'actual_status': actual_status,
                    }
            self._update_backup_error(backup, context, err)
            backup.save()
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self.driver)

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).backup_volume(context, backup,
                                                    backup_service)
        except Exception as err:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id,
                                      {'status': 'available'})
                self._update_backup_error(backup, context, six.text_type(err))

        self.db.volume_update(context, volume_id, {'status': 'available'})
        backup.status = 'available'
        backup.size = volume['size']
        backup.availability_zone = self.az
        backup.save()
        LOG.info(_LI('Create backup finished. backup: %s.'), backup.id)
        self._notify_about_backup_usage(context, backup, "create.end")
Example #27
0
    def import_record(self,
                      context,
                      backup,
                      backup_service,
                      backup_url,
                      backup_hosts):
        """Import all volume backup metadata details to the backup db.

        :param context: running context
        :param backup: The new backup object for the import
        :param backup_service: The needed backup driver for import
        :param backup_url: An identifier string to locate the backup
        :param backup_hosts: Potential hosts to execute the import
        :raises: InvalidBackup
        :raises: ServiceNotFound
        """
        LOG.info(_LI('Import record started, backup_url: %s.'), backup_url)

        # Can we import this backup?
        if (backup_service != self.driver_name):
            # No, are there additional potential backup hosts in the list?
            if len(backup_hosts) > 0:
                # try the next host on the list, maybe he can import
                first_host = backup_hosts.pop()
                self.backup_rpcapi.import_record(context,
                                                 first_host,
                                                 backup,
                                                 backup_service,
                                                 backup_url,
                                                 backup_hosts)
            else:
                # empty list - we are the last host on the list, fail
                err = _('Import record failed, cannot find backup '
                        'service to perform the import. Request service '
                        '%(service)s') % {'service': backup_service}
                self._update_backup_error(backup, context, err)
                raise exception.ServiceNotFound(service_id=backup_service)
        else:
            # Yes...
            try:
                # Deserialize backup record information
                backup_options = backup.decode_record(backup_url)

                # Extract driver specific info and pass it to the driver
                driver_options = backup_options.pop('driver_info', {})
                utils.require_driver_initialized(self.driver)
                backup_service = self.service.get_backup_driver(context)
                backup_service.import_record(backup, driver_options)
            except Exception as err:
                msg = six.text_type(err)
                self._update_backup_error(backup, context, msg)
                raise exception.InvalidBackup(reason=msg)

            required_import_options = {
                'display_name',
                'display_description',
                'container',
                'size',
                'service_metadata',
                'service',
                'object_count',
                'id'
            }

            # Check for missing fields in imported data
            missing_opts = required_import_options - set(backup_options)
            if missing_opts:
                msg = (_('Driver successfully decoded imported backup data, '
                         'but there are missing fields (%s).') %
                       ', '.join(missing_opts))
                self._update_backup_error(backup, context, msg)
                raise exception.InvalidBackup(reason=msg)

            # Confirm the ID from the record in the DB is the right one
            backup_id = backup_options['id']
            if backup_id != backup.id:
                msg = (_('Trying to import backup metadata from id %(meta_id)s'
                         ' into backup %(id)s.') %
                       {'meta_id': backup_id, 'id': backup.id})
                self._update_backup_error(backup, context, msg)
                raise exception.InvalidBackup(reason=msg)

            # Overwrite some fields
            backup_options['status'] = 'available'
            backup_options['service'] = self.driver_name
            backup_options['availability_zone'] = self.az
            backup_options['host'] = self.host

            # Remove some values which are not actual fields and some that
            # were set by the API node
            for key in ('name', 'user_id', 'project_id'):
                backup_options.pop(key, None)

            # Update the database
            backup.update(backup_options)
            backup.save()

            # Verify backup
            try:
                if isinstance(backup_service, driver.BackupDriverWithVerify):
                    backup_service.verify(backup.id)
                else:
                    LOG.warning(_LW('Backup service %(service)s does not '
                                    'support verify. Backup id %(id)s is '
                                    'not verified. Skipping verify.'),
                                {'service': self.driver_name,
                                 'id': backup.id})
            except exception.InvalidBackup as err:
                with excutils.save_and_reraise_exception():
                    self._update_backup_error(backup, context,
                                              six.text_type(err))

            LOG.info(_LI('Import record id %s metadata from driver '
                         'finished.'), backup.id)
Example #28
0
    def restore_backup(self, context, backup, volume_id):
        """Restore volume backups from configured backup service."""
        LOG.info(
            _LI('Restore backup started, backup: %(backup_id)s '
                'volume: %(volume_id)s.'), {
                    'backup_id': backup.id,
                    'volume_id': volume_id
                })

        volume = self.db.volume_get(context, volume_id)
        volume_host = volume_utils.extract_host(volume['host'], 'backend')
        backend = self._get_volume_backend(host=volume_host)
        self._notify_about_backup_usage(context, backup, "restore.start")

        backup.host = self.host
        backup.save()

        expected_status = 'restoring-backup'
        actual_status = volume['status']
        if actual_status != expected_status:
            err = (_('Restore backup aborted, expected volume status '
                     '%(expected_status)s but got %(actual_status)s.') % {
                         'expected_status': expected_status,
                         'actual_status': actual_status
                     })
            backup.status = fields.BackupStatus.AVAILABLE
            backup.save()
            raise exception.InvalidVolume(reason=err)

        expected_status = fields.BackupStatus.RESTORING
        actual_status = backup['status']
        if actual_status != expected_status:
            err = (_('Restore backup aborted: expected backup status '
                     '%(expected_status)s but got %(actual_status)s.') % {
                         'expected_status': expected_status,
                         'actual_status': actual_status
                     })
            self._update_backup_error(backup, context, err)
            self.db.volume_update(context, volume_id, {'status': 'error'})
            raise exception.InvalidBackup(reason=err)

        if volume['size'] > backup['size']:
            LOG.info(
                _LI('Volume: %(vol_id)s, size: %(vol_size)d is '
                    'larger than backup: %(backup_id)s, '
                    'size: %(backup_size)d, continuing with restore.'), {
                        'vol_id': volume['id'],
                        'vol_size': volume['size'],
                        'backup_id': backup['id'],
                        'backup_size': backup['size']
                    })

        backup_service = self._map_service_to_driver(backup['service'])
        configured_service = self.driver_name
        if backup_service != configured_service:
            err = _('Restore backup aborted, the backup service currently'
                    ' configured [%(configured_service)s] is not the'
                    ' backup service that was used to create this'
                    ' backup [%(backup_service)s].') % {
                        'configured_service': configured_service,
                        'backup_service': backup_service,
                    }
            backup.status = fields.BackupStatus.AVAILABLE
            backup.save()
            self.db.volume_update(context, volume_id, {'status': 'error'})
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self._get_driver(backend))

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).restore_backup(context, backup, volume,
                                                     backup_service)
        except Exception:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id,
                                      {'status': 'error_restoring'})
                backup.status = fields.BackupStatus.AVAILABLE
                backup.save()

        self.db.volume_update(context, volume_id, {'status': 'available'})
        backup.status = fields.BackupStatus.AVAILABLE
        backup.save()
        LOG.info(
            _LI('Restore backup finished, backup %(backup_id)s restored'
                ' to volume %(volume_id)s.'), {
                    'backup_id': backup.id,
                    'volume_id': volume_id
                })
        self._notify_about_backup_usage(context, backup, "restore.end")
Example #29
0
    def import_record(self, context, backup, backup_service, backup_url,
                      backup_hosts):
        """Import all volume backup metadata details to the backup db.

        :param context: running context
        :param backup: The new backup object for the import
        :param backup_service: The needed backup driver for import
        :param backup_url: An identifier string to locate the backup
        :param backup_hosts: Potential hosts to execute the import
        :raises: InvalidBackup
        :raises: ServiceNotFound
        """
        LOG.info(_LI('Import record started, backup_url: %s.'), backup_url)

        # Can we import this backup?
        if (backup_service != self.driver_name):
            # No, are there additional potential backup hosts in the list?
            if len(backup_hosts) > 0:
                # try the next host on the list, maybe he can import
                first_host = backup_hosts.pop()
                self.backup_rpcapi.import_record(context, first_host, backup,
                                                 backup_service, backup_url,
                                                 backup_hosts)
            else:
                # empty list - we are the last host on the list, fail
                err = _('Import record failed, cannot find backup '
                        'service to perform the import. Request service '
                        '%(service)s') % {
                            'service': backup_service
                        }
                self._update_backup_error(backup, context, err)
                raise exception.ServiceNotFound(service_id=backup_service)
        else:
            # Yes...
            try:
                # Deserialize backup record information
                backup_options = backup.decode_record(backup_url)

                # Extract driver specific info and pass it to the driver
                driver_options = backup_options.pop('driver_info', {})
                utils.require_driver_initialized(self.driver)
                backup_service = self.service.get_backup_driver(context)
                backup_service.import_record(backup, driver_options)
            except Exception as err:
                msg = six.text_type(err)
                self._update_backup_error(backup, context, msg)
                raise exception.InvalidBackup(reason=msg)

            required_import_options = {
                'display_name', 'display_description', 'container', 'size',
                'service_metadata', 'service', 'object_count', 'id'
            }

            # Check for missing fields in imported data
            missing_opts = required_import_options - set(backup_options)
            if missing_opts:
                msg = (_('Driver successfully decoded imported backup data, '
                         'but there are missing fields (%s).') %
                       ', '.join(missing_opts))
                self._update_backup_error(backup, context, msg)
                raise exception.InvalidBackup(reason=msg)

            # Confirm the ID from the record in the DB is the right one
            backup_id = backup_options['id']
            if backup_id != backup.id:
                msg = (_('Trying to import backup metadata from id %(meta_id)s'
                         ' into backup %(id)s.') % {
                             'meta_id': backup_id,
                             'id': backup.id
                         })
                self._update_backup_error(backup, context, msg)
                raise exception.InvalidBackup(reason=msg)

            # Overwrite some fields
            backup_options['status'] = fields.BackupStatus.AVAILABLE
            backup_options['service'] = self.driver_name
            backup_options['availability_zone'] = self.az
            backup_options['host'] = self.host

            # Remove some values which are not actual fields and some that
            # were set by the API node
            for key in ('name', 'user_id', 'project_id'):
                backup_options.pop(key, None)

            # Update the database
            backup.update(backup_options)
            backup.save()

            # Verify backup
            try:
                if isinstance(backup_service, driver.BackupDriverWithVerify):
                    backup_service.verify(backup.id)
                else:
                    LOG.warning(
                        _LW('Backup service %(service)s does not '
                            'support verify. Backup id %(id)s is '
                            'not verified. Skipping verify.'), {
                                'service': self.driver_name,
                                'id': backup.id
                            })
            except exception.InvalidBackup as err:
                with excutils.save_and_reraise_exception():
                    self._update_backup_error(backup, context,
                                              six.text_type(err))

            LOG.info(
                _LI('Import record id %s metadata from driver '
                    'finished.'), backup.id)
    def import_record(self, context, backup_id, backup_service, backup_url,
                      backup_hosts):
        """Import all volume backup metadata details to the backup db.

        :param context: running context
        :param backup_id: The new backup id for the import
        :param backup_service: The needed backup driver for import
        :param backup_url: An identifier string to locate the backup
        :param backup_hosts: Potential hosts to execute the import
        :raises: InvalidBackup
        :raises: ServiceNotFound
        """
        LOG.info(_('Import record started, backup_url: %s.'), backup_url)

        # Can we import this backup?
        if (backup_service != self.driver_name):
            # No, are there additional potential backup hosts in the list?
            if len(backup_hosts) > 0:
                # try the next host on the list, maybe he can import
                first_host = backup_hosts.pop()
                self.backup_rpcapi.import_record(context, first_host,
                                                 backup_id, backup_service,
                                                 backup_url, backup_hosts)
            else:
                # empty list - we are the last host on the list, fail
                err = _('Import record failed, cannot find backup '
                        'service to perform the import. Request service '
                        '%(service)s') % {
                            'service': backup_service
                        }
                self.db.backup_update(context, backup_id, {
                    'status': 'error',
                    'fail_reason': err
                })
                raise exception.ServiceNotFound(service_id=backup_service)
        else:
            # Yes...
            try:
                utils.require_driver_initialized(self.driver)
                backup_service = self.service.get_backup_driver(context)
                backup_options = backup_service.import_record(backup_url)
            except Exception as err:
                msg = unicode(err)
                self.db.backup_update(context, backup_id, {
                    'status': 'error',
                    'fail_reason': msg
                })
                raise exception.InvalidBackup(reason=msg)

            required_import_options = [
                'display_name', 'display_description', 'container', 'size',
                'service_metadata', 'service', 'object_count'
            ]

            backup_update = {}
            backup_update['status'] = 'available'
            backup_update['service'] = self.driver_name
            backup_update['availability_zone'] = self.az
            backup_update['host'] = self.host
            for entry in required_import_options:
                if entry not in backup_options:
                    msg = (_('Backup metadata received from driver for '
                             'import is missing %s.'), entry)
                    self.db.backup_update(context, backup_id, {
                        'status': 'error',
                        'fail_reason': msg
                    })
                    raise exception.InvalidBackup(reason=msg)
                backup_update[entry] = backup_options[entry]
            # Update the database
            self.db.backup_update(context, backup_id, backup_update)

            # Verify backup
            try:
                if isinstance(backup_service, driver.BackupDriverWithVerify):
                    backup_service.verify(backup_id)
                else:
                    LOG.warn(
                        _('Backup service %(service)s does not support '
                          'verify. Backup id %(id)s is not verified. '
                          'Skipping verify.') % {
                              'service': self.driver_name,
                              'id': backup_id
                          })
            except exception.InvalidBackup as err:
                with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id, {
                        'status': 'error',
                        'fail_reason': unicode(err)
                    })

            LOG.info(
                _('Import record id %s metadata from driver '
                  'finished.') % backup_id)
Example #31
0
    def import_record(self,
                      context,
                      backup_id,
                      backup_service,
                      backup_url,
                      backup_hosts):
        """Import all volume backup metadata details to the backup db.

        :param context: running context
        :param backup_id: The new backup id for the import
        :param backup_service: The needed backup driver for import
        :param backup_url: An identifier string to locate the backup
        :param backup_hosts: Potential hosts to execute the import
        :raises: InvalidBackup
        :raises: ServiceNotFound
        """
        LOG.info(_('Import record started, backup_url: %s.'), backup_url)

        # Can we import this backup?
        if (backup_service != self.driver_name):
            # No, are there additional potential backup hosts in the list?
            if len(backup_hosts) > 0:
                # try the next host on the list, maybe he can import
                first_host = backup_hosts.pop()
                self.backup_rpcapi.import_record(context,
                                                 first_host,
                                                 backup_id,
                                                 backup_service,
                                                 backup_url,
                                                 backup_hosts)
            else:
                # empty list - we are the last host on the list, fail
                err = _('Import record failed, cannot find backup '
                        'service to perform the import. Request service '
                        '%(service)s') % {'service': backup_service}
                self.db.backup_update(context, backup_id, {'status': 'error',
                                                           'fail_reason': err})
                raise exception.ServiceNotFound(service_id=backup_service)
        else:
            # Yes...
            try:
                utils.require_driver_initialized(self.driver)
                backup_service = self.service.get_backup_driver(context)
                backup_options = backup_service.import_record(backup_url)
            except Exception as err:
                msg = unicode(err)
                self.db.backup_update(context,
                                      backup_id,
                                      {'status': 'error',
                                       'fail_reason': msg})
                raise exception.InvalidBackup(reason=msg)

            required_import_options = ['display_name',
                                       'display_description',
                                       'container',
                                       'size',
                                       'service_metadata',
                                       'service',
                                       'object_count']

            backup_update = {}
            backup_update['status'] = 'available'
            backup_update['service'] = self.driver_name
            backup_update['availability_zone'] = self.az
            backup_update['host'] = self.host
            for entry in required_import_options:
                if entry not in backup_options:
                    msg = (_('Backup metadata received from driver for '
                             'import is missing %s.'), entry)
                    self.db.backup_update(context,
                                          backup_id,
                                          {'status': 'error',
                                           'fail_reason': msg})
                    raise exception.InvalidBackup(reason=msg)
                backup_update[entry] = backup_options[entry]
            # Update the database
            self.db.backup_update(context, backup_id, backup_update)

            # Verify backup
            try:
                backup_service.verify(backup_id)
            except NotImplementedError:
                LOG.warn(_('Backup service %(service)s does not support '
                           'verify. Backup id %(id)s is not verified. '
                           'Skipping verify.') % {'service': self.driver_name,
                                                  'id': backup_id})
            except exception.InvalidBackup as err:
                with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id,
                                          {'status': 'error',
                                           'fail_reason':
                                           unicode(err)})

            LOG.info(_('Import record id %s metadata from driver '
                       'finished.') % backup_id)
Example #32
0
    def initialize_connection(self, context, volume_id, connector):
        """Prepare volume for connection from host represented by connector.

        This method calls the driver initialize_connection and returns
        it to the caller.  The connector parameter is a dictionary with
        information about the host that will connect to the volume in the
        following format::

            {
                'ip': ip,
                'initiator': initiator,
            }

        ip: the ip address of the connecting machine

        initiator: the iscsi initiator name of the connecting machine.
        This can be None if the connecting machine does not support iscsi
        connections.

        driver is responsible for doing any necessary security setup and
        returning a connection_info dictionary in the following format::

            {
                'driver_volume_type': driver_volume_type,
                'data': data,
            }

        driver_volume_type: a string to identify the type of volume.  This
                           can be used by the calling code to determine the
                           strategy for connecting to the volume. This could
                           be 'iscsi', 'rbd', 'sheepdog', etc.

        data: this is the data that the calling code will use to connect
              to the volume. Keep in mind that this will be serialized to
              json in various places, so it should not contain any non-json
              data types.
        """
        # NOTE(flaper87): Verify the driver is enabled
        # before going forward. The exception will be caught
        # and the volume status updated.
        utils.require_driver_initialized(self.driver)

        volume = self.db.volume_get(context, volume_id)
        self.driver.validate_connector(connector)
        instance_uuid = connector.get('instance_uuid', None)
        self._check_attach_same_instance(context, volume, instance_uuid)
        conn_info = self.driver.initialize_connection(volume, connector)

        # Add qos_specs to connection info
        typeid = volume['volume_type_id']
        specs = None
        if typeid:
            res = volume_types.get_volume_type_qos_specs(typeid)
            qos = res['qos_specs']
            # only pass qos_specs that is designated to be consumed by
            # front-end, or both front-end and back-end.
            if qos and qos.get('consumer') in ['front-end', 'both']:
                specs = qos.get('specs')

        qos_spec = dict(qos_specs=specs)
        conn_info['data'].update(qos_spec)

        # Add access_mode to connection info
        volume_metadata = self.db.volume_admin_metadata_get(context.elevated(),
                                                            volume_id)
        if conn_info['data'].get('access_mode') is None:
            access_mode = volume_metadata.get('attached_mode')
            if access_mode is None:
                # NOTE(zhiyan): client didn't call 'os-attach' before
                access_mode = ('ro'
                               if volume_metadata.get('readonly') == 'True'
                               else 'rw')
            conn_info['data']['access_mode'] = access_mode
        return conn_info
Example #33
0
    def delete_volume(self, context, volume_id, unmanage_only=False):
        """Deletes and unexports volume."""
        context = context.elevated()
        volume_ref = self.db.volume_get(context, volume_id)

        if context.project_id != volume_ref['project_id']:
            project_id = volume_ref['project_id']
        else:
            project_id = context.project_id

        LOG.info(_("volume %s: deleting"), volume_ref['id'])
        if volume_ref['attach_status'] == "attached":
            # Volume is still attached, need to detach first
            raise exception.VolumeAttached(volume_id=volume_id)
        if (vol_utils.extract_host(volume_ref['host']) != self.host):
            raise exception.InvalidVolume(
                reason=_("volume is not local to this node"))

        volume_type_id = volume_ref.get('volume_type_id', None)

        self._check_volume_dependence(context, volume_id, volume_type_id,
                                      project_id)

        self._notify_about_volume_usage(context, volume_ref, "delete.start")
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the volume status updated.
            utils.require_driver_initialized(self.driver)

            LOG.debug(_("volume %s: removing export"), volume_ref['id'])
            self.driver.remove_export(context, volume_ref)
            LOG.debug(_("volume %s: deleting"), volume_ref['id'])
            self.driver.delete_volume(volume_ref)
        except exception.VolumeIsBusy:
            LOG.error(_("Cannot delete volume %s: volume is busy"),
                      volume_ref['id'])
            self.driver.ensure_export(context, volume_ref)
            self.db.volume_update(context, volume_ref['id'],
                                  {'status': 'available'})
            return True
        except Exception:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context,
                                      volume_ref['id'],
                                      {'status': 'error_deleting'})

        # If deleting the source volume in a migration, we want to skip quotas
        # and other database updates.
        if volume_ref['migration_status']:
            return True

        # Get reservations
        try:
            reserve_opts = {'volumes': -1, 'gigabytes': -volume_ref['size']}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        volume_ref.get('volume_type_id'))
            reservations = QUOTAS.reserve(context,
                                          project_id=project_id,
                                          **reserve_opts)
        except Exception:
            reservations = None
            LOG.exception(_("Failed to update usages deleting volume"))

        # Delete glance metadata if it exists
        try:
            self.db.volume_glance_metadata_delete_by_volume(context, volume_id)
            LOG.debug(_("volume %s: glance metadata deleted"),
                      volume_ref['id'])
        except exception.GlanceMetadataNotFound:
            LOG.debug(_("no glance metadata found for volume %s"),
                      volume_ref['id'])

        self.db.volume_destroy(context, volume_id)
        LOG.info(_("volume %s: deleted successfully"), volume_ref['id'])
        self._notify_about_volume_usage(context, volume_ref, "delete.end")

        # Commit the reservations
        if reservations:
            QUOTAS.commit(context, reservations, project_id=project_id)

        self.publish_service_capabilities(context)

        return True
Example #34
0
    def import_record(self, context, backup_id, backup_service, backup_url, backup_hosts):
        """Import all volume backup metadata details to the backup db.

        :param context: running context
        :param backup_id: The new backup id for the import
        :param backup_service: The needed backup driver for import
        :param backup_url: An identifier string to locate the backup
        :param backup_hosts: Potential hosts to execute the import
        :raises: InvalidBackup
        :raises: ServiceNotFound
        """
        LOG.info(_LI("Import record started, backup_url: %s."), backup_url)

        # Can we import this backup?
        if backup_service != self.driver_name:
            # No, are there additional potential backup hosts in the list?
            if len(backup_hosts) > 0:
                # try the next host on the list, maybe he can import
                first_host = backup_hosts.pop()
                self.backup_rpcapi.import_record(
                    context, first_host, backup_id, backup_service, backup_url, backup_hosts
                )
            else:
                # empty list - we are the last host on the list, fail
                err = _(
                    "Import record failed, cannot find backup "
                    "service to perform the import. Request service "
                    "%(service)s"
                ) % {"service": backup_service}
                self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": err})
                raise exception.ServiceNotFound(service_id=backup_service)
        else:
            # Yes...
            try:
                utils.require_driver_initialized(self.driver)
                backup_service = self.service.get_backup_driver(context)
                backup_options = backup_service.import_record(backup_url)
            except Exception as err:
                msg = six.text_type(err)
                self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": msg})
                raise exception.InvalidBackup(reason=msg)

            required_import_options = [
                "display_name",
                "display_description",
                "container",
                "size",
                "service_metadata",
                "service",
                "object_count",
            ]

            backup_update = {}
            backup_update["status"] = "available"
            backup_update["service"] = self.driver_name
            backup_update["availability_zone"] = self.az
            backup_update["host"] = self.host
            for entry in required_import_options:
                if entry not in backup_options:
                    msg = (_("Backup metadata received from driver for " "import is missing %s."), entry)
                    self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": msg})
                    raise exception.InvalidBackup(reason=msg)
                backup_update[entry] = backup_options[entry]
            # Update the database
            self.db.backup_update(context, backup_id, backup_update)

            # Verify backup
            try:
                if isinstance(backup_service, driver.BackupDriverWithVerify):
                    backup_service.verify(backup_id)
                else:
                    LOG.warning(
                        _LW(
                            "Backup service %(service)s does not "
                            "support verify. Backup id %(id)s is "
                            "not verified. Skipping verify."
                        ),
                        {"service": self.driver_name, "id": backup_id},
                    )
            except exception.InvalidBackup as err:
                with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": six.text_type(err)})

            LOG.info(_LI("Import record id %s metadata from driver " "finished."), backup_id)
Example #35
0
    def restore_backup(self, context, backup_id, volume_id):
        """Restore volume backups from configured backup service."""
        LOG.info(
            _LI("Restore backup started, backup: %(backup_id)s " "volume: %(volume_id)s."),
            {"backup_id": backup_id, "volume_id": volume_id},
        )

        backup = self.db.backup_get(context, backup_id)
        volume = self.db.volume_get(context, volume_id)
        volume_host = volume_utils.extract_host(volume["host"], "backend")
        backend = self._get_volume_backend(host=volume_host)
        self._notify_about_backup_usage(context, backup, "restore.start")

        self.db.backup_update(context, backup_id, {"host": self.host})

        expected_status = "restoring-backup"
        actual_status = volume["status"]
        if actual_status != expected_status:
            err = _(
                "Restore backup aborted, expected volume status " "%(expected_status)s but got %(actual_status)s."
            ) % {"expected_status": expected_status, "actual_status": actual_status}
            self.db.backup_update(context, backup_id, {"status": "available"})
            raise exception.InvalidVolume(reason=err)

        expected_status = "restoring"
        actual_status = backup["status"]
        if actual_status != expected_status:
            err = _(
                "Restore backup aborted: expected backup status " "%(expected_status)s but got %(actual_status)s."
            ) % {"expected_status": expected_status, "actual_status": actual_status}
            self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": err})
            self.db.volume_update(context, volume_id, {"status": "error"})
            raise exception.InvalidBackup(reason=err)

        if volume["size"] > backup["size"]:
            LOG.info(
                _LI(
                    "Volume: %(vol_id)s, size: %(vol_size)d is "
                    "larger than backup: %(backup_id)s, "
                    "size: %(backup_size)d, continuing with restore."
                ),
                {
                    "vol_id": volume["id"],
                    "vol_size": volume["size"],
                    "backup_id": backup["id"],
                    "backup_size": backup["size"],
                },
            )

        backup_service = self._map_service_to_driver(backup["service"])
        configured_service = self.driver_name
        if backup_service != configured_service:
            err = _(
                "Restore backup aborted, the backup service currently"
                " configured [%(configured_service)s] is not the"
                " backup service that was used to create this"
                " backup [%(backup_service)s]."
            ) % {"configured_service": configured_service, "backup_service": backup_service}
            self.db.backup_update(context, backup_id, {"status": "available"})
            self.db.volume_update(context, volume_id, {"status": "error"})
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self.driver)

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).restore_backup(context, backup, volume, backup_service)
        except Exception:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id, {"status": "error_restoring"})
                self.db.backup_update(context, backup_id, {"status": "available"})

        self.db.volume_update(context, volume_id, {"status": "available"})
        backup = self.db.backup_update(context, backup_id, {"status": "available"})
        LOG.info(
            _LI("Restore backup finished, backup %(backup_id)s restored" " to volume %(volume_id)s."),
            {"backup_id": backup_id, "volume_id": volume_id},
        )
        self._notify_about_backup_usage(context, backup, "restore.end")
Example #36
0
    def import_record(self,
                      context,
                      backup,
                      backup_service,
                      backup_url,
                      backup_hosts):
        """Import all volume backup metadata details to the backup db.

        :param context: running context
        :param backup: The new backup object for the import
        :param backup_service: The needed backup driver for import
        :param backup_url: An identifier string to locate the backup
        :param backup_hosts: Potential hosts to execute the import
        :raises: InvalidBackup
        :raises: ServiceNotFound
        """
        LOG.info(_LI('Import record started, backup_url: %s.'), backup_url)

        # Can we import this backup?
        if (backup_service != self.driver_name):
            # No, are there additional potential backup hosts in the list?
            if len(backup_hosts) > 0:
                # try the next host on the list, maybe he can import
                first_host = backup_hosts.pop()
                self.backup_rpcapi.import_record(context,
                                                 first_host,
                                                 backup,
                                                 backup_service,
                                                 backup_url,
                                                 backup_hosts)
            else:
                # empty list - we are the last host on the list, fail
                err = _('Import record failed, cannot find backup '
                        'service to perform the import. Request service '
                        '%(service)s') % {'service': backup_service}
                self._update_backup_error(backup, context, err)
                raise exception.ServiceNotFound(service_id=backup_service)
        else:
            # Yes...
            try:
                # Deserialize backup record information
                backup_options = backup.decode_record(backup_url)

                # Extract driver specific info and pass it to the driver
                driver_options = backup_options.pop('driver_info', {})
                utils.require_driver_initialized(self.driver)
                backup_service = self.service.get_backup_driver(context)
                backup_service.import_record(backup, driver_options)
            except Exception as err:
                msg = six.text_type(err)
                self._update_backup_error(backup, context, msg)
                raise exception.InvalidBackup(reason=msg)

            required_import_options = ['display_name',
                                       'display_description',
                                       'container',
                                       'size',
                                       'service_metadata',
                                       'service',
                                       'object_count']

            backup_update = {}
            backup_update['status'] = 'available'
            backup_update['service'] = self.driver_name
            backup_update['availability_zone'] = self.az
            backup_update['host'] = self.host
            for entry in required_import_options:
                if entry not in backup_options:
                    msg = (_('Backup metadata received from driver for '
                             'import is missing %s.'), entry)
                    self._update_backup_error(backup, context, msg)
                    raise exception.InvalidBackup(reason=msg)
                backup_update[entry] = backup_options[entry]
            # Update the database
            backup.update(backup_update)
            backup.save()

            # Verify backup
            try:
                if isinstance(backup_service, driver.BackupDriverWithVerify):
                    backup_service.verify(backup.id)
                else:
                    LOG.warning(_LW('Backup service %(service)s does not '
                                    'support verify. Backup id %(id)s is '
                                    'not verified. Skipping verify.'),
                                {'service': self.driver_name,
                                 'id': backup.id})
            except exception.InvalidBackup as err:
                with excutils.save_and_reraise_exception():
                    self._update_backup_error(backup, context,
                                              six.text_type(err))

            LOG.info(_LI('Import record id %s metadata from driver '
                         'finished.'), backup.id)
Example #37
0
    def create_backup(self, context, backup):
        """Create volume backups using configured backup service."""
        volume_id = backup.volume_id
        volume = self.db.volume_get(context, volume_id)
        previous_status = volume.get('previous_status', None)
        LOG.info(
            _LI('Create backup started, backup: %(backup_id)s '
                'volume: %(volume_id)s.'), {
                    'backup_id': backup.id,
                    'volume_id': volume_id
                })

        self._notify_about_backup_usage(context, backup, "create.start")
        volume_host = volume_utils.extract_host(volume['host'], 'backend')
        backend = self._get_volume_backend(host=volume_host)

        backup.host = self.host
        backup.service = self.driver_name
        backup.save()

        expected_status = 'backing-up'
        actual_status = volume['status']
        if actual_status != expected_status:
            err = _('Create backup aborted, expected volume status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                        'expected_status': expected_status,
                        'actual_status': actual_status,
                    }
            self._update_backup_error(backup, context, err)
            raise exception.InvalidVolume(reason=err)

        expected_status = fields.BackupStatus.CREATING
        actual_status = backup.status
        if actual_status != expected_status:
            err = _('Create backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                        'expected_status': expected_status,
                        'actual_status': actual_status,
                    }
            self._update_backup_error(backup, context, err)
            backup.save()
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self._get_driver(backend))

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).backup_volume(context, backup,
                                                    backup_service)
        except Exception as err:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(
                    context, volume_id, {
                        'status': previous_status,
                        'previous_status': 'error_backing-up'
                    })
                self._update_backup_error(backup, context, six.text_type(err))

        # Restore the original status.
        self.db.volume_update(context, volume_id, {
            'status': previous_status,
            'previous_status': 'backing-up'
        })
        backup.status = fields.BackupStatus.AVAILABLE
        backup.size = volume['size']
        backup.availability_zone = self.az
        backup.save()
        # Handle the num_dependent_backups of parent backup when child backup
        # has created successfully.
        if backup.parent_id:
            parent_backup = objects.Backup.get_by_id(context, backup.parent_id)
            parent_backup.num_dependent_backups += 1
            parent_backup.save()
        LOG.info(_LI('Create backup finished. backup: %s.'), backup.id)
        self._notify_about_backup_usage(context, backup, "create.end")
Example #38
0
    def create_backup(self, context, backup):
        """Create volume backups using configured backup service."""
        volume_id = backup.volume_id
        volume = self.db.volume_get(context, volume_id)
        previous_status = volume.get('previous_status', None)
        LOG.info(_LI('Create backup started, backup: %(backup_id)s '
                     'volume: %(volume_id)s.'),
                 {'backup_id': backup.id, 'volume_id': volume_id})

        self._notify_about_backup_usage(context, backup, "create.start")
        volume_host = volume_utils.extract_host(volume['host'], 'backend')
        backend = self._get_volume_backend(host=volume_host)

        backup.host = self.host
        backup.service = self.driver_name
        backup.save()

        expected_status = 'backing-up'
        actual_status = volume['status']
        if actual_status != expected_status:
            err = _('Create backup aborted, expected volume status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                'expected_status': expected_status,
                'actual_status': actual_status,
            }
            self._update_backup_error(backup, context, err)
            raise exception.InvalidVolume(reason=err)

        expected_status = 'creating'
        actual_status = backup.status
        if actual_status != expected_status:
            err = _('Create backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                'expected_status': expected_status,
                'actual_status': actual_status,
            }
            self._update_backup_error(backup, context, err)
            backup.save()
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self._get_driver(backend))

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).backup_volume(context, backup,
                                                    backup_service)
        except Exception as err:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id,
                                      {'status': previous_status,
                                       'previous_status': 'error_backing-up'})
                self._update_backup_error(backup, context, six.text_type(err))

        # Restore the original status.
        self.db.volume_update(context, volume_id,
                              {'status': previous_status,
                               'previous_status': 'backing-up'})
        backup.status = 'available'
        backup.size = volume['size']
        backup.availability_zone = self.az
        backup.save()
        # Handle the num_dependent_backups of parent backup when child backup
        # has created successfully.
        if backup.parent_id:
            parent_backup = objects.Backup.get_by_id(context,
                                                     backup.parent_id)
            parent_backup.num_dependent_backups += 1
            parent_backup.save()
        LOG.info(_LI('Create backup finished. backup: %s.'), backup.id)
        self._notify_about_backup_usage(context, backup, "create.end")
Example #39
0
    def delete_backup(self, context, backup):
        """Delete volume backup from configured backup service."""
        LOG.info(_LI('Delete backup started, backup: %s.'), backup.id)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized as err:
            with excutils.save_and_reraise_exception():
                self._update_backup_error(backup, context, six.text_type(err))

        self._notify_about_backup_usage(context, backup, "delete.start")
        backup.host = self.host
        backup.save()

        expected_status = fields.BackupStatus.DELETING
        actual_status = backup.status
        if actual_status != expected_status:
            err = _('Delete_backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') \
                % {'expected_status': expected_status,
                   'actual_status': actual_status}
            self._update_backup_error(backup, context, err)
            raise exception.InvalidBackup(reason=err)

        backup_service = self._map_service_to_driver(backup['service'])
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Delete backup aborted, the backup service currently'
                        ' configured [%(configured_service)s] is not the'
                        ' backup service that was used to create this'
                        ' backup [%(backup_service)s].')\
                    % {'configured_service': configured_service,
                       'backup_service': backup_service}
                self._update_backup_error(backup, context, err)
                raise exception.InvalidBackup(reason=err)

            try:
                backup_service = self.service.get_backup_driver(context)
                backup_service.delete(backup)
            except Exception as err:
                with excutils.save_and_reraise_exception():
                    self._update_backup_error(backup, context,
                                              six.text_type(err))

        # Get reservations
        try:
            reserve_opts = {
                'backups': -1,
                'backup_gigabytes': -backup.size,
            }
            reservations = QUOTAS.reserve(context,
                                          project_id=backup.project_id,
                                          **reserve_opts)
        except Exception:
            reservations = None
            LOG.exception(_LE("Failed to update usages deleting backup"))

        backup.destroy()
        # If this backup is incremental backup, handle the
        # num_dependent_backups of parent backup
        if backup.parent_id:
            parent_backup = objects.Backup.get_by_id(context, backup.parent_id)
            if parent_backup.has_dependent_backups:
                parent_backup.num_dependent_backups -= 1
                parent_backup.save()
        # Commit the reservations
        if reservations:
            QUOTAS.commit(context, reservations, project_id=backup.project_id)

        LOG.info(_LI('Delete backup finished, backup %s deleted.'), backup.id)
        self._notify_about_backup_usage(context, backup, "delete.end")
Example #40
0
    def delete_backup(self, context, backup):
        """Delete volume backup from configured backup service."""
        LOG.info(_LI('Delete backup started, backup: %s.'), backup.id)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized as err:
            with excutils.save_and_reraise_exception():
                self._update_backup_error(backup, context, six.text_type(err))

        self._notify_about_backup_usage(context, backup, "delete.start")
        backup.host = self.host
        backup.save()

        expected_status = 'deleting'
        actual_status = backup.status
        if actual_status != expected_status:
            err = _('Delete_backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') \
                % {'expected_status': expected_status,
                   'actual_status': actual_status}
            self._update_backup_error(backup, context, err)
            raise exception.InvalidBackup(reason=err)

        backup_service = self._map_service_to_driver(backup['service'])
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Delete backup aborted, the backup service currently'
                        ' configured [%(configured_service)s] is not the'
                        ' backup service that was used to create this'
                        ' backup [%(backup_service)s].')\
                    % {'configured_service': configured_service,
                       'backup_service': backup_service}
                self._update_backup_error(backup, context, err)
                raise exception.InvalidBackup(reason=err)

            try:
                backup_service = self.service.get_backup_driver(context)
                backup_service.delete(backup)
            except Exception as err:
                with excutils.save_and_reraise_exception():
                    self._update_backup_error(backup, context,
                                              six.text_type(err))

        # Get reservations
        try:
            reserve_opts = {
                'backups': -1,
                'backup_gigabytes': -backup.size,
            }
            reservations = QUOTAS.reserve(context,
                                          project_id=backup.project_id,
                                          **reserve_opts)
        except Exception:
            reservations = None
            LOG.exception(_LE("Failed to update usages deleting backup"))

        backup.destroy()
        # If this backup is incremental backup, handle the
        # num_dependent_backups of parent backup
        if backup.parent_id:
            parent_backup = objects.Backup.get_by_id(context,
                                                     backup.parent_id)
            if parent_backup.has_dependent_backups:
                parent_backup.num_dependent_backups -= 1
                parent_backup.save()
        # Commit the reservations
        if reservations:
            QUOTAS.commit(context, reservations,
                          project_id=backup.project_id)

        LOG.info(_LI('Delete backup finished, backup %s deleted.'), backup.id)
        self._notify_about_backup_usage(context, backup, "delete.end")
Example #41
0
    def reset_status(self, context, backup, status):
        """Reset volume backup status.

        :param context: running context
        :param backup: The backup object for reset status operation
        :param status: The status to be set
        :raises: InvalidBackup
        :raises: BackupVerifyUnsupportedDriver
        :raises: AttributeError
        """
        LOG.info(
            _LI('Reset backup status started, backup_id: '
                '%(backup_id)s, status: %(status)s.'), {
                    'backup_id': backup.id,
                    'status': status
                })
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized:
            with excutils.save_and_reraise_exception():
                LOG.exception(_LE("Backup driver has not been initialized"))

        backup_service = self._map_service_to_driver(backup.service)
        LOG.info(_LI('Backup service: %s.'), backup_service)
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Reset backup status aborted, the backup service'
                        ' currently configured [%(configured_service)s] '
                        'is not the backup service that was used to create'
                        ' this backup [%(backup_service)s].') % \
                    {'configured_service': configured_service,
                     'backup_service': backup_service}
                raise exception.InvalidBackup(reason=err)
            # Verify backup
            try:
                # check whether the backup is ok or not
                if (status == fields.BackupStatus.AVAILABLE
                        and backup['status'] != fields.BackupStatus.RESTORING):
                    # check whether we could verify the backup is ok or not
                    if isinstance(backup_service,
                                  driver.BackupDriverWithVerify):
                        backup_service.verify(backup.id)
                        backup.status = status
                        backup.save()
                    # driver does not support verify function
                    else:
                        msg = (_('Backup service %(configured_service)s '
                                 'does not support verify. Backup id'
                                 ' %(id)s is not verified. '
                                 'Skipping verify.') % {
                                     'configured_service': self.driver_name,
                                     'id': backup.id
                                 })
                        raise exception.BackupVerifyUnsupportedDriver(
                            reason=msg)
                # reset status to error or from restoring to available
                else:
                    if (status == fields.BackupStatus.ERROR or
                        (status == fields.BackupStatus.AVAILABLE
                         and backup.status == fields.BackupStatus.RESTORING)):
                        backup.status = status
                        backup.save()
            except exception.InvalidBackup:
                with excutils.save_and_reraise_exception():
                    LOG.error(
                        _LE("Backup id %s is not invalid. "
                            "Skipping reset."), backup.id)
            except exception.BackupVerifyUnsupportedDriver:
                with excutils.save_and_reraise_exception():
                    LOG.error(
                        _LE('Backup service %(configured_service)s '
                            'does not support verify. Backup id '
                            '%(id)s is not verified. '
                            'Skipping verify.'), {
                                'configured_service': self.driver_name,
                                'id': backup.id
                            })
            except AttributeError:
                msg = (_('Backup service %(service)s does not support '
                         'verify. Backup id %(id)s is not verified. '
                         'Skipping reset.') % {
                             'service': self.driver_name,
                             'id': backup.id
                         })
                LOG.error(msg)
                raise exception.BackupVerifyUnsupportedDriver(reason=msg)

            # Needs to clean temporary volumes and snapshots.
            try:
                self._cleanup_temp_volumes_snapshots_for_one_backup(
                    context, backup)
            except Exception:
                LOG.exception(
                    _LE("Problem cleaning temp volumes and "
                        "snapshots for backup %(bkup)s."), {'bkup': backup.id})

            # send notification to ceilometer
            notifier_info = {'id': backup.id, 'update': {'status': status}}
            notifier = rpc.get_notifier('backupStatusUpdate')
            notifier.info(context, "backups.reset_status.end", notifier_info)
Example #42
0
    def delete_backup(self, context, backup_id):
        """Delete volume backup from configured backup service."""
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized as err:
            with excutils.save_and_reraise_exception():
                self.db.backup_update(context, backup_id, {
                    'status': 'error',
                    'fail_reason': six.text_type(err)
                })

        LOG.info(_LI('Delete backup started, backup: %s.'), backup_id)
        backup = self.db.backup_get(context, backup_id)
        self._notify_about_backup_usage(context, backup, "delete.start")
        self.db.backup_update(context, backup_id, {'host': self.host})

        expected_status = 'deleting'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = _('Delete_backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') \
                % {'expected_status': expected_status,
                   'actual_status': actual_status}
            self.db.backup_update(context, backup_id, {
                'status': 'error',
                'fail_reason': err
            })
            raise exception.InvalidBackup(reason=err)

        backup_service = self._map_service_to_driver(backup['service'])
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Delete backup aborted, the backup service currently'
                        ' configured [%(configured_service)s] is not the'
                        ' backup service that was used to create this'
                        ' backup [%(backup_service)s].')\
                    % {'configured_service': configured_service,
                       'backup_service': backup_service}
                self.db.backup_update(context, backup_id, {'status': 'error'})
                raise exception.InvalidBackup(reason=err)

            try:
                backup_service = self.service.get_backup_driver(context)
                backup_service.delete(backup)
            except Exception as err:
                with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id, {
                        'status': 'error',
                        'fail_reason': six.text_type(err)
                    })

        # Get reservations
        try:
            reserve_opts = {
                'backups': -1,
                'backup_gigabytes': -backup['size'],
            }
            reservations = QUOTAS.reserve(context,
                                          project_id=backup['project_id'],
                                          **reserve_opts)
        except Exception:
            reservations = None
            LOG.exception(_LE("Failed to update usages deleting backup"))

        context = context.elevated()
        self.db.backup_destroy(context, backup_id)

        # Commit the reservations
        if reservations:
            QUOTAS.commit(context,
                          reservations,
                          project_id=backup['project_id'])

        LOG.info(_LI('Delete backup finished, backup %s deleted.'), backup_id)
        self._notify_about_backup_usage(context, backup, "delete.end")
Example #43
0
    def delete_backup(self, context, backup_id):
        """Delete volume backup from configured backup service."""
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized as err:
            with excutils.save_and_reraise_exception():
                self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": six.text_type(err)})

        LOG.info(_LI("Delete backup started, backup: %s."), backup_id)
        backup = self.db.backup_get(context, backup_id)
        self._notify_about_backup_usage(context, backup, "delete.start")
        self.db.backup_update(context, backup_id, {"host": self.host})

        expected_status = "deleting"
        actual_status = backup["status"]
        if actual_status != expected_status:
            err = _(
                "Delete_backup aborted, expected backup status " "%(expected_status)s but got %(actual_status)s."
            ) % {"expected_status": expected_status, "actual_status": actual_status}
            self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": err})
            raise exception.InvalidBackup(reason=err)

        backup_service = self._map_service_to_driver(backup["service"])
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _(
                    "Delete backup aborted, the backup service currently"
                    " configured [%(configured_service)s] is not the"
                    " backup service that was used to create this"
                    " backup [%(backup_service)s]."
                ) % {"configured_service": configured_service, "backup_service": backup_service}
                self.db.backup_update(context, backup_id, {"status": "error"})
                raise exception.InvalidBackup(reason=err)

            try:
                backup_service = self.service.get_backup_driver(context)
                backup_service.delete(backup)
            except Exception as err:
                with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id, {"status": "error", "fail_reason": six.text_type(err)})

        # Get reservations
        try:
            reserve_opts = {"backups": -1, "backup_gigabytes": -backup["size"]}
            reservations = QUOTAS.reserve(context, project_id=backup["project_id"], **reserve_opts)
        except Exception:
            reservations = None
            LOG.exception(_LE("Failed to update usages deleting backup"))

        context = context.elevated()
        self.db.backup_destroy(context, backup_id)

        # Commit the reservations
        if reservations:
            QUOTAS.commit(context, reservations, project_id=backup["project_id"])

        LOG.info(_LI("Delete backup finished, backup %s deleted."), backup_id)
        self._notify_about_backup_usage(context, backup, "delete.end")
Example #44
0
    def delete_backup(self, context, backup_id):
        """Delete volume backup from configured backup service."""
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized as err:
            with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id,
                                          {'status': 'error',
                                           'fail_reason':
                                           unicode(err)})

        LOG.info(_LI('Delete backup started, backup: %s.'), backup_id)
        backup = self.db.backup_get(context, backup_id)
        self._notify_about_backup_usage(context, backup, "delete.start")
        self.db.backup_update(context, backup_id, {'host': self.host})

        expected_status = 'deleting'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = _('Delete_backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') \
                % {'expected_status': expected_status,
                   'actual_status': actual_status}
            self.db.backup_update(context, backup_id,
                                  {'status': 'error', 'fail_reason': err})
            raise exception.InvalidBackup(reason=err)

        backup_service = self._map_service_to_driver(backup['service'])
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _('Delete backup aborted, the backup service currently'
                        ' configured [%(configured_service)s] is not the'
                        ' backup service that was used to create this'
                        ' backup [%(backup_service)s].')\
                    % {'configured_service': configured_service,
                       'backup_service': backup_service}
                self.db.backup_update(context, backup_id,
                                      {'status': 'error'})
                raise exception.InvalidBackup(reason=err)

            try:
                backup_service = self.service.get_backup_driver(context)
                backup_service.delete(backup)
            except Exception as err:
                with excutils.save_and_reraise_exception():
                    self.db.backup_update(context, backup_id,
                                          {'status': 'error',
                                           'fail_reason':
                                           unicode(err)})

        # Get reservations
        try:
            reserve_opts = {
                'backups': -1,
                'backup_gigabytes': -backup['size'],
            }
            reservations = QUOTAS.reserve(context,
                                          project_id=backup['project_id'],
                                          **reserve_opts)
        except Exception:
            reservations = None
            LOG.exception(_LE("Failed to update usages deleting backup"))

        context = context.elevated()
        self.db.backup_destroy(context, backup_id)

        # Commit the reservations
        if reservations:
            QUOTAS.commit(context, reservations,
                          project_id=backup['project_id'])

        LOG.info(_LI('Delete backup finished, backup %s deleted.'), backup_id)
        self._notify_about_backup_usage(context, backup, "delete.end")
Example #45
0
    def reset_status(self, context, backup_id, status):
        """Reset volume backup status.

        :param context: running context
        :param backup_id: The backup id for reset status operation
        :param status: The status to be set
        :raises: InvalidBackup
        :raises: BackupVerifyUnsupportedDriver
        :raises: AttributeError
        """
        LOG.info(
            _LI("Reset backup status started, backup_id: " "%(backup_id)s, status: %(status)s."),
            {"backup_id": backup_id, "status": status},
        )
        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught
            # and the backup status updated. Fail early since there
            # are no other status to change but backup's
            utils.require_driver_initialized(self.driver)
        except exception.DriverNotInitialized:
            with excutils.save_and_reraise_exception():
                LOG.exception(_LE("Backup driver has not been initialized"))

        backup = self.db.backup_get(context, backup_id)
        backup_service = self._map_service_to_driver(backup["service"])
        LOG.info(_LI("Backup service: %s."), backup_service)
        if backup_service is not None:
            configured_service = self.driver_name
            if backup_service != configured_service:
                err = _(
                    "Reset backup status aborted, the backup service"
                    " currently configured [%(configured_service)s] "
                    "is not the backup service that was used to create"
                    " this backup [%(backup_service)s]."
                ) % {"configured_service": configured_service, "backup_service": backup_service}
                raise exception.InvalidBackup(reason=err)
            # Verify backup
            try:
                # check whether the backup is ok or not
                if status == "available" and backup["status"] != "restoring":
                    # check whether we could verify the backup is ok or not
                    if isinstance(backup_service, driver.BackupDriverWithVerify):
                        backup_service.verify(backup_id)
                        self.db.backup_update(context, backup_id, {"status": status})
                    # driver does not support verify function
                    else:
                        msg = _(
                            "Backup service %(configured_service)s "
                            "does not support verify. Backup id"
                            " %(id)s is not verified. "
                            "Skipping verify."
                        ) % {"configured_service": self.driver_name, "id": backup_id}
                        raise exception.BackupVerifyUnsupportedDriver(reason=msg)
                # reset status to error or from restoring to available
                else:
                    if status == "error" or (status == "available" and backup["status"] == "restoring"):
                        self.db.backup_update(context, backup_id, {"status": status})
            except exception.InvalidBackup:
                with excutils.save_and_reraise_exception():
                    LOG.error(_LE("Backup id %s is not invalid. " "Skipping reset."), backup_id)
            except exception.BackupVerifyUnsupportedDriver:
                with excutils.save_and_reraise_exception():
                    LOG.error(
                        _LE(
                            "Backup service %(configured_service)s "
                            "does not support verify. Backup id "
                            "%(id)s is not verified. "
                            "Skipping verify."
                        ),
                        {"configured_service": self.driver_name, "id": backup_id},
                    )
            except AttributeError:
                msg = _(
                    "Backup service %(service)s does not support "
                    "verify. Backup id %(id)s is not verified. "
                    "Skipping reset."
                ) % {"service": self.driver_name, "id": backup_id}
                LOG.error(msg)
                raise exception.BackupVerifyUnsupportedDriver(reason=msg)

            # send notification to ceilometer
            notifier_info = {"id": backup_id, "update": {"status": status}}
            notifier = rpc.get_notifier("backupStatusUpdate")
            notifier.info(context, "backups.reset_status.end", notifier_info)
    def create_backup(self, context, backup_id):
        """Create volume backups using configured backup service."""
        backup = self.db.backup_get(context, backup_id)
        volume_id = backup['volume_id']

        # code begin by luobin
        # Because volume could be available or in-use
        initial_vol_status = self.db.volume_get(context, volume_id)['status']
        self.db.volume_update(context, volume_id, {'status': 'backing-up'})
        # code end by luobin

        volume = self.db.volume_get(context, volume_id)

        LOG.info(
            _('Create backup started, backup: %(backup_id)s '
              'volume: %(volume_id)s.') % {
                  'backup_id': backup_id,
                  'volume_id': volume_id
              })
        volume_host = volume_utils.extract_host(volume['host'], 'backend')
        backend = self._get_volume_backend(host=volume_host)

        self.db.backup_update(context, backup_id, {
            'host': self.host,
            'service': self.driver_name
        })

        expected_status = 'backing-up'
        actual_status = volume['status']
        if actual_status != expected_status:
            err = _('Create backup aborted, expected volume status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                        'expected_status': expected_status,
                        'actual_status': actual_status,
                    }
            self.db.backup_update(context, backup_id, {
                'status': 'error',
                'fail_reason': err
            })
            raise exception.InvalidVolume(reason=err)

        expected_status = 'creating'
        actual_status = backup['status']
        if actual_status != expected_status:
            err = _('Create backup aborted, expected backup status '
                    '%(expected_status)s but got %(actual_status)s.') % {
                        'expected_status': expected_status,
                        'actual_status': actual_status,
                    }
            self.db.volume_update(context, volume_id,
                                  {'status': initial_vol_status})
            self.db.backup_update(context, backup_id, {
                'status': 'error',
                'fail_reason': err
            })
            raise exception.InvalidBackup(reason=err)

        try:
            # NOTE(flaper87): Verify the driver is enabled
            # before going forward. The exception will be caught,
            # the volume status will be set back to available and
            # the backup status to 'error'
            utils.require_driver_initialized(self.driver)

            backup_service = self.service.get_backup_driver(context)
            self._get_driver(backend).backup_volume(context, backup,
                                                    backup_service)
        except Exception as err:
            with excutils.save_and_reraise_exception():
                self.db.volume_update(context, volume_id,
                                      {'status': initial_vol_status})
                self.db.backup_update(context, backup_id, {
                    'status': 'error',
                    'fail_reason': unicode(err)
                })

        self.db.volume_update(context, volume_id,
                              {'status': initial_vol_status})
        self.db.backup_update(
            context, backup_id, {
                'status': 'available',
                'size': volume['size'],
                'availability_zone': self.az
            })
        LOG.info(_('Create backup finished. backup: %s.'), backup_id)