]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add vhost-user support via ovs capabilities/datapath_type
authorTerry Wilson <twilson@redhat.com>
Thu, 15 Oct 2015 23:50:40 +0000 (18:50 -0500)
committerTerry Wilson <twilson@redhat.com>
Wed, 18 Nov 2015 20:04:06 +0000 (14:04 -0600)
Adds the ovs 'config' property which returns the contents of the
single row of the Open_vSwitch table. This gives access to certain
OVS capabilities such as datapath_types and iface_types.

Using this information in concert with the datapath_type config
option, vif details are calculated by the OVS mech driver. If
datapath_type == 'netdev' and OVS on the agent host is capable of
supporting dpdkvhostuser, then it is used.

Authored-By: Terry Wilson <twilson@redhat.com>
Co-Authored-By: Sean Mooney <sean.k.mooney@intel.com>
Closes-Bug: #1506127
Change-Id: I5047f1d1276e2f52ff02a0cba136e222779d059c

doc/source/devref/ovs_vhostuser.rst [new file with mode: 0644]
etc/neutron/plugins/ml2/openvswitch_agent.ini
neutron/agent/common/ovs_lib.py
neutron/common/constants.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/plugins/ml2/drivers/openvswitch/mech_driver/mech_openvswitch.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

diff --git a/doc/source/devref/ovs_vhostuser.rst b/doc/source/devref/ovs_vhostuser.rst
new file mode 100644 (file)
index 0000000..775828b
--- /dev/null
@@ -0,0 +1,62 @@
+..
+      Licensed under the Apache License, Version 2.0 (the "License"); you may
+      not use this file except in compliance with the License. You may obtain
+      a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+      Unless required by applicable law or agreed to in writing, software
+      distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+      License for the specific language governing permissions and limitations
+      under the License.
+
+
+      Convention for heading levels in Neutron devref:
+      =======  Heading 0 (reserved for the title in a document)
+      -------  Heading 1
+      ~~~~~~~  Heading 2
+      +++++++  Heading 3
+      '''''''  Heading 4
+      (Avoid deeper levels because they do not render well.)
+
+
+Neutron Open vSwitch vhost-user support
+=======================================
+
+Neutron supports using Open vSwitch + DPDK vhost-user interfaces directly in
+the OVS ML2 driver and agent. The current implementation relies on a multiple
+configuration values and includes runtime verification of Open vSwitch's
+capability to provide these interfaces.
+
+The OVS agent detects the capability of the underlying Open vSwitch
+installation and passes that information over RPC via the agent
+'configurations' dictionary. The ML2 driver uses this information to select
+the proper VIF type and binding details.
+
+Neutron+OVS+DPDK platform requirements
+--------------------------------------
+OVS 2.4.0+
+DPDK 2.0+
+
+Neutron OVS+DPDK vhost-user config
+----------------------------------
+
+[OVS]
+datapath_type=netdev
+vhostuser_socket_dir=/var/run/openvswitch
+
+When OVS is running with DPDK support enabled, and the datapath_type is set to
+"netdev", then the OVS ML2 driver will use the vhost-user VIF type and pass
+the necessary binding details to use OVS+DPDK and vhost-user sockets. This
+includes the vhoustuser_socket_dir setting, which must match the directory
+passed to ovs-vswitchd on startup.
+
+What about the networking-ovs-dpdk repo?
+----------------------------------------
+
+The networking-ovs-dpdk repo will continue to exist and undergo active
+development. This feature just removes the necessity for a separate ML2 driver
+and OVS agent in the networking-ovs-dpdk repo. The networking-ovs-dpdk project
+also provides a devstack plugin which also allows automated CI, a puppet
+module, and an OpenFlow-based security group implementation.
index ad096871f678e46c2d547457e9b295e99ef277af..441079c786599e4d311caff6f4a2021f60081cfa 100644 (file)
 # To enable the userspace datapath set this value to 'netdev'
 # datapath_type = system
 
+# (StrOpt) OVS vhost-user socket directory.
+# '/var/run/openvswitch' is the default value
+# vhostuser_socket_dir = /var/run/openvswitch
+
 [agent]
 # Log agent heartbeats from this OVS agent
 # log_agent_heartbeats = False
index 406e5e35c69b3683b55a6c7645a043c2ab26f1b7..41ce3993bf1c513dd00c0d0dcaf8f9ab4f91b3e8 100644 (file)
@@ -53,6 +53,11 @@ cfg.CONF.register_opts(OPTS)
 
 LOG = logging.getLogger(__name__)
 
+OVS_DEFAULT_CAPS = {
+    'datapath_types': [],
+    'iface_types': [],
+}
+
 
 def _ofport_result_pending(result):
     """Return True if ovs-vsctl indicates the result is still pending."""
