179 lines
7.5 KiB
Python
179 lines
7.5 KiB
Python
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
|
|
from oslo_log import log as logging
|
|
from oslo_utils import timeutils
|
|
|
|
from trove.backup.state import BackupState
|
|
from trove.common import cfg
|
|
from trove.common.i18n import _
|
|
from trove.common.strategies.storage import get_storage_strategy
|
|
from trove.conductor import api as conductor_api
|
|
from trove.guestagent.dbaas import get_filesystem_volume_stats
|
|
from trove.guestagent.strategies.backup.base import BackupError
|
|
from trove.guestagent.strategies.backup.base import UnknownBackupType
|
|
from trove.guestagent.strategies.backup import get_backup_strategy
|
|
from trove.guestagent.strategies.restore import get_restore_strategy
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
CONF = cfg.CONF
|
|
|
|
CONFIG_MANAGER = CONF.get('mysql'
|
|
if not CONF.datastore_manager
|
|
else CONF.datastore_manager)
|
|
|
|
STRATEGY = CONFIG_MANAGER.backup_strategy
|
|
BACKUP_NAMESPACE = CONFIG_MANAGER.backup_namespace
|
|
RESTORE_NAMESPACE = CONFIG_MANAGER.restore_namespace
|
|
RUNNER = get_backup_strategy(STRATEGY, BACKUP_NAMESPACE)
|
|
EXTRA_OPTS = CONF.backup_runner_options.get(STRATEGY, '')
|
|
|
|
# Try to get the incremental strategy or return the default 'backup_strategy'
|
|
INCREMENTAL = CONFIG_MANAGER.backup_incremental_strategy.get(
|
|
STRATEGY, STRATEGY)
|
|
|
|
INCREMENTAL_RUNNER = get_backup_strategy(INCREMENTAL, BACKUP_NAMESPACE)
|
|
|
|
|
|
class BackupAgent(object):
|
|
def _get_restore_runner(self, backup_type):
|
|
"""Returns the RestoreRunner associated with this backup type."""
|
|
try:
|
|
runner = get_restore_strategy(backup_type, RESTORE_NAMESPACE)
|
|
except ImportError:
|
|
raise UnknownBackupType(_("Unknown Backup type: %(type)s in "
|
|
"namespace %(ns)s")
|
|
% {"type": backup_type,
|
|
"ns": RESTORE_NAMESPACE})
|
|
return runner
|
|
|
|
def stream_backup_to_storage(self, context, backup_info, runner, storage,
|
|
parent_metadata={}, extra_opts=EXTRA_OPTS):
|
|
backup_id = backup_info['id']
|
|
conductor = conductor_api.API(context)
|
|
|
|
# Store the size of the filesystem before the backup.
|
|
mount_point = CONFIG_MANAGER.mount_point
|
|
stats = get_filesystem_volume_stats(mount_point)
|
|
backup_state = {
|
|
'backup_id': backup_id,
|
|
'size': stats.get('used', 0.0),
|
|
'state': BackupState.BUILDING,
|
|
}
|
|
conductor.update_backup(CONF.guest_id,
|
|
sent=timeutils.utcnow_ts(microsecond=True),
|
|
**backup_state)
|
|
LOG.debug("Updated state for %s to %s.", backup_id, backup_state)
|
|
|
|
try:
|
|
with runner(filename=backup_id, extra_opts=extra_opts,
|
|
**parent_metadata) as bkup:
|
|
LOG.debug("Starting backup %s.", backup_id)
|
|
meta = {}
|
|
meta['datastore'] = backup_info['datastore']
|
|
meta['datastore_version'] = backup_info['datastore_version']
|
|
success, note, checksum, location = storage.save(
|
|
bkup.manifest,
|
|
bkup,
|
|
metadata=meta)
|
|
|
|
backup_state.update({
|
|
'checksum': checksum,
|
|
'location': location,
|
|
'note': note,
|
|
'success': success,
|
|
'backup_type': bkup.backup_type,
|
|
})
|
|
|
|
LOG.debug("Backup %(backup_id)s completed status: "
|
|
"%(success)s.", backup_state)
|
|
LOG.debug("Backup %(backup_id)s file swift checksum: "
|
|
"%(checksum)s.", backup_state)
|
|
LOG.debug("Backup %(backup_id)s location: "
|
|
"%(location)s.", backup_state)
|
|
|
|
if not success:
|
|
raise BackupError(note)
|
|
|
|
backup_state.update({'state': BackupState.COMPLETED})
|
|
|
|
return meta
|
|
|
|
except Exception:
|
|
LOG.exception(
|
|
"Error saving backup: %(backup_id)s.", backup_state)
|
|
backup_state.update({'state': BackupState.FAILED})
|
|
raise
|
|
finally:
|
|
LOG.info("Completed backup %(backup_id)s.", backup_state)
|
|
conductor.update_backup(CONF.guest_id,
|
|
sent=timeutils.utcnow_ts(
|
|
microsecond=True),
|
|
**backup_state)
|
|
LOG.info("Updated state for %s to %s.", backup_id, backup_state)
|
|
|
|
def execute_backup(self, context, backup_info,
|
|
runner=RUNNER, extra_opts=EXTRA_OPTS,
|
|
incremental_runner=INCREMENTAL_RUNNER):
|
|
|
|
LOG.info("Running backup %(id)s.", backup_info)
|
|
storage = get_storage_strategy(
|
|
CONF.storage_strategy,
|
|
CONF.storage_namespace)(context)
|
|
|
|
# Check if this is an incremental backup and grab the parent metadata
|
|
parent_metadata = {}
|
|
if backup_info.get('parent'):
|
|
runner = incremental_runner
|
|
LOG.debug("Using incremental backup runner: %s.", runner.__name__)
|
|
parent = backup_info['parent']
|
|
parent_metadata = storage.load_metadata(parent['location'],
|
|
parent['checksum'])
|
|
# The parent could be another incremental backup so we need to
|
|
# reset the location and checksum to *this* parents info
|
|
parent_metadata.update({
|
|
'parent_location': parent['location'],
|
|
'parent_checksum': parent['checksum']
|
|
})
|
|
|
|
self.stream_backup_to_storage(context, backup_info, runner, storage,
|
|
parent_metadata, extra_opts)
|
|
|
|
def execute_restore(self, context, backup_info, restore_location):
|
|
try:
|
|
restore_runner = self._get_restore_runner(backup_info['type'])
|
|
storage = get_storage_strategy(
|
|
CONF.storage_strategy,
|
|
CONF.storage_namespace)(context)
|
|
|
|
runner = restore_runner(storage, location=backup_info['location'],
|
|
checksum=backup_info['checksum'],
|
|
restore_location=restore_location)
|
|
backup_info['restore_location'] = restore_location
|
|
|
|
LOG.info("Restoring instance from backup %(id)s to "
|
|
"%(restore_location)s", backup_info)
|
|
|
|
content_size = runner.restore()
|
|
LOG.info("Restore from backup %(id)s completed successfully "
|
|
"to %(restore_location)s", backup_info)
|
|
LOG.debug("Restore size: %s", content_size)
|
|
except Exception:
|
|
LOG.exception("Error restoring backup %(id)s", backup_info)
|
|
raise
|
|
else:
|
|
LOG.debug("Restored backup %(id)s", backup_info)
|