diff --git a/actions.yaml b/actions.yaml index 953ee8d1..a541ea5f 100644 --- a/actions.yaml +++ b/actions.yaml @@ -18,6 +18,9 @@ openstack-upgrade: description: >- Perform OpenStack upgrades. Config option action-managed-upgrade must be set to True. +package-upgrade: + description: | + Perform package upgrades for the current OpenStack release. instance-count: description: Return the number of VMs running on this unit. list-compute-nodes: diff --git a/actions/package-upgrade b/actions/package-upgrade new file mode 120000 index 00000000..6c6a7b84 --- /dev/null +++ b/actions/package-upgrade @@ -0,0 +1 @@ +package_upgrade.py \ No newline at end of file diff --git a/actions/package_upgrade.py b/actions/package_upgrade.py new file mode 100755 index 00000000..576f8049 --- /dev/null +++ b/actions/package_upgrade.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# +# Copyright 2022 Canonical Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys + + +_path = os.path.dirname(os.path.realpath(__file__)) +_hooks = os.path.abspath(os.path.join(_path, '../hooks')) + + +def _add_path(path): + if path not in sys.path: + sys.path.insert(1, path) + + +_add_path(_hooks) + + +from charmhelpers.contrib.openstack.utils import ( + do_action_package_upgrade, +) +from charmhelpers.core.hookenv import ( + relation_ids, +) +from nova_compute_utils import do_openstack_upgrade +from nova_compute_hooks import ( + config_changed, + CONFIGS, + neutron_plugin_joined, + nova_ceilometer_joined, +) + + +def package_upgrade(): + """Perform package upgrade within the current OpenStack release. + + In order to prevent this action from upgrading to a new release of + OpenStack, package upgrades are not run if a new OpenStack release is + available. See source of do_action_package_upgrade() for this check. + + Upgrades packages and sets the corresponding action status as a result.""" + + if (do_action_package_upgrade('nova-common', + do_openstack_upgrade, + CONFIGS)): + # we should restart the container scoped (subordinate) plugins after a + # managed openstack upgrade see: BUG#1835557 + for rid in relation_ids('neutron-plugin'): + neutron_plugin_joined(rid, remote_restart=True) + for rid in relation_ids('nova-ceilometer'): + nova_ceilometer_joined(rid, remote_restart=True) + # NOTE(ajkavanagh) - if unit is paused (usually true for managed + # upgrade) then the config_changed() function is a no-op + config_changed() + + +if __name__ == '__main__': + package_upgrade() diff --git a/unit_tests/test_actions_package_upgrade.py b/unit_tests/test_actions_package_upgrade.py new file mode 100644 index 00000000..9bd98cb6 --- /dev/null +++ b/unit_tests/test_actions_package_upgrade.py @@ -0,0 +1,63 @@ +# Copyright 2022 Canonical Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest.mock import patch +import os + +os.environ['JUJU_UNIT_NAME'] = 'nova_compute' + +with patch('charmhelpers.core.hookenv.config') as config: + config.return_value = 'nova' + with patch('charmhelpers.contrib.openstack.context.HostInfoContext'): + import nova_compute_utils as utils # noqa + +with patch('nova_compute_utils.restart_map'): + with patch('nova_compute_utils.register_configs'): + import package_upgrade + +from test_utils import ( + CharmTestCase +) + +TO_PATCH = [ + 'config_changed', + 'do_openstack_upgrade' +] + + +class TestNovaComputeUpgradeActions(CharmTestCase): + + def setUp(self): + super(TestNovaComputeUpgradeActions, self).setUp(package_upgrade, + TO_PATCH) + + @patch.object(package_upgrade, 'relation_ids') + @patch('charmhelpers.contrib.openstack.utils.action_set') + @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') + @patch('charmhelpers.contrib.openstack.utils.juju_log') + def test_package_upgrade_success(self, log, upgrade_avail, + action_set, relation_ids): + upgrade_avail.return_value = False + package_upgrade.package_upgrade() + self.assertTrue(self.do_openstack_upgrade.called) + + @patch.object(package_upgrade, 'relation_ids') + @patch('charmhelpers.contrib.openstack.utils.action_set') + @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') + @patch('charmhelpers.contrib.openstack.utils.juju_log') + def test_openstack_upgrade_failure(self, log, upgrade_avail, + action_set, relation_ids): + upgrade_avail.return_value = True + package_upgrade.package_upgrade() + self.assertFalse(self.do_openstack_upgrade.called)