Esempio n. 1
0
    def execute(self, context, size, volume_type_id, group_snapshot,
                optional_args):
        try:
            values = {'per_volume_gigabytes': size}
            QUOTAS.limit_check(context,
                               project_id=context.project_id,
                               **values)
        except exception.OverQuota as e:
            quotas = e.kwargs['quotas']
            raise exception.VolumeSizeExceedsLimit(
                size=size, limit=quotas['per_volume_gigabytes'])

        try:
            if group_snapshot:
                reserve_opts = {'volumes': 1}
            else:
                reserve_opts = {'volumes': 1, 'gigabytes': size}
            if ('update_size' in optional_args
                    and optional_args['update_size']):
                reserve_opts.pop('volumes', None)
            QUOTAS.add_volume_type_opts(context, reserve_opts, volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
            return {
                'reservations': reservations,
            }
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context,
                                                   e,
                                                   resource='volumes',
                                                   size=size)
Esempio n. 2
0
    def _handle_snapshot_quota(self, context, snapshots, volume_type_id,
                               donor_id):
        snapshots_num = len(snapshots)
        volume_sizes = 0
        if not CONF.no_snapshot_gb_quota:
            for snapshot in snapshots:
                volume_sizes += snapshot.volume_size
        try:
            reserve_opts = {'snapshots': snapshots_num,
                            'gigabytes': volume_sizes}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(
                context, e,
                resource='snapshots',
                size=volume_sizes)

        try:
            reserve_opts = {'snapshots': -snapshots_num,
                            'gigabytes': -volume_sizes}
            QUOTAS.add_volume_type_opts(context.elevated(),
                                        reserve_opts,
                                        volume_type_id)
            donor_reservations = QUOTAS.reserve(context,
                                                project_id=donor_id,
                                                **reserve_opts)
        except exception.OverQuota:
            donor_reservations = None
            LOG.exception("Failed to update volume providing snapshots quota:"
                          " Over quota.")

        return reservations, donor_reservations
Esempio n. 3
0
    def execute(self, context, size, volume_type_id, group_snapshot,
                optional_args):
        try:
            values = {'per_volume_gigabytes': size}
            QUOTAS.limit_check(context, project_id=context.project_id,
                               **values)
        except exception.OverQuota as e:
            quotas = e.kwargs['quotas']
            raise exception.VolumeSizeExceedsLimit(
                size=size, limit=quotas['per_volume_gigabytes'])

        try:
            if group_snapshot:
                reserve_opts = {'volumes': 1}
            else:
                reserve_opts = {'volumes': 1, 'gigabytes': size}
            if ('update_size' in optional_args
                    and optional_args['update_size']):
                reserve_opts.pop('volumes', None)
            QUOTAS.add_volume_type_opts(context, reserve_opts, volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
            return {
                'reservations': reservations,
            }
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context, e,
                                                   resource='volumes',
                                                   size=size)
 def execute(self, context, size, snapshot_ref, optional_args):
     try:
         if CONF.no_snapshot_gb_quota:
             reserve_opts = {'snapshots': 1}
         else:
             # NOTE(tommylikehu): We only use the difference of size here
             # as we already committed the original size at the API
             # service before and this reservation task is only used for
             # managing snapshots now.
             reserve_opts = {'snapshots': 1,
                             'gigabytes':
                                 int(size) - snapshot_ref.volume_size}
         if 'update_size' in optional_args and optional_args['update_size']:
             reserve_opts.pop('snapshots', None)
         volume = objects.Volume.get_by_id(context, snapshot_ref.volume_id)
         QUOTAS.add_volume_type_opts(context,
                                     reserve_opts,
                                     volume.volume_type_id)
         reservations = QUOTAS.reserve(context, **reserve_opts)
         return {
             'reservations': reservations,
         }
     except exception.OverQuota as e:
         quota_utils.process_reserve_over_quota(
             context, e,
             resource='snapshots',
             size=size)
 def execute(self, context, size, snapshot_ref, optional_args):
     try:
         if CONF.no_snapshot_gb_quota:
             reserve_opts = {'snapshots': 1}
         else:
             # NOTE(tommylikehu): We only use the difference of size here
             # as we already committed the original size at the API
             # service before and this reservation task is only used for
             # managing snapshots now.
             reserve_opts = {'snapshots': 1,
                             'gigabytes':
                                 int(size) - snapshot_ref.volume_size}
         if 'update_size' in optional_args and optional_args['update_size']:
             reserve_opts.pop('snapshots', None)
         volume = objects.Volume.get_by_id(context, snapshot_ref.volume_id)
         QUOTAS.add_volume_type_opts(context,
                                     reserve_opts,
                                     volume.volume_type_id)
         reservations = QUOTAS.reserve(context, **reserve_opts)
         return {
             'reservations': reservations,
         }
     except exception.OverQuota as e:
         quota_utils.process_reserve_over_quota(
             context, e,
             resource='snapshots',
             size=size)
