Enable PVVP deployment for DPDK Vhost User and Vhost Cuse 05/2205/3
authorMartin Klozik <martinx.klozik@intel.com>
Fri, 25 Sep 2015 17:00:17 +0000 (18:00 +0100)
committerMaryam Tahhan <maryam.tahhan@intel.com>
Wed, 7 Oct 2015 14:53:13 +0000 (14:53 +0000)
Generic PVVP deployment support has been added. Two
new testcase scenarios for throughput and back2back
tests with PVVP deployment were added. Original
implementation of PVP has been refactored
Following files were affected:
modified:   conf/01_testcases.conf
modified:   conf/02_vswitch.conf
modified:   conf/04_vnf.conf
modified:   core/__init__.py
modified:   core/component_factory.py
modified:   core/loader/loader_servant.py
modified:   core/vnf_controller.py
deleted:    core/vnf_controller_p2p.py
deleted:    core/vnf_controller_pvp.py
modified:   core/vswitch_controller_p2p.py
modified:   core/vswitch_controller_pvp.py
new file:   core/vswitch_controller_pvvp.py
modified:   docs/to-be-reorganized/NEWS.rst
modified:   docs/to-be-reorganized/quickstart.rst
modified:   src/dpdk/dpdk.py
modified:   src/ovs/ofctl.py
modified:   testcases/testcase.py
modified:   tools/tasks.py
new file:   vnfs/qemu/qemu.py
new file:   vnfs/qemu/qemu_dpdk.py
modified:   vnfs/qemu/qemu_dpdk_vhost_cuse.py
modified:   vnfs/qemu/qemu_dpdk_vhost_user.py
modified:   vnfs/vnf/vnf.py
modified:   vswitches/ovs_dpdk_vhost.py

Change-Id: Ib6869a29337a184cb58c57fd96bba1183aba00ab
JIRA: VSPERF-68, VSPERF-69
Signed-off-by: Martin Klozik (martinx.klozik@intel.com)
Reviewed-by: Maryam Tahhan <maryam.tahhan@intel.com>
Reviewed-by: Brian Castelli <brian.castelli@spirent.com>
24 files changed:
conf/01_testcases.conf
conf/02_vswitch.conf
conf/04_vnf.conf
core/__init__.py
core/component_factory.py
core/loader/loader_servant.py
core/vnf_controller.py
core/vnf_controller_p2p.py [deleted file]
core/vnf_controller_pvp.py [deleted file]
core/vswitch_controller_p2p.py
core/vswitch_controller_pvp.py
core/vswitch_controller_pvvp.py [new file with mode: 0644]
docs/to-be-reorganized/NEWS.rst
docs/to-be-reorganized/quickstart.rst
src/dpdk/dpdk.py
src/ovs/ofctl.py
testcases/testcase.py
tools/tasks.py
vnfs/qemu/qemu.py [new file with mode: 0644]
vnfs/qemu/qemu_dpdk.py [new file with mode: 0644]
vnfs/qemu/qemu_dpdk_vhost_cuse.py
vnfs/qemu/qemu_dpdk_vhost_user.py
vnfs/vnf/vnf.py
vswitches/ovs_dpdk_vhost.py

index 815e3a7..39e924b 100755 (executable)
@@ -115,6 +115,22 @@ PERFORMANCE_TESTS = [
         "Description": "LTD.Throughput.RFC2544.BackToBackFrames",
         "biDirectional": "True",
     },
+    {
+        "Name": "pvvp_tput",
+        "Traffic Type": "rfc2544",
+        "Collector": "cpu",
+        "Deployment": "pvvp",
+        "Description": "LTD.Throughput.RFC2544.PacketLossRatio",
+        "biDirectional": "True",
+    },
+    {
+        "Name": "pvvp_back2back",
+        "Traffic Type": "back2back",
+        "Collector": "cpu",
+        "Deployment": "pvvp",
+        "Description": "LTD.Throughput.RFC2544.BackToBackFrames",
+        "biDirectional": "True",
+    },
     {
         "Name": "phy2phy_cpu_load",
         "Traffic Type": "rfc2544",
index 73c42a9..32f4c0e 100644 (file)
@@ -42,6 +42,9 @@ VSWITCHD_VANILLA_ARGS = ['--pidfile']
 VSWITCH_VANILLA_PHY_PORT_NAMES = ['', '']
 VSWITCH_VANILLA_KERNEL_MODULES = ['openvswitch']
 
+# Bridge name to be used by VSWTICH
+VSWITCH_BRIDGE_NAME = 'br0'
+
 # directory where hugepages will be mounted on system init
 HUGEPAGE_DIR = '/dev/hugepages'
 
index 1c3712e..034e410 100644 (file)
 # VNF configuration
 # ############################
 VNF_DIR = 'vnfs/'
-VNF = 'QemuDpdkVhost'
+VNF = 'QemuDpdkVhostUser'
 
 # ############################
 # Guest configuration
 # ############################
 
 # directory which is shared to QEMU guests. Useful for exchanging files
-# between host and guest
-GUEST_SHARE_DIR = '/tmp/qemu_share'
+# between host and guest, VNF specific share will be created
+# For 2 VNFs you may use ['/tmp/qemu0_share', '/tmp/qemu1_share']
+GUEST_SHARE_DIR = ['/tmp/qemu0_share', '/tmp/qemu1_share']
 
 # location of guest disk image
-GUEST_IMAGE = ''
+# For 2 VNFs you may use ['guest1.img', 'guest2.img']
+GUEST_IMAGE = ['', '']
 
 # username for guest image
 GUEST_USERNAME = ''
@@ -57,34 +59,25 @@ LOG_FILE_GUEST_CMDS = 'guest-cmds.log'
 
 QEMU_BIN = 'qemu-system-x86_64'
 
-# Guest shell prompt when inside DPDK dir
-# for example: root@ovdk_guest DPDK]#'
-QEMU_GUEST_DPDK_PROMPT = ''
-
-# Guest shell prompt when inside the
-# test-pmd directory of DPDK
-# for example: 'root@ovdk_guest test-pmd]#'
-QEMU_GUEST_TEST_PMD_PROMPT = ''
-
 OVS_VAR_DIR = '/usr/local/var/run/openvswitch/'
 
-GUEST_NET1_MAC = '00:00:00:00:00:01'
-GUEST_NET2_MAC = '00:00:00:00:00:02'
+# For 2 VNFs you may use ['00:00:00:00:00:01', '00:00:00:00:00:03']
+GUEST_NET1_MAC = ['00:00:00:00:00:01', '00:00:00:00:00:03']
+GUEST_NET2_MAC = ['00:00:00:00:00:02', '00:00:00:00:00:04']
 
-GUEST_NET1_PCI_ADDRESS = '00:04.0'
-GUEST_NET2_PCI_ADDRESS = '00:05.0'
 
-GUEST_MEMORY = '3072'
+# For 2 VNFs you may use ['00:04.0', '00:04.0']
+GUEST_NET1_PCI_ADDRESS = ['00:04.0', '00:04.0']
+GUEST_NET2_PCI_ADDRESS = ['00:05.0', '00:05.0']
+
+GUEST_MEMORY = ['4096', '4096']
 
 # test-pmd requires 2 VM cores
-GUEST_SMP = '2'
+GUEST_SMP = ['2', '2']
 
 # Host cores to use to affinitize the SMP cores of a QEMU instance
 # For 2 VNFs you may use [(4,5), (6, 7)]
-GUEST_CORE_BINDING = [(4, 5)]
-
-# Starte Qemu on cores 3, 4,5 (0x038)
-QEMU_CORE = '38'
+GUEST_CORE_BINDING = [(6, 7), (9, 10)]
 
 GUEST_OVS_DPDK_DIR = '/root/ovs_dpdk'
 OVS_DPDK_SHARE = '/mnt/ovs_dpdk_share'
index 0b41aac..e39ec88 100644 (file)
@@ -16,5 +16,4 @@
 """
 import core.component_factory
 from core.traffic_controller import (ITrafficController)
-from core.vnf_controller import (IVnfController)
 from core.vswitch_controller import (IVswitchController)
index e8bb4de..4da37fb 100644 (file)
@@ -18,8 +18,8 @@
 from core.traffic_controller_rfc2544 import TrafficControllerRFC2544
 from core.vswitch_controller_p2p import VswitchControllerP2P
 from core.vswitch_controller_pvp import VswitchControllerPVP
-from core.vnf_controller_p2p import VnfControllerP2P
-from core.vnf_controller_pvp import VnfControllerPVP
+from core.vswitch_controller_pvvp import VswitchControllerPVVP
+from core.vnf_controller import VnfController
 from tools.load_gen.stress.stress import Stress
 from tools.load_gen.stress_ng.stress_ng import StressNg
 from tools.load_gen.dummy.dummy import DummyLoadGen
@@ -58,16 +58,16 @@ def create_vswitch(deployment_scenario, vswitch_class, bidir=True):
     :param vswitch_class: Reference to vSwitch class to be used.
     :return: IVSwitchController for the deployment_scenario
     """
-    #TODO - full mapping from all deployment_scenarios to
-    #correct controller class
     deployment_scenario = deployment_scenario.lower()
     if deployment_scenario.find("p2p") >= 0:
         return VswitchControllerP2P(vswitch_class)
     elif deployment_scenario.find("pvp") >= 0:
         return VswitchControllerPVP(vswitch_class, bidir)
+    elif deployment_scenario.find("pvvp") >= 0:
+        return VswitchControllerPVVP(vswitch_class, bidir)
 
 def create_vnf(deployment_scenario, vnf_class):
-    """Return a new IVnfController for the deployment_scenario.
+    """Return a new VnfController for the deployment_scenario.
 
     The returned controller is configured with the given VNF class.
 
@@ -75,15 +75,9 @@ def create_vnf(deployment_scenario, vnf_class):
 
     :param deployment_scenario: The deployment scenario name
     :param vswitch_class: Reference to vSwitch class to be used.
-    :return: IVnfController for the deployment_scenario
+    :return: VnfController for the deployment_scenario
     """
-    #TODO - full mapping from all deployment_scenarios to
-    #correct controller class
-    deployment_scenario = deployment_scenario.lower()
-    if deployment_scenario.find("p2p") >= 0:
-        return VnfControllerP2P(None)
-    elif deployment_scenario.find("pvp") >= 0:
-        return VnfControllerPVP(vnf_class)
+    return VnfController(deployment_scenario, vnf_class)
 
 def create_collector(collector_class, result_dir, test_name):
     """Return a new Collector of the given class
index 7966532..3b729c2 100644 (file)
@@ -138,10 +138,12 @@ class LoaderServant(object):
         result = {}
 
         for _, mod in LoaderServant._load_all_modules(path):
-            # find all system metric loggers defined in the module
+            # find all classes derived from given interface, but suppress
+            # interface itself and any abstract class starting with iface name
             gens = dict((k, v) for (k, v) in list(mod.__dict__.items())
                         if type(v) == type and
-                        issubclass(v, interface) and k != interface.__name__)
+                        issubclass(v, interface) and
+                        not k.startswith(interface.__name__))
             if gens:
                 for (genname, gen) in list(gens.items()):
                     result[genname] = gen
index be1c7c4..3d3be04 100644 (file)
 """ VNF Controller interface
 """
 
