Merge "MySQL Manager Refactor"

This commit is contained in:
Jenkins
2015-08-30 01:32:34 +00:00
committed by Gerrit Code Review
14 changed files with 1832 additions and 1411 deletions

View File

@@ -0,0 +1,47 @@
# Copyright 2015 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 oslo_utils import importutils
from trove.common import cfg
from trove.guestagent.datastore.mysql import manager_base
from trove.guestagent.strategies.replication import get_replication_strategy
CONF = cfg.CONF
MANAGER = CONF.datastore_manager if CONF.datastore_manager else 'mysql'
REPLICATION_STRATEGY = CONF.get(MANAGER).replication_strategy
REPLICATION_NAMESPACE = CONF.get(MANAGER).replication_namespace
REPLICATION_STRATEGY_CLASS = get_replication_strategy(REPLICATION_STRATEGY,
REPLICATION_NAMESPACE)
MYSQL_APP = "trove.guestagent.datastore.experimental.mariadb." \
"service.MySqlApp"
MYSQL_APP_STATUS = "trove.guestagent.datastore.experimental.mariadb." \
"service.MySqlAppStatus"
MYSQL_ADMIN = "trove.guestagent.datastore.experimental.mariadb.service." \
"MySqlAdmin"
class Manager(manager_base.BaseMySqlManager):
def __init__(self):
mysql_app = importutils.import_class(MYSQL_APP)
mysql_app_status = importutils.import_class(MYSQL_APP_STATUS)
mysql_admin = importutils.import_class(MYSQL_ADMIN)
super(Manager, self).__init__(mysql_app, mysql_app_status,
mysql_admin, REPLICATION_STRATEGY,
REPLICATION_NAMESPACE,
REPLICATION_STRATEGY_CLASS, MANAGER)

View File

@@ -0,0 +1,50 @@
# Copyright 2015 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 oslo_log import log as logging
from trove.guestagent.datastore.mysql import service_base
LOG = logging.getLogger(__name__)
class KeepAliveConnection(service_base.BaseKeepAliveConnection):
pass
class MySqlAppStatus(service_base.BaseMySqlAppStatus):
pass
class LocalSqlClient(service_base.BaseLocalSqlClient):
pass
class MySqlApp(service_base.BaseMySqlApp):
def __init__(self, status):
super(MySqlApp, self).__init__(status, LocalSqlClient,
KeepAliveConnection)
class MySqlRootAccess(service_base.BaseMySqlRootAccess):
def __init__(self):
super(MySqlRootAccess, self).__init__(LocalSqlClient,
MySqlApp(MySqlAppStatus.get()))
class MySqlAdmin(service_base.BaseMySqlAdmin):
def __init__(self):
super(MySqlAdmin, self).__init__(LocalSqlClient, MySqlRootAccess(),
MySqlApp)

View File

@@ -0,0 +1,47 @@
# Copyright 2015 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 oslo_utils import importutils
from trove.common import cfg
from trove.guestagent.datastore.mysql import manager_base
from trove.guestagent.strategies.replication import get_replication_strategy
CONF = cfg.CONF
MANAGER = CONF.datastore_manager if CONF.datastore_manager else 'mysql'
REPLICATION_STRATEGY = CONF.get(MANAGER).replication_strategy
REPLICATION_NAMESPACE = CONF.get(MANAGER).replication_namespace
REPLICATION_STRATEGY_CLASS = get_replication_strategy(REPLICATION_STRATEGY,
REPLICATION_NAMESPACE)
MYSQL_APP = "trove.guestagent.datastore.experimental.percona." \
"service.MySqlApp"
MYSQL_APP_STATUS = "trove.guestagent.datastore.experimental.percona." \
"service.MySqlAppStatus"
MYSQL_ADMIN = "trove.guestagent.datastore.experimental.percona." \
"service.MySqlAdmin"
class Manager(manager_base.BaseMySqlManager):
def __init__(self):
mysql_app = importutils.import_class(MYSQL_APP)
mysql_app_status = importutils.import_class(MYSQL_APP_STATUS)
mysql_admin = importutils.import_class(MYSQL_ADMIN)
super(Manager, self).__init__(mysql_app, mysql_app_status,
mysql_admin, REPLICATION_STRATEGY,
REPLICATION_NAMESPACE,
REPLICATION_STRATEGY_CLASS, MANAGER)

View File

@@ -0,0 +1,50 @@
# Copyright 2015 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 oslo_log import log as logging
from trove.guestagent.datastore.mysql import service_base
LOG = logging.getLogger(__name__)
class KeepAliveConnection(service_base.BaseKeepAliveConnection):
pass
class MySqlAppStatus(service_base.BaseMySqlAppStatus):
pass
class LocalSqlClient(service_base.BaseLocalSqlClient):
pass
class MySqlApp(service_base.BaseMySqlApp):
def __init__(self, status):
super(MySqlApp, self).__init__(status, LocalSqlClient,
KeepAliveConnection)
class MySqlRootAccess(service_base.BaseMySqlRootAccess):
def __init__(self):
super(MySqlRootAccess, self).__init__(LocalSqlClient,
MySqlApp(MySqlAppStatus.get()))
class MySqlAdmin(service_base.BaseMySqlAdmin):
def __init__(self):
super(MySqlAdmin, self).__init__(LocalSqlClient, MySqlRootAccess(),
MySqlApp)

View File

