Implement MariaDB Clustering
For MariaDB 10.1, the Galera clustering is included as part of the base installation, so clustering for MariaDB is implemented within the MariaDB datastore directly (rather than as a separate datastore as was done with pxc). Change Manifest: - refactor galera clustering support from pxc to galera_common - incorporate the galera_common component into MariaDB datastore - a little cleanup of how pxc and mariadb were implementing the mysql_common component - refactored pxc unittests to pxc and galera specific tests - implemented unittests for MariaDB - implemented clustering unittests for MariaDB - mariadb_supported scenario tests run (except replication due to slave_of issues) - cluster scenario tests run successfully Change-Id: Ieae77cf9ff797472ef13f2532fff1e64bc654151 Implements-blueprint: mariadb-clustering
This commit is contained in:
parent
a2e505941e
commit
22c9c77be7
|
@ -680,16 +680,16 @@ pxc_opts = [
|
|||
help='Minimum number of members in PXC cluster.'),
|
||||
cfg.StrOpt('api_strategy',
|
||||
default='trove.common.strategies.cluster.experimental.'
|
||||
'pxc.api.PXCAPIStrategy',
|
||||
'galera_common.api.GaleraCommonAPIStrategy',
|
||||
help='Class that implements datastore-specific API logic.'),
|
||||
cfg.StrOpt('taskmanager_strategy',
|
||||
default='trove.common.strategies.cluster.experimental.pxc.'
|
||||
'taskmanager.PXCTaskManagerStrategy',
|
||||
default='trove.common.strategies.cluster.experimental.'
|
||||
'galera_common.taskmanager.GaleraCommonTaskManagerStrategy',
|
||||
help='Class that implements datastore-specific task manager '
|
||||
'logic.'),
|
||||
cfg.StrOpt('guestagent_strategy',
|
||||
default='trove.common.strategies.cluster.experimental.'
|
||||
'pxc.guestagent.PXCGuestAgentStrategy',
|
||||
'galera_common.guestagent.GaleraCommonGuestAgentStrategy',
|
||||
help='Class that implements datastore-specific Guest Agent API '
|
||||
'logic.'),
|
||||
cfg.StrOpt('root_controller',
|
||||
|
@ -702,6 +702,7 @@ pxc_opts = [
|
|||
'in order to be logged in the slow_query log.'),
|
||||
]
|
||||
|
||||
|
||||
# Redis
|
||||
redis_group = cfg.OptGroup(
|
||||
'redis', title='Redis options',
|
||||
|
@ -1182,7 +1183,7 @@ mariadb_group = cfg.OptGroup(
|
|||
'mariadb', title='MariaDB options',
|
||||
help="Oslo option group designed for MariaDB datastore")
|
||||
mariadb_opts = [
|
||||
cfg.ListOpt('tcp_ports', default=["3306"],
|
||||
cfg.ListOpt('tcp_ports', default=["3306", "4444", "4567", "4568"],
|
||||
help='List of TCP ports and/or port ranges to open '
|
||||
'in the security group (only applicable '
|
||||
'if trove_security_groups_support is True).'),
|
||||
|
@ -1250,6 +1251,24 @@ mariadb_opts = [
|
|||
cfg.IntOpt('guest_log_long_query_time', default=1000,
|
||||
help='The time in milliseconds that a statement must take in '
|
||||
'in order to be logged in the slow_query log.'),
|
||||
cfg.BoolOpt('cluster_support', default=True,
|
||||
help='Enable clusters to be created and managed.'),
|
||||
cfg.IntOpt('min_cluster_member_count', default=3,
|
||||
help='Minimum number of members in MariaDB cluster.'),
|
||||
cfg.StrOpt('api_strategy',
|
||||
default='trove.common.strategies.cluster.experimental.'
|
||||
'galera_common.api.GaleraCommonAPIStrategy',
|
||||
help='Class that implements datastore-specific API logic.'),
|
||||
cfg.StrOpt('taskmanager_strategy',
|
||||
default='trove.common.strategies.cluster.experimental.'
|
||||
'galera_common.taskmanager.GaleraCommonTaskManagerStrategy',
|
||||
help='Class that implements datastore-specific task manager '
|
||||
'logic.'),
|
||||
cfg.StrOpt('guestagent_strategy',
|
||||
default='trove.common.strategies.cluster.experimental.'
|
||||
'galera_common.guestagent.GaleraCommonGuestAgentStrategy',
|
||||
help='Class that implements datastore-specific Guest Agent API '
|
||||
'logic.'),
|
||||
]
|
||||
|
||||
# RPC version groups
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2016 Tesora Inc.
|
||||
# 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
|
||||
|
@ -15,13 +16,13 @@ from novaclient import exceptions as nova_exceptions
|
|||
from oslo_log import log as logging
|
||||
|
||||
|
||||
from trove.cluster import models
|
||||
from trove.cluster import models as cluster_models
|
||||
from trove.cluster.tasks import ClusterTasks
|
||||
from trove.cluster.views import ClusterView
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common import remote
|
||||
from trove.common.strategies.cluster import base
|
||||
from trove.common.strategies.cluster import base as cluster_base
|
||||
from trove.extensions.mgmt.clusters.views import MgmtClusterView
|
||||
from trove.instance.models import DBInstance
|
||||
from trove.instance.models import Instance
|
||||
|
@ -33,34 +34,34 @@ LOG = logging.getLogger(__name__)
|
|||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class PXCAPIStrategy(base.BaseAPIStrategy):
|
||||
class GaleraCommonAPIStrategy(cluster_base.BaseAPIStrategy):
|
||||
|
||||
@property
|
||||
def cluster_class(self):
|
||||
return PXCCluster
|
||||
return GaleraCommonCluster
|
||||
|
||||
@property
|
||||
def cluster_view_class(self):
|
||||
return PXCClusterView
|
||||
return GaleraCommonClusterView
|
||||
|
||||
@property
|
||||
def mgmt_cluster_view_class(self):
|
||||
return PXCMgmtClusterView
|
||||
return GaleraCommonMgmtClusterView
|
||||
|
||||
|
||||
class PXCCluster(models.Cluster):
|
||||
class GaleraCommonCluster(cluster_models.Cluster):
|
||||
|
||||
@staticmethod
|
||||
def _validate_cluster_instances(context, instances, datastore,
|
||||
datastore_version):
|
||||
"""Validate the flavor and volume"""
|
||||
pxc_conf = CONF.get(datastore_version.manager)
|
||||
ds_conf = CONF.get(datastore_version.manager)
|
||||
num_instances = len(instances)
|
||||
|
||||
# Check number of instances is at least min_cluster_member_count
|
||||
if num_instances < pxc_conf.min_cluster_member_count:
|
||||
if num_instances < ds_conf.min_cluster_member_count:
|
||||
raise exception.ClusterNumInstancesNotLargeEnough(
|
||||
num_instances=pxc_conf.min_cluster_member_count)
|
||||
num_instances=ds_conf.min_cluster_member_count)
|
||||
|
||||
# Checking flavors and get delta for quota check
|
||||
flavor_ids = [instance['flavor_id'] for instance in instances]
|
||||
|
@ -78,18 +79,18 @@ class PXCCluster(models.Cluster):
|
|||
volume_sizes = [instance['volume_size'] for instance in instances
|
||||
if instance.get('volume_size', None)]
|
||||
volume_size = None
|
||||
if pxc_conf.volume_support:
|
||||
if ds_conf.volume_support:
|
||||
if len(volume_sizes) != num_instances:
|
||||
raise exception.ClusterVolumeSizeRequired()
|
||||
if len(set(volume_sizes)) != 1:
|
||||
raise exception.ClusterVolumeSizesNotEqual()
|
||||
volume_size = volume_sizes[0]
|
||||
models.validate_volume_size(volume_size)
|
||||
cluster_models.validate_volume_size(volume_size)
|
||||
deltas['volumes'] = volume_size * num_instances
|
||||
else:
|
||||
if len(volume_sizes) > 0:
|
||||
raise exception.VolumeNotSupported()
|
||||
ephemeral_support = pxc_conf.device_path
|
||||
ephemeral_support = ds_conf.device_path
|
||||
if ephemeral_support and flavor.ephemeral == 0:
|
||||
raise exception.LocalStorageNotSpecified(flavor=flavor_id)
|
||||
|
||||
|
@ -140,11 +141,11 @@ class PXCCluster(models.Cluster):
|
|||
@classmethod
|
||||
def create(cls, context, name, datastore, datastore_version,
|
||||
instances, extended_properties):
|
||||
LOG.debug("Initiating PXC cluster creation.")
|
||||
LOG.debug("Initiating Galera cluster creation.")
|
||||
cls._validate_cluster_instances(context, instances, datastore,
|
||||
datastore_version)
|
||||
# Updating Cluster Task
|
||||
db_info = models.DBCluster.create(
|
||||
db_info = cluster_models.DBCluster.create(
|
||||
name=name, tenant_id=context.tenant,
|
||||
datastore_version_id=datastore_version.id,
|
||||
task_status=ClusterTasks.BUILDING_INITIAL)
|
||||
|
@ -156,7 +157,7 @@ class PXCCluster(models.Cluster):
|
|||
task_api.load(context, datastore_version.manager).create_cluster(
|
||||
db_info.id)
|
||||
|
||||
return PXCCluster(context, db_info, datastore, datastore_version)
|
||||
return cls(context, db_info, datastore, datastore_version)
|
||||
|
||||
def _get_cluster_network_interfaces(self):
|
||||
nova_client = remote.create_nova_client(self.context)
|
||||
|
@ -176,21 +177,23 @@ class PXCCluster(models.Cluster):
|
|||
datastore = self.ds
|
||||
datastore_version = self.ds_version
|
||||
|
||||
# Get the network of the existing cluster instances.
|
||||
interface_ids = self._get_cluster_network_interfaces()
|
||||
for instance in instances:
|
||||
instance["nics"] = interface_ids
|
||||
|
||||
db_info.update(task_status=ClusterTasks.GROWING_CLUSTER)
|
||||
try:
|
||||
# Get the network of the existing cluster instances.
|
||||
interface_ids = self._get_cluster_network_interfaces()
|
||||
for instance in instances:
|
||||
instance["nics"] = interface_ids
|
||||
|
||||
new_instances = self._create_instances(context, db_info,
|
||||
datastore, datastore_version,
|
||||
instances)
|
||||
new_instances = self._create_instances(
|
||||
context, db_info, datastore, datastore_version, instances)
|
||||
|
||||
task_api.load(context, datastore_version.manager).grow_cluster(
|
||||
db_info.id, [instance.id for instance in new_instances])
|
||||
task_api.load(context, datastore_version.manager).grow_cluster(
|
||||
db_info.id, [instance.id for instance in new_instances])
|
||||
except Exception:
|
||||
db_info.update(task_status=ClusterTasks.NONE)
|
||||
|
||||
return PXCCluster(context, db_info, datastore, datastore_version)
|
||||
return self.__class__(context, db_info,
|
||||
datastore, datastore_version)
|
||||
|
||||
def shrink(self, instances):
|
||||
"""Removes instances from a cluster."""
|
||||
|
@ -204,19 +207,25 @@ class PXCCluster(models.Cluster):
|
|||
raise exception.ClusterShrinkMustNotLeaveClusterEmpty()
|
||||
|
||||
self.db_info.update(task_status=ClusterTasks.SHRINKING_CLUSTER)
|
||||
task_api.load(self.context, self.ds_version.manager).shrink_cluster(
|
||||
self.db_info.id, [instance.id for instance in removal_instances])
|
||||
try:
|
||||
task_api.load(self.context, self.ds_version.manager
|
||||
).shrink_cluster(self.db_info.id,
|
||||
[instance.id
|
||||
for instance in removal_instances])
|
||||
except Exception:
|
||||
self.db_info.update(task_status=ClusterTasks.NONE)
|
||||
|
||||
return PXCCluster(self.context, self.db_info, self.ds, self.ds_version)
|
||||
return self.__class__(self.context, self.db_info,
|
||||
self.ds, self.ds_version)
|
||||
|
||||
|
||||
class PXCClusterView(ClusterView):
|
||||
class GaleraCommonClusterView(ClusterView):
|
||||
|
||||
def build_instances(self):
|
||||
return self._build_instances(['member'], ['member'])
|
||||
|
||||
|
||||
class PXCMgmtClusterView(MgmtClusterView):
|
||||
class GaleraCommonMgmtClusterView(MgmtClusterView):
|
||||
|
||||
def build_instances(self):
|
||||
return self._build_instances(['member'], ['member'])
|
|
@ -1,4 +1,5 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2016 Tesora Inc.
|
||||
# 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
|
||||
|
@ -14,7 +15,7 @@
|
|||
from oslo_log import log as logging
|
||||
|
||||
from trove.common import cfg
|
||||
from trove.common.strategies.cluster import base
|
||||
from trove.common.strategies.cluster import base as cluster_base
|
||||
from trove.guestagent import api as guest_api
|
||||
|
||||
|
||||
|
@ -22,19 +23,19 @@ LOG = logging.getLogger(__name__)
|
|||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class PXCGuestAgentStrategy(base.BaseGuestAgentStrategy):
|
||||
class GaleraCommonGuestAgentStrategy(cluster_base.BaseGuestAgentStrategy):
|
||||
|
||||
@property
|
||||
def guest_client_class(self):
|
||||
return PXCGuestAgentAPI
|
||||
return GaleraCommonGuestAgentAPI
|
||||
|
||||
|
||||
class PXCGuestAgentAPI(guest_api.API):
|
||||
class GaleraCommonGuestAgentAPI(guest_api.API):
|
||||
|
||||
def install_cluster(self, replication_user, cluster_configuration,
|
||||
bootstrap):
|
||||
"""Install the cluster."""
|
||||
LOG.debug("Installing PXC cluster.")
|
||||
LOG.debug("Installing Galera cluster.")
|
||||
self._call("install_cluster", CONF.cluster_usage_timeout,
|
||||
self.version_cap,
|
||||
replication_user=replication_user,
|
|
@ -1,4 +1,5 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2016 Tesora Inc.
|
||||
# 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
|
||||
|
@ -19,7 +20,7 @@ from trove.common.exception import PollTimeOut
|
|||
from trove.common.exception import TroveError
|
||||
from trove.common.i18n import _
|
||||
from trove.common.remote import create_nova_client
|
||||
from trove.common.strategies.cluster import base
|
||||
from trove.common.strategies.cluster import base as cluster_base
|
||||
from trove.common.template import ClusterConfigTemplate
|
||||
from trove.common import utils
|
||||
from trove.extensions.common import models as ext_models
|
||||
|
@ -32,10 +33,9 @@ import trove.taskmanager.models as task_models
|
|||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
USAGE_SLEEP_TIME = CONF.usage_sleep_time # seconds.
|
||||
|
||||
|
||||
class PXCTaskManagerStrategy(base.BaseTaskManagerStrategy):
|
||||
class GaleraCommonTaskManagerStrategy(cluster_base.BaseTaskManagerStrategy):
|
||||
|
||||
@property
|
||||
def task_manager_api_class(self):
|
||||
|
@ -43,10 +43,10 @@ class PXCTaskManagerStrategy(base.BaseTaskManagerStrategy):
|
|||
|
||||
@property
|
||||
def task_manager_cluster_tasks_class(self):
|
||||
return PXCClusterTasks
|
||||
return GaleraCommonClusterTasks
|
||||
|
||||
|
||||
class PXCClusterTasks(task_models.ClusterTasks):
|
||||
class GaleraCommonClusterTasks(task_models.ClusterTasks):
|
||||
|
||||
CLUSTER_REPLICATION_USER = "clusterrepuser"
|
||||
|
||||
|
@ -89,16 +89,16 @@ class PXCClusterTasks(task_models.ClusterTasks):
|
|||
for instance in instances]
|
||||
|
||||
# Create replication user and password for synchronizing the
|
||||
# PXC cluster
|
||||
# galera cluster
|
||||
replication_user = {
|
||||
"name": self.CLUSTER_REPLICATION_USER,
|
||||
"password": utils.generate_random_password(),
|
||||
}
|
||||
|
||||
# PXC cluster name must be unique and be shorter than a full
|
||||
# Galera cluster name must be unique and be shorter than a full
|
||||
# uuid string so we remove the hyphens and chop it off. It was
|
||||
# recommended to be 16 chars or less.
|
||||
# (this is not currently documented on PXC docs)
|
||||
# (this is not currently documented on Galera docs)
|
||||
cluster_name = utils.generate_uuid().replace("-", "")[:16]
|
||||
|
||||
LOG.debug("Configuring cluster configuration.")
|
||||
|
@ -163,7 +163,7 @@ class PXCClusterTasks(task_models.ClusterTasks):
|
|||
return
|
||||
|
||||
def grow_cluster(self, context, cluster_id, new_instance_ids):
|
||||
LOG.debug("Begin pxc grow_cluster for id: %s." % cluster_id)
|
||||
LOG.debug("Begin Galera grow_cluster for id: %s." % cluster_id)
|
||||
|
||||
def _grow_cluster():
|
||||
|
||||
|
@ -256,7 +256,7 @@ class PXCClusterTasks(task_models.ClusterTasks):
|
|||
LOG.debug("End grow_cluster for id: %s." % cluster_id)
|
||||
|
||||
def shrink_cluster(self, context, cluster_id, removal_instance_ids):
|
||||
LOG.debug("Begin pxc shrink_cluster for id: %s." % cluster_id)
|
||||
LOG.debug("Begin Galera shrink_cluster for id: %s." % cluster_id)
|
||||
|
||||
def _shrink_cluster():
|
||||
removal_instances = [Instance.load(context, instance_id)
|
|
@ -14,23 +14,16 @@
|
|||
# under the License.
|
||||
#
|
||||
|
||||
from oslo_utils import importutils
|
||||
from trove.guestagent.datastore.mysql_common import manager
|
||||
from trove.guestagent.datastore.experimental.mariadb import (
|
||||
service as mariadb_service)
|
||||
from trove.guestagent.datastore.galera_common import manager as galera_manager
|
||||
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
||||
|
||||
|
||||
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.MySqlManager):
|
||||
class Manager(galera_manager.GaleraManager):
|
||||
|
||||
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)
|
||||
super(Manager, self).__init__(
|
||||
mariadb_service.MariaDBApp,
|
||||
mysql_service.BaseMySqlAppStatus,
|
||||
mariadb_service.MariaDBAdmin)
|
||||
|
|
|
@ -15,27 +15,37 @@
|
|||
#
|
||||
|
||||
from oslo_log import log as logging
|
||||
from trove.guestagent.datastore.mysql_common import service
|
||||
|
||||
from trove.guestagent.datastore.galera_common import service as galera_service
|
||||
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KeepAliveConnection(service.BaseKeepAliveConnection):
|
||||
pass
|
||||
class MariaDBApp(galera_service.GaleraApp):
|
||||
|
||||
|
||||
class MySqlAppStatus(service.BaseMySqlAppStatus):
|
||||
pass
|
||||
|
||||
|
||||
class LocalSqlClient(service.BaseLocalSqlClient):
|
||||
pass
|
||||
|
||||
|
||||
class MySqlApp(service.BaseMySqlApp):
|
||||
def __init__(self, status):
|
||||
super(MySqlApp, self).__init__(status, LocalSqlClient,
|
||||
KeepAliveConnection)
|
||||
super(MariaDBApp, self).__init__(
|
||||
status, mysql_service.BaseLocalSqlClient,
|
||||
mysql_service.BaseKeepAliveConnection)
|
||||
|
||||
@property
|
||||
def mysql_service(self):
|
||||
result = super(MariaDBApp, self).mysql_service
|
||||
if result['type'] == 'sysvinit':
|
||||
result['cmd_bootstrap_galera_cluster'] = (
|
||||
"sudo service %s bootstrap"
|
||||
% result['service'])
|
||||
elif result['type'] == 'systemd':
|
||||
# TODO(mwj 2016/01/28): determine RHEL start for MariaDB Cluster
|
||||
result['cmd_bootstrap_galera_cluster'] = (
|
||||
"sudo systemctl start %s@bootstrap.service"
|
||||
% result['service'])
|
||||
return result
|
||||
|
||||
@property
|
||||
def cluster_configuration(self):
|
||||
return self.configuration_manager.get_value('galera')
|
||||
|
||||
def _get_slave_status(self):
|
||||
with self.local_sql_client(self.get_engine()) as client:
|
||||
|
@ -70,13 +80,15 @@ class MySqlApp(service.BaseMySqlApp):
|
|||
client.execute("SELECT MASTER_GTID_WAIT('%s')" % txn)
|
||||
|
||||
|
||||
class MySqlRootAccess(service.BaseMySqlRootAccess):
|
||||
class MariaDBRootAccess(mysql_service.BaseMySqlRootAccess):
|
||||
def __init__(self):
|
||||
super(MySqlRootAccess, self).__init__(LocalSqlClient,
|
||||
MySqlApp(MySqlAppStatus.get()))
|
||||
super(MariaDBRootAccess, self).__init__(
|
||||
mysql_service.BaseLocalSqlClient,
|
||||
MariaDBApp(mysql_service.BaseMySqlAppStatus.get()))
|
||||
|
||||
|
||||
class MySqlAdmin(service.BaseMySqlAdmin):
|
||||
class MariaDBAdmin(mysql_service.BaseMySqlAdmin):
|
||||
def __init__(self):
|
||||
super(MySqlAdmin, self).__init__(LocalSqlClient, MySqlRootAccess(),
|
||||
MySqlApp)
|
||||
super(MariaDBAdmin, self).__init__(
|
||||
mysql_service.BaseLocalSqlClient, MariaDBRootAccess(),
|
||||
MariaDBApp)
|
||||
|
|
|
@ -14,72 +14,14 @@
|
|||
# under the License.
|
||||
#
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from trove.common.i18n import _
|
||||
from trove.common import instance as rd_instance
|
||||
from trove.guestagent.datastore.mysql_common import manager
|
||||
from trove.guestagent.datastore.experimental.pxc import service as pxc_service
|
||||
from trove.guestagent.datastore.galera_common import manager
|
||||
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
||||
|
||||
|
||||
MYSQL_APP = ("trove.guestagent.datastore.experimental.pxc.service."
|
||||
"PXCApp")
|
||||
MYSQL_APP_STATUS = ("trove.guestagent.datastore.experimental.pxc.service."
|
||||
"PXCAppStatus")
|
||||
MYSQL_ADMIN = ("trove.guestagent.datastore.experimental.pxc.service."
|
||||
"PXCAdmin")
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Manager(manager.MySqlManager):
|
||||
class Manager(manager.GaleraManager):
|
||||
|
||||
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)
|
||||
|
||||
def do_prepare(self, context, packages, databases, memory_mb, users,
|
||||
device_path, mount_point, backup_info,
|
||||
config_contents, root_password, overrides,
|
||||
cluster_config, snapshot):
|
||||
self.volume_do_not_start_on_reboot = True
|
||||
super(Manager, self).do_prepare(
|
||||
context, packages, databases, memory_mb, users,
|
||||
device_path, mount_point, backup_info,
|
||||
config_contents, root_password, overrides,
|
||||
cluster_config, snapshot)
|
||||
|
||||
def install_cluster(self, context, replication_user, cluster_configuration,
|
||||
bootstrap):
|
||||
app = self.mysql_app(self.mysql_app_status.get())
|
||||
try:
|
||||
app.install_cluster(
|
||||
replication_user, cluster_configuration, bootstrap)
|
||||
LOG.debug("install_cluster call has finished.")
|
||||
except Exception:
|
||||
LOG.exception(_('Cluster installation failed.'))
|
||||
app.status.set_status(
|
||||
rd_instance.ServiceStatuses.FAILED)
|
||||
raise
|
||||
|
||||
def reset_admin_password(self, context, admin_password):
|
||||
LOG.debug("Storing the admin password on the instance.")
|
||||
app = self.mysql_app(self.mysql_app_status.get())
|
||||
app.reset_admin_password(admin_password)
|
||||
|
||||
def get_cluster_context(self, context):
|
||||
LOG.debug("Getting the cluster context.")
|
||||
app = self.mysql_app(self.mysql_app_status.get())
|
||||
return app.get_cluster_context()
|
||||
|
||||
def write_cluster_configuration_overrides(self, context,
|
||||
cluster_configuration):
|
||||
LOG.debug("Apply the updated cluster configuration.")
|
||||
app = self.mysql_app(self.mysql_app_status.get())
|
||||
app.write_cluster_configuration_overrides(cluster_configuration)
|
||||
|
||||
def enable_root_with_password(self, context, root_password=None):
|
||||
return self.mysql_admin().enable_root(root_password)
|
||||
super(Manager, self).__init__(pxc_service.PXCApp,
|
||||
mysql_service.BaseMySqlAppStatus,
|
||||
pxc_service.PXCAdmin)
|
||||
|
|
|
@ -18,66 +18,58 @@ from oslo_log import log as logging
|
|||
import sqlalchemy
|
||||
from sqlalchemy.sql.expression import text
|
||||
|
||||
from trove.common import cfg
|
||||
from trove.common.i18n import _
|
||||
from trove.common import utils
|
||||
from trove.guestagent.common import sql_query
|
||||
from trove.guestagent.datastore.experimental.pxc import system
|
||||
from trove.guestagent.datastore.mysql_common import service
|
||||
|
||||
from trove.common import utils as utils
|
||||
from trove.guestagent.datastore.galera_common import service as galera_service
|
||||
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = service.CONF
|
||||
|
||||
CNF_CLUSTER = "cluster"
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class KeepAliveConnection(service.BaseKeepAliveConnection):
|
||||
pass
|
||||
class PXCApp(galera_service.GaleraApp):
|
||||
|
||||
|
||||
class PXCAppStatus(service.BaseMySqlAppStatus):
|
||||
pass
|
||||
|
||||
|
||||
class LocalSqlClient(service.BaseLocalSqlClient):
|
||||
pass
|
||||
|
||||
|
||||
class PXCApp(service.BaseMySqlApp):
|
||||
def __init__(self, status):
|
||||
super(PXCApp, self).__init__(status, LocalSqlClient,
|
||||
KeepAliveConnection)
|
||||
super(PXCApp, self).__init__(
|
||||
status, mysql_service.BaseLocalSqlClient,
|
||||
mysql_service.BaseKeepAliveConnection)
|
||||
|
||||
def _test_mysql(self):
|
||||
engine = sqlalchemy.create_engine("mysql://root:@localhost:3306",
|
||||
echo=True)
|
||||
try:
|
||||
with LocalSqlClient(engine) as client:
|
||||
out = client.execute(text("select 1;"))
|
||||
for line in out:
|
||||
LOG.debug("line: %s" % line)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
@property
|
||||
def mysql_service(self):
|
||||
result = super(PXCApp, self).mysql_service
|
||||
if result['type'] == 'sysvinit':
|
||||
result['cmd_bootstrap_galera_cluster'] = (
|
||||
"sudo service %s bootstrap-pxc" % result['service'])
|
||||
elif result['type'] == 'systemd':
|
||||
result['cmd_bootstrap_galera_cluster'] = (
|
||||
"sudo systemctl start %s@bootstrap.service"
|
||||
% result['service'])
|
||||
return result
|
||||
|
||||
def _wait_for_mysql_to_be_really_alive(self, max_time):
|
||||
utils.poll_until(self._test_mysql, sleep_time=3, time_out=max_time)
|
||||
@property
|
||||
def cluster_configuration(self):
|
||||
return self.configuration_manager.get_value('mysqld')
|
||||
|
||||
def secure(self, config_contents):
|
||||
LOG.info(_("Generating admin password."))
|
||||
admin_password = utils.generate_random_password()
|
||||
service.clear_expired_password()
|
||||
mysql_service.clear_expired_password()
|
||||
engine = sqlalchemy.create_engine("mysql://root:@localhost:3306",
|
||||
echo=True)
|
||||
with LocalSqlClient(engine) as client:
|
||||
with self.local_sql_client(engine) as client:
|
||||
self._remove_anonymous_user(client)
|
||||
self._create_admin_user(client, admin_password)
|
||||
|
||||
self.stop_db()
|
||||
|
||||
self._reset_configuration(config_contents, admin_password)
|
||||
self.start_mysql()
|
||||
|
||||
# TODO(cp16net) figure out reason for PXC not updating the password
|
||||
try:
|
||||
with LocalSqlClient(engine) as client:
|
||||
with self.local_sql_client(engine) as client:
|
||||
query = text("select Host, User from mysql.user;")
|
||||
client.execute(query)
|
||||
except Exception:
|
||||
|
@ -87,7 +79,7 @@ class PXCApp(service.BaseMySqlApp):
|
|||
# removing the annon users.
|
||||
self._wait_for_mysql_to_be_really_alive(
|
||||
CONF.timeout_wait_for_service)
|
||||
with LocalSqlClient(engine) as client:
|
||||
with self.local_sql_client(engine) as client:
|
||||
self._create_admin_user(client, admin_password)
|
||||
self.stop_db()
|
||||
|
||||
|
@ -97,68 +89,16 @@ class PXCApp(service.BaseMySqlApp):
|
|||
CONF.timeout_wait_for_service)
|
||||
LOG.debug("MySQL secure complete.")
|
||||
|
||||
def _grant_cluster_replication_privilege(self, replication_user):
|
||||
LOG.info(_("Granting Replication Slave privilege."))
|
||||
with self.local_sql_client(self.get_engine()) as client:
|
||||
perms = ['REPLICATION CLIENT', 'RELOAD', 'LOCK TABLES']
|
||||
g = sql_query.Grant(permissions=perms,
|
||||
user=replication_user['name'],
|
||||
clear=replication_user['password'])
|
||||
t = text(str(g))
|
||||
client.execute(t)
|
||||
|
||||
def _bootstrap_cluster(self, timeout=120):
|
||||
LOG.info(_("Bootstraping cluster."))
|
||||
try:
|
||||
mysql_service = system.service_discovery(
|
||||
service.MYSQL_SERVICE_CANDIDATES)
|
||||
utils.execute_with_timeout(
|
||||
mysql_service['cmd_bootstrap_pxc_cluster'],
|
||||
shell=True, timeout=timeout)
|
||||
except KeyError:
|
||||
LOG.exception(_("Error bootstrapping cluster."))
|
||||
raise RuntimeError(_("Service is not discovered."))
|
||||
class PXCRootAccess(mysql_service.BaseMySqlRootAccess):
|
||||
|
||||
def write_cluster_configuration_overrides(self, cluster_configuration):
|
||||
self.configuration_manager.apply_system_override(
|
||||
cluster_configuration, CNF_CLUSTER)
|
||||
|
||||
def install_cluster(self, replication_user, cluster_configuration,
|
||||
bootstrap=False):
|
||||
LOG.info(_("Installing cluster configuration."))
|
||||
self._grant_cluster_replication_privilege(replication_user)
|
||||
self.stop_db()
|
||||
self.write_cluster_configuration_overrides(cluster_configuration)
|
||||
self.wipe_ib_logfiles()
|
||||
LOG.debug("bootstrap the instance? : %s" % bootstrap)
|
||||
# Have to wait to sync up the joiner instances with the donor instance.
|
||||
if bootstrap:
|
||||
self._bootstrap_cluster(timeout=CONF.restore_usage_timeout)
|
||||
else:
|
||||
self.start_mysql(timeout=CONF.restore_usage_timeout)
|
||||
|
||||
def get_cluster_context(self):
|
||||
auth = self.configuration_manager.get_value('mysqld').get(
|
||||
"wsrep_sst_auth").replace('"', '')
|
||||
cluster_name = self.configuration_manager.get_value(
|
||||
'mysqld').get("wsrep_cluster_name")
|
||||
return {
|
||||
'replication_user': {
|
||||
'name': auth.split(":")[0],
|
||||
'password': auth.split(":")[1],
|
||||
},
|
||||
'cluster_name': cluster_name,
|
||||
'admin_password': self.get_auth_password()
|
||||
}
|
||||
|
||||
|
||||
class PXCRootAccess(service.BaseMySqlRootAccess):
|
||||
def __init__(self):
|
||||
super(PXCRootAccess, self).__init__(LocalSqlClient,
|
||||
PXCApp(PXCAppStatus.get()))
|
||||
super(PXCRootAccess, self).__init__(
|
||||
mysql_service.BaseLocalSqlClient,
|
||||
PXCApp(mysql_service.BaseMySqlAppStatus.get()))
|
||||
|
||||
|
||||
class PXCAdmin(service.BaseMySqlAdmin):
|
||||
class PXCAdmin(mysql_service.BaseMySqlAdmin):
|
||||
def __init__(self):
|
||||
super(PXCAdmin, self).__init__(LocalSqlClient, PXCRootAccess(),
|
||||
PXCApp)
|
||||
super(PXCAdmin, self).__init__(
|
||||
mysql_service.BaseLocalSqlClient, PXCRootAccess(), PXCApp)
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.common import operating_system
|
||||
|
||||
|
||||
def service_discovery(service_candidates):
|
||||
result = operating_system.service_discovery(service_candidates)
|
||||
if result['type'] == 'sysvinit':
|
||||
result['cmd_bootstrap_pxc_cluster'] = ("sudo service %s bootstrap-pxc"
|
||||
% result['service'])
|
||||
elif result['type'] == 'systemd':
|
||||
result['cmd_bootstrap_pxc_cluster'] = ("sudo systemctl start "
|
||||
"%s@bootstrap.service"
|
||||
% result['service'])
|
||||
return result
|
|
@ -0,0 +1,81 @@
|
|||
# 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 oslo_log import log as logging
|
||||
|
||||
from trove.common.i18n import _
|
||||
from trove.common import instance as rd_instance
|
||||
from trove.guestagent.datastore.mysql_common import manager
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GaleraManager(manager.MySqlManager):
|
||||
|
||||
def __init__(self, mysql_app, mysql_app_status, mysql_admin,
|
||||
manager_name='galera'):
|
||||
|
||||
super(GaleraManager, self).__init__(
|
||||
mysql_app, mysql_app_status, mysql_admin, manager_name)
|
||||
self._mysql_app = mysql_app
|
||||
self._mysql_app_status = mysql_app_status
|
||||
self._mysql_admin = mysql_admin
|
||||
|
||||
self.volume_do_not_start_on_reboot = False
|
||||
|
||||
def do_prepare(self, context, packages, databases, memory_mb, users,
|
||||
device_path, mount_point, backup_info,
|
||||
config_contents, root_password, overrides,
|
||||
cluster_config, snapshot):
|
||||
self.volume_do_not_start_on_reboot = True
|
||||
super(GaleraManager, self).do_prepare(
|
||||
context, packages, databases, memory_mb, users,
|
||||
device_path, mount_point, backup_info,
|
||||
config_contents, root_password, overrides,
|
||||
cluster_config, snapshot)
|
||||
|
||||
def install_cluster(self, context, replication_user, cluster_configuration,
|
||||
bootstrap):
|
||||
app = self.mysql_app(self.mysql_app_status.get())
|
||||
try:
|
||||
app.install_cluster(
|
||||
replication_user, cluster_configuration, bootstrap)
|
||||
LOG.debug("install_cluster call has finished.")
|
||||
except Exception:
|
||||
LOG.exception(_('Cluster installation failed.'))
|
||||
app.status.set_status(
|
||||
rd_instance.ServiceStatuses.FAILED)
|
||||
raise
|
||||
|
||||
def reset_admin_password(self, context, admin_password):
|
||||
LOG.debug("Storing the admin password on the instance.")
|
||||
app = self.mysql_app(self.mysql_app_status.get())
|
||||
app.reset_admin_password(admin_password)
|
||||
|
||||
def get_cluster_context(self, context):
|
||||
LOG.debug("Getting the cluster context.")
|
||||
app = self.mysql_app(self.mysql_app_status.get())
|
||||
return app.get_cluster_context()
|
||||
|
||||
def write_cluster_configuration_overrides(self, context,
|
||||
cluster_configuration):
|
||||
LOG.debug("Apply the updated cluster configuration.")
|
||||
app = self.mysql_app(self.mysql_app_status.get())
|
||||
app.write_cluster_configuration_overrides(cluster_configuration)
|
||||
|
||||
def enable_root_with_password(self, context, root_password=None):
|
||||
return self.mysql_admin().enable_root(root_password)
|
|
@ -0,0 +1,109 @@
|
|||
# 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.
|
||||
#
|
||||
|
||||
import abc
|
||||
|
||||
from oslo_log import log as logging
|
||||
import sqlalchemy
|
||||
from sqlalchemy.sql.expression import text
|
||||
|
||||
from trove.common.i18n import _
|
||||
from trove.common import utils
|
||||
from trove.guestagent.common import sql_query
|
||||
from trove.guestagent.datastore.mysql_common import service
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = service.CONF
|
||||
|
||||
|
||||
class GaleraApp(service.BaseMySqlApp):
|
||||
|
||||
def __init__(self, status, local_sql_client, keep_alive_connection_cls):
|
||||
super(GaleraApp, self).__init__(status, local_sql_client,
|
||||
keep_alive_connection_cls)
|
||||
|
||||
def _test_mysql(self):
|
||||
engine = sqlalchemy.create_engine("mysql://root:@localhost:3306",
|
||||
echo=True)
|
||||
try:
|
||||
with self.local_sql_client(engine) as client:
|
||||
out = client.execute(text("select 1;"))
|
||||
for line in out:
|
||||
LOG.debug("line: %s" % line)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _wait_for_mysql_to_be_really_alive(self, max_time):
|
||||
utils.poll_until(self._test_mysql, sleep_time=3, time_out=max_time)
|
||||
|
||||
def _grant_cluster_replication_privilege(self, replication_user):
|
||||
LOG.info(_("Granting Replication Slave privilege."))
|
||||
with self.local_sql_client(self.get_engine()) as client:
|
||||
perms = ['REPLICATION CLIENT', 'RELOAD', 'LOCK TABLES']
|
||||
g = sql_query.Grant(permissions=perms,
|
||||
user=replication_user['name'],
|
||||
clear=replication_user['password'])
|
||||
t = text(str(g))
|
||||
client.execute(t)
|
||||
|
||||
def _bootstrap_cluster(self, timeout=120):
|
||||
LOG.info(_("Bootstraping cluster."))
|
||||
try:
|
||||
utils.execute_with_timeout(
|
||||
self.mysql_service['cmd_bootstrap_galera_cluster'],
|
||||
shell=True, timeout=timeout)
|
||||
except KeyError:
|
||||
LOG.exception(_("Error bootstrapping cluster."))
|
||||
raise RuntimeError(_("Service is not discovered."))
|
||||
|
||||
def write_cluster_configuration_overrides(self, cluster_configuration):
|
||||
self.configuration_manager.apply_system_override(
|
||||
cluster_configuration, 'cluster')
|
||||
|
||||
def install_cluster(self, replication_user, cluster_configuration,
|
||||
bootstrap=False):
|
||||
LOG.info(_("Installing cluster configuration."))
|
||||
self._grant_cluster_replication_privilege(replication_user)
|
||||
self.stop_db()
|
||||
self.write_cluster_configuration_overrides(cluster_configuration)
|
||||
self.wipe_ib_logfiles()
|
||||
LOG.debug("bootstrap the instance? : %s" % bootstrap)
|
||||
# Have to wait to sync up the joiner instances with the donor instance.
|
||||
if bootstrap:
|
||||
self._bootstrap_cluster(timeout=CONF.restore_usage_timeout)
|
||||
else:
|
||||
self.start_mysql(timeout=CONF.restore_usage_timeout)
|
||||
|
||||
@abc.abstractproperty
|
||||
def cluster_configuration(self):
|
||||
"""
|
||||
Returns the cluster section from the configuration manager.
|
||||
"""
|
||||
|
||||
def get_cluster_context(self):
|
||||
auth = self.cluster_configuration.get(
|
||||
"wsrep_sst_auth").replace('"', '')
|
||||
cluster_name = self.cluster_configuration.get("wsrep_cluster_name")
|
||||
return {
|
||||
'replication_user': {
|
||||
'name': auth.split(":")[0],
|
||||
'password': auth.split(":")[1],
|
||||
},
|
||||
'cluster_name': cluster_name,
|
||||
'admin_password': self.get_auth_password()
|
||||
}
|
|
@ -68,7 +68,6 @@ OS_NAME = operating_system.get_os()
|
|||
MYSQL_CONFIG = {operating_system.REDHAT: "/etc/my.cnf",
|
||||
operating_system.DEBIAN: "/etc/mysql/my.cnf",
|
||||
operating_system.SUSE: "/etc/my.cnf"}[OS_NAME]
|
||||
MYSQL_SERVICE_CANDIDATES = ["mysql", "mysqld", "mysql-server"]
|
||||
MYSQL_BIN_CANDIDATES = ["/usr/sbin/mysqld", "/usr/libexec/mysqld"]
|
||||
MYSQL_OWNER = 'mysql'
|
||||
CNF_EXT = 'cnf'
|
||||
|
@ -588,6 +587,11 @@ class BaseMySqlApp(object):
|
|||
def keep_alive_connection_cls(self):
|
||||
return self._keep_alive_connection_cls
|
||||
|
||||
@property
|
||||
def mysql_service(self):
|
||||
MYSQL_SERVICE_CANDIDATES = ["mysql", "mysqld", "mysql-server"]
|
||||
return operating_system.service_discovery(MYSQL_SERVICE_CANDIDATES)
|
||||
|
||||
configuration_manager = ConfigurationManager(
|
||||
MYSQL_CONFIG, MYSQL_OWNER, MYSQL_OWNER, CFG_CODEC, requires_root=True,
|
||||
override_strategy=ImportOverrideStrategy(CNF_INCLUDE_DIR, CNF_EXT))
|
||||
|
@ -737,18 +741,15 @@ class BaseMySqlApp(object):
|
|||
def _enable_mysql_on_boot(self):
|
||||
LOG.debug("Enabling MySQL on boot.")
|
||||
try:
|
||||
mysql_service = operating_system.service_discovery(
|
||||
MYSQL_SERVICE_CANDIDATES)
|
||||
utils.execute_with_timeout(mysql_service['cmd_enable'], shell=True)
|
||||
utils.execute_with_timeout(self.mysql_service['cmd_enable'],
|
||||
shell=True)
|
||||
except KeyError:
|
||||
LOG.exception(_("Error enabling MySQL start on boot."))
|
||||
raise RuntimeError("Service is not discovered.")
|
||||
|
||||
def _disable_mysql_on_boot(self):
|
||||
try:
|
||||
mysql_service = operating_system.service_discovery(
|
||||
MYSQL_SERVICE_CANDIDATES)
|
||||
utils.execute_with_timeout(mysql_service['cmd_disable'],
|
||||
utils.execute_with_timeout(self.mysql_service['cmd_disable'],
|
||||
shell=True)
|
||||
except KeyError:
|
||||
LOG.exception(_("Error disabling MySQL start on boot."))
|
||||
|
@ -759,9 +760,8 @@ class BaseMySqlApp(object):
|
|||
if do_not_start_on_reboot:
|
||||
self._disable_mysql_on_boot()
|
||||
try:
|
||||
mysql_service = operating_system.service_discovery(
|
||||
MYSQL_SERVICE_CANDIDATES)
|
||||
utils.execute_with_timeout(mysql_service['cmd_stop'], shell=True)
|
||||
utils.execute_with_timeout(self.mysql_service['cmd_stop'],
|
||||
shell=True)
|
||||
except KeyError:
|
||||
LOG.exception(_("Error stopping MySQL."))
|
||||
raise RuntimeError("Service is not discovered.")
|
||||
|
@ -951,10 +951,8 @@ class BaseMySqlApp(object):
|
|||
self._enable_mysql_on_boot()
|
||||
|
||||
try:
|
||||
mysql_service = operating_system.service_discovery(
|
||||
MYSQL_SERVICE_CANDIDATES)
|
||||
utils.execute_with_timeout(mysql_service['cmd_start'], shell=True,
|
||||
timeout=timeout)
|
||||
utils.execute_with_timeout(self.mysql_service['cmd_start'],
|
||||
shell=True, timeout=timeout)
|
||||
except KeyError:
|
||||
raise RuntimeError("Service is not discovered.")
|
||||
except exception.ProcessExecutionError:
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
[mysqld]
|
||||
bind-address=0.0.0.0
|
||||
default-storage-engine=innodb
|
||||
|
||||
[galera]
|
||||
binlog_format=ROW
|
||||
innodb_autoinc_lock_mode=2
|
||||
innodb_flush_log_at_trx_commit=0
|
||||
innodb_doublewrite=1
|
||||
query_cache_size=0
|
||||
wsrep_on=ON
|
||||
wsrep_slave_threads=8
|
||||
wsrep_provider=/usr/lib/libgalera_smm.so
|
||||
wsrep_provider_options="gcache.size={{ (128 * flavor['ram']/512)|int }}M; gcache.page_size=1G"
|
||||
|
||||
wsrep_sst_method=rsync
|
||||
wsrep_sst_auth="{{ replication_user_pass }}"
|
||||
|
||||
wsrep_cluster_address="gcomm://{{ cluster_ips }}"
|
||||
|
||||
wsrep_cluster_name={{ cluster_name }}
|
||||
wsrep_node_name={{ instance_name }}
|
||||
wsrep_node_address={{ instance_ip }}
|
|
@ -193,10 +193,13 @@ register(["couchdb_supported"], common_groups)
|
|||
register(["postgresql_supported"], common_groups,
|
||||
backup_groups, database_actions_groups, configuration_groups,
|
||||
root_actions_groups, user_actions_groups)
|
||||
register(["mariadb_supported", "mysql_supported", "percona_supported"],
|
||||
common_groups,
|
||||
register(["mysql_supported", "percona_supported"], common_groups,
|
||||
backup_groups, configuration_groups, database_actions_groups,
|
||||
replication_groups, root_actions_groups, user_actions_groups)
|
||||
register(["mariadb_supported"], common_groups,
|
||||
backup_groups, cluster_actions_groups, configuration_groups,
|
||||
database_actions_groups, replication_groups, root_actions_groups,
|
||||
user_actions_groups)
|
||||
register(["mongodb_supported"], common_groups,
|
||||
backup_groups, cluster_actions_groups, configuration_groups,
|
||||
database_actions_groups, root_actions_groups, user_actions_groups)
|
||||
|
|
|
@ -57,6 +57,7 @@ class CassandraClient(object):
|
|||
class CassandraHelper(TestHelper):
|
||||
|
||||
DATA_COLUMN_NAME = 'value'
|
||||
cluster_node_count = 2
|
||||
|
||||
def __init__(self, expected_override_name):
|
||||
super(CassandraHelper, self).__init__(expected_override_name)
|
||||
|
|
|
@ -18,6 +18,8 @@ from trove.tests.scenario.helpers.mysql_helper import MysqlHelper
|
|||
|
||||
class MariadbHelper(MysqlHelper):
|
||||
|
||||
cluster_node_count = 3
|
||||
|
||||
def __init__(self, expected_override_name):
|
||||
super(MariadbHelper, self).__init__(expected_override_name)
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ from trove.tests.scenario.helpers.test_helper import TestHelper
|
|||
|
||||
class MongodbHelper(TestHelper):
|
||||
|
||||
cluster_node_count = 2
|
||||
|
||||
def __init__(self, expected_override_name):
|
||||
super(MongodbHelper, self).__init__(expected_override_name)
|
||||
|
||||
|
|
|
@ -18,5 +18,7 @@ from trove.tests.scenario.helpers.mysql_helper import MysqlHelper
|
|||
|
||||
class PxcHelper(MysqlHelper):
|
||||
|
||||
cluster_node_count = 3
|
||||
|
||||
def __init__(self, expected_override_name):
|
||||
super(PxcHelper, self).__init__(expected_override_name)
|
||||
|
|
|
@ -22,6 +22,8 @@ from trove.tests.scenario.runners.test_runners import TestRunner
|
|||
|
||||
class RedisHelper(TestHelper):
|
||||
|
||||
cluster_node_count = 2
|
||||
|
||||
def __init__(self, expected_override_name):
|
||||
super(RedisHelper, self).__init__(expected_override_name)
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import os
|
|||
from proboscis import SkipTest
|
||||
import time as timer
|
||||
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common.utils import poll_until
|
||||
from trove.tests.scenario.helpers.test_helper import DataType
|
||||
|
@ -26,6 +27,9 @@ from trove.tests.util.check import TypeCheck
|
|||
from troveclient.compat import exceptions
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class ClusterActionsRunner(TestRunner):
|
||||
|
||||
USE_CLUSTER_ID_FLAG = 'TESTS_USE_CLUSTER_ID'
|
||||
|
@ -47,9 +51,12 @@ class ClusterActionsRunner(TestRunner):
|
|||
def has_do_not_delete_cluster(self):
|
||||
return self.has_env_flag(self.DO_NOT_DELETE_CLUSTER_FLAG)
|
||||
|
||||
def run_cluster_create(self, num_nodes=2, expected_task_name='BUILDING',
|
||||
def run_cluster_create(self, num_nodes=None, expected_task_name='BUILDING',
|
||||
expected_instance_states=['BUILD', 'ACTIVE'],
|
||||
expected_http_code=200):
|
||||
if not num_nodes:
|
||||
num_nodes = self.test_helper.cluster_node_count
|
||||
|
||||
instances_def = [
|
||||
self.build_flavor(
|
||||
flavor_id=self.instance_info.dbaas_flavor_href,
|
||||
|
@ -330,23 +337,19 @@ class ClusterActionsRunner(TestRunner):
|
|||
self.assert_client_code(404)
|
||||
|
||||
|
||||
class MariadbClusterActionsRunner(ClusterActionsRunner):
|
||||
|
||||
def run_cluster_root_enable(self):
|
||||
raise SkipTest("Operation is currently not supported.")
|
||||
|
||||
|
||||
class RedisClusterActionsRunner(ClusterActionsRunner):
|
||||
|
||||
def run_cluster_root_enable(self):
|
||||
raise SkipTest("Operation is currently not supported.")
|
||||
|
||||
|
||||
class MongodbClusterActionsRunner(ClusterActionsRunner):
|
||||
|
||||
def run_cluster_create(self, num_nodes=3, expected_task_name='BUILDING',
|
||||
expected_instance_states=['BUILD', 'ACTIVE'],
|
||||
expected_http_code=200):
|
||||
super(MongodbClusterActionsRunner, self).run_cluster_create(
|
||||
num_nodes=num_nodes, expected_task_name=expected_task_name,
|
||||
expected_instance_states=expected_instance_states,
|
||||
expected_http_code=expected_http_code)
|
||||
|
||||
|
||||
class PxcClusterActionsRunner(ClusterActionsRunner):
|
||||
|
||||
def run_cluster_create(self, num_nodes=3, expected_task_name='BUILDING',
|
||||
expected_instance_states=['BUILD', 'ACTIVE'],
|
||||
expected_http_code=200):
|
||||
super(PxcClusterActionsRunner, self).run_cluster_create(
|
||||
num_nodes=num_nodes, expected_task_name=expected_task_name,
|
||||
expected_instance_states=expected_instance_states,
|
||||
expected_http_code=expected_http_code)
|
||||
def run_cluster_root_enable(self):
|
||||
raise SkipTest("Operation is currently not supported.")
|
||||
|
|
|
@ -15,15 +15,17 @@ import uuid
|
|||
|
||||
from mock import Mock
|
||||
from mock import patch
|
||||
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
|
||||
from trove.cluster.models import Cluster
|
||||
from trove.cluster.models import ClusterTasks
|
||||
from trove.cluster.models import DBCluster
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common import remote
|
||||
from trove.common.strategies.cluster.experimental.pxc import (
|
||||
api as pxc_api)
|
||||
from trove.common.strategies.cluster.experimental.galera_common import (
|
||||
api as galera_api)
|
||||
from trove.instance import models as inst_models
|
||||
from trove.quota.quota import QUOTAS
|
||||
from trove.taskmanager import api as task_api
|
||||
|
@ -61,9 +63,8 @@ class ClusterTest(trove_testtools.TestCase):
|
|||
self.dv = Mock()
|
||||
self.dv.manager = "pxc"
|
||||
self.datastore_version = self.dv
|
||||
self.cluster = pxc_api.PXCCluster(self.context, self.db_info,
|
||||
self.datastore,
|
||||
self.datastore_version)
|
||||
self.cluster = galera_api.GaleraCommonCluster(
|
||||
self.context, self.db_info, self.datastore, self.datastore_version)
|
||||
self.instances = [{'volume_size': 1, 'flavor_id': '1234'},
|
||||
{'volume_size': 1, 'flavor_id': '1234'},
|
||||
{'volume_size': 1, 'flavor_id': '1234'}]
|
||||
|
@ -130,7 +131,7 @@ class ClusterTest(trove_testtools.TestCase):
|
|||
)
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
@patch.object(pxc_api, 'CONF')
|
||||
@patch.object(galera_api, 'CONF')
|
||||
def test_create_storage_specified_with_no_volume_support(self,
|
||||
mock_conf,
|
||||
mock_client):
|
||||
|
@ -150,7 +151,7 @@ class ClusterTest(trove_testtools.TestCase):
|
|||
)
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
@patch.object(pxc_api, 'CONF')
|
||||
@patch.object(galera_api, 'CONF')
|
||||
def test_create_storage_not_specified_and_no_ephemeral_flavor(self,
|
||||
mock_conf,
|
||||
mock_client):
|
||||
|
@ -218,8 +219,30 @@ class ClusterTest(trove_testtools.TestCase):
|
|||
mock_db_create.return_value.id)
|
||||
self.assertEqual(3, mock_ins_create.call_count)
|
||||
|
||||
@patch.object(inst_models.Instance, 'create')
|
||||
@patch.object(DBCluster, 'create')
|
||||
@patch.object(task_api, 'load')
|
||||
@patch.object(QUOTAS, 'check_quotas')
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
def test_create_over_limit(self, mock_client, mock_check_quotas,
|
||||
mock_task_api, mock_db_create, mock_ins_create):
|
||||
instances = [{'volume_size': 1, 'flavor_id': '1234'},
|
||||
{'volume_size': 1, 'flavor_id': '1234'},
|
||||
{'volume_size': 1, 'flavor_id': '1234'},
|
||||
{'volume_size': 1, 'flavor_id': '1234'}]
|
||||
flavors = Mock()
|
||||
mock_client.return_value.flavors = flavors
|
||||
self.cluster.create(Mock(),
|
||||
self.cluster_name,
|
||||
self.datastore,
|
||||
self.datastore_version,
|
||||
instances, {})
|
||||
mock_task_api.return_value.create_cluster.assert_called_with(
|
||||
mock_db_create.return_value.id)
|
||||
self.assertEqual(4, mock_ins_create.call_count)
|
||||
|
||||
@patch.object(inst_models.DBInstance, 'find_all')
|
||||
@patch.object(pxc_api, 'CONF')
|
||||
@patch.object(galera_api, 'CONF')
|
||||
@patch.object(inst_models.Instance, 'create')
|
||||
@patch.object(DBCluster, 'create')
|
||||
@patch.object(task_api, 'load')
|
||||
|
@ -284,9 +307,10 @@ class ClusterTest(trove_testtools.TestCase):
|
|||
self.cluster.delete()
|
||||
mock_update_db.assert_called_with(task_status=ClusterTasks.DELETING)
|
||||
|
||||
@patch.object(pxc_api.PXCCluster, '_get_cluster_network_interfaces')
|
||||
@patch.object(galera_api.GaleraCommonCluster,
|
||||
'_get_cluster_network_interfaces')
|
||||
@patch.object(DBCluster, 'update')
|
||||
@patch.object(pxc_api, 'CONF')
|
||||
@patch.object(galera_api, 'CONF')
|
||||
@patch.object(inst_models.Instance, 'create')
|
||||
@patch.object(task_api, 'load')
|
||||
@patch.object(QUOTAS, 'check_quotas')
|
||||
|
@ -313,7 +337,7 @@ class ClusterTest(trove_testtools.TestCase):
|
|||
exception.ClusterShrinkMustNotLeaveClusterEmpty,
|
||||
self.cluster.shrink, [instance])
|
||||
|
||||
@patch.object(pxc_api.PXCCluster, '__init__')
|
||||
@patch.object(galera_api.GaleraCommonCluster, '__init__')
|
||||
@patch.object(task_api, 'load')
|
||||
@patch.object(DBCluster, 'update')
|
||||
@patch.object(inst_models.DBInstance, 'find_all')
|
|
@ -50,6 +50,8 @@ from trove.guestagent.datastore.experimental.couchdb import (
|
|||
service as couchdb_service)
|
||||
from trove.guestagent.datastore.experimental.db2 import (
|
||||
service as db2service)
|
||||
from trove.guestagent.datastore.experimental.mariadb import (
|
||||
service as mariadb_service)
|
||||
from trove.guestagent.datastore.experimental.mongodb import (
|
||||
service as mongo_service)
|
||||
from trove.guestagent.datastore.experimental.mongodb import (
|
||||
|
@ -62,8 +64,6 @@ from trove.guestagent.datastore.experimental.postgresql.service import (
|
|||
status as pg_status)
|
||||
from trove.guestagent.datastore.experimental.pxc import (
|
||||
service as pxc_service)
|
||||
from trove.guestagent.datastore.experimental.pxc import (
|
||||
system as pxc_system)
|
||||
from trove.guestagent.datastore.experimental.redis import service as rservice
|
||||
from trove.guestagent.datastore.experimental.redis.service import RedisApp
|
||||
from trove.guestagent.datastore.experimental.redis import system as RedisSystem
|
||||
|
@ -78,7 +78,7 @@ 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.datastore.mysql.service import MySqlRootAccess
|
||||
import trove.guestagent.datastore.mysql_common.service as dbaas_base
|
||||
import trove.guestagent.datastore.mysql_common.service as mysql_common_service
|
||||
import trove.guestagent.datastore.service as base_datastore_service
|
||||
from trove.guestagent.datastore.service import BaseDbStatus
|
||||
from trove.guestagent.db import models
|
||||
|
@ -149,32 +149,32 @@ class DbaasTest(trove_testtools.TestCase):
|
|||
def setUp(self):
|
||||
super(DbaasTest, self).setUp()
|
||||
self.orig_utils_execute_with_timeout = \
|
||||
dbaas_base.utils.execute_with_timeout
|
||||
self.orig_utils_execute = dbaas_base.utils.execute
|
||||
mysql_common_service.utils.execute_with_timeout
|
||||
self.orig_utils_execute = mysql_common_service.utils.execute
|
||||
|
||||
def tearDown(self):
|
||||
super(DbaasTest, self).tearDown()
|
||||
dbaas_base.utils.execute_with_timeout = \
|
||||
mysql_common_service.utils.execute_with_timeout = \
|
||||
self.orig_utils_execute_with_timeout
|
||||
dbaas_base.utils.execute = self.orig_utils_execute
|
||||
mysql_common_service.utils.execute = self.orig_utils_execute
|
||||
|
||||
@patch.object(operating_system, 'remove')
|
||||
def test_clear_expired_password(self, mock_remove):
|
||||
secret_content = ("# The random password set for the "
|
||||
"root user at Wed May 14 14:06:38 2014 "
|
||||
"(local time): somepassword")
|
||||
with patch.object(dbaas_base.utils, 'execute',
|
||||
with patch.object(mysql_common_service.utils, 'execute',
|
||||
return_value=(secret_content, None)):
|
||||
dbaas_base.clear_expired_password()
|
||||
self.assertEqual(2, dbaas_base.utils.execute.call_count)
|
||||
mysql_common_service.clear_expired_password()
|
||||
self.assertEqual(2, mysql_common_service.utils.execute.call_count)
|
||||
self.assertEqual(1, mock_remove.call_count)
|
||||
|
||||
@patch.object(operating_system, 'remove')
|
||||
def test_no_secret_content_clear_expired_password(self, mock_remove):
|
||||
with patch.object(dbaas_base.utils, 'execute',
|
||||
with patch.object(mysql_common_service.utils, 'execute',
|
||||
return_value=('', None)):
|
||||
dbaas_base.clear_expired_password()
|
||||
self.assertEqual(1, dbaas_base.utils.execute.call_count)
|
||||
mysql_common_service.clear_expired_password()
|
||||
self.assertEqual(1, mysql_common_service.utils.execute.call_count)
|
||||
mock_remove.assert_not_called()
|
||||
|
||||
@patch.object(operating_system, 'remove')
|
||||
|
@ -185,44 +185,50 @@ class DbaasTest(trove_testtools.TestCase):
|
|||
secret_content = ("# The random password set for the "
|
||||
"root user at Wed May 14 14:06:38 2014 "
|
||||
"(local time): somepassword")
|
||||
with patch.object(dbaas_base.utils, 'execute',
|
||||
with patch.object(mysql_common_service.utils, 'execute',
|
||||
side_effect=[(secret_content, None),
|
||||
ProcessExecutionError]):
|
||||
dbaas_base.clear_expired_password()
|
||||
self.assertEqual(2, dbaas_base.utils.execute.call_count)
|
||||
mysql_common_service.clear_expired_password()
|
||||
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(dbaas_base.utils, 'execute',
|
||||
@patch.object(mysql_common_service.utils, 'execute',
|
||||
side_effect=ProcessExecutionError)
|
||||
def test_fail_retrieve_secret_content_clear_expired_password(self,
|
||||
mock_execute,
|
||||
mock_remove,
|
||||
mock_logging):
|
||||
dbaas_base.clear_expired_password()
|
||||
mysql_common_service.clear_expired_password()
|
||||
self.assertEqual(1, mock_execute.call_count)
|
||||
mock_remove.assert_not_called()
|
||||
|
||||
@patch.object(operating_system, 'read_file',
|
||||
return_value={'client':
|
||||
{'password': 'some password'}})
|
||||
def test_get_auth_password(self, read_file_mock):
|
||||
@patch.object(mysql_common_service.BaseMySqlApp.configuration_manager,
|
||||
'get_value',
|
||||
return_value=MagicMock({'get': 'some password'}))
|
||||
def test_get_auth_password(self, get_cnf_mock, read_file_mock):
|
||||
password = MySqlApp.get_auth_password()
|
||||
read_file_mock.assert_called_once_with(MySqlApp.get_client_auth_file(),
|
||||
codec=MySqlApp.CFG_CODEC)
|
||||
self.assertEqual("some password", password)
|
||||
|
||||
@patch.object(mysql_common_service.BaseMySqlApp.configuration_manager,
|
||||
'get_value',
|
||||
side_effect=RuntimeError('Error'))
|
||||
@patch.object(operating_system, 'read_file',
|
||||
side_effect=RuntimeError('read_file error'))
|
||||
def test_get_auth_password_error(self, _):
|
||||
def test_get_auth_password_error(self, _, get_cnf_mock):
|
||||
self.assertRaisesRegexp(RuntimeError, "read_file error",
|
||||
MySqlApp.get_auth_password)
|
||||
|
||||
def test_service_discovery(self):
|
||||
with patch.object(os.path, 'isfile', return_value=True):
|
||||
mysql_service = \
|
||||
dbaas_base.operating_system.service_discovery(["mysql"])
|
||||
mysql_service = mysql_common_service.operating_system.\
|
||||
service_discovery(["mysql"])
|
||||
self.assertIsNotNone(mysql_service['cmd_start'])
|
||||
self.assertIsNotNone(mysql_service['cmd_enable'])
|
||||
|
||||
|
@ -233,8 +239,9 @@ class DbaasTest(trove_testtools.TestCase):
|
|||
"--tmpdir=/tmp --skip-external-locking"
|
||||
|
||||
with patch.object(os.path, 'isfile', return_value=True):
|
||||
dbaas_base.utils.execute = Mock(return_value=(output, None))
|
||||
options = dbaas_base.load_mysqld_options()
|
||||
mysql_common_service.utils.execute = Mock(
|
||||
return_value=(output, None))
|
||||
options = mysql_common_service.load_mysqld_options()
|
||||
|
||||
self.assertEqual(5, len(options))
|
||||
self.assertEqual(["mysql"], options["user"])
|
||||
|
@ -249,8 +256,9 @@ class DbaasTest(trove_testtools.TestCase):
|
|||
"--plugin-load=federated=ha_federated.so")
|
||||
|
||||
with patch.object(os.path, 'isfile', return_value=True):
|
||||
dbaas_base.utils.execute = Mock(return_value=(output, None))
|
||||
options = dbaas_base.load_mysqld_options()
|
||||
mysql_common_service.utils.execute = Mock(
|
||||
return_value=(output, None))
|
||||
options = mysql_common_service.load_mysqld_options()
|
||||
|
||||
self.assertEqual(1, len(options))
|
||||
self.assertEqual(["blackhole=ha_blackhole.so",
|
||||
|
@ -260,9 +268,10 @@ class DbaasTest(trove_testtools.TestCase):
|
|||
@patch.object(os.path, 'isfile', return_value=True)
|
||||
def test_load_mysqld_options_error(self, mock_exists):
|
||||
|
||||
dbaas_base.utils.execute = Mock(side_effect=ProcessExecutionError())
|
||||
mysql_common_service.utils.execute = Mock(
|
||||
side_effect=ProcessExecutionError())
|
||||
|
||||
self.assertFalse(dbaas_base.load_mysqld_options())
|
||||
self.assertFalse(mysql_common_service.load_mysqld_options())
|
||||
|
||||
|
||||
class ResultSetStub(object):
|
||||
|
@ -379,9 +388,9 @@ class MySqlAdminMockTest(trove_testtools.TestCase):
|
|||
def tearDown(self):
|
||||
super(MySqlAdminMockTest, self).tearDown()
|
||||
|
||||
@patch('trove.guestagent.datastore.mysql.service.MySqlApp'
|
||||
'.get_auth_password', return_value='some_password')
|
||||
def test_list_databases(self, auth_pwd_mock):
|
||||
@patch.object(mysql_common_service.BaseMySqlApp, 'get_auth_password',
|
||||
Mock(return_value='some_password'))
|
||||
def test_list_databases(self):
|
||||
with patch.object(self.mock_client, 'execute',
|
||||
return_value=ResultSetStub(
|
||||
[('db1', 'utf8', 'utf8_bin'),
|
||||
|
@ -420,6 +429,9 @@ class MySqlAdminTest(trove_testtools.TestCase):
|
|||
dbaas.MySqlApp.configuration_manager = Mock()
|
||||
dbaas.orig_get_auth_password = dbaas.MySqlApp.get_auth_password
|
||||
dbaas.MySqlApp.get_auth_password = Mock()
|
||||
self.orig_configuration_manager = \
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager = Mock()
|
||||
|
||||
self.mySqlAdmin = MySqlAdmin()
|
||||
|
||||
|
@ -431,6 +443,8 @@ class MySqlAdminTest(trove_testtools.TestCase):
|
|||
dbaas.orig_configuration_manager
|
||||
dbaas.MySqlApp.get_auth_password = \
|
||||
dbaas.orig_get_auth_password
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager = \
|
||||
self.orig_configuration_manager
|
||||
super(MySqlAdminTest, self).tearDown()
|
||||
|
||||
def test__associate_dbs(self):
|
||||
|
@ -587,9 +601,9 @@ class MySqlAdminTest(trove_testtools.TestCase):
|
|||
self._assert_execute_call(access_grants_expected,
|
||||
mock_execute, call_idx=1)
|
||||
|
||||
@patch('trove.guestagent.datastore.mysql.service.MySqlApp'
|
||||
'.get_auth_password', return_value='some_password')
|
||||
def test_list_databases(self, auth_pwd_mock):
|
||||
@patch.object(mysql_common_service.BaseMySqlApp, 'get_auth_password',
|
||||
Mock(return_value='some_password'))
|
||||
def test_list_databases(self):
|
||||
expected = ("SELECT schema_name as name,"
|
||||
" default_character_set_name as charset,"
|
||||
" default_collation_name as collation"
|
||||
|
@ -798,13 +812,13 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
def setUp(self):
|
||||
super(MySqlAppTest, self).setUp()
|
||||
self.orig_utils_execute_with_timeout = \
|
||||
dbaas_base.utils.execute_with_timeout
|
||||
mysql_common_service.utils.execute_with_timeout
|
||||
self.orig_time_sleep = time.sleep
|
||||
self.orig_time_time = time.time
|
||||
self.orig_unlink = os.unlink
|
||||
self.orig_get_auth_password = MySqlApp.get_auth_password
|
||||
self.orig_service_discovery = operating_system.service_discovery
|
||||
mysql_app_patcher = patch.multiple(MySqlApp, get_engine=DEFAULT,
|
||||
mysql_app_patcher = patch.multiple(mysql_common_service.BaseMySqlApp,
|
||||
get_engine=DEFAULT,
|
||||
get_auth_password=DEFAULT,
|
||||
configuration_manager=DEFAULT)
|
||||
self.addCleanup(mysql_app_patcher.stop)
|
||||
|
@ -819,7 +833,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
'cmd_stop': Mock(),
|
||||
'cmd_enable': Mock(),
|
||||
'cmd_disable': Mock(),
|
||||
'cmd_bootstrap_pxc_cluster': Mock(),
|
||||
'cmd_bootstrap_galera_cluster': Mock(),
|
||||
'bin': Mock()}
|
||||
operating_system.service_discovery = Mock(
|
||||
return_value=mysql_service)
|
||||
|
@ -834,7 +848,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
self.orig_create_engine = sqlalchemy.create_engine
|
||||
|
||||
def tearDown(self):
|
||||
dbaas_base.utils.execute_with_timeout = \
|
||||
mysql_common_service.utils.execute_with_timeout = \
|
||||
self.orig_utils_execute_with_timeout
|
||||
time.sleep = self.orig_time_sleep
|
||||
time.time = self.orig_time_time
|
||||
|
@ -877,7 +891,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
|
||||
def test_stop_mysql(self):
|
||||
|
||||
dbaas_base.utils.execute_with_timeout = Mock()
|
||||
mysql_common_service.utils.execute_with_timeout = Mock()
|
||||
self.appStatus.set_next_status(
|
||||
rd_instance.ServiceStatuses.SHUTDOWN)
|
||||
|
||||
|
@ -888,7 +902,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
|
||||
def test_stop_mysql_with_db_update(self):
|
||||
|
||||
dbaas_base.utils.execute_with_timeout = Mock()
|
||||
mysql_common_service.utils.execute_with_timeout = Mock()
|
||||
self.appStatus.set_next_status(
|
||||
rd_instance.ServiceStatuses.SHUTDOWN)
|
||||
|
||||
|
@ -918,7 +932,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
@patch('trove.guestagent.datastore.service.LOG')
|
||||
@patch('trove.guestagent.datastore.mysql_common.service.LOG')
|
||||
def test_stop_mysql_error(self, *args):
|
||||
dbaas_base.utils.execute_with_timeout = Mock()
|
||||
mysql_common_service.utils.execute_with_timeout = Mock()
|
||||
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
||||
self.mySqlApp.state_change_wait_time = 1
|
||||
with patch.object(BaseDbStatus, 'prepare_completed') as patch_pc:
|
||||
|
@ -975,14 +989,14 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
def test_wipe_ib_logfiles_error(self, get_datadir_mock, mock_logging):
|
||||
|
||||
mocked = Mock(side_effect=ProcessExecutionError('Error'))
|
||||
dbaas_base.utils.execute_with_timeout = mocked
|
||||
mysql_common_service.utils.execute_with_timeout = mocked
|
||||
|
||||
self.assertRaises(ProcessExecutionError,
|
||||
self.mySqlApp.wipe_ib_logfiles)
|
||||
|
||||
def test_start_mysql(self):
|
||||
|
||||
dbaas_base.utils.execute_with_timeout = Mock()
|
||||
mysql_common_service.utils.execute_with_timeout = Mock()
|
||||
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
||||
self.mySqlApp._enable_mysql_on_boot = Mock()
|
||||
self.mySqlApp.start_mysql()
|
||||
|
@ -990,7 +1004,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
|
||||
def test_start_mysql_with_db_update(self):
|
||||
|
||||
dbaas_base.utils.execute_with_timeout = Mock()
|
||||
mysql_common_service.utils.execute_with_timeout = Mock()
|
||||
self.mySqlApp._enable_mysql_on_boot = Mock()
|
||||
self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING)
|
||||
|
||||
|
@ -1006,7 +1020,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
@patch('trove.guestagent.datastore.service.LOG')
|
||||
def test_start_mysql_runs_forever(self, *args):
|
||||
|
||||
dbaas_base.utils.execute_with_timeout = Mock()
|
||||
mysql_common_service.utils.execute_with_timeout = Mock()
|
||||
self.mySqlApp._enable_mysql_on_boot = Mock()
|
||||
self.mySqlApp.state_change_wait_time = 1
|
||||
self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN)
|
||||
|
@ -1025,7 +1039,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
|
||||
self.mySqlApp._enable_mysql_on_boot = Mock()
|
||||
mocked = Mock(side_effect=ProcessExecutionError('Error'))
|
||||
dbaas_base.utils.execute_with_timeout = mocked
|
||||
mysql_common_service.utils.execute_with_timeout = mocked
|
||||
|
||||
with patch.object(BaseDbStatus, 'prepare_completed') as patch_pc:
|
||||
patch_pc.__get__ = Mock(return_value=True)
|
||||
|
@ -1060,8 +1074,8 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
self.mySqlApp.reset_configuration(configuration=configuration)
|
||||
cfg_reset.assert_called_once_with('some junk')
|
||||
|
||||
@patch.object(dbaas.MySqlApp, 'get_auth_password',
|
||||
return_value='some_password')
|
||||
@patch.object(dbaas.MySqlApp,
|
||||
'get_auth_password', return_value='some_password')
|
||||
def test_reset_configuration(self, auth_pwd_mock):
|
||||
save_cfg_mock = Mock()
|
||||
save_auth_mock = Mock()
|
||||
|
@ -1077,12 +1091,13 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
save_cfg_mock.assert_called_once_with('some junk')
|
||||
save_auth_mock.assert_called_once_with(
|
||||
auth_pwd_mock.return_value)
|
||||
|
||||
wipe_ib_mock.assert_called_once_with()
|
||||
|
||||
@patch.object(utils, 'execute_with_timeout', return_value=('0', ''))
|
||||
def test__enable_mysql_on_boot(self, mock_execute):
|
||||
mysql_service = \
|
||||
dbaas_base.operating_system.service_discovery(["mysql"])
|
||||
mysql_common_service.operating_system.service_discovery(["mysql"])
|
||||
self.mySqlApp._enable_mysql_on_boot()
|
||||
self.assertEqual(1, mock_execute.call_count)
|
||||
mock_execute.assert_called_with(mysql_service['cmd_enable'],
|
||||
|
@ -1101,7 +1116,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
@patch.object(utils, 'execute_with_timeout', return_value=('0', ''))
|
||||
def test__disable_mysql_on_boot(self, mock_execute):
|
||||
mysql_service = \
|
||||
dbaas_base.operating_system.service_discovery(["mysql"])
|
||||
mysql_common_service.operating_system.service_discovery(["mysql"])
|
||||
self.mySqlApp._disable_mysql_on_boot()
|
||||
self.assertEqual(1, mock_execute.call_count)
|
||||
mock_execute.assert_called_with(mysql_service['cmd_disable'],
|
||||
|
@ -1134,27 +1149,29 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
with patch.object(self.mySqlApp.configuration_manager,
|
||||
'apply_system_override') as apply_sys_mock:
|
||||
self.mySqlApp.write_replication_source_overrides('something')
|
||||
apply_sys_mock.assert_called_once_with('something',
|
||||
dbaas_base.CNF_MASTER)
|
||||
apply_sys_mock.assert_called_once_with(
|
||||
'something', mysql_common_service.CNF_MASTER)
|
||||
|
||||
def test_write_replication_replica_overrides(self):
|
||||
with patch.object(self.mySqlApp.configuration_manager,
|
||||
'apply_system_override') as apply_sys_mock:
|
||||
self.mySqlApp.write_replication_replica_overrides('something')
|
||||
apply_sys_mock.assert_called_once_with('something',
|
||||
dbaas_base.CNF_SLAVE)
|
||||
apply_sys_mock.assert_called_once_with(
|
||||
'something', mysql_common_service.CNF_SLAVE)
|
||||
|
||||
def test_remove_replication_source_overrides(self):
|
||||
with patch.object(self.mySqlApp.configuration_manager,
|
||||
'remove_system_override') as remove_sys_mock:
|
||||
self.mySqlApp.remove_replication_source_overrides()
|
||||
remove_sys_mock.assert_called_once_with(dbaas_base.CNF_MASTER)
|
||||
remove_sys_mock.assert_called_once_with(
|
||||
mysql_common_service.CNF_MASTER)
|
||||
|
||||
def test_remove_replication_replica_overrides(self):
|
||||
with patch.object(self.mySqlApp.configuration_manager,
|
||||
'remove_system_override') as remove_sys_mock:
|
||||
self.mySqlApp.remove_replication_replica_overrides()
|
||||
remove_sys_mock.assert_called_once_with(dbaas_base.CNF_SLAVE)
|
||||
remove_sys_mock.assert_called_once_with(
|
||||
mysql_common_service.CNF_SLAVE)
|
||||
|
||||
def test_exists_replication_source_overrides(self):
|
||||
with patch.object(self.mySqlApp.configuration_manager,
|
||||
|
@ -1369,12 +1386,12 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
MySqlApp.get_client_auth_file(),
|
||||
{'client': {'host': '127.0.0.1',
|
||||
'password': 'some_password',
|
||||
'user': dbaas_base.ADMIN_USER_NAME}},
|
||||
'user': mysql_common_service.ADMIN_USER_NAME}},
|
||||
codec=MySqlApp.CFG_CODEC)
|
||||
|
||||
@patch.object(utils, 'generate_random_password',
|
||||
return_value='some_password')
|
||||
@patch.object(dbaas_base, 'clear_expired_password')
|
||||
@patch.object(mysql_common_service, 'clear_expired_password')
|
||||
def test_secure(self, clear_pwd_mock, auth_pwd_mock):
|
||||
|
||||
self.mySqlApp.start_mysql = Mock()
|
||||
|
@ -1471,7 +1488,7 @@ class MySqlAppTest(trove_testtools.TestCase):
|
|||
|
||||
self.assert_reported_status(rd_instance.ServiceStatuses.NEW)
|
||||
|
||||
@patch.object(dbaas_base, 'clear_expired_password')
|
||||
@patch.object(mysql_common_service, 'clear_expired_password')
|
||||
def test_secure_write_conf_error(self, clear_pwd_mock):
|
||||
|
||||
self.mySqlApp.start_mysql = Mock()
|
||||
|
@ -1538,7 +1555,7 @@ class MySqlAppMockTest(trove_testtools.TestCase):
|
|||
utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
||||
super(MySqlAppMockTest, self).tearDown()
|
||||
|
||||
@patch.object(dbaas_base, 'clear_expired_password')
|
||||
@patch.object(mysql_common_service, 'clear_expired_password')
|
||||
@patch.object(utils, 'generate_random_password',
|
||||
return_value='some_password')
|
||||
def test_secure_keep_root(self, auth_pwd_mock, clear_pwd_mock):
|
||||
|
@ -1561,9 +1578,9 @@ class MySqlAppMockTest(trove_testtools.TestCase):
|
|||
app._reset_configuration.assert_has_calls(reset_config_calls)
|
||||
self.assertTrue(mock_execute.called)
|
||||
|
||||
@patch.object(dbaas_base, 'clear_expired_password')
|
||||
@patch('trove.guestagent.datastore.mysql.service.MySqlApp'
|
||||
'.get_auth_password', return_value='some_password')
|
||||
@patch.object(mysql_common_service, 'clear_expired_password')
|
||||
@patch.object(mysql_common_service.BaseMySqlApp,
|
||||
'get_auth_password', return_value='some_password')
|
||||
def test_secure_with_mycnf_error(self, auth_pwd_mock, clear_pwd_mock):
|
||||
with patch.object(self.mock_client,
|
||||
'execute', return_value=None) as mock_execute:
|
||||
|
@ -1576,10 +1593,10 @@ class MySqlAppMockTest(trove_testtools.TestCase):
|
|||
mock_status = MagicMock()
|
||||
mock_status.wait_for_real_status_to_change_to = MagicMock(
|
||||
return_value=True)
|
||||
dbaas_base.clear_expired_password = \
|
||||
mysql_common_service.clear_expired_password = \
|
||||
MagicMock(return_value=None)
|
||||
app = MySqlApp(mock_status)
|
||||
dbaas_base.clear_expired_password = \
|
||||
mysql_common_service.clear_expired_password = \
|
||||
MagicMock(return_value=None)
|
||||
self.assertRaises(RuntimeError, app.secure, None)
|
||||
self.assertTrue(mock_execute.called)
|
||||
|
@ -1618,25 +1635,25 @@ class MySqlRootStatusTest(trove_testtools.TestCase):
|
|||
utils.execute_with_timeout = self.orig_utils_execute_with_timeout
|
||||
super(MySqlRootStatusTest, self).tearDown()
|
||||
|
||||
@patch.object(dbaas.MySqlApp, 'get_auth_password',
|
||||
return_value='some_password')
|
||||
@patch.object(mysql_common_service.BaseMySqlApp,
|
||||
'get_auth_password', return_value='some_password')
|
||||
def test_root_is_enabled(self, auth_pwd_mock):
|
||||
mock_rs = MagicMock()
|
||||
mock_rs.rowcount = 1
|
||||
with patch.object(self.mock_client, 'execute', return_value=mock_rs):
|
||||
self.assertTrue(MySqlRootAccess().is_root_enabled())
|
||||
|
||||
@patch.object(dbaas.MySqlApp, 'get_auth_password',
|
||||
return_value='some_password')
|
||||
@patch.object(mysql_common_service.BaseMySqlApp,
|
||||
'get_auth_password', return_value='some_password')
|
||||
def test_root_is_not_enabled(self, auth_pwd_mock):
|
||||
mock_rs = MagicMock()
|
||||
mock_rs.rowcount = 0
|
||||
with patch.object(self.mock_client, 'execute', return_value=mock_rs):
|
||||
self.assertFalse(MySqlRootAccess().is_root_enabled())
|
||||
|
||||
@patch.object(dbaas_base, 'clear_expired_password')
|
||||
@patch.object(dbaas.MySqlApp, 'get_auth_password',
|
||||
return_value='some_password')
|
||||
@patch.object(mysql_common_service, 'clear_expired_password')
|
||||
@patch.object(mysql_common_service.BaseMySqlApp,
|
||||
'get_auth_password', return_value='some_password')
|
||||
def test_enable_root(self, auth_pwd_mock, clear_pwd_mock):
|
||||
with patch.object(self.mock_client,
|
||||
'execute', return_value=None) as mock_execute:
|
||||
|
@ -1846,12 +1863,12 @@ class KeepAliveConnectionTest(trove_testtools.TestCase):
|
|||
def setUp(self):
|
||||
super(KeepAliveConnectionTest, self).setUp()
|
||||
self.orig_utils_execute_with_timeout = \
|
||||
dbaas_base.utils.execute_with_timeout
|
||||
mysql_common_service.utils.execute_with_timeout
|
||||
self.orig_LOG_err = dbaas.LOG
|
||||
|
||||
def tearDown(self):
|
||||
super(KeepAliveConnectionTest, self).tearDown()
|
||||
dbaas_base.utils.execute_with_timeout = \
|
||||
mysql_common_service.utils.execute_with_timeout = \
|
||||
self.orig_utils_execute_with_timeout
|
||||
dbaas.LOG = self.orig_LOG_err
|
||||
|
||||
|
@ -2223,9 +2240,11 @@ class MySqlAppStatusTest(trove_testtools.TestCase):
|
|||
super(MySqlAppStatusTest, self).setUp()
|
||||
util.init_db()
|
||||
self.orig_utils_execute_with_timeout = \
|
||||
dbaas_base.utils.execute_with_timeout
|
||||
self.orig_load_mysqld_options = dbaas_base.load_mysqld_options
|
||||
self.orig_dbaas_base_os_path_exists = dbaas_base.os.path.exists
|
||||
mysql_common_service.utils.execute_with_timeout
|
||||
self.orig_load_mysqld_options = \
|
||||
mysql_common_service.load_mysqld_options
|
||||
self.orig_mysql_common_service_os_path_exists = \
|
||||
mysql_common_service.os.path.exists
|
||||
self.orig_dbaas_time_sleep = time.sleep
|
||||
self.orig_time_time = time.time
|
||||
self.FAKE_ID = str(uuid4())
|
||||
|
@ -2234,10 +2253,12 @@ class MySqlAppStatusTest(trove_testtools.TestCase):
|
|||
dbaas.CONF.guest_id = self.FAKE_ID
|
||||
|
||||
def tearDown(self):
|
||||
dbaas_base.utils.execute_with_timeout = \
|
||||
mysql_common_service.utils.execute_with_timeout = \
|
||||
self.orig_utils_execute_with_timeout
|
||||
dbaas_base.load_mysqld_options = self.orig_load_mysqld_options
|
||||
dbaas_base.os.path.exists = self.orig_dbaas_base_os_path_exists
|
||||
mysql_common_service.load_mysqld_options = \
|
||||
self.orig_load_mysqld_options
|
||||
mysql_common_service.os.path.exists = \
|
||||
self.orig_mysql_common_service_os_path_exists
|
||||
time.sleep = self.orig_dbaas_time_sleep
|
||||
time.time = self.orig_time_time
|
||||
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
||||
|
@ -2246,7 +2267,8 @@ class MySqlAppStatusTest(trove_testtools.TestCase):
|
|||
|
||||
def test_get_actual_db_status(self):
|
||||
|
||||
dbaas_base.utils.execute_with_timeout = Mock(return_value=(None, None))
|
||||
mysql_common_service.utils.execute_with_timeout = \
|
||||
Mock(return_value=(None, None))
|
||||
|
||||
self.mySqlAppStatus = MySqlAppStatus.get()
|
||||
status = self.mySqlAppStatus._get_actual_db_status()
|
||||
|
@ -2260,7 +2282,7 @@ class MySqlAppStatusTest(trove_testtools.TestCase):
|
|||
def test_get_actual_db_status_error_crashed(self, mock_logging,
|
||||
mock_exists,
|
||||
mock_execute):
|
||||
dbaas_base.load_mysqld_options = Mock(return_value={})
|
||||
mysql_common_service.load_mysqld_options = Mock(return_value={})
|
||||
self.mySqlAppStatus = MySqlAppStatus.get()
|
||||
status = self.mySqlAppStatus._get_actual_db_status()
|
||||
self.assertEqual(rd_instance.ServiceStatuses.CRASHED, status)
|
||||
|
@ -2269,9 +2291,9 @@ class MySqlAppStatusTest(trove_testtools.TestCase):
|
|||
def test_get_actual_db_status_error_shutdown(self, *args):
|
||||
|
||||
mocked = Mock(side_effect=ProcessExecutionError())
|
||||
dbaas_base.utils.execute_with_timeout = mocked
|
||||
dbaas_base.load_mysqld_options = Mock(return_value={})
|
||||
dbaas_base.os.path.exists = Mock(return_value=False)
|
||||
mysql_common_service.utils.execute_with_timeout = mocked
|
||||
mysql_common_service.load_mysqld_options = Mock(return_value={})
|
||||
mysql_common_service.os.path.exists = Mock(return_value=False)
|
||||
|
||||
self.mySqlAppStatus = MySqlAppStatus.get()
|
||||
status = self.mySqlAppStatus._get_actual_db_status()
|
||||
|
@ -2281,10 +2303,10 @@ class MySqlAppStatusTest(trove_testtools.TestCase):
|
|||
@patch('trove.guestagent.datastore.mysql_common.service.LOG')
|
||||
def test_get_actual_db_status_error_blocked(self, *args):
|
||||
|
||||
dbaas_base.utils.execute_with_timeout = MagicMock(
|
||||
mysql_common_service.utils.execute_with_timeout = MagicMock(
|
||||
side_effect=[ProcessExecutionError(), ("some output", None)])
|
||||
dbaas_base.load_mysqld_options = Mock()
|
||||
dbaas_base.os.path.exists = Mock(return_value=True)
|
||||
mysql_common_service.load_mysqld_options = Mock()
|
||||
mysql_common_service.os.path.exists = Mock(return_value=True)
|
||||
|
||||
self.mySqlAppStatus = MySqlAppStatus.get()
|
||||
status = self.mySqlAppStatus._get_actual_db_status()
|
||||
|
@ -3427,52 +3449,56 @@ class PXCAppTest(trove_testtools.TestCase):
|
|||
def setUp(self):
|
||||
super(PXCAppTest, self).setUp()
|
||||
self.orig_utils_execute_with_timeout = \
|
||||
dbaas_base.utils.execute_with_timeout
|
||||
mysql_common_service.utils.execute_with_timeout
|
||||
self.orig_time_sleep = time.sleep
|
||||
self.orig_time_time = time.time
|
||||
self.orig_unlink = os.unlink
|
||||
self.orig_get_auth_password = pxc_service.PXCApp.get_auth_password
|
||||
self.orig_pxc_system_service_discovery = pxc_system.service_discovery
|
||||
self.orig_get_auth_password = \
|
||||
mysql_common_service.BaseMySqlApp.get_auth_password
|
||||
self.FAKE_ID = str(uuid4())
|
||||
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
||||
status=rd_instance.ServiceStatuses.NEW)
|
||||
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
||||
rd_instance.ServiceStatuses.NEW)
|
||||
self.PXCApp = pxc_service.PXCApp(self.appStatus)
|
||||
mysql_service = {'cmd_start': Mock(),
|
||||
'cmd_stop': Mock(),
|
||||
'cmd_enable': Mock(),
|
||||
'cmd_disable': Mock(),
|
||||
'cmd_bootstrap_pxc_cluster': Mock(),
|
||||
'bin': Mock()}
|
||||
pxc_system.service_discovery = Mock(
|
||||
return_value=mysql_service)
|
||||
mysql_service = patch.object(
|
||||
pxc_service.PXCApp, 'mysql_service',
|
||||
PropertyMock(return_value={
|
||||
'cmd_start': Mock(),
|
||||
'cmd_stop': Mock(),
|
||||
'cmd_enable': Mock(),
|
||||
'cmd_disable': Mock(),
|
||||
'cmd_bootstrap_galera_cluster': Mock(),
|
||||
'bin': Mock()
|
||||
}))
|
||||
mysql_service.start()
|
||||
self.addCleanup(mysql_service.stop)
|
||||
time.sleep = Mock()
|
||||
time.time = Mock(side_effect=faketime)
|
||||
os.unlink = Mock()
|
||||
pxc_service.PXCApp.get_auth_password = Mock()
|
||||
mysql_common_service.BaseMySqlApp.get_auth_password = Mock()
|
||||
self.mock_client = Mock()
|
||||
self.mock_execute = Mock()
|
||||
self.mock_client.__enter__ = Mock()
|
||||
self.mock_client.__exit__ = Mock()
|
||||
self.mock_client.__enter__.return_value.execute = self.mock_execute
|
||||
pxc_service.orig_configuration_manager = (
|
||||
pxc_service.PXCApp.configuration_manager)
|
||||
pxc_service.PXCApp.configuration_manager = Mock()
|
||||
self.orig_configuration_manager = \
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager = Mock()
|
||||
self.orig_create_engine = sqlalchemy.create_engine
|
||||
|
||||
def tearDown(self):
|
||||
self.PXCApp = None
|
||||
dbaas_base.utils.execute_with_timeout = \
|
||||
mysql_common_service.utils.execute_with_timeout = \
|
||||
self.orig_utils_execute_with_timeout
|
||||
time.sleep = self.orig_time_sleep
|
||||
time.time = self.orig_time_time
|
||||
os.unlink = self.orig_unlink
|
||||
pxc_system.service_discovery = self.orig_pxc_system_service_discovery
|
||||
pxc_service.PXCApp.get_auth_password = self.orig_get_auth_password
|
||||
mysql_common_service.BaseMySqlApp.get_auth_password = \
|
||||
self.orig_get_auth_password
|
||||
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
||||
pxc_service.PXCApp.configuration_manager = \
|
||||
pxc_service.orig_configuration_manager
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager = \
|
||||
self.orig_configuration_manager
|
||||
sqlalchemy.create_engine = self.orig_create_engine
|
||||
super(PXCAppTest, self).tearDown()
|
||||
|
||||
|
@ -3494,11 +3520,11 @@ class PXCAppTest(trove_testtools.TestCase):
|
|||
|
||||
@patch.object(utils, 'execute_with_timeout', return_value=('0', ''))
|
||||
def test__bootstrap_cluster(self, mock_execute):
|
||||
pxc_service_cmds = pxc_system.service_discovery(['mysql'])
|
||||
pxc_service_cmds = self.PXCApp.mysql_service
|
||||
self.PXCApp._bootstrap_cluster(timeout=20)
|
||||
self.assertEqual(1, mock_execute.call_count)
|
||||
mock_execute.assert_called_with(
|
||||
pxc_service_cmds['cmd_bootstrap_pxc_cluster'],
|
||||
pxc_service_cmds['cmd_bootstrap_galera_cluster'],
|
||||
shell=True,
|
||||
timeout=20)
|
||||
|
||||
|
@ -3541,6 +3567,131 @@ class PXCAppTest(trove_testtools.TestCase):
|
|||
self.assertEqual(1, self.PXCApp._bootstrap_cluster.call_count)
|
||||
|
||||
|
||||
class MariaDBAppTest(trove_testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(MariaDBAppTest, self).setUp()
|
||||
self.orig_utils_execute_with_timeout = \
|
||||
mysql_common_service.utils.execute_with_timeout
|
||||
self.orig_time_sleep = time.sleep
|
||||
self.orig_time_time = time.time
|
||||
self.orig_unlink = os.unlink
|
||||
self.orig_get_auth_password = \
|
||||
mysql_common_service.BaseMySqlApp.get_auth_password
|
||||
self.FAKE_ID = str(uuid4())
|
||||
InstanceServiceStatus.create(instance_id=self.FAKE_ID,
|
||||
status=rd_instance.ServiceStatuses.NEW)
|
||||
self.appStatus = FakeAppStatus(self.FAKE_ID,
|
||||
rd_instance.ServiceStatuses.NEW)
|
||||
self.MariaDBApp = mariadb_service.MariaDBApp(self.appStatus)
|
||||
mysql_service = patch.object(
|
||||
mariadb_service.MariaDBApp, 'mysql_service',
|
||||
PropertyMock(return_value={
|
||||
'cmd_start': Mock(),
|
||||
'cmd_stop': Mock(),
|
||||
'cmd_enable': Mock(),
|
||||
'cmd_disable': Mock(),
|
||||
'cmd_bootstrap_galera_cluster': Mock(),
|
||||
'bin': Mock()
|
||||
}))
|
||||
mysql_service.start()
|
||||
self.addCleanup(mysql_service.stop)
|
||||
time.sleep = Mock()
|
||||
time.time = Mock(side_effect=faketime)
|
||||
os.unlink = Mock()
|
||||
mysql_common_service.BaseMySqlApp.get_auth_password = Mock()
|
||||
self.mock_client = Mock()
|
||||
self.mock_execute = Mock()
|
||||
self.mock_client.__enter__ = Mock()
|
||||
self.mock_client.__exit__ = Mock()
|
||||
self.mock_client.__enter__.return_value.execute = self.mock_execute
|
||||
self.orig_configuration_manager = \
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager = Mock()
|
||||
self.orig_create_engine = sqlalchemy.create_engine
|
||||
|
||||
def tearDown(self):
|
||||
self.MariaDBApp = None
|
||||
mysql_common_service.utils.execute_with_timeout = \
|
||||
self.orig_utils_execute_with_timeout
|
||||
time.sleep = self.orig_time_sleep
|
||||
time.time = self.orig_time_time
|
||||
os.unlink = self.orig_unlink
|
||||
mysql_common_service.BaseMySqlApp.get_auth_password = \
|
||||
self.orig_get_auth_password
|
||||
InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete()
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager = \
|
||||
self.orig_configuration_manager
|
||||
sqlalchemy.create_engine = self.orig_create_engine
|
||||
super(MariaDBAppTest, self).tearDown()
|
||||
|
||||
@patch.object(mariadb_service.MariaDBApp, 'get_engine',
|
||||
return_value=MagicMock(name='get_engine'))
|
||||
def test__grant_cluster_replication_privilege(self, mock_engine):
|
||||
repl_user = {
|
||||
'name': 'test-user',
|
||||
'password': 'test-user-password',
|
||||
}
|
||||
with patch.object(mariadb_service.MariaDBApp, 'local_sql_client',
|
||||
return_value=self.mock_client):
|
||||
self.MariaDBApp._grant_cluster_replication_privilege(repl_user)
|
||||
args, _ = self.mock_execute.call_args_list[0]
|
||||
expected = ("GRANT LOCK TABLES, RELOAD, REPLICATION CLIENT ON *.* "
|
||||
"TO `test-user`@`%` IDENTIFIED BY 'test-user-password';")
|
||||
self.assertEqual(expected, args[0].text,
|
||||
"Sql statements are not the same")
|
||||
|
||||
@patch.object(utils, 'execute_with_timeout', return_value=('0', ''))
|
||||
def test__bootstrap_cluster(self, mock_execute):
|
||||
mariadb_service_cmds = self.MariaDBApp.mysql_service
|
||||
self.MariaDBApp._bootstrap_cluster(timeout=20)
|
||||
self.assertEqual(1, mock_execute.call_count)
|
||||
mock_execute.assert_called_with(
|
||||
mariadb_service_cmds['cmd_bootstrap_galera_cluster'],
|
||||
shell=True,
|
||||
timeout=20)
|
||||
|
||||
def test_install_cluster(self):
|
||||
repl_user = {
|
||||
'name': 'test-user',
|
||||
'password': 'test-user-password',
|
||||
}
|
||||
apply_mock = Mock()
|
||||
self.MariaDBApp.configuration_manager.apply_system_override = \
|
||||
apply_mock
|
||||
self.MariaDBApp.stop_db = Mock()
|
||||
self.MariaDBApp._grant_cluster_replication_privilege = Mock()
|
||||
self.MariaDBApp.wipe_ib_logfiles = Mock()
|
||||
self.MariaDBApp.start_mysql = Mock()
|
||||
self.MariaDBApp.install_cluster(repl_user, "something")
|
||||
self.assertEqual(1, self.MariaDBApp.stop_db.call_count)
|
||||
self.assertEqual(
|
||||
1, self.MariaDBApp._grant_cluster_replication_privilege.call_count)
|
||||
self.assertEqual(1, apply_mock.call_count)
|
||||
self.assertEqual(1, self.MariaDBApp.wipe_ib_logfiles.call_count)
|
||||
self.assertEqual(1, self.MariaDBApp.start_mysql.call_count)
|
||||
|
||||
def test_install_cluster_with_bootstrap(self):
|
||||
repl_user = {
|
||||
'name': 'test-user',
|
||||
'password': 'test-user-password',
|
||||
}
|
||||
apply_mock = Mock()
|
||||
self.MariaDBApp.configuration_manager.apply_system_override = \
|
||||
apply_mock
|
||||
self.MariaDBApp.stop_db = Mock()
|
||||
self.MariaDBApp._grant_cluster_replication_privilege = Mock()
|
||||
self.MariaDBApp.wipe_ib_logfiles = Mock()
|
||||
self.MariaDBApp._bootstrap_cluster = Mock()
|
||||
self.MariaDBApp.install_cluster(repl_user, "something", bootstrap=True)
|
||||
self.assertEqual(1, self.MariaDBApp.stop_db.call_count)
|
||||
self.assertEqual(
|
||||
1, self.MariaDBApp._grant_cluster_replication_privilege.call_count)
|
||||
self.assertEqual(1, self.MariaDBApp.wipe_ib_logfiles.call_count)
|
||||
self.assertEqual(1, apply_mock.call_count)
|
||||
self.assertEqual(1, self.MariaDBApp._bootstrap_cluster.call_count)
|
||||
|
||||
|
||||
class PostgresAppTest(BaseAppTest.AppTestCase):
|
||||
|
||||
class FakePostgresApp(pg_manager.Manager):
|
||||
|
|
|
@ -18,8 +18,8 @@ import mock
|
|||
import trove.common.context as context
|
||||
from trove.common import exception
|
||||
from trove.common.rpc.version import RPC_API_VERSION
|
||||
from trove.common.strategies.cluster.experimental.pxc.guestagent import (
|
||||
PXCGuestAgentAPI)
|
||||
from trove.common.strategies.cluster.experimental.galera_common.guestagent \
|
||||
import GaleraCommonGuestAgentStrategy
|
||||
from trove import rpc
|
||||
from trove.tests.unittests import trove_testtools
|
||||
|
||||
|
@ -39,10 +39,12 @@ class ApiTest(trove_testtools.TestCase):
|
|||
@mock.patch.object(rpc, 'get_client')
|
||||
def setUp(self, *args):
|
||||
super(ApiTest, self).setUp()
|
||||
cluster_guest_api = (GaleraCommonGuestAgentStrategy()
|
||||
.guest_client_class)
|
||||
self.context = context.TroveContext()
|
||||
self.guest = PXCGuestAgentAPI(self.context, 0)
|
||||
self.guest = cluster_guest_api(self.context, 0)
|
||||
self.guest._call = _mock_call
|
||||
self.api = PXCGuestAgentAPI(self.context, "instance-id-x23d2d")
|
||||
self.api = cluster_guest_api(self.context, "instance-id-x23d2d")
|
||||
self._mock_rpc_client()
|
||||
|
||||
def test_get_routing_key(self):
|
|
@ -0,0 +1,123 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 mock import MagicMock
|
||||
from mock import patch
|
||||
|
||||
from trove.common.context import TroveContext
|
||||
from trove.guestagent.datastore.galera_common import manager as galera_manager
|
||||
from trove.guestagent.datastore.galera_common import service as galera_service
|
||||
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
||||
from trove.tests.unittests import trove_testtools
|
||||
|
||||
|
||||
class GaleraTestApp(galera_service.GaleraApp):
|
||||
|
||||
def __init__(self, status):
|
||||
super(GaleraTestApp, self).__init__(
|
||||
status, mysql_service.BaseLocalSqlClient,
|
||||
mysql_service.BaseKeepAliveConnection)
|
||||
|
||||
@property
|
||||
def cluster_configuration(self):
|
||||
return self.configuration_manager.get_value('mysqld')
|
||||
|
||||
|
||||
class GaleraTestRootAccess(mysql_service.BaseMySqlRootAccess):
|
||||
|
||||
def __init__(self):
|
||||
super(GaleraTestRootAccess, self).__init__(
|
||||
mysql_service.BaseLocalSqlClient,
|
||||
GaleraTestApp(mysql_service.BaseMySqlAppStatus.get()))
|
||||
|
||||
|
||||
class GaleraTestAdmin(mysql_service.BaseMySqlAdmin):
|
||||
def __init__(self):
|
||||
super(GaleraTestAdmin, self).__init__(
|
||||
mysql_service.BaseLocalSqlClient, GaleraTestRootAccess(),
|
||||
GaleraTestApp)
|
||||
|
||||
|
||||
class GuestAgentManagerTest(trove_testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GuestAgentManagerTest, self).setUp()
|
||||
self.manager = galera_manager.GaleraManager(
|
||||
GaleraTestApp, mysql_service.BaseMySqlAppStatus,
|
||||
GaleraTestAdmin)
|
||||
self.context = TroveContext()
|
||||
patcher_rs = patch(
|
||||
'trove.guestagent.strategies.replication.get_instance')
|
||||
patcher_rs.start()
|
||||
self.addCleanup(patcher_rs.stop)
|
||||
|
||||
@patch.object(mysql_service.BaseMySqlAppStatus, 'get',
|
||||
new_callable=MagicMock)
|
||||
@patch.object(galera_service.GaleraApp, 'install_cluster',
|
||||
new_callable=MagicMock)
|
||||
def test_install_cluster(self, install_cluster, app_status_get):
|
||||
install_cluster.return_value = MagicMock()
|
||||
app_status_get.return_value = None
|
||||
|
||||
replication_user = "repuser"
|
||||
configuration = "configuration"
|
||||
bootstrap = True
|
||||
self.manager.install_cluster(self.context, replication_user,
|
||||
configuration, bootstrap)
|
||||
app_status_get.assert_any_call()
|
||||
install_cluster.assert_called_with(
|
||||
replication_user, configuration, bootstrap)
|
||||
|
||||
@patch.object(mysql_service.BaseMySqlAppStatus, 'get',
|
||||
new_callable=MagicMock)
|
||||
@patch.object(galera_service.GaleraApp, 'reset_admin_password',
|
||||
new_callable=MagicMock)
|
||||
def test_reset_admin_password(self, reset_admin_password, app_status_get):
|
||||
reset_admin_password.return_value = None
|
||||
app_status_get.return_value = MagicMock()
|
||||
|
||||
admin_password = "password"
|
||||
self.manager.reset_admin_password(self.context, admin_password)
|
||||
app_status_get.assert_any_call()
|
||||
reset_admin_password.assert_called_with(admin_password)
|
||||
|
||||
@patch.object(mysql_service.BaseMySqlAppStatus, 'get',
|
||||
new_callable=MagicMock)
|
||||
@patch.object(galera_service.GaleraApp, 'get_cluster_context')
|
||||
def test_get_cluster_context(self, get_cluster_ctxt, app_status_get):
|
||||
get_cluster_ctxt.return_value = {'cluster': 'info'}
|
||||
self.manager.get_cluster_context(self.context)
|
||||
app_status_get.assert_any_call()
|
||||
get_cluster_ctxt.assert_any_call()
|
||||
|
||||
@patch.object(mysql_service.BaseMySqlAppStatus, 'get',
|
||||
new_callable=MagicMock)
|
||||
@patch.object(galera_service.GaleraApp,
|
||||
'write_cluster_configuration_overrides')
|
||||
def test_write_cluster_configuration_overrides(self, conf_overries,
|
||||
app_status_get):
|
||||
cluster_configuration = "cluster_configuration"
|
||||
self.manager.write_cluster_configuration_overrides(
|
||||
self.context, cluster_configuration)
|
||||
app_status_get.assert_any_call()
|
||||
conf_overries.assert_called_with(cluster_configuration)
|
||||
|
||||
@patch.object(mysql_service.BaseMySqlAppStatus, 'get',
|
||||
new_callable=MagicMock)
|
||||
@patch.object(mysql_service.BaseMySqlAdmin, 'enable_root')
|
||||
def test_enable_root_with_password(self, reset_admin_pwd,
|
||||
app_status_get):
|
||||
admin_password = "password"
|
||||
self.manager.enable_root_with_password(self.context, admin_password)
|
||||
reset_admin_pwd.assert_called_with(admin_password)
|
|
@ -0,0 +1,66 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 mock import MagicMock
|
||||
from mock import patch
|
||||
import testtools
|
||||
|
||||
from trove.common.context import TroveContext
|
||||
from trove.guestagent.datastore.experimental.mariadb import (
|
||||
manager as mariadb_manager)
|
||||
from trove.guestagent.datastore.experimental.mariadb import (
|
||||
service as mariadb_service)
|
||||
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
||||
|
||||
|
||||
class GuestAgentManagerTest(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GuestAgentManagerTest, self).setUp()
|
||||
self.manager = mariadb_manager.Manager()
|
||||
self.context = TroveContext()
|
||||
patcher_rs = patch(
|
||||
'trove.guestagent.strategies.replication.get_instance')
|
||||
patcher_rs.start()
|
||||
self.addCleanup(patcher_rs.stop)
|
||||
|
||||
@patch.object(mysql_service.BaseMySqlAppStatus, 'get',
|
||||
new_callable=MagicMock)
|
||||
@patch.object(mariadb_service.MariaDBApp, 'install_cluster',
|
||||
new_callable=MagicMock)
|
||||
def test_install_cluster(self, install_cluster, app_status_get):
|
||||
install_cluster.return_value = MagicMock()
|
||||
app_status_get.return_value = None
|
||||
|
||||
replication_user = "repuser"
|
||||
configuration = "configuration"
|
||||
bootstrap = True
|
||||
self.manager.install_cluster(self.context, replication_user,
|
||||
configuration, bootstrap)
|
||||
app_status_get.assert_any_call()
|
||||
install_cluster.assert_called_with(
|
||||
replication_user, configuration, bootstrap)
|
||||
|
||||
@patch.object(mysql_service.BaseMySqlAppStatus, 'get',
|
||||
new_callable=MagicMock)
|
||||
@patch.object(mariadb_service.MariaDBApp, 'reset_admin_password',
|
||||
new_callable=MagicMock)
|
||||
def test_reset_admin_password(self, reset_admin_password, app_status_get):
|
||||
reset_admin_password.return_value = None
|
||||
app_status_get.return_value = MagicMock()
|
||||
|
||||
admin_password = "password"
|
||||
self.manager.reset_admin_password(self.context, admin_password)
|
||||
app_status_get.assert_any_call()
|
||||
reset_admin_password.assert_called_with(admin_password)
|
|
@ -1,80 +0,0 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 mock import Mock
|
||||
from mock import patch
|
||||
|
||||
from trove.guestagent.datastore.experimental.pxc.manager import Manager
|
||||
import trove.guestagent.datastore.experimental.pxc.service as dbaas
|
||||
import trove.guestagent.datastore.mysql_common.service as mysql_common
|
||||
from trove.tests.unittests import trove_testtools
|
||||
|
||||
|
||||
class GuestAgentManagerTest(trove_testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(GuestAgentManagerTest, self).setUp()
|
||||
self.manager = Manager()
|
||||
self.context = trove_testtools.TroveTestContext(self)
|
||||
self.patcher_rs = patch(
|
||||
'trove.guestagent.strategies.replication.get_instance')
|
||||
self.mock_rs_class = self.patcher_rs.start()
|
||||
|
||||
status_patcher = patch.object(dbaas.PXCAppStatus, 'get',
|
||||
return_value=Mock())
|
||||
self.addCleanup(status_patcher.stop)
|
||||
self.status_get_mock = status_patcher.start()
|
||||
|
||||
def tearDown(self):
|
||||
super(GuestAgentManagerTest, self).tearDown()
|
||||
self.patcher_rs.stop()
|
||||
|
||||
@patch.object(dbaas.PXCApp, 'install_cluster')
|
||||
def test_install_cluster(self, install_cluster_mock):
|
||||
replication_user = "repuser"
|
||||
configuration = "configuration"
|
||||
bootstrap = True
|
||||
self.manager.install_cluster(self.context, replication_user,
|
||||
configuration, bootstrap)
|
||||
self.status_get_mock.assert_any_call()
|
||||
install_cluster_mock.assert_called_with(
|
||||
replication_user, configuration, bootstrap)
|
||||
|
||||
@patch.object(dbaas.PXCApp, 'reset_admin_password')
|
||||
def test_reset_admin_password(self, reset_admin_pwd):
|
||||
admin_password = "password"
|
||||
self.manager.reset_admin_password(self.context, admin_password)
|
||||
self.status_get_mock.assert_any_call()
|
||||
reset_admin_pwd.assert_called_with(admin_password)
|
||||
|
||||
@patch.object(dbaas.PXCApp, 'get_cluster_context')
|
||||
def test_get_cluster_context(self, get_cluster_ctxt):
|
||||
get_cluster_ctxt.return_value = {'cluster': 'info'}
|
||||
self.manager.get_cluster_context(self.context)
|
||||
self.status_get_mock.assert_any_call()
|
||||
get_cluster_ctxt.assert_any_call()
|
||||
|
||||
@patch.object(dbaas.PXCApp, 'write_cluster_configuration_overrides')
|
||||
def test_write_cluster_configuration_overrides(self, conf_overries):
|
||||
cluster_configuration = "cluster_configuration"
|
||||
self.manager.write_cluster_configuration_overrides(
|
||||
self.context, cluster_configuration)
|
||||
self.status_get_mock.assert_any_call()
|
||||
conf_overries.assert_called_with(cluster_configuration)
|
||||
|
||||
@patch.object(mysql_common.BaseMySqlAdmin, 'enable_root')
|
||||
def test_enable_root_with_password(self, reset_admin_pwd):
|
||||
admin_password = "password"
|
||||
self.manager.enable_root_with_password(self.context, admin_password)
|
||||
reset_admin_pwd.assert_called_with(admin_password)
|
|
@ -18,11 +18,11 @@ from mock import patch
|
|||
|
||||
from trove.cluster.models import ClusterTasks as ClusterTaskStatus
|
||||
from trove.cluster.models import DBCluster
|
||||
from trove.common import exception
|
||||
from trove.common.strategies.cluster.experimental.pxc.taskmanager import (
|
||||
PXCClusterTasks as ClusterTasks)
|
||||
from trove.common.strategies.cluster.experimental.pxc.taskmanager import (
|
||||
PXCTaskManagerStrategy as task_strategy)
|
||||
from trove.common.exception import GuestError
|
||||
from trove.common.strategies.cluster.experimental.galera_common.taskmanager \
|
||||
import GaleraCommonClusterTasks
|
||||
from trove.common.strategies.cluster.experimental.galera_common.taskmanager \
|
||||
import GaleraCommonTaskManagerStrategy
|
||||
from trove.datastore import models as datastore_models
|
||||
from trove.instance.models import BaseInstance
|
||||
from trove.instance.models import DBInstance
|
||||
|
@ -34,9 +34,9 @@ from trove.tests.unittests import trove_testtools
|
|||
from trove.tests.unittests.util import util
|
||||
|
||||
|
||||
class PXCClusterTasksTest(trove_testtools.TestCase):
|
||||
class GaleraClusterTasksTest(trove_testtools.TestCase):
|
||||
def setUp(self):
|
||||
super(PXCClusterTasksTest, self).setUp()
|
||||
super(GaleraClusterTasksTest, self).setUp()
|
||||
util.init_db()
|
||||
self.cluster_id = "1232"
|
||||
self.cluster_name = "Cluster-1234"
|
||||
|
@ -78,10 +78,9 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
mock_ds1.name = 'pxc'
|
||||
mock_dv1 = Mock()
|
||||
mock_dv1.name = '7.1'
|
||||
self.clustertasks = ClusterTasks(Mock(),
|
||||
self.db_cluster,
|
||||
datastore=mock_ds1,
|
||||
datastore_version=mock_dv1)
|
||||
self.clustertasks = GaleraCommonClusterTasks(
|
||||
Mock(), self.db_cluster, datastore=mock_ds1,
|
||||
datastore_version=mock_dv1)
|
||||
self.cluster_context = {
|
||||
'replication_user': {
|
||||
'name': "name",
|
||||
|
@ -91,7 +90,7 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
'admin_password': "admin_password"
|
||||
}
|
||||
|
||||
@patch.object(ClusterTasks, 'update_statuses_on_failure')
|
||||
@patch.object(GaleraCommonClusterTasks, 'update_statuses_on_failure')
|
||||
@patch.object(InstanceServiceStatus, 'find_by')
|
||||
@patch('trove.taskmanager.models.LOG')
|
||||
def test_all_instances_ready_bad_status(self, mock_logging,
|
||||
|
@ -111,17 +110,16 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
self.cluster_id)
|
||||
self.assertTrue(ret_val)
|
||||
|
||||
@patch('trove.common.strategies.cluster.experimental.pxc.taskmanager.LOG')
|
||||
@patch.object(ClusterTasks, 'update_statuses_on_failure')
|
||||
@patch.object(ClusterTasks, '_all_instances_ready', return_value=False)
|
||||
@patch.object(GaleraCommonClusterTasks, 'update_statuses_on_failure')
|
||||
@patch.object(GaleraCommonClusterTasks, '_all_instances_ready',
|
||||
return_value=False)
|
||||
@patch.object(Instance, 'load')
|
||||
@patch.object(DBInstance, 'find_all')
|
||||
@patch.object(datastore_models.Datastore, 'load')
|
||||
@patch.object(datastore_models.DatastoreVersion, 'load_by_uuid')
|
||||
def test_create_cluster_instance_not_ready(self, mock_dv, mock_ds,
|
||||
mock_find_all, mock_load,
|
||||
mock_ready, mock_update,
|
||||
mock_logging):
|
||||
mock_ready, mock_update):
|
||||
mock_find_all.return_value.all.return_value = [self.dbinst1]
|
||||
mock_load.return_value = BaseInstance(Mock(),
|
||||
self.dbinst1, Mock(),
|
||||
|
@ -130,15 +128,16 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
self.clustertasks.create_cluster(Mock(), self.cluster_id)
|
||||
mock_update.assert_called_with(self.cluster_id)
|
||||
|
||||
@patch.object(ClusterTasks, 'update_statuses_on_failure')
|
||||
@patch.object(ClusterTasks, 'reset_task')
|
||||
@patch.object(ClusterTasks, 'get_ip')
|
||||
@patch.object(ClusterTasks, '_all_instances_ready')
|
||||
@patch.object(GaleraCommonClusterTasks, 'update_statuses_on_failure')
|
||||
@patch.object(GaleraCommonClusterTasks, 'reset_task')
|
||||
@patch.object(GaleraCommonClusterTasks, 'get_ip')
|
||||
@patch.object(GaleraCommonClusterTasks, '_all_instances_ready')
|
||||
@patch.object(Instance, 'load')
|
||||
@patch.object(DBInstance, 'find_all')
|
||||
@patch.object(datastore_models.Datastore, 'load')
|
||||
@patch.object(datastore_models.DatastoreVersion, 'load_by_uuid')
|
||||
@patch('trove.common.strategies.cluster.experimental.pxc.taskmanager.LOG')
|
||||
@patch('trove.common.strategies.cluster.experimental.galera_common.'
|
||||
'taskmanager.LOG')
|
||||
def test_create_cluster_fail(self, mock_logging, mock_dv, mock_ds,
|
||||
mock_find_all, mock_load, mock_ready, mock_ip,
|
||||
mock_reset_task, mock_update_status):
|
||||
|
@ -149,16 +148,16 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
ServiceStatuses.NEW))
|
||||
mock_ip.return_value = "10.0.0.2"
|
||||
guest_client = Mock()
|
||||
guest_client.install_cluster = Mock(
|
||||
side_effect=exception.GuestError("Error"))
|
||||
with patch.object(ClusterTasks, 'get_guest',
|
||||
guest_client.install_cluster = Mock(side_effect=GuestError("Error"))
|
||||
with patch.object(GaleraCommonClusterTasks, 'get_guest',
|
||||
return_value=guest_client):
|
||||
self.clustertasks.create_cluster(Mock(), self.cluster_id)
|
||||
mock_update_status.assert_called_with('1232')
|
||||
mock_reset_task.assert_called_with()
|
||||
|
||||
@patch.object(ClusterTasks, 'update_statuses_on_failure')
|
||||
@patch('trove.common.strategies.cluster.experimental.pxc.taskmanager.LOG')
|
||||
@patch.object(GaleraCommonClusterTasks, 'update_statuses_on_failure')
|
||||
@patch('trove.common.strategies.cluster.experimental.galera_common.'
|
||||
'taskmanager.LOG')
|
||||
def test_grow_cluster_does_not_exist(self, mock_logging,
|
||||
mock_update_status):
|
||||
context = Mock()
|
||||
|
@ -169,12 +168,13 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
'1234',
|
||||
status=InstanceTasks.GROWING_ERROR)
|
||||
|
||||
@patch.object(ClusterTasks, '_check_cluster_for_root')
|
||||
@patch.object(ClusterTasks, 'reset_task')
|
||||
@patch.object(ClusterTasks, '_render_cluster_config')
|
||||
@patch.object(ClusterTasks, 'get_ip')
|
||||
@patch.object(ClusterTasks, 'get_guest')
|
||||
@patch.object(ClusterTasks, '_all_instances_ready', return_value=True)
|
||||
@patch.object(GaleraCommonClusterTasks, '_check_cluster_for_root')
|
||||
@patch.object(GaleraCommonClusterTasks, 'reset_task')
|
||||
@patch.object(GaleraCommonClusterTasks, '_render_cluster_config')
|
||||
@patch.object(GaleraCommonClusterTasks, 'get_ip')
|
||||
@patch.object(GaleraCommonClusterTasks, 'get_guest')
|
||||
@patch.object(GaleraCommonClusterTasks, '_all_instances_ready',
|
||||
return_value=True)
|
||||
@patch.object(Instance, 'load')
|
||||
@patch.object(DBInstance, 'find_all')
|
||||
@patch.object(datastore_models.Datastore, 'load')
|
||||
|
@ -195,13 +195,13 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
new_instances)
|
||||
mock_reset_task.assert_called_with()
|
||||
|
||||
@patch.object(ClusterTasks, 'reset_task')
|
||||
@patch.object(GaleraCommonClusterTasks, 'reset_task')
|
||||
@patch.object(Instance, 'load')
|
||||
@patch.object(Instance, 'delete')
|
||||
@patch.object(DBInstance, 'find_all')
|
||||
@patch.object(ClusterTasks, 'get_guest')
|
||||
@patch.object(ClusterTasks, 'get_ip')
|
||||
@patch.object(ClusterTasks, '_render_cluster_config')
|
||||
@patch.object(GaleraCommonClusterTasks, 'get_guest')
|
||||
@patch.object(GaleraCommonClusterTasks, 'get_ip')
|
||||
@patch.object(GaleraCommonClusterTasks, '_render_cluster_config')
|
||||
def test_shrink_cluster_success(self, mock_render, mock_ip, mock_guest,
|
||||
mock_find_all, mock_delete, mock_load,
|
||||
mock_reset_task):
|
||||
|
@ -215,8 +215,9 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
remove_instances)
|
||||
mock_reset_task.assert_called_with()
|
||||
|
||||
@patch.object(ClusterTasks, 'update_statuses_on_failure')
|
||||
@patch('trove.common.strategies.cluster.experimental.pxc.taskmanager.LOG')
|
||||
@patch.object(GaleraCommonClusterTasks, 'update_statuses_on_failure')
|
||||
@patch('trove.common.strategies.cluster.experimental.galera_common.'
|
||||
'taskmanager.LOG')
|
||||
def test_shrink_cluster_does_not_exist(self, mock_logging,
|
||||
mock_update_status):
|
||||
context = Mock()
|
||||
|
@ -229,17 +230,17 @@ class PXCClusterTasksTest(trove_testtools.TestCase):
|
|||
status=InstanceTasks.SHRINKING_ERROR)
|
||||
|
||||
|
||||
class PXCTaskManagerStrategyTest(trove_testtools.TestCase):
|
||||
class GaleraTaskManagerStrategyTest(trove_testtools.TestCase):
|
||||
|
||||
def test_task_manager_cluster_tasks_class(self):
|
||||
percona_strategy = task_strategy()
|
||||
strategy = GaleraCommonTaskManagerStrategy()
|
||||
self.assertFalse(
|
||||
hasattr(percona_strategy.task_manager_cluster_tasks_class,
|
||||
hasattr(strategy.task_manager_cluster_tasks_class,
|
||||
'rebuild_cluster'))
|
||||
self.assertTrue(callable(
|
||||
percona_strategy.task_manager_cluster_tasks_class.create_cluster))
|
||||
strategy.task_manager_cluster_tasks_class.create_cluster))
|
||||
|
||||
def test_task_manager_api_class(self):
|
||||
percona_strategy = task_strategy()
|
||||
self.assertFalse(hasattr(percona_strategy.task_manager_api_class,
|
||||
strategy = GaleraCommonTaskManagerStrategy()
|
||||
self.assertFalse(hasattr(strategy.task_manager_api_class,
|
||||
'add_new_node'))
|
Loading…
Reference in New Issue