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..b24048b4 --- /dev/null +++ b/actions/openstack_upgrade.py @@ -0,0 +1,70 @@ +#!/usr/bin/python +import sys +import traceback +import uuid + +sys.path.append('hooks/') + +from charmhelpers.core.hookenv import ( + action_set, + action_fail, + config, + relation_ids, + relation_set +) + +from cinder_hooks import config_changed + +from charmhelpers.contrib.openstack.utils import ( + juju_log, + git_install_requested, + openstack_upgrade_available +) + +from cinder_utils import ( + do_openstack_upgrade, + register_configs +) + + +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 git_install_requested(): + action_set({'outcome': 'installed from source, skipped upgrade.'}) + else: + if openstack_upgrade_available('cinder-common'): + if config('action-managed-upgrade'): + juju_log('Upgrading OpenStack release') + + try: + do_openstack_upgrade(configs=CONFIGS) + + for rid in relation_ids('storage-backend'): + relation_set(relation_id=rid, + upgrade_nonce=uuid.uuid4()) + + action_set({'outcome': 'success, upgrade completed.'}) + except: + action_set({'outcome': 'upgrade failed, see traceback.'}) + action_set({'traceback': traceback.format_exc()}) + action_fail('do_openstack_upgrade resulted in an ' + 'unexpected error') + + config_changed() + else: + action_set({'outcome': 'action-managed-upgrade config is ' + 'False, skipped upgrade.'}) + else: + action_set({'outcome': 'no upgrade available.'}) + +if __name__ == '__main__': + openstack_upgrade() diff --git a/config.yaml b/config.yaml index 92bdff3b..3930317f 100644 --- a/config.yaml +++ b/config.yaml @@ -283,8 +283,8 @@ options: A comma-separated list of nagios servicegroups. If left empty, the nagios_context will be used as the servicegroup action-managed-upgrade: - default: False type: boolean + default: False description: | If True enables openstack upgrades for this charm via juju actions. You will still need to set the config variable for the openstack version diff --git a/unit_tests/test_actions_openstack_upgrade.py b/unit_tests/test_actions_openstack_upgrade.py new file mode 100644 index 00000000..6b80794b --- /dev/null +++ b/unit_tests/test_actions_openstack_upgrade.py @@ -0,0 +1,119 @@ +from mock import patch +import os + +os.environ['JUJU_UNIT_NAME'] = 'cinder' + +with patch('cinder_utils.register_configs') as register_configs: + import openstack_upgrade + +from test_utils import ( + CharmTestCase +) + +TO_PATCH = [ + 'config' +] + + +class TestCinderUpgradeActions(CharmTestCase): + + def setUp(self): + super(TestCinderUpgradeActions, self).setUp(openstack_upgrade, + TO_PATCH) + self.config.side_effect = self.test_config.get + + @patch.object(openstack_upgrade, 'relation_set') + @patch.object(openstack_upgrade, 'relation_ids') + @patch.object(openstack_upgrade, 'action_set') + @patch.object(openstack_upgrade, 'action_fail') + @patch.object(openstack_upgrade, 'do_openstack_upgrade') + @patch.object(openstack_upgrade, 'openstack_upgrade_available') + @patch.object(openstack_upgrade, 'config_changed') + @patch('charmhelpers.contrib.openstack.utils.config') + def test_openstack_upgrade(self, _config, config_changed, + openstack_upgrade_available, + do_openstack_upgrade, action_fail, + action_set, relation_ids, relation_set): + _config.return_value = None + openstack_upgrade_available.return_value = True + + self.test_config.set('action-managed-upgrade', True) + + openstack_upgrade.openstack_upgrade() + + self.assertTrue(do_openstack_upgrade.called) + self.assertTrue(config_changed.called) + self.assertFalse(action_fail.called) + + @patch.object(openstack_upgrade, 'action_set') + @patch.object(openstack_upgrade, 'do_openstack_upgrade') + @patch.object(openstack_upgrade, 'openstack_upgrade_available') + @patch.object(openstack_upgrade, 'config_changed') + @patch('charmhelpers.contrib.openstack.utils.config') + def test_openstack_upgrade_not_configured(self, _config, config_changed, + openstack_upgrade_available, + do_openstack_upgrade, + action_set): + _config.return_value = None + openstack_upgrade_available.return_value = True + + openstack_upgrade.openstack_upgrade() + + msg = ('action-managed-upgrade config is False, skipped upgrade.') + + action_set.assert_called_with({'outcome': msg}) + self.assertFalse(do_openstack_upgrade.called) + + @patch.object(openstack_upgrade, 'action_set') + @patch.object(openstack_upgrade, 'do_openstack_upgrade') + @patch.object(openstack_upgrade, 'openstack_upgrade_available') + @patch.object(openstack_upgrade, 'config_changed') + @patch('charmhelpers.contrib.openstack.utils.config') + def test_openstack_upgrade_git_install(self, _config, config_changed, + openstack_upgrade_available, + do_openstack_upgrade, + action_set): + + self.test_config.set('action-managed-upgrade', True) + self.test_config.set('openstack-origin-git', True) + + openstack_upgrade.openstack_upgrade() + + msg = ('installed from source, skipped upgrade.') + action_set.assert_called_with({'outcome': msg}) + self.assertFalse(do_openstack_upgrade.called) + + @patch.object(openstack_upgrade, 'action_set') + @patch.object(openstack_upgrade, 'action_fail') + @patch.object(openstack_upgrade, 'do_openstack_upgrade') + @patch.object(openstack_upgrade, 'openstack_upgrade_available') + @patch.object(openstack_upgrade, 'config_changed') + @patch('traceback.format_exc') + @patch('charmhelpers.contrib.openstack.utils.config') + def test_openstack_upgrade_exception(self, _config, format_exc, + config_changed, + openstack_upgrade_available, + do_openstack_upgrade, + action_fail, action_set): + _config.return_value = None + self.test_config.set('action-managed-upgrade', True) + openstack_upgrade_available.return_value = True + + e = OSError('something bad happened') + do_openstack_upgrade.side_effect = e + traceback = ( + "Traceback (most recent call last):\n" + " File \"actions/openstack_upgrade.py\", line 37, in openstack_upgrade\n" # noqa + " openstack_upgrade(config(\'openstack-origin-git\'))\n" + " File \"/usr/lib/python2.7/dist-packages/mock.py\", line 964, in __call__\n" # noqa + " return _mock_self._mock_call(*args, **kwargs)\n" + " File \"/usr/lib/python2.7/dist-packages/mock.py\", line 1019, in _mock_call\n" # noqa + " raise effect\n" + "OSError: something bad happened\n") + format_exc.return_value = traceback + + openstack_upgrade.openstack_upgrade() + + msg = 'do_openstack_upgrade resulted in an unexpected error' + action_fail.assert_called_with(msg) + action_set.assert_called_with({'traceback': traceback})