Move mysql datadir to a sub-directory on mounted volume
Having the mysql datadir located at root of a mounted volume (which happens with volume support enabled) causes problems for backup/restore and resize. Mysql decides that the "lost+found" directory, which the operating system puts at the root of each file system, is actually a mysql "database". This change causes the guest agent to move the datadir into a sub-directory called "data" on the mounted volume. Change-Id: Ica628012a5708374d73e5394e370da2514300939 Co-Authored-By: Simon Chang <schang@tesora.com> Closes-bug: 1370646
This commit is contained in:
parent
a41fe2c2d5
commit
ad42de03d3
@ -1,5 +1,5 @@
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: 1077
|
||||
Content-Length: 1082
|
||||
Date: Mon, 18 Mar 2013 19:09:17 GMT
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
"configuration": {
|
||||
"basedir": "/usr",
|
||||
"connect_timeout": "15",
|
||||
"datadir": "/var/lib/mysql",
|
||||
"datadir": "/var/lib/mysql/data",
|
||||
"default_storage_engine": "innodb",
|
||||
"innodb_buffer_pool_size": "150M",
|
||||
"innodb_data_file_path": "ibdata1:10M:autoextend",
|
||||
|
@ -23,6 +23,7 @@ from trove.common import exception
|
||||
from trove.common.i18n import _
|
||||
from trove.common import instance as rd_instance
|
||||
from trove.guestagent import backup
|
||||
from trove.guestagent.common import operating_system
|
||||
from trove.guestagent.datastore.mysql.service import MySqlAdmin
|
||||
from trove.guestagent.datastore.mysql.service import MySqlApp
|
||||
from trove.guestagent.datastore.mysql.service import MySqlAppStatus
|
||||
@ -130,15 +131,24 @@ class Manager(periodic_task.PeriodicTasks):
|
||||
device.unmount_device(device_path)
|
||||
device.format()
|
||||
if os.path.exists(mount_point):
|
||||
# rsync exiting data
|
||||
device.migrate_data(mount_point)
|
||||
# rsync existing data to a "data" sub-directory
|
||||
# on the new volume
|
||||
device.migrate_data(mount_point, target_subdir="data")
|
||||
# mount the volume
|
||||
device.mount(mount_point)
|
||||
LOG.debug("Mounted the volume.")
|
||||
operating_system.chown(mount_point, 'mysql', 'mysql',
|
||||
recursive=False, as_root=True)
|
||||
|
||||
LOG.debug("Mounted the volume at %s." % mount_point)
|
||||
# We need to temporarily update the default my.cnf so that
|
||||
# mysql will start after the volume is mounted. Later on it
|
||||
# will be changed based on the config template and restart.
|
||||
app.update_overrides("[mysqld]\ndatadir=%s/data\n"
|
||||
% mount_point)
|
||||
app.start_mysql()
|
||||
if backup_info:
|
||||
self._perform_restore(backup_info, context,
|
||||
mount_point, app)
|
||||
mount_point + "/data", app)
|
||||
LOG.debug("Securing MySQL now.")
|
||||
app.secure(config_contents, overrides)
|
||||
enable_root_on_restore = (backup_info and
|
||||
|
@ -27,6 +27,7 @@ from sqlalchemy import interfaces
|
||||
from sqlalchemy.sql.expression import text
|
||||
|
||||
from trove.common import cfg
|
||||
from trove.common import configurations
|
||||
from trove.common import exception
|
||||
from trove.common.exception import PollTimeOut
|
||||
from trove.common.i18n import _
|
||||
@ -44,11 +45,11 @@ ADMIN_USER_NAME = "os_admin"
|
||||
LOG = logging.getLogger(__name__)
|
||||
FLUSH = text(sql_query.FLUSH)
|
||||
ENGINE = None
|
||||
DATADIR = None
|
||||
PREPARING = False
|
||||
UUID = False
|
||||
|
||||
TMP_MYCNF = "/tmp/my.cnf.tmp"
|
||||
MYSQL_BASE_DIR = "/var/lib/mysql"
|
||||
|
||||
CONF = cfg.CONF
|
||||
MANAGER = CONF.datastore_manager if CONF.datastore_manager else 'mysql'
|
||||
@ -156,6 +157,28 @@ def load_mysqld_options():
|
||||
return {}
|
||||
|
||||
|
||||
def read_mycnf():
|
||||
with open(MYSQL_CONFIG, 'r') as file:
|
||||
config_contents = file.read()
|
||||
|
||||
return config_contents
|
||||
|
||||
|
||||
def get_datadir(reset_cache=False):
|
||||
"""Return the data directory currently used by Mysql."""
|
||||
global DATADIR
|
||||
if not reset_cache and DATADIR:
|
||||
return DATADIR
|
||||
|
||||
mycnf_contents = read_mycnf()
|
||||
|
||||
# look for datadir parameter in my.cnf
|
||||
mycnf = dict(configurations.MySQLConfParser(mycnf_contents).parse())
|
||||
DATADIR = mycnf['datadir']
|
||||
|
||||
return DATADIR
|
||||
|
||||
|
||||
class MySqlAppStatus(service.BaseDbStatus):
|
||||
@classmethod
|
||||
def get(cls):
|
||||
@ -427,10 +450,6 @@ class MySqlAdmin(object):
|
||||
"be omitted from the listing: %s" % ignored_database_names)
|
||||
databases = []
|
||||
with LocalSqlClient(get_engine()) as client:
|
||||
# If you have an external volume mounted at /var/lib/mysql
|
||||
# the lost+found directory will show up in mysql as a database
|
||||
# which will create errors if you try to do any database ops
|
||||
# on it. So we remove it here if it exists.
|
||||
q = sql_query.Query()
|
||||
q.columns = [
|
||||
'schema_name as name',
|
||||
@ -769,7 +788,7 @@ class MySqlApp(object):
|
||||
# to be deleted. That's why its ok if they aren't found and
|
||||
# that is why we use the "force" option to "remove".
|
||||
operating_system.remove("%s/ib_logfile%d"
|
||||
% (MYSQL_BASE_DIR, index), force=True,
|
||||
% (get_datadir(), index), force=True,
|
||||
as_root=True)
|
||||
except exception.ProcessExecutionError:
|
||||
LOG.exception("Could not delete logfile.")
|
||||
|
@ -19,6 +19,7 @@ import re
|
||||
|
||||
from trove.guestagent.datastore.mysql.service import ADMIN_USER_NAME
|
||||
from trove.guestagent.datastore.mysql.service import get_auth_password
|
||||
from trove.guestagent.datastore.mysql.service import get_datadir
|
||||
from trove.guestagent.strategies.backup import base
|
||||
from trove.openstack.common import log as logging
|
||||
|
||||
@ -51,8 +52,9 @@ class InnoBackupEx(base.BackupRunner):
|
||||
def cmd(self):
|
||||
cmd = ('sudo innobackupex'
|
||||
' --stream=xbstream'
|
||||
' %(extra_opts)s'
|
||||
' /var/lib/mysql 2>/tmp/innobackupex.log'
|
||||
' %(extra_opts)s '
|
||||
+ get_datadir() +
|
||||
' 2>/tmp/innobackupex.log'
|
||||
)
|
||||
return cmd + self.zip_cmd + self.encrypt_cmd
|
||||
|
||||
@ -105,8 +107,8 @@ class InnoBackupExIncremental(InnoBackupEx):
|
||||
' --stream=xbstream'
|
||||
' --incremental'
|
||||
' --incremental-lsn=%(lsn)s'
|
||||
' %(extra_opts)s'
|
||||
' /var/lib/mysql'
|
||||
' %(extra_opts)s '
|
||||
+ get_datadir() +
|
||||
' 2>/tmp/innobackupex.log')
|
||||
return cmd + self.zip_cmd + self.encrypt_cmd
|
||||
|
||||
|
@ -22,6 +22,7 @@ from trove.common.i18n import _
|
||||
from trove.guestagent.backup.backupagent import BackupAgent
|
||||
from trove.guestagent.common import operating_system
|
||||
from trove.guestagent.common.operating_system import FileMode
|
||||
from trove.guestagent.datastore.mysql.service import get_datadir
|
||||
from trove.guestagent.strategies.replication import mysql_base
|
||||
from trove.openstack.common import log as logging
|
||||
|
||||
@ -61,7 +62,7 @@ class MysqlBinlogReplication(mysql_base.MysqlReplicationBase):
|
||||
service.start_slave()
|
||||
|
||||
def _read_log_position(self):
|
||||
INFO_FILE = '/var/lib/mysql/data/xtrabackup_binlog_info'
|
||||
INFO_FILE = ('%s/xtrabackup_binlog_info' % get_datadir())
|
||||
LOG.info(_("Setting read permissions on %s") % INFO_FILE)
|
||||
operating_system.chmod(INFO_FILE, FileMode.ADD_READ_ALL, as_root=True)
|
||||
LOG.info(_("Reading log position from %s") % INFO_FILE)
|
||||
|
@ -54,8 +54,7 @@ class RestoreRunner(Strategy):
|
||||
self.storage = storage
|
||||
self.location = kwargs.pop('location')
|
||||
self.checksum = kwargs.pop('checksum')
|
||||
self.restore_location = kwargs.get('restore_location',
|
||||
'/var/lib/mysql')
|
||||
self.restore_location = kwargs.get('restore_location')
|
||||
self.restore_cmd = (self.decrypt_cmd +
|
||||
self.unzip_cmd +
|
||||
(self.base_restore_cmd % kwargs))
|
||||
|
@ -293,7 +293,7 @@ class InnoBackupExIncremental(InnoBackupEx):
|
||||
self._incremental_restore(parent_location, parent_checksum)
|
||||
# for *this* backup set the incremental_dir
|
||||
# just use the checksum for the incremental path as it is
|
||||
# sufficiently unique /var/lib/mysql/<checksum>
|
||||
# sufficiently unique /var/lib/mysql/data/<checksum>
|
||||
incremental_dir = os.path.join(self.restore_location, checksum)
|
||||
operating_system.create_directory(incremental_dir, as_root=True)
|
||||
command = self._incremental_restore_cmd(incremental_dir)
|
||||
|
@ -37,16 +37,19 @@ class VolumeDevice(object):
|
||||
def __init__(self, device_path):
|
||||
self.device_path = device_path
|
||||
|
||||
def migrate_data(self, source_dir):
|
||||
def migrate_data(self, source_dir, target_subdir=None):
|
||||
"""Synchronize the data from the source directory to the new
|
||||
volume.
|
||||
volume; optionally to a new sub-directory on the new volume.
|
||||
"""
|
||||
self.mount(TMP_MOUNT_POINT, write_to_fstab=False)
|
||||
if not source_dir[-1] == '/':
|
||||
source_dir = "%s/" % source_dir
|
||||
target_dir = TMP_MOUNT_POINT
|
||||
if target_subdir:
|
||||
target_dir = target_dir + "/" + target_subdir
|
||||
utils.execute("sudo", "rsync", "--safe-links", "--perms",
|
||||
"--recursive", "--owner", "--group", "--xattrs",
|
||||
"--sparse", source_dir, TMP_MOUNT_POINT)
|
||||
"--sparse", source_dir, target_dir)
|
||||
self.unmount(TMP_MOUNT_POINT)
|
||||
|
||||
def _check_device_exists(self):
|
||||
|
@ -1,4 +1,4 @@
|
||||
[mysqld]
|
||||
log_bin = /var/lib/mysql/mysql-bin.log
|
||||
relay_log = /var/lib/mysql/mysql-relay-bin.log
|
||||
log_bin = /var/lib/mysql/data/mysql-bin.log
|
||||
relay_log = /var/lib/mysql/data/mysql-relay-bin.log
|
||||
read_only = true
|
||||
|
@ -1,2 +1,2 @@
|
||||
[mysqld]
|
||||
log_bin = /var/lib/mysql/mysql-bin.log
|
||||
log_bin = /var/lib/mysql/data/mysql-bin.log
|
||||
|
@ -8,7 +8,7 @@ nice = 0
|
||||
user = mysql
|
||||
port = 3306
|
||||
basedir = /usr
|
||||
datadir = /var/lib/mysql
|
||||
datadir = /var/lib/mysql/data
|
||||
####tmpdir = /tmp
|
||||
tmpdir = /var/tmp
|
||||
pid_file = /var/run/mysqld/mysqld.pid
|
||||
|
@ -1,6 +1,6 @@
|
||||
[mysqld]
|
||||
log_bin = /var/lib/mysql/mysql-bin.log
|
||||
relay_log = /var/lib/mysql/mysql-relay-bin.log
|
||||
log_bin = /var/lib/mysql/data/mysql-bin.log
|
||||
relay_log = /var/lib/mysql/data/mysql-relay-bin.log
|
||||
relay_log_info_repository = TABLE
|
||||
relay_log_recovery = 1
|
||||
relay_log_purge = 1
|
||||
|
@ -1,5 +1,5 @@
|
||||
[mysqld]
|
||||
log_bin = /var/lib/mysql/mysql-bin.log
|
||||
log_bin = /var/lib/mysql/data/mysql-bin.log
|
||||
binlog_format = MIXED
|
||||
enforce_gtid_consistency = ON
|
||||
gtid_mode = ON
|
||||
|
@ -1,4 +1,4 @@
|
||||
[mysqld]
|
||||
log_bin = /var/lib/mysql/mysql-bin.log
|
||||
relay_log = /var/lib/mysql/mysql-relay-bin.log
|
||||
log_bin = /var/lib/mysql/data/mysql-bin.log
|
||||
relay_log = /var/lib/mysql/data/mysql-relay-bin.log
|
||||
read_only = true
|
||||
|
@ -1,2 +1,2 @@
|
||||
[mysqld]
|
||||
log_bin = /var/lib/mysql/mysql-bin.log
|
||||
log_bin = /var/lib/mysql/data/mysql-bin.log
|
||||
|
@ -8,7 +8,7 @@ nice = 0
|
||||
user = mysql
|
||||
port = 3306
|
||||
basedir = /usr
|
||||
datadir = /var/lib/mysql
|
||||
datadir = /var/lib/mysql/data
|
||||
####tmpdir = /tmp
|
||||
tmpdir = /var/tmp
|
||||
pid_file = /var/run/mysqld/mysqld.pid
|
||||
|
@ -1,6 +1,6 @@
|
||||
[mysqld]
|
||||
log_bin = /var/lib/mysql/mysql-bin.log
|
||||
relay_log = /var/lib/mysql/mysql-relay-bin.log
|
||||
log_bin = /var/lib/mysql/data/mysql-bin.log
|
||||
relay_log = /var/lib/mysql/data/mysql-relay-bin.log
|
||||
relay_log_info_repository = TABLE
|
||||
relay_log_recovery = 1
|
||||
relay_log_purge = 1
|
||||
|
@ -1,5 +1,5 @@
|
||||
[mysqld]
|
||||
log_bin = /var/lib/mysql/mysql-bin.log
|
||||
log_bin = /var/lib/mysql/data/mysql-bin.log
|
||||
binlog_format = MIXED
|
||||
enforce_gtid_consistency = ON
|
||||
gtid_mode = ON
|
||||
|
@ -235,7 +235,7 @@ class RebootTestBase(ActionTestBase):
|
||||
def mess_up_mysql(self):
|
||||
"""Ruin MySQL's ability to restart."""
|
||||
server = create_server_connection(self.instance_id)
|
||||
cmd = "sudo cp /dev/null /var/lib/mysql/ib_logfile%d"
|
||||
cmd = "sudo cp /dev/null /var/lib/mysql/data/ib_logfile%d"
|
||||
instance_info.dbaas_admin.management.stop(self.instance_id)
|
||||
for index in range(2):
|
||||
server.execute(cmd % index)
|
||||
@ -244,7 +244,7 @@ class RebootTestBase(ActionTestBase):
|
||||
"""Fix MySQL's ability to restart."""
|
||||
if not FAKE_MODE:
|
||||
server = create_server_connection(self.instance_id)
|
||||
cmd = "sudo rm /var/lib/mysql/ib_logfile%d"
|
||||
cmd = "sudo rm /var/lib/mysql/data/ib_logfile%d"
|
||||
# We want to stop mysql so that upstart does not keep trying to
|
||||
# respawn it and block the guest agent from accessing the logs.
|
||||
instance_info.dbaas_admin.management.stop(self.instance_id)
|
||||
|
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import hashlib
|
||||
import mock
|
||||
import os
|
||||
|
||||
from mock import Mock, MagicMock, patch, ANY
|
||||
@ -203,16 +204,18 @@ class BackupAgentTest(trove_testtools.TestCase):
|
||||
self.assertIsNotNone(mysql_dump.manifest)
|
||||
self.assertEqual('abc.gz.enc', mysql_dump.manifest)
|
||||
|
||||
def test_backup_impl_InnoBackupEx(self):
|
||||
@mock.patch('trove.guestagent.strategies.backup.mysql_impl.get_datadir')
|
||||
def test_backup_impl_InnoBackupEx(self, mock_datadir):
|
||||
"""This test is for
|
||||
guestagent/strategies/backup/impl
|
||||
"""
|
||||
mock_datadir.return_value = '/var/lib/mysql/data'
|
||||
inno_backup_ex = mysql_impl.InnoBackupEx('innobackupex', extra_opts='')
|
||||
self.assertIsNotNone(inno_backup_ex.cmd)
|
||||
str_innobackup_cmd = ('sudo innobackupex'
|
||||
' --stream=xbstream'
|
||||
' %(extra_opts)s'
|
||||
' /var/lib/mysql 2>/tmp/innobackupex.log'
|
||||
' /var/lib/mysql/data 2>/tmp/innobackupex.log'
|
||||
' | gzip |'
|
||||
' openssl enc -aes-256-cbc -salt '
|
||||
'-pass pass:default_aes_cbc_key')
|
||||
@ -394,7 +397,7 @@ class BackupAgentTest(trove_testtools.TestCase):
|
||||
}
|
||||
agent.execute_restore(TroveContext(),
|
||||
bkup_info,
|
||||
'/var/lib/mysql')
|
||||
'/var/lib/mysql/data')
|
||||
|
||||
def test_restore_unknown(self):
|
||||
with patch.object(backupagent, 'get_restore_strategy',
|
||||
@ -410,7 +413,7 @@ class BackupAgentTest(trove_testtools.TestCase):
|
||||
|
||||
self.assertRaises(UnknownBackupType, agent.execute_restore,
|
||||
context=None, backup_info=bkup_info,
|
||||
restore_location='/var/lib/mysql')
|
||||
restore_location='/var/lib/mysql/data')
|
||||
|
||||
@patch.object(conductor_api.API, 'get_client', Mock(return_value=Mock()))
|
||||
@patch.object(MockSwift, 'load_metadata', return_value={'lsn': '54321'})
|
||||
@ -440,7 +443,7 @@ class BackupAgentTest(trove_testtools.TestCase):
|
||||
|
||||
agent.execute_backup(TroveContext(),
|
||||
bkup_info,
|
||||
'/var/lib/mysql')
|
||||
'/var/lib/mysql/data')
|
||||
|
||||
self.assertTrue(MockStorage.save_metadata.called_once_with(
|
||||
ANY,
|
||||
|
@ -50,12 +50,13 @@ UNZIP = "gzip -d -c"
|
||||
ENCRYPT = "openssl enc -aes-256-cbc -salt -pass pass:default_aes_cbc_key"
|
||||
DECRYPT = "openssl enc -d -aes-256-cbc -salt -pass pass:default_aes_cbc_key"
|
||||
XTRA_BACKUP_RAW = ("sudo innobackupex --stream=xbstream %(extra_opts)s"
|
||||
" /var/lib/mysql 2>/tmp/innobackupex.log")
|
||||
" /var/lib/mysql/data 2>/tmp/innobackupex.log")
|
||||
XTRA_BACKUP = XTRA_BACKUP_RAW % {'extra_opts': ''}
|
||||
XTRA_BACKUP_EXTRA_OPTS = XTRA_BACKUP_RAW % {'extra_opts': '--no-lock'}
|
||||
XTRA_BACKUP_INCR = ('sudo innobackupex --stream=xbstream'
|
||||
' --incremental --incremental-lsn=%(lsn)s'
|
||||
' %(extra_opts)s /var/lib/mysql 2>/tmp/innobackupex.log')
|
||||
' %(extra_opts)s /var/lib/mysql/data'
|
||||
' 2>/tmp/innobackupex.log')
|
||||
SQLDUMP_BACKUP_RAW = ("mysqldump --all-databases %(extra_opts)s "
|
||||
"--opt --password=password -u os_admin"
|
||||
" 2>/tmp/mysqldump.log")
|
||||
@ -63,15 +64,15 @@ SQLDUMP_BACKUP = SQLDUMP_BACKUP_RAW % {'extra_opts': ''}
|
||||
SQLDUMP_BACKUP_EXTRA_OPTS = (SQLDUMP_BACKUP_RAW %
|
||||
{'extra_opts': '--events --routines --triggers'})
|
||||
XTRA_RESTORE_RAW = "sudo xbstream -x -C %(restore_location)s"
|
||||
XTRA_RESTORE = XTRA_RESTORE_RAW % {'restore_location': '/var/lib/mysql'}
|
||||
XTRA_RESTORE = XTRA_RESTORE_RAW % {'restore_location': '/var/lib/mysql/data'}
|
||||
XTRA_INCR_PREPARE = ("sudo innobackupex --apply-log"
|
||||
" --redo-only /var/lib/mysql"
|
||||
" --defaults-file=/var/lib/mysql/backup-my.cnf"
|
||||
" --redo-only /var/lib/mysql/data"
|
||||
" --defaults-file=/var/lib/mysql/data/backup-my.cnf"
|
||||
" --ibbackup xtrabackup %(incr)s"
|
||||
" 2>/tmp/innoprepare.log")
|
||||
SQLDUMP_RESTORE = "sudo mysql"
|
||||
PREPARE = ("sudo innobackupex --apply-log /var/lib/mysql "
|
||||
"--defaults-file=/var/lib/mysql/backup-my.cnf "
|
||||
PREPARE = ("sudo innobackupex --apply-log /var/lib/mysql/data "
|
||||
"--defaults-file=/var/lib/mysql/data/backup-my.cnf "
|
||||
"--ibbackup xtrabackup 2>/tmp/innoprepare.log")
|
||||
CRYPTO_KEY = "default_aes_cbc_key"
|
||||
|
||||
@ -92,11 +93,16 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
|
||||
mysql_impl.get_auth_password = mock.Mock(
|
||||
return_value='password')
|
||||
self.orig_exec_with_to = utils.execute_with_timeout
|
||||
self.patcher_get_datadir = patch(
|
||||
'trove.guestagent.strategies.backup.mysql_impl.get_datadir')
|
||||
self.mock_get_datadir = self.patcher_get_datadir.start()
|
||||
self.mock_get_datadir.return_value = '/var/lib/mysql/data'
|
||||
|
||||
def tearDown(self):
|
||||
super(GuestAgentBackupTest, self).tearDown()
|
||||
mysql_impl.get_auth_password = self.orig
|
||||
utils.execute_with_timeout = self.orig_exec_with_to
|
||||
self.patcher_get_datadir.stop()
|
||||
|
||||
def test_backup_decrypted_xtrabackup_command(self):
|
||||
backupBase.BackupRunner.is_zipped = True
|
||||
@ -186,7 +192,7 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
|
||||
restoreBase.RestoreRunner.is_zipped = True
|
||||
restoreBase.RestoreRunner.is_encrypted = False
|
||||
RunnerClass = utils.import_class(RESTORE_XTRA_CLS)
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql",
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql/data",
|
||||
location="filename", checksum="md5")
|
||||
self.assertEqual(UNZIP + PIPE + XTRA_RESTORE, restr.restore_cmd)
|
||||
self.assertEqual(PREPARE, restr.prepare_cmd)
|
||||
@ -196,7 +202,7 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
|
||||
restoreBase.RestoreRunner.is_encrypted = True
|
||||
restoreBase.RestoreRunner.decrypt_key = CRYPTO_KEY
|
||||
RunnerClass = utils.import_class(RESTORE_XTRA_CLS)
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql",
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql/data",
|
||||
location="filename", checksum="md5")
|
||||
self.assertEqual(DECRYPT + PIPE + UNZIP + PIPE + XTRA_RESTORE,
|
||||
restr.restore_cmd)
|
||||
@ -204,7 +210,7 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
|
||||
|
||||
def test_restore_xtrabackup_incremental_prepare_command(self):
|
||||
RunnerClass = utils.import_class(RESTORE_XTRA_INCR_CLS)
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql",
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql/data",
|
||||
location="filename", checksum="m5d")
|
||||
# Final prepare command (same as normal xtrabackup)
|
||||
self.assertEqual(PREPARE, restr.prepare_cmd)
|
||||
@ -221,7 +227,7 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
|
||||
restoreBase.RestoreRunner.is_zipped = True
|
||||
restoreBase.RestoreRunner.is_encrypted = False
|
||||
RunnerClass = utils.import_class(RESTORE_XTRA_INCR_CLS)
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql",
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql/data",
|
||||
location="filename", checksum="m5d")
|
||||
# Full restore command
|
||||
expected = UNZIP + PIPE + XTRA_RESTORE
|
||||
@ -237,7 +243,7 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
|
||||
restoreBase.RestoreRunner.is_encrypted = True
|
||||
restoreBase.RestoreRunner.decrypt_key = CRYPTO_KEY
|
||||
RunnerClass = utils.import_class(RESTORE_XTRA_INCR_CLS)
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql",
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql/data",
|
||||
location="filename", checksum="md5")
|
||||
# Full restore command
|
||||
expected = DECRYPT + PIPE + UNZIP + PIPE + XTRA_RESTORE
|
||||
@ -252,7 +258,7 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
|
||||
restoreBase.RestoreRunner.is_zipped = True
|
||||
restoreBase.RestoreRunner.is_encrypted = False
|
||||
RunnerClass = utils.import_class(RESTORE_SQLDUMP_CLS)
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql",
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql/data",
|
||||
location="filename", checksum="md5")
|
||||
self.assertEqual(UNZIP + PIPE + SQLDUMP_RESTORE, restr.restore_cmd)
|
||||
|
||||
@ -261,7 +267,7 @@ class GuestAgentBackupTest(trove_testtools.TestCase):
|
||||
restoreBase.RestoreRunner.is_encrypted = True
|
||||
restoreBase.RestoreRunner.decrypt_key = CRYPTO_KEY
|
||||
RunnerClass = utils.import_class(RESTORE_SQLDUMP_CLS)
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql",
|
||||
restr = RunnerClass(None, restore_location="/var/lib/mysql/data",
|
||||
location="filename", checksum="md5")
|
||||
self.assertEqual(DECRYPT + PIPE + UNZIP + PIPE + SQLDUMP_RESTORE,
|
||||
restr.restore_cmd)
|
||||
|
@ -223,8 +223,15 @@ class DbaasTest(testtools.TestCase):
|
||||
def test_load_mysqld_options_error(self, mock_exists):
|
||||
|
||||
dbaas.utils.execute = Mock(side_effect=ProcessExecutionError())
|
||||
|
||||
self.assertFalse(dbaas.load_mysqld_options())
|
||||
|
||||
def test_get_datadir(self):
|
||||
cnf_value = '[mysqld]\ndatadir=/var/lib/mysql/data'
|
||||
with patch.object(dbaas, 'read_mycnf', Mock(return_value=cnf_value)):
|
||||
self.assertEqual('/var/lib/mysql/data',
|
||||
dbaas.get_datadir(reset_cache=True))
|
||||
|
||||
|
||||
class ResultSetStub(object):
|
||||
|
||||
|
@ -25,6 +25,7 @@ from trove.common.exception import InsufficientSpaceForReplica
|
||||
from trove.common.exception import ProcessExecutionError
|
||||
from trove.common import instance as rd_instance
|
||||
from trove.guestagent import backup
|
||||
from trove.guestagent.common import operating_system
|
||||
from trove.guestagent.datastore.mysql.manager import Manager
|
||||
import trove.guestagent.datastore.mysql.service as dbaas
|
||||
from trove.guestagent import dbaas as base_dbaas
|
||||
@ -47,8 +48,10 @@ class GuestAgentManagerTest(testtools.TestCase):
|
||||
self.origin_mount_points = volume.VolumeDevice.mount_points
|
||||
self.origin_stop_mysql = dbaas.MySqlApp.stop_db
|
||||
self.origin_start_mysql = dbaas.MySqlApp.start_mysql
|
||||
self.origin_update_overrides = dbaas.MySqlApp.update_overrides
|
||||
self.origin_pkg_is_installed = pkg.Package.pkg_is_installed
|
||||
self.origin_os_path_exists = os.path.exists
|
||||
self.origin_chown = operating_system.chown
|
||||
# set up common mock objects, etc. for replication testing
|
||||
self.patcher_gfvs = patch(
|
||||
'trove.guestagent.dbaas.get_filesystem_volume_stats')
|
||||
@ -71,6 +74,8 @@ class GuestAgentManagerTest(testtools.TestCase):
|
||||
volume.VolumeDevice.mount_points = self.origin_mount_points
|
||||
dbaas.MySqlApp.stop_db = self.origin_stop_mysql
|
||||
dbaas.MySqlApp.start_mysql = self.origin_start_mysql
|
||||
dbaas.MySqlApp.update_overrides = self.origin_update_overrides
|
||||
operating_system.chown = self.origin_chown
|
||||
pkg.Package.pkg_is_installed = self.origin_pkg_is_installed
|
||||
os.path.exists = self.origin_os_path_exists
|
||||
# teardown the replication mock objects
|
||||
@ -220,6 +225,7 @@ class GuestAgentManagerTest(testtools.TestCase):
|
||||
VolumeDevice.unmount = MagicMock(return_value=None)
|
||||
dbaas.MySqlApp.stop_db = MagicMock(return_value=None)
|
||||
dbaas.MySqlApp.start_mysql = MagicMock(return_value=None)
|
||||
dbaas.MySqlApp.update_overrides = MagicMock(return_value=None)
|
||||
dbaas.MySqlApp.install_if_needed = MagicMock(return_value=None)
|
||||
backup.restore = MagicMock(return_value=None)
|
||||
dbaas.MySqlApp.secure = MagicMock(return_value=None)
|
||||
@ -231,6 +237,7 @@ class GuestAgentManagerTest(testtools.TestCase):
|
||||
dbaas.MySqlAdmin.create_user = MagicMock(return_value=None)
|
||||
dbaas.MySqlAdmin.create_database = MagicMock(return_value=None)
|
||||
dbaas.MySqlAdmin.enable_root = MagicMock(return_value=None)
|
||||
operating_system.chown = MagicMock(return_value=None)
|
||||
os.path.exists = MagicMock(return_value=True)
|
||||
mock_replication = MagicMock()
|
||||
mock_replication.enable_as_slave = MagicMock()
|
||||
@ -263,7 +270,7 @@ class GuestAgentManagerTest(testtools.TestCase):
|
||||
if backup_info:
|
||||
backup.restore.assert_any_call(self.context,
|
||||
backup_info,
|
||||
'/var/lib/mysql')
|
||||
'/var/lib/mysql/data')
|
||||
dbaas.MySqlApp.install_if_needed.assert_any_call(None)
|
||||
# We don't need to make sure the exact contents are there
|
||||
dbaas.MySqlApp.secure.assert_any_call(None, None)
|
||||
|
Loading…
x
Reference in New Issue
Block a user