-class IVnfController(object):
-    """Abstract class which defines a VNF controller
+import logging
 
-    Used to set-up and control a VNF provider for a particular
-    deployment scenario.
+class VnfController(object):
+    """VNF controller class
+
+    Used to set-up and control VNFs for specified scenario
+
+    Attributes:
+        _vnf_class: A class object representing the VNF to be used.
+        _deployment_scenario: A string describing the scenario to set-up in the
+            constructor.
+        _vnfs: A list of vnfs controlled by the controller.
     """
 
+    def __init__(self, deployment_scenario, vnf_class):
+        """Sets up the VNF infrastructure for the PVP deployment scenario.
+
+        :param vnf_class: The VNF class to be used.
+        """
+        self._logger = logging.getLogger(__name__)
+        self._vnf_class = vnf_class
+        self._deployment_scenario = deployment_scenario.upper()
+        if self._deployment_scenario == 'P2P':
+            self._vnfs = []
+        if self._deployment_scenario == 'PVP':
+            self._vnfs = [vnf_class()]
+        elif self._deployment_scenario == 'PVVP':
+            self._vnfs = [vnf_class(), vnf_class()]
+        self._logger.debug('__init__ ' + str(len(self._vnfs)) +
+                           ' VNF[s] with ' + ' '.join(map(str, self._vnfs)))
+
     def get_vnfs(self):
         """Returns a list of vnfs controlled by this controller.
         """
-        raise NotImplementedError(
-            "The VnfController does not implement",
-            "the \"get_vnfs\" function.")
+        self._logger.debug('get_vnfs ' + str(len(self._vnfs)) +
+                           ' VNF[s] with ' + ' '.join(map(str, self._vnfs)))
+        return self._vnfs
 
-    #TODO: Decide on contextmanager or __enter/exit__ strategy <MH 2015-05-01>
     def start(self):
         """Boots all VNFs set-up by __init__.
 
         This is a blocking function.
         """
-        raise NotImplementedError(
-            "The VnfController does not implement",
-            "the \"start\" function.")
+        self._logger.debug('start ' + str(len(self._vnfs)) +
+                           ' VNF[s] with ' + ' '.join(map(str, self._vnfs)))
+        for vnf in self._vnfs:
+            vnf.start()
 
     def stop(self):
         """Stops all VNFs set-up by __init__.
 
         This is a blocking function.
         """
-        raise NotImplementedError(
-            "The VnfController does not implement",
-            "the \"stop\" function.")
+        self._logger.debug('stop ' + str(len(self._vnfs)) +
+                           ' VNF[s] with ' + ' '.join(map(str, self._vnfs)))
+        for vnf in self._vnfs:
+            vnf.stop()
+
+    def __enter__(self):
+        self.start()
+
+    def __exit__(self, type_, value, traceback):
+        self.stop()
diff --git a/core/vnf_controller_p2p.py b/core/vnf_controller_p2p.py
deleted file mode 100644 (file)
index a881d34..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright 2015 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.
-"""VNF Controller for the P2P scenario
-"""
-
-import logging
-
-from core.vnf_controller import IVnfController
-
-class VnfControllerP2P(IVnfController):
-    """VNF controller for the P2P scenario.
-
-    Does nothing as there is no VNF in P2P
-
-    Attributes:
-        _vnf_class: A class object representing the VNF to be used.
-        _deployment_scenario: A string describing the scenario to set-up in the
-            constructor.
-        _vnfs: A list of vnfs controlled by the controller.
-    """
-
-    #TODO: Decide on contextmanager or __enter/exit__ strategy <MH 2015-05-01>
-    def __init__(self, vnf_class):
-        """Sets up the VNF infrastructure for the P2P deployment scenario.
-
-        :param vnf_class: The VNF class to be used, this is mostly ignored.
-        """
-        self._logger = logging.getLogger(__name__)
-        self._vnf_class = vnf_class
-        self._deployment_scenario = "P2P"
-        self._logger.debug('__init__ with ' + str(self._vnf_class))
-
-    def get_vnfs(self):
-        """Returns an empty list of vnfs.
-        """
-        self._logger.debug('get_vnfs with ' + str(self._vnf_class))
-        return []
-
-    def start(self):
-        """Starts nothing.
-        """
-        self._logger.debug('start with ' + str(self._vnf_class))
-
-    def stop(self):
-        """Stops nothing.
-        """
-        self._logger.debug('stop with ' + str(self._vnf_class))
-
-    def __enter__(self):
-        self.start()
-
-    def __exit__(self, type_, value, traceback):
-        self.stop()
diff --git a/core/vnf_controller_pvp.py b/core/vnf_controller_pvp.py
deleted file mode 100644 (file)
index 1878db1..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright 2015 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.
-"""VNF Controller for the PVP scenario
-"""
-
-import logging
-
-from core.vnf_controller import IVnfController
-
-class VnfControllerPVP(IVnfController):
-    """VNF controller for the PVP scenario.
-
-    Used to set-up and control a VNF provider for the PVP scenario.
-
-    Attributes:
-        _vnf_class: A class object representing the VNF to be used.
-        _deployment_scenario: A string describing the scenario to set-up in the
-            constructor.
-        _vnfs: A list of vnfs controlled by the controller.
-    """
-
-    #TODO: Decide on contextmanager or __enter/exit__ strategy <MH 2015-05-01>
-    def __init__(self, vnf_class):
-        """Sets up the VNF infrastructure for the PVP deployment scenario.
-
-        :param vnf_class: The VNF class to be used.
-        """
-        self._logger = logging.getLogger(__name__)
-        self._vnf_class = vnf_class()
-        self._deployment_scenario = "PVP"
-        self._vnfs = [vnf_class(deployment=self._deployment_scenario)]
-        self._logger.debug('__init__ with ' + str(self._vnfs[0]))
-        #TODO call vnf.xxx to carry out the required setup
-
-    def get_vnfs(self):
-        """See IVnfController for description
-        """
-        self._logger.debug('get_vnfs with ' + str(self._vnfs[0]))
-        return self._vnfs
-
-    def start(self):
-        """See IVnfController for description
-        """
-        self._logger.debug('start with ' + str(self._vnfs[0]))
-        for vnf in self._vnfs:
-            vnf.start()
-
-    def stop(self):
-        """See IVnfController for description
-        """
-        self._logger.debug('stop with ' + str(self._vnfs[0]))
-        self._vnfs[0].stop()
-
-    def __enter__(self):
-        self.start()
-
-    def __exit__(self, type_, value, traceback):
-        self.stop()
index a1158d4..c15f7ef 100644 (file)
 import logging
 
 from core.vswitch_controller import IVswitchController
-from vswitches.utils import add_ports_to_flow
+from conf import settings
 
 _FLOW_TEMPLATE = {
     'idle_timeout': '0'
 }
-BRIDGE_NAME = 'br0'
 
 class VswitchControllerP2P(IVswitchController):
     """VSwitch controller for P2P deployment scenario.
@@ -53,41 +52,42 @@ class VswitchControllerP2P(IVswitchController):
         try:
             self._vswitch.start()
 
-            self._vswitch.add_switch(BRIDGE_NAME)
+            bridge = settings.getValue('VSWITCH_BRIDGE_NAME')
+            self._vswitch.add_switch(bridge)
 
-            (_, phy1_number) = self._vswitch.add_phy_port(BRIDGE_NAME)
-            (_, phy2_number) = self._vswitch.add_phy_port(BRIDGE_NAME)
+            (_, _) = self._vswitch.add_phy_port(bridge)
+            (_, _) = self._vswitch.add_phy_port(bridge)
 
-            self._vswitch.del_flow(BRIDGE_NAME)
+            self._vswitch.del_flow(bridge)
 
             # table#0 - flows designed to force 5 & 13 tuple matches go here
             flow = {'table':'0', 'priority':'1', 'actions': ['goto_table:1']}
-            self._vswitch.add_flow(BRIDGE_NAME, flow)
+            self._vswitch.add_flow(bridge, flow)
 
             # table#1 - flows to route packets between ports goes here. The
             # chosen port is communicated to subsequent tables by setting the
             # metadata value to the egress port number
             flow = {'table':'1', 'priority':'1', 'in_port':'1',
                     'actions': ['write_actions(output:2)', 'write_metadata:2',
-                        'goto_table:2']}
-            self._vswitch.add_flow(BRIDGE_NAME, flow)
+                                'goto_table:2']}
+            self._vswitch.add_flow(bridge, flow)
             flow = {'table':'1', 'priority':'1', 'in_port':'2',
                     'actions': ['write_actions(output:1)', 'write_metadata:1',
-                        'goto_table:2']}
-            self._vswitch.add_flow(BRIDGE_NAME, flow)
+                                'goto_table:2']}
+            self._vswitch.add_flow(bridge, flow)
 
             # Frame modification table. Frame modification flow rules are
             # isolated in this table so that they can be turned on or off
             # without affecting the routing or tuple-matching flow rules.
             flow = {'table':'2', 'priority':'1', 'actions': ['goto_table:3']}
-            self._vswitch.add_flow(BRIDGE_NAME, flow)
+            self._vswitch.add_flow(bridge, flow)
 
             # Egress table
             # (TODO) Billy O'Mahony - the drop action here actually required in
             # order to egress the packet. This is the subject of a thread on
             # ovs-discuss 2015-06-30.
             flow = {'table':'3', 'priority':'1', 'actions': ['drop']}
-            self._vswitch.add_flow(BRIDGE_NAME, flow)
+            self._vswitch.add_flow(bridge, flow)
         except:
             self._vswitch.stop()
             raise
@@ -113,4 +113,4 @@ class VswitchControllerP2P(IVswitchController):
         """See IVswitchController for description
         """
         self._logger.debug('get_ports_info  using ' + str(self._vswitch_class))
-        return self._vswitch.get_ports(BRIDGE_NAME)
+        return self._vswitch.get_ports(settings.getValue('VSWITCH_BRIDGE_NAME'))
index 8c409dc..80c0fdb 100644 (file)
@@ -19,11 +19,11 @@ import logging
 
 from core.vswitch_controller import IVswitchController
 from vswitches.utils import add_ports_to_flow
+from conf import settings
 
 _FLOW_TEMPLATE = {
     'idle_timeout': '0'
 }
