Merge "Implement MariaDB Clustering"
This commit is contained in:
@@ -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',
|
||||
@@ -1183,7 +1184,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).'),
|
||||
@@ -1251,6 +1252,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
|
||||
81
trove/guestagent/datastore/galera_common/manager.py
Normal file
81
trove/guestagent/datastore/galera_common/manager.py
Normal file
@@ -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)
|
||||
109
trove/guestagent/datastore/galera_common/service.py
Normal file
109
trove/guestagent/datastore/galera_common/service.py
Normal file
@@ -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:
|
||||
|
||||
23
trove/templates/mariadb/cluster.config.template
Normal file
23
trove/templates/mariadb/cluster.config.template
Normal file
@@ -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):
|
||||
123
trove/tests/unittests/guestagent/test_galera_manager.py
Normal file
123
trove/tests/unittests/guestagent/test_galera_manager.py
Normal file
@@ -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)
|
||||
66
trove/tests/unittests/guestagent/test_mariadb_manager.py
Normal file
66
trove/tests/unittests/guestagent/test_mariadb_manager.py
Normal file
@@ -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'))
|
||||
Reference in New Issue
Block a user