@@ -146,6 +151,23 @@ class BaseOVS(object):
         return self.ovsdb.db_get(table, record, column).execute(
             check_error=check_error, log_errors=log_errors)
 
+    @property
+    def config(self):
+        """A dict containing the only row from the root Open_vSwitch table
+
+        This row contains several columns describing the Open vSwitch install
+        and the system on which it is installed. Useful keys include:
+            datapath_types: a list of supported datapath types
+            iface_types: a list of supported interface types
+            ovs_version: the OVS version
+        """
+        return self.ovsdb.db_list("Open_vSwitch").execute()[0]
+
+    @property
+    def capabilities(self):
+        _cfg = self.config
+        return {k: _cfg.get(k, OVS_DEFAULT_CAPS[k]) for k in OVS_DEFAULT_CAPS}
+
 
 class OVSBridge(BaseOVS):
     def __init__(self, br_name, datapath_type=constants.OVS_DATAPATH_SYSTEM):
index a5f34ee9d2d864e18fedd54f1993c21e38e7ed06..70072534361bff01e59454d612fb048473e1ca5a 100644 (file)
@@ -155,6 +155,8 @@ LLA_TASK_TIMEOUT = 40
 # Linux interface max length
 DEVICE_NAME_MAX_LEN = 15
 
+# vhost-user device names start with "vhu"
+VHOST_USER_DEVICE_PREFIX = 'vhu'
 # Device names start with "tap"
 TAP_DEVICE_PREFIX = 'tap'
 # The vswitch side of a veth pair for a nova iptables filter setup
index 5fcdb56234beef5c2a53bf38f298e4772fd5adc4..d42482544627dceb466ac4492f8f128b2e42c48c 100644 (file)
@@ -53,6 +53,8 @@ ovs_opts = [
                choices=[constants.OVS_DATAPATH_SYSTEM,
                         constants.OVS_DATAPATH_NETDEV],
                help=_("OVS datapath to use.")),
+    cfg.StrOpt('vhostuser_socket_dir', default=constants.VHOST_USER_SOCKET_DIR,
+               help=_("OVS vhost-user socket directory.")),
     cfg.IPOpt('of_listen_address', default='127.0.0.1',
               help=_("Address to listen on for OpenFlow connections. "
                      "Used only for 'native' driver.")),
index 095553d5c3157442e99836202f5fef44a917c915..ba256c1f826b7c8bb0d9f5af8e1ca45678e375c8 100644 (file)
@@ -100,5 +100,9 @@ EXTENSION_DRIVER_TYPE = 'ovs'
 # ovs datapath types
 OVS_DATAPATH_SYSTEM = 'system'
 OVS_DATAPATH_NETDEV = 'netdev'
+OVS_DPDK_VHOST_USER = 'dpdkvhostuser'
+
+# default ovs vhost-user socket location
+VHOST_USER_SOCKET_DIR = '/var/run/openvswitch'
 
 MAX_DEVICE_RETRIES = 5