-BRIDGE_NAME = 'br0'
 
 class VswitchControllerPVP(IVswitchController):
     """VSwitch controller for PVP deployment scenario.
@@ -54,28 +54,29 @@ class VswitchControllerPVP(IVswitchController):
         try:
             self._vswitch.start()
 
-            self._vswitch.add_switch(BRIDGE_NAME)
+            bridge = settings.getValue('VSWITCH_BRIDGE_NAME')
+            self._vswitch.add_switch(bridge)
 
-            (_, phy1_number) = self._vswitch.add_phy_port(BRIDGE_NAME)
-            (_, phy2_number) = self._vswitch.add_phy_port(BRIDGE_NAME)
-            (_, vport1_number) = self._vswitch.add_vport(BRIDGE_NAME)
-            (_, vport2_number) = self._vswitch.add_vport(BRIDGE_NAME)
+            (_, phy1_number) = self._vswitch.add_phy_port(bridge)
+            (_, phy2_number) = self._vswitch.add_phy_port(bridge)
+            (_, vport1_number) = self._vswitch.add_vport(bridge)
+            (_, vport2_number) = self._vswitch.add_vport(bridge)
 
-            self._vswitch.del_flow(BRIDGE_NAME)
+            self._vswitch.del_flow(bridge)
             flow1 = add_ports_to_flow(_FLOW_TEMPLATE, phy1_number,
                                       vport1_number)
             flow2 = add_ports_to_flow(_FLOW_TEMPLATE, vport2_number,
                                       phy2_number)
-            self._vswitch.add_flow(BRIDGE_NAME, flow1)
-            self._vswitch.add_flow(BRIDGE_NAME, flow2)
+            self._vswitch.add_flow(bridge, flow1)
+            self._vswitch.add_flow(bridge, flow2)
 
             if self._bidir:
                 flow3 = add_ports_to_flow(_FLOW_TEMPLATE, phy2_number,
                                           vport2_number)
                 flow4 = add_ports_to_flow(_FLOW_TEMPLATE, vport1_number,
                                           phy1_number)
-                self._vswitch.add_flow(BRIDGE_NAME, flow3)
-                self._vswitch.add_flow(BRIDGE_NAME, flow4)
+                self._vswitch.add_flow(bridge, flow3)
+                self._vswitch.add_flow(bridge, flow4)
 
         except:
             self._vswitch.stop()
@@ -102,4 +103,4 @@ class VswitchControllerPVP(IVswitchController):
         """See IVswitchController for description
         """
         self._logger.debug('get_ports_info  using ' + str(self._vswitch_class))
-        return self._vswitch.get_ports(BRIDGE_NAME)
+        return self._vswitch.get_ports(settings.getValue('VSWITCH_BRIDGE_NAME'))
diff --git a/core/vswitch_controller_pvvp.py b/core/vswitch_controller_pvvp.py
new file mode 100644 (file)
index 0000000..b445f9b
--- /dev/null
@@ -0,0 +1,114 @@
+# Copyright 2015 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.
+
+"""VSwitch controller for Physical to VM to Physical deployment
+"""
+
+import logging
+
+from core.vswitch_controller import IVswitchController
+from vswitches.utils import add_ports_to_flow
+from conf import settings
+
+_FLOW_TEMPLATE = {
+    'idle_timeout': '0'
+}
+
+class VswitchControllerPVVP(IVswitchController):
+    """VSwitch controller for PVVP deployment scenario.
+
+    Attributes:
+        _vswitch_class: The vSwitch class to be used.
+        _vswitch: The vSwitch object controlled by this controller
+        _deployment_scenario: A string describing the scenario to set-up in the
+            constructor.
+    """
+    def __init__(self, vswitch_class, bidir=False):
+        """Initializes up the prerequisites for the PVVP deployment scenario.
+
+        :vswitch_class: the vSwitch class to be used.
+        """
+        self._logger = logging.getLogger(__name__)
+        self._vswitch_class = vswitch_class
+        self._vswitch = vswitch_class()
+        self._deployment_scenario = "PVVP"
+        self._bidir = bidir
+        self._logger.debug('Creation using ' + str(self._vswitch_class))
+
+    def setup(self):
+        """ Sets up the switch for PVVP
+        """
+        self._logger.debug('Setup using ' + str(self._vswitch_class))
+
+        try:
+            self._vswitch.start()
+
+            bridge = settings.getValue('VSWITCH_BRIDGE_NAME')
+            self._vswitch.add_switch(bridge)
+
+            (_, phy1_number) = self._vswitch.add_phy_port(bridge)
+            (_, phy2_number) = self._vswitch.add_phy_port(bridge)
+            (_, vport1_number) = self._vswitch.add_vport(bridge)
+            (_, vport2_number) = self._vswitch.add_vport(bridge)
+            (_, vport3_number) = self._vswitch.add_vport(bridge)
+            (_, vport4_number) = self._vswitch.add_vport(bridge)
+
+            self._vswitch.del_flow(bridge)
+            flow1 = add_ports_to_flow(_FLOW_TEMPLATE, phy1_number,
+                                      vport1_number)
+            flow2 = add_ports_to_flow(_FLOW_TEMPLATE, vport2_number,
+                                      vport3_number)
+            flow3 = add_ports_to_flow(_FLOW_TEMPLATE, vport4_number,
+                                      phy2_number)
+            self._vswitch.add_flow(bridge, flow1)
+            self._vswitch.add_flow(bridge, flow2)
+            self._vswitch.add_flow(bridge, flow3)
+
+            if self._bidir:
+                flow4 = add_ports_to_flow(_FLOW_TEMPLATE, phy2_number,
+                                          vport4_number)
+                flow5 = add_ports_to_flow(_FLOW_TEMPLATE, vport3_number,
+                                          vport2_number)
+                flow6 = add_ports_to_flow(_FLOW_TEMPLATE, vport1_number,
+                                          phy1_number)
+                self._vswitch.add_flow(bridge, flow4)
+                self._vswitch.add_flow(bridge, flow5)
+                self._vswitch.add_flow(bridge, flow6)
+
+        except:
+            self._vswitch.stop()
+            raise
+
+    def stop(self):
+        """Tears down the switch created in setup().
+        """
+        self._logger.debug('Stop using ' + str(self._vswitch_class))
+        self._vswitch.stop()
+
+    def __enter__(self):
+        self.setup()
+
+    def __exit__(self, type_, value, traceback):
+        self.stop()
+
+    def get_vswitch(self):
+        """See IVswitchController for description
+        """
+        return self._vswitch
+
+    def get_ports_info(self):
+        """See IVswitchController for description
+        """
+        self._logger.debug('get_ports_info  using ' + str(self._vswitch_class))
+        return self._vswitch.get_ports(settings.getValue('VSWITCH_BRIDGE_NAME'))
index 93be5da..69df747 100644 (file)
@@ -3,6 +3,8 @@ September 2015
 New
 ---
 - Implementation of system statistics based upon pidstat command line tool.
+- Support of PVVP deployment scenario using bhost-cuse and vhost user access
+  methods
 
 August 2015
 ===========
index 8dead99..485bf1a 100644 (file)
@@ -63,8 +63,8 @@ to specify path to the kernel sources by WITH\_LINUX parameter:
      cd src
      make WITH_LINUX=/lib/modules/`uname -r`/build
 
-To build DPDK and OVS for PVP testing with vhost_user as the guest access
-method, use:
+To build DPDK and OVS for PVP and PVVP testing with vhost_user as the guest
+access method, use:
 
   .. code-block:: console
 
@@ -155,8 +155,8 @@ For all available options, check out the help dialog:
 
     ./vsperf --help
 
-Executing PVP tests
--------------------
+Executing PVP and PVVP tests
+----------------------------
 To run tests using vhost-user as guest access method:
 
 1. Set VHOST_METHOD and VNF of your settings file to:
@@ -212,8 +212,8 @@ OVS with DPDK and QEMU
 ~~~~~~~~~~~~~~~~~~~~~~~
 If you encounter the following error: "before (last 100 chars):
 '-path=/dev/hugepages,share=on: unable to map backing store for
-hugepages: Cannot allocate memory\r\n\r\n" with the PVP deployment
-scenario, check the amount of hugepages on your system:
+hugepages: Cannot allocate memory\r\n\r\n" with the PVP or PVVP
+deployment scenario, check the amount of hugepages on your system:
 
 .. code:: bash
 
index 093670c..0633c7a 100644 (file)
@@ -302,7 +302,7 @@ def _unbind_nics_get_driver():
     for line in _output.decode(_my_encoding).split('\n'):
         for nic in settings.getValue('WHITELIST_NICS'):
             if nic in line:
-                _driver_list.append((line.split("unused=",1)[1]))
+                _driver_list.append((line.split("unused=", 1)[1]))
     return _driver_list
 
 def _unbind_nics():
@@ -325,10 +325,10 @@ def _unbind_nics():
         try:
             if nic_drivers[i] != '':
                 tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind',
-                            nic_drivers[i], nic],
-                           _LOGGER, 'Binding NIC %s...' %
-                           nic,
-                           True)
+                                nic_drivers[i], nic],
+                               _LOGGER, 'Binding NIC %s...' %
+                               nic,
+                               True)
         except subprocess.CalledProcessError:
             _LOGGER.error('Unable to bind NICs %s to drivers %s',
                           str(settings.getValue('WHITELIST_NICS')),
@@ -336,23 +336,23 @@ def _unbind_nics():
 
 
 def _copy_dpdk_for_guest():
-    """Copy dpdk code to GUEST_SHARE_DIR for use by guests.
+    """Copy dpdk code to GUEST_SHARE_DIR[s] for use by guests.
     """
-    guest_share_dir = os.path.join(
-        settings.getValue('GUEST_SHARE_DIR'), 'DPDK')
+    for guest_dir in settings.getValue('GUEST_SHARE_DIR'):
+        guest_share_dir = os.path.join(guest_dir, 'DPDK')
 
-    if not os.path.exists(guest_share_dir):
-        os.makedirs(guest_share_dir)
+        if not os.path.exists(guest_share_dir):
+            os.makedirs(guest_share_dir)
 
-    try:
-        tasks.run_task(['rsync', '-a', '-r', '-l', r'--exclude="\.git"',
-                        os.path.join(settings.getValue('RTE_SDK'), ''),
-                        guest_share_dir],
-                       _LOGGER,
-                       'Copying DPDK to shared directory...',
-                       True)
-    except subprocess.CalledProcessError:
-        _LOGGER.error('Unable to copy DPDK to shared directory')
+        try:
+            tasks.run_task(['rsync', '-a', '-r', '-l', r'--exclude="\.git"',
+                            os.path.join(settings.getValue('RTE_SDK'), ''),
+                            guest_share_dir],
+                           _LOGGER,
+                           'Copying DPDK to shared directory...',
+                           True)
+        except subprocess.CalledProcessError:
+            _LOGGER.error('Unable to copy DPDK to shared directory')
 
 
 #
index 1c5e651..7cbdfe2 100644 (file)
@@ -34,6 +34,8 @@ _OVS_OFCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities',
 
 _OVS_VAR_DIR = '/usr/local/var/run/openvswitch/'
 
+_OVS_BRIDGE_NAME = settings.getValue('VSWITCH_BRIDGE_NAME')
+
 class OFBase(object):
     """Add/remove/show datapaths using ``ovs-ofctl``.
     """
@@ -63,7 +65,7 @@ class OFBase(object):
 
     # datapath management
 
-    def add_br(self, br_name='br0'):
+    def add_br(self, br_name=_OVS_BRIDGE_NAME):
         """Add datapath.
 
         :param br_name: Name of bridge
@@ -75,7 +77,7 @@ class OFBase(object):
 
         return OFBridge(br_name, self.timeout)
 
-    def del_br(self, br_name='br0'):
+    def del_br(self, br_name=_OVS_BRIDGE_NAME):
         """Delete datapath.
 
         :param br_name: Name of bridge
@@ -89,7 +91,7 @@ class OFBase(object):
 class OFBridge(OFBase):
     """Control a bridge instance using ``ovs-vsctl`` and ``ovs-ofctl``.
     """
-    def __init__(self, br_name='br0', timeout=10):
+    def __init__(self, br_name=_OVS_BRIDGE_NAME, timeout=10):
         """Initialise bridge.
 
         :param br_name: Bridge name
@@ -126,7 +128,8 @@ class OFBridge(OFBase):
 
         :return: None
         """
