Add support to change root password

Change the configured root's password in percona and update the leader
settings.

Change-Id: I7168a96cac7a3b4df7fcfa1afc6f35076748578b
Partial-Bug: 1436093
This commit is contained in:
Felipe Reyes 2017-06-13 22:42:18 -04:00
parent 62ed38b7b2
commit e7f611ff2e
5 changed files with 104 additions and 2 deletions

View File

@ -105,6 +105,7 @@ from percona_utils import (
pxc_installed,
update_bootstrap_uuid,
LeaderNoBootstrapUUIDError,
update_root_password,
)
@ -338,6 +339,11 @@ def config_changed():
open_port(DEFAULT_MYSQL_PORT)
# the password needs to be updated only if the node was already
# bootstrapped
if bootstrapped:
update_root_password()
@hooks.hook('cluster-relation-joined')
def cluster_joined():

View File

@ -820,3 +820,30 @@ def pxc_installed():
@returns: boolean: indicating installation
'''
return os.path.exists('/usr/sbin/mysqld')
def update_root_password():
"""Update root password if needed
:returns: `False` when configured password has not changed
"""
cfg = config()
if not cfg.changed('root-password'):
return False
m_helper = get_db_helper()
# password that needs to be set
new_root_passwd = cfg['root-password']
m_helper.set_mysql_root_password(new_root_passwd)
# check the password was changed
try:
m_helper.connect(user='root', password=new_root_passwd)
m_helper.execute('select 1;')
except OperationalError as ex:
log("Error connecting using new passowrd: %s" % str(ex), level=DEBUG)
log(('Cannot connect using new password, not updating password in '
'the relation'), level=WARNING)
return

View File

@ -13,6 +13,9 @@ from charmhelpers.contrib.openstack.amulet.deployment import (
from charmhelpers.contrib.amulet.utils import AmuletUtils
PXC_ROOT_PASSWD = 'ubuntu'
class BasicDeployment(OpenStackAmuletDeployment):
utils = AmuletUtils()
@ -73,7 +76,8 @@ class BasicDeployment(OpenStackAmuletDeployment):
def _get_configs(self):
"""Configure all of the services."""
cfg_percona = {'min-cluster-size': self.units,
'vip': self.vip}
'vip': self.vip,
'root-password': PXC_ROOT_PASSWD}
cfg_ha = {'debug': True,
'corosync_key': ('xZP7GDWV0e8Qs0GxWThXirNNYlScgi3sRTdZk/IXKD'
@ -240,6 +244,31 @@ class BasicDeployment(OpenStackAmuletDeployment):
assert self.is_port_open(address=self.vip), 'cannot connect to vip'
def test_change_root_password(self):
"""
Change root password and verify the change was effectively applied.
"""
new_root_passwd = 'openstack'
u = self.master_unit
root_password, _ = PXC_ROOT_PASSWD
cmd = "mysql -uroot -p{} -e\"select 1;\" ".format(root_password)
output, code = u.run(cmd)
assert code == 0, output
self.d.configure('percona-cluster', {'root-password': new_root_passwd})
time.sleep(5) # give some time to the unit to start the hook
self.d.sentry.wait() # wait until the hook finishes
# try to connect using the new root password
cmd = "mysql -uroot -p{} -e\"select 1;\" ".format(new_root_passwd)
output, code = u.run(cmd)
assert code == 0, output
def find_master(self, ha=True):
for unit in self.d.sentry['percona-cluster']:
if not ha:

View File

@ -523,6 +523,8 @@ class TestUpdateBootstrapUUID(CharmTestCase):
'relation_set',
'is_leader',
'leader_set',
'config',
'leader_get',
]
def setUp(self):
@ -583,3 +585,24 @@ class TestUpdateBootstrapUUID(CharmTestCase):
self.get_wsrep_value.side_effect = fake_wsrep
self.assertRaises(percona_utils.InconsistentUUIDError,
percona_utils.update_bootstrap_uuid)
@mock.patch('charmhelpers.contrib.database.mysql.leader_set')
@mock.patch('charmhelpers.contrib.database.mysql.is_leader')
@mock.patch('charmhelpers.contrib.database.mysql.leader_get')
def test_update_root_password(self, mock_leader_get, mock_is_leader,
mock_leader_set):
cur_password = 'openstack'
new_password = 'ubuntu'
leader_config = {'mysql.passwd': cur_password}
mock_leader_get.side_effect = lambda k: leader_config[k]
mock_is_leader.return_value = True
self.config.side_effect = self.test_config.get
self.assertFalse(percona_utils.update_root_password())
self.test_config.set_previous('root-password', cur_password)
self.test_config.set('root-password', new_password)
percona_utils.update_root_password()
mock_leader_set.assert_called_with(
settings={'mysql.passwd': new_password})

View File

@ -70,10 +70,24 @@ class TestConfig(object):
def __init__(self):
self.config = get_default_config()
self.config_prev = {}
def previous(self, k):
return self.config_prev[k] if k in self.config_prev else self.config[k]
def set_previous(self, k, v):
self.config_prev[k] = v
def unset_previous(self, k):
if k in self.config_prev:
self.config_prev.pop(k)
def changed(self, k):
return self.get(k) != self.previous(k)
def get(self, attr=None):
if not attr:
return self.get_all()
return self
try:
return self.config[attr]
except KeyError:
@ -87,6 +101,9 @@ class TestConfig(object):
raise KeyError
self.config[attr] = value
def __getitem__(self, k):
return self.get(k)
class TestRelation(object):