Exemple #1
0
    def _build_snapshot_package_cmd(self, data_dir, snapshot_name):
        """Collect all files for a given snapshot and build a package
        command for them.
        Transform the paths such that the backup can be restored simply by
        extracting the archive right to an existing data directory
        (i.e. place the root into the <data dir> and
        remove the 'snapshots/<snapshot name>' portion of the path).
        Attempt to preserve access modifiers on the archived files.
        Assert the backup is not empty as there should always be
        at least the system keyspace. Fail if there is nothing to backup.
        """

        LOG.debug('Searching for all snapshot(s) with name "%s".',
                  snapshot_name)
        snapshot_files = operating_system.list_files_in_directory(
            data_dir, recursive=True, include_dirs=False,
            pattern='.*/snapshots/%s/.*\.%s' % (snapshot_name,
                                                self._SNAPSHOT_EXTENSION),
            as_root=True)
        num_snapshot_files = len(snapshot_files)
        LOG.debug('Found %(num)d snapshot (*.%(ext)s) files.',
                  {'num': num_snapshot_files, 'ext': self._SNAPSHOT_EXTENSION})
        if num_snapshot_files > 0:
            return ('sudo tar '
                    '--transform="s#snapshots/%s/##" -cpPf - -C "%s" "%s"'
                    % (snapshot_name, data_dir, '" "'.join(snapshot_files)))

        # There should always be at least the system keyspace snapshot.
        raise exception.BackupCreationError(_("No data found."))
Exemple #2
0
    def persist_data(self):
        save_cmd = 'SAVE'
        last_save = self.__client.lastsave()
        LOG.debug("Starting Redis data persist")
        if self.__client.bgsave():
            save_cmd = 'BGSAVE'

            def _timestamp_changed():
                return last_save != self.__client.lastsave()

            try:
                utils.poll_until(_timestamp_changed,
                                 sleep_time=2,
                                 time_out=TIME_OUT)
            except exception.PollTimeOut:
                raise RuntimeError(
                    _("Timeout occurred waiting for Redis "
                      "persist (%s) to complete.") % save_cmd)

        # If the background save fails for any reason, try doing a foreground
        # one.  This blocks client connections, so we don't want it to be
        # the default.
        elif not self.__client.save():
            raise exception.BackupCreationError(
                _("Could not persist "
                  "Redis data (%s)") % save_cmd)
        LOG.debug("Redis data persist (%s) completed" % save_cmd)
Exemple #3
0
        def _create_resources():
            # parse the ID from the Ref
            instance_id = utils.get_id_from_href(instance)

            # verify that the instance exists and can perform actions
            from trove.instance.models import Instance
            instance_model = Instance.load(context, instance_id)
            instance_model.validate_can_perform_action()

            cls.verify_swift_auth_token(context)

            try:
                db_info = DBBackup.create(name=name,
                                          description=description,
                                          tenant_id=context.tenant,
                                          state=BackupState.NEW,
                                          instance_id=instance_id,
                                          deleted=False)
            except exception.InvalidModelError as ex:
                LOG.exception("Unable to create Backup record:")
                raise exception.BackupCreationError(str(ex))

            backup_info = {'id': db_info.id,
                           'name': name,
                           'description': description,
                           'instance_id': instance_id,
                           'backup_type': db_info.backup_type,
                           'checksum': db_info.checksum,
                           }
            api.API(context).create_backup(backup_info, instance_id)
            return db_info