-        cmd = ['sudo', _OVS_OFCTL_BIN, '-O', 'OpenFlow13', '--timeout', str(self.timeout)] + args
+        cmd = ['sudo', _OVS_OFCTL_BIN, '-O', 'OpenFlow13', '--timeout',
+               str(self.timeout)] + args
         return tasks.run_task(
             cmd, self.logger, 'Running ovs-ofctl...', check_error)
 
index 6d37ce5..e15572d 100644 (file)
@@ -23,6 +23,7 @@ from core.results.results_constants import ResultsConstants
 import core.component_factory as component_factory
 from core.loader import Loader
 from tools.report import report
+from conf import settings
 
 class TestCase(object):
     """TestCase base class
@@ -96,62 +97,67 @@ class TestCase(object):
                 # physical ports are ports 1 & 2. The actual numbers
                 # need to be retrived from the vSwitch and the metadata value
                 # updated accordingly.
+                bridge = settings.getValue('VSWITCH_BRIDGE_NAME')
                 if self._frame_mod == "vlan":
                     # 0x8100 => VLAN ethertype
                     self._logger.debug(" ****   VLAN   ***** ")
                     flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                             'actions': ['push_vlan:0x8100', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                    vswitch.add_flow(bridge, flow)
                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                             'actions': ['push_vlan:0x8100', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                    vswitch.add_flow(bridge, flow)
                 elif self._frame_mod == "mpls":
                     # 0x8847 => MPLS unicast ethertype
                     self._logger.debug(" ****   MPLS  ***** ")
                     flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                             'actions': ['push_mpls:0x8847', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                    vswitch.add_flow(bridge, flow)
                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                             'actions': ['push_mpls:0x8847', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                    vswitch.add_flow(bridge, flow)
                 elif self._frame_mod == "mac":
                     flow = {'table':'2', 'priority':'1000', 'metadata':'2',
-                            'actions': ['mod_dl_src:22:22:22:22:22:22', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                            'actions': ['mod_dl_src:22:22:22:22:22:22',
+                                        'goto_table:3']}
+                    vswitch.add_flow(bridge, flow)
                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
-                            'actions': ['mod_dl_src:11:11:11:11:11:11', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                            'actions': ['mod_dl_src:11:11:11:11:11:11',
+                                        'goto_table:3']}
+                    vswitch.add_flow(bridge, flow)
                 elif self._frame_mod == "dscp":
                     # DSCP 184d == 0x4E<<2 => 'Expedited Forwarding'
                     flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                             'dl_type':'0x0800',
                             'actions': ['mod_nw_tos:184', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                    vswitch.add_flow(bridge, flow)
                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                             'dl_type':'0x0800',
                             'actions': ['mod_nw_tos:184', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                    vswitch.add_flow(bridge, flow)
                 elif self._frame_mod == "ttl":
                     # 251 and 241 are the highest prime numbers < 255
                     flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                             'dl_type':'0x0800',
                             'actions': ['mod_nw_ttl:251', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                    vswitch.add_flow(bridge, flow)
                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                             'dl_type':'0x0800',
                             'actions': ['mod_nw_ttl:241', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                    vswitch.add_flow(bridge, flow)
                 elif self._frame_mod == "ip_addr":
                     flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                             'dl_type':'0x0800',
                             'actions': ['mod_nw_src:10.10.10.10',
-                            'mod_nw_dst:20.20.20.20', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                                        'mod_nw_dst:20.20.20.20',
+                                        'goto_table:3']}
+                    vswitch.add_flow(bridge, flow)
                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                             'dl_type':'0x0800',
                             'actions': ['mod_nw_src:20.20.20.20',
-                            'mod_nw_dst:10.10.10.10', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                                        'mod_nw_dst:10.10.10.10',
+                                        'goto_table:3']}
+                    vswitch.add_flow(bridge, flow)
                 elif self._frame_mod == "ip_port":
                     # TODO BOM 15-08-27 The traffic generated is assumed
                     # to be UDP (nw_proto 17d) which is the default case but
@@ -159,13 +165,13 @@ class TestCase(object):
                     flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                             'dl_type':'0x0800', 'nw_proto':'17',
                             'actions': ['mod_tp_src:44444',
-                            'mod_tp_dst:44444', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                                        'mod_tp_dst:44444', 'goto_table:3']}
+                    vswitch.add_flow(bridge, flow)
                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                             'dl_type':'0x0800', 'nw_proto':'17',
                             'actions': ['mod_tp_src:44444',
-                            'mod_tp_dst:44444', 'goto_table:3']}
-                    vswitch.add_flow('br0', flow)
+                                        'mod_tp_dst:44444', 'goto_table:3']}
+                    vswitch.add_flow(bridge, flow)
                 else:
                     pass
 
index 555a592..33a5931 100644 (file)
@@ -241,14 +241,17 @@ class Process(object):
             self.kill()
             raise exc
 
-    def kill(self):
+    def kill(self, signal='-15', sleep=2):
         """Kill process instance if it is alive.
