diff --git a/trove/common/cfg.py b/trove/common/cfg.py index 5d2d1231c6..de748a8dfa 100644 --- a/trove/common/cfg.py +++ b/trove/common/cfg.py @@ -1292,7 +1292,7 @@ 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='InnoBackupEx', + cfg.StrOpt('backup_strategy', default='MariaDBInnoBackupEx', help='Default strategy to perform backups.', deprecated_name='backup_strategy', deprecated_group='DEFAULT'), @@ -1314,12 +1314,14 @@ mariadb_opts = [ help='Maximum time (in seconds) to wait for a Guest to become ' 'active.'), cfg.StrOpt('backup_namespace', - default='trove.guestagent.strategies.backup.mysql_impl', + 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.mysql_impl', + default='trove.guestagent.strategies.restore.experimental' + '.mariadb_impl', help='Namespace to load restore strategies from.', deprecated_name='restore_namespace', deprecated_group='DEFAULT'), @@ -1328,7 +1330,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={'InnoBackupEx': 'InnoBackupExIncremental'}, + default={'MariaDBInnoBackupEx': + 'MariaDBInnoBackupExIncremental'}, 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 ' diff --git a/trove/guestagent/datastore/experimental/mariadb/service.py b/trove/guestagent/datastore/experimental/mariadb/service.py index e7dddb38c5..8695bb5703 100644 --- a/trove/guestagent/datastore/experimental/mariadb/service.py +++ b/trove/guestagent/datastore/experimental/mariadb/service.py @@ -16,6 +16,7 @@ from oslo_log import log as logging +from trove.guestagent.common import operating_system from trove.guestagent.datastore.galera_common import service as galera_service from trove.guestagent.datastore.mysql_common import service as mysql_service @@ -24,11 +25,22 @@ LOG = logging.getLogger(__name__) class MariaDBApp(galera_service.GaleraApp): + OS = operating_system.get_os() + def __init__(self, status): super(MariaDBApp, self).__init__( status, mysql_service.BaseLocalSqlClient, mysql_service.BaseKeepAliveConnection) + @property + def service_candidates(self): + service_candidates = super(MariaDBApp, self).service_candidates + return { + operating_system.DEBIAN: service_candidates, + operating_system.REDHAT: ["mariadb"], + operating_system.SUSE: service_candidates + }[self.OS] + @property def mysql_service(self): result = super(MariaDBApp, self).mysql_service diff --git a/trove/guestagent/datastore/mysql_common/service.py b/trove/guestagent/datastore/mysql_common/service.py index e779f8228a..40e06867a3 100644 --- a/trove/guestagent/datastore/mysql_common/service.py +++ b/trove/guestagent/datastore/mysql_common/service.py @@ -591,10 +591,14 @@ class BaseMySqlApp(object): def keep_alive_connection_cls(self): return self._keep_alive_connection_cls + @property + def service_candidates(self): + return ["mysql", "mysqld", "mysql-server"] + @property def mysql_service(self): - MYSQL_SERVICE_CANDIDATES = ["mysql", "mysqld", "mysql-server"] - return operating_system.service_discovery(MYSQL_SERVICE_CANDIDATES) + service_candidates = self.service_candidates + return operating_system.service_discovery(service_candidates) configuration_manager = ConfigurationManager( MYSQL_CONFIG, MYSQL_OWNER, MYSQL_OWNER, CFG_CODEC, requires_root=True, diff --git a/trove/guestagent/strategies/backup/experimental/mariadb_impl.py b/trove/guestagent/strategies/backup/experimental/mariadb_impl.py new file mode 100644 index 0000000000..4a8a33c3ca --- /dev/null +++ b/trove/guestagent/strategies/backup/experimental/mariadb_impl.py @@ -0,0 +1,28 @@ +# Copyright 2016 Tesora Inc. +# 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 trove.guestagent.datastore.experimental.mariadb.service import MariaDBApp +from trove.guestagent.datastore.mysql.service import MySqlAppStatus +from trove.guestagent.strategies.backup import mysql_impl + + +class MariaDBInnoBackupEx(mysql_impl.InnoBackupEx): + + def _build_app(self): + return MariaDBApp(MySqlAppStatus.get()) + + +class MariaDBInnoBackupExIncremental(MariaDBInnoBackupEx): + pass diff --git a/trove/guestagent/strategies/replication/base.py b/trove/guestagent/strategies/replication/base.py index c6ec9365c0..692c6b89c9 100644 --- a/trove/guestagent/strategies/replication/base.py +++ b/trove/guestagent/strategies/replication/base.py @@ -62,3 +62,18 @@ class Replication(Strategy): @abc.abstractmethod def demote_master(self, service): """Turn off replication on a master site.""" + + @property + def repl_backup_runner(self): + """Backup runner to be used to snapshot for replication""" + return None + + @property + def repl_incr_backup_runner(self): + """Incremental backup runner to be used to snapshot for replication""" + return None + + @property + def repl_backup_extra_opts(self): + """Extra options to be passed to the backup agent""" + return None diff --git a/trove/guestagent/strategies/replication/experimental/mariadb_gtid.py b/trove/guestagent/strategies/replication/experimental/mariadb_gtid.py index ffdf25d216..89446ed529 100644 --- a/trove/guestagent/strategies/replication/experimental/mariadb_gtid.py +++ b/trove/guestagent/strategies/replication/experimental/mariadb_gtid.py @@ -18,17 +18,35 @@ 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__) class MariaDBGTIDReplication(mysql_base.MysqlReplicationBase): """MariaDB Replication coordinated by GTIDs.""" + @property + def repl_backup_runner(self): + return backup.get_backup_strategy('MariaDBInnoBackupEx', + REPL_BACKUP_NAMESPACE) + + @property + def repl_incr_backup_runner(self): + return backup.get_backup_strategy('MariaDBInnoBackupExIncremental', + REPL_BACKUP_NAMESPACE) + + @property + def repl_backup_extra_opts(self): + return CONF.backup_runner_options.get('MariaDBInnoBackupEx', '') + def connect_to_master(self, service, snapshot): logging_config = snapshot['log_position'] LOG.debug("connect_to_master %s" % logging_config['replication_user']) diff --git a/trove/guestagent/strategies/replication/mysql_base.py b/trove/guestagent/strategies/replication/mysql_base.py index 26abcf405d..6a28b3b0b7 100644 --- a/trove/guestagent/strategies/replication/mysql_base.py +++ b/trove/guestagent/strategies/replication/mysql_base.py @@ -33,13 +33,6 @@ AGENT = BackupAgent() CONF = cfg.CONF REPL_BACKUP_NAMESPACE = 'trove.guestagent.strategies.backup.mysql_impl' -REPL_BACKUP_STRATEGY = 'InnoBackupEx' -REPL_BACKUP_INCREMENTAL_STRATEGY = 'InnoBackupExIncremental' -REPL_BACKUP_RUNNER = backup.get_backup_strategy( - REPL_BACKUP_STRATEGY, REPL_BACKUP_NAMESPACE) -REPL_BACKUP_INCREMENTAL_RUNNER = backup.get_backup_strategy( - REPL_BACKUP_INCREMENTAL_STRATEGY, REPL_BACKUP_NAMESPACE) -REPL_EXTRA_OPTS = CONF.backup_runner_options.get(REPL_BACKUP_STRATEGY, '') LOG = logging.getLogger(__name__) @@ -47,6 +40,20 @@ LOG = logging.getLogger(__name__) class MysqlReplicationBase(base.Replication): """Base class for MySql Replication strategies.""" + @property + def repl_backup_runner(self): + return backup.get_backup_strategy('InnoBackupEx', + REPL_BACKUP_NAMESPACE) + + @property + def repl_incr_backup_runner(self): + return backup.get_backup_strategy('InnoBackupExIncremental', + REPL_BACKUP_NAMESPACE) + + @property + def repl_backup_extra_opts(self): + return CONF.backup_runner_options.get('InnoBackupEx', '') + def get_master_ref(self, service, snapshot_info): master_ref = { 'host': netutils.get_my_ipv4(), @@ -81,13 +88,6 @@ class MysqlReplicationBase(base.Replication): return replication_user - def backup_runner_for_replication(self): - return { - 'runner': REPL_BACKUP_RUNNER, - 'extra_opts': REPL_EXTRA_OPTS, - 'incremental_runner': REPL_BACKUP_INCREMENTAL_RUNNER - } - def snapshot_for_replication(self, context, service, location, snapshot_info): snapshot_id = snapshot_info['id'] @@ -97,8 +97,9 @@ class MysqlReplicationBase(base.Replication): # Only create a backup if it's the first replica if replica_number == 1: AGENT.execute_backup( - context, snapshot_info, - **self.backup_runner_for_replication()) + context, snapshot_info, runner=self.repl_backup_runner, + extra_opts=self.repl_backup_extra_opts, + incremental_runner=self.repl_incr_backup_runner) else: LOG.debug("Using existing backup created for previous replica.") LOG.debug("Replication snapshot %s used for replica number %d." diff --git a/trove/guestagent/strategies/restore/experimental/mariadb_impl.py b/trove/guestagent/strategies/restore/experimental/mariadb_impl.py new file mode 100644 index 0000000000..9060d140ea --- /dev/null +++ b/trove/guestagent/strategies/restore/experimental/mariadb_impl.py @@ -0,0 +1,28 @@ +# Copyright 2016 Tesora Inc. +# 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 trove.guestagent.datastore.experimental.mariadb.service import MariaDBApp +from trove.guestagent.datastore.mysql.service import MySqlAppStatus +from trove.guestagent.strategies.restore import mysql_impl + + +class MariaDBInnoBackupEx(mysql_impl.InnoBackupEx): + + def _build_app(self): + return MariaDBApp(MySqlAppStatus.get()) + + +class MariaDBInnoBackupExIncremental(MariaDBInnoBackupEx): + pass diff --git a/trove/guestagent/strategies/restore/mysql_impl.py b/trove/guestagent/strategies/restore/mysql_impl.py index e21e04b385..bde7cf7ec1 100644 --- a/trove/guestagent/strategies/restore/mysql_impl.py +++ b/trove/guestagent/strategies/restore/mysql_impl.py @@ -205,13 +205,22 @@ class InnoBackupEx(base.RestoreRunner, MySQLRestoreMixin): ' 2>/tmp/innoprepare.log') def __init__(self, *args, **kwargs): + self._app = None super(InnoBackupEx, self).__init__(*args, **kwargs) self.prepare_cmd = self.base_prepare_cmd % kwargs self.prep_retcode = None + @property + def app(self): + if self._app is None: + self._app = self._build_app() + return self._app + + def _build_app(self): + return dbaas.MySqlApp(dbaas.MySqlAppStatus.get()) + def pre_restore(self): - app = dbaas.MySqlApp(dbaas.MySqlAppStatus.get()) - app.stop_db() + self.app.stop_db() LOG.info(_("Cleaning out restore location: %s."), self.restore_location) operating_system.chmod(self.restore_location, FileMode.SET_FULL, @@ -229,8 +238,7 @@ class InnoBackupEx(base.RestoreRunner, MySQLRestoreMixin): force=True, as_root=True) self._delete_old_binlogs() self.reset_root_password() - app = dbaas.MySqlApp(dbaas.MySqlAppStatus.get()) - app.start_mysql() + self.app.start_mysql() def _delete_old_binlogs(self): files = glob.glob(os.path.join(self.restore_location, "ib_logfile*"))