Esempio n. 6
0
File: api.py Progetto: mahak/cinder
    def _handle_snapshot_quota(self, context, snapshots, volume_type_id,
                               donor_id):
        snapshots_num = len(snapshots)
        volume_sizes = 0
        if not CONF.no_snapshot_gb_quota:
            for snapshot in snapshots:
                volume_sizes += snapshot.volume_size
        try:
            reserve_opts = {'snapshots': snapshots_num,
                            'gigabytes': volume_sizes}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(
                context, e,
                resource='snapshots',
                size=volume_sizes)

        try:
            reserve_opts = {'snapshots': -snapshots_num,
                            'gigabytes': -volume_sizes}
            QUOTAS.add_volume_type_opts(context.elevated(),
                                        reserve_opts,
                                        volume_type_id)
            donor_reservations = QUOTAS.reserve(context,
                                                project_id=donor_id,
                                                **reserve_opts)
        except exception.OverQuota as e:
            donor_reservations = None
            LOG.exception("Failed to update volume providing snapshots quota:"
                          " Over quota.")

        return reservations, donor_reservations
Esempio n. 7
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, "accept_transfer")
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer["salt"], auth_key)
        if crypt_hash != transfer["crypt_hash"]:
            msg = _("Attempt to transfer %s with invalid auth key.") % transfer_id
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer["volume_id"]
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        if vol_ref["consistencygroup_id"]:
            msg = _("Volume %s must not be part of a consistency " "group.") % vol_ref["id"]
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        volume_utils.notify_about_volume_usage(context, vol_ref, "transfer.accept.start")

        try:
            reserve_opts = {"volumes": 1, "gigabytes": vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts, vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context, e, resource="volumes", size=vol_ref.size)
        try:
            donor_id = vol_ref["project_id"]
            reserve_opts = {"volumes": -1, "gigabytes": -vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts, vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(), project_id=donor_id, **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(_LE("Failed to update quota donating volume" " transfer id %s"), transfer_id)

        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context, vol_ref, context.user_id, context.project_id)
            self.db.transfer_accept(context.elevated(), transfer_id, context.user_id, context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context, donor_reservations, project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref, "transfer.accept.end")
        return {"id": transfer_id, "display_name": transfer["display_name"], "volume_id": vol_ref["id"]}
Esempio n. 8
0
 def execute(self, context, size, optional_args):
     try:
         if CONF.no_snapshot_gb_quota:
             reserve_opts = {'snapshots': 1}
         else:
             reserve_opts = {'snapshots': 1, 'gigabytes': size}
         reservations = QUOTAS.reserve(context, **reserve_opts)
         return {
             'reservations': reservations,
         }
     except exception.OverQuota as e:
         quota_utils.process_reserve_over_quota(context,
                                                e,
                                                resource='snapshots',
                                                size=size)
 def execute(self, context, size, optional_args):
     try:
         if CONF.no_snapshot_gb_quota:
             reserve_opts = {'snapshots': 1}
         else:
             reserve_opts = {'snapshots': 1, 'gigabytes': size}
         reservations = QUOTAS.reserve(context, **reserve_opts)
         return {
             'reservations': reservations,
         }
     except exception.OverQuota as e:
         quota_utils.process_reserve_over_quota(
             context, e,
             resource='snapshots',
             size=size)
Esempio n. 10
0
 def update_quota(self, context, group, num, project_id=None):
     reserve_opts = {'groups': num}
     try:
         reservations = GROUP_QUOTAS.reserve(context,
                                             project_id=project_id,
                                             **reserve_opts)
         if reservations:
             GROUP_QUOTAS.commit(context, reservations)
     except Exception as e:
         with excutils.save_and_reraise_exception():
             try:
                 group.destroy()
                 if isinstance(e, exception.OverQuota):
                     quota_utils.process_reserve_over_quota(
                         context, e, resource='groups')
             finally:
                 LOG.error("Failed to update quota for group %s.", group.id)
Esempio n. 11
0
 def execute(self, context, size, snapshot_ref, optional_args):
     try:
         if CONF.no_snapshot_gb_quota:
             reserve_opts = {'snapshots': 1}
         else:
             reserve_opts = {'snapshots': 1, 'gigabytes': size}
         volume = objects.Volume.get_by_id(context, snapshot_ref.volume_id)
         QUOTAS.add_volume_type_opts(context, reserve_opts,
                                     volume.volume_type_id)
         reservations = QUOTAS.reserve(context, **reserve_opts)
         return {
             'reservations': reservations,
         }
     except exception.OverQuota as e:
         quota_utils.process_reserve_over_quota(context,
                                                e,
                                                resource='snapshots',
                                                size=size)