+
+        :param signal: signal to be sent to the process
+        :param sleep: delay in seconds after signal is sent
         """
         if self._child and self._child.isalive():
-            run_task(['sudo', 'kill', '-15', str(self._child.pid)],
+            run_task(['sudo', 'kill', signal, str(self._child.pid)],
                      self._logger)
             self._logger.debug('Wait for process to terminate')
-            time.sleep(2)
+            time.sleep(sleep)
 
             if self.is_relinquished():
                 self._relinquish_thread.join()
diff --git a/vnfs/qemu/qemu.py b/vnfs/qemu/qemu.py
new file mode 100644 (file)
index 0000000..338ca77
--- /dev/null
@@ -0,0 +1,203 @@
+# Copyright 2015 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.
+
+"""Automation of QEMU hypervisor for launching vhost-cuse enabled guests.
+"""
+
+import os
+import logging
+import locale
+import re
+import subprocess
+
+from conf import settings as S
+from vnfs.vnf.vnf import IVnf
+
+class IVnfQemu(IVnf):
+    """
+    Abstract class for controling an instance of QEMU
+    """
+    _cmd = None
+    _expect = S.getValue('GUEST_PROMPT_LOGIN')
+    _proc_name = 'qemu'
+
+    class GuestCommandFilter(logging.Filter):
+        """
+        Filter out strings beginning with 'guestcmd :'.
+        """
+        def filter(self, record):
+            return record.getMessage().startswith(self.prefix)
+
+    def __init__(self):
+        """
+        :param timeout: Time to wait for login prompt. If set to
+            0 do not wait.
+        :param number: Number of QEMU instance, used when multiple QEMU
+            instances are started at once.
+        :param args: Arguments to pass to QEMU.
+
+        :returns: None
+        """
+        super(IVnfQemu, self).__init__()
+        self._logger = logging.getLogger(__name__)
+        self._logfile = os.path.join(
+            S.getValue('LOG_DIR'),
+            S.getValue('LOG_FILE_QEMU')) + str(self._number)
+        self._timeout = 120
+        self._monitor = '%s/vm%dmonitor' % ('/tmp', self._number)
+
+        name = 'Client%d' % self._number
+        vnc = ':%d' % self._number
+        # don't use taskset to affinize main qemu process; It causes hangup
+        # of 2nd VM in case of DPDK. It also slows down VM responsivnes.
+        self._cmd = ['sudo', '-E', S.getValue('QEMU_BIN'),
+                     '-m', S.getValue('GUEST_MEMORY')[self._number],
+                     '-smp', str(S.getValue('GUEST_SMP')[self._number]),
+                     '-cpu', 'host',
+                     '-drive', 'if=scsi,file=' +
+                     S.getValue('GUEST_IMAGE')[self._number],
+                     '-drive',
+                     'if=scsi,file=fat:rw:%s,snapshot=off' %
+                     S.getValue('GUEST_SHARE_DIR')[self._number],
+                     '-boot', 'c', '--enable-kvm',
+                     '-monitor', 'unix:%s,server,nowait' % self._monitor,
+                     '-object',
+                     'memory-backend-file,id=mem,size=' +
+                     str(S.getValue('GUEST_MEMORY')[self._number]) + 'M,' +
+                     'mem-path=' + S.getValue('HUGEPAGE_DIR') + ',share=on',
+                     '-numa', 'node,memdev=mem -mem-prealloc',
+                     '-nographic', '-vnc', str(vnc), '-name', name,
+                     '-snapshot', '-net none', '-no-reboot',
+                    ]
+        self._configure_logging()
+
+    def _configure_logging(self):
+        """
+        Configure logging.
+        """
+        self.GuestCommandFilter.prefix = self._log_prefix
+
+        logger = logging.getLogger()
+        cmd_logger = logging.FileHandler(
+            filename=os.path.join(S.getValue('LOG_DIR'),
+                                  S.getValue('LOG_FILE_GUEST_CMDS')) +
+            str(self._number))
+        cmd_logger.setLevel(logging.DEBUG)
+        cmd_logger.addFilter(self.GuestCommandFilter())
+        logger.addHandler(cmd_logger)
+
+    # startup/Shutdown
+
+    def start(self):
+        """
+        Start QEMU instance, login and prepare for commands.
+        """
+        super(IVnfQemu, self).start()
+        self._affinitize()
+
+        if self._timeout:
+            self._login()
+            self._config_guest_loopback()
+
+    # helper functions
+
+    def _login(self, timeout=120):
+        """
+        Login to QEMU instance.
+
+        This can be used immediately after booting the machine, provided a
+        sufficiently long ``timeout`` is given.
+
+        :param timeout: Timeout to wait for login to complete.
+
+        :returns: None
+        """
+        # if no timeout was set, we likely started QEMU without waiting for it
+        # to boot. This being the case, we best check that it has finished
+        # first.
+        if not self._timeout:
+            self._expect_process(timeout=timeout)
+
+        self._child.sendline(S.getValue('GUEST_USERNAME'))
+        self._child.expect(S.getValue('GUEST_PROMPT_PASSWORD'), timeout=5)
+        self._child.sendline(S.getValue('GUEST_PASSWORD'))
+
+        self._expect_process(S.getValue('GUEST_PROMPT'), timeout=5)
+
+    def send_and_pass(self, cmd, timeout=30):
+        """
+        Send ``cmd`` and wait ``timeout`` seconds for it to pass.
+
+        :param cmd: Command to send to guest.
+        :param timeout: Time to wait for prompt before checking return code.
+
+        :returns: None
+        """
+        self.execute(cmd)
+        self.wait(S.getValue('GUEST_PROMPT'), timeout=timeout)
+        self.execute('echo $?')
+        self._child.expect('^0$', timeout=1)  # expect a 0
+        self.wait(S.getValue('GUEST_PROMPT'), timeout=timeout)
+
+    def _affinitize(self):
+        """
+        Affinitize the SMP cores of a QEMU instance.
+
+        This is a bit of a hack. The 'socat' utility is used to
+        interact with the QEMU HMP. This is necessary due to the lack
+        of QMP in older versions of QEMU, like v1.6.2. In future
+        releases, this should be replaced with calls to libvirt or
+        another Python-QEMU wrapper library.
+
+        :returns: None
+        """
+        thread_id = (r'.* CPU #%d: .* thread_id=(\d+)')
+
+        self._logger.info('Affinitizing guest...')
+
+        cur_locale = locale.getlocale()[1]
+        proc = subprocess.Popen(
+            ('echo', 'info cpus'), stdout=subprocess.PIPE)
+        output = subprocess.check_output(
+            ('sudo', 'socat', '-', 'UNIX-CONNECT:%s' % self._monitor),
+            stdin=proc.stdout)
+        proc.wait()
+
+        for cpu in range(0, int(S.getValue('GUEST_SMP')[self._number])):
+            match = None
+            for line in output.decode(cur_locale).split('\n'):
+                match = re.search(thread_id % cpu, line)
+                if match:
+                    self._affinitize_pid(
+                        S.getValue('GUEST_CORE_BINDING')[self._number][cpu],
+                        match.group(1))
+                    break
+
+            if not match:
+                self._logger.error('Failed to affinitize guest core #%d. Could'
+                                   ' not parse tid.', cpu)
+
+    def _config_guest_loopback(self):
+        """
+        Configure VM to run VNF (e.g. port forwarding application)
+        """
+        pass
+
+    def wait(self, prompt=S.getValue('GUEST_PROMPT'), timeout=30):
+        super(IVnfQemu, self).wait(prompt=prompt, timeout=timeout)
+
+    def execute_and_wait(self, cmd, timeout=30,
+                         prompt=S.getValue('GUEST_PROMPT')):
+        super(IVnfQemu, self).execute_and_wait(cmd, timeout=timeout,
+                                               prompt=prompt)
diff --git a/vnfs/qemu/qemu_dpdk.py b/vnfs/qemu/qemu_dpdk.py
new file mode 100644 (file)
index 0000000..0b8f90a
--- /dev/null
@@ -0,0 +1,115 @@
+# Copyright 2015 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.
+
+"""Automation of QEMU hypervisor for launching vhost-cuse enabled guests.
+"""
+
+from vnfs.qemu.qemu import IVnfQemu
+from conf import settings as S
+
+class IVnfQemuDpdk(IVnfQemu):
+    """
+    An abstract class for controling an instance of QEMU with DPDK vHost support
+    """
+
+    def __init__(self):
+        """
+        :param timeout: Time to wait for login prompt. If set to
+            0 do not wait.
+        :param number: Number of QEMU instance, used when multiple QEMU
+            instances are started at once.
+        :param args: Arguments to pass to QEMU.
+
+        :returns: None
+        """
+        super(IVnfQemuDpdk, self).__init__()
+        self._cmd += []
+
+    def _modify_dpdk_makefile(self):
+        """
+        Modifies DPDK makefile in Guest before compilation
+        """
+        pass
+
+    def _config_guest_loopback(self):
+        """
+        Configure VM to run testpmd
+
+        Configure performs the following:
+        * Mount hugepages
+        * mount shared directory for copying DPDK
+        * Disable firewall
+        * Compile DPDK
+        * DPDK NIC bind
+        * Run testpmd
+        """
+
+        # Guest images _should_ have 1024 hugepages by default,
+        # but just in case:'''
+        self.execute_and_wait('sysctl vm.nr_hugepages=1024')
+
+        # Mount hugepages
+        self.execute_and_wait('mkdir -p /dev/hugepages')
+        self.execute_and_wait(
+            'mount -t hugetlbfs hugetlbfs /dev/hugepages')
+
+        # mount shared directory
+        self.execute_and_wait('umount ' + S.getValue('OVS_DPDK_SHARE'))
+        self.execute_and_wait('rm -rf ' + S.getValue('GUEST_OVS_DPDK_DIR'))
+        self.execute_and_wait('mkdir -p ' + S.getValue('OVS_DPDK_SHARE'))
+        self.execute_and_wait('mount -o iocharset=utf8 /dev/sdb1 ' +
+                              S.getValue('OVS_DPDK_SHARE'))
+        self.execute_and_wait('mkdir -p ' + S.getValue('GUEST_OVS_DPDK_DIR'))
+        self.execute_and_wait('cp -a ' + S.getValue('OVS_DPDK_SHARE') + '/* ' +
+                              S.getValue('GUEST_OVS_DPDK_DIR'))
+        # Get VM info
+        self.execute_and_wait('cat /etc/default/grub')
+
+        # Disable services (F16)
+        self.execute_and_wait('systemctl status iptables.service')
+        self.execute_and_wait('systemctl stop iptables.service')
+
+        # build and configure system for dpdk
+        self.execute_and_wait('cd ' + S.getValue('GUEST_OVS_DPDK_DIR') +
+                              '/DPDK')
+        self.execute_and_wait('export CC=gcc')
+        self.execute_and_wait('export RTE_SDK=' +
+                              S.getValue('GUEST_OVS_DPDK_DIR') + '/DPDK')
+        self.execute_and_wait('export RTE_TARGET=%s' % S.getValue('RTE_TARGET'))
+
+        # modify makefile if needed
+        self._modify_dpdk_makefile()
+
+        self.execute_and_wait('make RTE_OUTPUT=$RTE_SDK/$RTE_TARGET -C '
+                              '$RTE_SDK/lib/librte_eal/linuxapp/igb_uio')
+        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_nic_bind.py -b igb_uio' ' ' +
+            S.getValue('GUEST_NET1_PCI_ADDRESS')[self._number] + ' ' +
+            S.getValue('GUEST_NET2_PCI_ADDRESS')[self._number])
+
+        # build and run 'test-pmd'
+        self.execute_and_wait('cd ' + S.getValue('GUEST_OVS_DPDK_DIR') +
+                              '/DPDK/app/test-pmd')
+        self.execute_and_wait('make clean')
+        self.execute_and_wait('make')
+        self.execute_and_wait('./testpmd -c 0x3 -n 4 --socket-mem 512 --'
+                              ' --burst=64 -i --txqflags=0xf00 ' +
+                              '--disable-hw-vlan', 60, "Done")
+        self.execute('set fwd mac_retry', 1)
+        self.execute_and_wait('start', 20,
+                              'TX RS bit threshold=0 - TXQ flags=0xf00')
index 43b732f..e535181 100644 (file)
 """Automation of QEMU hypervisor for launching vhost-cuse enabled guests.
 """
 
-import os
-import time
 import logging
-import locale
-import re
-import subprocess
 
-from tools import tasks
-from conf import settings
-from vnfs.vnf.vnf import IVnf
+from conf import settings as S
+from vnfs.qemu.qemu_dpdk import IVnfQemuDpdk
 
-_QEMU_BIN = settings.getValue('QEMU_BIN')
-_RTE_TARGET = settings.getValue('RTE_TARGET')
-
-_GUEST_MEMORY = settings.getValue('GUEST_MEMORY')
-_GUEST_SMP = settings.getValue('GUEST_SMP')
-_GUEST_CORE_BINDING = settings.getValue('GUEST_CORE_BINDING')
-_QEMU_CORE = settings.getValue('QEMU_CORE')
-
-_GUEST_IMAGE = settings.getValue('GUEST_IMAGE')
-_GUEST_SHARE_DIR = settings.getValue('GUEST_SHARE_DIR')
-
-_GUEST_USERNAME = settings.getValue('GUEST_USERNAME')
-_GUEST_PASSWORD = settings.getValue('GUEST_PASSWORD')
-
-_GUEST_PROMPT_LOGIN = settings.getValue('GUEST_PROMPT_LOGIN')
-_GUEST_PROMPT_PASSWORD = settings.getValue('GUEST_PROMPT_PASSWORD')
-_GUEST_PROMPT = settings.getValue('GUEST_PROMPT')
-
-_QEMU_GUEST_DPDK_PROMPT = settings.getValue('QEMU_GUEST_DPDK_PROMPT')
-_QEMU_GUEST_TEST_PMD_PROMPT = settings.getValue('QEMU_GUEST_TEST_PMD_PROMPT')
-_HUGEPAGE_DIR = settings.getValue('HUGEPAGE_DIR')
-
-_GUEST_OVS_DPDK_DIR = settings.getValue('GUEST_OVS_DPDK_DIR')
-_OVS_DPDK_SHARE = settings.getValue('OVS_DPDK_SHARE')
-
-_LOG_FILE_QEMU = os.path.join(
-    settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_QEMU'))
-_LOG_FILE_GUEST_CMDS = os.path.join(
-    settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_GUEST_CMDS'))
-
-_OVS_VAR_DIR = settings.getValue('OVS_VAR_DIR')
-_GUEST_NET1_MAC = settings.getValue('GUEST_NET1_MAC')
-_GUEST_NET2_MAC = settings.getValue('GUEST_NET2_MAC')
-_GUEST_NET1_PCI_ADDRESS = settings.getValue('GUEST_NET1_PCI_ADDRESS')
-_GUEST_NET2_PCI_ADDRESS = settings.getValue('GUEST_NET2_PCI_ADDRESS')
-
-class QemuDpdkVhostCuse(tasks.Process, IVnf):
+class QemuDpdkVhostCuse(IVnfQemuDpdk):
     """
-    Control an instance of QEMU with vHost user guest communication.
+    Control an instance of QEMU with vHost cuse guest communication.
     """
-    _bin = _QEMU_BIN
-    _logfile = _LOG_FILE_QEMU
-    _cmd = None
-    _expect = _GUEST_PROMPT_LOGIN
-    _proc_name = 'qemu'
-    _number_vnfs = 0
-
-    class GuestCommandFilter(logging.Filter):
-        """
-        Filter out strings beginning with 'guestcmd :'.
-        """
-        def filter(self, record):
-            return record.getMessage().startswith(self.prefix)
-
-    def __init__(self, memory=_GUEST_MEMORY, cpus=_GUEST_SMP,
-                 monitor_path='/tmp', shared_path_host=_GUEST_SHARE_DIR,
-                 args='', timeout=120, deployment="PVP"):
+    def __init__(self):
         """
         Initialisation function.
