vpp: Initial support of VPP vSwitch 91/30191/3
authorMartin Klozik <martinx.klozik@intel.com>
Mon, 27 Feb 2017 09:00:50 +0000 (09:00 +0000)
committerMartin Klozik <martinx.klozik@intel.com>
Wed, 15 Mar 2017 16:11:45 +0000 (16:11 +0000)
Support of VPP was implemented into VSPERF. Initial implementation
uses step driven testcases to configure P2P, PVP and PVVP network
scenarios. These testcases were prepared for three RFC2544 traffic
types, i.e. throughput, continuous stream and back to back.
VPP configuration is driven by new configuration option VSWITCH_VPP_ARGS.
It is possible to use three types of l2 port connection supported
by VPP, i.e. l2 xconnect (default), l2patch and l2 bridge features.
Configuration is driven by parameter VSWITCH_VPP_L2_CONNECT_MODE.

JIRA: VSPERF-495

Change-Id: Idebef9b10fb0d70796adb3405fec77302de00a7e
Signed-off-by: Martin Klozik <martinx.klozik@intel.com>
Reviewed-by: Al Morton <acmorton@att.com>
Reviewed-by: Christian Trautman <ctrautma@redhat.com>
Reviewed-by: Sridhar Rao <sridhar.rao@spirent.com>
Reviewed-by: Trevor Cooper <trevor.cooper@intel.com>
17 files changed:
conf/01_testcases.conf
conf/02_vswitch.conf
conf/05_collector.conf
conf/06_pktfwd.conf
core/loader/loader_servant.py
docs/configguide/installation.rst
docs/configguide/trafficgen.rst
docs/design/vswitchperf_design.rst
docs/userguide/teststeps.rst
docs/userguide/testusage.rst
testcases/testcase.py
tools/pkt_fwd/testpmd.py
vsperf
vswitches/ovs_dpdk_vhost.py
vswitches/ovs_vanilla.py
vswitches/vpp_dpdk_vhost.py [new file with mode: 0644]
vswitches/vswitch.py

index b30afc8..4851b04 100755 (executable)
 # "Test Modifier": [FrameMod|Other],
 # "Dependency": [Test_Case_Name |None],
 
