diff --git a/src/lib/charm/openstack/neutron_api_plugin_ovn.py b/src/lib/charm/openstack/neutron_api_plugin_ovn.py index 9247f00..96bf99a 100644 --- a/src/lib/charm/openstack/neutron_api_plugin_ovn.py +++ b/src/lib/charm/openstack/neutron_api_plugin_ovn.py @@ -70,7 +70,30 @@ class BaseNeutronAPIPluginCharm(charms_openstack.charm.OpenStackCharm): # make sure we can write secrets readable by the ``neutron-server`` process group = 'neutron' db_migration_needed = False - service_plugins = [] + # Neutron service plugins to add + svc_plugins = [] + # Neutron service plugins to remove + svc_plugin_blacklist = [ + # FWaaS is not supported and also deprecated + 'firewall', + 'firewall_v2', + # Security groups logging not supported at this time + 'log', + # Port forwarding is not supported at this time + 'port_forwarding', + # OVN has its own service driver for that replaces Neutron ``router`` + 'router', + # Neutron Dynamic Routing is not supported at this time + 'neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin', + # VPNaaS is not supported + 'vpnaas', + ] + # Neutron mechanism driers to prepend + mech_drivers = ['ovn'] + # Neutron mechanism drivers to allow + mech_driver_whitelist = ['sriovnicswitch'] + # Neutron tenant network types to prepend + network_types = ['geneve'] def configure_tls(self, certificates_interface=None): """Override configure_tls method for neutron-api-plugin-ovn. @@ -108,16 +131,67 @@ class BaseNeutronAPIPluginCharm(charms_openstack.charm.OpenStackCharm): """ return self.db_migration_needed - @property - def service_plugins(self): + def service_plugins(self, neutron_svc_plugins=None): """Provide list of service plugins for current OpenStack release. - The returned variable must be set in the release specifc charm classes. + The ``svc_plugins`` class variable must be set in the release + specifc charm classes. + The ``svc_plugin_blacklist`` class variable defines which + service plugins to not use together with OVN. + + :param neutron_svc_plugins: Comma separated list of service plugins + from Neutron. + :type neutron_svc_plugins: Optional[str] :returns: List of service plugins - :rtype: List + :rtype: List[str] """ - return self.service_plugins + neutron_svc_plugins = neutron_svc_plugins or '' + return [ + service_plugin + for service_plugin in neutron_svc_plugins.split(',') + if service_plugin not in self.svc_plugin_blacklist + ] + self.svc_plugins + + def mechanism_drivers(self, neutron_mech_drivers=None): + """Provide list of mechanism drivers for current OpenStack release. + + The ``mech_drivers`` class variable defines which drivers to add + and must be set in the release specific charm classes. + + The ``mech_driver_whitelist`` class variable defines which + mechanism drivers are allowed to use together with OVN. + + :param neutron_mech_drivers: Comma separated list of mechanism drivers + from Neutron. + :type neutron_mech_drivers: Optional[str] + :returns: List of mechanism drivers + :rtype: List[str] + """ + neutron_mech_drivers = neutron_mech_drivers or '' + return self.mech_drivers + [ + mech_driver + for mech_driver in neutron_mech_drivers.split(',') + if mech_driver in self.mech_driver_whitelist + ] + + def tenant_network_types(self, neutron_tenant_network_types=None): + """Provide list of tenant network types for current OpenStack release. + + The ``network_types`` class variable dfines which types to + prepend and must be set in the release specific charm classes. + + :param neutron_tenant_network_types: Comma separated list of tenant + network types from Neutron. + :type neutron_tenant_network_types: Optional[str] + :returns: List of tenant network types + :rtype: List[str] + """ + neutron_tenant_network_types = neutron_tenant_network_types or '' + return self.network_types + [ + network_type + for network_type in neutron_tenant_network_types.split(',') + ] class TrainNeutronAPIPluginCharm(BaseNeutronAPIPluginCharm): @@ -125,7 +199,7 @@ class TrainNeutronAPIPluginCharm(BaseNeutronAPIPluginCharm): release = 'train' packages = ['python3-networking-ovn'] db_migration_needed = True - service_plugins = ['networking_ovn.l3.l3_ovn.OVNL3RouterPlugin'] + svc_plugins = ['networking_ovn.l3.l3_ovn.OVNL3RouterPlugin'] class UssuriNeutronAPIPluginCharm(BaseNeutronAPIPluginCharm): @@ -140,7 +214,7 @@ class UssuriNeutronAPIPluginCharm(BaseNeutronAPIPluginCharm): release = 'ussuri' packages = [] db_migration_needed = False - service_plugins = ['ovn-router'] + svc_plugins = ['ovn-router'] def install(self): """We no longer need to install anything.""" diff --git a/src/reactive/neutron_api_plugin_ovn_handlers.py b/src/reactive/neutron_api_plugin_ovn_handlers.py index 08fd0cf..6e68c96 100644 --- a/src/reactive/neutron_api_plugin_ovn_handlers.py +++ b/src/reactive/neutron_api_plugin_ovn_handlers.py @@ -59,19 +59,18 @@ def configure_neutron(): 'ovsdb-cms.available') ch_core.hookenv.log('DEBUG: neutron_config_data="{}"' .format(neutron.neutron_config_data)) - service_plugins = neutron.neutron_config_data.get( - 'service_plugins', '').split(',') - service_plugins = [svc for svc in service_plugins if svc not in ['router']] - tenant_network_types = neutron.neutron_config_data.get( - 'tenant_network_types', '').split(',') - tenant_network_types.insert(0, 'geneve') def _split_if_str(s): _s = s or '' return _s.split() with charm.provide_charm_instance() as instance: - service_plugins.extend(instance.service_plugins) + mechanism_drivers = instance.mechanism_drivers( + neutron.neutron_config_data.get('mechanism_drivers')) + service_plugins = instance.service_plugins( + neutron.neutron_config_data.get('service_plugins')) + tenant_network_types = instance.tenant_network_types( + neutron.neutron_config_data.get('tenant_network_types')) options = instance.adapters_instance.options sections = { 'ovn': [ @@ -116,7 +115,7 @@ def configure_neutron(): neutron.configure_plugin( 'ovn', service_plugins=','.join(service_plugins), - mechanism_drivers='ovn', + mechanism_drivers=','.join(mechanism_drivers), tenant_network_types=','.join(tenant_network_types), subordinate_configuration={ 'neutron-api': { diff --git a/tox.ini b/tox.ini index 23750a8..a45c381 100644 --- a/tox.ini +++ b/tox.ini @@ -77,4 +77,4 @@ commands = {posargs} [flake8] # E402 ignore necessary for path append before sys module import in actions -ignore = E402 +ignore = E402,W504 diff --git a/unit_tests/test_lib_charm_openstack_ovn.py b/unit_tests/test_lib_charm_openstack_ovn.py index 03f79c2..c0a5224 100644 --- a/unit_tests/test_lib_charm_openstack_ovn.py +++ b/unit_tests/test_lib_charm_openstack_ovn.py @@ -87,3 +87,32 @@ class TestNeutronAPIPluginOvnCharm(Helper): 'fakekey', cn='host', ) + + def test_service_plugins(self): + c = neutron_api_plugin_ovn.UssuriNeutronAPIPluginCharm() + svc_plugins = ( + 'router,firewall,firewall_v2,metering,segments,log,' + 'neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin,' + 'lbaasv2,port_forwarding,vpnaas') + expect = [ + 'metering', + 'segments', + 'lbaasv2', + 'ovn-router', + ] + self.assertEquals(c.service_plugins(svc_plugins), expect) + + def test_mechanism_drivers(self): + c = neutron_api_plugin_ovn.UssuriNeutronAPIPluginCharm() + mech_drivers = 'openvswitch,hyperv,l2population,sriovnicswitch' + expect = [ + 'ovn', + 'sriovnicswitch', + ] + self.assertEquals(c.mechanism_drivers(mech_drivers), expect) + + def test_tenant_network_types(self): + c = neutron_api_plugin_ovn.UssuriNeutronAPIPluginCharm() + network_types = 'gre,vlan,flat,local' + expect = ['geneve', 'gre', 'vlan', 'flat', 'local'] + self.assertEquals(c.tenant_network_types(network_types), expect) diff --git a/unit_tests/test_reactive_neutron_api_plugin_ovn_handlers.py b/unit_tests/test_reactive_neutron_api_plugin_ovn_handlers.py index 6598fdb..53618a5 100644 --- a/unit_tests/test_reactive_neutron_api_plugin_ovn_handlers.py +++ b/unit_tests/test_reactive_neutron_api_plugin_ovn_handlers.py @@ -64,6 +64,14 @@ class TestOvnHandlers(test_utils.PatchHelper): self.charm self.provide_charm_instance().__exit__.return_value = None + def patch_charm(self, attr, return_value=None): + mocked = mock.patch.object(self.charm, attr) + self._patches[attr] = mocked + started = mocked.start() + started.return_value = return_value + self._patches_start[attr] = started + setattr(self, attr, started) + def pmock(self, return_value=None): p = mock.PropertyMock().return_value = return_value return p @@ -93,12 +101,15 @@ class TestOvnHandlers(test_utils.PatchHelper): neutron = mock.MagicMock() ovsdb = mock.MagicMock() self.endpoint_from_flag.side_effect = [neutron, ovsdb] - neutron.neutron_config_data.get.side_effect = [ - 'router,firewall_v2,metering,segments,' - 'neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin,' - 'lbaasv2', - 'gre,vlan,flat,local', - ] + neutron.neutron_config_data.get.side_effect = lambda x: { + 'mechanism_drivers': ( + 'openvswitch,hyperv,l2population,sriovnicswitch'), + 'service_plugins': ( + 'router,firewall_v2,metering,segments,' + 'neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin,' + 'lbaasv2'), + 'tenant_network_types': 'gre,vlan,flat,local', + }.get(x) options = self.charm.adapters_instance.options options.ovn_key = self.pmock('aKey') options.ovn_cert = self.pmock('aCert') @@ -111,15 +122,19 @@ class TestOvnHandlers(test_utils.PatchHelper): options.dhcp_default_lease_time = self.pmock(42) options.ovn_dhcp4_global_options = self.pmock('a:A4 b:B4') options.ovn_dhcp6_global_options = self.pmock('a:A6 b:B6') - self.charm.service_plugins = self.pmock(['ovn-router']) + self.patch_charm('mechanism_drivers') + self.mechanism_drivers.return_value = ['ovn', 'sriovnicswitch'] + self.patch_charm('service_plugins') + self.service_plugins.return_value = [ + 'metering', 'segments', 'lbaasv2', 'ovn-router'] + self.patch_charm('tenant_network_types') + self.tenant_network_types.return_value = [ + 'geneve', 'gre', 'vlan', 'flat', 'local'] handlers.configure_neutron() neutron.configure_plugin.assert_called_once_with( 'ovn', - service_plugins=( - 'firewall_v2,metering,segments,' - 'neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin,' - 'lbaasv2,ovn-router'), - mechanism_drivers='ovn', + service_plugins='metering,segments,lbaasv2,ovn-router', + mechanism_drivers='ovn,sriovnicswitch', tenant_network_types='geneve,gre,vlan,flat,local', subordinate_configuration={ 'neutron-api': {