-
-        :param timeout: Time to wait for login prompt. If set to
-            0 do not wait.
-        :param number: Number of QEMU instance, used when multiple QEMU
-            instances are started at once.
-        :param args: Arguments to pass to QEMU.
-
-        :returns: None
         """
+        super(QemuDpdkVhostCuse, self).__init__()
         self._logger = logging.getLogger(__name__)
-        self._number = self._number_vnfs
-        self._number_vnfs = self._number_vnfs + 1
-        self._logfile = self._logfile + str(self._number)
-        self._log_prefix = 'guest_%d_cmd : ' % self._number
-        self._timeout = timeout
-        self._monitor = '%s/vm%dmonitor' % (monitor_path, self._number)
 
-        name = 'Client%d' % self._number
-        vnc = ':%d' % self._number
-        self._cmd = ['sudo', '-E', 'taskset ' + str(_QEMU_CORE), self._bin,
-                     '-m', str(memory), '-smp', str(cpus), '-cpu', 'host',
-                     '-drive', 'if=scsi,file='+_GUEST_IMAGE,
-                     '-drive',
-                     'if=scsi,file=fat:rw:%s,snapshot=off' % shared_path_host,
-                     '-boot', 'c', '--enable-kvm',
-                     '-monitor', 'unix:%s,server,nowait' % self._monitor,
-                     '-object',
-                     'memory-backend-file,id=mem,size=' + str(memory) + 'M,' +
-                     'mem-path=' + _HUGEPAGE_DIR + ',share=on',
-                     '-numa', 'node,memdev=mem -mem-prealloc',
-                     '-net', 'none', '-no-reboot',
-                     '-netdev',
-                     'type=tap,id=net1,script=no,downscript=no,' +
-                     'ifname=dpdkvhostcuse0,vhost=on',
-                     '-device',
-                     'virtio-net-pci,netdev=net1,mac=' + _GUEST_NET1_MAC,
-                     '-netdev',
-                     'type=tap,id=net2,script=no,downscript=no,' +
-                     'ifname=dpdkvhostcuse1,vhost=on',
-                     '-device',
-                     'virtio-net-pci,netdev=net2,mac=' + _GUEST_NET2_MAC,
-                     '-nographic', '-vnc', str(vnc), '-name', name,
-                     '-snapshot',
-                    ]
-        self._cmd.extend(args)
-        self._configure_logging()
-
-    def _configure_logging(self):
-        """
-        Configure logging.
-        """
-        self.GuestCommandFilter.prefix = self._log_prefix
-
-        logger = logging.getLogger()
-        cmd_logger = logging.FileHandler(
-            filename=_LOG_FILE_GUEST_CMDS + str(self._number))
-        cmd_logger.setLevel(logging.DEBUG)
-        cmd_logger.addFilter(self.GuestCommandFilter())
-        logger.addHandler(cmd_logger)
-
-    # startup/Shutdown
-
-    def start(self):
-        """
-        Start QEMU instance, login and prepare for commands.
-        """
-        super(QemuDpdkVhostCuse, self).start()
-        self._affinitize()
-
-        if self._timeout:
-            self._login()
-            self._config_guest_loopback()
-
-    def stop(self):
-        """
-        Kill QEMU instance if it is alive.
-        """
-        self._logger.info('Killing QEMU...')
-
-        super(QemuDpdkVhostCuse, self).kill()
+        # calculate indexes of guest devices (e.g. charx, dpdkvhostuserx)
+        i = self._number * 2
+        if1 = str(i)
+        if2 = str(i + 1)
+        net1 = 'net' + str(i + 1)
+        net2 = 'net' + str(i + 2)
+
+        self._cmd += ['-netdev',
+                      'type=tap,id=' + net1 + ',script=no,downscript=no,' +
+                      'ifname=dpdkvhostcuse' + if1 + ',vhost=on',
+                      '-device',
+                      'virtio-net-pci,mac=' +
+                      S.getValue('GUEST_NET1_MAC')[self._number] +
+                      ',netdev=' + net1 + ',csum=off,gso=off,' +
+                      'guest_tso4=off,guest_tso6=off,guest_ecn=off',
+                      '-netdev',
+                      'type=tap,id=' + net2 +
+                      ',script=no,downscript=no,' +
+                      'ifname=dpdkvhostcuse' + if2 + ',vhost=on',
+                      '-device',
+                      'virtio-net-pci,mac=' +
+                      S.getValue('GUEST_NET2_MAC')[self._number] +
+                      ',netdev=' + net2 + ',csum=off,gso=off,' +
+                      'guest_tso4=off,guest_tso6=off,guest_ecn=off',
+                     ]
 
     # helper functions
 
-    def _login(self, timeout=120):
+    def _modify_dpdk_makefile(self):
         """
-        Login to QEMU instance.
-
-        This can be used immediately after booting the machine, provided a
-        sufficiently long ``timeout`` is given.
-
-        :param timeout: Timeout to wait for login to complete.
-
-        :returns: None
+        Modifies DPDK makefile in Guest before compilation
         """
-        # if no timeout was set, we likely started QEMU without waiting for it
-        # to boot. This being the case, we best check that it has finished
-        # first.
-        if not self._timeout:
-            self._expect_process(timeout=timeout)
-
-        self._child.sendline(_GUEST_USERNAME)
-        self._child.expect(_GUEST_PROMPT_PASSWORD, timeout=5)
-        self._child.sendline(_GUEST_PASSWORD)
-
-        self._expect_process(_GUEST_PROMPT, timeout=5)
-
-    def execute(self, cmd, delay=0):
-        """
-        Send ``cmd`` with no wait.
-
-        Useful for asynchronous commands.
-
-        :param cmd: Command to send to guest.
-        :param timeout: Delay to wait after sending command before returning.
-
-        :returns: None
-        """
-        self._logger.debug('%s%s', self._log_prefix, cmd)
-        self._child.sendline(cmd)
-        time.sleep(delay)
-
-    def wait(self, msg=_GUEST_PROMPT, timeout=30):
-        """
-        Wait for ``msg``.
-
-        :param msg: Message to wait for from guest.
-        :param timeout: Time to wait for message.
-
-        :returns: None
-        """
-        self._child.expect(msg, timeout=timeout)
-
-    def execute_and_wait(self, cmd, timeout=30, prompt=_GUEST_PROMPT):
-        """
-        Send ``cmd`` and wait ``timeout`` seconds for prompt.
-
-        :param cmd: Command to send to guest.
-        :param timeout: Time to wait for prompt.
-
-        :returns: None
-        """
-        self.execute(cmd)
-        self.wait(prompt, timeout=timeout)
-
-    def send_and_pass(self, cmd, timeout=30):
-        """
-        Send ``cmd`` and wait ``timeout`` seconds for it to pass.
-
-        :param cmd: Command to send to guest.
-        :param timeout: Time to wait for prompt before checking return code.
-
-        :returns: None
-        """
-        self.execute(cmd)
-        self.wait(_GUEST_PROMPT, timeout=timeout)
-        self.execute('echo $?')
-        self._child.expect('^0$', timeout=1)  # expect a 0
-        self.wait(_GUEST_PROMPT, timeout=timeout)
-
-    def _affinitize(self):
-        """
-        Affinitize the SMP cores of a QEMU instance.
-
-        This is a bit of a hack. The 'socat' utility is used to
-        interact with the QEMU HMP. This is necessary due to the lack
-        of QMP in older versions of QEMU, like v1.6.2. In future
-        releases, this should be replaced with calls to libvirt or
-        another Python-QEMU wrapper library.
-
-        :returns: None
-        """
-        thread_id = (r'.* CPU #%d: .* thread_id=(\d+)')
-
-        self._logger.info('Affinitizing guest...')
-
-        cur_locale = locale.getlocale()[1]
-        proc = subprocess.Popen(
-            ('echo', 'info cpus'), stdout=subprocess.PIPE)
-        output = subprocess.check_output(
-            ('sudo', 'socat', '-', 'UNIX-CONNECT:%s' % self._monitor),
-            stdin=proc.stdout)
-        proc.wait()
-
-        for cpu in range(0, int(_GUEST_SMP)):
-            match = None
-            for line in output.decode(cur_locale).split('\n'):
-                match = re.search(thread_id % cpu, line)
-                if match:
-                    self._affinitize_pid(
-                        _GUEST_CORE_BINDING[self._number - 1][cpu],
-                        match.group(1))
-                    break
-
-            if not match:
-                self._logger.error('Failed to affinitize guest core #%d. Could'
-                                   ' not parse tid.', cpu)
-
-    def _config_guest_loopback(self):
-        """
-        Configure VM to run testpmd
-
-        Configure performs the following:
-        * Mount hugepages
-        * mount shared directory for copying DPDK
-        * Disable firewall
-        * Compile DPDK
-        * DPDK NIC bind
-        * Run testpmd
-        """
-
-        # Guest images _should_ have 1024 hugepages by default,
-        # but just in case:'''
-        self.execute_and_wait('sysctl vm.nr_hugepages=1024')
-
-        # Mount hugepages
-        self.execute_and_wait('mkdir -p /dev/hugepages')
-        self.execute_and_wait(
-            'mount -t hugetlbfs hugetlbfs /dev/hugepages')
-
-        # mount shared directory
-        self.execute_and_wait('umount ' + _OVS_DPDK_SHARE)
-        self.execute_and_wait('rm -rf ' + _GUEST_OVS_DPDK_DIR)
-        self.execute_and_wait('mkdir -p ' + _OVS_DPDK_SHARE)
-        self.execute_and_wait('mount -o iocharset=utf8 /dev/sdb1 ' +
-                              _OVS_DPDK_SHARE)
-        self.execute_and_wait('mkdir -p ' + _GUEST_OVS_DPDK_DIR)
-        self.execute_and_wait('cp -a ' + _OVS_DPDK_SHARE + '/* ' + _GUEST_OVS_DPDK_DIR)
-
-        # Disable services (F16)
-        self.execute_and_wait('systemctl status iptables.service')
-        self.execute_and_wait('systemctl stop iptables.service')
-
-        # build and configure system for dpdk
-        self.execute_and_wait('cd ' + _GUEST_OVS_DPDK_DIR + '/DPDK',
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        self.execute_and_wait('export CC=gcc', prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('export RTE_SDK=' + _GUEST_OVS_DPDK_DIR + '/DPDK',
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('export RTE_TARGET=%s' % _RTE_TARGET,
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-
         self.execute_and_wait("sed -i -e 's/CONFIG_RTE_LIBRTE_VHOST_USER=n/" +
-                              "CONFIG_RTE_LIBRTE_VHOST_USER=y/g' config/common_linuxapp",
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        self.execute_and_wait('make uninstall', prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('make install T=%s -j 2' % _RTE_TARGET,
-                              timeout=300, prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        self.execute_and_wait('modprobe uio', prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('insmod %s/kmod/igb_uio.ko' % _RTE_TARGET,
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('./tools/dpdk_nic_bind.py --status',
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('./tools/dpdk_nic_bind.py -b igb_uio'
-                              ' ' + _GUEST_NET1_PCI_ADDRESS + ' '
-                              + _GUEST_NET2_PCI_ADDRESS,
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        self.execute_and_wait('./tools/dpdk_nic_bind.py --status',
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        self.execute_and_wait('cd ' +  _RTE_TARGET + '/build/app/test-pmd',
-                              prompt=_QEMU_GUEST_TEST_PMD_PROMPT)
-
-        self.execute_and_wait('./testpmd -c 0x3 -n 4 --socket-mem 512 --'
-                              ' --burst=64 -i --txqflags=0xf00 ' +
-                              '--disable-hw-vlan', 20, "Done")
-        self.execute('set fwd mac_retry', 1)
-        self.execute_and_wait('start', 20,
-                              'TX RS bit threshold=0 - TXQ flags=0xf00')
-
-
-if __name__ == '__main__':
-    import sys
-
-    with QemuDpdkVhostCuse() as vm1:
-        print(
-            '\n\n************************\n'
-            'Basic command line suitable for ls, cd, grep and cat.\n If you'
-            ' try to run Vim from here you\'re going to have a bad time.\n'
-            'For more complex tasks please use \'vncviewer :1\' to connect to'
-            ' this VM\nUsername: %s Password: %s\nPress ctrl-C to quit\n'
-            '************************\n' % (_GUEST_USERNAME, _GUEST_PASSWORD))
-
-        if sys.argv[1]:
-            with open(sys.argv[1], 'r') as file_:
-                for logline in file_:
-                    # lines are of format:
-                    #   guest_N_cmd : <command>
-                    # and we only want the <command> piece
-                    cmdline = logline.split(':')[1].strip()
-
-                    # use a no timeout since we don't know how long we
-                    # should wait
-                    vm1.send_and_wait(cmdline, timeout=-1)
-
-        while True:
-            USER_INPUT = input()
-            vm1.send_and_wait(USER_INPUT, timeout=5)
+                              "CONFIG_RTE_LIBRTE_VHOST_USER=y/g'" +
+                              "config/common_linuxapp")
index a3b96b1..94d87f9 100644 (file)
 """Automation of QEMU hypervisor for launching vhost-user enabled guests.
 """
 
-import os
-import time
 import logging
-import locale
-import re
-import subprocess
 
-from tools import tasks
-from conf import settings
-from vnfs.vnf.vnf import IVnf
+from conf import settings as S
+from vnfs.qemu.qemu_dpdk import IVnfQemuDpdk
 
-_QEMU_BIN = settings.getValue('QEMU_BIN')
-_RTE_TARGET = settings.getValue('RTE_TARGET')
-
-GUEST_MEMORY = '4096M'
-GUEST_SMP = '2'
-GUEST_CORE_BINDING = [(4, 5), (6, 7), (9, 10)]
-
-GUEST_IMAGE = settings.getValue('GUEST_IMAGE')
-GUEST_SHARE_DIR = settings.getValue('GUEST_SHARE_DIR')
-
-GUEST_USERNAME = settings.getValue('GUEST_USERNAME')
-GUEST_PASSWORD = settings.getValue('GUEST_PASSWORD')
-
-GUEST_PROMPT_LOGIN = settings.getValue('GUEST_PROMPT_LOGIN')
-GUEST_PROMPT_PASSWORD = settings.getValue('GUEST_PROMPT_PASSWORD')
-GUEST_PROMPT = settings.getValue('GUEST_PROMPT')
-
-_QEMU_GUEST_DPDK_PROMPT = settings.getValue('QEMU_GUEST_DPDK_PROMPT')
-_QEMU_GUEST_TEST_PMD_PROMPT = settings.getValue('QEMU_GUEST_TEST_PMD_PROMPT')
-_HUGEPAGE_DIR = settings.getValue('HUGEPAGE_DIR')
-
-_GUEST_OVS_DPDK_DIR = '/root/ovs_dpdk'
-_OVS_DPDK_SHARE = '/mnt/ovs_dpdk_share'
-
-LOG_FILE_QEMU = os.path.join(
-    settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_QEMU'))
-LOG_FILE_GUEST_CMDS = os.path.join(
-    settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_GUEST_CMDS'))
-
-VHOST_DEV_PATH = os.path.join('/dev', settings.getValue('VHOST_DEV_FILE'))
-
-_OVS_VAR_DIR = settings.getValue('OVS_VAR_DIR')
-_GUEST_NET1_MAC = settings.getValue('GUEST_NET1_MAC')
-_GUEST_NET2_MAC = settings.getValue('GUEST_NET2_MAC')
-_GUEST_NET1_PCI_ADDRESS = settings.getValue('GUEST_NET1_PCI_ADDRESS')
-_GUEST_NET2_PCI_ADDRESS = settings.getValue('GUEST_NET2_PCI_ADDRESS')
-
-class QemuDpdkVhost(tasks.Process, IVnf):
+class QemuDpdkVhostUser(IVnfQemuDpdk):
     """
     Control an instance of QEMU with vHost user guest communication.
     """
-    _bin = _QEMU_BIN
-    _logfile = LOG_FILE_QEMU
-    _cmd = None
-    _expect = GUEST_PROMPT_LOGIN
-    _proc_name = 'qemu'
-    _number_vnfs = 0
-
-    class GuestCommandFilter(logging.Filter):
-        """
-        Filter out strings beginning with 'guestcmd :'.
-        """
-        def filter(self, record):
-            return record.getMessage().startswith(self.prefix)
-
-    def __init__(self, memory=GUEST_MEMORY, cpus=GUEST_SMP,
-                 monitor_path='/tmp', shared_path_host=GUEST_SHARE_DIR,
-                 args='', timeout=120, deployment="P2P"):
+    def __init__(self):
         """
         Initialisation function.