+#
+# VPP specific macros used in TC defintions
+#
+VPP_P2P =   [
+                ['vswitch', 'add_switch', 'int_br0'],           # STEP 0
+                ['vswitch', 'add_phy_port', 'int_br0'],         # STEP 1
+                ['vswitch', 'add_phy_port', 'int_br0'],         # STEP 2
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[1][0]', '#STEP[2][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[2][0]', '#STEP[1][0]', True],
+                ['trafficgen', 'send_traffic', {}],
+                ['vswitch', 'dump_connections', 'int_br0'],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[1][0]', '#STEP[2][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[2][0]', '#STEP[1][0]', True],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+                ['vswitch', 'del_switch', 'int_br0'],
+            ]
+VPP_PVP =   [
+                ['vswitch', 'add_switch', 'int_br0'],           # STEP 0
+                ['vswitch', 'add_phy_port', 'int_br0'],         # STEP 1
+                ['vswitch', 'add_phy_port', 'int_br0'],         # STEP 2
+                ['vswitch', 'add_vport', 'int_br0'],            # STEP 3
+                ['vswitch', 'add_vport', 'int_br0'],            # STEP 4
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[1][0]', '#STEP[3][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[4][0]', '#STEP[2][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[2][0]', '#STEP[4][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[3][0]', '#STEP[1][0]', True],
+                ['vnf', 'start'],
+                ['trafficgen', 'send_traffic', {}],
+                ['vnf', 'stop'],
+                ['vswitch', 'dump_connections', 'int_br0'],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[1][0]', '#STEP[3][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[4][0]', '#STEP[2][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[2][0]', '#STEP[4][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[3][0]', '#STEP[1][0]', True],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[3][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[4][0]'],
+                ['vswitch', 'del_switch', 'int_br0'],
+            ]
+VPP_PVVP =   [
+                ['vswitch', 'add_switch', 'int_br0'],           # STEP 0
+                ['vswitch', 'add_phy_port', 'int_br0'],         # STEP 1
+                ['vswitch', 'add_phy_port', 'int_br0'],         # STEP 2
+                ['vswitch', 'add_vport', 'int_br0'],            # STEP 3
+                ['vswitch', 'add_vport', 'int_br0'],            # STEP 4
+                ['vswitch', 'add_vport', 'int_br0'],            # STEP 5
+                ['vswitch', 'add_vport', 'int_br0'],            # STEP 6
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[1][0]', '#STEP[3][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[4][0]', '#STEP[5][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[6][0]', '#STEP[2][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[2][0]', '#STEP[6][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[5][0]', '#STEP[4][0]', True],
+                ['vswitch', 'add_connection', 'int_br0', '#STEP[3][0]', '#STEP[1][0]', True],
+                ['vnf1', 'start'],
+                ['vnf2', 'start'],
+                ['trafficgen', 'send_traffic', {}],
+                ['vnf2', 'stop'],
+                ['vnf1', 'stop'],
+                ['vswitch', 'dump_connections', 'int_br0'],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[1][0]', '#STEP[3][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[4][0]', '#STEP[5][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[6][0]', '#STEP[2][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[2][0]', '#STEP[6][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[5][0]', '#STEP[4][0]', True],
+                ['vswitch', 'del_connection', 'int_br0', '#STEP[3][0]', '#STEP[1][0]', True],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[3][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[4][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[5][0]'],
+                ['vswitch', 'del_port', 'int_br0', '#STEP[6][0]'],
+                ['vswitch', 'del_switch', 'int_br0'],
+            ]
+
+#
+# Generic performance TC definitions
+#
 PERFORMANCE_TESTS = [
     {
         "Name": "phy2phy_tput",
@@ -269,4 +348,113 @@ PERFORMANCE_TESTS = [
             },
         },
     },
+    {
+        "Name": "phy2phy_tput_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: LTD.Throughput.RFC2544.PacketLossRatio",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_throughput",
+            },
+        },
+        "TestSteps": VPP_P2P,
+    },
+    {
+        "Name": "phy2phy_cont_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: Phy2Phy Continuous Stream",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_continuous",
+                "frame_rate" : 100,
+            },
+        },
+        "TestSteps": VPP_P2P,
+    },
+    {
+        "Name": "phy2phy_back2back_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: LTD.Throughput.RFC2544.BackToBackFrames",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_back2back",
+            },
+        },
+        "TestSteps": VPP_P2P,
+    },
+    {
+        "Name": "pvp_tput_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: LTD.Throughput.RFC2544.PacketLossRatio",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_throughput",
+            },
+        },
+        "TestSteps": VPP_PVP,
+    },
+    {
+        "Name": "pvp_cont_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: PVP Continuous Stream",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_continuous",
+            },
+        },
+        "TestSteps": VPP_PVP,
+    },
+    {
+        "Name": "pvp_back2back_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: LTD.Throughput.RFC2544.BackToBackFrames",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_back2back",
+            },
+        },
+        "TestSteps": VPP_PVP,
+    },
+    {
+        "Name": "pvvp_tput_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: LTD.Throughput.RFC2544.PacketLossRatio",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_throughput",
+            },
+        },
+        "TestSteps": VPP_PVVP,
+    },
+    {
+        "Name": "pvvp_cont_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: PVP Continuous Stream",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_continuous",
+            },
+        },
+        "TestSteps": VPP_PVVP,
+    },
+    {
+        "Name": "pvvp_back2back_vpp",
+        "Deployment": "clean",
+        "Description": "VPP: LTD.Throughput.RFC2544.BackToBackFrames",
+        "vSwitch" : "VppDpdkVhost",
+        "Parameters" : {
+            "TRAFFIC" : {
+                "traffic_type" : "rfc2544_back2back",
+            },
+        },
+        "TestSteps": VPP_PVVP,
+    },
 ]
index 2ca7591..2bac173 100644 (file)
@@ -33,6 +33,12 @@ RTE_TARGET = 'x86_64-native-linuxapp-gcc'
 # will be used for testing
 WHITELIST_NICS = []
 
+# List defines an amount of memory to be allocated by DPDK at NUMA nodes. This
+# option is shared by all vSwitches with DPDK support. In case, that there is
+# a socket-mem configuration specified in vSwitch specific configuration option,
+# then it will be overridden by DPDK_SOCKET_MEM value.
+DPDK_SOCKET_MEM = ['1024', '0']
+
 # vhost character device file used by dpdkvhostport QemuWrap cases
 VHOST_DEV_FILE = 'ovs-vhost-net'
 
@@ -103,6 +109,18 @@ PATHS['vswitch'] = {
     },
     'ovs_var_tmp': '/usr/local/var/run/openvswitch/',
     'ovs_etc_tmp': '/usr/local/etc/openvswitch/',