Exemple #4
0
        def _create_resources():
            # parse the ID from the Ref
            instance_id = utils.get_id_from_href(instance)

            # verify that the instance exists and can perform actions
            from trove.instance.models import Instance
            instance_model = Instance.load(context, instance_id)
            instance_model.validate_can_perform_action()
            cls.validate_can_perform_action(instance_model, 'backup_create')
            cls.verify_swift_auth_token(context)
            if instance_model.cluster_id is not None:
                raise exception.ClusterInstanceOperationNotSupported()

            ds = instance_model.datastore
            ds_version = instance_model.datastore_version
            parent = None
            if parent_id:
                # Look up the parent info or fail early if not found or if
                # the user does not have access to the parent.
                _parent = cls.get_by_id(context, parent_id)
                parent = {
                    'location': _parent.location,
                    'checksum': _parent.checksum,
                }
            try:
                db_info = DBBackup.create(name=name,
                                          description=description,
                                          tenant_id=context.tenant,
                                          state=BackupState.NEW,
                                          instance_id=instance_id,
                                          parent_id=parent_id,
                                          datastore_version_id=ds_version.id,
                                          deleted=False)
            except exception.InvalidModelError as ex:
                LOG.exception(
                    _("Unable to create backup record for "
                      "instance: %s"), instance_id)
                raise exception.BackupCreationError(str(ex))

            backup_info = {
                'id': db_info.id,
                'name': name,
                'description': description,
                'instance_id': instance_id,
                'backup_type': db_info.backup_type,
                'checksum': db_info.checksum,
                'parent': parent,
                'datastore': ds.name,
                'datastore_version': ds_version.name,
            }
            api.API(context).create_backup(backup_info, instance_id)
            return db_info
Exemple #5
0
    def create(self, req, body, tenant_id):
        LOG.info("Creating a backup for tenant %s", tenant_id)
        context = req.environ[wsgi.CONTEXT_KEY]
        policy.authorize_on_tenant(context, 'backup:create')
        data = body['backup']
        instance = data.get('instance')
        name = data['name']
        desc = data.get('description')
        parent = data.get('parent_id')
        incremental = data.get('incremental')
        swift_container = data.get('swift_container')
        restore_from = data.get('restore_from')

        context.notification = notification.DBaaSBackupCreate(context,
                                                              request=req)

        if not restore_from:
            if not instance:
                raise exception.BackupCreationError('instance is missing.')
            if not swift_container:
                instance_id = utils.get_id_from_href(instance)
                backup_strategy = BackupStrategy.get(context, instance_id)
                if backup_strategy:
                    swift_container = backup_strategy.swift_container

        with StartNotification(context,
                               name=name,
                               instance_id=instance,
                               description=desc,
                               parent_id=parent):
            backup = Backup.create(context,
                                   instance,
                                   name,
                                   desc,
                                   parent_id=parent,
                                   incremental=incremental,
                                   swift_container=swift_container,
                                   restore_from=restore_from)

        return wsgi.Result(views.BackupView(backup).data(), 202)
Exemple #6
0
        def _create_resources():
            try:
                db_info = DBBackup.create(name=name,
                                          description=description,
                                          tenant_id=context.project_id,
                                          state=backup_state,
                                          instance_id=instance_id,
                                          parent_id=parent_id
                                          or last_backup_id,
                                          datastore_version_id=ds_version.id,
                                          deleted=False,
                                          location=location,
                                          checksum=checksum,
                                          backup_type=backup_type,
                                          size=size)
            except exception.InvalidModelError as ex:
                LOG.exception(
                    "Unable to create backup record for "
                    "instance: %s", instance_id)
                raise exception.BackupCreationError(str(ex))

            if not restore_from:
                backup_info = {
                    'id': db_info.id,
                    'name': name,
                    'description': description,
                    'instance_id': instance_id,
                    'backup_type': db_info.backup_type,
                    'checksum': db_info.checksum,
                    'parent': parent,
                    'datastore': ds.name,
                    'datastore_version': ds_version.name,
                    'swift_container': swift_container
                }
                api.API(context).create_backup(backup_info, instance_id)
            else:
                context.notification.payload.update({'backup_id': db_info.id})

            return db_info
Exemple #7
0
    def persist_data(self):
        save_cmd = 'SAVE'
        last_save = self.__client.lastsave()
        LOG.debug("Starting Redis data persist")
        save_ok = True
        try:
            save_ok = self.__client.bgsave()
        except redis.exceptions.ResponseError as re:
            # If an auto-save is in progress just use it, since it must have
            # just happened
            if "Background save already in progress" in str(re):
                LOG.info("Waiting for existing background save to finish")
            else:
                raise
        if save_ok:
            save_cmd = 'BGSAVE'

            def _timestamp_changed():
                return last_save != self.__client.lastsave()

            try:
                utils.poll_until(_timestamp_changed,
                                 sleep_time=2,
                                 time_out=TIME_OUT)
            except exception.PollTimeOut:
                raise RuntimeError(
                    _("Timeout occurred waiting for Redis "
                      "persist (%s) to complete.") % save_cmd)

        # If the background save fails for any reason, try doing a foreground
        # one.  This blocks client connections, so we don't want it to be
        # the default.
        elif not self.__client.save():
            raise exception.BackupCreationError(
                _("Could not persist "
                  "Redis data (%s)") % save_cmd)
        LOG.debug("Redis data persist (%s) completed", save_cmd)
