Fix secure method to work with PXC
When PXC was implemented, the secure method was overridden as the stock one was causing problems on prepare. This issue seems to be caused by the order of work done in the call. If the admin user is set up first and then used to delete the anon users, everything works for all flavors of MySQL. Tested using the scenario tests on MySQL, Percona, PXC and MariaDB. Also cleaned up some of the common code and made the connection URL more resilient to special password characters. Updated the 'supported' tests and fixed the user actions one to work with PXC. Change-Id: Idf9a096b8870e428090454528b79930f3d2fd760 Closes-Bug: #1585355
This commit is contained in:
parent
ff49045744
commit
0eb332ae8a
|
@ -15,12 +15,8 @@
|
|||
#
|
||||
|
||||
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 as utils
|
||||
from trove.guestagent.datastore.galera_common import service as galera_service
|
||||
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
||||
|
||||
|
@ -52,43 +48,6 @@ class PXCApp(galera_service.GaleraApp):
|
|||
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()
|
||||
mysql_service.clear_expired_password()
|
||||
uri = "mysql+pymysql://root:@localhost:3306"
|
||||
engine = sqlalchemy.create_engine(uri, echo=True)
|
||||
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 self.local_sql_client(engine) as client:
|
||||
query = text("select Host, User from mysql.user;")
|
||||
client.execute(query)
|
||||
except Exception:
|
||||
LOG.debug('failed to query mysql')
|
||||
# creating the admin user after the config files are written because
|
||||
# pxc was not committing the grant for the admin user after removing
|
||||
# the annon users.
|
||||
self._wait_for_mysql_to_be_really_alive(
|
||||
CONF.timeout_wait_for_service)
|
||||
with self.local_sql_client(engine) as client:
|
||||
self._create_admin_user(client, admin_password)
|
||||
self.stop_db()
|
||||
|
||||
self._reset_configuration(config_contents, admin_password)
|
||||
self.start_mysql()
|
||||
self._wait_for_mysql_to_be_really_alive(
|
||||
CONF.timeout_wait_for_service)
|
||||
LOG.debug("MySQL secure complete.")
|
||||
|
||||
|
||||
class PXCRootAccess(mysql_service.BaseMySqlRootAccess):
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
import abc
|
||||
|
||||
from oslo_log import log as logging
|
||||
import sqlalchemy
|
||||
from sqlalchemy.sql.expression import text
|
||||
|
||||
from trove.common.i18n import _
|
||||
|
@ -36,21 +35,6 @@ class GaleraApp(service.BaseMySqlApp):
|
|||
super(GaleraApp, self).__init__(status, local_sql_client,
|
||||
keep_alive_connection_cls)
|
||||
|
||||
def _test_mysql(self):
|
||||
uri = "mysql+pymysql://root:@localhost:3306"
|
||||
engine = sqlalchemy.create_engine(uri, 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:
|
||||
|
|
|
@ -230,7 +230,6 @@ class MySqlManager(manager.Manager):
|
|||
if backup_info:
|
||||
self._perform_restore(backup_info, context,
|
||||
mount_point + "/data", app)
|
||||
LOG.debug("Securing MySQL now.")
|
||||
app.secure(config_contents)
|
||||
enable_root_on_restore = (backup_info and
|
||||
self.mysql_admin().is_root_enabled())
|
||||
|
|
|
@ -21,6 +21,7 @@ from collections import defaultdict
|
|||
import os
|
||||
import re
|
||||
import six
|
||||
import urllib
|
||||
import uuid
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
@ -47,6 +48,7 @@ from trove.guestagent.db import models
|
|||
from trove.guestagent import pkg
|
||||
|
||||
ADMIN_USER_NAME = "os_admin"
|
||||
CONNECTION_STR_FORMAT = "mysql+pymysql://%s:%s@127.0.0.1:3306"
|
||||
LOG = logging.getLogger(__name__)
|
||||
FLUSH = text(sql_query.FLUSH)
|
||||
ENGINE = None
|
||||
|
@ -606,14 +608,11 @@ class BaseMySqlApp(object):
|
|||
return ENGINE
|
||||
|
||||
pwd = self.get_auth_password()
|
||||
uri = ("mysql+pymysql://%s:%s@localhost:3306"
|
||||
% (ADMIN_USER_NAME, pwd.strip()))
|
||||
ENGINE = sqlalchemy.create_engine(uri,
|
||||
pool_recycle=7200,
|
||||
echo=CONF.sql_query_logging,
|
||||
listeners=[
|
||||
self.keep_alive_connection_cls()]
|
||||
)
|
||||
ENGINE = sqlalchemy.create_engine(
|
||||
CONNECTION_STR_FORMAT % (ADMIN_USER_NAME,
|
||||
urllib.quote(pwd.strip())),
|
||||
pool_recycle=120, echo=CONF.sql_query_logging,
|
||||
listeners=[self.keep_alive_connection_cls()])
|
||||
return ENGINE
|
||||
|
||||
@classmethod
|
||||
|
@ -648,11 +647,13 @@ class BaseMySqlApp(object):
|
|||
Create a os_admin user with a random password
|
||||
with all privileges similar to the root user.
|
||||
"""
|
||||
LOG.debug("Creating Trove admin user '%s'." % ADMIN_USER_NAME)
|
||||
localhost = "localhost"
|
||||
g = sql_query.Grant(permissions='ALL', user=ADMIN_USER_NAME,
|
||||
host=localhost, grant_option=True, clear=password)
|
||||
t = text(str(g))
|
||||
client.execute(t)
|
||||
LOG.debug("Trove admin user '%s' created." % ADMIN_USER_NAME)
|
||||
|
||||
@staticmethod
|
||||
def _generate_root_password(client):
|
||||
|
@ -680,20 +681,26 @@ class BaseMySqlApp(object):
|
|||
self.start_mysql()
|
||||
|
||||
def secure(self, config_contents):
|
||||
LOG.info(_("Generating admin password."))
|
||||
admin_password = utils.generate_random_password()
|
||||
LOG.debug("Securing MySQL now.")
|
||||
clear_expired_password()
|
||||
uri = "mysql+pymysql://root:@localhost:3306"
|
||||
engine = sqlalchemy.create_engine(uri, echo=True)
|
||||
with self.local_sql_client(engine) as client:
|
||||
self._remove_anonymous_user(client)
|
||||
LOG.debug("Generating admin password.")
|
||||
admin_password = utils.generate_random_password()
|
||||
engine = sqlalchemy.create_engine(
|
||||
CONNECTION_STR_FORMAT % ('root', ''), echo=True)
|
||||
with self.local_sql_client(engine, use_flush=False) as client:
|
||||
self._create_admin_user(client, admin_password)
|
||||
|
||||
self.stop_db()
|
||||
LOG.debug("Switching to the '%s' user now." % ADMIN_USER_NAME)
|
||||
engine = sqlalchemy.create_engine(
|
||||
CONNECTION_STR_FORMAT % (ADMIN_USER_NAME,
|
||||
urllib.quote(admin_password)),
|
||||
echo=True)
|
||||
with self.local_sql_client(engine) as client:
|
||||
self._remove_anonymous_user(client)
|
||||
|
||||
self.stop_db()
|
||||
self._reset_configuration(config_contents, admin_password)
|
||||
self.start_mysql()
|
||||
|
||||
LOG.debug("MySQL secure complete.")
|
||||
|
||||
def _reset_configuration(self, configuration, admin_password=None):
|
||||
|
@ -774,12 +781,16 @@ class BaseMySqlApp(object):
|
|||
raise RuntimeError("Could not stop MySQL!")
|
||||
|
||||
def _remove_anonymous_user(self, client):
|
||||
LOG.debug("Removing anonymous user.")
|
||||
t = text(sql_query.REMOVE_ANON)
|
||||
client.execute(t)
|
||||
LOG.debug("Anonymous user removed.")
|
||||
|
||||
def _remove_remote_root_access(self, client):
|
||||
LOG.debug("Removing root access.")
|
||||
t = text(sql_query.REMOVE_ROOT)
|
||||
client.execute(t)
|
||||
LOG.debug("Root access removed.")
|
||||
|
||||
def restart(self):
|
||||
try:
|
||||
|
|
|
@ -228,7 +228,8 @@ register(["mongodb_supported"], common_groups,
|
|||
backup_groups, cluster_actions_groups, configuration_groups,
|
||||
database_actions_groups, root_actions_groups, user_actions_groups)
|
||||
register(["pxc_supported"], common_groups,
|
||||
cluster_actions_groups, root_actions_groups)
|
||||
backup_groups, configuration_groups, database_actions_groups,
|
||||
cluster_actions_groups, root_actions_groups, user_actions_groups)
|
||||
register(["redis_supported"], common_groups,
|
||||
backup_groups, replication_groups, cluster_actions_groups)
|
||||
register(["vertica_supported"], common_groups,
|
||||
|
|
|
@ -474,3 +474,9 @@ class PerconaUserActionsRunner(MysqlUserActionsRunner):
|
|||
|
||||
def __init__(self):
|
||||
super(PerconaUserActionsRunner, self).__init__()
|
||||
|
||||
|
||||
class PxcUserActionsRunner(MysqlUserActionsRunner):
|
||||
|
||||
def __init__(self):
|
||||
super(PxcUserActionsRunner, self).__init__()
|
||||
|
|
|
@ -426,7 +426,7 @@ class MySqlAdminTest(trove_testtools.TestCase):
|
|||
dbaas.orig_configuration_manager = dbaas.MySqlApp.configuration_manager
|
||||
dbaas.MySqlApp.configuration_manager = Mock()
|
||||
dbaas.orig_get_auth_password = dbaas.MySqlApp.get_auth_password
|
||||
dbaas.MySqlApp.get_auth_password = Mock()
|
||||
dbaas.MySqlApp.get_auth_password = Mock(return_value='root_pwd')
|
||||
self.orig_configuration_manager = \
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager
|
||||
mysql_common_service.BaseMySqlApp.configuration_manager = Mock()
|
||||
|
@ -1586,8 +1586,6 @@ class MySqlAppMockTest(trove_testtools.TestCase):
|
|||
app = MySqlApp(mock_status)
|
||||
app._reset_configuration = MagicMock()
|
||||
app.start_mysql = MagicMock(return_value=None)
|
||||
app._wait_for_mysql_to_be_really_alive = MagicMock(
|
||||
return_value=True)
|
||||
app.stop_db = MagicMock(return_value=None)
|
||||
app.secure('foo')
|
||||
reset_config_calls = [call('foo', auth_pwd_mock.return_value)]
|
||||
|
|
Loading…
Reference in New Issue