+    'VppDpdkVhost': {
+        'type' : 'bin',
+        'src': {
+            'path': os.path.join(ROOT_DIR, 'src/vpp/vpp/build-root/build-vpp-native'),
+            'vpp': 'vpp',
+            'vppctl': 'vppctl',
+        },
+        'bin': {
+            'vpp': 'vpp',
+            'vppctl': 'vppctl',
+        }
+    },
 }
 
 # default OvsVanilla configuration is similar to OvsDpdkVhost except 'path' and 'modules'
@@ -116,18 +134,19 @@ PATHS['vswitch']['OvsVanilla']['bin']['modules'] = ['openvswitch']
 # ############################
 # These are DPDK EAL parameters and they may need to be changed depending on
 # hardware configuration, like cpu numbering and NUMA.
-#
+
 # parameters used for legacy DPDK configuration through '--dpdk' option of ovs-vswitchd
 # e.g. ovs-vswitchd --dpdk --socket-mem 1024,0
 # This config line is also used for pkt_fwd option (TestPMD phy2phy and pvp tests)
-VSWITCHD_DPDK_ARGS = ['-c', '0x4', '-n', '4', '--socket-mem 1024,0']
+# NOTE: DPDK socket mem allocation is driven by parameter DPDK_SOCKET_MEM
+VSWITCHD_DPDK_ARGS = ['-c', '0x4', '-n', '4']
 
 # options used for new type of OVS configuration via calls to ovs-vsctl
 # e.g. ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-socket-mem="1024,0"
+# NOTE: DPDK socket mem allocation is driven by parameter DPDK_SOCKET_MEM
 VSWITCHD_DPDK_CONFIG = {
     'dpdk-init' : 'true',
     'dpdk-lcore-mask' : '0x4',
-    'dpdk-socket-mem' : '1024,0',
 }
 # Note: VSPERF will automatically detect, which type of DPDK configuration should
 # be used.
@@ -169,3 +188,30 @@ LOG_FILE_OVS = 'ovs.log'
 
 # default vswitch implementation
 VSWITCH = "OvsDpdkVhost"
+
+#########################
+## VPP
+#########################
+# Set of arguments used for startup of VPP
+# NOTE: DPDK socket mem allocation is driven by parameter DPDK_SOCKET_MEM
+VSWITCH_VPP_ARGS = {
+    'unix' : [
+        'interactive',      # required by VSPERF to detect successful VPP startup
+        'log /tmp/vpp.log',
+        'full-coredump',
+    ],
+    'cpu' : [
+        'main-core 3',
+        'corelist-workers 4,5',
+    ],
+}
+
+# log file for VPP
+LOG_FILE_VPP = 'vsperf-vpp.log'
+
+# Select l2 connection method used by VPP.
+# Supported values are: 'xconnect', 'l2patch' and 'bridge'
+VSWITCH_VPP_L2_CONNECT_MODE = 'xconnect'
+
+# Options used during creation of dpdkvhostuser interface
+VSWITCH_VPP_VHOSTUSER_ARGS = ['feature-mask',  '0xFF']
index bda0ac8..9fd2558 100644 (file)
@@ -20,7 +20,7 @@ COLLECTOR = 'Pidstat'
 COLLECTOR_DIR = os.path.join(ROOT_DIR, 'tools/collectors')
 
 # processes to be monitored by pidstat
-PIDSTAT_MONITOR = ['ovs-vswitchd', 'ovsdb-server', 'qemu-system-x86_64']
+PIDSTAT_MONITOR = ['ovs-vswitchd', 'ovsdb-server', 'qemu-system-x86_64', 'vpp']
 
 # options which will be passed to pidstat
 PIDSTAT_OPTIONS = '-dur'
index 6175aa6..bb4c1d7 100644 (file)
@@ -36,4 +36,4 @@ TESTPMD_CSUM_CALC = 'sw'
 # recognize tunnel headers: on|off
 TESTPMD_CSUM_PARSE_TUNNEL = 'off'
 
-PIDSTAT_MONITOR = ['ovs-vswitchd', 'ovsdb-server', 'qemu-system-x86_64', 'testpmd']
+PIDSTAT_MONITOR = ['ovs-vswitchd', 'ovsdb-server', 'qemu-system-x86_64', 'vpp', 'testpmd']
index bbb4ea9..8bad9ab 100644 (file)
@@ -86,7 +86,8 @@ class LoaderServant(object):
                                 interface=self._interface)
         results = []
 