-
-        :param timeout: Time to wait for login prompt. If set to
-            0 do not wait.
-        :param number: Number of QEMU instance, used when multiple QEMU
-            instances are started at once.
-        :param args: Arguments to pass to QEMU.
-
-        :returns: None
         """
+        super(QemuDpdkVhostUser, self).__init__()
         self._logger = logging.getLogger(__name__)
-        self._number = self._number_vnfs
-        self._number_vnfs = self._number_vnfs + 1
-        self._logfile = self._logfile + str(self._number)
-        self._log_prefix = 'guest_%d_cmd : ' % self._number
-        self._timeout = timeout
-        self._monitor = '%s/vm%dmonitor' % (monitor_path, self._number)
-
-        name = 'Client%d' % self._number
-        vnc = ':%d' % self._number
-        self._cmd = ['sudo', '-E', self._bin, '-m', str(memory),
-                     '-smp', str(cpus), '-cpu', 'host',
-                     '-drive', 'if=scsi,file='+GUEST_IMAGE,
-                     '-drive',
-                     'if=scsi,file=fat:rw:%s,snapshot=off' % shared_path_host,
-                     '-boot', 'c', '--enable-kvm', '-pidfile', '/tmp/vm1.pid',
-                     '-monitor', 'unix:%s,server,nowait' % self._monitor,
-                     '-object',
-                     'memory-backend-file,id=mem,size=4096M,' +
-                     'mem-path=' + _HUGEPAGE_DIR + ',share=on',
-                     '-numa', 'node,memdev=mem -mem-prealloc',
-                     '-chardev',
-                     'socket,id=char0,path=' + _OVS_VAR_DIR + 'dpdkvhostuser0',
-                     '-chardev',
-                     'socket,id=char1,path=' + _OVS_VAR_DIR + 'dpdkvhostuser1',
-                     '-netdev',
-                     'type=vhost-user,id=net1,chardev=char0,vhostforce',
-                     '-device',
-                     'virtio-net-pci,mac=' + _GUEST_NET1_MAC +
-                     ',netdev=net1,csum=off,gso=off,' +
-                     'guest_tso4=off,guest_tso6=off,guest_ecn=off',
-                     '-netdev',
-                     'type=vhost-user,id=net2,chardev=char1,vhostforce',
-                     '-device',
-                     'virtio-net-pci,mac=' + _GUEST_NET2_MAC +
-                     ',netdev=net2,csum=off,gso=off,' +
-                     'guest_tso4=off,guest_tso6=off,guest_ecn=off',
-                     '-nographic', '-vnc', str(vnc), '-name', name,
-                     '-snapshot',
-                    ]
-        self._cmd.extend(args)
-        self._configure_logging()
-
-    def _configure_logging(self):
-        """
-        Configure logging.
-        """
-        self.GuestCommandFilter.prefix = self._log_prefix
-
-        logger = logging.getLogger()
-        cmd_logger = logging.FileHandler(
-            filename=LOG_FILE_GUEST_CMDS + str(self._number))
-        cmd_logger.setLevel(logging.DEBUG)
-        cmd_logger.addFilter(self.GuestCommandFilter())
-        logger.addHandler(cmd_logger)
-
-    # startup/Shutdown
-
-    def start(self):
-        """
-        Start QEMU instance, login and prepare for commands.
-        """
-        super(QemuDpdkVhost, self).start()
-        self._affinitize()
-
-        if self._timeout:
-            self._login()
-            self._config_guest_loopback()
-
-    def stop(self):
-        """
-        Kill QEMU instance if it is alive.
-        """
-        self._logger.info('Killing QEMU...')
-
-        super(QemuDpdkVhost, self).kill()
-
-    # helper functions
-
-    def _login(self, timeout=120):
-        """
-        Login to QEMU instance.
-
-        This can be used immediately after booting the machine, provided a
-        sufficiently long ``timeout`` is given.
-
-        :param timeout: Timeout to wait for login to complete.
-
-        :returns: None
-        """
-        # if no timeout was set, we likely started QEMU without waiting for it
-        # to boot. This being the case, we best check that it has finished
-        # first.
-        if not self._timeout:
-            self._expect_process(timeout=timeout)
-
-        self._child.sendline(GUEST_USERNAME)
-        self._child.expect(GUEST_PROMPT_PASSWORD, timeout=5)
-        self._child.sendline(GUEST_PASSWORD)
-
-        self._expect_process(GUEST_PROMPT, timeout=5)
-
-    def execute(self, cmd, delay=0):
-        """
-        Send ``cmd`` with no wait.
-
-        Useful for asynchronous commands.
-
-        :param cmd: Command to send to guest.
-        :param timeout: Delay to wait after sending command before returning.
-
-        :returns: None
-        """
-        self._logger.debug('%s%s', self._log_prefix, cmd)
-        self._child.sendline(cmd)
-        time.sleep(delay)
-
-    def wait(self, msg=GUEST_PROMPT, timeout=30):
-        """
-        Wait for ``msg``.
-
-        :param msg: Message to wait for from guest.
-        :param timeout: Time to wait for message.
-
-        :returns: None
-        """
-        self._child.expect(msg, timeout=timeout)
-
-    def execute_and_wait(self, cmd, timeout=30, prompt=GUEST_PROMPT):
-        """
-        Send ``cmd`` and wait ``timeout`` seconds for prompt.
-
-        :param cmd: Command to send to guest.
-        :param timeout: Time to wait for prompt.
-
-        :returns: None
-        """
-        self.execute(cmd)
-        self.wait(prompt, timeout=timeout)
-
-    def send_and_pass(self, cmd, timeout=30):
-        """
-        Send ``cmd`` and wait ``timeout`` seconds for it to pass.
-
-        :param cmd: Command to send to guest.
-        :param timeout: Time to wait for prompt before checking return code.
-
-        :returns: None
-        """
-        self.execute(cmd)
-        self.wait(GUEST_PROMPT, timeout=timeout)
-        self.execute('echo $?')
-        self._child.expect('^0$', timeout=1)  # expect a 0
-        self.wait(GUEST_PROMPT, timeout=timeout)
-
-    def _affinitize(self):
-        """
-        Affinitize the SMP cores of a QEMU instance.
-
-        This is a bit of a hack. The 'socat' utility is used to
-        interact with the QEMU HMP. This is necessary due to the lack
-        of QMP in older versions of QEMU, like v1.6.2. In future
-        releases, this should be replaced with calls to libvirt or
-        another Python-QEMU wrapper library.
-
-        :returns: None
-        """
-        thread_id = (r'.* CPU #%d: .* thread_id=(\d+)')
-
-        self._logger.info('Affinitizing guest...')
-
-        cur_locale = locale.getlocale()[1]
-        proc = subprocess.Popen(
-            ('echo', 'info cpus'), stdout=subprocess.PIPE)
-        output = subprocess.check_output(
-            ('sudo', 'socat', '-', 'UNIX-CONNECT:%s' % self._monitor),
-            stdin=proc.stdout)
-        proc.wait()
-
-        for cpu in range(0, int(GUEST_SMP)):
-            match = None
-            for line in output.decode(cur_locale).split('\n'):
-                match = re.search(thread_id % cpu, line)
-                if match:
-                    self._affinitize_pid(
-                        GUEST_CORE_BINDING[self._number - 1][cpu],
-                        match.group(1))
-                    break
-
-            if not match:
-                self._logger.error('Failed to affinitize guest core #%d. Could'
-                                   ' not parse tid.', cpu)
-
-    def _config_guest_loopback(self):
-        '''# mount hugepages
-        # Guest images _should_ have 1024 hugepages by default,
-        # but just in case:'''
-        self.execute_and_wait('sysctl vm.nr_hugepages=1024')
-        self.execute_and_wait('mkdir -p /dev/hugepages')
-        self.execute_and_wait(
-            'mount -t hugetlbfs hugetlbfs /dev/hugepages')
-
-        # mount shared directory
-        self.execute_and_wait('umount ' + _OVS_DPDK_SHARE)
-        self.execute_and_wait('rm -rf ' + _GUEST_OVS_DPDK_DIR)
-        self.execute_and_wait('mkdir -p ' + _OVS_DPDK_SHARE)
-        self.execute_and_wait('mount -o iocharset=utf8 /dev/sdb1 ' +
-                              _OVS_DPDK_SHARE)
-        self.execute_and_wait('mkdir -p ' + _GUEST_OVS_DPDK_DIR)
-        self.execute_and_wait('cp -a ' + _OVS_DPDK_SHARE + '/* ' + _GUEST_OVS_DPDK_DIR)
-        # Get VM info
-        self.execute_and_wait('cat /etc/default/grub')
-
-        # Disable services (F16)
-        self.execute_and_wait('systemctl status iptables.service')
-        self.execute_and_wait('systemctl stop iptables.service')
-
-        # build and configure system for dpdk
-        self.execute_and_wait('cd ' + _GUEST_OVS_DPDK_DIR + '/DPDK',
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        self.execute_and_wait('export CC=gcc', prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('export RTE_SDK=' + _GUEST_OVS_DPDK_DIR + '/DPDK',
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('export RTE_TARGET=%s' % _RTE_TARGET,
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        self.execute_and_wait('make uninstall', prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('make install T=%s -j 2' % _RTE_TARGET,
-                              timeout=300, prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        self.execute_and_wait('modprobe uio', prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('insmod %s/kmod/igb_uio.ko' % _RTE_TARGET,
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('./tools/dpdk_nic_bind.py --status',
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-        self.execute_and_wait('./tools/dpdk_nic_bind.py -b igb_uio'
-                              ' ' + _GUEST_NET1_PCI_ADDRESS + ' '
-                              + _GUEST_NET2_PCI_ADDRESS,
-                              prompt=_QEMU_GUEST_DPDK_PROMPT)
-
-        # build and run 'test-pmd'
-        self.execute_and_wait('cd ' + _GUEST_OVS_DPDK_DIR +
-                              '/DPDK/app/test-pmd',
-                              prompt=_QEMU_GUEST_TEST_PMD_PROMPT)
-        self.execute_and_wait('make clean', prompt=_QEMU_GUEST_TEST_PMD_PROMPT)
-        self.execute_and_wait('make', prompt=_QEMU_GUEST_TEST_PMD_PROMPT)
-        self.execute_and_wait('./testpmd -c 0x3 -n 4 --socket-mem 512 --'
-                              ' --burst=64 -i --txqflags=0xf00 ' +
-                              '--disable-hw-vlan', 20, "Done")
-        self.execute('set fwd mac_retry', 1)
-        self.execute_and_wait('start', 20,
-                              'TX RS bit threshold=0 - TXQ flags=0xf00')
-
-
-if __name__ == '__main__':
-    import sys
-
-    with QemuDpdkVhost() as vm1:
-        print(
-            '\n\n************************\n'
-            'Basic command line suitable for ls, cd, grep and cat.\n If you'
-            ' try to run Vim from here you\'re going to have a bad time.\n'
-            'For more complex tasks please use \'vncviewer :1\' to connect to'
-            ' this VM\nUsername: %s Password: %s\nPress ctrl-C to quit\n'
-            '************************\n' % (GUEST_USERNAME, GUEST_PASSWORD))
-
-        if sys.argv[1]:
-            with open(sys.argv[1], 'r') as file_:
-                for logline in file_:
-                    # lines are of format:
-                    #   guest_N_cmd : <command>
-                    # and we only want the <command> piece
-                    cmdline = logline.split(':')[1].strip()
 
-                    # use a no timeout since we don't know how long we
-                    # should wait
-                    vm1.send_and_wait(cmdline, timeout=-1)
+        # calculate indexes of guest devices (e.g. charx, dpdkvhostuserx)
+        i = self._number * 2
+        if1 = str(i)
+        if2 = str(i + 1)
+        net1 = 'net' + str(i + 1)
+        net2 = 'net' + str(i + 2)
+
+        self._cmd += ['-chardev',
+                      'socket,id=char' + if1 +
+                      ',path=' + S.getValue('OVS_VAR_DIR') +
+                      'dpdkvhostuser' + if1,
+                      '-chardev',
+                      'socket,id=char' + if2 +
+                      ',path=' + S.getValue('OVS_VAR_DIR') +
+                      'dpdkvhostuser' + if2,
+                      '-netdev',
+                      'type=vhost-user,id=' + net1 +
+                      ',chardev=char' + if1 + ',vhostforce',
+                      '-device',
+                      'virtio-net-pci,mac=' +
+                      S.getValue('GUEST_NET1_MAC')[self._number] +
+                      ',netdev=' + net1 + ',csum=off,gso=off,' +
+                      'guest_tso4=off,guest_tso6=off,guest_ecn=off',
+                      '-netdev',
+                      'type=vhost-user,id=' + net2 +
+                      ',chardev=char' + if2 + ',vhostforce',
+                      '-device',
+                      'virtio-net-pci,mac=' +
+                      S.getValue('GUEST_NET2_MAC')[self._number] +
+                      ',netdev=' + net2 + ',csum=off,gso=off,' +
+                      'guest_tso4=off,guest_tso6=off,guest_ecn=off',
+                     ]
 
-        while True:
-            USER_INPUT = input()
-            vm1.send_and_wait(USER_INPUT, timeout=5)
index c746aa8..f8d2df9 100644 (file)
 Interface for VNF.
 """
 
