Merge "Series Upgrade"

This commit is contained in:
Zuul 2018-09-17 14:58:06 +00:00 committed by Gerrit Code Review
commit 2d4fba0360
8 changed files with 127 additions and 2 deletions

View File

@ -21,3 +21,9 @@ check-queues:
type: string type: string
default: "/" default: "/"
description: Show queues from the specified vhost. Eg; "openstack". description: Show queues from the specified vhost. Eg; "openstack".
complete-cluster-series-upgrade:
description: |
Perform final operations post series upgrade. Inform all nodes in the
cluster the upgrade is complete cluster wide.
This action should be performed on the current leader. Note the leader may
have changed during the series upgrade process.

View File

@ -29,6 +29,8 @@ from charmhelpers.core.hookenv import (
action_fail, action_fail,
action_set, action_set,
action_get, action_get,
is_leader,
leader_set,
) )
from rabbit_utils import ( from rabbit_utils import (
@ -36,6 +38,7 @@ from rabbit_utils import (
CONFIG_FILES, CONFIG_FILES,
pause_unit_helper, pause_unit_helper,
resume_unit_helper, resume_unit_helper,
assess_status,
) )
@ -89,10 +92,24 @@ def check_queues(args):
action_fail('Failed to run rabbitmqctl list_queues') action_fail('Failed to run rabbitmqctl list_queues')
def complete_cluster_series_upgrade(args):
""" Complete the series upgrade process
After all nodes have been upgraded, this action is run to inform the whole
cluster the upgrade is done. Config files will be re-rendered with each
peer in the wsrep_cluster_address config.
"""
if is_leader():
# Unset cluster_series_upgrading
leader_set(cluster_series_upgrading="")
assess_status(ConfigRenderer(CONFIG_FILES))
# A dictionary of all the defined actions to callables (which take # A dictionary of all the defined actions to callables (which take
# parsed arguments). # parsed arguments).
ACTIONS = {"pause": pause, "resume": resume, "cluster-status": cluster_status, ACTIONS = {"pause": pause, "resume": resume, "cluster-status": cluster_status,
"check-queues": check_queues} "check-queues": check_queues,
"complete-cluster-series-upgrade": complete_cluster_series_upgrade}
def main(args): def main(args):

View File

@ -0,0 +1 @@
actions.py

1
hooks/post-series-upgrade Symbolic link
View File

@ -0,0 +1 @@
rabbitmq_server_relations.py

1
hooks/pre-series-upgrade Symbolic link
View File

@ -0,0 +1 @@
rabbitmq_server_relations.py

View File

@ -807,6 +807,9 @@ def clustered():
def assess_cluster_status(*args): def assess_cluster_status(*args):
''' Assess the status for the current running unit ''' ''' Assess the status for the current running unit '''
if is_unit_paused_set():
return "maintenance", "Paused"
# NOTE: ensure rabbitmq is actually installed before doing # NOTE: ensure rabbitmq is actually installed before doing
# any checks # any checks
if rabbitmq_is_installed(): if rabbitmq_is_installed():
@ -909,6 +912,18 @@ def assess_status_func(configs):
services=services(), ports=None) services=services(), ports=None)
if state == 'active' and clustered(): if state == 'active' and clustered():
message = 'Unit is ready and clustered' message = 'Unit is ready and clustered'
# Remind the administrator cluster_series_upgrading is set.
# If the cluster has completed the series upgrade, run the
# complete-cluster-series-upgrade action to clear this setting.
if leader_get('cluster_series_upgrading'):
message += (", Run complete-cluster-series-upgrade when the "
"cluster has completed its upgrade.")
# Edge case when the first rabbitmq unit is upgraded it will show
# waiting for peers. Force "active" workload state for various
# testing suites like zaza to recognize a successful series upgrade
# of the first unit.
if state == "waiting":
state = "active"
status_set(state, message) status_set(state, message)
return _assess_status_func return _assess_status_func

View File