Esempio n. 12
0
 def update_quota(self, context, group, num, project_id=None):
     reserve_opts = {'consistencygroups': num}
     try:
         reservations = CGQUOTAS.reserve(context,
                                         project_id=project_id,
                                         **reserve_opts)
         if reservations:
             CGQUOTAS.commit(context, reservations)
     except Exception as e:
         with excutils.save_and_reraise_exception():
             try:
                 group.destroy()
                 if isinstance(e, exception.OverQuota):
                     quota_utils.process_reserve_over_quota(
                         context, e, resource='groups')
             finally:
                 LOG.error(_LE("Failed to update quota for "
                               "consistency group %s."), group.id)
 def execute(self, context, size, snapshot_ref, optional_args):
     try:
         if CONF.no_snapshot_gb_quota:
             reserve_opts = {'snapshots': 1}
         else:
             reserve_opts = {'snapshots': 1, 'gigabytes': size}
         volume = objects.Volume.get_by_id(context, snapshot_ref.volume_id)
         QUOTAS.add_volume_type_opts(context,
                                     reserve_opts,
                                     volume.volume_type_id)
         reservations = QUOTAS.reserve(context, **reserve_opts)
         return {
             'reservations': reservations,
         }
     except exception.OverQuota as e:
         quota_utils.process_reserve_over_quota(
             context, e,
             resource='snapshots',
             size=size)