Exemple #8
0
    def create(cls,
               context,
               instance,
               name,
               description=None,
               parent_id=None,
               incremental=False,
               swift_container=None,
               restore_from=None):
        """
        create db record for Backup
        :param cls:
        :param context: tenant_id included
        :param instance:
        :param name:
        :param description:
        :param parent_id:
        :param incremental: flag to indicate incremental backup
                            based on previous backup
        :param swift_container: Swift container name.
        :param restore_from: A dict that contains backup information of another
                             region.
        :return:
        """
        backup_state = BackupState.NEW
        checksum = None
        instance_id = None
        parent = None
        last_backup_id = None
        location = None
        backup_type = constants.BACKUP_TYPE_FULL
        size = None

        if restore_from:
            # Check location and datastore version.
            LOG.info(f"Restoring backup, restore_from: {restore_from}")
            backup_state = BackupState.RESTORED

            ds_version_id = restore_from.get('local_datastore_version_id')
            ds_version = datastore_models.DatastoreVersion.load_by_uuid(
                ds_version_id)

            location = restore_from.get('remote_location')
            swift_client = clients.create_swift_client(context)
            try:
                obj_meta = swift.get_metadata(swift_client,
                                              location,
                                              extra_attrs=['etag'])
            except Exception:
                msg = f'Failed to restore backup from {location}'
                LOG.exception(msg)
                raise exception.BackupCreationError(msg)

            checksum = obj_meta['etag']
            if 'parent_location' in obj_meta:
                backup_type = constants.BACKUP_TYPE_INC

            size = restore_from['size']
        else:
            instance_id = utils.get_id_from_href(instance)
            # Import here to avoid circular imports.
            from trove.instance import models as inst_model
            instance_model = inst_model.Instance.load(context, instance_id)
            instance_model.validate_can_perform_action()
            if instance_model.cluster_id is not None:
                raise exception.ClusterInstanceOperationNotSupported()

            cls.validate_can_perform_action(instance_model, 'backup_create')

            cls.verify_swift_auth_token(context)

            ds = instance_model.datastore
            ds_version = instance_model.datastore_version

            if parent_id:
                # Look up the parent info or fail early if not found or if
                # the user does not have access to the parent.
                _parent = cls.get_by_id(context, parent_id)
                parent = {
                    'location': _parent.location,
                    'checksum': _parent.checksum,
                }
            elif incremental:
                _parent = Backup.get_last_completed(context, instance_id)
                if _parent:
                    parent = {
                        'location': _parent.location,
                        'checksum': _parent.checksum
                    }
                    last_backup_id = _parent.id

            if parent:
                backup_type = constants.BACKUP_TYPE_INC

        def _create_resources():
            try:
                db_info = DBBackup.create(name=name,
                                          description=description,
                                          tenant_id=context.project_id,
                                          state=backup_state,
                                          instance_id=instance_id,
                                          parent_id=parent_id
                                          or last_backup_id,
                                          datastore_version_id=ds_version.id,
                                          deleted=False,
                                          location=location,
                                          checksum=checksum,
                                          backup_type=backup_type,
                                          size=size)
            except exception.InvalidModelError as ex:
                LOG.exception(
                    "Unable to create backup record for "
                    "instance: %s", instance_id)
                raise exception.BackupCreationError(str(ex))

            if not restore_from:
                backup_info = {
                    'id': db_info.id,
                    'name': name,
                    'description': description,
                    'instance_id': instance_id,
                    'backup_type': db_info.backup_type,
                    'checksum': db_info.checksum,
                    'parent': parent,
                    'datastore': ds.name,
                    'datastore_version': ds_version.name,
                    'swift_container': swift_container
                }
                api.API(context).create_backup(backup_info, instance_id)
            else:
                context.notification.payload.update({'backup_id': db_info.id})

            return db_info

        return run_with_quotas(context.project_id, {'backups': 1},
                               _create_resources)