@@ -16,27 +16,11 @@
# under the License. # under the License.
# #
import os from oslo_utils import importutils
from oslo_log import log as logging
from oslo_service import periodic_task
from trove.common import cfg from trove.common import cfg
from trove.common import exception from trove.guestagent.datastore.mysql import manager_base
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 import service
from trove.guestagent.datastore.mysql.service import MySqlAdmin
from trove.guestagent.datastore.mysql.service import MySqlApp
from trove.guestagent.datastore.mysql.service import MySqlAppStatus
from trove.guestagent import dbaas
from trove.guestagent.strategies.replication import get_replication_strategy from trove.guestagent.strategies.replication import get_replication_strategy
from trove.guestagent import volume
LOG = logging.getLogger(__name__)
CONF = cfg.CONF CONF = cfg.CONF
MANAGER = CONF.datastore_manager if CONF.datastore_manager else 'mysql' MANAGER = CONF.datastore_manager if CONF.datastore_manager else 'mysql'
REPLICATION_STRATEGY = CONF.get(MANAGER).replication_strategy REPLICATION_STRATEGY = CONF.get(MANAGER).replication_strategy
@@ -44,309 +28,19 @@ REPLICATION_NAMESPACE = CONF.get(MANAGER).replication_namespace
REPLICATION_STRATEGY_CLASS = get_replication_strategy(REPLICATION_STRATEGY, REPLICATION_STRATEGY_CLASS = get_replication_strategy(REPLICATION_STRATEGY,
REPLICATION_NAMESPACE) REPLICATION_NAMESPACE)
MYSQL_APP = "trove.guestagent.datastore.mysql.service.MySqlApp"
MYSQL_APP_STATUS = "trove.guestagent.datastore.mysql.service.MySqlAppStatus"
MYSQL_ADMIN = "trove.guestagent.datastore.mysql.service.MySqlAdmin"
class Manager(periodic_task.PeriodicTasks):
class Manager(manager_base.BaseMySqlManager):
def __init__(self): def __init__(self):
super(Manager, self).__init__(CONF) mysql_app = importutils.import_class(MYSQL_APP)
mysql_app_status = importutils.import_class(MYSQL_APP_STATUS)
mysql_admin = importutils.import_class(MYSQL_ADMIN)
@periodic_task.periodic_task super(Manager, self).__init__(mysql_app, mysql_app_status,
def update_status(self, context): mysql_admin, REPLICATION_STRATEGY,
"""Update the status of the MySQL service.""" REPLICATION_NAMESPACE,
MySqlAppStatus.get().update() REPLICATION_STRATEGY_CLASS, MANAGER)
def rpc_ping(self, context):
LOG.debug("Responding to RPC ping.")
return True
def change_passwords(self, context, users):
return MySqlAdmin().change_passwords(users)
def update_attributes(self, context, username, hostname, user_attrs):
return MySqlAdmin().update_attributes(username, hostname, user_attrs)
def reset_configuration(self, context, configuration):
app = MySqlApp(MySqlAppStatus.get())
app.reset_configuration(configuration)
def create_database(self, context, databases):
return MySqlAdmin().create_database(databases)
def create_user(self, context, users):
MySqlAdmin().create_user(users)
def delete_database(self, context, database):
return MySqlAdmin().delete_database(database)
def delete_user(self, context, user):
MySqlAdmin().delete_user(user)
def get_user(self, context, username, hostname):
return MySqlAdmin().get_user(username, hostname)
def grant_access(self, context, username, hostname, databases):
return MySqlAdmin().grant_access(username, hostname, databases)
def revoke_access(self, context, username, hostname, database):
return MySqlAdmin().revoke_access(username, hostname, database)
def list_access(self, context, username, hostname):
return MySqlAdmin().list_access(username, hostname)
def list_databases(self, context, limit=None, marker=None,
include_marker=False):
return MySqlAdmin().list_databases(limit, marker,
include_marker)
def list_users(self, context, limit=None, marker=None,
include_marker=False):
return MySqlAdmin().list_users(limit, marker,
include_marker)
def enable_root(self, context):
return MySqlAdmin().enable_root()
def is_root_enabled(self, context):
return MySqlAdmin().is_root_enabled()
def _perform_restore(self, backup_info, context, restore_location, app):
LOG.info(_("Restoring database from backup %s.") % backup_info['id'])
try:
backup.restore(context, backup_info, restore_location)
except Exception:
LOG.exception(_("Error performing restore from backup %s.") %
backup_info['id'])
app.status.set_status(rd_instance.ServiceStatuses.FAILED)
raise
LOG.info(_("Restored database successfully."))
def prepare(self, context, packages, databases, memory_mb, users,
device_path=None, mount_point=None, backup_info=None,
config_contents=None, root_password=None, overrides=None,
cluster_config=None, snapshot=None):
"""Makes ready DBAAS on a Guest container."""
MySqlAppStatus.get().begin_install()
# status end_mysql_install set with secure()
app = MySqlApp(MySqlAppStatus.get())
app.install_if_needed(packages)
if device_path:
# stop and do not update database
app.stop_db()
device = volume.VolumeDevice(device_path)
# unmount if device is already mounted
device.unmount_device(device_path)
device.format()
if os.path.exists(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)
operating_system.chown(
mount_point, service.MYSQL_OWNER, service.MYSQL_OWNER,
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
# (see MySqlApp.secure()) and restart.
app.set_data_dir(mount_point + '/data')
app.start_mysql()
if backup_info:
self._perform_restore(backup_info, context,
mount_point + "/data", app)
LOG.debug("Securing MySQL now.")
app.secure(config_contents, overrides)
enable_root_on_restore = (backup_info and
MySqlAdmin().is_root_enabled())
if root_password and not backup_info:
app.secure_root(secure_remote_root=True)
MySqlAdmin().enable_root(root_password)
elif enable_root_on_restore:
app.secure_root(secure_remote_root=False)
MySqlAppStatus.get().report_root(context, 'root')
else:
app.secure_root(secure_remote_root=True)
app.complete_install_or_restart()
if databases:
self.create_database(context, databases)
if users:
self.create_user(context, users)
if snapshot:
self.attach_replica(context, snapshot, snapshot['config'])
LOG.info(_('Completed setup of MySQL database instance.'))
def restart(self, context):
app = MySqlApp(MySqlAppStatus.get())
app.restart()
def start_db_with_conf_changes(self, context, config_contents):
app = MySqlApp(MySqlAppStatus.get())
app.start_db_with_conf_changes(config_contents)
def stop_db(self, context, do_not_start_on_reboot=False):
app = MySqlApp(MySqlAppStatus.get())
app.stop_db(do_not_start_on_reboot=do_not_start_on_reboot)
def get_filesystem_stats(self, context, fs_path):
"""Gets the filesystem stats for the path given."""
mount_point = CONF.get(MANAGER).mount_point
return dbaas.get_filesystem_volume_stats(mount_point)
def create_backup(self, context, backup_info):
"""
Entry point for initiating a backup for this guest agents db instance.
The call currently blocks until the backup is complete or errors. If
device_path is specified, it will be mounted based to a point specified
in configuration.
:param backup_info: a dictionary containing the db instance id of the
backup task, location, type, and other data.
"""
backup.backup(context, backup_info)
def mount_volume(self, context, device_path=None, mount_point=None):
device = volume.VolumeDevice(device_path)
device.mount(mount_point, write_to_fstab=False)
LOG.debug("Mounted the device %s at the mount point %s." %
(device_path, mount_point))
def unmount_volume(self, context, device_path=None, mount_point=None):
device = volume.VolumeDevice(device_path)
device.unmount(mount_point)
LOG.debug("Unmounted the device %s from the mount point %s." %
(device_path, mount_point))
def resize_fs(self, context, device_path=None, mount_point=None):
device = volume.VolumeDevice(device_path)
device.resize_fs(mount_point)
LOG.debug("Resized the filesystem %s." % mount_point)
def update_overrides(self, context, overrides, remove=False):
app = MySqlApp(MySqlAppStatus.get())
if remove:
app.remove_overrides()
app.update_overrides(overrides)
def apply_overrides(self, context, overrides):
LOG.debug("Applying overrides (%s)." % overrides)
app = MySqlApp(MySqlAppStatus.get())
app.apply_overrides(overrides)
def get_replication_snapshot(self, context, snapshot_info,
replica_source_config=None):
LOG.debug("Getting replication snapshot.")
app = MySqlApp(MySqlAppStatus.get())
replication = REPLICATION_STRATEGY_CLASS(context)
replication.enable_as_master(app, replica_source_config)
snapshot_id, log_position = (
replication.snapshot_for_replication(context, app, None,
snapshot_info))
mount_point = CONF.get(MANAGER).mount_point
volume_stats = dbaas.get_filesystem_volume_stats(mount_point)
replication_snapshot = {
'dataset': {
'datastore_manager': MANAGER,
'dataset_size': volume_stats.get('used', 0.0),
'volume_size': volume_stats.get('total', 0.0),
'snapshot_id': snapshot_id
},
'replication_strategy': REPLICATION_STRATEGY,
'master': replication.get_master_ref(app, snapshot_info),
'log_position': log_position
}
return replication_snapshot
def enable_as_master(self, context, replica_source_config):
LOG.debug("Calling enable_as_master.")
app = MySqlApp(MySqlAppStatus.get())
replication = REPLICATION_STRATEGY_CLASS(context)
replication.enable_as_master(app, replica_source_config)
# DEPRECATED: Maintain for API Compatibility
def get_txn_count(self, context):
LOG.debug("Calling get_txn_count")
return MySqlApp(MySqlAppStatus.get()).get_txn_count()
def get_last_txn(self, context):
LOG.debug("Calling get_last_txn")
return MySqlApp(MySqlAppStatus.get()).get_last_txn()
def get_latest_txn_id(self, context):
LOG.debug("Calling get_latest_txn_id.")
return MySqlApp(MySqlAppStatus.get()).get_latest_txn_id()
def wait_for_txn(self, context, txn):
LOG.debug("Calling wait_for_txn.")
MySqlApp(MySqlAppStatus.get()).wait_for_txn(txn)
def detach_replica(self, context, for_failover=False):
LOG.debug("Detaching replica.")
app = MySqlApp(MySqlAppStatus.get())
replication = REPLICATION_STRATEGY_CLASS(context)
replica_info = replication.detach_slave(app, for_failover)
return replica_info
def get_replica_context(self, context):
LOG.debug("Getting replica context.")
app = MySqlApp(MySqlAppStatus.get())
replication = REPLICATION_STRATEGY_CLASS(context)
replica_info = replication.get_replica_context(app)
return replica_info
def _validate_slave_for_replication(self, context, replica_info):
if (replica_info['replication_strategy'] != REPLICATION_STRATEGY):
raise exception.IncompatibleReplicationStrategy(
replica_info.update({
'guest_strategy': REPLICATION_STRATEGY
}))
mount_point = CONF.get(MANAGER).mount_point
volume_stats = dbaas.get_filesystem_volume_stats(mount_point)
if (volume_stats.get('total', 0.0) <
replica_info['dataset']['dataset_size']):
raise exception.InsufficientSpaceForReplica(
replica_info.update({
'slave_volume_size': volume_stats.get('total', 0.0)
}))
def attach_replica(self, context, replica_info, slave_config):
LOG.debug("Attaching replica.")
app = MySqlApp(MySqlAppStatus.get())
try:
if 'replication_strategy' in replica_info:
self._validate_slave_for_replication(context, replica_info)
replication = REPLICATION_STRATEGY_CLASS(context)
replication.enable_as_slave(app, replica_info, slave_config)
except Exception:
LOG.exception("Error enabling replication.")
app.status.set_status(rd_instance.ServiceStatuses.FAILED)
raise
def make_read_only(self, context, read_only):
LOG.debug("Executing make_read_only(%s)" % read_only)
app = MySqlApp(MySqlAppStatus.get())
app.make_read_only(read_only)
def cleanup_source_on_replica_detach(self, context, replica_info):
LOG.debug("Cleaning up the source on the detach of a replica.")
replication = REPLICATION_STRATEGY_CLASS(context)
replication.cleanup_source_on_replica_detach(MySqlAdmin(),
replica_info)
def demote_replication_master(self, context):
LOG.debug("Demoting replication master.")
app = MySqlApp(MySqlAppStatus.get())
replication = REPLICATION_STRATEGY_CLASS(context)
replication.demote_master(app)

View File

@@ -0,0 +1,384 @@
# Copyright 2013 OpenStack Foundation
# Copyright 2013 Rackspace Hosting
# Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import os
from oslo_log import log as logging
from oslo_service import periodic_task
from trove.common import cfg
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 import service_base
from trove.guestagent import dbaas
from trove.guestagent.strategies.replication import get_replication_strategy
from trove.guestagent import volume
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class BaseMySqlManager(periodic_task.PeriodicTasks):
def __init__(self, mysql_app, mysql_app_status, mysql_admin,
replication_strategy, replication_namespace,
replication_strategy_class, manager):
super(BaseMySqlManager, self).__init__(CONF)
self._mysql_app = mysql_app
self._mysql_app_status = mysql_app_status
self._mysql_admin = mysql_admin
self._replication_strategy = replication_strategy
self._replication_namespace = replication_namespace
self._replication_strategy_class = replication_strategy_class
self._manager = manager
@property
def mysql_app(self):
return self._mysql_app
@property
def mysql_app_status(self):
return self._mysql_app_status
@property
def mysql_admin(self):
return self._mysql_admin
@property
def replication_strategy(self):
return self._replication_strategy
@property
def replication_namespace(self):
return self._replication_namespace
@property
def replication_strategy_class(self):
return get_replication_strategy(self._replication_strategy,
self._replication_namespace)
@property
def manager(self):
return self._manager
@periodic_task.periodic_task
def update_status(self, context):
"""Update the status of the MySQL service."""
self.mysql_app_status.get().update()
def rpc_ping(self, context):
LOG.debug("Responding to RPC ping.")
return True
def change_passwords(self, context, users):
return self.mysql_admin().change_passwords(users)
def update_attributes(self, context, username, hostname, user_attrs):
return self.mysql_admin().update_attributes(
username, hostname, user_attrs)
def reset_configuration(self, context, configuration):
app = self.mysql_app(self.mysql_app_status.get())
app.reset_configuration(configuration)
def create_database(self, context, databases):
return self.mysql_admin().create_database(databases)
def create_user(self, context, users):
self.mysql_admin().create_user(users)
def delete_database(self, context, database):
return self.mysql_admin().delete_database(database)
def delete_user(self, context, user):
self.mysql_admin().delete_user(user)
def get_user(self, context, username, hostname):
return self.mysql_admin().get_user(username, hostname)
def grant_access(self, context, username, hostname, databases):
return self.mysql_admin().grant_access(username, hostname, databases)
def revoke_access(self, context, username, hostname, database):
return self.mysql_admin().revoke_access(username, hostname, database)
def list_access(self, context, username, hostname):
return self.mysql_admin().list_access(username, hostname)
def list_databases(self, context, limit=None, marker=None,
include_marker=False):
return self.mysql_admin().list_databases(limit, marker,
include_marker)
def list_users(self, context, limit=None, marker=None,
include_marker=False):
return self.mysql_admin().list_users(limit, marker,
include_marker)
def enable_root(self, context):
return self.mysql_admin().enable_root()
def is_root_enabled(self, context):
return self.mysql_admin().is_root_enabled()
def _perform_restore(self, backup_info, context, restore_location, app):
LOG.info(_("Restoring database from backup %s.") % backup_info['id'])
try:
backup.restore(context, backup_info, restore_location)
except Exception:
LOG.exception(_("Error performing restore from backup %s.") %
backup_info['id'])
app.status.set_status(rd_instance.ServiceStatuses.FAILED)
raise
LOG.info(_("Restored database successfully."))
def prepare(self, context, packages, databases, memory_mb, users,
device_path=None, mount_point=None, backup_info=None,
config_contents=None, root_password=None, overrides=None,
cluster_config=None, snapshot=None):
"""Makes ready DBAAS on a Guest container."""
self.mysql_app_status.get().begin_install()
# status end_mysql_install set with secure()
app = self.mysql_app(self.mysql_app_status.get())
app.install_if_needed(packages)
if device_path:
# stop and do not update database
app.stop_db()
device = volume.VolumeDevice(device_path)
# unmount if device is already mounted
device.unmount_device(device_path)
device.format()
if os.path.exists(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)
operating_system.chown(mount_point, service_base.MYSQL_OWNER,
service_base.MYSQL_OWNER,
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
# (see MySqlApp.secure()) and restart.
app.set_data_dir(mount_point + '/data')
app.start_mysql()
if backup_info:
self._perform_restore(backup_info, context,
mount_point + "/data", app)
LOG.debug("Securing MySQL now.")
app.secure(config_contents, overrides)
enable_root_on_restore = (backup_info and
self.mysql_admin().is_root_enabled())
if root_password and not backup_info:
app.secure_root(secure_remote_root=True)
self.mysql_admin().enable_root(root_password)
elif enable_root_on_restore:
app.secure_root(secure_remote_root=False)
self.mysql_app_status.get().report_root(context, 'root')
else:
app.secure_root(secure_remote_root=True)
app.complete_install_or_restart()
if databases:
self.create_database(context, databases)
if users:
self.create_user(context, users)
if snapshot:
self.attach_replica(context, snapshot, snapshot['config'])
LOG.info(_('Completed setup of MySQL database instance.'))
def restart(self, context):
app = self.mysql_app(self.mysql_app_status.get())
app.restart()
def start_db_with_conf_changes(self, context, config_contents):
app = self.mysql_app(self.mysql_app_status.get())
app.start_db_with_conf_changes(config_contents)
def stop_db(self, context, do_not_start_on_reboot=False):
app = self.mysql_app(self.mysql_app_status.get())
app.stop_db(do_not_start_on_reboot=do_not_start_on_reboot)
def get_filesystem_stats(self, context, fs_path):
"""Gets the filesystem stats for the path given."""
mount_point = CONF.get(self.manager).mount_point
return dbaas.get_filesystem_volume_stats(mount_point)
def create_backup(self, context, backup_info):
"""
Entry point for initiating a backup for this guest agents db instance.
The call currently blocks until the backup is complete or errors. If
device_path is specified, it will be mounted based to a point specified
in configuration.
:param backup_info: a dictionary containing the db instance id of the
backup task, location, type, and other data.
"""
backup.backup(context, backup_info)
def mount_volume(self, context, device_path=None, mount_point=None):
device = volume.VolumeDevice(device_path)
device.mount(mount_point, write_to_fstab=False)
LOG.debug("Mounted the device %s at the mount point %s." %
(device_path, mount_point))
def unmount_volume(self, context, device_path=None, mount_point=None):
device = volume.VolumeDevice(device_path)
device.unmount(mount_point)
LOG.debug("Unmounted the device %s from the mount point %s." %
(device_path, mount_point))
def resize_fs(self, context, device_path=None, mount_point=None):
device = volume.VolumeDevice(device_path)
device.resize_fs(mount_point)
LOG.debug("Resized the filesystem %s." % mount_point)
def update_overrides(self, context, overrides, remove=False):
app = self.mysql_app(self.mysql_app_status.get())
if remove:
app.remove_overrides()
app.update_overrides(overrides)
def apply_overrides(self, context, overrides):
LOG.debug("Applying overrides (%s)." % overrides)
app = self.mysql_app(self.mysql_app_status.get())
app.apply_overrides(overrides)
def get_replication_snapshot(self, context, snapshot_info,
replica_source_config=None):
LOG.debug("Getting replication snapshot.")
app = self.mysql_app(self.mysql_app_status.get())
replication = self.replication_strategy_class(context)
replication.enable_as_master(app, replica_source_config)
snapshot_id, log_position = (
replication.snapshot_for_replication(context, app, None,
snapshot_info))
mount_point = CONF.get(self.manager).mount_point
volume_stats = dbaas.get_filesystem_volume_stats(mount_point)
replication_snapshot = {
'dataset': {
'datastore_manager': self.manager,
'dataset_size': volume_stats.get('used', 0.0),
'volume_size': volume_stats.get('total', 0.0),
'snapshot_id': snapshot_id
},
'replication_strategy': self.replication_strategy,
'master': replication.get_master_ref(app, snapshot_info),
'log_position': log_position
}
return replication_snapshot
def enable_as_master(self, context, replica_source_config):
LOG.debug("Calling enable_as_master.")
app = self.mysql_app(self.mysql_app_status.get())
replication = self.replication_strategy_class(context)
replication.enable_as_master(app, replica_source_config)
# DEPRECATED: Maintain for API Compatibility
def get_txn_count(self, context):
LOG.debug("Calling get_txn_count")
return self.mysql_app(self.mysql_app_status.get()).get_txn_count()
def get_last_txn(self, context):
LOG.debug("Calling get_last_txn")
return self.mysql_app(self.mysql_app_status.get()).get_last_txn()
def get_latest_txn_id(self, context):
LOG.debug("Calling get_latest_txn_id.")
return self.mysql_app(self.mysql_app_status.get()).get_latest_txn_id()
def wait_for_txn(self, context, txn):
LOG.debug("Calling wait_for_txn.")
self.mysql_app(self.mysql_app_status.get()).wait_for_txn(txn)
def detach_replica(self, context, for_failover=False):
LOG.debug("Detaching replica.")
app = self.mysql_app(self.mysql_app_status.get())
replication = self.replication_strategy_class(context)
replica_info = replication.detach_slave(app, for_failover)
return replica_info
def get_replica_context(self, context):
LOG.debug("Getting replica context.")
app = self.mysql_app(self.mysql_app_status.get())
replication = self.replication_strategy_class(context)
replica_info = replication.get_replica_context(app)
return replica_info
def _validate_slave_for_replication(self, context, replica_info):
if (replica_info['replication_strategy'] != self.replication_strategy):
raise exception.IncompatibleReplicationStrategy(
replica_info.update({
'guest_strategy': self.replication_strategy
}))
mount_point = CONF.get(self.manager).mount_point
volume_stats = dbaas.get_filesystem_volume_stats(mount_point)
if (volume_stats.get('total', 0.0) <
replica_info['dataset']['dataset_size']):
raise exception.InsufficientSpaceForReplica(
replica_info.update({
'slave_volume_size': volume_stats.get('total', 0.0)
}))
def attach_replica(self, context, replica_info, slave_config):
LOG.debug("Attaching replica.")
app = self.mysql_app(self.mysql_app_status.get())
try:
if 'replication_strategy' in replica_info:
self._validate_slave_for_replication(context, replica_info)
replication = self.replication_strategy_class(context)
replication.enable_as_slave(app, replica_info, slave_config)
except Exception:
LOG.exception("Error enabling replication.")
app.status.set_status(rd_instance.ServiceStatuses.FAILED)
raise
def make_read_only(self, context, read_only):
LOG.debug("Executing make_read_only(%s)" % read_only)
app = self.mysql_app(self.mysql_app_status.get())
app.make_read_only(read_only)
def cleanup_source_on_replica_detach(self, context, replica_info):
LOG.debug("Cleaning up the source on the detach of a replica.")
replication = self.replication_strategy_class(context)
replication.cleanup_source_on_replica_detach(self.mysql_admin(),
replica_info)
def demote_replication_master(self, context):
LOG.debug("Demoting replication master.")
app = self.mysql_app(self.mysql_app_status.get())
replication = self.replication_strategy_class(context)
replication.demote_master(app)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,7 @@ defaults = {
'mysql': 'mysql':
'trove.guestagent.datastore.mysql.manager.Manager', 'trove.guestagent.datastore.mysql.manager.Manager',
'percona': 'percona':
'trove.guestagent.datastore.mysql.manager.Manager', 'trove.guestagent.datastore.experimental.percona.manager.Manager',
'redis': 'redis':
'trove.guestagent.datastore.experimental.redis.manager.Manager', 'trove.guestagent.datastore.experimental.redis.manager.Manager',
'cassandra': 'cassandra':
@@ -54,6 +54,8 @@ defaults = {
'trove.guestagent.datastore.experimental.vertica.manager.Manager', 'trove.guestagent.datastore.experimental.vertica.manager.Manager',
'db2': 'db2':
'trove.guestagent.datastore.experimental.db2.manager.Manager', 'trove.guestagent.datastore.experimental.db2.manager.Manager',
'mariadb':
'trove.guestagent.datastore.experimental.mariadb.manager.Manager'
} }
CONF = cfg.CONF CONF = cfg.CONF

View File

@@ -20,8 +20,8 @@ import re
from oslo_log import log as logging from oslo_log import log as logging
from trove.common.i18n import _ from trove.common.i18n import _
from trove.guestagent.datastore.mysql.service import ADMIN_USER_NAME
from trove.guestagent.datastore.mysql.service import MySqlApp from trove.guestagent.datastore.mysql.service import MySqlApp
from trove.guestagent.datastore.mysql.service_base import ADMIN_USER_NAME
from trove.guestagent.strategies.backup import base from trove.guestagent.strategies.backup import base
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)

View File

@@ -70,6 +70,7 @@ from trove.guestagent.datastore.mysql.service import MySqlAdmin
from trove.guestagent.datastore.mysql.service import MySqlApp from trove.guestagent.datastore.mysql.service import MySqlApp
from trove.guestagent.datastore.mysql.service import MySqlAppStatus from trove.guestagent.datastore.mysql.service import MySqlAppStatus
from trove.guestagent.datastore.mysql.service import MySqlRootAccess from trove.guestagent.datastore.mysql.service import MySqlRootAccess
import trove.guestagent.datastore.mysql.service_base as dbaas_base
from trove.guestagent.datastore.service import BaseDbStatus from trove.guestagent.datastore.service import BaseDbStatus
from trove.guestagent.db import models from trove.guestagent.db import models
from trove.guestagent import dbaas as dbaas_sr from trove.guestagent import dbaas as dbaas_sr
@@ -118,30 +119,33 @@ class DbaasTest(testtools.TestCase):
def setUp(self): def setUp(self):
super(DbaasTest, self).setUp() super(DbaasTest, self).setUp()
self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout self.orig_utils_execute_with_timeout = \
self.orig_utils_execute = dbaas.utils.execute dbaas_base.utils.execute_with_timeout
self.orig_utils_execute = dbaas_base.utils.execute
def tearDown(self): def tearDown(self):
super(DbaasTest, self).tearDown() super(DbaasTest, self).tearDown()
dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout dbaas_base.utils.execute_with_timeout = \
dbaas.utils.execute = self.orig_utils_execute self.orig_utils_execute_with_timeout
dbaas_base.utils.execute = self.orig_utils_execute
@patch.object(operating_system, 'remove') @patch.object(operating_system, 'remove')
def test_clear_expired_password(self, mock_remove): def test_clear_expired_password(self, mock_remove):
secret_content = ("# The random password set for the " secret_content = ("# The random password set for the "
"root user at Wed May 14 14:06:38 2014 " "root user at Wed May 14 14:06:38 2014 "
"(local time): somepassword") "(local time): somepassword")
with patch.object(dbaas.utils, 'execute', with patch.object(dbaas_base.utils, 'execute',
return_value=(secret_content, None)): return_value=(secret_content, None)):
dbaas.clear_expired_password() dbaas_base.clear_expired_password()
self.assertEqual(2, dbaas.utils.execute.call_count) self.assertEqual(2, dbaas_base.utils.execute.call_count)
self.assertEqual(1, mock_remove.call_count) self.assertEqual(1, mock_remove.call_count)
@patch.object(operating_system, 'remove') @patch.object(operating_system, 'remove')
def test_no_secret_content_clear_expired_password(self, mock_remove): def test_no_secret_content_clear_expired_password(self, mock_remove):
with patch.object(dbaas.utils, 'execute', return_value=('', None)): with patch.object(dbaas_base.utils, 'execute',
dbaas.clear_expired_password() return_value=('', None)):
self.assertEqual(1, dbaas.utils.execute.call_count) dbaas_base.clear_expired_password()
self.assertEqual(1, dbaas_base.utils.execute.call_count)
mock_remove.assert_not_called() mock_remove.assert_not_called()
@patch.object(operating_system, 'remove') @patch.object(operating_system, 'remove')
@@ -150,19 +154,20 @@ class DbaasTest(testtools.TestCase):
secret_content = ("# The random password set for the " secret_content = ("# The random password set for the "
"root user at Wed May 14 14:06:38 2014 " "root user at Wed May 14 14:06:38 2014 "
"(local time): somepassword") "(local time): somepassword")
with patch.object(dbaas.utils, 'execute', with patch.object(dbaas_base.utils, 'execute',
side_effect=[(secret_content, None), side_effect=[(secret_content, None),
ProcessExecutionError]): ProcessExecutionError]):
dbaas.clear_expired_password() dbaas_base.clear_expired_password()
self.assertEqual(2, dbaas.utils.execute.call_count) self.assertEqual(2, dbaas_base.utils.execute.call_count)
mock_remove.assert_not_called() mock_remove.assert_not_called()
@patch.object(operating_system, 'remove') @patch.object(operating_system, 'remove')
@patch.object(dbaas.utils, 'execute', side_effect=ProcessExecutionError) @patch.object(dbaas_base.utils, 'execute',
side_effect=ProcessExecutionError)
def test_fail_retrieve_secret_content_clear_expired_password(self, def test_fail_retrieve_secret_content_clear_expired_password(self,
mock_execute, mock_execute,
mock_remove): mock_remove):
dbaas.clear_expired_password() dbaas_base.clear_expired_password()
self.assertEqual(1, mock_execute.call_count) self.assertEqual(1, mock_execute.call_count)
mock_remove.assert_not_called() mock_remove.assert_not_called()
@@ -181,7 +186,8 @@ class DbaasTest(testtools.TestCase):
def test_service_discovery(self): def test_service_discovery(self):
with patch.object(os.path, 'isfile', return_value=True): with patch.object(os.path, 'isfile', return_value=True):
mysql_service = dbaas.operating_system.service_discovery(["mysql"]) mysql_service = \
dbaas_base.operating_system.service_discovery(["mysql"])
self.assertIsNotNone(mysql_service['cmd_start']) self.assertIsNotNone(mysql_service['cmd_start'])
self.assertIsNotNone(mysql_service['cmd_enable']) self.assertIsNotNone(mysql_service['cmd_enable'])
@@ -192,8 +198,8 @@ class DbaasTest(testtools.TestCase):
"--tmpdir=/tmp --skip-external-locking" "--tmpdir=/tmp --skip-external-locking"
with patch.object(os.path, 'isfile', return_value=True): with patch.object(os.path, 'isfile', return_value=True):
dbaas.utils.execute = Mock(return_value=(output, None)) dbaas_base.utils.execute = Mock(return_value=(output, None))
options = dbaas.load_mysqld_options() options = dbaas_base.load_mysqld_options()
self.assertEqual(5, len(options)) self.assertEqual(5, len(options))
self.assertEqual(["mysql"], options["user"]) self.assertEqual(["mysql"], options["user"])
@@ -208,8 +214,8 @@ class DbaasTest(testtools.TestCase):
"--plugin-load=federated=ha_federated.so") "--plugin-load=federated=ha_federated.so")
with patch.object(os.path, 'isfile', return_value=True): with patch.object(os.path, 'isfile', return_value=True):
dbaas.utils.execute = Mock(return_value=(output, None)) dbaas_base.utils.execute = Mock(return_value=(output, None))
options = dbaas.load_mysqld_options() options = dbaas_base.load_mysqld_options()
self.assertEqual(1, len(options)) self.assertEqual(1, len(options))
self.assertEqual(["blackhole=ha_blackhole.so", self.assertEqual(["blackhole=ha_blackhole.so",
@@ -219,9 +225,9 @@ class DbaasTest(testtools.TestCase):
@patch.object(os.path, 'isfile', return_value=True) @patch.object(os.path, 'isfile', return_value=True)
def test_load_mysqld_options_error(self, mock_exists): def test_load_mysqld_options_error(self, mock_exists):
dbaas.utils.execute = Mock(side_effect=ProcessExecutionError()) dbaas_base.utils.execute = Mock(side_effect=ProcessExecutionError())
self.assertFalse(dbaas.load_mysqld_options()) self.assertFalse(dbaas_base.load_mysqld_options())
class ResultSetStub(object): class ResultSetStub(object):
@@ -242,8 +248,15 @@ class ResultSetStub(object):
class MySqlAdminMockTest(testtools.TestCase): class MySqlAdminMockTest(testtools.TestCase):
def setUp(self):
super(MySqlAdminMockTest, self).setUp()
dbaas.orig_configuration_manager = dbaas.MySqlApp.configuration_manager
dbaas.MySqlApp.configuration_manager = Mock()
def tearDown(self): def tearDown(self):
super(MySqlAdminMockTest, self).tearDown() super(MySqlAdminMockTest, self).tearDown()
dbaas.MySqlApp.configuration_manager = \
dbaas.orig_configuration_manager
@patch('trove.guestagent.datastore.mysql.service.MySqlApp' @patch('trove.guestagent.datastore.mysql.service.MySqlApp'
'.get_auth_password', return_value='some_password') '.get_auth_password', return_value='some_password')
@@ -279,6 +292,10 @@ class MySqlAdminTest(testtools.TestCase):
dbaas.LocalSqlClient.__enter__ = Mock() dbaas.LocalSqlClient.__enter__ = Mock()
dbaas.LocalSqlClient.__exit__ = Mock() dbaas.LocalSqlClient.__exit__ = Mock()
dbaas.LocalSqlClient.execute = Mock() dbaas.LocalSqlClient.execute = Mock()
# trove.guestagent.common.configuration import ConfigurationManager
dbaas.orig_configuration_manager = dbaas.MySqlApp.configuration_manager
dbaas.MySqlApp.configuration_manager = Mock()
self.mySqlAdmin = MySqlAdmin() self.mySqlAdmin = MySqlAdmin()
def tearDown(self): def tearDown(self):
@@ -291,6 +308,8 @@ class MySqlAdminTest(testtools.TestCase):
dbaas.LocalSqlClient.execute = self.orig_LocalSqlClient_execute dbaas.LocalSqlClient.execute = self.orig_LocalSqlClient_execute
models.MySQLUser._is_valid_user_name = ( models.MySQLUser._is_valid_user_name = (
self.orig_MySQLUser_is_valid_user_name) self.orig_MySQLUser_is_valid_user_name)
dbaas.MySqlApp.configuration_manager = \
dbaas.orig_configuration_manager
def test__associate_dbs(self): def test__associate_dbs(self):
db_result = [{"grantee": "'test_user'@'%'", "table_schema": "db1"}, db_result = [{"grantee": "'test_user'@'%'", "table_schema": "db1"},
@@ -732,7 +751,8 @@ class MySqlAppTest(testtools.TestCase):
def setUp(self): def setUp(self):
super(MySqlAppTest, self).setUp() super(MySqlAppTest, self).setUp()
self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout self.orig_utils_execute_with_timeout = \
dbaas_base.utils.execute_with_timeout
self.orig_time_sleep = time.sleep self.orig_time_sleep = time.sleep
self.orig_unlink = os.unlink self.orig_unlink = os.unlink
self.orig_get_auth_password = MySqlApp.get_auth_password self.orig_get_auth_password = MySqlApp.get_auth_password
@@ -759,15 +779,20 @@ class MySqlAppTest(testtools.TestCase):
self.mock_client.__enter__ = Mock() self.mock_client.__enter__ = Mock()
self.mock_client.__exit__ = Mock() self.mock_client.__exit__ = Mock()
self.mock_client.__enter__.return_value.execute = self.mock_execute self.mock_client.__enter__.return_value.execute = self.mock_execute
dbaas.orig_configuration_manager = dbaas.MySqlApp.configuration_manager
dbaas.MySqlApp.configuration_manager = Mock()
def tearDown(self): def tearDown(self):
super(MySqlAppTest, self).tearDown() super(MySqlAppTest, self).tearDown()
dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout dbaas_base.utils.execute_with_timeout = \
self.orig_utils_execute_with_timeout
time.sleep = self.orig_time_sleep time.sleep = self.orig_time_sleep
os.unlink = self.orig_unlink os.unlink = self.orig_unlink
operating_system.service_discovery = self.orig_service_discovery operating_system.service_discovery = self.orig_service_discovery
MySqlApp.get_auth_password = self.orig_get_auth_password MySqlApp.get_auth_password = self.orig_get_auth_password
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete() InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
dbaas.MySqlApp.configuration_manager = \
dbaas.orig_configuration_manager
def assert_reported_status(self, expected_status): def assert_reported_status(self, expected_status):
service_status = InstanceServiceStatus.find_by( service_status = InstanceServiceStatus.find_by(
@@ -802,7 +827,7 @@ class MySqlAppTest(testtools.TestCase):
def test_stop_mysql(self): def test_stop_mysql(self):
dbaas.utils.execute_with_timeout = Mock() dbaas_base.utils.execute_with_timeout = Mock()
self.appStatus.set_next_status( self.appStatus.set_next_status(
rd_instance.ServiceStatuses.SHUTDOWN) rd_instance.ServiceStatuses.SHUTDOWN)
@@ -811,7 +836,7 @@ class MySqlAppTest(testtools.TestCase):
def test_stop_mysql_with_db_update(self): def test_stop_mysql_with_db_update(self):
dbaas.utils.execute_with_timeout = Mock() dbaas_base.utils.execute_with_timeout = Mock()
self.appStatus.set_next_status( self.appStatus.set_next_status(
rd_instance.ServiceStatuses.SHUTDOWN) rd_instance.ServiceStatuses.SHUTDOWN)
@@ -836,7 +861,7 @@ class MySqlAppTest(testtools.TestCase):
def test_stop_mysql_error(self): def test_stop_mysql_error(self):
dbaas.utils.execute_with_timeout = Mock() dbaas_base.utils.execute_with_timeout = Mock()
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING) self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
self.mySqlApp.state_change_wait_time = 1 self.mySqlApp.state_change_wait_time = 1
self.assertRaises(RuntimeError, self.mySqlApp.stop_db) self.assertRaises(RuntimeError, self.mySqlApp.stop_db)
@@ -882,14 +907,14 @@ class MySqlAppTest(testtools.TestCase):
def test_wipe_ib_logfiles_error(self, get_datadir_mock): def test_wipe_ib_logfiles_error(self, get_datadir_mock):
mocked = Mock(side_effect=ProcessExecutionError('Error')) mocked = Mock(side_effect=ProcessExecutionError('Error'))
dbaas.utils.execute_with_timeout = mocked dbaas_base.utils.execute_with_timeout = mocked
self.assertRaises(ProcessExecutionError, self.assertRaises(ProcessExecutionError,
self.mySqlApp.wipe_ib_logfiles) self.mySqlApp.wipe_ib_logfiles)
def test_start_mysql(self): def test_start_mysql(self):
dbaas.utils.execute_with_timeout = Mock() dbaas_base.utils.execute_with_timeout = Mock()
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING) self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
self.mySqlApp._enable_mysql_on_boot = Mock() self.mySqlApp._enable_mysql_on_boot = Mock()
self.mySqlApp.start_mysql() self.mySqlApp.start_mysql()
@@ -897,7 +922,7 @@ class MySqlAppTest(testtools.TestCase):
def test_start_mysql_with_db_update(self): def test_start_mysql_with_db_update(self):
dbaas.utils.execute_with_timeout = Mock() dbaas_base.utils.execute_with_timeout = Mock()
self.mySqlApp._enable_mysql_on_boot = Mock() self.mySqlApp._enable_mysql_on_boot = Mock()
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING) self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
@@ -909,7 +934,7 @@ class MySqlAppTest(testtools.TestCase):
def test_start_mysql_runs_forever(self): def test_start_mysql_runs_forever(self):
dbaas.utils.execute_with_timeout = Mock() dbaas_base.utils.execute_with_timeout = Mock()
self.mySqlApp._enable_mysql_on_boot = Mock() self.mySqlApp._enable_mysql_on_boot = Mock()
self.mySqlApp.state_change_wait_time = 1 self.mySqlApp.state_change_wait_time = 1
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN) self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
@@ -924,7 +949,7 @@ class MySqlAppTest(testtools.TestCase):
self.mySqlApp._enable_mysql_on_boot = Mock() self.mySqlApp._enable_mysql_on_boot = Mock()
mocked = Mock(side_effect=ProcessExecutionError('Error')) mocked = Mock(side_effect=ProcessExecutionError('Error'))
dbaas.utils.execute_with_timeout = mocked dbaas_base.utils.execute_with_timeout = mocked
self.assertRaises(RuntimeError, self.mySqlApp.start_mysql) self.assertRaises(RuntimeError, self.mySqlApp.start_mysql)
@@ -976,13 +1001,14 @@ class MySqlAppTest(testtools.TestCase):
save_cfg_mock.assert_called_once_with('some junk') save_cfg_mock.assert_called_once_with('some junk')
apply_mock.assert_called_once_with( apply_mock.assert_called_once_with(
{'client': {'user': dbaas.ADMIN_USER_NAME, {'client': {'user': dbaas_base.ADMIN_USER_NAME,
'password': auth_pwd_mock.return_value}}) 'password': auth_pwd_mock.return_value}})
wipe_ib_mock.assert_called_once_with() wipe_ib_mock.assert_called_once_with()
@patch.object(utils, 'execute_with_timeout') @patch.object(utils, 'execute_with_timeout')
def test__enable_mysql_on_boot(self, mock_execute): def test__enable_mysql_on_boot(self, mock_execute):
mysql_service = dbaas.operating_system.service_discovery(["mysql"]) mysql_service = \
dbaas_base.operating_system.service_discovery(["mysql"])
self.mySqlApp._enable_mysql_on_boot() self.mySqlApp._enable_mysql_on_boot()
self.assertEqual(1, mock_execute.call_count) self.assertEqual(1, mock_execute.call_count)
mock_execute.assert_called_with(mysql_service['cmd_enable'], mock_execute.assert_called_with(mysql_service['cmd_enable'],
@@ -998,7 +1024,8 @@ class MySqlAppTest(testtools.TestCase):
@patch.object(utils, 'execute_with_timeout') @patch.object(utils, 'execute_with_timeout')
def test__disable_mysql_on_boot(self, mock_execute): def test__disable_mysql_on_boot(self, mock_execute):
mysql_service = dbaas.operating_system.service_discovery(["mysql"]) mysql_service = \
dbaas_base.operating_system.service_discovery(["mysql"])
self.mySqlApp._disable_mysql_on_boot() self.mySqlApp._disable_mysql_on_boot()
self.assertEqual(1, mock_execute.call_count) self.assertEqual(1, mock_execute.call_count)
mock_execute.assert_called_with(mysql_service['cmd_disable'], mock_execute.assert_called_with(mysql_service['cmd_disable'],
@@ -1030,26 +1057,26 @@ class MySqlAppTest(testtools.TestCase):
'apply_system_override') as apply_sys_mock: 'apply_system_override') as apply_sys_mock:
self.mySqlApp.write_replication_source_overrides('something') self.mySqlApp.write_replication_source_overrides('something')
apply_sys_mock.assert_called_once_with('something', apply_sys_mock.assert_called_once_with('something',
dbaas.CNF_MASTER) dbaas_base.CNF_MASTER)
def test_write_replication_replica_overrides(self): def test_write_replication_replica_overrides(self):
with patch.object(self.mySqlApp.configuration_manager, with patch.object(self.mySqlApp.configuration_manager,
'apply_system_override') as apply_sys_mock: 'apply_system_override') as apply_sys_mock:
self.mySqlApp.write_replication_replica_overrides('something') self.mySqlApp.write_replication_replica_overrides('something')
apply_sys_mock.assert_called_once_with('something', apply_sys_mock.assert_called_once_with('something',
dbaas.CNF_SLAVE) dbaas_base.CNF_SLAVE)
def test_remove_replication_source_overrides(self): def test_remove_replication_source_overrides(self):
with patch.object(self.mySqlApp.configuration_manager, with patch.object(self.mySqlApp.configuration_manager,
'remove_system_override') as remove_sys_mock: 'remove_system_override') as remove_sys_mock:
self.mySqlApp.remove_replication_source_overrides() self.mySqlApp.remove_replication_source_overrides()
remove_sys_mock.assert_called_once_with(dbaas.CNF_MASTER) remove_sys_mock.assert_called_once_with(dbaas_base.CNF_MASTER)
def test_remove_replication_replica_overrides(self): def test_remove_replication_replica_overrides(self):
with patch.object(self.mySqlApp.configuration_manager, with patch.object(self.mySqlApp.configuration_manager,
'remove_system_override') as remove_sys_mock: 'remove_system_override') as remove_sys_mock:
self.mySqlApp.remove_replication_replica_overrides() self.mySqlApp.remove_replication_replica_overrides()
remove_sys_mock.assert_called_once_with(dbaas.CNF_SLAVE) remove_sys_mock.assert_called_once_with(dbaas_base.CNF_SLAVE)
def test_exists_replication_source_overrides(self): def test_exists_replication_source_overrides(self):
with patch.object(self.mySqlApp.configuration_manager, with patch.object(self.mySqlApp.configuration_manager,
@@ -1063,7 +1090,7 @@ class MySqlAppTest(testtools.TestCase):
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
def test_grant_replication_privilege(self, *args): def test_grant_replication_privilege(self, *args):
replication_user = {'name': 'testUSr', 'password': 'somePwd'} replication_user = {'name': 'testUSr', 'password': 'somePwd'}
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.grant_replication_privilege(replication_user) self.mySqlApp.grant_replication_privilege(replication_user)
args, _ = self.mock_execute.call_args_list[0] args, _ = self.mock_execute.call_args_list[0]
@@ -1075,7 +1102,7 @@ class MySqlAppTest(testtools.TestCase):
@patch.object(dbaas, 'get_engine', @patch.object(dbaas, 'get_engine',
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
def test_get_port(self, *args): def test_get_port(self, *args):
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.get_port() self.mySqlApp.get_port()
args, _ = self.mock_execute.call_args_list[0] args, _ = self.mock_execute.call_args_list[0]
@@ -1088,7 +1115,7 @@ class MySqlAppTest(testtools.TestCase):
def test_get_binlog_position(self, *args): def test_get_binlog_position(self, *args):
result = {'File': 'mysql-bin.003', 'Position': '73'} result = {'File': 'mysql-bin.003', 'Position': '73'}
self.mock_execute.return_value.first = Mock(return_value=result) self.mock_execute.return_value.first = Mock(return_value=result)
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
found_result = self.mySqlApp.get_binlog_position() found_result = self.mySqlApp.get_binlog_position()
@@ -1103,7 +1130,7 @@ class MySqlAppTest(testtools.TestCase):
@patch.object(dbaas, 'get_engine', @patch.object(dbaas, 'get_engine',
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
def test_execute_on_client(self, *args): def test_execute_on_client(self, *args):
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.execute_on_client('show tables') self.mySqlApp.execute_on_client('show tables')
args, _ = self.mock_execute.call_args_list[0] args, _ = self.mock_execute.call_args_list[0]
@@ -1115,7 +1142,7 @@ class MySqlAppTest(testtools.TestCase):
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
@patch.object(dbaas.MySqlApp, '_wait_for_slave_status') @patch.object(dbaas.MySqlApp, '_wait_for_slave_status')
def test_start_slave(self, *args): def test_start_slave(self, *args):
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.start_slave() self.mySqlApp.start_slave()
args, _ = self.mock_execute.call_args_list[0] args, _ = self.mock_execute.call_args_list[0]
@@ -1129,7 +1156,7 @@ class MySqlAppTest(testtools.TestCase):
def test_stop_slave_with_failover(self, *args): def test_stop_slave_with_failover(self, *args):
self.mock_execute.return_value.first = Mock( self.mock_execute.return_value.first = Mock(
return_value={'Master_User': 'root'}) return_value={'Master_User': 'root'})
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
result = self.mySqlApp.stop_slave(True) result = self.mySqlApp.stop_slave(True)
self.assertEqual('root', result['replication_user']) self.assertEqual('root', result['replication_user'])
@@ -1147,7 +1174,7 @@ class MySqlAppTest(testtools.TestCase):
def test_stop_slave_without_failover(self, *args): def test_stop_slave_without_failover(self, *args):
self.mock_execute.return_value.first = Mock( self.mock_execute.return_value.first = Mock(
return_value={'Master_User': 'root'}) return_value={'Master_User': 'root'})
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
result = self.mySqlApp.stop_slave(False) result = self.mySqlApp.stop_slave(False)
self.assertEqual('root', result['replication_user']) self.assertEqual('root', result['replication_user'])
@@ -1163,7 +1190,7 @@ class MySqlAppTest(testtools.TestCase):
@patch.object(dbaas, 'get_engine', @patch.object(dbaas, 'get_engine',
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
def test_stop_master(self, *args): def test_stop_master(self, *args):
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.stop_master() self.mySqlApp.stop_master()
args, _ = self.mock_execute.call_args_list[0] args, _ = self.mock_execute.call_args_list[0]
@@ -1197,7 +1224,7 @@ class MySqlAppTest(testtools.TestCase):
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
def test__get_slave_status(self, *args): def test__get_slave_status(self, *args):
self.mock_execute.return_value.first = Mock(return_value='some_thing') self.mock_execute.return_value.first = Mock(return_value='some_thing')
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
result = self.mySqlApp._get_slave_status() result = self.mySqlApp._get_slave_status()
self.assertEqual('some_thing', result) self.assertEqual('some_thing', result)
@@ -1211,7 +1238,7 @@ class MySqlAppTest(testtools.TestCase):
def test_get_latest_txn_id(self, *args): def test_get_latest_txn_id(self, *args):
self.mock_execute.return_value.first = Mock(return_value=['some_thing'] self.mock_execute.return_value.first = Mock(return_value=['some_thing']
) )
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
result = self.mySqlApp.get_latest_txn_id() result = self.mySqlApp.get_latest_txn_id()
self.assertEqual('some_thing', result) self.assertEqual('some_thing', result)
@@ -1223,7 +1250,7 @@ class MySqlAppTest(testtools.TestCase):
@patch.object(dbaas, 'get_engine', @patch.object(dbaas, 'get_engine',
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
def test_wait_for_txn(self, *args): def test_wait_for_txn(self, *args):
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.wait_for_txn('abcd') self.mySqlApp.wait_for_txn('abcd')
args, _ = self.mock_execute.call_args_list[0] args, _ = self.mock_execute.call_args_list[0]
@@ -1236,7 +1263,7 @@ class MySqlAppTest(testtools.TestCase):
def test_get_txn_count(self, *args): def test_get_txn_count(self, *args):
self.mock_execute.return_value.first = Mock( self.mock_execute.return_value.first = Mock(
return_value=['b1f3f33a-0789-ee1c-43f3-f8373e12f1ea:1']) return_value=['b1f3f33a-0789-ee1c-43f3-f8373e12f1ea:1'])
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
result = self.mySqlApp.get_txn_count() result = self.mySqlApp.get_txn_count()
self.assertEqual(1, result) self.assertEqual(1, result)
@@ -1251,7 +1278,7 @@ class MySqlAppInstallTest(MySqlAppTest):
def setUp(self): def setUp(self):
super(MySqlAppInstallTest, self).setUp() super(MySqlAppInstallTest, self).setUp()
self.orig_create_engine = sqlalchemy.create_engine self.orig_create_engine = sqlalchemy.create_engine
self.orig_pkg_version = dbaas.packager.pkg_version self.orig_pkg_version = dbaas_base.packager.pkg_version
self.orig_utils_execute_with_timeout = utils.execute_with_timeout self.orig_utils_execute_with_timeout = utils.execute_with_timeout
self.mock_client = Mock() self.mock_client = Mock()
self.mock_execute = Mock() self.mock_execute = Mock()
@@ -1262,7 +1289,7 @@ class MySqlAppInstallTest(MySqlAppTest):
def tearDown(self): def tearDown(self):
super(MySqlAppInstallTest, self).tearDown() super(MySqlAppInstallTest, self).tearDown()
sqlalchemy.create_engine = self.orig_create_engine sqlalchemy.create_engine = self.orig_create_engine
dbaas.packager.pkg_version = self.orig_pkg_version dbaas_base.packager.pkg_version = self.orig_pkg_version
utils.execute_with_timeout = self.orig_utils_execute_with_timeout utils.execute_with_timeout = self.orig_utils_execute_with_timeout
def test_install(self): def test_install(self):
@@ -1282,7 +1309,7 @@ class MySqlAppInstallTest(MySqlAppTest):
return_value='some_password') return_value='some_password')
def test_secure(self, auth_pwd_mock): def test_secure(self, auth_pwd_mock):
dbaas.clear_expired_password = Mock() dbaas_base.clear_expired_password = Mock()
self.mySqlApp.start_mysql = Mock() self.mySqlApp.start_mysql = Mock()
self.mySqlApp.stop_db = Mock() self.mySqlApp.stop_db = Mock()
self.mySqlApp._reset_configuration = Mock() self.mySqlApp._reset_configuration = Mock()
@@ -1306,7 +1333,7 @@ class MySqlAppInstallTest(MySqlAppTest):
@patch.object(utils, 'generate_random_password', @patch.object(utils, 'generate_random_password',
return_value='some_password') return_value='some_password')
def test_secure_root(self, *args): def test_secure_root(self, *args):
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.secure_root() self.mySqlApp.secure_root()
update_root_password, _ = self.mock_execute.call_args_list[0] update_root_password, _ = self.mock_execute.call_args_list[0]
@@ -1344,7 +1371,7 @@ class MySqlAppInstallTest(MySqlAppTest):
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
def test_apply_overrides(self, *args): def test_apply_overrides(self, *args):
overrides = {'sort_buffer_size': 1000000} overrides = {'sort_buffer_size': 1000000}
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.apply_overrides(overrides) self.mySqlApp.apply_overrides(overrides)
args, _ = self.mock_execute.call_args_list[0] args, _ = self.mock_execute.call_args_list[0]
@@ -1355,7 +1382,7 @@ class MySqlAppInstallTest(MySqlAppTest):
@patch.object(dbaas, 'get_engine', @patch.object(dbaas, 'get_engine',
return_value=MagicMock(name='get_engine')) return_value=MagicMock(name='get_engine'))
def test_make_read_only(self, *args): def test_make_read_only(self, *args):
with patch.object(dbaas, 'LocalSqlClient', with patch.object(dbaas.MySqlApp, 'local_sql_client',
return_value=self.mock_client): return_value=self.mock_client):
self.mySqlApp.make_read_only('ON') self.mySqlApp.make_read_only('ON')
args, _ = self.mock_execute.call_args_list[0] args, _ = self.mock_execute.call_args_list[0]
@@ -1380,7 +1407,7 @@ class MySqlAppInstallTest(MySqlAppTest):
def test_secure_write_conf_error(self): def test_secure_write_conf_error(self):
dbaas.clear_expired_password = Mock() dbaas_base.clear_expired_password = Mock()
self.mySqlApp.start_mysql = Mock() self.mySqlApp.start_mysql = Mock()
self.mySqlApp.stop_db = Mock() self.mySqlApp.stop_db = Mock()
self.mySqlApp._reset_configuration = Mock( self.mySqlApp._reset_configuration = Mock(
@@ -1443,7 +1470,8 @@ class MySqlAppMockTest(testtools.TestCase):
mock_status = MagicMock() mock_status = MagicMock()
mock_status.wait_for_real_status_to_change_to = MagicMock( mock_status.wait_for_real_status_to_change_to = MagicMock(
return_value=True) return_value=True)
dbaas.clear_expired_password = MagicMock(return_value=None) dbaas_base.clear_expired_password = \
MagicMock(return_value=None)
app = MySqlApp(mock_status) app = MySqlApp(mock_status)
app._reset_configuration = MagicMock() app._reset_configuration = MagicMock()
app._write_mycnf = MagicMock(return_value=True) app._write_mycnf = MagicMock(return_value=True)
@@ -1469,9 +1497,11 @@ class MySqlAppMockTest(testtools.TestCase):
mock_status = MagicMock() mock_status = MagicMock()
mock_status.wait_for_real_status_to_change_to = MagicMock( mock_status.wait_for_real_status_to_change_to = MagicMock(
return_value=True) return_value=True)
dbaas.clear_expired_password = MagicMock(return_value=None) dbaas_base.clear_expired_password = \
MagicMock(return_value=None)
app = MySqlApp(mock_status) app = MySqlApp(mock_status)
dbaas.clear_expired_password = MagicMock(return_value=None) dbaas_base.clear_expired_password = \
MagicMock(return_value=None)
self.assertRaises(RuntimeError, app.secure, None, None) self.assertRaises(RuntimeError, app.secure, None, None)
self.assertTrue(mock_conn.execute.called) self.assertTrue(mock_conn.execute.called)
# At least called twice # At least called twice
@@ -1486,10 +1516,14 @@ class MySqlRootStatusTest(testtools.TestCase):
def setUp(self): def setUp(self):
super(MySqlRootStatusTest, self).setUp() super(MySqlRootStatusTest, self).setUp()
self.orig_utils_execute_with_timeout = utils.execute_with_timeout self.orig_utils_execute_with_timeout = utils.execute_with_timeout
dbaas.orig_configuration_manager = dbaas.MySqlApp.configuration_manager
dbaas.MySqlApp.configuration_manager = Mock()
def tearDown(self): def tearDown(self):
super(MySqlRootStatusTest, self).tearDown() super(MySqlRootStatusTest, self).tearDown()
utils.execute_with_timeout = self.orig_utils_execute_with_timeout utils.execute_with_timeout = self.orig_utils_execute_with_timeout
dbaas.MySqlApp.configuration_manager = \
dbaas.orig_configuration_manager
@patch.object(dbaas.MySqlApp, 'get_auth_password', @patch.object(dbaas.MySqlApp, 'get_auth_password',
return_value='some_password') return_value='some_password')
@@ -1509,7 +1543,7 @@ class MySqlRootStatusTest(testtools.TestCase):
mock_rs = MagicMock() mock_rs = MagicMock()
mock_rs.rowcount = 0 mock_rs.rowcount = 0
with patch.object(mock_conn, 'execute', return_value=mock_rs): with patch.object(mock_conn, 'execute', return_value=mock_rs):
self.assertThat(MySqlRootAccess.is_root_enabled(), Equals(False)) self.assertThat(MySqlRootAccess().is_root_enabled(), Equals(False))
@patch.object(dbaas.MySqlApp, 'get_auth_password', @patch.object(dbaas.MySqlApp, 'get_auth_password',
return_value='some_password') return_value='some_password')
@@ -1518,7 +1552,7 @@ class MySqlRootStatusTest(testtools.TestCase):
with patch.object(mock_conn, 'execute', return_value=None): with patch.object(mock_conn, 'execute', return_value=None):
# invocation # invocation
user_ser = MySqlRootAccess.enable_root() user_ser = MySqlRootAccess().enable_root()
# verification # verification
self.assertThat(user_ser, Not(Is(None))) self.assertThat(user_ser, Not(Is(None)))
mock_conn.execute.assert_any_call(TextClauseMatcher('CREATE USER'), mock_conn.execute.assert_any_call(TextClauseMatcher('CREATE USER'),
@@ -1591,8 +1625,8 @@ class ServiceRegistryTest(testtools.TestCase):
self.assertEqual('trove.guestagent.datastore.mysql.' self.assertEqual('trove.guestagent.datastore.mysql.'
'manager.Manager', 'manager.Manager',
test_dict.get('mysql')) test_dict.get('mysql'))
self.assertEqual('trove.guestagent.datastore.mysql.' self.assertEqual('trove.guestagent.datastore.experimental.'
'manager.Manager', 'percona.manager.Manager',
test_dict.get('percona')) test_dict.get('percona'))
self.assertEqual('trove.guestagent.datastore.experimental.redis.' self.assertEqual('trove.guestagent.datastore.experimental.redis.'
'manager.Manager', 'manager.Manager',
@@ -1624,8 +1658,8 @@ class ServiceRegistryTest(testtools.TestCase):
self.assertEqual('trove.guestagent.datastore.mysql.' self.assertEqual('trove.guestagent.datastore.mysql.'
'manager.Manager123', 'manager.Manager123',
test_dict.get('mysql')) test_dict.get('mysql'))
self.assertEqual('trove.guestagent.datastore.mysql.' self.assertEqual('trove.guestagent.datastore.experimental.'
'manager.Manager', 'percona.manager.Manager',
test_dict.get('percona')) test_dict.get('percona'))
self.assertEqual('trove.guestagent.datastore.experimental.redis.' self.assertEqual('trove.guestagent.datastore.experimental.redis.'
'manager.Manager', 'manager.Manager',
@@ -1657,8 +1691,8 @@ class ServiceRegistryTest(testtools.TestCase):
self.assertEqual('trove.guestagent.datastore.mysql.' self.assertEqual('trove.guestagent.datastore.mysql.'
'manager.Manager', 'manager.Manager',
test_dict.get('mysql')) test_dict.get('mysql'))
self.assertEqual('trove.guestagent.datastore.mysql.' self.assertEqual('trove.guestagent.datastore.experimental.'
'manager.Manager', 'percona.manager.Manager',
test_dict.get('percona')) test_dict.get('percona'))
self.assertEqual('trove.guestagent.datastore.experimental.redis.' self.assertEqual('trove.guestagent.datastore.experimental.redis.'
'manager.Manager', 'manager.Manager',
@@ -1695,12 +1729,14 @@ class KeepAliveConnectionTest(testtools.TestCase):
def setUp(self): def setUp(self):
super(KeepAliveConnectionTest, self).setUp() super(KeepAliveConnectionTest, self).setUp()
self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout self.orig_utils_execute_with_timeout = \
dbaas_base.utils.execute_with_timeout
self.orig_LOG_err = dbaas.LOG self.orig_LOG_err = dbaas.LOG
def tearDown(self): def tearDown(self):
super(KeepAliveConnectionTest, self).tearDown() super(KeepAliveConnectionTest, self).tearDown()
dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout dbaas_base.utils.execute_with_timeout = \
self.orig_utils_execute_with_timeout
dbaas.LOG = self.orig_LOG_err dbaas.LOG = self.orig_LOG_err
def test_checkout_type_error(self): def test_checkout_type_error(self):
@@ -1855,9 +1891,10 @@ class MySqlAppStatusTest(testtools.TestCase):
def setUp(self): def setUp(self):
super(MySqlAppStatusTest, self).setUp() super(MySqlAppStatusTest, self).setUp()
util.init_db() util.init_db()
self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout self.orig_utils_execute_with_timeout = \
self.orig_load_mysqld_options = dbaas.load_mysqld_options dbaas_base.utils.execute_with_timeout
self.orig_dbaas_os_path_exists = dbaas.os.path.exists self.orig_load_mysqld_options = dbaas_base.load_mysqld_options
self.orig_dbaas_base_os_path_exists = dbaas_base.os.path.exists
self.orig_dbaas_time_sleep = time.sleep self.orig_dbaas_time_sleep = time.sleep
self.FAKE_ID = str(uuid4()) self.FAKE_ID = str(uuid4())
InstanceServiceStatus.create(instance_id=self.FAKE_ID, InstanceServiceStatus.create(instance_id=self.FAKE_ID,
@@ -1866,18 +1903,19 @@ class MySqlAppStatusTest(testtools.TestCase):
def tearDown(self): def tearDown(self):
super(MySqlAppStatusTest, self).tearDown() super(MySqlAppStatusTest, self).tearDown()
dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout dbaas_base.utils.execute_with_timeout = \
dbaas.load_mysqld_options = self.orig_load_mysqld_options self.orig_utils_execute_with_timeout
dbaas.os.path.exists = self.orig_dbaas_os_path_exists dbaas_base.load_mysqld_options = self.orig_load_mysqld_options
dbaas_base.os.path.exists = self.orig_dbaas_base_os_path_exists
time.sleep = self.orig_dbaas_time_sleep time.sleep = self.orig_dbaas_time_sleep
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete() InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
dbaas.CONF.guest_id = None dbaas.CONF.guest_id = None
def test_get_actual_db_status(self): def test_get_actual_db_status(self):
dbaas.utils.execute_with_timeout = Mock(return_value=(None, None)) dbaas_base.utils.execute_with_timeout = Mock(return_value=(None, None))
self.mySqlAppStatus = MySqlAppStatus() self.mySqlAppStatus = MySqlAppStatus.get()
status = self.mySqlAppStatus._get_actual_db_status() status = self.mySqlAppStatus._get_actual_db_status()
self.assertEqual(rd_instance.ServiceStatuses.RUNNING, status) self.assertEqual(rd_instance.ServiceStatuses.RUNNING, status)
@@ -1887,31 +1925,31 @@ class MySqlAppStatusTest(testtools.TestCase):
@patch.object(os.path, 'exists', return_value=True) @patch.object(os.path, 'exists', return_value=True)
def test_get_actual_db_status_error_crashed(self, mock_exists, def test_get_actual_db_status_error_crashed(self, mock_exists,
mock_execute): mock_execute):
dbaas.load_mysqld_options = Mock(return_value={}) dbaas_base.load_mysqld_options = Mock(return_value={})
self.mySqlAppStatus = MySqlAppStatus() self.mySqlAppStatus = MySqlAppStatus.get()
status = self.mySqlAppStatus._get_actual_db_status() status = self.mySqlAppStatus._get_actual_db_status()
self.assertEqual(rd_instance.ServiceStatuses.CRASHED, status) self.assertEqual(rd_instance.ServiceStatuses.CRASHED, status)
def test_get_actual_db_status_error_shutdown(self): def test_get_actual_db_status_error_shutdown(self):
mocked = Mock(side_effect=ProcessExecutionError()) mocked = Mock(side_effect=ProcessExecutionError())
dbaas.utils.execute_with_timeout = mocked dbaas_base.utils.execute_with_timeout = mocked
dbaas.load_mysqld_options = Mock(return_value={}) dbaas_base.load_mysqld_options = Mock(return_value={})
dbaas.os.path.exists = Mock(return_value=False) dbaas_base.os.path.exists = Mock(return_value=False)
self.mySqlAppStatus = MySqlAppStatus() self.mySqlAppStatus = MySqlAppStatus.get()
status = self.mySqlAppStatus._get_actual_db_status() status = self.mySqlAppStatus._get_actual_db_status()
self.assertEqual(rd_instance.ServiceStatuses.SHUTDOWN, status) self.assertEqual(rd_instance.ServiceStatuses.SHUTDOWN, status)
def test_get_actual_db_status_error_blocked(self): def test_get_actual_db_status_error_blocked(self):
dbaas.utils.execute_with_timeout = MagicMock( dbaas_base.utils.execute_with_timeout = MagicMock(
side_effect=[ProcessExecutionError(), ("some output", None)]) side_effect=[ProcessExecutionError(), ("some output", None)])
dbaas.load_mysqld_options = Mock() dbaas_base.load_mysqld_options = Mock()
dbaas.os.path.exists = Mock(return_value=True) dbaas_base.os.path.exists = Mock(return_value=True)
self.mySqlAppStatus = MySqlAppStatus() self.mySqlAppStatus = MySqlAppStatus.get()
status = self.mySqlAppStatus._get_actual_db_status() status = self.mySqlAppStatus._get_actual_db_status()
self.assertEqual(rd_instance.ServiceStatuses.BLOCKED, status) self.assertEqual(rd_instance.ServiceStatuses.BLOCKED, status)

View File

@@ -26,6 +26,8 @@ from trove.common.exception import ProcessExecutionError
from trove.common import instance as rd_instance from trove.common import instance as rd_instance
from trove.guestagent import backup from trove.guestagent import backup
from trove.guestagent.common import operating_system from trove.guestagent.common import operating_system
# TODO(atomic77) The test cases should be made configurable
# to make it easier to test the various derived datastores.
from trove.guestagent.datastore.mysql.manager import Manager from trove.guestagent.datastore.mysql.manager import Manager
import trove.guestagent.datastore.mysql.service as dbaas import trove.guestagent.datastore.mysql.service as dbaas
from trove.guestagent import dbaas as base_dbaas from trove.guestagent import dbaas as base_dbaas
@@ -55,9 +57,8 @@ class GuestAgentManagerTest(testtools.TestCase):
# set up common mock objects, etc. for replication testing # set up common mock objects, etc. for replication testing
self.patcher_gfvs = patch( self.patcher_gfvs = patch(
'trove.guestagent.dbaas.get_filesystem_volume_stats') 'trove.guestagent.dbaas.get_filesystem_volume_stats')
self.patcher_rs = patch( self.patcher_rs = patch(self.manager.replication_namespace + "." +
'trove.guestagent.datastore.mysql.manager.' self.manager.replication_strategy)
'REPLICATION_STRATEGY_CLASS')
self.mock_gfvs_class = self.patcher_gfvs.start() self.mock_gfvs_class = self.patcher_gfvs.start()
self.mock_rs_class = self.patcher_rs.start() self.mock_rs_class = self.patcher_rs.start()
self.repl_datastore_manager = 'mysql' self.repl_datastore_manager = 'mysql'