Esempio n. 14
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        context.authorize(policy.ACCEPT_POLICY)
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)
        if crypt_hash != transfer['crypt_hash']:
            msg = (_("Attempt to transfer %s with invalid auth key.") %
                   transfer_id)
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer['volume_id']
        vol_ref = objects.Volume.get_by_id(context.elevated(), volume_id)
        if vol_ref['consistencygroup_id']:
            msg = _("Volume %s must not be part of a consistency "
                    "group.") % vol_ref['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        try:
            values = {'per_volume_gigabytes': vol_ref.size}
            QUOTAS.limit_check(context,
                               project_id=context.project_id,
                               **values)
        except exception.OverQuota as e:
            quotas = e.kwargs['quotas']
            raise exception.VolumeSizeExceedsLimit(
                size=vol_ref.size, limit=quotas['per_volume_gigabytes'])

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context,
                                                   e,
                                                   resource='volumes',
                                                   size=vol_ref.size)
        try:
            donor_id = vol_ref['project_id']
            reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(),
                                                project_id=donor_id,
                                                **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(
                "Failed to update quota donating volume"
                " transfer id %s", transfer_id)

        snap_res = None
        snap_donor_res = None
        if transfer['no_snapshots'] is False:
            snapshots = objects.SnapshotList.get_all_for_volume(
                context.elevated(), volume_id)
            volume_type_id = vol_ref.volume_type_id
            snap_res, snap_donor_res = self._handle_snapshot_quota(
                context, snapshots, volume_type_id, vol_ref['project_id'])

        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.start")
        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context, vol_ref, context.user_id,
                                            context.project_id,
                                            transfer['no_snapshots'])
            self.db.transfer_accept(context.elevated(), transfer_id,
                                    context.user_id, context.project_id,
                                    transfer['no_snapshots'])
            QUOTAS.commit(context, reservations)
            if snap_res:
                QUOTAS.commit(context, snap_res)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            if snap_donor_res:
                QUOTAS.commit(context, snap_donor_res, project_id=donor_id)
            LOG.info("Volume %s has been transferred.", volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if snap_res:
                    QUOTAS.rollback(context, snap_res)
                if donor_reservations:
                    QUOTAS.rollback(context,
                                    donor_reservations,
                                    project_id=donor_id)
                if snap_donor_res:
                    QUOTAS.rollback(context,
                                    snap_donor_res,
                                    project_id=donor_id)

        vol_ref = objects.Volume.get_by_id(context.elevated(), volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.end")
        return {
            'id': transfer_id,
            'display_name': transfer['display_name'],
            'volume_id': vol_ref['id']
        }
Esempio n. 15
0
File: api.py Progetto: mahak/cinder
    def _get_import_backup(self, context, backup_url):
        """Prepare database backup record for import.

        This method decodes provided backup_url and expects to find the id of
        the backup in there.

        Then checks the DB for the presence of this backup record and if it
        finds it and is not deleted it will raise an exception because the
        record cannot be created or used.

        If the record is in deleted status then we must be trying to recover
        this record, so we'll reuse it.

        If the record doesn't already exist we create it with provided id.

        :param context: running context
        :param backup_url: backup description to be used by the backup driver
        :return: BackupImport object
        :raises InvalidBackup:
        :raises InvalidInput:
        """
        reservations = None
        backup = None
        # Deserialize string backup record into a dictionary
        backup_record = objects.Backup.decode_record(backup_url)

        # ID is a required field since it's what links incremental backups
        if 'id' not in backup_record:
            msg = _('Provided backup record is missing an id')
            raise exception.InvalidInput(reason=msg)

        # Since we use size to reserve&commit quota, size is another required
        # field.
        if 'size' not in backup_record:
            msg = _('Provided backup record is missing size attribute')
            raise exception.InvalidInput(reason=msg)

        try:
            reserve_opts = {'backups': 1,
                            'backup_gigabytes': backup_record['size']}
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(
                context, e,
                resource='backups',
                size=backup_record['size'])

        kwargs = {
            'user_id': context.user_id,
            'project_id': context.project_id,
            'volume_id': IMPORT_VOLUME_ID,
            'status': fields.BackupStatus.CREATING,
            'deleted_at': None,
            'deleted': False,
            'metadata': {}
        }

        try:
            try:
                # Try to get the backup with that ID in all projects even among
                # deleted entries.
                backup = objects.BackupImport.get_by_id(
                    context.elevated(read_deleted='yes'),
                    backup_record['id'],
                    project_only=False)

                # If record exists and it's not deleted we cannot proceed
                # with the import
                if backup.status != fields.BackupStatus.DELETED:
                    msg = _('Backup already exists in database.')
                    raise exception.InvalidBackup(reason=msg)

                # Otherwise we'll "revive" delete backup record
                backup.update(kwargs)
                backup.save()
                QUOTAS.commit(context, reservations)
            except exception.BackupNotFound:
                # If record doesn't exist create it with the specific ID
                backup = objects.BackupImport(context=context,
                                              id=backup_record['id'], **kwargs)
                backup.create()
                QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    if backup and 'id' in backup:
                        backup.destroy()
                finally:
                    QUOTAS.rollback(context, reservations)
        return backup
Esempio n. 16
0
    def create(self,
               context,
               name,
               description,
               volume_id,
               container,
               incremental=False,
               availability_zone=None,
               force=False,
               snapshot_id=None,
               metadata=None):
        """Make the RPC call to create a volume backup."""
        volume = self.volume_api.get(context, volume_id)
        context.authorize(policy.CREATE_POLICY, target_obj=volume)
        snapshot = None
        if snapshot_id:
            snapshot = self.volume_api.get_snapshot(context, snapshot_id)

            if volume_id != snapshot.volume_id:
                msg = (_('Volume %(vol1)s does not match with '
                         'snapshot.volume_id %(vol2)s.') % {
                             'vol1': volume_id,
                             'vol2': snapshot.volume_id
                         })
                raise exception.InvalidVolume(reason=msg)
            if snapshot['status'] not in ["available"]:
                msg = (_('Snapshot to be backed up must be available, '
                         'but the current status is "%s".') %
                       snapshot['status'])
                raise exception.InvalidSnapshot(reason=msg)
        elif volume['status'] not in ["available", "in-use"]:
            msg = (_('Volume to be backed up must be available '
                     'or in-use, but the current status is "%s".') %
                   volume['status'])
            raise exception.InvalidVolume(reason=msg)
        elif volume['status'] in ["in-use"] and not force:
            msg = _('Backing up an in-use volume must use ' 'the force flag.')
            raise exception.InvalidVolume(reason=msg)

        previous_status = volume['status']
        volume_host = volume_utils.extract_host(volume.host, 'host')
        availability_zone = availability_zone or volume.availability_zone
        host = self._get_available_backup_service_host(volume_host,
                                                       availability_zone)

        # Reserve a quota before setting volume status and backup status
        try:
            reserve_opts = {'backups': 1, 'backup_gigabytes': volume['size']}
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context,
                                                   e,
                                                   resource='backups',
                                                   size=volume.size)
        # Find the latest backup and use it as the parent backup to do an
        # incremental backup.
        latest_backup = None
        if incremental:
            backups = objects.BackupList.get_all_by_volume(
                context.elevated(), volume_id)
            if backups.objects:
                # NOTE(xyang): The 'data_timestamp' field records the time
                # when the data on the volume was first saved. If it is
                # a backup from volume, 'data_timestamp' will be the same
                # as 'created_at' for a backup. If it is a backup from a
                # snapshot, 'data_timestamp' will be the same as
                # 'created_at' for a snapshot.
                # If not backing up from snapshot, the backup with the latest
                # 'data_timestamp' will be the parent; If backing up from
                # snapshot, the backup with the latest 'data_timestamp' will
                # be chosen only if 'data_timestamp' is earlier than the
                # 'created_at' timestamp of the snapshot; Otherwise, the
                # backup will not be chosen as the parent.
                # For example, a volume has a backup taken at 8:00, then
                # a snapshot taken at 8:10, and then a backup at 8:20.
                # When taking an incremental backup of the snapshot, the
                # parent should be the backup at 8:00, not 8:20, and the
                # 'data_timestamp' of this new backup will be 8:10.
                latest_backup = max(
                    backups.objects,
                    key=lambda x: x['data_timestamp']
                    if (not snapshot or (snapshot and x['data_timestamp'] <
                                         snapshot['created_at'])) else
                    datetime(1, 1, 1, 1, 1, 1, tzinfo=timezone('UTC')))
            else:
                QUOTAS.rollback(context, reservations)
                msg = _('No backups available to do an incremental backup.')
                raise exception.InvalidBackup(reason=msg)

        parent_id = None
        parent = None

        if latest_backup:
            parent = latest_backup
            parent_id = latest_backup.id
            if latest_backup['status'] != fields.BackupStatus.AVAILABLE:
                QUOTAS.rollback(context, reservations)
                msg = _('The parent backup must be available for '
                        'incremental backup.')
                raise exception.InvalidBackup(reason=msg)

        data_timestamp = None
        if snapshot_id:
            snapshot = objects.Snapshot.get_by_id(context, snapshot_id)
            data_timestamp = snapshot.created_at
            self.db.snapshot_update(
                context, snapshot_id,
                {'status': fields.SnapshotStatus.BACKING_UP})
        else:
            self.db.volume_update(context, volume_id, {
                'status': 'backing-up',
                'previous_status': previous_status
            })

        backup = None
        try:
            kwargs = {
                'user_id': context.user_id,
                'project_id': context.project_id,
                'display_name': name,
                'display_description': description,
                'volume_id': volume_id,
                'status': fields.BackupStatus.CREATING,
                'container': container,
                'parent_id': parent_id,
                'size': volume['size'],
                'host': host,
                'availability_zone': availability_zone,
                'snapshot_id': snapshot_id,
                'data_timestamp': data_timestamp,
                'parent': parent,
                'metadata': metadata or {}
            }
            backup = objects.Backup(context=context, **kwargs)
            backup.create()
            if not snapshot_id:
                backup.data_timestamp = backup.created_at
                backup.save()
            QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    if backup and 'id' in backup:
                        backup.destroy()
                finally:
                    QUOTAS.rollback(context, reservations)

        # TODO(DuncanT): In future, when we have a generic local attach,
        #                this can go via the scheduler, which enables
        #                better load balancing and isolation of services
        self.backup_rpcapi.create_backup(context, backup)

        return backup
Esempio n. 17
0
File: api.py Progetto: NetApp/cinder
    def create(self, context, name, description, volume_id,
               container, incremental=False, availability_zone=None,
               force=False, snapshot_id=None):
        """Make the RPC call to create a volume backup."""
        check_policy(context, 'create')
        volume = self.volume_api.get(context, volume_id)
        snapshot = None
        if snapshot_id:
            snapshot = self.volume_api.get_snapshot(context, snapshot_id)

            if volume_id != snapshot.volume_id:
                msg = (_('Volume %(vol1)s does not match with '
                         'snapshot.volume_id %(vol2)s.')
                       % {'vol1': volume_id,
                          'vol2': snapshot.volume_id})
                raise exception.InvalidVolume(reason=msg)
        if volume['status'] not in ["available", "in-use"]:
            msg = (_('Volume to be backed up must be available '
                     'or in-use, but the current status is "%s".')
                   % volume['status'])
            raise exception.InvalidVolume(reason=msg)
        elif volume['status'] in ["in-use"] and not snapshot_id and not force:
            msg = _('Backing up an in-use volume must use '
                    'the force flag.')
            raise exception.InvalidVolume(reason=msg)
        elif snapshot_id and snapshot['status'] not in ["available"]:
            msg = (_('Snapshot to be backed up must be available, '
                     'but the current status is "%s".')
                   % snapshot['status'])
            raise exception.InvalidSnapshot(reason=msg)

        previous_status = volume['status']
        volume_host = volume_utils.extract_host(volume.host, 'host')
        host = self._get_available_backup_service_host(
            volume_host, volume.availability_zone)

        # Reserve a quota before setting volume status and backup status
        try:
            reserve_opts = {'backups': 1,
                            'backup_gigabytes': volume['size']}
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(
                context, e,
                resource='backups',
                size=volume.size)
        # Find the latest backup and use it as the parent backup to do an
        # incremental backup.
        latest_backup = None
        if incremental:
            backups = objects.BackupList.get_all_by_volume(context.elevated(),
                                                           volume_id)
            if backups.objects:
                # NOTE(xyang): The 'data_timestamp' field records the time
                # when the data on the volume was first saved. If it is
                # a backup from volume, 'data_timestamp' will be the same
                # as 'created_at' for a backup. If it is a backup from a
                # snapshot, 'data_timestamp' will be the same as
                # 'created_at' for a snapshot.
                # If not backing up from snapshot, the backup with the latest
                # 'data_timestamp' will be the parent; If backing up from
                # snapshot, the backup with the latest 'data_timestamp' will
                # be chosen only if 'data_timestamp' is earlier than the
                # 'created_at' timestamp of the snapshot; Otherwise, the
                # backup will not be chosen as the parent.
                # For example, a volume has a backup taken at 8:00, then
                # a snapshot taken at 8:10, and then a backup at 8:20.
                # When taking an incremental backup of the snapshot, the
                # parent should be the backup at 8:00, not 8:20, and the
                # 'data_timestamp' of this new backup will be 8:10.
                latest_backup = max(
                    backups.objects,
                    key=lambda x: x['data_timestamp']
                    if (not snapshot or (snapshot and x['data_timestamp']
                                         < snapshot['created_at']))
                    else datetime(1, 1, 1, 1, 1, 1, tzinfo=timezone('UTC')))
            else:
                msg = _('No backups available to do an incremental backup.')
                raise exception.InvalidBackup(reason=msg)

        parent_id = None
        if latest_backup:
            parent_id = latest_backup.id
            if latest_backup['status'] != fields.BackupStatus.AVAILABLE:
                msg = _('The parent backup must be available for '
                        'incremental backup.')
                raise exception.InvalidBackup(reason=msg)

        data_timestamp = None
        if snapshot_id:
            snapshot = objects.Snapshot.get_by_id(context, snapshot_id)
            data_timestamp = snapshot.created_at

        self.db.volume_update(context, volume_id,
                              {'status': 'backing-up',
                               'previous_status': previous_status})

        backup = None
        try:
            kwargs = {
                'user_id': context.user_id,
                'project_id': context.project_id,
                'display_name': name,
                'display_description': description,
                'volume_id': volume_id,
                'status': fields.BackupStatus.CREATING,
                'container': container,
                'parent_id': parent_id,
                'size': volume['size'],
                'host': host,
                'snapshot_id': snapshot_id,
                'data_timestamp': data_timestamp,
            }
            backup = objects.Backup(context=context, **kwargs)
            backup.create()
            if not snapshot_id:
                backup.data_timestamp = backup.created_at
                backup.save()
            QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    if backup and 'id' in backup:
                        backup.destroy()
                finally:
                    QUOTAS.rollback(context, reservations)

        # TODO(DuncanT): In future, when we have a generic local attach,
        #                this can go via the scheduler, which enables
        #                better load balancing and isolation of services
        self.backup_rpcapi.create_backup(context, backup)

        return backup
Esempio n. 18
0
    def create(self, context, name, description, group_type,
               volume_types, availability_zone=None):
        check_policy(context, 'create')

        req_volume_types = []
        # NOTE: Admin context is required to get extra_specs of volume_types.
        req_volume_types = (self.db.volume_types_get_by_name_or_id(
            context.elevated(), volume_types))

        if not uuidutils.is_uuid_like(group_type):
            req_group_type = self.db.group_type_get_by_name(context,
                                                            group_type)
        else:
            req_group_type = self.db.group_type_get(context, group_type)

        availability_zone = self._extract_availability_zone(availability_zone)
        kwargs = {'user_id': context.user_id,
                  'project_id': context.project_id,
                  'availability_zone': availability_zone,
                  'status': c_fields.GroupStatus.CREATING,
                  'name': name,
                  'description': description,
                  'volume_type_ids': [t['id'] for t in req_volume_types],
                  'group_type_id': req_group_type['id'],
                  'replication_status': c_fields.ReplicationStatus.DISABLED}
        try:
            reservations = GROUP_QUOTAS.reserve(context,
                                                project_id=context.project_id,
                                                groups=1)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context, e,
                                                   resource='groups')
        group = None
        try:
            group = objects.Group(context=context, **kwargs)
            group.create()
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.error("Error occurred when creating group"
                          " %s.", name)
                GROUP_QUOTAS.rollback(context, reservations)

        request_spec_list = []
        filter_properties_list = []
        for req_volume_type in req_volume_types:
            request_spec = {'volume_type': req_volume_type.copy(),
                            'group_id': group.id}
            filter_properties = {}
            request_spec_list.append(request_spec)
            filter_properties_list.append(filter_properties)

        group_spec = {'group_type': req_group_type.copy(),
                      'group_id': group.id}
        group_filter_properties = {}

        # Update quota for groups
        GROUP_QUOTAS.commit(context, reservations)

        self._cast_create_group(context, group,
                                group_spec,
                                request_spec_list,
                                group_filter_properties,
                                filter_properties_list)

        return group
