diff --git a/actions.yaml b/actions.yaml index e00e5f38..ff365819 100644 --- a/actions.yaml +++ b/actions.yaml @@ -1,2 +1,4 @@ git-reinstall: description: Reinstall nova-cloud-controller from the openstack-origin-git repositories. +openstack-upgrade: + description: Perform openstack upgrades. Config option action-managed-upgrade must be set to True. diff --git a/actions/openstack-upgrade b/actions/openstack-upgrade new file mode 120000 index 00000000..61793013 --- /dev/null +++ b/actions/openstack-upgrade @@ -0,0 +1 @@ +openstack_upgrade.py \ No newline at end of file diff --git a/actions/openstack_upgrade.py b/actions/openstack_upgrade.py new file mode 100755 index 00000000..ab76509f --- /dev/null +++ b/actions/openstack_upgrade.py @@ -0,0 +1,42 @@ +#!/usr/bin/python +import sys + +sys.path.append('hooks/') + +from charmhelpers.contrib.openstack.utils import ( + do_action_openstack_upgrade, +) + +from charmhelpers.core.hookenv import ( + relation_ids, +) + +from nova_cc_hooks import neutron_api_relation_joined + +from nova_cc_utils import ( + do_openstack_upgrade, + register_configs +) + +from nova_cc_hooks import config_changed + +CONFIGS = register_configs() + + +def openstack_upgrade(): + """Upgrade packages to config-set Openstack version. + + If the charm was installed from source we cannot upgrade it. + For backwards compatibility a config flag must be set for this + code to run, otherwise a full service level upgrade will fire + on config-changed.""" + + if (do_action_openstack_upgrade('nova-common', + do_openstack_upgrade, + CONFIGS)): + [neutron_api_relation_joined(rid=rid, remote_restart=True) + for rid in relation_ids('neutron-api')] + config_changed() + +if __name__ == '__main__': + openstack_upgrade() diff --git a/config.yaml b/config.yaml index 96a8ccd5..7688b702 100644 --- a/config.yaml +++ b/config.yaml @@ -395,3 +395,13 @@ options: If memcached is being used to store the tokens, then it's recommended to change this configuration to False. + action-managed-upgrade: + type: boolean + default: False + description: | + If True enables openstack upgrades for this charm via juju actions. + You will still need to set openstack-origin to the new repository but + instead of an upgrade running automatically across all units, it will + wait for you to execute the openstack-upgrade action for this charm on + each unit. If False it will revert to existing behavior of upgrading + all units on config change. diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/hooks/charmhelpers/contrib/openstack/utils.py index c98c5c9e..2f5280e6 100644 --- a/hooks/charmhelpers/contrib/openstack/utils.py +++ b/hooks/charmhelpers/contrib/openstack/utils.py @@ -25,6 +25,7 @@ import sys import re import six +import traceback import yaml from charmhelpers.contrib.network import ip @@ -34,6 +35,8 @@ from charmhelpers.core import ( ) from charmhelpers.core.hookenv import ( + action_fail, + action_set, config, log as juju_log, charm_dir, @@ -114,6 +117,7 @@ SWIFT_CODENAMES = OrderedDict([ ('2.2.1', 'kilo'), ('2.2.2', 'kilo'), ('2.3.0', 'liberty'), + ('2.4.0', 'liberty'), ]) # >= Liberty version->codename mapping @@ -142,6 +146,9 @@ PACKAGE_CODENAMES = { 'glance-common': OrderedDict([ ('11.0.0', 'liberty'), ]), + 'openstack-dashboard': OrderedDict([ + ('8.0.0', 'liberty'), + ]), } DEFAULT_LOOPBACK_SIZE = '5G' @@ -745,3 +752,47 @@ def git_yaml_value(projects_yaml, key): return projects[key] return None + + +def do_action_openstack_upgrade(package, upgrade_callback, configs): + """Perform action-managed OpenStack upgrade. + + Upgrades packages to the configured openstack-origin version and sets + the corresponding action status as a result. + + If the charm was installed from source we cannot upgrade it. + For backwards compatibility a config flag (action-managed-upgrade) must + be set for this code to run, otherwise a full service level upgrade will + fire on config-changed. + + @param package: package name for determining if upgrade available + @param upgrade_callback: function callback to charm's upgrade function + @param configs: templating object derived from OSConfigRenderer class + + @return: True if upgrade successful; False if upgrade failed or skipped + """ + ret = False + + if git_install_requested(): + action_set({'outcome': 'installed from source, skipped upgrade.'}) + else: + if openstack_upgrade_available(package): + if config('action-managed-upgrade'): + juju_log('Upgrading OpenStack release') + + try: + upgrade_callback(configs=configs) + action_set({'outcome': 'success, upgrade completed.'}) + ret = True + except: + action_set({'outcome': 'upgrade failed, see traceback.'}) + action_set({'traceback': traceback.format_exc()}) + action_fail('do_openstack_upgrade resulted in an ' + 'unexpected error') + else: + action_set({'outcome': 'action-managed-upgrade config is ' + 'False, skipped upgrade.'}) + else: + action_set({'outcome': 'no upgrade available.'}) + + return ret diff --git a/hooks/nova_cc_hooks.py b/hooks/nova_cc_hooks.py index 72657cf4..077c2a23 100755 --- a/hooks/nova_cc_hooks.py +++ b/hooks/nova_cc_hooks.py @@ -182,9 +182,9 @@ def config_changed(): if git_install_requested(): if config_value_changed('openstack-origin-git'): git_install(config('openstack-origin-git')) - else: + elif not config('action-managed-upgrade'): if openstack_upgrade_available('nova-common'): - CONFIGS = do_openstack_upgrade() + CONFIGS = do_openstack_upgrade(CONFIGS) [neutron_api_relation_joined(rid=rid, remote_restart=True) for rid in relation_ids('neutron-api')] save_script_rc() diff --git a/hooks/nova_cc_utils.py b/hooks/nova_cc_utils.py index e7a63b61..99771331 100644 --- a/hooks/nova_cc_utils.py +++ b/hooks/nova_cc_utils.py @@ -624,7 +624,7 @@ def _do_openstack_upgrade(new_src): return configs -def do_openstack_upgrade(): +def do_openstack_upgrade(configs): new_src = config('openstack-origin') if new_src[:6] != 'cloud:': raise ValueError("Unable to perform upgrade to %s" % new_src) diff --git a/unit_tests/test_nova_cc_utils.py b/unit_tests/test_nova_cc_utils.py index 7974173d..e884e196 100644 --- a/unit_tests/test_nova_cc_utils.py +++ b/unit_tests/test_nova_cc_utils.py @@ -647,7 +647,7 @@ class NovaCCUtilsTests(CharmTestCase): 'icehouse'] self.is_elected_leader.return_value = True self.relation_ids.return_value = [] - utils.do_openstack_upgrade() + utils.do_openstack_upgrade(self.register_configs()) expected = [call(['stamp', 'grizzly']), call(['upgrade', 'head']), call(['stamp', 'havana']), call(['upgrade', 'head'])] self.assertEquals(self.neutron_db_manage.call_args_list, expected) @@ -655,7 +655,7 @@ class NovaCCUtilsTests(CharmTestCase): self.apt_upgrade.assert_called_with(options=DPKG_OPTS, fatal=True, dist=True) self.apt_install.assert_called_with(determine_packages(), fatal=True) - expected = [call(release='havana'), call(release='icehouse')] + expected = [call(), call(release='havana'), call(release='icehouse')] self.assertEquals(self.register_configs.call_args_list, expected) self.assertEquals(self.ml2_migration.call_count, 1) self.assertTrue(migrate_nova_database.call_count, 2) @@ -673,7 +673,7 @@ class NovaCCUtilsTests(CharmTestCase): self.get_os_codename_install_source.return_value = 'icehouse' self.is_elected_leader.return_value = True self.relation_ids.return_value = [] - utils.do_openstack_upgrade() + utils.do_openstack_upgrade(self.register_configs()) self.neutron_db_manage.assert_called_with(['upgrade', 'head']) self.apt_update.assert_called_with(fatal=True) self.apt_upgrade.assert_called_with(options=DPKG_OPTS, fatal=True, @@ -696,7 +696,7 @@ class NovaCCUtilsTests(CharmTestCase): self.get_os_codename_install_source.return_value = 'juno' self.is_elected_leader.return_value = True self.relation_ids.return_value = [] - utils.do_openstack_upgrade() + utils.do_openstack_upgrade(self.register_configs()) neutron_db_calls = [call(['stamp', 'icehouse']), call(['upgrade', 'head'])] self.neutron_db_manage.assert_has_calls(neutron_db_calls, @@ -722,7 +722,7 @@ class NovaCCUtilsTests(CharmTestCase): self.get_os_codename_install_source.return_value = 'kilo' self.is_elected_leader.return_value = True self.relation_ids.return_value = [] - utils.do_openstack_upgrade() + utils.do_openstack_upgrade(self.register_configs()) self.assertEquals(self.neutron_db_manage.call_count, 0) self.apt_update.assert_called_with(fatal=True) self.apt_upgrade.assert_called_with(options=DPKG_OPTS, fatal=True, @@ -741,7 +741,7 @@ class NovaCCUtilsTests(CharmTestCase): _file.read = MagicMock() _file.readline.return_value = ("deb url" " precise-updates/grizzly main") - utils.do_openstack_upgrade() + utils.do_openstack_upgrade(self.register_configs()) expected = [call('cloud:precise-havana'), call('cloud:precise-icehouse')] self.assertEquals(_do_openstack_upgrade.call_args_list, expected) @@ -754,7 +754,7 @@ class NovaCCUtilsTests(CharmTestCase): with patch_open() as (_open, _file): _file.read = MagicMock() _file.readline.return_value = "deb url precise-updates/havana main" - utils.do_openstack_upgrade() + utils.do_openstack_upgrade(self.register_configs()) expected = [call('cloud:precise-icehouse')] self.assertEquals(_do_openstack_upgrade.call_args_list, expected)