From ad42de03d3c3438fe00971d168405075cf1d6b14 Mon Sep 17 00:00:00 2001 From: Doug Shelley Date: Fri, 16 Jan 2015 16:22:37 +0000 Subject: [PATCH] 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 Closes-bug: 1370646 --- ...t-instance-configuration-response-json.txt | 2 +- ...fault-instance-configuration-response.json | 2 +- trove/guestagent/datastore/mysql/manager.py | 18 +++++++--- trove/guestagent/datastore/mysql/service.py | 31 +++++++++++++---- .../strategies/backup/mysql_impl.py | 10 +++--- .../strategies/replication/mysql_binlog.py | 3 +- trove/guestagent/strategies/restore/base.py | 3 +- .../strategies/restore/mysql_impl.py | 2 +- trove/guestagent/volume.py | 9 +++-- .../mysql/5.5/replica.config.template | 4 +-- .../mysql/5.5/replica_source.config.template | 2 +- trove/templates/mysql/config.template | 2 +- trove/templates/mysql/replica.config.template | 4 +-- .../mysql/replica_source.config.template | 2 +- .../percona/5.5/replica.config.template | 4 +-- .../5.5/replica_source.config.template | 2 +- trove/templates/percona/config.template | 2 +- .../templates/percona/replica.config.template | 4 +-- .../percona/replica_source.config.template | 2 +- trove/tests/api/instances_actions.py | 4 +-- .../unittests/backup/test_backupagent.py | 13 ++++--- .../unittests/guestagent/test_backups.py | 34 +++++++++++-------- .../tests/unittests/guestagent/test_dbaas.py | 7 ++++ .../guestagent/test_mysql_manager.py | 9 ++++- 24 files changed, 116 insertions(+), 59 deletions(-) diff --git a/apidocs/src/samples/db-get-default-instance-configuration-response-json.txt b/apidocs/src/samples/db-get-default-instance-configuration-response-json.txt index b59d8be511..6eb4f3ca8c 100644 --- a/apidocs/src/samples/db-get-default-instance-configuration-response-json.txt +++ b/apidocs/src/samples/db-get-default-instance-configuration-response-json.txt @@ -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 diff --git a/apidocs/src/samples/db-get-default-instance-configuration-response.json b/apidocs/src/samples/db-get-default-instance-configuration-response.json index c200ff84b2..eeb55b6ee0 100644 --- a/apidocs/src/samples/db-get-default-instance-configuration-response.json +++ b/apidocs/src/samples/db-get-default-instance-configuration-response.json @@ -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", diff --git a/trove/guestagent/datastore/mysql/manager.py b/trove/guestagent/datastore/mysql/manager.py index 490baff873..cfe4b4a1e7 100644 --- a/trove/guestagent/datastore/mysql/manager.py +++ b/trove/guestagent/datastore/mysql/manager.py @@ -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 diff --git a/trove/guestagent/datastore/mysql/service.py b/trove/guestagent/datastore/mysql/service.py index 4dbc007ebc..1dce3aba79 100644 --- a/trove/guestagent/datastore/mysql/service.py +++ b/trove/guestagent/datastore/mysql/service.py @@ -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.") diff --git a/trove/guestagent/strategies/backup/mysql_impl.py b/trove/guestagent/strategies/backup/mysql_impl.py index d434b3b954..013a2b7c1d 100644 --- a/trove/guestagent/strategies/backup/mysql_impl.py +++ b/trove/guestagent/strategies/backup/mysql_impl.py @@ -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 diff --git a/trove/guestagent/strategies/replication/mysql_binlog.py b/trove/guestagent/strategies/replication/mysql_binlog.py index a5018d73c7..fb66eb709d 100644 --- a/trove/guestagent/strategies/replication/mysql_binlog.py +++ b/trove/guestagent/strategies/replication/mysql_binlog.py @@ -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) diff --git a/trove/guestagent/strategies/restore/base.py b/trove/guestagent/strategies/restore/base.py index e2e305a3d7..a30e2b87a0 100644 --- a/trove/guestagent/strategies/restore/base.py +++ b/trove/guestagent/strategies/restore/base.py @@ -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)) diff --git a/trove/guestagent/strategies/restore/mysql_impl.py b/trove/guestagent/strategies/restore/mysql_impl.py index 3bc00add68..51a93a8998 100644 --- a/trove/guestagent/strategies/restore/mysql_impl.py +++ b/trove/guestagent/strategies/restore/mysql_impl.py @@ -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/ + # sufficiently unique /var/lib/mysql/data/ 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) diff --git a/trove/guestagent/volume.py b/trove/guestagent/volume.py index 94a6dcae3d..a48e132901 100644 --- a/trove/guestagent/volume.py +++ b/trove/guestagent/volume.py @@ -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): diff --git a/trove/templates/mysql/5.5/replica.config.template b/trove/templates/mysql/5.5/replica.config.template index a148d79773..ec94b3eb09 100644 --- a/trove/templates/mysql/5.5/replica.config.template +++ b/trove/templates/mysql/5.5/replica.config.template @@ -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 diff --git a/trove/templates/mysql/5.5/replica_source.config.template b/trove/templates/mysql/5.5/replica_source.config.template index 7aa39885ce..c971d03be5 100644 --- a/trove/templates/mysql/5.5/replica_source.config.template +++ b/trove/templates/mysql/5.5/replica_source.config.template @@ -1,2 +1,2 @@ [mysqld] -log_bin = /var/lib/mysql/mysql-bin.log +log_bin = /var/lib/mysql/data/mysql-bin.log diff --git a/trove/templates/mysql/config.template b/trove/templates/mysql/config.template index 912d381f82..ee70128ace 100644 --- a/trove/templates/mysql/config.template +++ b/trove/templates/mysql/config.template @@ -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 diff --git a/trove/templates/mysql/replica.config.template b/trove/templates/mysql/replica.config.template index a845e42d57..fe8871c0ef 100644 --- a/trove/templates/mysql/replica.config.template +++ b/trove/templates/mysql/replica.config.template @@ -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 diff --git a/trove/templates/mysql/replica_source.config.template b/trove/templates/mysql/replica_source.config.template index 9c4409e45f..dcbb046580 100644 --- a/trove/templates/mysql/replica_source.config.template +++ b/trove/templates/mysql/replica_source.config.template @@ -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 diff --git a/trove/templates/percona/5.5/replica.config.template b/trove/templates/percona/5.5/replica.config.template index a148d79773..ec94b3eb09 100644 --- a/trove/templates/percona/5.5/replica.config.template +++ b/trove/templates/percona/5.5/replica.config.template @@ -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 diff --git a/trove/templates/percona/5.5/replica_source.config.template b/trove/templates/percona/5.5/replica_source.config.template index 7aa39885ce..c971d03be5 100644 --- a/trove/templates/percona/5.5/replica_source.config.template +++ b/trove/templates/percona/5.5/replica_source.config.template @@ -1,2 +1,2 @@ [mysqld] -log_bin = /var/lib/mysql/mysql-bin.log +log_bin = /var/lib/mysql/data/mysql-bin.log diff --git a/trove/templates/percona/config.template b/trove/templates/percona/config.template index 912d381f82..ee70128ace 100644 --- a/trove/templates/percona/config.template +++ b/trove/templates/percona/config.template @@ -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 diff --git a/trove/templates/percona/replica.config.template b/trove/templates/percona/replica.config.template index 5d32cc54e0..0326a62ef7 100644 --- a/trove/templates/percona/replica.config.template +++ b/trove/templates/percona/replica.config.template @@ -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 diff --git a/trove/templates/percona/replica_source.config.template b/trove/templates/percona/replica_source.config.template index cf75d2655d..92e55673aa 100644 --- a/trove/templates/percona/replica_source.config.template +++ b/trove/templates/percona/replica_source.config.template @@ -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 diff --git a/trove/tests/api/instances_actions.py b/trove/tests/api/instances_actions.py index f27ae099cf..7b21afa222 100644 --- a/trove/tests/api/instances_actions.py +++ b/trove/tests/api/instances_actions.py @@ -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) diff --git a/trove/tests/unittests/backup/test_backupagent.py b/trove/tests/unittests/backup/test_backupagent.py index 8220cc5f0c..6d90bf5066 100644 --- a/trove/tests/unittests/backup/test_backupagent.py +++ b/trove/tests/unittests/backup/test_backupagent.py @@ -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, diff --git a/trove/tests/unittests/guestagent/test_backups.py b/trove/tests/unittests/guestagent/test_backups.py index e3a493e529..c2fc77c824 100644 --- a/trove/tests/unittests/guestagent/test_backups.py +++ b/trove/tests/unittests/guestagent/test_backups.py @@ -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) diff --git a/trove/tests/unittests/guestagent/test_dbaas.py b/trove/tests/unittests/guestagent/test_dbaas.py index 3dc41144d0..45e3e9470e 100644 --- a/trove/tests/unittests/guestagent/test_dbaas.py +++ b/trove/tests/unittests/guestagent/test_dbaas.py @@ -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): diff --git a/trove/tests/unittests/guestagent/test_mysql_manager.py b/trove/tests/unittests/guestagent/test_mysql_manager.py index d21bf3160c..18569b9a47 100644 --- a/trove/tests/unittests/guestagent/test_mysql_manager.py +++ b/trove/tests/unittests/guestagent/test_mysql_manager.py @@ -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)