Esempio n. 19
0
File: api.py Progetto: NetApp/cinder
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, 'accept_transfer')
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)
        if crypt_hash != transfer['crypt_hash']:
            msg = (_("Attempt to transfer %s with invalid auth key.") %
                   transfer_id)
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer['volume_id']
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        if vol_ref['consistencygroup_id']:
            msg = _("Volume %s must not be part of a consistency "
                    "group.") % vol_ref['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        try:
            values = {'per_volume_gigabytes': vol_ref.size}
            QUOTAS.limit_check(context, project_id=context.project_id,
                               **values)
        except exception.OverQuota as e:
            quotas = e.kwargs['quotas']
            raise exception.VolumeSizeExceedsLimit(
                size=vol_ref.size, limit=quotas['per_volume_gigabytes'])

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context, e,
                                                   resource='volumes',
                                                   size=vol_ref.size)
        try:
            donor_id = vol_ref['project_id']
            reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
            QUOTAS.add_volume_type_opts(context,
                                        reserve_opts,
                                        vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(),
                                                project_id=donor_id,
                                                **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(_LE("Failed to update quota donating volume"
                              " transfer id %s"), transfer_id)

        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.start")
        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context,
                                            vol_ref,
                                            context.user_id,
                                            context.project_id)
            self.db.transfer_accept(context.elevated(),
                                    transfer_id,
                                    context.user_id,
                                    context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context, donor_reservations,
                                    project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.end")
        return {'id': transfer_id,
                'display_name': transfer['display_name'],
                'volume_id': vol_ref['id']}
Esempio n. 20
0
    def create_from_src(self, context, name, description=None,
                        group_snapshot_id=None, source_group_id=None):
        check_policy(context, 'create')

        # Populate group_type_id and volume_type_ids
        group_type_id = None
        volume_type_ids = []
        if group_snapshot_id:
            grp_snap = self.get_group_snapshot(context, group_snapshot_id)
            group_type_id = grp_snap.group_type_id
            grp_snap_src_grp = self.get(context, grp_snap.group_id)
            volume_type_ids = [vt.id for vt in grp_snap_src_grp.volume_types]
        elif source_group_id:
            source_group = self.get(context, source_group_id)
            group_type_id = source_group.group_type_id
            volume_type_ids = [vt.id for vt in source_group.volume_types]

        kwargs = {
            'user_id': context.user_id,
            'project_id': context.project_id,
            'status': c_fields.GroupStatus.CREATING,
            'name': name,
            'description': description,
            'group_snapshot_id': group_snapshot_id,
            'source_group_id': source_group_id,
            'group_type_id': group_type_id,
            'volume_type_ids': volume_type_ids,
            'replication_status': c_fields.ReplicationStatus.DISABLED
        }
        try:
            reservations = GROUP_QUOTAS.reserve(context,
                                                project_id=context.project_id,
                                                groups=1)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context, e,
                                                   resource='groups')
        group = None
        try:
            group = objects.Group(context=context, **kwargs)
            group.create(group_snapshot_id=group_snapshot_id,
                         source_group_id=source_group_id)
        except exception.GroupNotFound:
            with excutils.save_and_reraise_exception():
                LOG.error("Source Group %(source_group)s not found when "
                          "creating group %(group)s from source.",
                          {'group': name, 'source_group': source_group_id})
                GROUP_QUOTAS.rollback(context, reservations)
        except exception.GroupSnapshotNotFound:
            with excutils.save_and_reraise_exception():
                LOG.error("Group snapshot %(group_snap)s not found when "
                          "creating group %(group)s from source.",
                          {'group': name, 'group_snap': group_snapshot_id})
                GROUP_QUOTAS.rollback(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.error("Error occurred when creating group"
                          " %(group)s from group_snapshot %(grp_snap)s.",
                          {'group': name, 'grp_snap': group_snapshot_id})
                GROUP_QUOTAS.rollback(context, reservations)

        # Update quota for groups
        GROUP_QUOTAS.commit(context, reservations)

        if not group.host:
            msg = _("No host to create group %s.") % group.id
            LOG.error(msg)
            raise exception.InvalidGroup(reason=msg)

        group.assert_not_frozen()

        if group_snapshot_id:
            self._create_group_from_group_snapshot(context, group,
                                                   group_snapshot_id)
        elif source_group_id:
            self._create_group_from_source_group(context, group,
                                                 source_group_id)

        return group
Esempio n. 21
0
    def _get_import_backup(self, context, backup_url):
        """Prepare database backup record for import.

        This method decodes provided backup_url and expects to find the id of
        the backup in there.

        Then checks the DB for the presence of this backup record and if it
        finds it and is not deleted it will raise an exception because the
        record cannot be created or used.

        If the record is in deleted status then we must be trying to recover
        this record, so we'll reuse it.

        If the record doesn't already exist we create it with provided id.

        :param context: running context
        :param backup_url: backup description to be used by the backup driver
        :return: BackupImport object
        :raises InvalidBackup:
        :raises InvalidInput:
        """
        reservations = None
        backup = None
        # Deserialize string backup record into a dictionary
        backup_record = objects.Backup.decode_record(backup_url)

        # ID is a required field since it's what links incremental backups
        if 'id' not in backup_record:
            msg = _('Provided backup record is missing an id')
            raise exception.InvalidInput(reason=msg)

        # Since we use size to reserve&commit quota, size is another required
        # field.
        if 'size' not in backup_record:
            msg = _('Provided backup record is missing size attribute')
            raise exception.InvalidInput(reason=msg)

        try:
            reserve_opts = {
                'backups': 1,
                'backup_gigabytes': backup_record['size']
            }
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context,
                                                   e,
                                                   resource='backups',
                                                   size=backup_record['size'])

        kwargs = {
            'user_id': context.user_id,
            'project_id': context.project_id,
            'volume_id': IMPORT_VOLUME_ID,
            'status': fields.BackupStatus.CREATING,
            'deleted_at': None,
            'deleted': False,
            'metadata': {}
        }

        try:
            try:
                # Try to get the backup with that ID in all projects even among
                # deleted entries.
                backup = objects.BackupImport.get_by_id(
                    context.elevated(read_deleted='yes'),
                    backup_record['id'],
                    project_only=False)

                # If record exists and it's not deleted we cannot proceed
                # with the import
                if backup.status != fields.BackupStatus.DELETED:
                    msg = _('Backup already exists in database.')
                    raise exception.InvalidBackup(reason=msg)

                # Otherwise we'll "revive" delete backup record
                backup.update(kwargs)
                backup.save()
                QUOTAS.commit(context, reservations)
            except exception.BackupNotFound:
                # If record doesn't exist create it with the specific ID
                backup = objects.BackupImport(context=context,
                                              id=backup_record['id'],
                                              **kwargs)
                backup.create()
                QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    if backup and 'id' in backup:
                        backup.destroy()
                finally:
                    QUOTAS.rollback(context, reservations)
        return backup