-        for (name, mod) in list(out.items()):
+        # sort modules to produce the same output everytime
+        for (name, mod) in sorted(out.items()):
             desc = (mod.__doc__ or 'No description').strip().split('\n')[0]
             results.append((name, desc))
 
index bda5a0b..1965a8f 100644 (file)
@@ -165,6 +165,22 @@ built from upstream source due to kernel incompatibilities. Please see the
 instructions in the vswitchperf_design document for details on configuring
 OVS Vanilla for binary package usage.
 
+.. _vpp-installation:
+
+VPP installation
+================
+
+Currently vswitchperf installation scripts do not support automatic build
+of VPP. In order to execute tests with VPP, it is required to install it
+manually. Please refer to the official documentation of `fd.io`_ project to
+install VPP from `packages`_ or from the `sources`_.
+
+See details about :ref:`vpp-test`.
+
+.. _fd.io: https://fd.io/
+.. _packages: https://wiki.fd.io/view/VPP/Installing_VPP_binaries_from_packages
+.. _sources: https://wiki.fd.io/view/VPP/Build,_install,_and_test_images
+
 Using vswitchperf
 -----------------
 
@@ -260,20 +276,10 @@ your configuration in the ``02_vswitch.conf`` file.
 
 .. code:: bash
 
-    VSWITCHD_DPDK_ARGS = ['-c', '0x4', '-n', '4', '--socket-mem 1024,1024']
-    VSWITCHD_DPDK_CONFIG = {
-        'dpdk-init' : 'true',
-        'dpdk-lcore-mask' : '0x4',
-        'dpdk-socket-mem' : '1024,1024',
-    }
-
-**NOTE:** Option ``VSWITCHD_DPDK_ARGS`` is used for vswitchd, which supports ``--dpdk``
-parameter. In recent vswitchd versions, option ``VSWITCHD_DPDK_CONFIG`` is
-used to configure vswitchd via ``ovs-vsctl`` calls.
+    DPDK_SOCKET_MEM = ['1024', '0']
 
-With the ``--socket-mem`` argument set to use 1 hugepage on the specified sockets as
-seen above, the configuration will need 10 hugepages total to run all tests
-within vsperf if the pagesize is set correctly to 1GB.
+**NOTE:** Option ``DPDK_SOCKET_MEM`` is used by all vSwitches with DPDK support.
+It means Open vSwitch, VPP and TestPMD.
 
 VSPerf will verify hugepage amounts are free before executing test
 environments. In case of hugepage amounts not being free, test initialization
index 3c33d4e..4e42b2b 100644 (file)
@@ -469,6 +469,7 @@ For RFC2889 tests, specifying the locations for the monitoring ports is mandator
 Necessary parameters are:
 
 .. code-block:: console
+
     TRAFFICGEN_STC_RFC2889_TEST_FILE_NAME
     TRAFFICGEN_STC_EAST_CHASSIS_ADDR = " "
     TRAFFICGEN_STC_EAST_SLOT_NUM = " "
index 9e74e59..da7ec6f 100644 (file)
@@ -498,6 +498,8 @@ Other Configuration
 
 ``conf.settings`` also loads configuration from the command line and from the environment.
 
+.. _pxp-deployment:
+
 PXP Deployment
 ==============
 
index 4e6c080..870c3d8 100644 (file)
@@ -2,6 +2,8 @@
 .. http://creativecommons.org/licenses/by/4.0
 .. (c) OPNFV, Intel Corporation, AT&T and others.
 
+.. _step-driven-tests:
+
 Step driven tests
 =================
 
index 0055164..c6037aa 100644 (file)
@@ -335,6 +335,43 @@ To run tests using Vanilla OVS:
 
        $ ./vsperf --conf-file<path_to_custom_conf>/10_custom.conf
 
