]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add config option to specify ovs datapath.
authorSean Mooney <sean.k.mooney@intel.com>
Fri, 26 Jun 2015 09:48:26 +0000 (10:48 +0100)
committerSean Mooney <sean.k.mooney@intel.com>
Fri, 21 Aug 2015 11:18:32 +0000 (12:18 +0100)
This change introduces a new datapath_type parameter
to allow specification of the ovs datapath to be used.
This change introduces new functional and unit tests.

DocImpact
Change-Id: I929d8d15fc6cfdb799c53ef0f3722f4ed5c1096d
Partial-Bug: #1469871

13 files changed:
etc/neutron/plugins/ml2/openvswitch_agent.ini
neutron/agent/common/ovs_lib.py
neutron/agent/ovsdb/api.py
neutron/agent/ovsdb/impl_idl.py
neutron/agent/ovsdb/impl_vsctl.py
neutron/agent/ovsdb/native/commands.py
neutron/plugins/ml2/drivers/openvswitch/agent/common/config.py
neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py
neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py
neutron/tests/functional/agent/test_l2_ovs_agent.py
neutron/tests/unit/agent/common/test_ovs_lib.py
neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py
neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py

index b6fd3e01a2d11cbd034d505fb89c5cea6a2b0079..99cbaca5465a35efd4de17397e664b3078ac5a68 100644 (file)
 # 'ovs-ofctl' is currently the only available choice.
 # of_interface = ovs-ofctl
 
+# (StrOpt) ovs datapath to use.
+# 'system' is the default value and corresponds to the kernel datapath.
+# To enable the userspace datapath set this value to 'netdev'
+# datapath_type = system
+
 [agent]
 # Log agent heartbeats from this OVS agent
 # log_agent_heartbeats = False
index 9c64f67d11e94eec6271b2c7fdf5bca930e57cca..fc0927543e19f8a86ea56acfab663e9efd8b6778 100644 (file)
@@ -30,6 +30,8 @@ from neutron.agent.ovsdb import api as ovsdb
 from neutron.common import exceptions
 from neutron.i18n import _LE, _LI, _LW
 from neutron.plugins.common import constants as p_const
+from neutron.plugins.ml2.drivers.openvswitch.agent.common \
+    import constants
 
 # Default timeout for ovs-vsctl command
 DEFAULT_OVS_VSCTL_TIMEOUT = 10
@@ -102,8 +104,11 @@ class BaseOVS(object):
         self.vsctl_timeout = cfg.CONF.ovs_vsctl_timeout
         self.ovsdb = ovsdb.API.get(self)
 
-    def add_bridge(self, bridge_name):
-        self.ovsdb.add_br(bridge_name).execute()
+    def add_bridge(self, bridge_name,
+                   datapath_type=constants.OVS_DATAPATH_SYSTEM):
+
+        self.ovsdb.add_br(bridge_name,
+                          datapath_type).execute()
         br = OVSBridge(bridge_name)
         # Don't return until vswitchd sets up the internal port
         br.get_port_ofport(bridge_name)
@@ -143,9 +148,10 @@ class BaseOVS(object):
 
 
 class OVSBridge(BaseOVS):
-    def __init__(self, br_name):
+    def __init__(self, br_name, datapath_type=constants.OVS_DATAPATH_SYSTEM):
         super(OVSBridge, self).__init__()
         self.br_name = br_name