Esempio n. 22
0
    def accept(self, context, transfer_id, auth_key):
        """Accept a volume that has been offered for transfer."""
        # We must use an elevated context to see the volume that is still
        # owned by the donor.
        volume_api.check_policy(context, 'accept_transfer')
        transfer = self.db.transfer_get(context.elevated(), transfer_id)

        crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)
        if crypt_hash != transfer['crypt_hash']:
            msg = (_("Attempt to transfer %s with invalid auth key.") %
                   transfer_id)
            LOG.error(msg)
            raise exception.InvalidAuthKey(reason=msg)

        volume_id = transfer['volume_id']
        vol_ref = self.db.volume_get(context.elevated(), volume_id)
        if vol_ref['consistencygroup_id']:
            msg = _("Volume %s must not be part of a consistency "
                    "group.") % vol_ref['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.start")

        try:
            reserve_opts = {'volumes': 1, 'gigabytes': vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context,
                                                   e,
                                                   resource='volumes',
                                                   size=vol_ref.size)
        try:
            donor_id = vol_ref['project_id']
            reserve_opts = {'volumes': -1, 'gigabytes': -vol_ref.size}
            QUOTAS.add_volume_type_opts(context, reserve_opts,
                                        vol_ref.volume_type_id)
            donor_reservations = QUOTAS.reserve(context.elevated(),
                                                project_id=donor_id,
                                                **reserve_opts)
        except Exception:
            donor_reservations = None
            LOG.exception(
                _LE("Failed to update quota donating volume"
                    " transfer id %s"), transfer_id)

        try:
            # Transfer ownership of the volume now, must use an elevated
            # context.
            self.volume_api.accept_transfer(context, vol_ref, context.user_id,
                                            context.project_id)
            self.db.transfer_accept(context.elevated(), transfer_id,
                                    context.user_id, context.project_id)
            QUOTAS.commit(context, reservations)
            if donor_reservations:
                QUOTAS.commit(context, donor_reservations, project_id=donor_id)
            LOG.info(_LI("Volume %s has been transferred."), volume_id)
        except Exception:
            with excutils.save_and_reraise_exception():
                QUOTAS.rollback(context, reservations)
                if donor_reservations:
                    QUOTAS.rollback(context,
                                    donor_reservations,
                                    project_id=donor_id)

        vol_ref = self.db.volume_get(context, volume_id)
        volume_utils.notify_about_volume_usage(context, vol_ref,
                                               "transfer.accept.end")
        return {
            'id': transfer_id,
            'display_name': transfer['display_name'],
            'volume_id': vol_ref['id']
        }