+.. _vpp-test:
+
+Executing VPP tests
+^^^^^^^^^^^^^^^^^^^
+
+Currently it is not possible to use standard scenario deployments for execution of
+tests with VPP. It means, that deployments ``p2p``, ``pvp``, ``pvvp`` and in general any
+:ref:`pxp-deployment` won't work with VPP. However it is possible to use VPP in
+:ref:`step-driven-tests`. A basic set of VPP testcases covering ``phy2phy``, ``pvp``
+and ``pvvp`` tests are already prepared.
+
+List of performance tests with VPP support follows:
+
+* phy2phy_tput_vpp:              VPP: LTD.Throughput.RFC2544.PacketLossRatio
+* phy2phy_cont_vpp:              VPP: Phy2Phy Continuous Stream
+* phy2phy_back2back_vpp:         VPP: LTD.Throughput.RFC2544.BackToBackFrames
+* pvp_tput_vpp:                  VPP: LTD.Throughput.RFC2544.PacketLossRatio
+* pvp_cont_vpp:                  VPP: PVP Continuous Stream
+* pvp_back2back_vpp:             VPP: LTD.Throughput.RFC2544.BackToBackFrames
+* pvvp_tput_vpp:                 VPP: LTD.Throughput.RFC2544.PacketLossRatio
+* pvvp_cont_vpp:                 VPP: PVP Continuous Stream
+* pvvp_back2back_vpp:            VPP: LTD.Throughput.RFC2544.BackToBackFrames
+
+In order to execute testcases with VPP it is required to:
+
+* install VPP manually, see :ref:`vpp-installation`
+* configure ``WHITELIST_NICS``, with two physical NICs connected to the traffic generator
+* configure traffic generator, see :ref:`trafficgen-installation`
+
+After that it is possible to execute VPP testcases listed above.
+
+For example:
+
+.. code-block:: console
+
+    $ ./vsperf --conf-file=<path_to_custom_conf> phy2phy_tput_vpp
+
 .. _vfio-pci:
 
 Using vfio_pci with DPDK
@@ -689,7 +726,8 @@ application to use the correct number of nb-cores.
 
     .. code-block:: python
 
-        VSWITCHD_DPDK_ARGS = ['-l', '46,44,42,40,38', '-n', '4', '--socket-mem 1024,0']
+        DPDK_SOCKET_MEM = ['1024', '0']
+        VSWITCHD_DPDK_ARGS = ['-l', '46,44,42,40,38', '-n', '4']
         TESTPMD_ARGS = ['--nb-cores=4', '--txq=1', '--rxq=1']
 
 For guest TestPMD 3 VCpus should be assigned with the following TestPMD params.
@@ -790,16 +828,17 @@ an appropriate amount of memory:
 
 .. code-block:: python
 
-    VSWITCHD_DPDK_ARGS = ['-c', '0x4', '-n', '4', '--socket-mem 1024,0']
+    DPDK_SOCKET_MEM = ['1024', '0']
+    VSWITCHD_DPDK_ARGS = ['-c', '0x4', '-n', '4']
     VSWITCHD_DPDK_CONFIG = {
         'dpdk-init' : 'true',
         'dpdk-lcore-mask' : '0x4',
         'dpdk-socket-mem' : '1024,0',
     }
 
-Note: Option VSWITCHD_DPDK_ARGS is used for vswitchd, which supports --dpdk
-parameter. In recent vswitchd versions, option VSWITCHD_DPDK_CONFIG will be
-used to configure vswitchd via ovs-vsctl calls.
+Note: Option ``VSWITCHD_DPDK_ARGS`` is used for vswitchd, which supports ``--dpdk``
+parameter. In recent vswitchd versions, option ``VSWITCHD_DPDK_CONFIG`` will be
+used to configure vswitchd via ``ovs-vsctl`` calls.
 
 
 More information
index d74f34a..4fbf9c0 100644 (file)
@@ -257,9 +257,6 @@ class TestCase(object):
         # umount hugepages if mounted
         self._umount_hugepages()
 
-        # restore original settings
-        S.load_from_dict(self._settings_original)
-
         # cleanup any namespaces created
         if os.path.isdir('/tmp/namespaces'):
             namespace_list = os.listdir('/tmp/namespaces')
@@ -333,6 +330,9 @@ class TestCase(object):
         # report test results
         self.run_report()
 