+        self.datapath_type = datapath_type
 
     def set_controller(self, controllers):
         self.ovsdb.set_controller(self.br_name,
@@ -173,7 +179,9 @@ class OVSBridge(BaseOVS):
 
     def create(self, secure_mode=False):
         with self.ovsdb.transaction() as txn:
-            txn.add(self.ovsdb.add_br(self.br_name))
+            txn.add(
+                self.ovsdb.add_br(self.br_name,
+                datapath_type=self.datapath_type))
             if secure_mode:
                 txn.add(self.ovsdb.set_fail_mode(self.br_name,
                                                  FAILMODE_SECURE))
@@ -186,7 +194,8 @@ class OVSBridge(BaseOVS):
     def reset_bridge(self, secure_mode=False):
         with self.ovsdb.transaction() as txn:
             txn.add(self.ovsdb.del_br(self.br_name))
-            txn.add(self.ovsdb.add_br(self.br_name))
+            txn.add(self.ovsdb.add_br(self.br_name,
+                                      datapath_type=self.datapath_type))
             if secure_mode:
                 txn.add(self.ovsdb.set_fail_mode(self.br_name,
                                                  FAILMODE_SECURE))
index 56a4c2be6d1878268cbd17f583f724849608b3dd..7dc88d02df9e9cd6ec1ff12cd6094bde3ebef5fb 100644 (file)
@@ -95,14 +95,16 @@ class API(object):
         """
 
     @abc.abstractmethod
-    def add_br(self, name, may_exist=True):
+    def add_br(self, name, may_exist=True, datapath_type=None):
         """Create an command to add an OVS bridge
 
-        :param name:      The name of the bridge
-        :type name:       string
-        :param may_exist: Do not fail if bridge already exists
-        :type may_exist:  bool
-        :returns:        :class:`Command` with no result
+        :param name:            The name of the bridge
+        :type name:             string
+        :param may_exist:       Do not fail if bridge already exists
+        :type may_exist:        bool
+        :param datapath_type:   The datapath_type of the bridge
+        :type datapath_type:    string
+        :returns:               :class:`Command` with no result
         """
 
     @abc.abstractmethod
index 4aed00acdd795308867901485eb8ee719168d2f9..c4459b94e86c93fd163e0d8f7fa64bc9463a71e0 100644 (file)
@@ -144,8 +144,8 @@ class OvsdbIdl(api.API):
                            self.context.vsctl_timeout,
                            check_error, log_errors)
 
-    def add_br(self, name, may_exist=True):
-        return cmd.AddBridgeCommand(self, name, may_exist)
+    def add_br(self, name, may_exist=True, datapath_type=None):
+        return cmd.AddBridgeCommand(self, name, may_exist, datapath_type)
 
     def del_br(self, name, if_exists=True):
         return cmd.DelBridgeCommand(self, name, if_exists)
index e410c4100f57a0075d1ef78e49009644b549dcee..306f5e486690721c31f98c0edbfc844a573bc504 100644 (file)
@@ -160,9 +160,13 @@ class OvsdbVsctl(ovsdb.API):
     def transaction(self, check_error=False, log_errors=True, **kwargs):
         return Transaction(self.context, check_error, log_errors, **kwargs)
 
-    def add_br(self, name, may_exist=True):
+    def add_br(self, name, may_exist=True, datapath_type=None):
         opts = ['--may-exist'] if may_exist else None
-        return BaseCommand(self.context, 'add-br', opts, [name])
+        params = [name]
+        if datapath_type:
+            params += ['--', 'set', 'Bridge', name,
+                       'datapath_type=%s' % datapath_type]
+        return BaseCommand(self.context, 'add-br', opts, params)
 
     def del_br(self, name, if_exists=True):
         opts = ['--if-exists'] if if_exists else None
index b5f873a66ab9d4cc502171ea7420024790208973..beb185a58153ccb38cba94f57fe4256341fc26b1 100644 (file)
@@ -50,10 +50,11 @@ class BaseCommand(api.Command):
 
 
 class AddBridgeCommand(BaseCommand):
-    def __init__(self, api, name, may_exist):
+    def __init__(self, api, name, may_exist, datapath_type):
         super(AddBridgeCommand, self).__init__(api)
         self.name = name
         self.may_exist = may_exist
+        self.datapath_type = datapath_type
 
     def run_idl(self, txn):
         if self.may_exist:
@@ -63,6 +64,8 @@ class AddBridgeCommand(BaseCommand):
                 return
         row = txn.insert(self.api._tables['Bridge'])
         row.name = self.name
+        if self.datapath_type:
+            row.datapath_type = self.datapath_type
         self.api._ovs.verify('bridges')
         self.api._ovs.bridges = self.api._ovs.bridges + [row]
 
index 7d866b6e852c391a7facb5b12c9ddd1ea3b0ac42..56e86f766427dfead52c0ba3ba97775e4cefc7f4 100644 (file)
@@ -47,6 +47,10 @@ ovs_opts = [
                        "integration bridge to physical bridges.")),
     cfg.StrOpt('of_interface', default='ovs-ofctl', choices=['ovs-ofctl'],
                help=_("OpenFlow interface to use.")),
+    cfg.StrOpt('datapath_type', default=constants.OVS_DATAPATH_SYSTEM,
+               choices=[constants.OVS_DATAPATH_SYSTEM,
+                        constants.OVS_DATAPATH_NETDEV],
+               help=_("OVS datapath to use.")),
 ]
 
 agent_opts = [
index ad6b897c267c6b51cde208cac8b5f97b26fc4bbc..6dde277a88a000fb7b586bb146fd98b1ad304d22 100644 (file)
@@ -90,3 +90,7 @@ OVS_NORMAL = 1
 OVS_DEAD = 2
 
 EXTENSION_DRIVER_TYPE = 'ovs'
+
+# ovs datapath types
+OVS_DATAPATH_SYSTEM = 'system'
+OVS_DATAPATH_NETDEV = 'netdev'
index 2122fe339a2bcaa641dae1bac29382d8de9c35b7..885a7a59b4f39a316591a350e9159074112f68f6 100644 (file)
@@ -19,6 +19,7 @@ import sys
 import time
 import uuid
 
+import functools
 import netaddr
 from oslo_config import cfg
 from oslo_log import log as logging
@@ -173,9 +174,14 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
         :param conf: an instance of ConfigOpts
         '''
         super(OVSNeutronAgent, self).__init__()
-        self.br_int_cls = bridge_classes['br_int']
-        self.br_phys_cls = bridge_classes['br_phys']
-        self.br_tun_cls = bridge_classes['br_tun']
+        self.conf = conf or cfg.CONF
+
+        # init bridge classes with configured datapath type.
+        self.br_int_cls, self.br_phys_cls, self.br_tun_cls = (
+            functools.partial(bridge_classes[b],
+                              datapath_type=self.conf.OVS.datapath_type)
+            for b in ('br_int', 'br_phys', 'br_tun'))
+
         self.use_veth_interconnection = use_veth_interconnection
         self.veth_mtu = veth_mtu
         self.available_local_vlans = set(moves.range(p_const.MIN_VLAN_TAG,
@@ -188,7 +194,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
         self.enable_distributed_routing = enable_distributed_routing
         self.arp_responder_enabled = arp_responder and self.l2_pop
         self.prevent_arp_spoofing = prevent_arp_spoofing
-        self.conf = conf or cfg.CONF
 
         self.agent_state = {
             'binary': 'neutron-openvswitch-agent',
index a18d4c5e2e5a0ac06c09932ecb1197f5b93bc279..3987c9f54892b78184fffd0f3410739700f4b7bc 100644 (file)
@@ -16,6 +16,7 @@
 
 import time
 
+from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
 from neutron.tests.common import net_helpers
 from neutron.tests.functional.agent.l2 import base
 
@@ -32,6 +33,35 @@ class TestOVSAgent(base.OVSAgentTestFramework):
 
         self.wait_until_ports_state(self.ports, up=False)
 
+    def test_datapath_type_system(self):
+        expected = constants.OVS_DATAPATH_SYSTEM
+        agent = self.create_agent()
+        self.start_agent(agent)
+        actual = self.ovs.db_get_val('Bridge',
+                                     agent.int_br.br_name,
+                                     'datapath_type')
+        self.assertEqual(expected, actual)
+        actual = self.ovs.db_get_val('Bridge',
+                                     agent.tun_br.br_name,
+                                     'datapath_type')
+        self.assertEqual(expected, actual)
+
+    def test_datapath_type_netdev(self):
+        expected = constants.OVS_DATAPATH_NETDEV
+        self.config.set_override('datapath_type',
+                                 expected,
+                                 "OVS")
+        agent = self.create_agent()
+        self.start_agent(agent)
+        actual = self.ovs.db_get_val('Bridge',
+                                     agent.int_br.br_name,
+                                     'datapath_type')
+        self.assertEqual(expected, actual)
+        actual = self.ovs.db_get_val('Bridge',
+                                     agent.tun_br.br_name,
+                                     'datapath_type')
+        self.assertEqual(expected, actual)
+
     def test_resync_devices_set_up_after_exception(self):
         self.setup_agent_and_ports(
             port_dicts=self.create_test_ports(),
index e21fef5f20d0c8af1865e214909b405c643fe6e1..b6ab9dd2bbce0f2f1cbf4142e251fbb73a2847e7 100644 (file)
@@ -22,6 +22,8 @@ from neutron.agent.common import ovs_lib
 from neutron.agent.common import utils
 from neutron.common import exceptions
 from neutron.plugins.common import constants
+from neutron.plugins.ml2.drivers.openvswitch.agent.common \
+    import constants as p_const
 from neutron.tests import base
 from neutron.tests import tools
 
@@ -255,6 +257,16 @@ class OVS_Lib_Test(base.BaseTestCase):
         self._test_get_port_ofport(ovs_lib.INVALID_OFPORT,
                                    ovs_lib.INVALID_OFPORT)
 
+    def test_default_datapath(self):
+        # verify kernel datapath is default
+        expected = p_const.OVS_DATAPATH_SYSTEM
+        self.assertEqual(expected, self.br.datapath_type)
+
+    def test_non_default_datapath(self):
+        expected = p_const.OVS_DATAPATH_NETDEV
+        self.br = ovs_lib.OVSBridge(self.BR_NAME, datapath_type=expected)
+        self.assertEqual(expected, self.br.datapath_type)
+
     def test_count_flows(self):
         self.execute.return_value = 'ignore\nflow-1\n'
         # counts the number of flows as total lines of output - 2
index 07c02f36251a08de2ffca8092d0777e3da255d0a..9c5bda57d7fc297c6959aa4761c612e78b86dbbb 100644 (file)
@@ -187,6 +187,37 @@ class TestOvsNeutronAgent(object):
             else:
                 self.assertFalse(provision_local_vlan.called)
 
+    def test_datapath_type_system(self):
+        # verify kernel datapath is default
+        expected = constants.OVS_DATAPATH_SYSTEM
+        self.assertEqual(expected, self.agent.int_br.datapath_type)
+
+    def test_datapath_type_netdev(self):
+
+        with mock.patch.object(self.mod_agent.OVSNeutronAgent,
+                               'setup_integration_br'), \
+            mock.patch.object(self.mod_agent.OVSNeutronAgent,
+                           'setup_ancillary_bridges',
+                           return_value=[]), \
+            mock.patch('neutron.agent.linux.utils.get_interface_mac',
+                    return_value='00:00:00:00:00:01'), \
+            mock.patch(
+                'neutron.agent.common.ovs_lib.BaseOVS.get_bridges'), \
+            mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall',
+                       new=MockFixedIntervalLoopingCall), \
+            mock.patch(
+                'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports',
+                return_value=[]):
+            # validate setting non default datapath
+            expected = constants.OVS_DATAPATH_NETDEV
+            cfg.CONF.set_override('datapath_type',
+                                  expected,
+                                  group='OVS')
+            kwargs = self.mod_agent.create_agent_config_map(cfg.CONF)
+            self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(),
+                **kwargs)
+            self.assertEqual(expected, self.agent.int_br.datapath_type)
+
     def test_restore_local_vlan_map_with_device_has_tag(self):
         self._test_restore_local_vlan_maps(2)
 
index 315360b8a7353d8b5dd304a300ad40ac8b33595f..cc4f21911ac1560b5576a57996d95df023ae9eca 100644 (file)
@@ -156,13 +156,16 @@ class TunnelTest(object):
 
     def _define_expected_calls(self, arp_responder=False):
         self.mock_int_bridge_cls_expected = [
-            mock.call(self.INT_BRIDGE),
+            mock.call(self.INT_BRIDGE,
+                      datapath_type=mock.ANY),
         ]
         self.mock_phys_bridge_cls_expected = [
-            mock.call(self.MAP_TUN_BRIDGE),
+            mock.call(self.MAP_TUN_BRIDGE,
+                      datapath_type=mock.ANY),
         ]
         self.mock_tun_bridge_cls_expected = [
-            mock.call(self.TUN_BRIDGE),
+            mock.call(self.TUN_BRIDGE,
+                      datapath_type=mock.ANY),
         ]
 
         self.mock_int_bridge = self.ovs_bridges[self.INT_BRIDGE]
@@ -570,13 +573,16 @@ class TunnelTestUseVethInterco(TunnelTest):
 
     def _define_expected_calls(self, arp_responder=False):
         self.mock_int_bridge_cls_expected = [
-            mock.call(self.INT_BRIDGE),
+            mock.call(self.INT_BRIDGE,
+                      datapath_type=mock.ANY),
         ]
         self.mock_phys_bridge_cls_expected = [
-            mock.call(self.MAP_TUN_BRIDGE),
+            mock.call(self.MAP_TUN_BRIDGE,
+                      datapath_type=mock.ANY),
         ]
         self.mock_tun_bridge_cls_expected = [
-            mock.call(self.TUN_BRIDGE),
+            mock.call(self.TUN_BRIDGE,
+                      datapath_type=mock.ANY),
         ]
 
         self.mock_int_bridge_expected = [