aboutsummaryrefslogtreecommitdiffstats
path: root/vswitches
diff options
context:
space:
mode:
Diffstat (limited to 'vswitches')
-rw-r--r--vswitches/ovs_dpdk_vhost.py50
-rw-r--r--vswitches/ovs_vanilla.py15
-rw-r--r--vswitches/vpp_dpdk_vhost.py403
-rw-r--r--vswitches/vswitch.py33
4 files changed, 488 insertions, 13 deletions
diff --git a/vswitches/ovs_dpdk_vhost.py b/vswitches/ovs_dpdk_vhost.py
index a49c8dd7..3387fda7 100644
--- a/vswitches/ovs_dpdk_vhost.py
+++ b/vswitches/ovs_dpdk_vhost.py
@@ -20,7 +20,7 @@ import subprocess
from src.ovs import OFBridge
from src.dpdk import dpdk
-from conf import settings
+from conf import settings as S
from vswitches.ovs import IVSwitchOvs
class OvsDpdkVhost(IVSwitchOvs):
@@ -43,7 +43,14 @@ class OvsDpdkVhost(IVSwitchOvs):
# legacy DPDK configuration through --dpdk option of vswitchd
if self.old_dpdk_config():
- vswitchd_args = ['--dpdk'] + settings.getValue('VSWITCHD_DPDK_ARGS')
+ # override socket-mem settings
+ tmp_dpdk_args = S.getValue('VSWITCHD_DPDK_ARGS')
+ for tmp_arg in tmp_dpdk_args:
+ if tmp_arg.startswith('--socket-mem'):
+ tmp_dpdk_args.remove(tmp_arg)
+ tmp_dpdk_args += ['--socket-mem ' + ','.join(S.getValue('DPDK_SOCKET_MEM'))]
+ vswitchd_args = ['--dpdk'] + tmp_dpdk_args
+ # add dpdk args to generic ovs-vswitchd settings
if self._vswitchd_args:
self._vswitchd_args = vswitchd_args + ['--'] + self._vswitchd_args
else:
@@ -52,8 +59,10 @@ class OvsDpdkVhost(IVSwitchOvs):
def configure(self):
""" Configure vswitchd DPDK options through ovsdb if needed
"""
- dpdk_config = settings.getValue('VSWITCHD_DPDK_CONFIG')
+ dpdk_config = S.getValue('VSWITCHD_DPDK_CONFIG')
if dpdk_config and not self.old_dpdk_config():
+ # override socket-mem settings
+ dpdk_config['dpdk-socket-mem'] = ','.join(S.getValue('DPDK_SOCKET_MEM'))
# enforce calls to ovs-vsctl with --no-wait
tmp_br = OFBridge(timeout=-1)
for option in dpdk_config:
@@ -68,12 +77,12 @@ class OvsDpdkVhost(IVSwitchOvs):
dpdk.init()
super(OvsDpdkVhost, self).start()
# old style OVS <= 2.5.0 multi-queue enable
- if settings.getValue('OVS_OLD_STYLE_MQ') and \
- int(settings.getValue('VSWITCH_DPDK_MULTI_QUEUES')):
+ if S.getValue('OVS_OLD_STYLE_MQ') and \
+ int(S.getValue('VSWITCH_DPDK_MULTI_QUEUES')):
tmp_br = OFBridge(timeout=-1)
tmp_br.set_db_attribute(
'Open_vSwitch', '.', 'other_config:' +
- 'n-dpdk-rxqs', settings.getValue('VSWITCH_DPDK_MULTI_QUEUES'))
+ 'n-dpdk-rxqs', S.getValue('VSWITCH_DPDK_MULTI_QUEUES'))
def stop(self):
"""See IVswitch for general description
@@ -92,12 +101,12 @@ class OvsDpdkVhost(IVSwitchOvs):
switch_params = switch_params + params
super(OvsDpdkVhost, self).add_switch(switch_name, switch_params)
- if settings.getValue('VSWITCH_AFFINITIZATION_ON') == 1:
+ if S.getValue('VSWITCH_AFFINITIZATION_ON') == 1:
# Sets the PMD core mask to VSWITCH_PMD_CPU_MASK
# for CPU core affinitization
self._bridges[switch_name].set_db_attribute('Open_vSwitch', '.',
'other_config:pmd-cpu-mask',
- settings.getValue('VSWITCH_PMD_CPU_MASK'))
+ S.getValue('VSWITCH_PMD_CPU_MASK'))
def add_phy_port(self, switch_name):
"""See IVswitch for general description
@@ -110,15 +119,15 @@ class OvsDpdkVhost(IVSwitchOvs):
port_name = 'dpdk' + str(dpdk_count)
# PCI info. Please note there must be no blank space, eg must be
# like 'options:dpdk-devargs=0000:06:00.0'
- _nics = settings.getValue('NICS')
+ _nics = S.getValue('NICS')
nic_pci = 'options:dpdk-devargs=' + _nics[dpdk_count]['pci']
params = ['--', 'set', 'Interface', port_name, 'type=dpdk', nic_pci]
# multi-queue enable
- if int(settings.getValue('VSWITCH_DPDK_MULTI_QUEUES')) and \
- not settings.getValue('OVS_OLD_STYLE_MQ'):
+ if int(S.getValue('VSWITCH_DPDK_MULTI_QUEUES')) and \
+ not S.getValue('OVS_OLD_STYLE_MQ'):
params += ['options:n_rxq={}'.format(
- settings.getValue('VSWITCH_DPDK_MULTI_QUEUES'))]
+ S.getValue('VSWITCH_DPDK_MULTI_QUEUES'))]
of_port = bridge.add_port(port_name, params)
return (port_name, of_port)
@@ -144,9 +153,24 @@ class OvsDpdkVhost(IVSwitchOvs):
:returns: True if legacy --dpdk option is supported, otherwise it returns False
"""
- ovs_vswitchd_bin = settings.getValue('TOOLS')['ovs-vswitchd']
+ ovs_vswitchd_bin = S.getValue('TOOLS')['ovs-vswitchd']
try:
subprocess.check_output(ovs_vswitchd_bin + r' --help | grep "\-\-dpdk"', shell=True)
return True
except subprocess.CalledProcessError:
return False
+
+ def add_connection(self, switch_name, port1, port2, bidir=False):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def del_connection(self, switch_name, port1, port2, bidir=False):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def dump_connections(self, switch_name):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
diff --git a/vswitches/ovs_vanilla.py b/vswitches/ovs_vanilla.py
index 649a5ab9..75870ab7 100644
--- a/vswitches/ovs_vanilla.py
+++ b/vswitches/ovs_vanilla.py
@@ -125,3 +125,18 @@ class OvsVanilla(IVSwitchOvs):
bridge = self._bridges[switch_name]
of_port = bridge.add_port(tap_name, [])
return (tap_name, of_port)
+
+ def add_connection(self, switch_name, port1, port2, bidir=False):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def del_connection(self, switch_name, port1, port2, bidir=False):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def dump_connections(self, switch_name):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
diff --git a/vswitches/vpp_dpdk_vhost.py b/vswitches/vpp_dpdk_vhost.py
new file mode 100644
index 00000000..d0d9e2ac
--- /dev/null
+++ b/vswitches/vpp_dpdk_vhost.py
@@ -0,0 +1,403 @@
+# Copyright 2017 Intel Corporation.
+#
+# 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.
+
+"""VSPERF VPP implementation using DPDK and vhostuser vports
+"""
+
+import logging
+import os
+import copy
+import re
+import pexpect
+
+from src.dpdk import dpdk
+from conf import settings as S
+from vswitches.vswitch import IVSwitch
+from tools import tasks
+
+# pylint: disable=too-many-public-methods
+class VppDpdkVhost(IVSwitch, tasks.Process):
+ """ VPP with DPDK support
+ """
+ _proc_name = 'vpp'
+ _bridge_idx_counter = 100
+
+ def __init__(self):
+ """See IVswitch for general description
+ """
+ self._logfile = os.path.join(S.getValue('LOG_DIR'),
+ S.getValue('LOG_FILE_VPP'))
+ self._logger = logging.getLogger(__name__)
+ self._expect = r'vpp#'
+ self._timeout = 30
+ self._vswitch_args = []
+ self._cmd = []
+ self._cmd_template = ['sudo', '-E', S.getValue('TOOLS')['vpp']]
+ self._stamp = None
+ self._logger = logging.getLogger(__name__)
+ self._phy_ports = []
+ self._virt_ports = []
+ self._switches = {}
+
+ # configure DPDK NICs
+ tmp_args = copy.deepcopy(S.getValue('VSWITCH_VPP_ARGS'))
+ if 'dpdk' not in tmp_args:
+ tmp_args['dpdk'] = []
+
+ # override socket-mem settings
+ for tmp_arg in tmp_args['dpdk']:
+ if tmp_arg.startswith('socket-mem'):
+ tmp_args['dpdk'].remove(tmp_arg)
+ tmp_args['dpdk'].append('socket-mem ' +
+ ','.join(S.getValue('DPDK_SOCKET_MEM')))
+
+ for nic in S.getValue('NICS'):
+ tmp_args['dpdk'].append("dev {}".format(nic['pci']))
+ self._vswitch_args = self._process_vpp_args(tmp_args)
+
+ def _get_nic_info(self, key='Name'):
+ """Read NIC info from VPP and return NIC details in a dictionary
+ indexed by given ``key``
+
+ :param key: Name of the key to be used for indexing result dictionary
+
+ :returns: Dictionary with NIC infos including their PCI addresses
+ """
+ result = {}
+ output = self.run_vppctl(['show', 'hardware', 'brief'])
+ # parse output and store basic info about NICS
+ ifaces = output[0].split('\n')
+ keys = ifaces[0].split()
+ keys.append('Pci')
+ keyidx = keys.index(key)
+ for iface in ifaces[1:]:
+ tmpif = iface.split()
+ # get PCI address of given interface
+ output = self.run_vppctl(['show', 'hardware', tmpif[1], 'detail'])
+ match = re.search(r'pci address:\s*([\d:\.]+)', output[0])
+ if match:
+ # normalize PCI address, e.g. 0000:05:10.01 => 0000:05:10.1
+ tmp_pci = match.group(1).split('.')
+ tmp_pci[1] = str(int(tmp_pci[1]))
+ tmpif.append('.'.join(tmp_pci))
+ else:
+ tmpif.append(None)
+ # store only NICs with reasonable index
+ if tmpif[keyidx] is not None:
+ result[tmpif[keyidx]] = dict(zip(keys, tmpif))
+
+ return result
+
+ def _process_vpp_args(self, args):
+ """Produce VPP CLI args from input dictionary ``args``
+ """
+ cli_args = []
+ for cfg_key in args:
+ cli_args.append(cfg_key)
+ cli_args.append("{{ {} }}".format(' '.join(args[cfg_key])))
+
+ self._logger.debug("VPP CLI args: %s", cli_args)
+ return cli_args
+
+
+ def start(self):
+ """Activates DPDK kernel modules and starts VPP
+
+ :raises: pexpect.EOF, pexpect.TIMEOUT
+ """
+ dpdk.init()
+ self._logger.info("Starting VPP...")
+
+ self._cmd = self._cmd_template + self._vswitch_args
+
+ try:
+ tasks.Process.start(self)
+ self.relinquish()
+ except (pexpect.EOF, pexpect.TIMEOUT) as exc:
+ logging.error("Exception during VPP start.")
+ raise exc
+
+ self._logger.info("VPP...Started.")
+
+ def stop(self):
+ """See IVswitch for general description
+
+ Kills VPP and removes DPDK kernel modules.
+ """
+ self._logger.info("Terminating VPP...")
+ self.kill()
+ self._logger.info("VPP...Terminated.")
+ dpdk.cleanup()
+
+ def kill(self, signal='-15', sleep=10):
+ """See IVswitch for general description
+
+ Kills ``vpp``
+ """
+ # try to get VPP pid
+ output = self.run_vppctl(['show', 'version', 'verbose'])
+ match = re.search(r'Current PID:\s*([0-9]+)', output[0])
+ if match:
+ vpp_pid = match.group(1)
+ tasks.terminate_task(vpp_pid, logger=self._logger)
+
+ # in case, that pid was not detected or sudo envelope
+ # has not been terminated yet
+ tasks.Process.kill(self, signal, sleep)
+
+ def add_switch(self, switch_name, dummy_params=None):
+ """See IVswitch for general description
+ """
+ if switch_name in self._switches:
+ self._logger.warning("switch %s already exists...", switch_name)
+ else:
+ self._switches[switch_name] = self._bridge_idx_counter
+ self._bridge_idx_counter += 1
+
+ def del_switch(self, switch_name):
+ """See IVswitch for general description
+ """
+ if switch_name in self._switches:
+ del self._switches[switch_name]
+
+ def add_phy_port(self, dummy_switch_name):
+ """See IVswitch for general description
+ :raises: RuntimeError
+ """
+ # get list of physical interfaces with PCI addresses
+ vpp_nics = self._get_nic_info(key='Pci')
+ # check if there are any NICs left
+ if len(self._phy_ports) >= len(S.getValue('NICS')):
+ raise RuntimeError('All available NICs are already configured!')
+
+ nic = S.getValue('NICS')[len(self._phy_ports)]
+ if not nic['pci'] in vpp_nics:
+ raise RuntimeError('VPP cannot access nic with PCI address: {}'.format(nic['pci']))
+ nic_name = vpp_nics[nic['pci']]['Name']
+ self._phy_ports.append(nic_name)
+ self.run_vppctl(['set', 'int', 'state', nic_name, 'up'])
+ return (nic_name, vpp_nics[nic['pci']]['Idx'])
+
+ def add_vport(self, dummy_switch_name):
+ """See IVswitch for general description
+ """
+ socket_name = S.getValue('TOOLS')['ovs_var_tmp'] + 'dpdkvhostuser' + str(len(self._virt_ports))
+ output = self.run_vppctl(['create', 'vhost-user', 'socket', socket_name, 'server'] +
+ S.getValue('VSWITCH_VPP_VHOSTUSER_ARGS'))
+ nic_name = output[0]
+ self._virt_ports.append(nic_name)
+ self.run_vppctl(['set', 'int', 'state', nic_name, 'up'])
+ return (nic_name, None)
+
+ def del_port(self, switch_name, port_name):
+ """See IVswitch for general description
+ """
+ if port_name in self._phy_ports:
+ self.run_vppctl(['set', 'int', 'state', port_name, 'down'])
+ self._phy_ports.remove(port_name)
+ elif port_name in self._virt_ports:
+ self.run_vppctl(['set', 'int', 'state', port_name, 'down'])
+ self.run_vppctl(['delete', 'vhost-user', port_name])
+ self._virt_ports.remove(port_name)
+ else:
+ self._logger.warning("Port %s is not configured.", port_name)
+
+ def add_l2patch(self, port1, port2, bidir=False):
+ """Create l2patch connection between given ports
+ """
+ self.run_vppctl(['test', 'l2patch', 'rx', port1, 'tx', port2])
+ if bidir:
+ self.run_vppctl(['test', 'l2patch', 'rx', port2, 'tx', port1])
+
+ def add_xconnect(self, port1, port2, bidir=False):
+ """Create l2patch connection between given ports
+ """
+ self.run_vppctl(['set', 'interface', 'l2', 'xconnect', port1, port2])
+ if bidir:
+ self.run_vppctl(['set', 'interface', 'l2', 'xconnect', port2, port1])
+
+ def add_bridge(self, switch_name, port1, port2, dummy_bidir=False):
+ """Add given ports to bridge ``switch_name``
+ """
+ self.run_vppctl(['set', 'interface', 'l2', 'bridge', port1,
+ str(self._switches[switch_name])])
+ self.run_vppctl(['set', 'interface', 'l2', 'bridge', port2,
+ str(self._switches[switch_name])])
+
+ def add_connection(self, switch_name, port1, port2, bidir=False):
+ """See IVswitch for general description
+
+ :raises: RuntimeError
+ """
+ mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE')
+ if mode == 'l2patch':
+ self.add_l2patch(port1, port2, bidir)
+ elif mode == 'xconnect':
+ self.add_xconnect(port1, port2, bidir)
+ elif mode == 'bridge':
+ self.add_bridge(switch_name, port1, port2)
+ else:
+ raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode)
+
+ def del_l2patch(self, port1, port2, bidir=False):
+ """Remove l2patch connection between given ports
+
+ :param port1: port to be used in connection
+ :param port2: port to be used in connection
+ :param bidir: switch between uni and bidirectional traffic
+ """
+ self.run_vppctl(['test', 'l2patch', 'rx', port1, 'tx', port2, 'del'])
+ if bidir:
+ self.run_vppctl(['test', 'l2patch', 'rx', port2, 'tx', port1, 'del'])
+
+ def del_xconnect(self, dummy_port1, dummy_port2, dummy_bidir=False):
+ """Remove xconnect connection between given ports
+ """
+ self._logger.warning('VPP: Removal of l2 xconnect is not implemented.')
+
+ def del_bridge(self, dummy_switch_name, dummy_port1, dummy_port2):
+ """Remove given ports from the bridge
+ """
+ self._logger.warning('VPP: Removal of interfaces from bridge is not implemented.')
+
+ def del_connection(self, switch_name, port1, port2, bidir=False):
+ """See IVswitch for general description
+
+ :raises: RuntimeError
+ """
+ mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE')
+ if mode == 'l2patch':
+ self.del_l2patch(port1, port2, bidir)
+ elif mode == 'xconnect':
+ self.del_xconnect(port1, port2, bidir)
+ elif mode == 'bridge':
+ self.del_bridge(switch_name, port1, port2)
+ else:
+ raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode)
+
+ def dump_l2patch(self):
+ """Dump l2patch connections
+ """
+ self.run_vppctl(['show', 'l2patch'])
+
+ def dump_xconnect(self):
+ """Dump l2 xconnect connections
+ """
+ self._logger.warning("VPP: Dump of l2 xconnections is not supported.")
+
+ def dump_bridge(self, switch_name):
+ """Show bridge details
+
+ :param switch_name: switch on which to operate
+ """
+ self.run_vppctl(['show', 'bridge-domain', str(self._switches[switch_name]), 'int'])
+
+ def dump_connections(self, switch_name):
+ """See IVswitch for general description
+
+ :raises: RuntimeError
+ """
+ mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE')
+ if mode == 'l2patch':
+ self.dump_l2patch()
+ elif mode == 'xconnect':
+ self.dump_xconnect()
+ elif mode == 'bridge':
+ self.dump_bridge(switch_name)
+ else:
+ raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode)
+
+ def run_vppctl(self, args, check_error=False):
+ """Run ``vppctl`` with supplied arguments.
+
+ :param args: Arguments to pass to ``vppctl``
+ :param check_error: Throw exception on error
+
+ :return: None
+ """
+ cmd = ['sudo', S.getValue('TOOLS')['vppctl']] + args
+ return tasks.run_task(cmd, self._logger, 'Running vppctl...', check_error)
+
+ #
+ # Validate methods
+ #
+ def validate_add_switch(self, dummy_result, switch_name, dummy_params=None):
+ """Validate - Create a new logical switch with no ports
+ """
+ return switch_name in self._switches
+
+ def validate_del_switch(self, dummy_result, switch_name):
+ """Validate removal of switch
+ """
+ return not self.validate_add_switch(dummy_result, switch_name)
+
+ def validate_add_phy_port(self, result, dummy_switch_name):
+ """ Validate that physical port was added to bridge.
+ """
+ return result[0] in self._phy_ports
+
+ def validate_add_vport(self, result, dummy_switch_name):
+ """ Validate that virtual port was added to bridge.
+ """
+ return result[0] in self._virt_ports
+
+ def validate_del_port(self, dummy_result, dummy_switch_name, port_name):
+ """ Validate that port_name was removed from bridge.
+ """
+ return not (port_name in self._phy_ports or port_name in self._virt_ports)
+
+ # pylint: disable=no-self-use
+ def validate_run_vppctl(self, result, dummy_args, dummy_check_error=False):
+ """validate execution of ``vppctl`` with supplied arguments.
+ """
+ # there shouldn't be any stderr
+ return not result[1]
+
+ #
+ # Non implemented methods
+ #
+ def add_flow(self, switch_name, flow, cache='off'):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def del_flow(self, switch_name, flow=None):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def dump_flows(self, switch_name):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def add_route(self, switch_name, network, destination):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def set_tunnel_arp(self, ip_addr, mac_addr, switch_name):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
+
+ def get_ports(self, switch_name):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError()
diff --git a/vswitches/vswitch.py b/vswitches/vswitch.py
index 73e0a0c3..893bd1ff 100644
--- a/vswitches/vswitch.py
+++ b/vswitches/vswitch.py
@@ -130,6 +130,39 @@ class IVSwitch(object):
"""
raise NotImplementedError()
+ def add_connection(self, switch_name, port1, port2, bidir=False):
+ """Creates connection between given ports.
+
+ :param switch_name: switch on which to operate
+ :param port1: port to be used in connection
+ :param port2: port to be used in connection
+ :param bidir: switch between uni and bidirectional traffic
+
+ :raises: RuntimeError
+ """
+ raise NotImplementedError()
+
+ def del_connection(self, switch_name, port1, port2, bidir=False):
+ """Remove connection between two interfaces.
+
+ :param switch_name: switch on which to operate
+ :param port1: port to be used in connection
+ :param port2: port to be used in connection
+ :param bidir: switch between uni and bidirectional traffic
+
+ :raises: RuntimeError
+ """
+ raise NotImplementedError()
+
+ def dump_connections(self, switch_name):
+ """Dump connections between interfaces.
+
+ :param switch_name: switch on which to operate
+
+ :raises: RuntimeError
+ """
+ raise NotImplementedError()
+
def dump_flows(self, switch_name):
"""Dump flows from the logical switch