107 lines
3.4 KiB
Python
107 lines
3.4 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 eventlet.green import subprocess
|
|
from oslo_log import log as logging
|
|
|
|
from trove.common import cfg
|
|
from trove.common.strategies.strategy import Strategy
|
|
from trove.common import utils
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
CONF = cfg.CONF
|
|
CHUNK_SIZE = CONF.backup_chunk_size
|
|
BACKUP_USE_GZIP = CONF.backup_use_gzip_compression
|
|
BACKUP_USE_OPENSSL = CONF.backup_use_openssl_encryption
|
|
BACKUP_DECRYPT_KEY = CONF.backup_aes_cbc_key
|
|
|
|
|
|
class RestoreError(Exception):
|
|
"""Error running the Backup Command."""
|
|
|
|
|
|
class RestoreRunner(Strategy):
|
|
"""Base class for Restore Strategy implementations."""
|
|
"""Restore a database from a previous backup."""
|
|
|
|
__strategy_type__ = 'restore_runner'
|
|
__strategy_ns__ = 'trove.guestagent.strategies.restore'
|
|
|
|
# The actual system calls to run the restore and prepare
|
|
restore_cmd = None
|
|
|
|
# The backup format type
|
|
restore_type = None
|
|
|
|
# Decryption Parameters
|
|
is_zipped = BACKUP_USE_GZIP
|
|
is_encrypted = BACKUP_USE_OPENSSL
|
|
decrypt_key = BACKUP_DECRYPT_KEY
|
|
|
|
def __init__(self, storage, **kwargs):
|
|
self.storage = storage
|
|
self.location = kwargs.pop('location')
|
|
self.checksum = kwargs.pop('checksum')
|
|
self.restore_location = kwargs.get('restore_location')
|
|
self.restore_cmd = (self.decrypt_cmd +
|
|
self.unzip_cmd +
|
|
(self.base_restore_cmd % kwargs))
|
|
super(RestoreRunner, self).__init__()
|
|
|
|
def pre_restore(self):
|
|
"""Hook that is called before the restore command."""
|
|
pass
|
|
|
|
def post_restore(self):
|
|
"""Hook that is called after the restore command."""
|
|
pass
|
|
|
|
def restore(self):
|
|
self.pre_restore()
|
|
content_length = self._run_restore()
|
|
self.post_restore()
|
|
return content_length
|
|
|
|
def _run_restore(self):
|
|
return self._unpack(self.location, self.checksum, self.restore_cmd)
|
|
|
|
def _unpack(self, location, checksum, command):
|
|
stream = self.storage.load(location, checksum)
|
|
process = subprocess.Popen(command, shell=True,
|
|
stdin=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
content_length = 0
|
|
for chunk in stream:
|
|
process.stdin.write(chunk)
|
|
content_length += len(chunk)
|
|
process.stdin.close()
|
|
utils.raise_if_process_errored(process, RestoreError)
|
|
LOG.debug("Restored %s bytes from stream.", content_length)
|
|
|
|
return content_length
|
|
|
|
@property
|
|
def decrypt_cmd(self):
|
|
if self.is_encrypted:
|
|
return ('openssl enc -d -aes-256-cbc -salt -pass pass:%s | '
|
|
% self.decrypt_key)
|
|
else:
|
|
return ''
|
|
|
|
@property
|
|
def unzip_cmd(self):
|
|
return 'gzip -d -c | ' if self.is_zipped else ''
|