+import time
+from tools import tasks
 
-class IVnf(object):
+class IVnf(tasks.Process):
 
     """
     Interface for VNF.
     """
 
-    def __init__(self, memory, cpus,
-                 monitor_path, shared_path_host,
-                 shared_path_guest, guest_prompt):
+    _number_vnfs = 0
+
+    def __init__(self):
         """
         Initialization method.
 
         Purpose of this method is to initialize all
         common Vnf data, no services should be started by
         this call (use ``start`` method instead).
-
-        :param memory:   Virtual RAM size in megabytes.
-        :param cpus:     Number of Processors.
-        :param monitor_path: Configure monitor to given path.
-        :param shared_path_host: HOST path to shared location.
-        :param shared_path_guest: GUEST path to shared location.
-        :param guest_prompt: preconfigured command prompt which is used
-                           in execute_and_wait & wait methods
-                           to detect if particular call is finished.
         """
-        raise NotImplementedError()
+        self._number = IVnf._number_vnfs
+        IVnf._number_vnfs = IVnf._number_vnfs + 1
+        self._log_prefix = 'vnf_%d_cmd : ' % self._number
 
     def start(self):
         """
         Starts VNF instance.
+
+        This is a blocking function
         """
-        raise NotImplementedError()
+        super(IVnf, self).start()
 
     def stop(self):
         """
         Stops VNF instance.
         """
-        raise NotImplementedError()
+        self._logger.info('Killing VNF...')
+
+        # force termination of VNF and wait for it to terminate; It will avoid
+        # sporadic reboot of host. (caused by hugepages or DPDK ports)
+        super(IVnf, self).kill(signal='-9', sleep=10)
 
-    def execute(self, command, delay=30):
+    def execute(self, cmd, delay=0):
         """
-        execute ``command`` with given ``delay``.
+        execute ``cmd`` with given ``delay``.
 
         This method makes asynchronous call to guest system
         and waits given ``delay`` before returning. Can be
         used with ``wait`` method to create synchronous call.
 
-        :param command: Command to execute on guest system.
+        :param cmd: Command to execute on guest system.
         :param delay: Delay (in seconds) to wait after sending
                       command before returning. Please note that
                       this value can be floating point which
@@ -72,17 +73,19 @@ class IVnf(object):
 
         :returns: None.
         """
-        raise NotImplementedError()
+        self._logger.debug('%s%s', self._log_prefix, cmd)
+        self._child.sendline(cmd)
+        time.sleep(delay)
 
-    def wait(self, guest_prompt, timeout=30):
+    def wait(self, prompt='', timeout=30):
         """
-        wait for ``guest_prompt`` on guest system for given ``timeout``.
+        wait for ``prompt`` on guest system for given ``timeout``.
 
         This method ends based on two conditions:
-        * ``guest_prompt`` has been detected
+        * ``prompt`` has been detected
         * ``timeout`` has been reached.
 
-        :param guest_prompt: method end condition. If ``guest_prompt``
+        :param prompt: method end condition. If ``prompt``
                              won't be detected during given timeout,
                              method will return False.
         :param timeout: Time to wait for prompt (in seconds).
@@ -92,28 +95,29 @@ class IVnf(object):
         :returns: True if result_cmd has been detected before
                   timeout has been reached, False otherwise.
         """
-        raise NotImplementedError()
+        self._child.expect(prompt, timeout=timeout)
 
-    def execute_and_wait(self, command, timeout=30, guest_prompt=None):
+    def execute_and_wait(self, cmd, timeout=30, prompt=''):
         """
-        execute ``command`` with given ``timeout``.
+        execute ``cmd`` with given ``timeout``.
 
         This method makes synchronous call to guest system
-        and waits till ``command`` execution is finished
-        (based on ``guest_prompt value) or ''timeout'' has
+        and waits till ``cmd`` execution is finished
+        (based on ``prompt value) or ''timeout'' has
         been reached.
 
-        :param command: Command to execute on guest system.
+        :param cmd: Command to execute on guest system.
         :param timeout: Timeout till the end of execution is not
                         detected.
-        :param guest_prompt: method end condition. If ``guest_prompt``
+        :param prompt: method end condition. If ``prompt``
                              won't be detected during given timeout,
                              method will return False. If no argument
                              or None value will be passed, default
-                             ``guest_prompt`` passed in __init__
+                             ``prompt`` passed in __init__
                              method will be used.
 
         :returns: True if end of execution has been detected
                   before timeout has been reached, False otherwise.
         """
-        raise NotImplementedError()
+        self.execute(cmd)
+        self.wait(prompt=prompt, timeout=timeout)
index b1fd08b..1a53bd6 100644 (file)
@@ -43,8 +43,8 @@ class OvsDpdkVhost(IVSwitch):
         vswitchd_args += settings.getValue('VSWITCHD_DPDK_ARGS')
         vswitchd_args += _VSWITCHD_CONST_ARGS
 
-        if _VHOST_METHOD == "cuse":
-            self._logger.info("Inserting VHOST modules into kernel...")
+        if settings.getValue('VNF').endswith('Cuse'):
+            self._logger.info("Inserting VHOST Cuse modules into kernel...")
             dpdk.insert_vhost_modules()
 
         self._vswitchd = VSwitchd(vswitchd_args=vswitchd_args,
@@ -119,7 +119,7 @@ class OvsDpdkVhost(IVSwitch):
         """
         bridge = self._bridges[switch_name]
         # Changed dpdkvhost to dpdkvhostuser to be able to run in Qemu 2.2
-        if _VHOST_METHOD == "cuse":
+        if settings.getValue('VNF').endswith('Cuse'):
             vhost_count = self._get_port_count(bridge, 'type=dpdkvhostcuse')
             port_name = 'dpdkvhostcuse' + str(vhost_count)
             params = ['--', 'set', 'Interface', port_name, 'type=dpdkvhostcuse']