Fix mariadb CI - trove-scenario-mariadb-single

- Trove supports MariaDB 10.4
- From MariaDB 10.3, Mariabackup is used instead of Percona XtraBackup
  for backup functionality
- Some log improvements

Change-Id: Ibaa6fd7273b98451097b32fb6b881008a236be9f
This commit is contained in:
Lingxian Kong 2019-11-28 10:27:35 +13:00
parent 8fc0b7695d
commit 11b0b8d6f2
20 changed files with 224 additions and 127 deletions

View File

@ -18,13 +18,14 @@ curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup |
wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb
dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb
apt-get update
apt-get install -y -qq apt-transport-https ca-certificates
apt-get update -qq
# Disable password prompt
debconf-set-selections <<< "mariadb-server mysql-server/root_password password ''"
debconf-set-selections <<< "mariadb-server mysql-server/root_password_again password ''"
apt-get install -y --allow-unauthenticated mariadb-server mariadb-client galera-4 libmariadb3 mariadb-backup mariadb-common percona-xtrabackup-24
apt-get install -y -qq --allow-unauthenticated mariadb-server mariadb-client galera-4 libmariadb3 mariadb-backup mariadb-common
cat <<EOF >/etc/mysql/conf.d/no_perf_schema.cnf
[mysqld]

View File