index 085c093ed97e46a17075c4609f7893e2e867eae6..3a5476d7275bb45c5e2be0d28ae0a9d96ffce151 100644 (file)
@@ -181,6 +181,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
         '''
         super(OVSNeutronAgent, self).__init__()
         self.conf = conf or cfg.CONF
+        self.ovs = ovs_lib.BaseOVS()
 
         # init bridge classes with configured datapath type.
         self.br_int_cls, self.br_phys_cls, self.br_tun_cls = (
@@ -282,7 +283,11 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
                                self.enable_distributed_routing,
                                'log_agent_heartbeats':
                                self.conf.AGENT.log_agent_heartbeats,
-                               'extensions': self.ext_manager.names()},
+                               'extensions': self.ext_manager.names(),
+                               'datapath_type': self.conf.OVS.datapath_type,
+                               'ovs_capabilities': self.ovs.capabilities,
+                               'vhostuser_socket_dir':
+                               self.conf.OVS.vhostuser_socket_dir},
             'agent_type': self.conf.AGENT.agent_type,
             'start_flag': True}
 
index 2ad29dd00b38c61c6d0f4744ca5ec9d11b5364ce..cc7007ac397e4124aa528e2fb51a68edddc715f2 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import os
+
 from oslo_log import log
 
 from neutron.agent import securitygroups_rpc
 from neutron.common import constants
 from neutron.extensions import portbindings
 from neutron.plugins.common import constants as p_constants
+from neutron.plugins.ml2 import driver_api as api
 from neutron.plugins.ml2.drivers import mech_agent
+from neutron.plugins.ml2.drivers.openvswitch.agent.common \
+    import constants as a_const
 from neutron.services.qos import qos_consts
 
 LOG = log.getLogger(__name__)
@@ -57,3 +62,44 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
     def check_vlan_transparency(self, context):
         """Currently Openvswitch driver doesn't support vlan transparency."""
         return False
+
+    def try_to_bind_segment_for_agent(self, context, segment, agent):
+        if self.check_segment_for_agent(segment, agent):
+            context.set_binding(segment[api.ID],
+                                self.get_vif_type(agent, context),
+                                self.get_vif_details(agent, context))
+            return True
+        else:
+            return False
+
+    def get_vif_type(self, agent, context):
+        caps = agent['configurations'].get('ovs_capabilities', {})
+        if (a_const.OVS_DPDK_VHOST_USER in caps.get('iface_types', []) and
+                agent['configurations'].get('datapath_type') ==
+                a_const.OVS_DATAPATH_NETDEV):
+            return portbindings.VIF_TYPE_VHOST_USER
+        return self.vif_type
+
+    def get_vif_details(self, agent, context):
+        if (agent['configurations'].get('datapath_type') !=
+                a_const.OVS_DATAPATH_NETDEV):
+            return self.vif_details
+        caps = agent['configurations'].get('ovs_capabilities', {})
+        if a_const.OVS_DPDK_VHOST_USER in caps.get('iface_types', []):
+            sock_path = self.agent_vhu_sockpath(agent, context.current['id'])
+            return {
+                portbindings.CAP_PORT_FILTER: False,
+                portbindings.VHOST_USER_MODE:
+                    portbindings.VHOST_USER_MODE_CLIENT,
+                portbindings.VHOST_USER_OVS_PLUG: True,
+                portbindings.VHOST_USER_SOCKET: sock_path
+            }
+        return self.vif_details
+
+    @staticmethod
+    def agent_vhu_sockpath(agent, port_id):
+        """Return the agent's vhost-user socket path for a given port"""
+        sockdir = agent['configurations'].get('vhostuser_socket_dir',
+                                              a_const.VHOST_USER_SOCKET_DIR)
+        sock_name = (constants.VHOST_USER_DEVICE_PREFIX + port_id)[:14]
+        return os.path.join(sockdir, sock_name)
index 7838be8a88c09bff9ce7f8ac098628e5791c731b..cd85720c90622b17362d91a73622e6f5950dc763 100644 (file)
@@ -129,6 +129,9 @@ class TestOvsNeutronAgent(object):
             'neutron.agent.common.ovs_lib.OVSBridge.get_ports_attributes',
             return_value=[]).start()
 
+        mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config',
+                   new_callable=mock.PropertyMock,
+                   return_value={}).start()
         with mock.patch.object(self.mod_agent.OVSNeutronAgent,
                                'setup_integration_br'),\
                 mock.patch.object(self.mod_agent.OVSNeutronAgent,
@@ -199,7 +202,10 @@ class TestOvsNeutronAgent(object):
                        new=MockFixedIntervalLoopingCall), \
             mock.patch(
                 'neutron.agent.common.ovs_lib.OVSBridge.' 'get_vif_ports',
-                return_value=[]):
+                return_value=[]), \
+            mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config',
+                       new_callable=mock.PropertyMock,
+                       return_value={'datapath_types': ['netdev']}):
             # validate setting non default datapath
             expected = constants.OVS_DATAPATH_NETDEV
             cfg.CONF.set_override('datapath_type',
@@ -1610,6 +1616,9 @@ class AncillaryBridgesTest(object):
                              group='SECURITYGROUP')
         cfg.CONF.set_override('report_interval', 0, 'AGENT')
         self.kwargs = self.mod_agent.create_agent_config_map(cfg.CONF)
+        mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config',
+                   new_callable=mock.PropertyMock,
+                   return_value={}).start()
 
     def _test_ancillary_bridges(self, bridges, ancillary):
         device_ids = ancillary[:]
@@ -1727,6 +1736,9 @@ class TestOvsDvrNeutronAgent(object):
                              group='SECURITYGROUP')
         kwargs = self.mod_agent.create_agent_config_map(cfg.CONF)
 
+        mock.patch('neutron.agent.common.ovs_lib.BaseOVS.config',
+                   new_callable=mock.PropertyMock,
+                   return_value={}).start()
         with mock.patch.object(self.mod_agent.OVSNeutronAgent,
                                'setup_integration_br'),\
                 mock.patch.object(self.mod_agent.OVSNeutronAgent,
index 079d2dea083e74df087daa34e887c574c5d10749..e0a38e79efef9a12b19cf253f2fde8ffa91a82aa 100644 (file)
@@ -97,6 +97,10 @@ class TunnelTest(object):
         self.inta = mock.Mock()
         self.intb = mock.Mock()
 
+        mock.patch.object(ovs_lib.BaseOVS, 'config',
+                          new_callable=mock.PropertyMock,
+                          return_value={}).start()
+
         self.ovs_bridges = {
             self.INT_BRIDGE: mock.create_autospec(
                 self.br_int_cls('br-int')),