diff options
Diffstat (limited to 'vswitches')
-rw-r--r-- | vswitches/README | 6 | ||||
-rw-r--r-- | vswitches/README.md | 11 | ||||
-rw-r--r-- | vswitches/ovs.py | 38 | ||||
-rw-r--r-- | vswitches/ovs_dpdk_vhost.py | 54 | ||||
-rw-r--r-- | vswitches/ovs_vanilla.py | 20 | ||||
-rw-r--r-- | vswitches/vpp_dpdk_vhost.py | 403 | ||||
-rw-r--r-- | vswitches/vswitch.py | 33 |
7 files changed, 522 insertions, 43 deletions
diff --git a/vswitches/README b/vswitches/README deleted file mode 100644 index 5af284ec..00000000 --- a/vswitches/README +++ /dev/null @@ -1,6 +0,0 @@ -This folder contains various vswitches to be tested - -Required API for each vswitch: - add_switch - add_port - add_flow diff --git a/vswitches/README.md b/vswitches/README.md new file mode 100644 index 00000000..ea1e11fb --- /dev/null +++ b/vswitches/README.md @@ -0,0 +1,11 @@ +<!--- +This work is licensed under a Creative Commons Attribution 4.0 International License. +http://creativecommons.org/licenses/by/4.0 +--> + +This folder contains various vswitches to be tested + +Required API for each vswitch: + add_switch + add_port + add_flow diff --git a/vswitches/ovs.py b/vswitches/ovs.py index 886a98e0..b6d64fb7 100644 --- a/vswitches/ovs.py +++ b/vswitches/ovs.py @@ -1,4 +1,4 @@ -# Copyright 2015-2016 Intel Corporation. +# Copyright 2015-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. @@ -25,9 +25,10 @@ import pexpect from conf import settings from src.ovs import OFBridge, flow_key, flow_match -from tools import tasks from vswitches.vswitch import IVSwitch - +from tools import tasks +from tools.module_manager import ModuleManager +# pylint: disable=too-many-public-methods class IVSwitchOvs(IVSwitch, tasks.Process): """Open vSwitch base class implementation @@ -55,6 +56,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): self._cmd = [] self._cmd_template = ['sudo', '-E', settings.getValue('TOOLS')['ovs-vswitchd']] self._stamp = None + self._module_manager = ModuleManager() def start(self): """ Start ``ovsdb-server`` and ``ovs-vswitchd`` instance. @@ -138,7 +140,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): bridge = self._bridges[switch_name] remote_bridge = self._bridges[remote_switch_name] pcount = str(self._get_port_count('type=patch')) - # TODO ::: What if interface name longer than allowed width?? + # NOTE ::: What if interface name longer than allowed width?? local_port_name = switch_name + '-' + remote_switch_name + '-' + pcount remote_port_name = remote_switch_name + '-' + switch_name + '-' + pcount local_params = ['--', 'set', 'Interface', local_port_name, @@ -376,8 +378,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): # # validate methods required for integration testcases # - - def validate_add_switch(self, result, switch_name, params=None): + def validate_add_switch(self, dummy_result, switch_name, dummy_params=None): """Validate - Create a new logical switch with no ports """ bridge = self._bridges[switch_name] @@ -386,7 +387,9 @@ class IVSwitchOvs(IVSwitch, tasks.Process): assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is not None return True - def validate_del_switch(self, result, switch_name): + # Method could be a function + # pylint: disable=no-self-use + def validate_del_switch(self, dummy_result, switch_name): """Validate removal of switch """ bridge = OFBridge('tmp') @@ -410,7 +413,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ return self.validate_add_phy_port(result, switch_name) - def validate_del_port(self, result, switch_name, port_name): + def validate_del_port(self, dummy_result, switch_name, port_name): """ Validate that port_name was removed from bridge. """ bridge = self._bridges[switch_name] @@ -419,11 +422,12 @@ class IVSwitchOvs(IVSwitch, tasks.Process): assert 'Port "%s"' % port_name not in output[0] return True - def validate_add_flow(self, result, switch_name, flow, cache='off'): + def validate_add_flow(self, dummy_result, switch_name, flow, dummy_cache='off'): """ Validate insertion of the flow into the switch """ + if 'idle_timeout' in flow: - del(flow['idle_timeout']) + del flow['idle_timeout'] # Note: it should be possible to call `ovs-ofctl dump-flows switch flow` # to verify flow insertion, but it doesn't accept the same flow syntax @@ -439,38 +443,38 @@ class IVSwitchOvs(IVSwitch, tasks.Process): return True return False - def validate_del_flow(self, result, switch_name, flow=None): + def validate_del_flow(self, dummy_result, switch_name, flow=None): """ Validate removal of the flow """ if not flow: # what else we can do? return True - return not self.validate_add_flow(result, switch_name, flow) + return not self.validate_add_flow(dummy_result, switch_name, flow) - def validate_dump_flows(self, result, switch_name): + def validate_dump_flows(self, dummy_result, dummy_switch_name): """ Validate call of flow dump """ return True - def validate_disable_rstp(self, result, switch_name): + def validate_disable_rstp(self, dummy_result, switch_name): """ Validate rstp disable """ bridge = self._bridges[switch_name] return 'rstp_enable : false' in ''.join(bridge.bridge_info()) - def validate_enable_rstp(self, result, switch_name): + def validate_enable_rstp(self, dummy_result, switch_name): """ Validate rstp enable """ bridge = self._bridges[switch_name] return 'rstp_enable : true' in ''.join(bridge.bridge_info()) - def validate_disable_stp(self, result, switch_name): + def validate_disable_stp(self, dummy_result, switch_name): """ Validate stp disable """ bridge = self._bridges[switch_name] return 'stp_enable : false' in ''.join(bridge.bridge_info()) - def validate_enable_stp(self, result, switch_name): + def validate_enable_stp(self, dummy_result, switch_name): """ Validate stp enable """ bridge = self._bridges[switch_name] diff --git a/vswitches/ovs_dpdk_vhost.py b/vswitches/ovs_dpdk_vhost.py index 40f4533b..3387fda7 100644 --- a/vswitches/ovs_dpdk_vhost.py +++ b/vswitches/ovs_dpdk_vhost.py @@ -1,4 +1,4 @@ -# Copyright 2015-2016 Intel Corporation. +# Copyright 2015-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. @@ -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') - nic_pci = 'options:dpdk-devargs=' + _NICS[dpdk_count]['pci'] + _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 12a460af..75870ab7 100644 --- a/vswitches/ovs_vanilla.py +++ b/vswitches/ovs_vanilla.py @@ -1,4 +1,4 @@ -# Copyright 2015-2016 Intel Corporation. +# Copyright 2015-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. @@ -19,7 +19,6 @@ import logging from conf import settings from vswitches.ovs import IVSwitchOvs from src.ovs import DPCtl -from tools.module_manager import ModuleManager from tools import tasks class OvsVanilla(IVSwitchOvs): @@ -41,7 +40,6 @@ class OvsVanilla(IVSwitchOvs): self._logger = logging.getLogger(__name__) self._vswitchd_args += ["unix:%s" % self.get_db_sock_path()] self._vswitchd_args += settings.getValue('VSWITCHD_VANILLA_ARGS') - self._module_manager = ModuleManager() def stop(self): """See IVswitch for general description @@ -75,8 +73,7 @@ class OvsVanilla(IVSwitchOvs): self._logger.error("Can't add port! There are only " + len(self._ports) + " ports " + "defined in config!") - raise - + raise RuntimeError('Failed to add phy port') if not self._ports[self._current_id]: self._logger.error("Can't detect device name for NIC %s", self._current_id) raise ValueError("Invalid device name for %s" % self._current_id) @@ -129,4 +126,17 @@ class OvsVanilla(IVSwitchOvs): 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 |