From acfe92288878bf37b470e83c8fdc6beeb7b26f82 Mon Sep 17 00:00:00 2001 From: Kiran Shastri Date: Fri, 6 Apr 2018 00:26:40 -0700 Subject: [PATCH] Support for ovs-vhostuser and vpp datapath DPDK is a high performance data path. ovs-dpdk: https://docs.openstack.org/newton/networking-guide/config-ovs-dpdk.html VPP is a datapath that builds on top of dpdk for better performance. VPP: https://wiki.fd.io/view/VPP/What_is_VPP%3F Both of these use vhostuser-socket interfaces. Adding opflex network binding support for these data paths. Change-Id: Id18be10e73b4bcf84a826da2f3d483fe55bf5620 --- .../drivers/apic_aim/mechanism_driver.py | 75 +++++++++++++++++-- .../unit/plugins/ml2plus/test_apic_aim.py | 50 +++++++++++++ 2 files changed, 117 insertions(+), 8 deletions(-) diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py index 457dc1c9b..c4de73a79 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py @@ -15,6 +15,7 @@ import copy import netaddr +import os import re import sqlalchemy as sa @@ -46,6 +47,8 @@ from neutron.plugins.common import constants as pconst from neutron.plugins.ml2 import db as n_db from neutron.plugins.ml2 import driver_api as api from neutron.plugins.ml2 import driver_context as ml2_context +from neutron.plugins.ml2.drivers.openvswitch.agent.common import ( + constants as a_const) from neutron.plugins.ml2 import models from neutron_lib.api.definitions import portbindings from neutron_lib import constants as n_constants @@ -172,6 +175,7 @@ class KeystoneNotificationEndpoint(object): class ApicMechanismDriver(api_plus.MechanismDriver, db.DbMixin): + NIC_NAME_LEN = 14 class TopologyRpcEndpoint(object): target = oslo_messaging.Target(version='3.0') @@ -1746,6 +1750,11 @@ class ApicMechanismDriver(api_plus.MechanismDriver, self._opflex_bind_port): return + # Try to bind OpFlex VPP agent. + if self._agent_bind_port(context, ofcst.AGENT_TYPE_OPFLEX_VPP, + self._opflex_bind_port): + return + # If we reached here, it means that either there is no active opflex # agent running on the host, or the agent on the host is not # configured for this physical network. Treat the host as a physical @@ -2142,8 +2151,9 @@ class ApicMechanismDriver(api_plus.MechanismDriver, return False elif network_type != 'local': return False - - self._complete_binding(context, segment) + context.set_binding( + segment[api.ID], self._opflex_get_vif_type(agent), + self._opflex_get_vif_details(context, agent)) return True def _dvs_bind_port(self, context, segment, agent): @@ -2214,17 +2224,66 @@ class ApicMechanismDriver(api_plus.MechanismDriver, elif segment.get('aim_ml2_created'): # Complete binding if another driver did not bind the # dynamic segment that we created. - self._complete_binding(context, segment) + context.set_binding(segment[api.ID], portbindings.VIF_TYPE_OVS, + self._update_binding_sg()) return True - def _complete_binding(self, context, segment): + def _opflex_get_vif_type(self, agent): + if agent['agent_type'] == ofcst.AGENT_TYPE_OPFLEX_VPP: + return portbindings.VIF_TYPE_VHOST_USER + else: + if (agent['configurations'].get('datapath_type') == + a_const.OVS_DATAPATH_NETDEV): + return portbindings.VIF_TYPE_VHOST_USER + else: + return portbindings.VIF_TYPE_OVS + + @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 = (n_constants.VHOST_USER_DEVICE_PREFIX + + port_id)[:ApicMechanismDriver.NIC_NAME_LEN] + return os.path.join(sockdir, sock_name) + + def _get_vhost_mode(self): + # REVISIT(kshastri): this function converts the ovs vhost user + # driver mode into the qemu vhost user mode. If OVS is the server, + # qemu is the client and vice-versa. For ACI MD, we will need to + # support agent capabilities field to choose client-mode. As of + # now only support server mode for nova. + return portbindings.VHOST_USER_MODE_SERVER + + def _opflex_get_vif_details(self, context, agent): + vif_type = self._opflex_get_vif_type(agent) + details = {} + if vif_type == portbindings.VIF_TYPE_VHOST_USER: + sock_path = self._agent_vhu_sockpath(agent, + context.current['id']) + mode = self._get_vhost_mode() + details = {portbindings.VHOST_USER_MODE: mode, + portbindings.VHOST_USER_SOCKET: sock_path} + if agent['agent_type'] == ofcst.AGENT_TYPE_OPFLEX_VPP: + details.update({portbindings.CAP_PORT_FILTER: False, + portbindings.OVS_HYBRID_PLUG: False, + portbindings.VHOST_USER_OVS_PLUG: False, + ofcst.VHOST_USER_VPP_PLUG: True}) + else: + details.update({portbindings.OVS_DATAPATH_TYPE: + a_const.OVS_DATAPATH_NETDEV, + portbindings.VHOST_USER_OVS_PLUG: True}) + + if agent['agent_type'] == ofcst.AGENT_TYPE_OPFLEX_OVS: + details.update(self._update_binding_sg()) + return details + + def _update_binding_sg(self): enable_firewall = False if self.enable_iptables_firewall: enable_firewall = self.sg_enabled - context.set_binding( - segment[api.ID], portbindings.VIF_TYPE_OVS, - {portbindings.CAP_PORT_FILTER: enable_firewall, - portbindings.OVS_HYBRID_PLUG: enable_firewall}) + return {portbindings.CAP_PORT_FILTER: enable_firewall, + portbindings.OVS_HYBRID_PLUG: enable_firewall} @property def plugin(self): diff --git a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py index ff4f587fb..f35a7e390 100644 --- a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py +++ b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py @@ -85,6 +85,23 @@ AGENT_TYPE_DVS = md.AGENT_TYPE_DVS AGENT_CONF_DVS = {'alive': True, 'binary': 'anotherbinary', 'topic': 'anothertopic', 'agent_type': AGENT_TYPE_DVS, 'configurations': {'opflex_networks': None}} + +AGENT_TYPE_VPP = ofcst.AGENT_TYPE_OPFLEX_VPP +AGENT_CONF_VPP = {'alive': True, 'binary': 'somebinary', + 'topic': 'sometopic', 'agent_type': AGENT_TYPE_VPP, + 'configurations': { + 'opflex_networks': None, + 'vhostuser_socket_dir': '/var/run/vpp-sockets'}} + +AGENT_CONF_OPFLEX_OVS_DPDK = {'alive': True, 'binary': 'somebinary', + 'topic': 'sometopic', + 'agent_type': ofcst.AGENT_TYPE_OPFLEX_OVS, + 'configurations': { + 'opflex_networks': None, + 'bridge_mappings': {'physnet1': 'br-eth1'}, + 'vhostuser_socket_dir': + '/var/run/openvswitch', + 'datapath_type': 'netdev'}} BOOKED_PORT_VALUE = 'myBookedPort' DN = 'apic:distinguished_names' @@ -4029,6 +4046,39 @@ class TestPortBinding(ApicAimTestCase): port['binding:vif_details']) self.assertEqual(n_constants.PORT_STATUS_ACTIVE, port['status']) + def test_bind_port_ovs_dpdk(self): + self._register_agent('host1', AGENT_CONF_OPFLEX_OVS_DPDK) + net = self._make_network(self.fmt, 'net1', True) + self._make_subnet(self.fmt, net, '10.0.1.1', '10.0.1.0/24') + port = self._make_port(self.fmt, net['network']['id'])['port'] + port_id = port['id'] + port = self._bind_port_to_host(port_id, 'host1')['port'] + self.assertEqual('vhostuser', port['binding:vif_type']) + self.assertEqual( + {'datapath_type': 'netdev', 'port_filter': False, + 'ovs_hybrid_plug': False, 'vhostuser_ovs_plug': True, + 'vhostuser_mode': 'server', 'vhostuser_socket': + AGENT_CONF_OPFLEX_OVS_DPDK['configurations'][ + 'vhostuser_socket_dir'] + '/' + ('vhu' + port_id)[:14]}, + port['binding:vif_details']) + + def test_bind_port_vpp(self): + self._register_agent('host1', AGENT_CONF_VPP) + net = self._make_network(self.fmt, 'net1', True) + self._make_subnet(self.fmt, net, '10.0.1.1', '10.0.1.0/24') + port = self._make_port(self.fmt, net['network']['id'])['port'] + port_id = port['id'] + port = self._bind_port_to_host(port_id, 'host1')['port'] + self.assertEqual('vhostuser', port['binding:vif_type']) + self.assertEqual({'port_filter': False, 'ovs_hybrid_plug': False, + 'vhostuser_ovs_plug': False, + 'vhostuser_vpp_plug': True, + 'vhostuser_mode': 'server', + 'vhostuser_socket': AGENT_CONF_VPP['configurations'][ + 'vhostuser_socket_dir'] + '/' + ( + ('vhu' + port_id)[:14])}, + port['binding:vif_details']) + # TODO(rkukura): Add tests for opflex, local and unsupported # network_type values.