Skip to content

Commit

Permalink
Process subordinate releases packages map
Browse files Browse the repository at this point in the history
For principal - subordinate plugin type relations where the
principal Python payload imports code from packages managed by a
subordinate, upgrades can be problematic.

This change will allow a subordinate charm that have opted into the
feature to inform its principal about all implemented release -
packages combinations ahead of time. With this information in place
the principal can do the upgrade in one operation without risk of
charm relation RPC type processing at a critical moment.

This makes use of
juju/charm-helpers#643

This is similar to
https://review.opendev.org/c/openstack/charm-keystone/+/781822

Also fixed broken link to charm-guide.

Change-Id: Iaf5b44be70ee108cbe88b4a26f0f15f915d507fe
Closes-Bug: #1927277
(cherry picked from commit 8fb37dc)
  • Loading branch information
lourot authored and freyes committed Aug 21, 2023
1 parent 81d4d49 commit 8d30677
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 38 deletions.
25 changes: 19 additions & 6 deletions hooks/nova_compute_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
from charmhelpers.contrib.openstack.utils import (
configure_installation_source,
get_os_codename_install_source,
get_subordinate_release_packages,
get_subordinate_services,
os_release,
reset_os_release,
is_unit_paused_set,
Expand Down Expand Up @@ -437,8 +439,12 @@ def restart_map():


def services():
''' Returns a list of services associated with this charm '''
return list(set(chain(*restart_map().values())))
'''
Returns a list of services associated with this charm and its subordinates.
'''

return list(set(chain(*restart_map().values()))
| get_subordinate_services())


def register_configs():
Expand Down Expand Up @@ -535,15 +541,22 @@ def determine_packages():
if virt_type == 'lxd':
packages.append('python3-nova-lxd')

packages = sorted(set(packages).union(get_subordinate_release_packages(
release).install))

return packages


def determine_purge_packages():
'''Return a list of packages to purge for the current OS release'''
cmp_os_source = CompareOpenStackReleases(os_release('nova-common'))
if cmp_os_source >= 'rocky':
return PURGE_PACKAGES
return []
release = os_release('nova-common')
cmp_release = CompareOpenStackReleases(release)
packages = []
if cmp_release >= 'rocky':
packages.extend(PURGE_PACKAGES)
packages = sorted(set(packages).union(get_subordinate_release_packages(
release).purge))
return packages


def remove_old_packages():
Expand Down
2 changes: 1 addition & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ and its features, as exercised in a subset of the full OpenStack deployment
test bundle topology.

For full details on functional testing of OpenStack charms please refer to
the [functional testing](http://docs.openstack.org/developer/charm-guide/testing.html#functional-testing)
the [functional testing](https://docs.openstack.org/charm-guide/latest/reference/testing.html#functional-testing)
section of the OpenStack Charm Guide.
110 changes: 79 additions & 31 deletions unit_tests/test_nova_compute_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import os
import tempfile

import charmhelpers.contrib.openstack.utils as os_utils

import nova_compute_context as compute_context
import nova_compute_utils as utils

Expand Down Expand Up @@ -84,93 +86,117 @@ def setUp(self):
self.test_kv = TestKV()
self.kv.return_value = self.test_kv

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'network_manager')
@patch('platform.machine')
def test_determine_packages_nova_network(self, machine,
net_man, en_meta):
def test_determine_packages_nova_network(
self, machine, net_man, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'icehouse'
en_meta.return_value = (False, None)
net_man.return_value = 'flatdhcpmanager'
machine.return_value = 'x86_64'
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + [
'nova-api',
'nova-network',
'nova-compute-kvm'
]
self.assertTrue(ex == result)
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
def test_determine_packages_ironic(self, en_meta):
def test_determine_packages_ironic(self, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'victoria'
self.test_config.set('virt-type', 'ironic')
en_meta.return_value = (False, None)
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + [
'nova-compute-ironic'
]
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
def test_determine_packages_ironic_pre_victoria(self, en_meta):
def test_determine_packages_ironic_pre_victoria(
self, en_meta, mock_get_subordinate_release_packages):
self.os_release.return_value = 'train'
self.test_config.set('virt-type', 'ironic')
en_meta.return_value = (False, None)
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + [
'nova-compute-vmware',
'python3-ironicclient'
]
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'network_manager')
@patch('platform.machine')
def test_determine_packages_nova_network_ocata(self, machine,
net_man, en_meta):
def test_determine_packages_nova_network_ocata(
self, machine, net_man, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'ocata'
en_meta.return_value = (False, None)
net_man.return_value = 'flatdhcpmanager'
machine.return_value = 'x86_64'
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + [
'nova-compute-kvm'
]
self.assertTrue(ex == result)
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager')
@patch('platform.machine')
def test_determine_packages_neutron(self, machine, net_man,
n_plugin, en_meta):
def test_determine_packages_neutron(
self, machine, net_man, n_plugin, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'ocata'
en_meta.return_value = (False, None)
net_man.return_value = 'neutron'
n_plugin.return_value = 'ovs'
machine.return_value = 'x86_64'
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + ['nova-compute-kvm']
self.assertTrue(ex == result)
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager')
@patch('platform.machine')
def test_determine_packages_neutron_rocky(self, machine, net_man,
n_plugin, en_meta):
def test_determine_packages_neutron_rocky(
self, machine, net_man, n_plugin, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'rocky'
en_meta.return_value = (False, None)
net_man.return_value = 'neutron'
n_plugin.return_value = 'ovs'
machine.return_value = 'x86_64'
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = (
[p for p in utils.BASE_PACKAGES
Expand All @@ -179,15 +205,16 @@ def test_determine_packages_neutron_rocky(self, machine, net_man,
utils.PY3_PACKAGES +
['python3-ceilometer', 'python3-neutron', 'python3-neutron-fwaas']
)
self.assertEqual(ex, result)
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager')
@patch('platform.machine')
def test_determine_packages_neutron_aarch64_xenial(self, machine,
net_man, n_plugin,
en_meta):
def test_determine_packages_neutron_aarch64_xenial(
self, machine, net_man, n_plugin, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'ocata'
self.lsb_release.return_value = {
'DISTRIB_CODENAME': 'xenial'
Expand All @@ -197,17 +224,20 @@ def test_determine_packages_neutron_aarch64_xenial(self, machine,
n_plugin.return_value = 'ovs'
machine.return_value = 'aarch64'
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + ['nova-compute-kvm', 'qemu-efi']
self.assertTrue(ex == result)
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager')
@patch('platform.machine')
def test_determine_packages_neutron_aarch64_trusty(self, machine,
net_man, n_plugin,
en_meta):
def test_determine_packages_neutron_aarch64_trusty(
self, machine, net_man, n_plugin, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'ocata'
self.lsb_release.return_value = {
'DISTRIB_CODENAME': 'trusty'
Expand All @@ -217,63 +247,81 @@ def test_determine_packages_neutron_aarch64_trusty(self, machine,
n_plugin.return_value = 'ovs'
machine.return_value = 'aarch64'
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = utils.BASE_PACKAGES + ['nova-compute-kvm']
self.assertEqual(ex, result)
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager')
@patch('platform.machine')
def test_determine_packages_neutron_ceph(self, machine,
net_man, n_plugin, en_meta):
def test_determine_packages_neutron_ceph(
self, machine, net_man, n_plugin, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'ocata'
en_meta.return_value = (False, None)
net_man.return_value = 'neutron'
n_plugin.return_value = 'ovs'
machine.return_value = 'x86_64'
self.relation_ids.return_value = ['ceph:0']
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
ex = (utils.BASE_PACKAGES + ['ceph-common', 'nova-compute-kvm'])
self.assertEqual(ex, result)
self.assertTrue(ex.sort() == result.sort())

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager')
def test_determine_packages_metadata(self, net_man,
n_plugin, en_meta):
def test_determine_packages_metadata(
self, net_man, n_plugin, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'ocata'
en_meta.return_value = (True, None)
net_man.return_value = 'bob'
n_plugin.return_value = 'ovs'
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
self.assertTrue('nova-api-metadata' in result)

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager')
def test_determine_packages_use_multipath(self, net_man,
n_plugin, en_meta):
def test_determine_packages_use_multipath(
self, net_man, n_plugin, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'ocata'
en_meta.return_value = (False, None)
net_man.return_value = 'bob'
self.test_config.set('use-multipath', True)
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
for pkg in utils.MULTIPATH_PACKAGES:
self.assertTrue(pkg in result)

@patch.object(utils, 'get_subordinate_release_packages')
@patch.object(utils, 'nova_metadata_requirement')
@patch.object(utils, 'neutron_plugin')
@patch.object(utils, 'network_manager')
def test_determine_packages_no_multipath(self, net_man,
n_plugin, en_meta):
def test_determine_packages_no_multipath(
self, net_man, n_plugin, en_meta,
mock_get_subordinate_release_packages):
self.os_release.return_value = 'ocata'
en_meta.return_value = (False, None)
net_man.return_value = 'bob'
self.test_config.set('use-multipath', False)
self.relation_ids.return_value = []
mock_get_subordinate_release_packages.return_value = \
os_utils.SubordinatePackages(set(), set())
result = utils.determine_packages()
for pkg in utils.MULTIPATH_PACKAGES:
self.assertFalse(pkg in result)
Expand Down

0 comments on commit 8d30677

Please sign in to comment.