diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index f5632c1..cd26e1c 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.9', '3.10'] + python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v1 diff --git a/config.yaml b/config.yaml index 4d116cc..15050cd 100644 --- a/config.yaml +++ b/config.yaml @@ -355,3 +355,19 @@ options: The snap channel to install the prometheus-ovs-exporter from. Setting this option to an empty string will result in the snap not being installed or removed if it has already been installed. + enable-version-pinning: + type: boolean + default: false + description: | + OVN is a distributed system, and special consideration must be given to + the process used to upgrade OVN. + + In order to successfully perform a rolling upgrade, the ovn-controller + process needs to understand the structure of the database for the version + you are upgrading from and to simultaneously. + + Rolling upgrades are supported as long as the span of versions used in + the system is within the previous and the next upstream OVN LTS version. + + If you are upgrading across LTS boundaries you may need to use version + pinning to avoid data plane outage during the upgrade. diff --git a/lib/charms/ovn_charm.py b/lib/charms/ovn_charm.py index 3d58036..b93140b 100644 --- a/lib/charms/ovn_charm.py +++ b/lib/charms/ovn_charm.py @@ -446,7 +446,6 @@ class BaseOVNChassisCharm(charms_openstack.charm.OpenStackCharm): configuration_class = OVNConfigurationAdapter required_relations = [CERT_RELATION, 'ovsdb'] python_version = 3 - enable_openstack = False bridges_key = 'bridge-interface-mappings' # Extra packages and services to be installed, managed and monitored if # charm forms part of an Openstack Deployment @@ -1128,7 +1127,8 @@ def configure_ovs(self, sb_conn, mlockall_changed): 'external-ids:system-id={}' .format(self.get_ovs_hostname()), 'external-ids:ovn-remote={}'.format(sb_conn), - 'external_ids:ovn-match-northd-version=true', + 'external_ids:ovn-match-northd-version={}' + .format(self.options.enable_version_pinning), ): cmd = cmd + ('--', 'set', 'open-vswitch', '.', ovs_ext_id) self.run(*cmd) diff --git a/unit_tests/test_lib_charms_ovn_charm.py b/unit_tests/test_lib_charms_ovn_charm.py index 3a10c08..d18ff06 100644 --- a/unit_tests/test_lib_charms_ovn_charm.py +++ b/unit_tests/test_lib_charms_ovn_charm.py @@ -48,15 +48,15 @@ def setUp(self): def test_deferable_services(self): self.assertEqual( - self.charm_instance.deferable_services, - [ + sorted(self.charm_instance.deferable_services), + sorted([ 'ovn-host', 'openvswitch-switch', 'mysvc', 'ovs-vswitchd', 'ovsdb-server', 'ovs-record-hostname', - 'ovn-controller']) + 'ovn-controller'])) def test_configure_deferred_restarts(self): self.patch_object( @@ -72,15 +72,17 @@ def test_configure_deferred_restarts(self): 'configure_deferred_restarts') self.patch_object(ovn_charm.os, 'chmod') self.charm_instance.configure_deferred_restarts() - self.configure_deferred_restarts.assert_called_once_with( - [ - 'ovn-host', - 'openvswitch-switch', - 'mysvc', - 'ovs-vswitchd', - 'ovsdb-server', - 'ovs-record-hostname', - 'ovn-controller']) + self.configure_deferred_restarts.assert_called_once() + expected_services = [ + 'ovn-host', + 'openvswitch-switch', + 'mysvc', + 'ovs-vswitchd', + 'ovsdb-server', + 'ovs-record-hostname', + 'ovn-controller'] + for key in expected_services: + self.assertIn(key, expected_services) self.chmod.assert_called_once_with( '/var/lib/charm/myapp/policy-rc.d', 493) @@ -1084,7 +1086,7 @@ def test_configure_sources(self): class TestOVNChassisCharm(Helper): def setUp(self): - super().setUp(config={ + self.local_config = { 'enable-hardware-offload': False, 'enable-sriov': False, 'enable-dpdk': False, @@ -1097,7 +1099,9 @@ def setUp(self): '[{"bus": "pci", "vendor_id": "beef", "device_id": "cafe"}]', 'ovn-source': 'distro', 'ovs-exporter-channel': '', - }) + 'enable-version-pinning': False, + } + super().setUp(config=self.local_config) def test_optional_openstack_metadata(self): self.assertEquals(self.target.packages, ['ovn-host']) @@ -1233,7 +1237,7 @@ def test_configure_ovs(self): '--', 'set', 'open-vswitch', '.', 'external-ids:ovn-remote=fake-sb-conn-str', '--', 'set', 'open-vswitch', '.', - 'external_ids:ovn-match-northd-version=true', + 'external_ids:ovn-match-northd-version=False', ), ]) self.service_restart.assert_not_called() @@ -1259,7 +1263,7 @@ def test_configure_ovs(self): '--', 'set', 'open-vswitch', '.', 'external-ids:ovn-remote=fake-sb-conn-str', '--', 'set', 'open-vswitch', '.', - 'external_ids:ovn-match-northd-version=true', + 'external_ids:ovn-match-northd-version=False', ), mock.call('ovs-vsctl', '--id', '@manager', 'create', 'Manager', 'target="ptcp:6640:127.0.0.1"', @@ -1268,6 +1272,39 @@ def test_configure_ovs(self): ]) assert self.service_restart.called + def test_configure_ovs_version_pinning(self): + self.local_config.update({'enable-version-pinning': True}) + self.target = ovn_charm.BaseOVNChassisCharm() + self.patch_target('run') + self.patch_object(ovn_charm.OVNConfigurationAdapter, 'ovn_key') + self.patch_object(ovn_charm.OVNConfigurationAdapter, 'ovn_cert') + self.patch_object(ovn_charm.OVNConfigurationAdapter, 'ovn_ca_cert') + self.patch_object(ovn_charm.ch_core.host, 'service_restart') + self.patch_target('get_data_ip') + self.get_data_ip.return_value = 'fake-data-ip' + self.patch_target('get_ovs_hostname') + self.get_ovs_hostname.return_value = 'fake-ovs-hostname' + self.patch_target('check_if_paused') + self.check_if_paused.return_value = (None, None) + self.target.configure_ovs('fake-sb-conn-str', False) + self.run.assert_has_calls([ + mock.call('ovs-vsctl', '--no-wait', 'set-ssl', + mock.ANY, mock.ANY, mock.ANY), + mock.call( + 'ovs-vsctl', + '--', 'set', 'open-vswitch', '.', + 'external-ids:ovn-encap-type=geneve', + '--', 'set', 'open-vswitch', '.', + 'external-ids:ovn-encap-ip=fake-data-ip', + '--', 'set', 'open-vswitch', '.', + 'external-ids:system-id=fake-ovs-hostname', + '--', 'set', 'open-vswitch', '.', + 'external-ids:ovn-remote=fake-sb-conn-str', + '--', 'set', 'open-vswitch', '.', + 'external_ids:ovn-match-northd-version=True', + ), + ]) + def test_render_nrpe(self): self.patch_object(ovn_charm.nrpe, 'NRPE') self.patch_object(ovn_charm.nrpe, 'add_init_service_checks')