Allow for multiple instantiations of mysql-router
Before this change it was not possible to run more than one instantiation of mysql-router on a single unit. The Nova cells deployments require nova-cloud-controller to be connected to two different databases, therefore, requiring two instantiations of mysql-router. This change includes all the necessary steps to run multiple instantiations of mysql-router on a single unit. Review and merge charm-helpers PR: https://github.com/juju/charm-helpers/pull/469 Closes-Bug: #1876188 Depends-On: Iafcc106fca44479e89b4b66a0a3988ffeee01f04 Change-Id: I76d1bfdccf9a551188c9f86c06ee0a22c07d47cf
This commit is contained in:
parent
a3457b978f
commit
4032165915
|
@ -14,7 +14,6 @@
|
|||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import charms_openstack.charm
|
||||
|
@ -27,6 +26,8 @@ import charmhelpers.contrib.network.ip as ch_net_ip
|
|||
|
||||
import charmhelpers.contrib.database.mysql as mysql
|
||||
|
||||
import charmhelpers.contrib.openstack.templating as os_templating
|
||||
|
||||
|
||||
MYSQLROUTER_CNF = "/var/lib/mysql/mysqlrouter/mysqlrouter.conf"
|
||||
|
||||
|
@ -52,14 +53,18 @@ def shared_db_address(cls):
|
|||
|
||||
class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
||||
"""Charm class for the MySQLRouter charm."""
|
||||
name = "mysqlrouter"
|
||||
name = ch_core.hookenv.service_name()
|
||||
packages = ["mysql-router"]
|
||||
release = "stein"
|
||||
release_pkg = "mysql-router"
|
||||
required_relations = ["db-router", "shared-db"]
|
||||
source_config_key = "source"
|
||||
|
||||
services = ["mysqlrouter"]
|
||||
systemd_file = os.path.join(
|
||||
"/etc/systemd/system",
|
||||
"{}.service".format(name))
|
||||
|
||||
services = [name]
|
||||
restart_map = {
|
||||
MYSQLROUTER_CNF: services,
|
||||
}
|
||||
|
@ -170,6 +175,10 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
"""
|
||||
return self.options.shared_db_address
|
||||
|
||||
@property
|
||||
def mysqlrouter_port(self):
|
||||
return self.options.base_port
|
||||
|
||||
@property
|
||||
def mysqlrouter_working_dir(self):
|
||||
"""Determine the path to the mysqlrouter working directory.
|
||||
|
@ -179,7 +188,7 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
:returns: Path to the directory
|
||||
:rtype: str
|
||||
"""
|
||||
return "{}/mysqlrouter".format(self.mysqlrouter_home_dir)
|
||||
return "{}/{}".format(self.mysqlrouter_home_dir, self.name)
|
||||
|
||||
@property
|
||||
def mysqlrouter_home_dir(self):
|
||||
|
@ -236,12 +245,16 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
perms=0o755)
|
||||
|
||||
# Systemd File
|
||||
_systemd_filename = "mysqlrouter.service"
|
||||
_src_systemd = os.path.join(
|
||||
ch_core.hookenv.charm_dir(), "files", _systemd_filename)
|
||||
_dst_systemd = os.path.join("/etc/systemd/system", _systemd_filename)
|
||||
shutil.copy(_src_systemd, _dst_systemd)
|
||||
cmd = ["/usr/bin/systemctl", "enable", "mysqlrouter"]
|
||||
ch_core.templating.render(
|
||||
source="mysqlrouter.service",
|
||||
template_loader=os_templating.get_loader(
|
||||
'templates/', self.release),
|
||||
target=self.systemd_file,
|
||||
context=self.adapters_instance,
|
||||
group=self.group,
|
||||
perms=0o755,
|
||||
)
|
||||
cmd = ["/usr/bin/systemctl", "enable", self.name]
|
||||
subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
|
||||
def get_db_helper(self):
|
||||
|
@ -305,7 +318,8 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
try:
|
||||
m_helper.connect(self.db_router_user,
|
||||
self.db_router_password,
|
||||
self.shared_db_address)
|
||||
self.shared_db_address,
|
||||
port=self.mysqlrouter_port)
|
||||
return True
|
||||
except mysql.MySQLdb._exceptions.OperationalError:
|
||||
ch_core.hookenv.log("Could not connect to db", "DEBUG")
|
||||
|
@ -363,7 +377,7 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
self.cluster_address),
|
||||
"--directory", self.mysqlrouter_working_dir,
|
||||
"--conf-use-sockets",
|
||||
"--conf-base-port", str(self.options.base_port)]
|
||||
"--conf-base-port", str(self.mysqlrouter_port)]
|
||||
try:
|
||||
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
ch_core.hookenv.log(output, "DEBUG")
|
||||
|
@ -385,7 +399,7 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
:returns: This function is called for its side effect
|
||||
:rtype: None
|
||||
"""
|
||||
ch_core.host.service_start("mysqlrouter")
|
||||
ch_core.host.service_start(self.name)
|
||||
reactive.flags.set_flag(MYSQL_ROUTER_STARTED)
|
||||
|
||||
def stop_mysqlrouter(self):
|
||||
|
@ -399,7 +413,7 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
:returns: This function is called for its side effect
|
||||
:rtype: None
|
||||
"""
|
||||
ch_core.host.service_stop("mysqlrouter")
|
||||
ch_core.host.service_stop(self.name)
|
||||
|
||||
def restart_mysqlrouter(self):
|
||||
"""Restart MySQL Router.
|
||||
|
@ -413,7 +427,7 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
:returns: This function is called for its side effect
|
||||
:rtype: None
|
||||
"""
|
||||
ch_core.host.service_restart("mysqlrouter")
|
||||
ch_core.host.service_restart(self.name)
|
||||
|
||||
def proxy_db_and_user_requests(
|
||||
self, receiving_interface, sending_interface):
|
||||
|
@ -492,4 +506,5 @@ class MySQLRouterCharm(charms_openstack.charm.OpenStackCharm):
|
|||
_password,
|
||||
allowed_units=_allowed_hosts,
|
||||
prefix=prefix,
|
||||
wait_timeout=_wait_timeout)
|
||||
wait_timeout=_wait_timeout,
|
||||
db_port=self.mysqlrouter_port)
|
||||
|
|
|
@ -6,8 +6,8 @@ After=network.target
|
|||
|
||||
[Service]
|
||||
Type=exec
|
||||
ExecStart=/var/lib/mysql/mysqlrouter/start.sh
|
||||
ExecStop=/var/lib/mysql/mysqlrouter/stop.sh
|
||||
ExecStart=/var/lib/mysql/{{ options.charm_instance.name }}/start.sh
|
||||
ExecStop=/var/lib/mysql/{{ options.charm_instance.name }}/stop.sh
|
||||
RemainAfterExit=yes
|
||||
Restart=on-failure
|
||||
|
|
@ -218,9 +218,11 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
|
||||
def test_mysqlrouter_working_dir(self):
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
_name = "keystone-mysql-router"
|
||||
mrc.name = _name
|
||||
self.assertEqual(
|
||||
mrc.mysqlrouter_working_dir,
|
||||
"/var/lib/mysql/mysqlrouter")
|
||||
"/var/lib/mysql/{}".format(_name))
|
||||
|
||||
def test_mysqlrouter_home_dir(self):
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
|
@ -244,12 +246,14 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
self.patch_object(
|
||||
mysql_router.charms_openstack.charm.OpenStackCharm,
|
||||
"install", "super_install")
|
||||
self.patch_object(mysql_router.shutil, "copy")
|
||||
_name = "keystone-mysql-router"
|
||||
self.patch_object(mysql_router.ch_core.templating, "render")
|
||||
self.os.path.exists.return_value = False
|
||||
self.group_exists.return_value = False
|
||||
self.user_exists.return_value = False
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
mrc.configure_source = mock.MagicMock()
|
||||
mrc.name = _name
|
||||
mrc.install()
|
||||
self.super_install.assert_called_once()
|
||||
mrc.configure_source.assert_called_once()
|
||||
|
@ -260,9 +264,9 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
self.mkdir.assert_called_once_with(
|
||||
"/var/lib/mysql", group="mysql", owner="mysql", perms=0o755)
|
||||
|
||||
self.copy.assert_called_once()
|
||||
self.render.assert_called_once()
|
||||
self.subprocess.check_output.assert_called_once_with(
|
||||
['/usr/bin/systemctl', 'enable', 'mysqlrouter'],
|
||||
['/usr/bin/systemctl', 'enable', _name],
|
||||
stderr=self.subprocess.STDOUT)
|
||||
|
||||
def test_get_db_helper(self):
|
||||
|
@ -304,6 +308,7 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
_pass = "clusterpass"
|
||||
_user = "mysqlrouteruser"
|
||||
_addr = "127.0.0.1"
|
||||
_port = 3316
|
||||
self.endpoint_from_flag.return_value = self.db_router
|
||||
self.db_router.password.return_value = _json_pass
|
||||
|
||||
|
@ -312,20 +317,21 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
self._exceptions.OperationalError = Exception
|
||||
_helper = mock.MagicMock()
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
mrc.options.base_port = _port
|
||||
mrc.get_db_helper = mock.MagicMock()
|
||||
mrc.get_db_helper.return_value = _helper
|
||||
|
||||
# Connects
|
||||
self.assertTrue(mrc.check_mysql_connection())
|
||||
_helper.connect.assert_called_once_with(
|
||||
_user, _pass, _addr)
|
||||
_user, _pass, _addr, port=_port)
|
||||
|
||||
# Fails
|
||||
_helper.reset_mock()
|
||||
_helper.connect.side_effect = self._exceptions.OperationalError
|
||||
self.assertFalse(mrc.check_mysql_connection())
|
||||
_helper.connect.assert_called_once_with(
|
||||
_user, _pass, _addr)
|
||||
_user, _pass, _addr, port=_port)
|
||||
|
||||
def test_custom_assess_status_check(self):
|
||||
_check = mock.MagicMock()
|
||||
|
@ -394,26 +400,32 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
|
||||
def test_start_mysqlrouter(self):
|
||||
self.patch_object(mysql_router.ch_core.host, "service_start")
|
||||
_name = "keystone-mysql-router"
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
mrc.name = _name
|
||||
|
||||
mrc.start_mysqlrouter()
|
||||
self.service_start.assert_called_once_with("mysqlrouter")
|
||||
self.service_start.assert_called_once_with(_name)
|
||||
self.set_flag.assert_called_once_with(
|
||||
mysql_router.MYSQL_ROUTER_STARTED)
|
||||
|
||||
def test_stop_mysqlrouter(self):
|
||||
_name = "keystone-mysql-router"
|
||||
self.patch_object(mysql_router.ch_core.host, "service_stop")
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
mrc.name = _name
|
||||
|
||||
mrc.stop_mysqlrouter()
|
||||
self.service_stop.assert_called_once_with("mysqlrouter")
|
||||
self.service_stop.assert_called_once_with(_name)
|
||||
|
||||
def test_restart_mysqlrouter(self):
|
||||
_name = "keystone-mysql-router"
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
mrc.name = _name
|
||||
self.patch_object(mysql_router.ch_core.host, "service_restart")
|
||||
|
||||
mrc.restart_mysqlrouter()
|
||||
self.service_restart.assert_called_once_with("mysqlrouter")
|
||||
self.service_restart.assert_called_once_with(_name)
|
||||
|
||||
def test_proxy_db_and_user_requests_no_prefix(self):
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
|
@ -440,10 +452,12 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
_json_pass = '"pass"'
|
||||
_pass = json.loads(_json_pass)
|
||||
_local_unit = "kmr/5"
|
||||
_port = 3316
|
||||
self.db_router.password.return_value = _json_pass
|
||||
self.local_unit.return_value = _local_unit
|
||||
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
mrc.options.base_port = _port
|
||||
self.db_router.get_prefixes.return_value = [
|
||||
mrc._unprefixed, mrc.db_prefix]
|
||||
|
||||
|
@ -455,7 +469,8 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
self.db_router, self.keystone_shared_db)
|
||||
self.keystone_shared_db.set_db_connection_info.assert_called_once_with(
|
||||
self.keystone_shared_db.relation_id, mrc.shared_db_address,
|
||||
_pass, allowed_units=None, prefix=None, wait_timeout=None)
|
||||
_pass, allowed_units=None, prefix=None, wait_timeout=None,
|
||||
db_port=_port)
|
||||
|
||||
# Allowed Units and wait time set correctly
|
||||
self.db_router.wait_timeout.return_value = _json_wait_time
|
||||
|
@ -467,7 +482,7 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
self.keystone_shared_db.set_db_connection_info.assert_called_once_with(
|
||||
self.keystone_shared_db.relation_id, mrc.shared_db_address,
|
||||
_pass, allowed_units=self.keystone_unit_name, prefix=None,
|
||||
wait_timeout=_wait_time)
|
||||
wait_timeout=_wait_time, db_port=_port)
|
||||
|
||||
# Confirm msyqlrouter credentials are not sent over the shared-db
|
||||
# relation
|
||||
|
@ -483,10 +498,12 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
_nova = "nova"
|
||||
_novaapi = "novaapi"
|
||||
_novacell0 = "novacell0"
|
||||
_port = 3316
|
||||
self.db_router.password.return_value = _json_pass
|
||||
self.local_unit.return_value = _local_unit
|
||||
|
||||
mrc = mysql_router.MySQLRouterCharm()
|
||||
mrc.options.base_port = _port
|
||||
self.db_router.get_prefixes.return_value = [
|
||||
mrc.db_prefix, _nova, _novaapi, _novacell0]
|
||||
|
||||
|
@ -498,15 +515,15 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
mock.call(
|
||||
self.nova_shared_db.relation_id, mrc.shared_db_address, _pass,
|
||||
allowed_units=None, prefix=_nova,
|
||||
wait_timeout=None),
|
||||
wait_timeout=None, db_port=_port),
|
||||
mock.call(
|
||||
self.nova_shared_db.relation_id, mrc.shared_db_address, _pass,
|
||||
allowed_units=None, prefix=_novaapi,
|
||||
wait_timeout=None),
|
||||
wait_timeout=None, db_port=_port),
|
||||
mock.call(
|
||||
self.nova_shared_db.relation_id, mrc.shared_db_address, _pass,
|
||||
allowed_units=None, prefix=_novacell0,
|
||||
wait_timeout=None),
|
||||
wait_timeout=None, db_port=_port),
|
||||
]
|
||||
self.nova_shared_db.set_db_connection_info.assert_has_calls(
|
||||
_calls, any_order=True)
|
||||
|
@ -520,15 +537,15 @@ class TestMySQLRouterCharm(test_utils.PatchHelper):
|
|||
mock.call(
|
||||
self.nova_shared_db.relation_id, mrc.shared_db_address, _pass,
|
||||
allowed_units=self.nova_unit_name, prefix=_nova,
|
||||
wait_timeout=_wait_time),
|
||||
wait_timeout=_wait_time, db_port=_port),
|
||||
mock.call(
|
||||
self.nova_shared_db.relation_id, mrc.shared_db_address, _pass,
|
||||
allowed_units=self.nova_unit_name, prefix=_novaapi,
|
||||
wait_timeout=_wait_time),
|
||||
wait_timeout=_wait_time, db_port=_port),
|
||||
mock.call(
|
||||
self.nova_shared_db.relation_id, mrc.shared_db_address, _pass,
|
||||
allowed_units=self.nova_unit_name, prefix=_novacell0,
|
||||
wait_timeout=_wait_time),
|
||||
wait_timeout=_wait_time, db_port=_port),
|
||||
]
|
||||
self.nova_shared_db.set_db_connection_info.assert_has_calls(
|
||||
_calls, any_order=True)
|
||||
|
|
Loading…
Reference in New Issue