diff --git a/config.yaml b/config.yaml index 36e5298d..0e84d6e3 100644 --- a/config.yaml +++ b/config.yaml @@ -96,6 +96,13 @@ options: specifying physical_network names usable for VLAN provider and tenant networks, as well as ranges of VLAN tags on each available for allocation to tenant networks. + firewall-driver: + type: string + default: + description: | + Firewall driver to use to support use of security groups with + instances; valid values include iptables_hybrid (default) and + openvswitch (>= Mitaka on Ubuntu 16.04 or later). # Network configuration options # by default all access is over 'private-address' os-data-network: diff --git a/hooks/neutron_ovs_context.py b/hooks/neutron_ovs_context.py index 08d793b0..240d6886 100644 --- a/hooks/neutron_ovs_context.py +++ b/hooks/neutron_ovs_context.py @@ -24,6 +24,10 @@ from charmhelpers.core.hookenv import ( unit_get, network_get_primary_address, ) +from charmhelpers.core.host import ( + CompareHostReleases, + lsb_release, +) from charmhelpers.contrib.openstack import context from charmhelpers.contrib.openstack.utils import get_host_ip from charmhelpers.contrib.network.ip import get_address_in_network @@ -34,6 +38,31 @@ from charmhelpers.contrib.openstack.context import ( ) from charmhelpers.core.unitdata import kv +IPTABLES_HYBRID = 'iptables_hybrid' +OPENVSWITCH = 'openvswitch' +VALID_FIREWALL_DRIVERS = (IPTABLES_HYBRID, OPENVSWITCH) + + +def _get_firewall_driver(): + ''' + Determine the firewall driver to use based on configuration, + OpenStack and Ubuntu releases. + + @returns str: firewall driver to use for OpenvSwitch + ''' + driver = config('firewall-driver') or IPTABLES_HYBRID + release = lsb_release()['DISTRIB_CODENAME'] + if driver not in VALID_FIREWALL_DRIVERS: + return IPTABLES_HYBRID + if (driver == OPENVSWITCH and + CompareHostReleases(release) < 'xenial'): + # NOTE(jamespage): Switch back to iptables_hybrid for + # Ubuntu releases prior to Xenial due + # to requirements for Linux >= 4.4 and + # Open vSwitch >= 2.5 + return IPTABLES_HYBRID + return driver + class OVSPluginContext(context.NeutronContext): interfaces = [] @@ -117,6 +146,8 @@ class OVSPluginContext(context.NeutronContext): if vlan_ranges: ovs_ctxt['vlan_ranges'] = ','.join(vlan_ranges.split()) + ovs_ctxt['firewall_driver'] = _get_firewall_driver() + return ovs_ctxt diff --git a/templates/mitaka/openvswitch_agent.ini b/templates/mitaka/openvswitch_agent.ini index 58c9d340..54a77aae 100644 --- a/templates/mitaka/openvswitch_agent.ini +++ b/templates/mitaka/openvswitch_agent.ini @@ -24,7 +24,7 @@ veth_mtu = {{ veth_mtu }} [securitygroup] {% if neutron_security_groups and not enable_dpdk -%} enable_security_group = True -firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver +firewall_driver = {{ firewall_driver }} {% else -%} enable_security_group = False {% endif -%} diff --git a/unit_tests/test_neutron_ovs_context.py b/unit_tests/test_neutron_ovs_context.py index 3fbe9fb2..f0866385 100644 --- a/unit_tests/test_neutron_ovs_context.py +++ b/unit_tests/test_neutron_ovs_context.py @@ -18,6 +18,14 @@ from mock import patch, Mock import neutron_ovs_context as context import charmhelpers +_LSB_RELEASE_XENIAL = { + 'DISTRIB_CODENAME': 'xenial', +} + +_LSB_RELEASE_TRUSTY = { + 'DISTRIB_CODENAME': 'trusty', +} + TO_PATCH = [ 'config', 'unit_get', @@ -28,6 +36,7 @@ TO_PATCH = [ 'relation_ids', 'relation_get', 'related_units', + 'lsb_release', ] @@ -48,6 +57,7 @@ class OVSPluginContextTest(CharmTestCase): self.test_config.set('verbose', True) self.test_config.set('use-syslog', True) self.network_get_primary_address.side_effect = NotImplementedError + self.lsb_release.return_value = _LSB_RELEASE_XENIAL def tearDown(self): super(OVSPluginContextTest, self).tearDown() @@ -152,6 +162,7 @@ class OVSPluginContextTest(CharmTestCase): 'config': 'neutron.randomconfig', 'use_syslog': True, 'enable_dpdk': False, + 'firewall_driver': 'iptables_hybrid', 'network_manager': 'neutron', 'debug': True, 'core_plugin': 'neutron.randomdriver', @@ -219,6 +230,7 @@ class OVSPluginContextTest(CharmTestCase): 'config': 'neutron.randomconfig', 'use_syslog': True, 'enable_dpdk': False, + 'firewall_driver': 'iptables_hybrid', 'network_manager': 'neutron', 'debug': True, 'core_plugin': 'neutron.randomdriver', @@ -580,3 +592,39 @@ class TestRemoteRestartContext(CharmTestCase): context.RemoteRestartContext()(), {'restart_trigger_neutron': 'neutron-uuid'} ) + + +class TestFirewallDriver(CharmTestCase): + + TO_PATCH = [ + 'config', + 'lsb_release', + ] + + def setUp(self): + super(TestFirewallDriver, self).setUp(context, + self.TO_PATCH) + self.config.side_effect = self.test_config.get + + def test_get_firewall_driver_xenial_unset(self): + self.lsb_release.return_value = _LSB_RELEASE_XENIAL + self.assertEqual(context._get_firewall_driver(), + context.IPTABLES_HYBRID) + + def test_get_firewall_driver_xenial_openvswitch(self): + self.test_config.set('firewall-driver', 'openvswitch') + self.lsb_release.return_value = _LSB_RELEASE_XENIAL + self.assertEqual(context._get_firewall_driver(), + context.OPENVSWITCH) + + def test_get_firewall_driver_xenial_invalid(self): + self.test_config.set('firewall-driver', 'foobar') + self.lsb_release.return_value = _LSB_RELEASE_XENIAL + self.assertEqual(context._get_firewall_driver(), + context.IPTABLES_HYBRID) + + def test_get_firewall_driver_trusty_openvswitch(self): + self.test_config.set('firewall-driver', 'openvswitch') + self.lsb_release.return_value = _LSB_RELEASE_TRUSTY + self.assertEqual(context._get_firewall_driver(), + context.IPTABLES_HYBRID)