@ -86,7 +86,7 @@ function build_vm() {
popd > /dev/null
sudo rm -rf $TEMP
exclaim "Image ${image_output}.${GUEST_IMAGETYPE} was built successfully."
exclaim "Image ${image_output} was built successfully."
}
function build_guest_image() {

View File

@ -563,7 +563,7 @@ function cmd_set_datastore() {
VERSION="5.6"
elif [ "$DATASTORE_TYPE" == "mariadb" ]; then
PACKAGES=${PACKAGES:-"mariadb-server"}
VERSION="10.1"
VERSION="10.4"
elif [ "$DATASTORE_TYPE" == "mongodb" ]; then
PACKAGES=${PACKAGES:-"mongodb-org"}
VERSION="3.2"

View File

@ -1430,7 +1430,13 @@ mariadb_opts = [
help='List of UDP ports and/or port ranges to open '
'in the security group (only applicable '
'if trove_security_groups_support is True).'),
cfg.StrOpt('backup_strategy', default='MariaDBInnoBackupEx',
cfg.StrOpt('backup_namespace',
default='trove.guestagent.strategies.backup.experimental'
'.mariadb_impl',
help='Namespace to load backup strategies from.',
deprecated_name='backup_namespace',
deprecated_group='DEFAULT'),
cfg.StrOpt('backup_strategy', default='MariaBackup',
help='Default strategy to perform backups.',
deprecated_name='backup_strategy',
deprecated_group='DEFAULT'),
@ -1451,12 +1457,6 @@ mariadb_opts = [
cfg.IntOpt('usage_timeout', default=400,
help='Maximum time (in seconds) to wait for a Guest to become '
'active.'),
cfg.StrOpt('backup_namespace',
default='trove.guestagent.strategies.backup.experimental'
'.mariadb_impl',
help='Namespace to load backup strategies from.',
deprecated_name='backup_namespace',
deprecated_group='DEFAULT'),
cfg.StrOpt('restore_namespace',
default='trove.guestagent.strategies.restore.experimental'
'.mariadb_impl',
@ -1468,8 +1468,8 @@ mariadb_opts = [
cfg.StrOpt('device_path', default='/dev/vdb',
help='Device path for volume if volume support is enabled.'),
cfg.DictOpt('backup_incremental_strategy',
default={'MariaDBInnoBackupEx':
'MariaDBInnoBackupExIncremental'},
default={'MariaBackup':
'MariaBackupIncremental'},
help='Incremental Backup Runner based on the default '
'strategy. For strategies that do not implement an '
'incremental backup, the runner will use the default full '

View File

@ -22,5 +22,4 @@ LOG = logging.getLogger(__name__)
def get_storage_strategy(storage_driver, ns=__name__):
LOG.debug("Getting storage strategy: %s.", storage_driver)
return Strategy.get_strategy(storage_driver, ns)

View File

@ -123,14 +123,13 @@ class BackupAgent(object):
sent=timeutils.utcnow_ts(
microsecond=True),
**backup_state)
LOG.debug("Updated state for %s to %s.",
backup_id, 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.debug("Running backup %(id)s.", backup_info)
LOG.info("Running backup %(id)s.", backup_info)
storage = get_storage_strategy(
CONF.storage_strategy,
CONF.storage_namespace)(context)
@ -154,12 +153,8 @@ class BackupAgent(object):
parent_metadata, extra_opts)
def execute_restore(self, context, backup_info, restore_location):
try:
LOG.debug("Getting Restore Runner %(type)s.", backup_info)
restore_runner = self._get_restore_runner(backup_info['type'])
LOG.debug("Getting Storage Strategy.")
storage = get_storage_strategy(
CONF.storage_strategy,
CONF.storage_namespace)(context)
@ -168,16 +163,15 @@ class BackupAgent(object):
checksum=backup_info['checksum'],
restore_location=restore_location)
backup_info['restore_location'] = restore_location
LOG.debug("Restoring instance from backup %(id)s to "
"%(restore_location)s.", backup_info)
LOG.info("Restoring instance from backup %(id)s to "
"%(restore_location)s", backup_info)
content_size = runner.restore()
LOG.debug("Restore from backup %(id)s completed successfully "
"to %(restore_location)s.", backup_info)
LOG.debug("Restore size: %s.", content_size)
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)
LOG.exception("Error restoring backup %(id)s", backup_info)
raise
else:
LOG.debug("Restored backup %(id)s.", backup_info)
LOG.debug("Restored backup %(id)s", backup_info)

View File

@ -111,8 +111,8 @@ class Manager(periodic_task.PeriodicTasks):
try:
return repl_strategy.get_instance(self.manager)
except Exception as ex:
LOG.debug("Cannot get replication instance for '%(manager)s': "
"%(msg)s", {'manager': self.manager, 'msg': str(ex)})
LOG.warning("Cannot get replication instance for '%(manager)s': "
"%(msg)s", {'manager': self.manager, 'msg': str(ex)})
return None
@ -315,6 +315,7 @@ class Manager(periodic_task.PeriodicTasks):
except Exception as ex:
LOG.exception("An error occurred applying modules: "
"%s", str(ex))
# The following block performs single-instance initialization.
# Failures will be recorded, but won't stop the provisioning
# or change the instance state.

View File

@ -184,7 +184,8 @@ class MySqlManager(manager.Manager):
return self.mysql_admin().disable_root()
def _perform_restore(self, backup_info, context, restore_location, app):
LOG.info("Restoring database from backup %s.", backup_info['id'])
LOG.info("Restoring database from backup %s, backup_info: %s",
backup_info['id'], backup_info)
try:
backup.restore(context, backup_info, restore_location)
except Exception:
@ -202,9 +203,12 @@ class MySqlManager(manager.Manager):
app = self.mysql_app(self.mysql_app_status.get())
app.install_if_needed(packages)
if device_path:
# stop and do not update database
LOG.info('Prepare the storage for %s', device_path)
app.stop_db(
do_not_start_on_reboot=self.volume_do_not_start_on_reboot)
do_not_start_on_reboot=self.volume_do_not_start_on_reboot
)
device = volume.VolumeDevice(device_path)
# unmount if device is already mounted
device.unmount_device(device_path)
@ -219,13 +223,15 @@ class MySqlManager(manager.Manager):
service.MYSQL_OWNER,
recursive=False, as_root=True)
LOG.debug("Mounted the volume at %s.", mount_point)
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
# (see MySqlApp.secure()) and restart.
app.set_data_dir(mount_point + '/data')
app.start_mysql()
LOG.info('Finish to prepare the storage for %s', device_path)
if backup_info:
self._perform_restore(backup_info, context,
mount_point + "/data", app)
@ -337,7 +343,8 @@ class MySqlManager(manager.Manager):
def get_replication_snapshot(self, context, snapshot_info,
replica_source_config=None):
LOG.debug("Getting replication snapshot.")
LOG.info("Getting replication snapshot, snapshot_info: %s",
snapshot_info)
app = self.mysql_app(self.mysql_app_status.get())
self.replication.enable_as_master(app, replica_source_config)

View File

@ -95,20 +95,28 @@ def clear_expired_password():
out, err = utils.execute("cat", secret_file,
run_as_root=True, root_helper="sudo")
except exception.ProcessExecutionError:
LOG.exception("/root/.mysql_secret does not exist.")
return
m = re.match('# The random password set for the root user at .*: (.*)',
out)
if m:
try:
out, err = utils.execute("mysqladmin", "-p%s" % m.group(1),
"password", "", run_as_root=True,
root_helper="sudo")
except exception.ProcessExecutionError:
LOG.exception("Cannot change mysql password.")
return
operating_system.remove(secret_file, force=True, as_root=True)
LOG.debug("Expired password removed.")
LOG.warning("/root/.mysql_secret does not exist.")
else:
m = re.match('# The random password set for the root user at .*: (.*)',
out)
if m:
try:
out, err = utils.execute("mysqladmin", "-p%s" % m.group(1),
"password", "", run_as_root=True,
root_helper="sudo")
except exception.ProcessExecutionError:
LOG.exception("Cannot change mysql password.")
return
operating_system.remove(secret_file, force=True, as_root=True)
LOG.debug("Expired password removed.")
# The root user password will be changed in app.secure_root() later on
LOG.debug('Initializae the root password to empty')
try:
utils.execute("mysqladmin", "--user=root", "password", "",
run_as_root=True, root_helper="sudo")
except Exception:
LOG.exception("Failed to initializae the root password")
def load_mysqld_options():
@ -145,17 +153,19 @@ class BaseMySqlAppStatus(service.BaseDbStatus):
def _get_actual_db_status(self):
try:
out, err = utils.execute_with_timeout(
utils.execute_with_timeout(
"/usr/bin/mysqladmin",
"ping", run_as_root=True, root_helper="sudo",
log_output_on_error=True)
LOG.info("MySQL Service Status is RUNNING.")
LOG.debug("MySQL Service Status is RUNNING.")
return rd_instance.ServiceStatuses.RUNNING
except exception.ProcessExecutionError:
LOG.exception("Failed to get database status.")
LOG.warning("Failed to get database status.")
try:
out, err = utils.execute_with_timeout("/bin/ps", "-C",
"mysqld", "h")
out, _ = utils.execute_with_timeout(
"/bin/ps", "-C", "mysqld", "h",
log_output_on_error=True
)
pid = out.split()[0]
# TODO(rnirmal): Need to create new statuses for instances
# where the mysql service is up, but unresponsive
@ -163,7 +173,7 @@ class BaseMySqlAppStatus(service.BaseDbStatus):
{'pid': pid})
return rd_instance.ServiceStatuses.BLOCKED
except exception.ProcessExecutionError:
LOG.exception("Process execution failed.")
LOG.warning("Process execution failed.")
mysql_args = load_mysqld_options()
pid_file = mysql_args.get('pid_file',
['/var/run/mysqld/mysqld.pid'])[0]
@ -298,6 +308,7 @@ class BaseMySqlAdmin(object):
mydb.character_set,
mydb.collate)
t = text(str(cd))
LOG.debug('Creating database, command: %s', str(cd))
client.execute(t)
def create_user(self, users):
@ -319,6 +330,7 @@ class BaseMySqlAdmin(object):
g = sql_query.Grant(permissions='ALL', database=mydb.name,
user=user.name, host=user.host)
t = text(str(g))
LOG.debug('Creating user, command: %s', str(g))
client.execute(t)
def delete_database(self, database):
@ -569,11 +581,12 @@ class BaseKeepAliveConnection(interfaces.PoolListener):
else:
raise
# MariaDB seems to timeout the client in a different
# way than MySQL and PXC, which manifests itself as
# an invalid packet sequence. Handle it as well.
# way than MySQL and PXC
except pymysql_err.InternalError as ex:
if "Packet sequence number wrong" in str(ex):
raise exc.DisconnectionError()
elif 'Connection was killed' in str(ex):
raise exc.DisconnectionError()
else:
raise
@ -717,6 +730,7 @@ class BaseMySqlApp(object):
def secure(self, config_contents):
LOG.debug("Securing MySQL now.")
clear_expired_password()
LOG.debug("Generating admin password.")
admin_password = utils.generate_random_password()
engine = sqlalchemy.create_engine(

View File

@ -22,5 +22,4 @@ LOG = logging.getLogger(__name__)
def get_backup_strategy(backup_driver, ns=__name__):
LOG.debug("Getting backup strategy: %s.", backup_driver)
return Strategy.get_strategy(backup_driver, ns)

View File

@ -1,28 +1,70 @@
# Copyright 2016 Tesora Inc.
# All Rights Reserved.
# Copyright 2019 Catalyst Cloud Ltd.
#
# 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
# 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
# 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.
# 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.
import re
from trove.guestagent.datastore.experimental.mariadb.service import MariaDBApp
from trove.guestagent.datastore.mysql.service import MySqlAppStatus
from trove.guestagent.strategies.backup import mysql_impl
from oslo_log import log as logging
from trove.guestagent.datastore.mysql import service as mysql_service
from trove.guestagent.datastore.mysql_common import service as common_service
from trove.guestagent.strategies.backup import base
LOG = logging.getLogger(__name__)
BACKUP_LOG = '/tmp/mariabackup.log'
class MariaDBInnoBackupEx(mysql_impl.InnoBackupEx):
class MariaBackup(base.BackupRunner):
"""Implementation of Backup Strategy for mariabackup."""
__strategy_name__ = 'mariabackup'
def _build_app(self):
return MariaDBApp(MySqlAppStatus.get())
@property
def user_and_pass(self):
return (' --user=%(user)s --password=%(password)s --host=127.0.0.1 ' %
{'user': common_service.ADMIN_USER_NAME,
'password': mysql_service.MySqlApp.get_auth_password()})
@property
def cmd(self):
cmd = ('sudo mariabackup --backup --stream=xbstream' +
self.user_and_pass + ' 2>' + BACKUP_LOG)
return cmd + self.zip_cmd + self.encrypt_cmd
def check_process(self):
"""Check the output of mariabackup command for 'completed OK!'.
Return True if no error, otherwise return False.
"""
LOG.debug('Checking mariabackup process output.')
with open(BACKUP_LOG, 'r') as backup_log:
output = backup_log.read()
if not output:
LOG.error("mariabackup log file empty.")
return False
LOG.debug(output)
last_line = output.splitlines()[-1].strip()
if not re.search('completed OK!', last_line):
LOG.error("mariabackup command failed.")
return False
return True
@property
def filename(self):
return '%s.xbstream' % self.base_filename
class MariaDBInnoBackupExIncremental(MariaDBInnoBackupEx):
class MariaBackupIncremental(MariaBackup):
pass

View File

@ -17,16 +17,10 @@
from oslo_log import log as logging
from trove.common import cfg
from trove.guestagent.backup.backupagent import BackupAgent
from trove.guestagent.strategies import backup
from trove.guestagent.strategies.replication import mysql_base
AGENT = BackupAgent()
CONF = cfg.CONF
REPL_BACKUP_NAMESPACE = 'trove.guestagent.strategies.backup' \
'.experimental.mariadb_impl'
LOG = logging.getLogger(__name__)
@ -35,17 +29,27 @@ class MariaDBGTIDReplication(mysql_base.MysqlReplicationBase):
@property
def repl_backup_runner(self):
return backup.get_backup_strategy('MariaDBInnoBackupEx',
REPL_BACKUP_NAMESPACE)
return backup.get_backup_strategy(
CONF.mariadb.backup_strategy,
CONF.mariadb.backup_namespace
)
@property
def repl_incr_backup_runner(self):
return backup.get_backup_strategy('MariaDBInnoBackupExIncremental',
REPL_BACKUP_NAMESPACE)
strategy = CONF.mariadb.backup_incremental_strategy.get(
CONF.mariadb.backup_strategy, CONF.mariadb.backup_strategy
)
return backup.get_backup_strategy(
strategy,
CONF.mariadb.backup_namespace
)
@property
def repl_backup_extra_opts(self):
return CONF.backup_runner_options.get('MariaDBInnoBackupEx', '')
return CONF.backup_runner_options.get(
CONF.mariadb.backup_strategy, ''
)
def connect_to_master(self, service, snapshot):
logging_config = snapshot['log_position']

View File

@ -107,6 +107,7 @@ class MysqlReplicationBase(base.Replication):
incremental_runner=self.repl_incr_backup_runner)
else:
LOG.debug("Using existing backup created for previous replica.")
LOG.debug("Replication snapshot %(snapshot_id)s used for replica "
"number %(replica_number)d.",
{'snapshot_id': snapshot_id,

View File

@ -1,28 +1,59 @@
# Copyright 2016 Tesora Inc.
# All Rights Reserved.
# Copyright 2019 Catalyst Cloud Ltd.
#
# 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
# 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
# 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.
# 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 trove.guestagent.datastore.experimental.mariadb.service import MariaDBApp
from trove.guestagent.datastore.mysql.service import MySqlAppStatus
from oslo_log import log as logging
from trove.guestagent.common import operating_system
from trove.guestagent.datastore.experimental.mariadb import service
from trove.guestagent.datastore.mysql_common import service as mysql_service
from trove.guestagent.strategies.restore import mysql_impl
class MariaDBInnoBackupEx(mysql_impl.InnoBackupEx):
def _build_app(self):
return MariaDBApp(MySqlAppStatus.get())
LOG = logging.getLogger(__name__)
class MariaDBInnoBackupExIncremental(MariaDBInnoBackupEx):
class MariaBackup(mysql_impl.InnoBackupEx):
__strategy_name__ = 'mariabackup'
base_restore_cmd = ('sudo mbstream -x -C %(restore_location)s '
'2>/tmp/xbstream_extract.log')
@property
def app(self):
if self._app is None:
self._app = service.MariaDBApp(
mysql_service.BaseMySqlAppStatus.get()
)
return self._app
def post_restore(self):
operating_system.chown(self.restore_location, 'mysql', None,
force=True, as_root=True)
# When using Mariabackup from versions prior to MariaDB 10.2.10, you
# would also have to remove any pre-existing InnoDB redo log files.
self._delete_old_binlogs()
self.app.start_mysql()
LOG.debug("Finished post restore.")
def check_process(self):
LOG.debug('Checking return code of mbstream restore process.')
return_code = self.process.wait()
if return_code != 0:
LOG.error('mbstream exited with %s', return_code)
return False
return True
class MariaBackupIncremental(MariaBackup):
pass

View File

@ -57,7 +57,7 @@ class MySQLRestoreMixin(object):
def mysql_is_not_running(self):
try:
utils.execute_with_timeout("/usr/bin/pgrep", "mysqld")
LOG.info("MySQL is still running.")
LOG.debug("MySQL is still running.")
return False
except exception.ProcessExecutionError:
LOG.debug("MySQL is not running.")
@ -218,7 +218,7 @@ class InnoBackupEx(base.RestoreRunner, MySQLRestoreMixin):
utils.clean_out(self.restore_location)
def _run_prepare(self):
LOG.debug("Running innobackupex prepare: %s.", self.prepare_cmd)
LOG.info("Running innobackupex prepare: %s.", self.prepare_cmd)
self.prep_retcode = utils.execute(self.prepare_cmd, shell=True)
LOG.info("Innobackupex prepare finished successfully.")
@ -247,7 +247,7 @@ class InnoBackupEx(base.RestoreRunner, MySQLRestoreMixin):
LOG.debug('Checking return code of xbstream restore process.')
return_code = self.process.wait()
if return_code != 0:
LOG.erro('xbstream exited with %s', return_code)
LOG.error('xbstream exited with %s', return_code)
return False
LOG.debug('Checking xbstream restore process stderr output.')

View File

@ -330,8 +330,8 @@ class Manager(periodic_task.PeriodicTasks):
master_instance_tasks = BuiltInstanceTasks.load(context, slave_of_id)
server_group = master_instance_tasks.server_group
scheduler_hints = srv_grp.ServerGroup.convert_to_hint(server_group)
LOG.info("Using scheduler hints %s for creating instance %s",
scheduler_hints, instance_id)
LOG.debug("Using scheduler hints %s for creating instance %s",
scheduler_hints, instance_id)
try:
for replica_index in range(0, len(ids)):
@ -344,14 +344,17 @@ class Manager(periodic_task.PeriodicTasks):
snapshot = instance_tasks.get_replication_master_snapshot(
context, slave_of_id, flavor, replica_backup_id,
replica_number=replica_number)
replica_backup_id = snapshot['dataset']['snapshot_id']
replica_backup_created = (replica_backup_id is not None)
instance_tasks.create_instance(
flavor, image_id, databases, users, datastore_manager,
packages, volume_size, replica_backup_id,
availability_zone, root_passwords[replica_index],
nics, overrides, None, snapshot, volume_type,
modules, scheduler_hints)
replicas.append(instance_tasks)
except Exception:
# if it's the first replica, then we shouldn't continue
@ -390,8 +393,8 @@ class Manager(periodic_task.PeriodicTasks):
scheduler_hints = srv_grp.ServerGroup.build_scheduler_hint(
context, locality, instance_id
)
LOG.info("Using scheduler hints %s for creating instance %s",
scheduler_hints, instance_id)
LOG.debug("Using scheduler hints %s for creating instance %s",
scheduler_hints, instance_id)
instance_tasks = FreshInstanceTasks.load(context, instance_id)
instance_tasks.create_instance(

View File

@ -424,7 +424,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
error_message = ''
error_details = ''
try:
LOG.info("Waiting for instance %s up and running", self.id)
LOG.info("Waiting for instance %s up and running with "
"timeout %ss", self.id, timeout)
utils.poll_until(self._service_is_active,
sleep_time=CONF.usage_sleep_time,
time_out=timeout)
@ -621,7 +622,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
if backup:
backup_id = backup.id
else:
LOG.debug('Skipping replication backup, as none is required.')
LOG.debug('Will skip replication master backup')
snapshot_info = {
'name': "Replication snapshot for %s" % self.id,
'description': "Backup image used to initialize "
@ -1091,7 +1093,7 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
self.guest.create_backup(backup_info)
def backup_required_for_replication(self):
LOG.debug("Seeing if replication backup is required for instance %s.",
LOG.debug("Check if replication backup is required for instance %s.",
self.id)
return self.guest.backup_required_for_replication()
@ -1103,7 +1105,9 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
rep_source_config = self._render_replica_source_config(flavor)
result = self.guest.get_replication_snapshot(
snapshot_info, rep_source_config.config_contents)
LOG.debug("Got replication snapshot from guest successfully.")
LOG.info("Finnished getting replication snapshot for "
"instance %s", self.id)
return result
except Exception:
LOG.exception("Failed to get replication snapshot from %s.",

View File

@ -6,8 +6,6 @@ socket = /var/run/mysqld/mysqld.sock
nice = 0
[mysqld]
ignore_builtin_innodb
plugin_load=innodb=ha_innodb.so
user = mysql
port = 3306
basedir = /usr

View File

@ -318,7 +318,8 @@ register(
register(
["mariadb_supported"],
single=[common_groups,
backup_incremental_groups,
backup_groups,
# backup_incremental_groups,
configuration_groups,
database_actions_groups,
root_actions_groups,

View File

@ -158,7 +158,7 @@ class DbaasTest(trove_testtools.TestCase):
with patch.object(mysql_common_service.utils, 'execute',
return_value=(secret_content, None)):
mysql_common_service.clear_expired_password()
self.assertEqual(2, mysql_common_service.utils.execute.call_count)
self.assertEqual(3, mysql_common_service.utils.execute.call_count)
self.assertEqual(1, mock_remove.call_count)
@patch.object(operating_system, 'remove')
@ -166,7 +166,7 @@ class DbaasTest(trove_testtools.TestCase):
with patch.object(mysql_common_service.utils, 'execute',
return_value=('', None)):
mysql_common_service.clear_expired_password()
self.assertEqual(1, mysql_common_service.utils.execute.call_count)
self.assertEqual(2, mysql_common_service.utils.execute.call_count)
mock_remove.assert_not_called()
@patch.object(operating_system, 'remove')
@ -184,16 +184,14 @@ class DbaasTest(trove_testtools.TestCase):
self.assertEqual(2, mysql_common_service.utils.execute.call_count)
mock_remove.assert_not_called()
@patch('trove.guestagent.datastore.mysql_common.service.LOG')
@patch.object(operating_system, 'remove')
@patch.object(mysql_common_service.utils, 'execute',
side_effect=ProcessExecutionError)
side_effect=[ProcessExecutionError, (None, None)])
def test_fail_retrieve_secret_content_clear_expired_password(self,
mock_execute,
mock_remove,
mock_logging):
mock_remove):
mysql_common_service.clear_expired_password()
self.assertEqual(1, mock_execute.call_count)
self.assertEqual(2, mock_execute.call_count)
mock_remove.assert_not_called()
@patch.object(operating_system, 'read_file',