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."))
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)
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
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
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)
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
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)
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)