+        # restore original settings
+        S.load_from_dict(self._settings_original)
+
     def _update_settings(self, param, value):
         """ Check value of given configuration parameter
         In case that new value is different, then testcase
@@ -460,26 +460,11 @@ class TestCase(object):
 
         # get hugepage amounts for each socket on dpdk
         sock0_mem, sock1_mem = 0, 0
+
         if S.getValue('VSWITCH').lower().count('dpdk'):
-            # the import below needs to remain here and not put into the module
-            # imports because of an exception due to settings not yet loaded
-            from vswitches import ovs_dpdk_vhost
-            if ovs_dpdk_vhost.OvsDpdkVhost.old_dpdk_config():
-                match = re.search(
-                    r'-socket-mem\s+(\d+),(\d+)',
-                    ''.join(S.getValue('VSWITCHD_DPDK_ARGS')))
-                if match:
-                    sock0_mem, sock1_mem = (int(match.group(1)) * 1024 / hugepage_size,
-                                            int(match.group(2)) * 1024 / hugepage_size)
-                else:
-                    logging.info(
-                        'Could not parse socket memory config in dpdk params.')
-            else:
-                sock0_mem, sock1_mem = (
-                    S.getValue(
-                        'VSWITCHD_DPDK_CONFIG')['dpdk-socket-mem'].split(','))
-                sock0_mem, sock1_mem = (int(sock0_mem) * 1024 / hugepage_size,
-                                        int(sock1_mem) * 1024 / hugepage_size)
+            sock_mem = S.getValue('DPDK_SOCKET_MEM')
+            sock0_mem, sock1_mem = (int(sock_mem[0]) * 1024 / hugepage_size,
+                                    int(sock_mem[1]) * 1024 / hugepage_size)
 
         # If hugepages needed, verify the amounts are free
         if any([hugepages_needed, sock0_mem, sock1_mem]):
index 8255f1d..970259d 100644 (file)
@@ -41,6 +41,14 @@ class TestPMD(IPktFwd):
 
     def __init__(self, guest=False):
         vswitchd_args = settings.getValue('VSWITCHD_DPDK_ARGS')
+
+        # override socket-mem settings
+        for tmp_arg in vswitchd_args:
+            if tmp_arg.startswith('--socket-mem'):
+                vswitchd_args.remove(tmp_arg)
+        vswitchd_args += ['--socket-mem ' +
+                          ','.join(settings.getValue('DPDK_SOCKET_MEM'))]
+
         if guest:
             vswitchd_args += _TESTPMD_PVP_CONST_ARGS
         vswitchd_args += _VSWITCHD_CONST_ARGS
diff --git a/vsperf b/vsperf
index 44887fc..fea7997 100755 (executable)
--- a/vsperf
+++ b/vsperf
@@ -285,6 +285,28 @@ def check_and_set_locale():
         _LOGGER.warning("Locale was not properly configured. Default values were set. Old locale: %s, New locale: %s",
                         system_locale, locale.getdefaultlocale())
 
+def get_vswitch_names(rst_files):
+    """ Function will return a list of vSwitches detected in given ``rst_files``.
+    """
+    vswitch_names = set()
+    if len(rst_files):
+        try:
+            output = subprocess.check_output(['grep', '-h', '^* vSwitch'] + rst_files).decode().splitlines()
+            for line in output:
+                match = re.search(r'^\* vSwitch: ([^,]+)', str(line))
+                if match:
+                    vswitch_names.add(match.group(1))
+
+            if len(vswitch_names):
+                return list(vswitch_names)
+
+        except subprocess.CalledProcessError:
+            _LOGGER.warning('Cannot detect vSwitches used during testing.')
+
+    # fallback to the default value
+    return ['vSwitch']
+
+
 
 def generate_final_report():
     """ Function will check if partial test results are available
@@ -294,18 +316,15 @@ def generate_final_report():
     path = settings.getValue('RESULTS_PATH')
     # check if there are any results in rst format
     rst_results = glob.glob(os.path.join(path, 'result*rst'))
+    pkt_processors = get_vswitch_names(rst_results)
     if len(rst_results):
         try:
-            test_report = os.path.join(path, '{}_{}'.format(settings.getValue('VSWITCH'), _TEMPLATE_RST['final']))
+            test_report = os.path.join(path, '{}_{}'.format('_'.join(pkt_processors), _TEMPLATE_RST['final']))
             # create report caption directly - it is not worth to execute jinja machinery
-            if settings.getValue('VSWITCH').lower() != 'none':
-                pkt_processor = Loader().get_vswitches()[settings.getValue('VSWITCH')].__doc__.strip().split('\n')[0]
-            else:
-                pkt_processor = Loader().get_pktfwds()[settings.getValue('PKTFWD')].__doc__.strip().split('\n')[0]
             report_caption = '{}\n{} {}\n{}\n\n'.format(
                 '============================================================',
                 'Performance report for',
-                pkt_processor,
+                ', '.join(pkt_processors),
                 '============================================================')
 
             with open(_TEMPLATE_RST['tmp'], 'w') as file_:
index a49c8dd..3387fda 100644 (file)
@@ -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()
index 649a5ab..75870ab 100644 (file)
@@ -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 (file)
index 0000000..d0d9e2a
--- /dev/null
@@ -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()
index 73e0a0c..893bd1f 100644 (file)
@@ -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