@ -54,6 +54,10 @@ from charmhelpers.contrib.hahelpers.cluster import (
) )
from charmhelpers.contrib.openstack.utils import ( from charmhelpers.contrib.openstack.utils import (
is_unit_paused_set, is_unit_paused_set,
set_unit_upgrading,
is_unit_upgrading_set,
clear_unit_paused,
clear_unit_upgrading,
) )
import charmhelpers.contrib.storage.linux.ceph as ceph import charmhelpers.contrib.storage.linux.ceph as ceph
@ -71,6 +75,8 @@ from charmhelpers.core.hookenv import (
DEBUG, DEBUG,
ERROR, ERROR,
INFO, INFO,
leader_set,
leader_get,
relation_get, relation_get,
relation_clear, relation_clear,
relation_set, relation_set,
@ -735,6 +741,11 @@ MAN_PLUGIN = 'rabbitmq_management'
@rabbit.restart_on_change(rabbit.restart_map()) @rabbit.restart_on_change(rabbit.restart_map())
@harden() @harden()
def config_changed(): def config_changed():
if is_unit_paused_set():
log("Do not run config_changed while unit is paused", "WARNING")
return
# Update hosts with this unit's information # Update hosts with this unit's information
rabbit.update_hosts_file( rabbit.update_hosts_file(
{rabbit.get_unit_ip(config_override=rabbit.CLUSTER_OVERRIDE_CONFIG, {rabbit.get_unit_ip(config_override=rabbit.CLUSTER_OVERRIDE_CONFIG,
@ -820,6 +831,11 @@ def leader_elected():
@hooks.hook('leader-settings-changed') @hooks.hook('leader-settings-changed')
def leader_settings_changed(): def leader_settings_changed():
if is_unit_paused_set():
log("Do not run config_changed while unit is paused", "WARNING")
return
if not os.path.exists(rabbit.RABBITMQ_CTL): if not os.path.exists(rabbit.RABBITMQ_CTL):
log('Deferring cookie configuration, RabbitMQ not yet installed') log('Deferring cookie configuration, RabbitMQ not yet installed')
return return
@ -842,6 +858,29 @@ def pre_install_hooks():
subprocess.check_call(['sh', '-c', f]) subprocess.check_call(['sh', '-c', f])
@hooks.hook('pre-series-upgrade')
def series_upgrade_prepare():
set_unit_upgrading()
if not is_unit_paused_set():
log("Pausing unit for series upgrade.")
rabbit.pause_unit_helper(rabbit.ConfigRenderer(rabbit.CONFIG_FILES))
if is_leader():
if not leader_get('cluster_series_upgrading'):
# Inform the entire cluster a series upgrade is occurring.
# Run the complete-cluster-series-upgrade action on the leader to
# clear this setting when the full cluster has completed its
# upgrade.
leader_set(cluster_series_upgrading=True)
@hooks.hook('post-series-upgrade')
def series_upgrade_complete():
log("Running complete series upgrade hook", "INFO")
clear_unit_paused()
clear_unit_upgrading()
rabbit.resume_unit_helper(rabbit.ConfigRenderer(rabbit.CONFIG_FILES))
@hooks.hook('update-status') @hooks.hook('update-status')
@harden() @harden()
def update_status(): def update_status():
@ -860,7 +899,7 @@ def update_status():
# #
# Have a look at the docstring of the stop() function for detailed # Have a look at the docstring of the stop() function for detailed
# explanation. # explanation.
if is_leader(): if is_leader() and not is_unit_paused_set():
rabbit.check_cluster_memberships() rabbit.check_cluster_memberships()
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -360,6 +360,7 @@ class UtilsTests(CharmTestCase):
assess_cluster_status, assess_cluster_status,
status_set, status_set,
clustered): clustered):
self.leader_get.return_value = None
services.return_value = 's1' services.return_value = 's1'
_determine_os_workload_status.return_value = ('active', '') _determine_os_workload_status.return_value = ('active', '')
clustered.return_value = True clustered.return_value = True
@ -371,6 +372,50 @@ class UtilsTests(CharmTestCase):
status_set.assert_called_once_with('active', status_set.assert_called_once_with('active',
'Unit is ready and clustered') 'Unit is ready and clustered')
@mock.patch.object(rabbit_utils, 'clustered')
@mock.patch.object(rabbit_utils, 'status_set')
@mock.patch.object(rabbit_utils, 'assess_cluster_status')
@mock.patch.object(rabbit_utils, 'services')
@mock.patch.object(rabbit_utils, '_determine_os_workload_status')
def test_assess_status_func_cluster_upgrading(
self, _determine_os_workload_status, services,
assess_cluster_status, status_set, clustered):
self.leader_get.return_value = True
services.return_value = 's1'
_determine_os_workload_status.return_value = ('active', '')
clustered.return_value = True
rabbit_utils.assess_status_func('test-config')()
# ports=None whilst port checks are disabled.
_determine_os_workload_status.assert_called_once_with(
'test-config', {}, charm_func=assess_cluster_status, services='s1',
ports=None)
status_set.assert_called_once_with(
'active', 'Unit is ready and clustered, Run '
'complete-cluster-series-upgrade when the cluster has completed '
'its upgrade.')
@mock.patch.object(rabbit_utils, 'clustered')
@mock.patch.object(rabbit_utils, 'status_set')
@mock.patch.object(rabbit_utils, 'assess_cluster_status')
@mock.patch.object(rabbit_utils, 'services')
@mock.patch.object(rabbit_utils, '_determine_os_workload_status')
def test_assess_status_func_cluster_upgrading_first_unit(
self, _determine_os_workload_status, services,
assess_cluster_status, status_set, clustered):
self.leader_get.return_value = True
services.return_value = 's1'
_determine_os_workload_status.return_value = ('waiting', 'No peers')
clustered.return_value = False
rabbit_utils.assess_status_func('test-config')()
# ports=None whilst port checks are disabled.
_determine_os_workload_status.assert_called_once_with(
'test-config', {}, charm_func=assess_cluster_status, services='s1',
ports=None)
status_set.assert_called_once_with(
'active', 'No peers, Run '
'complete-cluster-series-upgrade when the cluster has completed '
'its upgrade.')
def test_pause_unit_helper(self): def test_pause_unit_helper(self):
with mock.patch.object(rabbit_utils, '_pause_resume_helper') as prh: with mock.patch.object(rabbit_utils, '_pause_resume_helper') as prh:
rabbit_utils.pause_unit_helper('random-config') rabbit_utils.pause_unit_helper('random-config')