diff options
-rw-r--r-- | docs/release/NEWS.rst | 9 | ||||
-rwxr-xr-x | docs/userguide/integration.rst | 328 | ||||
-rw-r--r-- | src/dpdk/dpdk.py | 13 | ||||
-rw-r--r-- | tools/networkcard.py | 2 | ||||
-rw-r--r-- | tools/pkt_gen/moongen/moongen.py | 41 | ||||
-rwxr-xr-x | tools/pkt_gen/xena/xena.py | 103 | ||||
-rw-r--r-- | tools/systeminfo.py | 41 | ||||
-rw-r--r-- | vnfs/qemu/qemu.py | 8 | ||||
-rw-r--r-- | vnfs/qemu/qemu_pci_passthrough.py | 3 |
9 files changed, 465 insertions, 83 deletions
diff --git a/docs/release/NEWS.rst b/docs/release/NEWS.rst index 19ad3240..47ede0a3 100644 --- a/docs/release/NEWS.rst +++ b/docs/release/NEWS.rst @@ -4,9 +4,18 @@ OPNFV Colorado Release ====================== +* Support for DPDK v16.07 +* Support for yardstick testing framework +* Support for stp/rstp configuration +* Support for veth ports and network namespaces +* Support for multi-queue usage by testpmd loopback app +* Support for reporting of test execution length +* Support for MoonGen traffic generator. * Support for OVS version 2.5 + DPDK 2.2. * Support for DPDK v16.04 * Support for Xena traffic generator. +* Support for Red Hat Enterprise Linux +* Support for mode of operation (trafficgen, trafficgen-off) * Support for Integration tests for OVS with DPDK including: * Physical ports. * Virtual ports (vhost user and vhost cuse). diff --git a/docs/userguide/integration.rst b/docs/userguide/integration.rst index eccd0c76..e45f2dc1 100755 --- a/docs/userguide/integration.rst +++ b/docs/userguide/integration.rst @@ -71,7 +71,7 @@ test step calls returns a value it can be later recalled, for example: ] } -This test profile uses the the vswitch add_vport method which returns a string +This test profile uses the vswitch add_vport method which returns a string value of the port added. This is later called by the del_port method using the name from step 1. @@ -103,6 +103,332 @@ This profile can then be used inside other testcases STEP_VSWITCH_PVP_FINIT } +HelloWorld and other basic Testcases +------------------------------------ + +The following examples are for demonstration purposes. +You can run them by copying and pasting into the +conf/integration/01_testcases.conf file. +A command-line instruction is shown at the end of each +example. + +HelloWorld +^^^^^^^^^^ + +The first example is a HelloWorld testcase. +It simply creates a bridge with 2 physical ports, then sets up a flow to drop +incoming packets from the port that was instantiated at the STEP #1. +There's no interaction with the traffic generator. +Then the flow, the 2 ports and the bridge are deleted. +'add_phy_port' method creates a 'dpdk' type interface that will manage the +physical port. The string value returned is the port name that will be referred +by 'del_port' later on. + +.. code-block:: python + + { + "Name": "HelloWorld", + "Description": "My first testcase", + "Deployment": "clean", + "TestSteps": [ + ['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_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'actions': ['drop'], 'idle_timeout': '0'}], + ['vswitch', 'del_flow', 'int_br0'], + ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'], + ['vswitch', 'del_switch', 'int_br0'], + ] + + } + +To run HelloWorld test: + + .. code-block:: console + + ./vsperf --conf-file user_settings.py --integration HelloWorld + +Specify a Flow by the IP address +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The next example shows how to explicitly set up a flow by specifying a +destination IP address. +All packets received from the port created at STEP #1 that have a destination +IP address = 90.90.90.90 will be forwarded to the port created at the STEP #2. + +.. code-block:: python + + { + "Name": "p2p_rule_l3da", + "Description": "Phy2Phy with rule on L3 Dest Addr", + "Deployment": "clean", + "biDirectional": "False", + "TestSteps": [ + ['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_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'dl_type': '0x0800', 'nw_dst': '90.90.90.90', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + ['trafficgen', 'send_traffic', {'traffic_type' : 'continuous'}], + ['vswitch', 'dump_flows', 'int_br0'], # STEP 5 + ['vswitch', 'del_flow', 'int_br0'], # STEP 7 == del-flows + ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'], + ['vswitch', 'del_switch', 'int_br0'], + ] + }, + +To run the test: + + .. code-block:: console + + ./vsperf --conf-file user_settings.py --integration p2p_rule_l3da + +Multistream feature +^^^^^^^^^^^^^^^^^^^ + +The next testcase uses the multistream feature. +The traffic generator will send packets with different UDP ports. +That is accomplished by using "Stream Type" and "MultiStream" keywords. +4 different flows are set to forward all incoming packets. + +.. code-block:: python + + { + "Name": "multistream_l4", + "Description": "Multistream on UDP ports", + "Deployment": "clean", + "Stream Type": "L4", + "MultiStream": 4, + "TestSteps": [ + ['vswitch', 'add_switch', 'int_br0'], # STEP 0 + ['vswitch', 'add_phy_port', 'int_br0'], # STEP 1 + ['vswitch', 'add_phy_port', 'int_br0'], # STEP 2 + # Setup Flows + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'dl_type': '0x0800', 'nw_proto': '17', 'udp_dst': '0', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'dl_type': '0x0800', 'nw_proto': '17', 'udp_dst': '1', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'dl_type': '0x0800', 'nw_proto': '17', 'udp_dst': '2', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'dl_type': '0x0800', 'nw_proto': '17', 'udp_dst': '3', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + # Send mono-dir traffic + ['trafficgen', 'send_traffic', {'traffic_type' : 'continuous', \ + 'bidir' : 'False'}], + # Clean up + ['vswitch', 'del_flow', 'int_br0'], + ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'], + ['vswitch', 'del_switch', 'int_br0'], + ] + }, + +To run the test: + + .. code-block:: console + + ./vsperf --conf-file user_settings.py --integration multistream_l4 + +PVP with a VM Replacement +^^^^^^^^^^^^^^^^^^^^^^^^^ + +This example launches a 1st VM in a PVP topology, then the VM is replaced +by another VM. +When VNF setup parameter in ./conf/04_vnf.conf is "QemuDpdkVhostUser" +'add_vport' method creates a 'dpdkvhostuser' type port to connect a VM. + +.. code-block:: python + + { + "Name": "ex_replace_vm", + "Description": "PVP with VM replacement", + "Deployment": "clean", + "TestSteps": [ + ['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 vm1 + ['vswitch', 'add_vport', 'int_br0'], # STEP 4 + + # Setup Flows + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'actions': ['output:#STEP[3][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[4][1]', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[2][1]', \ + 'actions': ['output:#STEP[4][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[3][1]', \ + 'actions': ['output:#STEP[1][1]'], 'idle_timeout': '0'}], + + # Start VM 1 + ['vnf1', 'start'], + # Now we want to replace VM 1 with another VM + ['vnf1', 'stop'], + + ['vswitch', 'add_vport', 'int_br0'], # STEP 11 vm2 + ['vswitch', 'add_vport', 'int_br0'], # STEP 12 + ['vswitch', 'del_flow', 'int_br0'], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'actions': ['output:#STEP[11][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[12][1]', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + + # Start VM 2 + ['vnf2', 'start'], + ['vnf2', 'stop'], + ['vswitch', 'dump_flows', 'int_br0'], + + # Clean up + ['vswitch', 'del_flow', 'int_br0'], + ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[3][0]'], # vm1 + ['vswitch', 'del_port', 'int_br0', '#STEP[4][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[11][0]'], # vm2 + ['vswitch', 'del_port', 'int_br0', '#STEP[12][0]'], + ['vswitch', 'del_switch', 'int_br0'], + ] + }, + +To run the test: + + .. code-block:: console + + ./vsperf --conf-file user_settings.py --integration ex_replace_vm + +VM with a Linux bridge +^^^^^^^^^^^^^^^^^^^^^^ + +In this example a command-line parameter allows to set up a Linux bridge into +the guest VM. +That's one of the available ways to specify the guest application. +Packets matching the flow will be forwarded to the VM. + +.. code-block:: python + + { + "Name": "ex_pvp_rule_l3da", + "Description": "PVP with flow on L3 Dest Addr", + "Deployment": "clean", + "TestSteps": [ + ['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 vm1 + ['vswitch', 'add_vport', 'int_br0'], # STEP 4 + # Setup Flows + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'dl_type': '0x0800', 'nw_dst': '90.90.90.90', \ + 'actions': ['output:#STEP[3][1]'], 'idle_timeout': '0'}], + # Each pkt from the VM is forwarded to the 2nd dpdk port + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[4][1]', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + # Start VMs + ['vnf1', 'start'], + ['trafficgen', 'send_traffic', {'traffic_type' : 'continuous', \ + 'bidir' : 'False'}], + ['vnf1', 'stop'], + # Clean up + ['vswitch', 'dump_flows', 'int_br0'], # STEP 10 + ['vswitch', 'del_flow', 'int_br0'], # STEP 11 + ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[3][0]'], # vm1 ports + ['vswitch', 'del_port', 'int_br0', '#STEP[4][0]'], + ['vswitch', 'del_switch', 'int_br0'], + ] + }, + +To run the test: + + .. code-block:: console + + ./vsperf --conf-file user_settings.py --test-params + "guest_loopback=linux_bridge" --integration ex_pvp_rule_l3da + +Forward packets based on UDP port +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This examples launches 2 VMs connected in parallel. +Incoming packets will be forwarded to one specific VM depending on the +destination UDP port. + +.. code-block:: python + + { + "Name": "ex_2pvp_rule_l4dp", + "Description": "2 PVP with flows on L4 Dest Port", + "Deployment": "clean", + "Stream Type": "L4", # loop UDP ports + "MultiStream": 2, + "TestSteps": [ + ['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 vm1 + ['vswitch', 'add_vport', 'int_br0'], # STEP 4 + ['vswitch', 'add_vport', 'int_br0'], # STEP 5 vm2 + ['vswitch', 'add_vport', 'int_br0'], # STEP 6 + # Setup Flows to reply ICMPv6 and similar packets, so to + # avoid flooding internal port with their re-transmissions + ['vswitch', 'add_flow', 'int_br0', \ + {'priority': '1', 'dl_src': '00:00:00:00:00:01', \ + 'actions': ['output:#STEP[3][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', \ + {'priority': '1', 'dl_src': '00:00:00:00:00:02', \ + 'actions': ['output:#STEP[4][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', \ + {'priority': '1', 'dl_src': '00:00:00:00:00:03', \ + 'actions': ['output:#STEP[5][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', \ + {'priority': '1', 'dl_src': '00:00:00:00:00:04', \ + 'actions': ['output:#STEP[6][1]'], 'idle_timeout': '0'}], + # Forward UDP packets depending on dest port + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'dl_type': '0x0800', 'nw_proto': '17', 'udp_dst': '0', \ + 'actions': ['output:#STEP[3][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', \ + 'dl_type': '0x0800', 'nw_proto': '17', 'udp_dst': '1', \ + 'actions': ['output:#STEP[5][1]'], 'idle_timeout': '0'}], + # Send VM output to phy port #2 + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[4][1]', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[6][1]', \ + 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}], + # Start VMs + ['vnf1', 'start'], # STEP 16 + ['vnf2', 'start'], # STEP 17 + ['trafficgen', 'send_traffic', {'traffic_type' : 'continuous', \ + 'bidir' : 'False'}], + ['vnf1', 'stop'], + ['vnf2', 'stop'], + ['vswitch', 'dump_flows', 'int_br0'], + # Clean up + ['vswitch', 'del_flow', 'int_br0'], + ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[3][0]'], # vm1 ports + ['vswitch', 'del_port', 'int_br0', '#STEP[4][0]'], + ['vswitch', 'del_port', 'int_br0', '#STEP[5][0]'], # vm2 ports + ['vswitch', 'del_port', 'int_br0', '#STEP[6][0]'], + ['vswitch', 'del_switch', 'int_br0'], + ] + }, + +To run the test: + + .. code-block:: console + + ./vsperf --conf-file user_settings.py --integration ex_2pvp_rule_l4dp + Executing Tunnel encapsulation tests ------------------------------------ diff --git a/src/dpdk/dpdk.py b/src/dpdk/dpdk.py index 30f228f7..477c1de4 100644 --- a/src/dpdk/dpdk.py +++ b/src/dpdk/dpdk.py @@ -14,7 +14,7 @@ """Automation of system configuration for DPDK use. -Parts of this based on ``tools/dpdk_nic_bind.py`` script from Intel(R) +Parts of this based on ``tools/dpdk*bind.py`` script from Intel(R) DPDK. """ @@ -23,14 +23,15 @@ from sys import platform as _platform import os import subprocess import logging +import glob from tools import tasks from conf import settings from tools.module_manager import ModuleManager _LOGGER = logging.getLogger(__name__) -RTE_PCI_TOOL = os.path.join( - settings.getValue('RTE_SDK_USER'), 'tools', 'dpdk_nic_bind.py') +RTE_PCI_TOOL = glob.glob(os.path.join( + settings.getValue('RTE_SDK_USER'), 'tools', 'dpdk*bind.py'))[0] _DPDK_MODULE_MANAGER = ModuleManager() @@ -168,7 +169,7 @@ def _vhost_user_cleanup(): def _bind_nics(): - """Bind NICs using the Intel DPDK ``dpdk_nic_bind.py`` tool. + """Bind NICs using the Intel DPDK ``dpdk*bind.py`` tool. """ try: _driver = 'igb_uio' @@ -189,7 +190,7 @@ def _bind_nics(): _LOGGER.error('Unable to bind NICs %s', str(_NICS_PCI)) def _unbind_nics(): - """Unbind NICs using the Intel DPDK ``dpdk_nic_bind.py`` tool. + """Unbind NICs using the Intel DPDK ``dpdk*bind.py`` tool. """ try: tasks.run_task(['sudo', RTE_PCI_TOOL, '--unbind'] + @@ -199,7 +200,7 @@ def _unbind_nics(): except subprocess.CalledProcessError: _LOGGER.error('Unable to unbind NICs %s', str(_NICS_PCI)) # Rebind NICs to their original drivers - # using the Intel DPDK ``dpdk_nic_bind.py`` tool. + # using the Intel DPDK ``dpdk*bind.py`` tool. for nic in _NICS: try: if nic['driver']: diff --git a/tools/networkcard.py b/tools/networkcard.py index c31be691..8d704fd5 100644 --- a/tools/networkcard.py +++ b/tools/networkcard.py @@ -249,7 +249,7 @@ def reinit_vfs(pf_pci_handle): :param pf_pci_handle: PCI slot identifier of PF with domain part. """ - rte_pci_tool = os.path.join(settings.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py') + rte_pci_tool = glob.glob(os.path.join(settings.getValue('RTE_SDK'), 'tools', 'dpdk*bind.py'))[0] for vf_nic in get_sriov_vfs_list(pf_pci_handle): nic_driver = get_driver(vf_nic) diff --git a/tools/pkt_gen/moongen/moongen.py b/tools/pkt_gen/moongen/moongen.py index 7af83f26..efd8e004 100644 --- a/tools/pkt_gen/moongen/moongen.py +++ b/tools/pkt_gen/moongen/moongen.py @@ -138,8 +138,9 @@ class Moongen(ITrafficGenerator): out_file.write("dstIp = \"" + \ str(traffic['l3']['dstip']) + "\",\n") - out_file.write("vlanId = " + \ - str(traffic['vlan']['id']) + ",\n") + if traffic['vlan']['enabled']: + out_file.write("vlanId = " + \ + str(traffic['vlan']['id']) + ",\n") out_file.write("searchRunTime = " + \ str(duration) + ",\n") @@ -316,31 +317,31 @@ class Moongen(ITrafficGenerator): results = OrderedDict() results[ResultsConstants.THROUGHPUT_RX_FPS] = ( - '{:,.6f}'.format(total_throughput_rx_fps)) + '{:.6f}'.format(total_throughput_rx_fps)) results[ResultsConstants.THROUGHPUT_RX_MBPS] = ( - '{:,.3f}'.format(total_throughput_rx_mbps)) + '{:.3f}'.format(total_throughput_rx_mbps)) results[ResultsConstants.THROUGHPUT_RX_PERCENT] = ( - '{:,.3f}'.format(total_throughput_rx_pct)) + '{:.3f}'.format(total_throughput_rx_pct)) results[ResultsConstants.TX_RATE_FPS] = ( - '{:,.6f}'.format(total_throughput_tx_fps)) + '{:.6f}'.format(total_throughput_tx_fps)) results[ResultsConstants.TX_RATE_MBPS] = ( - '{:,.3f}'.format(total_throughput_tx_mbps)) + '{:.3f}'.format(total_throughput_tx_mbps)) results[ResultsConstants.TX_RATE_PERCENT] = ( - '{:,.3f}'.format(total_throughput_tx_pct)) + '{:.3f}'.format(total_throughput_tx_pct)) results[ResultsConstants.MIN_LATENCY_NS] = ( - '{:,.3f}'.format(total_min_latency_ns)) + '{:.3f}'.format(total_min_latency_ns)) results[ResultsConstants.MAX_LATENCY_NS] = ( - '{:,.3f}'.format(total_max_latency_ns)) + '{:.3f}'.format(total_max_latency_ns)) results[ResultsConstants.AVG_LATENCY_NS] = ( - '{:,.3f}'.format(total_avg_latency_ns)) + '{:.3f}'.format(total_avg_latency_ns)) return results @@ -586,31 +587,31 @@ class Moongen(ITrafficGenerator): results = OrderedDict() results[ResultsConstants.THROUGHPUT_RX_FPS] = ( - '{:,.6f}'.format(total_throughput_rx_fps / trials)) + '{:.6f}'.format(total_throughput_rx_fps / trials)) results[ResultsConstants.THROUGHPUT_RX_MBPS] = ( - '{:,.3f}'.format(total_throughput_rx_mbps / trials)) + '{:.3f}'.format(total_throughput_rx_mbps / trials)) results[ResultsConstants.THROUGHPUT_RX_PERCENT] = ( - '{:,.3f}'.format(total_throughput_rx_pct / trials)) + '{:.3f}'.format(total_throughput_rx_pct / trials)) results[ResultsConstants.TX_RATE_FPS] = ( - '{:,.6f}'.format(total_throughput_tx_fps / trials)) + '{:.6f}'.format(total_throughput_tx_fps / trials)) results[ResultsConstants.TX_RATE_MBPS] = ( - '{:,.3f}'.format(total_throughput_tx_mbps / trials)) + '{:.3f}'.format(total_throughput_tx_mbps / trials)) results[ResultsConstants.TX_RATE_PERCENT] = ( - '{:,.3f}'.format(total_throughput_tx_pct / trials)) + '{:.3f}'.format(total_throughput_tx_pct / trials)) results[ResultsConstants.MIN_LATENCY_NS] = ( - '{:,.3f}'.format(total_min_latency_ns / trials)) + '{:.3f}'.format(total_min_latency_ns / trials)) results[ResultsConstants.MAX_LATENCY_NS] = ( - '{:,.3f}'.format(total_max_latency_ns / trials)) + '{:.3f}'.format(total_max_latency_ns / trials)) results[ResultsConstants.AVG_LATENCY_NS] = ( - '{:,.3f}'.format(total_avg_latency_ns / trials)) + '{:.3f}'.format(total_avg_latency_ns / trials)) return results diff --git a/tools/pkt_gen/xena/xena.py b/tools/pkt_gen/xena/xena.py index 7dd4b90b..67e9984f 100755 --- a/tools/pkt_gen/xena/xena.py +++ b/tools/pkt_gen/xena/xena.py @@ -25,6 +25,7 @@ Xena Traffic Generator Model # python imports import binascii import logging +import os import subprocess import sys from time import sleep @@ -50,6 +51,7 @@ from tools.pkt_gen.xena.XenaDriver import ( XenaManager, ) + class Xena(ITrafficGenerator): """ Xena Traffic generator wrapper class @@ -65,6 +67,19 @@ class Xena(ITrafficGenerator): self._duration = None self.tx_stats = None self.rx_stats = None + self._log_handle = None + + user_home = os.path.expanduser('~') + self._log_path = '{}/Xena/Xena2544-2G/Logs/xena2544.log'.format( + user_home) + + # make the folder and log file if they doesn't exist + if not os.path.exists(self._log_path): + os.makedirs(os.path.dirname(self._log_path)) + + # empty the file contents + open(self._log_path, 'w').close() + @property def traffic_defaults(self): @@ -99,7 +114,7 @@ class Xena(ITrafficGenerator): root[0][1][0][0].get('PortRxBpsL1')) + float( root[0][1][0][1].get('PortRxBpsL1')))/ 1000000 results[ResultsConstants.THROUGHPUT_RX_PERCENT] = ( - 100 - int(root[0][1][0].get('TotalLossRatioPcnt'))) * float( + 100 - float(root[0][1][0].get('TotalLossRatioPcnt'))) * float( root[0][1][0].get('TotalTxRatePcnt'))/100 results[ResultsConstants.TX_RATE_FPS] = root[0][1][0].get( 'TotalTxRateFps') @@ -451,6 +466,44 @@ class Xena(ITrafficGenerator): self.rx_stats = self.xmanager.ports[1].get_rx_stats() sleep(1) + def _start_xena_2544(self): + """ + Start the xena2544 exe. + :return: None + """ + args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c", + "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r", + "./tools/pkt_gen/xena", "-u", + settings.getValue('TRAFFICGEN_XENA_USER')] + # Sometimes Xena2544.exe completes, but mono holds the process without + # releasing it, this can cause a deadlock of the main thread. Use the + # xena log file as a way to detect this. + self._log_handle = open(self._log_path, 'r') + # read the contents of the log before we start so the next read in the + # wait method are only looking at the text from this test instance + self._log_handle.read() + self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout) + + def _wait_xena_2544_complete(self): + """ + Wait for Xena2544.exe completion. + :return: None + """ + data = '' + while True: + try: + self.mono_pipe.wait(60) + self._log_handle.close() + break + except subprocess.TimeoutExpired: + # check the log to see if Xena2544 has completed and mono is + # deadlocked. + data += self._log_handle.read() + if 'TestCompletedSuccessfully' in data: + self._log_handle.close() + self.mono_pipe.terminate() + break + def _stop_api_traffic(self): """ Stop traffic through the socket API @@ -495,13 +548,11 @@ class Xena(ITrafficGenerator): See ITrafficGenerator for description """ self._duration = duration - self._params.clear() self._params['traffic'] = self.traffic_defaults.copy() if traffic: self._params['traffic'] = merge_spec(self._params['traffic'], traffic) - self._start_traffic_api(numpkts) return self._stop_api_traffic() @@ -517,7 +568,6 @@ class Xena(ITrafficGenerator): if traffic: self._params['traffic'] = merge_spec(self._params['traffic'], traffic) - self._start_traffic_api(-1) return self._stop_api_traffic() @@ -533,7 +583,6 @@ class Xena(ITrafficGenerator): if traffic: self._params['traffic'] = merge_spec(self._params['traffic'], traffic) - self._start_traffic_api(-1) def stop_cont_traffic(self): @@ -554,15 +603,10 @@ class Xena(ITrafficGenerator): if traffic: self._params['traffic'] = merge_spec(self._params['traffic'], traffic) - self._setup_json_config(trials, lossrate, '2544_throughput') + self._start_xena_2544() + self._wait_xena_2544_complete() - args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c", - "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r", - "./tools/pkt_gen/xena", "-u", - settings.getValue('TRAFFICGEN_XENA_USER')] - self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout) - self.mono_pipe.communicate() root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot() return Xena._create_throughput_result(root) @@ -578,22 +622,15 @@ class Xena(ITrafficGenerator): if traffic: self._params['traffic'] = merge_spec(self._params['traffic'], traffic) - self._setup_json_config(trials, lossrate, '2544_throughput') - - args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c", - "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r", - "./tools/pkt_gen/xena", "-u", - settings.getValue('TRAFFICGEN_XENA_USER')] - self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout) + self._start_xena_2544() def wait_rfc2544_throughput(self): """Wait for and return results of RFC2544 test. See ITrafficGenerator for description """ - self.mono_pipe.communicate() - sleep(2) + self._wait_xena_2544_complete() root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot() return Xena._create_throughput_result(root) @@ -610,16 +647,9 @@ class Xena(ITrafficGenerator): if traffic: self._params['traffic'] = merge_spec(self._params['traffic'], traffic) - self._setup_json_config(trials, lossrate, '2544_b2b') - - args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c", - "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r", - "./tools/pkt_gen/xena", "-u", - settings.getValue('TRAFFICGEN_XENA_USER')] - self.mono_pipe = subprocess.Popen( - args, stdout=sys.stdout) - self.mono_pipe.communicate() + self._start_xena_2544() + self._wait_xena_2544_complete() root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot() return Xena._create_throughput_result(root) @@ -630,27 +660,18 @@ class Xena(ITrafficGenerator): See ITrafficGenerator for description """ self._duration = duration - self._params.clear() self._params['traffic'] = self.traffic_defaults.copy() if traffic: self._params['traffic'] = merge_spec(self._params['traffic'], traffic) - self._setup_json_config(trials, lossrate, '2544_b2b') - - args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c", - "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r", - "./tools/pkt_gen/xena", "-u", - settings.getValue('TRAFFICGEN_XENA_USER')] - self.mono_pipe = subprocess.Popen( - args, stdout=sys.stdout) + self._start_xena_2544() def wait_rfc2544_back2back(self): """Wait and set results of RFC2544 test. """ - self.mono_pipe.communicate() - sleep(2) + self._wait_xena_2544_complete() root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot() return Xena._create_throughput_result(root) diff --git a/tools/systeminfo.py b/tools/systeminfo.py index 9d8eb5cb..50dc17e0 100644 --- a/tools/systeminfo.py +++ b/tools/systeminfo.py @@ -223,22 +223,45 @@ def get_version(app_name): app_git_tag = get_git_tag(S.getValue('OVS_DIR')) elif app_name.lower() in ['dpdk', 'testpmd']: tmp_ver = ['', '', ''] - found = False + dpdk_16 = False with open(app_version_file['dpdk']) as file_: for line in file_: if not line.strip(): continue + # DPDK version < 16 if line.startswith('#define RTE_VER_MAJOR'): - found = True tmp_ver[0] = line.rstrip('\n').split(' ')[2] - if line.startswith('#define RTE_VER_MINOR'): - found = True - tmp_ver[1] = line.rstrip('\n').split(' ')[2] - if line.startswith('#define RTE_VER_PATCH_LEVEL'): - found = True + # DPDK version < 16 + elif line.startswith('#define RTE_VER_PATCH_LEVEL'): tmp_ver[2] = line.rstrip('\n').split(' ')[2] - - if found: + # DPDK version < 16 + elif line.startswith('#define RTE_VER_PATCH_RELEASE'): + release = line.rstrip('\n').split(' ')[2] + if not '16' in release: + tmp_ver[2] += line.rstrip('\n').split(' ')[2] + # DPDK all versions + elif line.startswith('#define RTE_VER_MINOR'): + if dpdk_16: + tmp_ver[2] = line.rstrip('\n').split(' ')[2] + else: + tmp_ver[1] = line.rstrip('\n').split(' ')[2] + # DPDK all versions + elif line.startswith('#define RTE_VER_SUFFIX'): + tmp_ver[2] += line.rstrip('\n').split('"')[1] + # DPDK version >= 16 + elif line.startswith('#define RTE_VER_YEAR'): + dpdk_16 = True + tmp_ver[0] = line.rstrip('\n').split(' ')[2] + # DPDK version >= 16 + elif line.startswith('#define RTE_VER_MONTH'): + tmp_ver[1] = '{:0>2}'.format(line.rstrip('\n').split(' ')[2]) + # DPDK version >= 16 + elif line.startswith('#define RTE_VER_RELEASE'): + release = line.rstrip('\n').split(' ')[2] + if not '16' in release: + tmp_ver[2] += line.rstrip('\n').split(' ')[2] + + if len(tmp_ver[0]): app_version = '.'.join(tmp_ver) app_git_tag = get_git_tag(S.getValue('RTE_SDK')) elif app_name.lower().startswith('qemu'): diff --git a/vnfs/qemu/qemu.py b/vnfs/qemu/qemu.py index 9382edef..02ada4b5 100644 --- a/vnfs/qemu/qemu.py +++ b/vnfs/qemu/qemu.py @@ -338,16 +338,16 @@ class IVnfQemu(IVnf): self.execute_and_wait('modprobe uio') self.execute_and_wait('insmod %s/kmod/igb_uio.ko' % S.getValue('RTE_TARGET')) - self.execute_and_wait('./tools/dpdk_nic_bind.py --status') + self.execute_and_wait('./tools/dpdk*bind.py --status') self.execute_and_wait( - './tools/dpdk_nic_bind.py -u' ' ' + + './tools/dpdk*bind.py -u' ' ' + S.getValue('GUEST_NET1_PCI_ADDRESS')[self._number] + ' ' + S.getValue('GUEST_NET2_PCI_ADDRESS')[self._number]) self.execute_and_wait( - './tools/dpdk_nic_bind.py -b igb_uio' ' ' + + './tools/dpdk*bind.py -b igb_uio' ' ' + S.getValue('GUEST_NET1_PCI_ADDRESS')[self._number] + ' ' + S.getValue('GUEST_NET2_PCI_ADDRESS')[self._number]) - self.execute_and_wait('./tools/dpdk_nic_bind.py --status') + self.execute_and_wait('./tools/dpdk*bind.py --status') # build and run 'test-pmd' self.execute_and_wait('cd ' + S.getValue('GUEST_OVS_DPDK_DIR') + diff --git a/vnfs/qemu/qemu_pci_passthrough.py b/vnfs/qemu/qemu_pci_passthrough.py index 1b55fdf2..14810f0a 100644 --- a/vnfs/qemu/qemu_pci_passthrough.py +++ b/vnfs/qemu/qemu_pci_passthrough.py @@ -19,6 +19,7 @@ import logging import subprocess import os +import glob from conf import settings as S from vnfs.qemu.qemu import IVnfQemu @@ -26,7 +27,7 @@ from tools import tasks from tools.module_manager import ModuleManager _MODULE_MANAGER = ModuleManager() -_RTE_PCI_TOOL = os.path.join(S.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py') +_RTE_PCI_TOOL = glob.glob(os.path.join(S.getValue('RTE_SDK'), 'tools', 'dpdk*bind.py'))[0] class QemuPciPassthrough(IVnfQemu): """ |