Use PapiSocketProvider for most PAPI calls 72/19272/298
authorVratko Polak <vrpolak@cisco.com>
Wed, 17 Jul 2019 10:40:49 +0000 (12:40 +0200)
committerVratko Polak <vrpolak@cisco.com>
Wed, 17 Jul 2019 10:40:49 +0000 (12:40 +0200)
Ticket: CSIT-1541
Ticket: VPP-1722
Ticket: CSIT-1546

+ Increase timeout to hide x520 slownes of show hardware detail.
- Install sshpass and update ssh client in virl bootstrap.
 + Added TODOs to remove when CSIT-1546 is fixed.
+ Enable default socksvr on any startup conf.
+ Improve OptionString init and repr.
- The non-socket executor still kept for stats.
 + Remove everything unrelated to stats from non-socket executor.
- Remove some debug-loooking calls to avoid failures.
  TODO: Introduce proper parsing to the affected keywords.
+ Reduce logging from PAPI code to level INFO.
 - Needs https://gerrit.fd.io/r/20660 to fully work.
+ Change default values for LocalExecution.run()
 + Return code check enabled by default.
   Code is more readable when rc!=0 is allowed explicitly,
   and the test code will now detect unexpected failures.
 + Logging disabled by default.
   Output XML is large already. Important logging can be enabled explicitly.
+ Restore alphabetical order in common.sh functions.

Change-Id: I05882cb6b620ad14638f7404b5ad38c7a5de9e6c
Signed-off-by: Vratko Polak <vrpolak@cisco.com>
30 files changed:
bootstrap.sh
docs/report/vpp_functional_tests/test_environment.rst
docs/report/vpp_performance_tests/test_environment.rst
resources/libraries/bash/function/common.sh
resources/libraries/python/Classify.py
resources/libraries/python/ContainerUtils.py
resources/libraries/python/FilteredLogger.py [new file with mode: 0644]
resources/libraries/python/IPUtil.py
resources/libraries/python/IPsecUtil.py
resources/libraries/python/IPv6Util.py
resources/libraries/python/InterfaceUtil.py
resources/libraries/python/KubernetesUtils.py
resources/libraries/python/L2Util.py
resources/libraries/python/LocalExecution.py
resources/libraries/python/Memif.py
resources/libraries/python/NATUtil.py
resources/libraries/python/OptionString.py
resources/libraries/python/PapiExecutor.py
resources/libraries/python/ProxyArp.py
resources/libraries/python/QemuUtils.py
resources/libraries/python/SetupFramework.py
resources/libraries/python/TestConfig.py
resources/libraries/python/Trace.py
resources/libraries/python/VPPUtil.py
resources/libraries/python/VhostUser.py
resources/libraries/python/VppConfigGenerator.py
resources/libraries/python/VppCounters.py
resources/libraries/python/telemetry/SPAN.py
resources/libraries/robot/honeycomb/performance.robot
resources/libraries/robot/shared/default.robot

index 44b0dc0..04cefee 100755 (executable)
@@ -26,13 +26,15 @@ OS_VERSION_ID=$(grep '^VERSION_ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\
 if [ "$OS_ID" == "centos" ]; then
     DISTRO="CENTOS"
     PACKAGE="rpm"
 if [ "$OS_ID" == "centos" ]; then
     DISTRO="CENTOS"
     PACKAGE="rpm"
-    sudo yum install -y python-devel python-virtualenv
+    # TODO: Remove when corresponding part of CSIT-1546 is addressed.
+    sudo yum install -y python-devel python-virtualenv openssh-clients sshpass
 elif [ "$OS_ID" == "ubuntu" ]; then
     DISTRO="UBUNTU"
     PACKAGE="deb"
 elif [ "$OS_ID" == "ubuntu" ]; then
     DISTRO="UBUNTU"
     PACKAGE="deb"
+    # TODO: Remove when corresponding part of CSIT-1546 is addressed.
     export DEBIAN_FRONTEND=noninteractive
     sudo apt-get -y update
     export DEBIAN_FRONTEND=noninteractive
     sudo apt-get -y update
-    sudo apt-get -y install libpython2.7-dev python-virtualenv
+    sudo apt-get -y install libpython2.7-dev python-virtualenv sshpass
 else
     echo "$OS_ID is not yet supported."
     exit 1
 else
     echo "$OS_ID is not yet supported."
     exit 1
index d8f2abf..b8a6b16 100644 (file)
@@ -402,6 +402,9 @@ There is used the default startup configuration as defined in `VPP startup.conf`
     {
       gid vpp
     }
     {
       gid vpp
     }
+    socksvr {
+      default
+    }
     dpdk
     {
       vdev cryptodev_aesni_gcm_pmd,socket_id=0
     dpdk
     {
       vdev cryptodev_aesni_gcm_pmd,socket_id=0
index 57e7973..3c179e1 100644 (file)
@@ -64,6 +64,9 @@ below:
       log /tmp/vpe.log
       nodaemon
     }
       log /tmp/vpe.log
       nodaemon
     }
+    socksvr {
+      default
+    }
     ip6
     {
       heap-size 4G
     ip6
     {
       heap-size 4G
index b0b97e0..549688f 100644 (file)
@@ -202,37 +202,41 @@ function common_dirs () {
 
     set -exuo pipefail
 
 
     set -exuo pipefail
 
-    BASH_FUNCTION_DIR="$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")" || {
-        die "Some error during localizing this source directory."
+    this_file=$(readlink -e "${BASH_SOURCE[0]}") || {
+        die "Some error during locating of this source file."
+    }
+    BASH_FUNCTION_DIR=$(dirname "${this_file}") || {
+        die "Some error during dirname call."
     }
     # Current working directory could be in a different repo, e.g. VPP.
     pushd "${BASH_FUNCTION_DIR}" || die "Pushd failed"
     }
     # Current working directory could be in a different repo, e.g. VPP.
     pushd "${BASH_FUNCTION_DIR}" || die "Pushd failed"
-    CSIT_DIR="$(readlink -e "$(git rev-parse --show-toplevel)")" || {
-        die "Readlink or git rev-parse failed."
+    relative_csit_dir=$(git rev-parse --show-toplevel) || {
+        die "Git rev-parse failed."
     }
     }
+    CSIT_DIR=$(readlink -e "${relative_csit_dir}") || die "Readlink failed."
     popd || die "Popd failed."
     popd || die "Popd failed."
-    TOPOLOGIES_DIR="$(readlink -e "${CSIT_DIR}/topologies/available")" || {
+    TOPOLOGIES_DIR=$(readlink -e "${CSIT_DIR}/topologies/available") || {
         die "Readlink failed."
     }
         die "Readlink failed."
     }
-    RESOURCES_DIR="$(readlink -e "${CSIT_DIR}/resources")" || {
+    RESOURCES_DIR=$(readlink -e "${CSIT_DIR}/resources") || {
         die "Readlink failed."
     }
         die "Readlink failed."
     }
-    TOOLS_DIR="$(readlink -e "${RESOURCES_DIR}/tools")" || {
+    TOOLS_DIR=$(readlink -e "${RESOURCES_DIR}/tools") || {
         die "Readlink failed."
     }
         die "Readlink failed."
     }
-    PYTHON_SCRIPTS_DIR="$(readlink -e "${TOOLS_DIR}/scripts")" || {
+    PYTHON_SCRIPTS_DIR=$(readlink -e "${TOOLS_DIR}/scripts") || {
         die "Readlink failed."
     }
 
         die "Readlink failed."
     }
 
-    ARCHIVE_DIR="$(readlink -f "${CSIT_DIR}/archive")" || {
+    ARCHIVE_DIR=$(readlink -f "${CSIT_DIR}/archive") || {
         die "Readlink failed."
     }
     mkdir -p "${ARCHIVE_DIR}" || die "Mkdir failed."
         die "Readlink failed."
     }
     mkdir -p "${ARCHIVE_DIR}" || die "Mkdir failed."
-    DOWNLOAD_DIR="$(readlink -f "${CSIT_DIR}/download_dir")" || {
+    DOWNLOAD_DIR=$(readlink -f "${CSIT_DIR}/download_dir") || {
         die "Readlink failed."
     }
     mkdir -p "${DOWNLOAD_DIR}" || die "Mkdir failed."
         die "Readlink failed."
     }
     mkdir -p "${DOWNLOAD_DIR}" || die "Mkdir failed."
-    GENERATED_DIR="$(readlink -f "${CSIT_DIR}/generated")" || {
+    GENERATED_DIR=$(readlink -f "${CSIT_DIR}/generated") || {
         die "Readlink failed."
     }
     mkdir -p "${GENERATED_DIR}" || die "Mkdir failed."
         die "Readlink failed."
     }
     mkdir -p "${GENERATED_DIR}" || die "Mkdir failed."
@@ -619,6 +623,42 @@ function run_pybot () {
 }
 
 
 }
 
 
+function select_os () {
+
+    # Populate variables related to local operating system.
+    #
+    # Also install any missing prerequisities CSIT tests need.
+    # TODO: Move the installation to a separate function?
+    #
+    # Variables set:
+    # - VPP_VER_FILE - Name of File in CSIT dir containing vpp stable version.
+    # - IMAGE_VER_FILE - Name of File in CSIT dir containing the image name.
+    # - PKG_SUFFIX - Suffix of OS package file name, "rpm" or "deb."
+
+    set -exuo pipefail
+
+    os_id=$(grep '^ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g') || {
+        die "Get OS release failed."
+    }
+
+    case "${os_id}" in
+        "ubuntu"*)
+            IMAGE_VER_FILE="VPP_DEVICE_IMAGE_UBUNTU"
+            VPP_VER_FILE="VPP_STABLE_VER_UBUNTU_BIONIC"
+            PKG_SUFFIX="deb"
+            ;;
+        "centos"*)
+            IMAGE_VER_FILE="VPP_DEVICE_IMAGE_CENTOS"
+            VPP_VER_FILE="VPP_STABLE_VER_CENTOS"
+            PKG_SUFFIX="rpm"
+            ;;
+        *)
+            die "Unable to identify distro or os from ${OS}"
+            ;;
+    esac
+}
+
+
 function select_tags () {
 
     # Variables read:
 function select_tags () {
 
     # Variables read:
@@ -778,82 +818,6 @@ function select_tags () {
 }
 
 
 }
 
 
-function select_vpp_device_tags () {
-
-    # Variables read:
-    # - TEST_CODE - String affecting test selection, usually jenkins job name.
-    # - TEST_TAG_STRING - String selecting tags, from gerrit comment.
-    #   Can be unset.
-    # Variables set:
-    # - TAGS - Array of processed tag boolean expressions.
-
-    set -exuo pipefail
-
-    case "${TEST_CODE}" in
-        # Select specific performance tests based on jenkins job type variable.
-        * )
-            if [[ -z "${TEST_TAG_STRING-}" ]]; then
-                # If nothing is specified, we will run pre-selected tests by
-                # following tags. Items of array will be concatenated by OR
-                # in Robot Framework.
-                test_tag_array=()
-            else
-                # If trigger contains tags, split them into array.
-                test_tag_array=(${TEST_TAG_STRING//:/ })
-            fi
-            ;;
-    esac
-
-    TAGS=()
-
-    # We will prefix with devicetest to prevent running other tests
-    # (e.g. Functional).
-    prefix="devicetestAND"
-    if [[ "${TEST_CODE}" == "vpp-"* ]]; then
-        # Automatic prefixing for VPP jobs to limit testing.
-        prefix="${prefix}"
-    fi
-    for tag in "${test_tag_array[@]}"; do
-        if [[ ${tag} == "!"* ]]; then
-            # Exclude tags are not prefixed.
-            TAGS+=("${tag}")
-        else
-            TAGS+=("${prefix}${tag}")
-        fi
-    done
-}
-
-function select_os () {
-
-    # Variables set:
-    # - VPP_VER_FILE - Name of File in CSIT dir containing vpp stable version.
-    # - IMAGE_VER_FILE - Name of File in CSIT dir containing the image name.
-    # - PKG_SUFFIX - Suffix of OS package file name, "rpm" or "deb."
-
-    set -exuo pipefail
-
-    os_id=$(grep '^ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g') || {
-        die "Get OS release failed."
-    }
-
-    case "${os_id}" in
-        "ubuntu"*)
-            IMAGE_VER_FILE="VPP_DEVICE_IMAGE_UBUNTU"
-            VPP_VER_FILE="VPP_STABLE_VER_UBUNTU_BIONIC"
-            PKG_SUFFIX="deb"
-            ;;
-        "centos"*)
-            IMAGE_VER_FILE="VPP_DEVICE_IMAGE_CENTOS"
-            VPP_VER_FILE="VPP_STABLE_VER_CENTOS"
-            PKG_SUFFIX="rpm"
-            ;;
-        *)
-            die "Unable to identify distro or os from ${OS}"
-            ;;
-    esac
-}
-
-
 function select_topology () {
 
     # Variables read:
 function select_topology () {
 
     # Variables read:
@@ -917,6 +881,51 @@ function select_topology () {
 }
 
 
 }
 
 
+function select_vpp_device_tags () {
+
+    # Variables read:
+    # - TEST_CODE - String affecting test selection, usually jenkins job name.
+    # - TEST_TAG_STRING - String selecting tags, from gerrit comment.
+    #   Can be unset.
+    # Variables set:
+    # - TAGS - Array of processed tag boolean expressions.
+
+    set -exuo pipefail
+
+    case "${TEST_CODE}" in
+        # Select specific performance tests based on jenkins job type variable.
+        * )
+            if [[ -z "${TEST_TAG_STRING-}" ]]; then
+                # If nothing is specified, we will run pre-selected tests by
+                # following tags. Items of array will be concatenated by OR
+                # in Robot Framework.
+                test_tag_array=()
+            else
+                # If trigger contains tags, split them into array.
+                test_tag_array=(${TEST_TAG_STRING//:/ })
+            fi
+            ;;
+    esac
+
+    TAGS=()
+
+    # We will prefix with devicetest to prevent running other tests
+    # (e.g. Functional).
+    prefix="devicetestAND"
+    if [[ "${TEST_CODE}" == "vpp-"* ]]; then
+        # Automatic prefixing for VPP jobs to limit testing.
+        prefix="${prefix}"
+    fi
+    for tag in "${test_tag_array[@]}"; do
+        if [[ ${tag} == "!"* ]]; then
+            # Exclude tags are not prefixed.
+            TAGS+=("${tag}")
+        else
+            TAGS+=("${prefix}${tag}")
+        fi
+    done
+}
+
 function untrap_and_unreserve_testbed () {
 
     # Use this as a trap function to ensure testbed does not remain reserved.
 function untrap_and_unreserve_testbed () {
 
     # Use this as a trap function to ensure testbed does not remain reserved.
index b2cc3a6..62508e1 100644 (file)
@@ -21,7 +21,7 @@ from ipaddress import ip_address
 from robot.api import logger
 
 from resources.libraries.python.topology import Topology
 from robot.api import logger
 
 from resources.libraries.python.topology import Topology
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 
 
 class Classify(object):
 
 
 class Classify(object):
@@ -289,7 +289,7 @@ class Classify(object):
         err_msg = "Failed to create a classify table on host {host}".format(
             host=node['host'])
 
         err_msg = "Failed to create a classify table on host {host}".format(
             host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
 
         return int(reply["new_table_index"]), int(reply["skip_n_vectors"]),\
             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
 
         return int(reply["new_table_index"]), int(reply["skip_n_vectors"]),\
@@ -355,7 +355,7 @@ class Classify(object):
         err_msg = "Failed to create a classify session on host {host}".format(
             host=node['host'])
 
         err_msg = "Failed to create a classify session on host {host}".format(
             host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -379,7 +379,7 @@ class Classify(object):
         err_msg = "Failed to create a classify session on host {host}".format(
             host=node['host'])
 
         err_msg = "Failed to create a classify session on host {host}".format(
             host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -407,7 +407,7 @@ class Classify(object):
         err_msg = "Failed to set acl list for interface {idx} on host {host}".\
             format(idx=sw_if_index, host=node['host'])
 
         err_msg = "Failed to set acl list for interface {idx} on host {host}".\
             format(idx=sw_if_index, host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -434,7 +434,7 @@ class Classify(object):
         err_msg = "Failed to add/replace acls on host {host}".format(
             host=node['host'])
 
         err_msg = "Failed to add/replace acls on host {host}".format(
             host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -732,7 +732,7 @@ class Classify(object):
         args = dict(
             table_id=int(table_index)
         )
         args = dict(
             table_id=int(table_index)
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
         return reply
 
             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
         return reply
 
@@ -751,7 +751,7 @@ class Classify(object):
         args = dict(
             table_id=int(table_index)
         )
         args = dict(
             table_id=int(table_index)
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details()
 
         return details
             details = papi_exec.add(cmd, **args).get_details()
 
         return details
@@ -764,7 +764,7 @@ class Classify(object):
         :param node: VPP node.
         :type node: dict
         """
         :param node: VPP node.
         :type node: dict
         """
-        PapiExecutor.dump_and_log(node, ["acl_dump", ])
+        PapiSocketExecutor.dump_and_log(node, ["acl_dump", ])
 
     @staticmethod
     def vpp_log_plugin_acl_interface_assignment(node):
 
     @staticmethod
     def vpp_log_plugin_acl_interface_assignment(node):
@@ -774,7 +774,7 @@ class Classify(object):
         :param node: VPP node.
         :type node: dict
         """
         :param node: VPP node.
         :type node: dict
         """
-        PapiExecutor.dump_and_log(node, ["acl_interface_list_dump", ])
+        PapiSocketExecutor.dump_and_log(node, ["acl_interface_list_dump", ])
 
     @staticmethod
     def set_acl_list_for_interface(node, interface, acl_type, acl_idx=None):
 
     @staticmethod
     def set_acl_list_for_interface(node, interface, acl_type, acl_idx=None):
@@ -902,7 +902,7 @@ class Classify(object):
         :param node: VPP node.
         :type node: dict
         """
         :param node: VPP node.
         :type node: dict
         """
-        PapiExecutor.dump_and_log(node, ["macip_acl_dump", ])
+        PapiSocketExecutor.dump_and_log(node, ["macip_acl_dump", ])
 
     @staticmethod
     def add_del_macip_acl_interface(node, interface, action, acl_idx):
 
     @staticmethod
     def add_del_macip_acl_interface(node, interface, action, acl_idx):
@@ -933,7 +933,7 @@ class Classify(object):
             sw_if_index=int(sw_if_index),
             acl_index=int(acl_idx)
         )
             sw_if_index=int(sw_if_index),
             acl_index=int(acl_idx)
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -946,6 +946,6 @@ class Classify(object):
         cmd = 'macip_acl_interface_get'
         err_msg = "Failed to get 'macip_acl_interface' on host {host}".format(
             host=node['host'])
         cmd = 'macip_acl_interface_get'
         err_msg = "Failed to get 'macip_acl_interface' on host {host}".format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add(cmd).get_reply(err_msg)
         logger.info(reply)
             reply = papi_exec.add(cmd).get_reply(err_msg)
         logger.info(reply)
index 2286489..a324465 100644 (file)
@@ -449,6 +449,7 @@ class ContainerEngine(object):
         vpp_config.add_unix_cli_listen()
         vpp_config.add_unix_nodaemon()
         vpp_config.add_unix_exec('/tmp/running.exec')
         vpp_config.add_unix_cli_listen()
         vpp_config.add_unix_nodaemon()
         vpp_config.add_unix_exec('/tmp/running.exec')
+        vpp_config.add_socksvr()
         # We will pop the first core from the list to be a main core
         vpp_config.add_cpu_main_core(str(cpuset_cpus.pop(0)))
         # If more cores in the list, the rest will be used as workers.
         # We will pop the first core from the list to be a main core
         vpp_config.add_cpu_main_core(str(cpuset_cpus.pop(0)))
         # If more cores in the list, the rest will be used as workers.
@@ -498,6 +499,7 @@ class ContainerEngine(object):
         vpp_config.add_unix_cli_listen()
         vpp_config.add_unix_nodaemon()
         vpp_config.add_unix_exec('/tmp/running.exec')
         vpp_config.add_unix_cli_listen()
         vpp_config.add_unix_nodaemon()
         vpp_config.add_unix_exec('/tmp/running.exec')
+        vpp_config.add_socksvr()
         vpp_config.add_plugin('disable', 'dpdk_plugin.so')
 
         # Apply configuration
         vpp_config.add_plugin('disable', 'dpdk_plugin.so')
 
         # Apply configuration
diff --git a/resources/libraries/python/FilteredLogger.py b/resources/libraries/python/FilteredLogger.py
new file mode 100644 (file)
index 0000000..a04eb67
--- /dev/null
@@ -0,0 +1,95 @@
+# Copyright (c) 2019 Cisco and/or its affiliates.
+# 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.
+
+"""Python library for customizing robot.api.logger
+
+As robot.api.logger is a module, it is not easy to copy, edit or inherit from.
+This module offers a class to wrap it.
+The main point of the class is to lower verbosity of Robot logging,
+especially when injected to third party code (such as vpp_papi.VPPApiClient).
+
+Also, String formatting using '%' operator is supported.
+
+Logger.console() is not supported.
+"""
+
+import logging
+
+_LEVELS = {
+    "TRACE": logging.DEBUG // 2,
+    "DEBUG": logging.DEBUG,
+    "INFO": logging.INFO,
+    "HTML": logging.INFO,
+    "WARN": logging.WARN,
+    "ERROR": logging.ERROR,
+    "CRITICAL": logging.CRITICAL,
+    "NONE": logging.CRITICAL,
+}
+
+class FilteredLogger(object):
+    """Instances of this class have the similar API to robot.api.logger.
+
+    TODO: Support html argument?
+    TODO: Support console with a filtering switch?
+    """
+
+    def __init__(self, logger_module, min_level="INFO"):
+        """Remember the values, check min_level is known.
+
+        Use min_level of "CRITICAL" or "NONE" to disable logging entirely.
+
+        :param logger_module: robot.api.logger, or a compatible object.
+        :param min_level: Minimal level to log, lower levels are ignored.
+        :type logger_module: Object with .write(msg, level="INFO") signature.
+        :type min_level: str
+        :raises KeyError: If given min_level is not supported.
+        """
+        self.logger_module = logger_module
+        self.min_level_num = _LEVELS[min_level.upper()]
+
+    def write(self, message, farg=None, level="INFO"):
+        """Forwards the message to logger if min_level is reached.
+
+        Formatting using '%' operator is used when farg argument is suplied.
+
+        :param message: Message to log.
+        :param farg: Value for '%' operator, or None.
+        :param level: Level to possibly log with.
+        :type message: str
+        :type farg: NoneTye, or whatever '%' accepts: str, int, float, dict...
+        :type level: str
+        """
+        if _LEVELS[level.upper()] >= self.min_level_num:
+            if farg is not None:
+                message = message % farg
+            self.logger_module.write(message, level=level)
+
+    def trace(self, message, farg=None):
+        """Forward the message using the ``TRACE`` level."""
+        self.write(message, farg=farg, level="TRACE")
+
+    def debug(self, message, farg=None):
+        """Forward the message using the ``DEBUG`` level."""
+        self.write(message, farg=farg, level="DEBUG")
+
+    def info(self, message, farg=None):
+        """Forward the message using the ``INFO`` level."""
+        self.write(message, farg=farg, level="INFO")
+
+    def warn(self, message, farg=None):
+        """Forward the message using the ``WARN`` level."""
+        self.write(message, farg=farg, level="WARN")
+
+    def error(self, message, farg=None):
+        """Forward the message using the ``ERROR`` level."""
+        self.write(message, farg=farg, level="ERROR")
index 6a8e1a2..0212ead 100644 (file)
 import re
 
 from enum import IntEnum
 import re
 
 from enum import IntEnum
-from ipaddress import ip_address, IPv4Network, IPv6Network
+from ipaddress import ip_address
 
 from resources.libraries.python.Constants import Constants
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 
 from resources.libraries.python.Constants import Constants
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd
 from resources.libraries.python.topology import Topology
 from resources.libraries.python.VatExecutor import VatTerminal
 from resources.libraries.python.ssh import exec_cmd_no_error, exec_cmd
 from resources.libraries.python.topology import Topology
 from resources.libraries.python.VatExecutor import VatTerminal
@@ -112,28 +112,22 @@ class IPUtil(object):
         """
         sw_if_index = InterfaceUtil.get_interface_index(node, interface)
 
         """
         sw_if_index = InterfaceUtil.get_interface_index(node, interface)
 
-        if sw_if_index:
-            is_ipv6 = 1 if ip_version == 'ipv6' else 0
-
-            cmd = 'ip_address_dump'
-            args = dict(sw_if_index=sw_if_index,
-                        is_ipv6=is_ipv6)
-            err_msg = 'Failed to get L2FIB dump on host {host}'.format(
-                host=node['host'])
-
-            with PapiExecutor(node) as papi_exec:
-                details = papi_exec.add(cmd, **args).get_details(err_msg)
-
-            for item in details:
-                item['ip'] = item['prefix'].split('/')[0]
-                item['prefix_length'] = int(item['prefix'].split('/')[1])
-                item['is_ipv6'] = is_ipv6
-                item['netmask'] = \
-                    str(IPv6Network(unicode('::/{pl}'.format(
-                        pl=item['prefix_length']))).netmask) \
-                    if is_ipv6 \
-                    else str(IPv4Network(unicode('0.0.0.0/{pl}'.format(
-                        pl=item['prefix_length']))).netmask)
+        if not sw_if_index:
+            return list()
+
+        is_ipv6 = 1 if ip_version == 'ipv6' else 0
+
+        cmd = 'ip_address_dump'
+        args = dict(sw_if_index=sw_if_index,
+                    is_ipv6=is_ipv6)
+        err_msg = 'Failed to get L2FIB dump on host {host}'.format(
+            host=node['host'])
+
+        with PapiSocketExecutor(node) as papi_exec:
+            details = papi_exec.add(cmd, **args).get_details(err_msg)
+
+        # TODO: CSIT currently looks only whether the list is empty.
+        # Add proper value processing if values become important.
 
         return details
 
 
         return details
 
@@ -145,10 +139,10 @@ class IPUtil(object):
         :type node: dict
         """
 
         :type node: dict
         """
 
-        PapiExecutor.run_cli_cmd(node, 'show ip fib')
-        PapiExecutor.run_cli_cmd(node, 'show ip fib summary')
-        PapiExecutor.run_cli_cmd(node, 'show ip6 fib')
-        PapiExecutor.run_cli_cmd(node, 'show ip6 fib summary')
+        PapiSocketExecutor.run_cli_cmd(node, 'show ip fib')
+        PapiSocketExecutor.run_cli_cmd(node, 'show ip fib summary')
+        PapiSocketExecutor.run_cli_cmd(node, 'show ip6 fib')
+        PapiSocketExecutor.run_cli_cmd(node, 'show ip6 fib summary')
 
     @staticmethod
     def vpp_get_ip_tables_prefix(node, address):
 
     @staticmethod
     def vpp_get_ip_tables_prefix(node, address):
@@ -159,7 +153,7 @@ class IPUtil(object):
         """
         addr = ip_address(unicode(address))
 
         """
         addr = ip_address(unicode(address))
 
-        PapiExecutor.run_cli_cmd(
+        PapiSocketExecutor.run_cli_cmd(
             node, 'show {ip_ver} fib {addr}/{addr_len}'.format(
                 ip_ver='ip6' if addr.version == 6 else 'ip',
                 addr=addr,
             node, 'show {ip_ver} fib {addr}/{addr_len}'.format(
                 ip_ver='ip6' if addr.version == 6 else 'ip',
                 addr=addr,
@@ -188,7 +182,7 @@ class IPUtil(object):
         err_msg = 'Failed to get VRF id assigned to interface {ifc}'.format(
             ifc=interface)
 
         err_msg = 'Failed to get VRF id assigned to interface {ifc}'.format(
             ifc=interface)
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
 
         return reply['vrf_id']
             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
 
         return reply['vrf_id']
@@ -209,7 +203,7 @@ class IPUtil(object):
             loose=0)
         err_msg = 'Failed to enable source check on interface {ifc}'.format(
             ifc=if_name)
             loose=0)
         err_msg = 'Failed to enable source check on interface {ifc}'.format(
             ifc=if_name)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -230,7 +224,7 @@ class IPUtil(object):
         err_msg = 'VPP ip probe {dev} {ip} failed on {h}'.format(
             dev=interface, ip=addr, h=node['host'])
 
         err_msg = 'VPP ip probe {dev} {ip} failed on {h}'.format(
             dev=interface, ip=addr, h=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -407,7 +401,7 @@ class IPUtil(object):
             address=ip_addr.packed)
         err_msg = 'Failed to add IP address on interface {ifc}'.format(
             ifc=interface)
             address=ip_addr.packed)
         err_msg = 'Failed to add IP address on interface {ifc}'.format(
             ifc=interface)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -436,7 +430,7 @@ class IPUtil(object):
             neighbor=neighbor)
         err_msg = 'Failed to add IP neighbor on interface {ifc}'.format(
             ifc=iface_key)
             neighbor=neighbor)
         err_msg = 'Failed to add IP neighbor on interface {ifc}'.format(
             ifc=iface_key)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -583,7 +577,7 @@ class IPUtil(object):
 
         err_msg = 'Failed to add route(s) on host {host}'.format(
             host=node['host'])
 
         err_msg = 'Failed to add route(s) on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             for i in xrange(kwargs.get('count', 1)):
                 args['route']['prefix']['address']['un'] = \
                     IPUtil.union_addr(net_addr + i)
             for i in xrange(kwargs.get('count', 1)):
                 args['route']['prefix']['address']['un'] = \
                     IPUtil.union_addr(net_addr + i)
@@ -608,7 +602,7 @@ class IPUtil(object):
             del_all=1)
         err_msg = 'Failed to flush IP address on interface {ifc}'.format(
             ifc=interface)
             del_all=1)
         err_msg = 'Failed to flush IP address on interface {ifc}'.format(
             ifc=interface)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -631,5 +625,5 @@ class IPUtil(object):
             is_add=1)
         err_msg = 'Failed to add FIB table on host {host}'.format(
             host=node['host'])
             is_add=1)
         err_msg = 'Failed to add FIB table on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
             papi_exec.add(cmd, **args).get_reply(err_msg)
index 78239f9..f28605f 100644 (file)
@@ -20,7 +20,7 @@ from ipaddress import ip_network, ip_address
 
 from enum import Enum, IntEnum
 
 
 from enum import Enum, IntEnum
 
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.topology import Topology
 from resources.libraries.python.VatExecutor import VatExecutor
 from resources.libraries.python.VatJsonUtil import VatJsonUtil
 from resources.libraries.python.topology import Topology
 from resources.libraries.python.VatExecutor import VatExecutor
 from resources.libraries.python.VatJsonUtil import VatJsonUtil
@@ -260,7 +260,7 @@ class IPsecUtil(object):
         err_msg = 'Failed to select IPsec backend on host {host}'.format(
             host=node['host'])
         args = dict(protocol=protocol, index=index)
         err_msg = 'Failed to select IPsec backend on host {host}'.format(
             host=node['host'])
         args = dict(protocol=protocol, index=index)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -273,7 +273,7 @@ class IPsecUtil(object):
 
         err_msg = 'Failed to dump IPsec backends on host {host}'.format(
             host=node['host'])
 
         err_msg = 'Failed to dump IPsec backends on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add('ipsec_backend_dump').get_details(err_msg)
 
     @staticmethod
             papi_exec.add('ipsec_backend_dump').get_details(err_msg)
 
     @staticmethod
index 9138c09..aacf0fb 100644 (file)
@@ -15,7 +15,7 @@
 
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 from resources.libraries.python.IPUtil import IPUtil
 
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 from resources.libraries.python.IPUtil import IPUtil
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.topology import NodeType
 
 
 from resources.libraries.python.topology import NodeType
 
 
@@ -38,7 +38,7 @@ class IPv6Util(object):
         err_msg = 'Failed to suppress ICMPv6 router advertisement message on ' \
                   'interface {ifc}'.format(ifc=interface)
 
         err_msg = 'Failed to suppress ICMPv6 router advertisement message on ' \
                   'interface {ifc}'.format(ifc=interface)
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -60,7 +60,7 @@ class IPv6Util(object):
         err_msg = 'Failed to set router advertisement interval on ' \
                   'interface {ifc}'.format(ifc=interface)
 
         err_msg = 'Failed to set router advertisement interval on ' \
                   'interface {ifc}'.format(ifc=interface)
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
index 0b1f06f..6de17d1 100644 (file)
@@ -24,7 +24,7 @@ from resources.libraries.python.Constants import Constants
 from resources.libraries.python.CpuUtils import CpuUtils
 from resources.libraries.python.DUTSetup import DUTSetup
 from resources.libraries.python.L2Util import L2Util
 from resources.libraries.python.CpuUtils import CpuUtils
 from resources.libraries.python.DUTSetup import DUTSetup
 from resources.libraries.python.L2Util import L2Util
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.parsers.JsonParser import JsonParser
 from resources.libraries.python.ssh import SSH, exec_cmd_no_error
 from resources.libraries.python.topology import NodeType, Topology
 from resources.libraries.python.parsers.JsonParser import JsonParser
 from resources.libraries.python.ssh import SSH, exec_cmd_no_error
 from resources.libraries.python.topology import NodeType, Topology
@@ -138,7 +138,7 @@ class InterfaceUtil(object):
                 host=node['host'])
             args = dict(sw_if_index=sw_if_index,
                         admin_up_down=admin_up_down)
                 host=node['host'])
             args = dict(sw_if_index=sw_if_index,
                         admin_up_down=admin_up_down)
-            with PapiExecutor(node) as papi_exec:
+            with PapiSocketExecutor(node) as papi_exec:
                 papi_exec.add(cmd, **args).get_reply(err_msg)
         elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
             cmd = 'ip link set {ifc} {state}'.format(
                 papi_exec.add(cmd, **args).get_reply(err_msg)
         elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
             cmd = 'ip link set {ifc} {state}'.format(
@@ -210,7 +210,7 @@ class InterfaceUtil(object):
         args = dict(sw_if_index=sw_if_index,
                     mtu=int(mtu))
         try:
         args = dict(sw_if_index=sw_if_index,
                     mtu=int(mtu))
         try:
-            with PapiExecutor(node) as papi_exec:
+            with PapiSocketExecutor(node) as papi_exec:
                 papi_exec.add(cmd, **args).get_reply(err_msg)
         except AssertionError as err:
             # TODO: Make failure tolerance optional.
                 papi_exec.add(cmd, **args).get_reply(err_msg)
         except AssertionError as err:
             # TODO: Make failure tolerance optional.
@@ -321,7 +321,7 @@ class InterfaceUtil(object):
                     name_filter='')
         err_msg = 'Failed to get interface dump on host {host}'.format(
             host=node['host'])
                     name_filter='')
         err_msg = 'Failed to get interface dump on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         def process_if_dump(if_dump):
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         def process_if_dump(if_dump):
@@ -729,7 +729,7 @@ class InterfaceUtil(object):
                     vlan_id=int(vlan))
         err_msg = 'Failed to create VLAN sub-interface on host {host}'.format(
             host=node['host'])
                     vlan_id=int(vlan))
         err_msg = 'Failed to create VLAN sub-interface on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'vlan_subif')
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'vlan_subif')
@@ -771,7 +771,7 @@ class InterfaceUtil(object):
                     vni=int(vni))
         err_msg = 'Failed to create VXLAN tunnel interface on host {host}'.\
             format(host=node['host'])
                     vni=int(vni))
         err_msg = 'Failed to create VXLAN tunnel interface on host {host}'.\
             format(host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'vxlan_tunnel')
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'vxlan_tunnel')
@@ -805,7 +805,7 @@ class InterfaceUtil(object):
         args = dict(sw_if_index=sw_if_index)
         err_msg = 'Failed to get VXLAN dump on host {host}'.format(
             host=node['host'])
         args = dict(sw_if_index=sw_if_index)
         err_msg = 'Failed to get VXLAN dump on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         def process_vxlan_dump(vxlan_dump):
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         def process_vxlan_dump(vxlan_dump):
@@ -853,7 +853,7 @@ class InterfaceUtil(object):
         cmd = 'sw_interface_vhost_user_dump'
         err_msg = 'Failed to get vhost-user dump on host {host}'.format(
             host=node['host'])
         cmd = 'sw_interface_vhost_user_dump'
         err_msg = 'Failed to get vhost-user dump on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd).get_details(err_msg)
 
         def process_vhost_dump(vhost_dump):
             details = papi_exec.add(cmd).get_details(err_msg)
 
         def process_vhost_dump(vhost_dump):
@@ -896,7 +896,7 @@ class InterfaceUtil(object):
         cmd = 'sw_interface_tap_v2_dump'
         err_msg = 'Failed to get TAP dump on host {host}'.format(
             host=node['host'])
         cmd = 'sw_interface_tap_v2_dump'
         err_msg = 'Failed to get TAP dump on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd).get_details(err_msg)
 
         def process_tap_dump(tap_dump):
             details = papi_exec.add(cmd).get_details(err_msg)
 
         def process_tap_dump(tap_dump):
@@ -972,7 +972,7 @@ class InterfaceUtil(object):
             inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0)
         err_msg = 'Failed to create sub-interface on host {host}'.format(
             host=node['host'])
             inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0)
         err_msg = 'Failed to create sub-interface on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'subinterface')
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'subinterface')
@@ -1007,7 +1007,7 @@ class InterfaceUtil(object):
                     tunnel=tunnel)
         err_msg = 'Failed to create GRE tunnel interface on host {host}'.format(
             host=node['host'])
                     tunnel=tunnel)
         err_msg = 'Failed to create GRE tunnel interface on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'gre_tunnel')
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'gre_tunnel')
@@ -1032,7 +1032,7 @@ class InterfaceUtil(object):
         args = dict(mac_address=0)
         err_msg = 'Failed to create loopback interface on host {host}'.format(
             host=node['host'])
         args = dict(mac_address=0)
         err_msg = 'Failed to create loopback interface on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'loopback')
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         if_key = Topology.add_new_port(node, 'loopback')
@@ -1071,7 +1071,7 @@ class InterfaceUtil(object):
                             lb=load_balance.upper())).value)
         err_msg = 'Failed to create bond interface on host {host}'.format(
             host=node['host'])
                             lb=load_balance.upper())).value)
         err_msg = 'Failed to create bond interface on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
@@ -1128,7 +1128,7 @@ class InterfaceUtil(object):
                     txq_size=0)
         err_msg = 'Failed to create AVF interface on host {host}'.format(
             host=node['host'])
                     txq_size=0)
         err_msg = 'Failed to create AVF interface on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
@@ -1160,7 +1160,7 @@ class InterfaceUtil(object):
                   'interface {bond} on host {host}'.format(ifc=interface,
                                                            bond=bond_if,
                                                            host=node['host'])
                   'interface {bond} on host {host}'.format(ifc=interface,
                                                            bond=bond_if,
                                                            host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -1177,7 +1177,7 @@ class InterfaceUtil(object):
             host=node['host'])
 
         data = ('Bond data on node {host}:\n'.format(host=node['host']))
             host=node['host'])
 
         data = ('Bond data on node {host}:\n'.format(host=node['host']))
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd).get_details(err_msg)
 
         for bond in details:
             details = papi_exec.add(cmd).get_details(err_msg)
 
         for bond in details:
@@ -1220,7 +1220,7 @@ class InterfaceUtil(object):
         err_msg = 'Failed to get slave dump on host {host}'.format(
             host=node['host'])
 
         err_msg = 'Failed to get slave dump on host {host}'.format(
             host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         def process_slave_dump(slave_dump):
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         def process_slave_dump(slave_dump):
@@ -1281,7 +1281,7 @@ class InterfaceUtil(object):
             is_add=1)
         err_msg = 'Failed to enable input acl on interface {ifc}'.format(
             ifc=interface)
             is_add=1)
         err_msg = 'Failed to enable input acl on interface {ifc}'.format(
             ifc=interface)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -1306,7 +1306,7 @@ class InterfaceUtil(object):
         args = dict(sw_if_index=sw_if_index)
         err_msg = 'Failed to get classify table name by interface {ifc}'.format(
             ifc=interface)
         args = dict(sw_if_index=sw_if_index)
         err_msg = 'Failed to get classify table name by interface {ifc}'.format(
             ifc=interface)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
 
         return reply
             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
 
         return reply
@@ -1350,7 +1350,7 @@ class InterfaceUtil(object):
         args = dict(sw_if_index=sw_if_index)
         err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format(
             host=node['host'])
         args = dict(sw_if_index=sw_if_index)
         err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         def process_vxlan_gpe_dump(vxlan_dump):
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         def process_vxlan_gpe_dump(vxlan_dump):
@@ -1405,7 +1405,7 @@ class InterfaceUtil(object):
             vrf_id=int(table_id))
         err_msg = 'Failed to assign interface {ifc} to FIB table'.format(
             ifc=interface)
             vrf_id=int(table_id))
         err_msg = 'Failed to assign interface {ifc} to FIB table'.format(
             ifc=interface)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -1563,7 +1563,7 @@ class InterfaceUtil(object):
         cmd = 'sw_interface_rx_placement_dump'
         err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format(
             cmd=cmd, host=node['host'])
         cmd = 'sw_interface_rx_placement_dump'
         err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format(
             cmd=cmd, host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             for ifc in node['interfaces'].values():
                 if ifc['vpp_sw_index'] is not None:
                     papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index'])
             for ifc in node['interfaces'].values():
                 if ifc['vpp_sw_index'] is not None:
                     papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index'])
@@ -1591,7 +1591,7 @@ class InterfaceUtil(object):
                   "{host}!".format(host=node['host'])
         args = dict(sw_if_index=sw_if_index, queue_id=queue_id,
                     worker_id=worker_id)
                   "{host}!".format(host=node['host'])
         args = dict(sw_if_index=sw_if_index, queue_id=queue_id,
                     worker_id=worker_id)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
index 60e1286..029d635 100644 (file)
@@ -483,6 +483,7 @@ class KubernetesUtils(object):
         vpp_config.set_node(kwargs['node'])
         vpp_config.add_unix_cli_listen(value='0.0.0.0:5002')
         vpp_config.add_unix_nodaemon()
         vpp_config.set_node(kwargs['node'])
         vpp_config.add_unix_cli_listen(value='0.0.0.0:5002')
         vpp_config.add_unix_nodaemon()
+        vpp_config.add_socksvr()
         vpp_config.add_heapsize('4G')
         vpp_config.add_ip_heap_size('4G')
         vpp_config.add_ip6_heap_size('4G')
         vpp_config.add_heapsize('4G')
         vpp_config.add_ip_heap_size('4G')
         vpp_config.add_ip6_heap_size('4G')
@@ -528,6 +529,7 @@ class KubernetesUtils(object):
         vpp_config.set_node(kwargs['node'])
         vpp_config.add_unix_cli_listen(value='0.0.0.0:5002')
         vpp_config.add_unix_nodaemon()
         vpp_config.set_node(kwargs['node'])
         vpp_config.add_unix_cli_listen(value='0.0.0.0:5002')
         vpp_config.add_unix_nodaemon()
+        vpp_config.add_socksvr()
         # We will pop first core from list to be main core
         vpp_config.add_cpu_main_core(str(cpuset_main.pop(0)))
         # if this is not only core in list, the rest will be used as workers.
         # We will pop first core from list to be main core
         vpp_config.add_cpu_main_core(str(cpuset_main.pop(0)))
         # if this is not only core in list, the rest will be used as workers.
index 7c575a2..4ca0c47 100644 (file)
@@ -19,7 +19,7 @@ from textwrap import wrap
 from enum import IntEnum
 
 from resources.libraries.python.Constants import Constants
 from enum import IntEnum
 
 from resources.libraries.python.Constants import Constants
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.topology import Topology
 from resources.libraries.python.ssh import exec_cmd_no_error
 
 from resources.libraries.python.topology import Topology
 from resources.libraries.python.ssh import exec_cmd_no_error
 
@@ -129,7 +129,7 @@ class L2Util(object):
                     static_mac=int(static_mac),
                     filter_mac=int(filter_mac),
                     bvi_mac=int(bvi_mac))
                     static_mac=int(static_mac),
                     filter_mac=int(filter_mac),
                     bvi_mac=int(bvi_mac))
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -168,7 +168,7 @@ class L2Util(object):
                     learn=int(learn),
                     arp_term=int(arp_term),
                     is_add=1)
                     learn=int(learn),
                     arp_term=int(arp_term),
                     is_add=1)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -200,7 +200,7 @@ class L2Util(object):
                     shg=int(shg),
                     port_type=int(port_type),
                     enable=1)
                     shg=int(shg),
                     port_type=int(port_type),
                     enable=1)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -248,7 +248,7 @@ class L2Util(object):
         err_msg = 'Failed to add L2 bridge domain with 2 interfaces on host' \
                   ' {host}'.format(host=node['host'])
 
         err_msg = 'Failed to add L2 bridge domain with 2 interfaces on host' \
                   ' {host}'.format(host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd1, **args1).add(cmd2, **args2).add(cmd2, **args3)
             papi_exec.get_replies(err_msg)
 
             papi_exec.add(cmd1, **args1).add(cmd2, **args2).add(cmd2, **args3)
             papi_exec.get_replies(err_msg)
 
@@ -285,7 +285,7 @@ class L2Util(object):
         err_msg = 'Failed to add L2 cross-connect between two interfaces on' \
                   ' host {host}'.format(host=node['host'])
 
         err_msg = 'Failed to add L2 cross-connect between two interfaces on' \
                   ' host {host}'.format(host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args1).add(cmd, **args2).get_replies(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args1).add(cmd, **args2).get_replies(err_msg)
 
     @staticmethod
@@ -321,7 +321,7 @@ class L2Util(object):
         err_msg = 'Failed to add L2 patch between two interfaces on' \
                   ' host {host}'.format(host=node['host'])
 
         err_msg = 'Failed to add L2 patch between two interfaces on' \
                   ' host {host}'.format(host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args1).add(cmd, **args2).get_replies(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args1).add(cmd, **args2).get_replies(err_msg)
 
     @staticmethod
@@ -391,18 +391,14 @@ class L2Util(object):
         args = dict(bd_id=int(bd_id))
         err_msg = 'Failed to get L2FIB dump on host {host}'.format(
             host=node['host'])
         args = dict(bd_id=int(bd_id))
         err_msg = 'Failed to get L2FIB dump on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
-        bd_data = list() if bd_id == Constants.BITWISE_NON_ZERO else dict()
+        if bd_id == Constants.BITWISE_NON_ZERO:
+            return details
         for bridge_domain in details:
         for bridge_domain in details:
-            if bd_id == Constants.BITWISE_NON_ZERO:
-                bd_data.append(bridge_domain)
-            else:
-                if bridge_domain['bd_id'] == bd_id:
-                    return bridge_domain
-
-        return bd_data
+            if bridge_domain['bd_id'] == bd_id:
+                return bridge_domain
 
     @staticmethod
     def l2_vlan_tag_rewrite(node, interface, tag_rewrite_method,
 
     @staticmethod
     def l2_vlan_tag_rewrite(node, interface, tag_rewrite_method,
@@ -444,7 +440,7 @@ class L2Util(object):
                     tag2=tag2_id)
         err_msg = 'Failed to set VLAN TAG rewrite on host {host}'.format(
             host=node['host'])
                     tag2=tag2_id)
         err_msg = 'Failed to set VLAN TAG rewrite on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -463,7 +459,7 @@ class L2Util(object):
         args = dict(bd_id=int(bd_id))
         err_msg = 'Failed to get L2FIB dump on host {host}'.format(
             host=node['host'])
         args = dict(bd_id=int(bd_id))
         err_msg = 'Failed to get L2FIB dump on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         for fib_item in details:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         for fib_item in details:
index bb4cf79..f9a7b94 100644 (file)
@@ -40,7 +40,7 @@ __all__ = ["run"]
 MESSAGE_TEMPLATE = "Command {com} ended with RC {ret} and output:\n{out}"
 
 
 MESSAGE_TEMPLATE = "Command {com} ended with RC {ret} and output:\n{out}"
 
 
-def run(command, msg="", check=False, log=True, console=False):
+def run(command, msg="", check=True, log=False, console=False):
     """Wrapper around subprocess.check_output that can tolerates nonzero RCs.
 
     Stderr is redirected to stdout, so it is part of output
     """Wrapper around subprocess.check_output that can tolerates nonzero RCs.
 
     Stderr is redirected to stdout, so it is part of output
index 5c38ec4..34cf6ce 100644 (file)
@@ -18,7 +18,7 @@ from enum import IntEnum
 from robot.api import logger
 
 from resources.libraries.python.topology import NodeType, Topology
 from robot.api import logger
 
 from resources.libraries.python.topology import NodeType, Topology
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.L2Util import L2Util
 
 
 from resources.libraries.python.L2Util import L2Util
 
 
@@ -43,7 +43,7 @@ class Memif(object):
         :returns: List of memif interfaces extracted from Papi response.
         :rtype: list
         """
         :returns: List of memif interfaces extracted from Papi response.
         :rtype: list
         """
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add("memif_dump").get_details()
 
         for memif in details:
             details = papi_exec.add("memif_dump").get_details()
 
         for memif in details:
@@ -78,7 +78,7 @@ class Memif(object):
             socket_id=int(sid),
             socket_filename=str('/tmp/' + filename)
         )
             socket_id=int(sid),
             socket_filename=str('/tmp/' + filename)
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             return papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
             return papi_exec.add(cmd, **args).get_reply(err_msg)
 
     @staticmethod
@@ -110,7 +110,7 @@ class Memif(object):
             socket_id=int(sid),
             id=int(mid)
         )
             socket_id=int(sid),
             id=int(mid)
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             return papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
     @staticmethod
             return papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
     @staticmethod
index 5c0278d..f018d38 100644 (file)
@@ -21,7 +21,7 @@ from enum import IntEnum
 from robot.api import logger
 
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 from robot.api import logger
 
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 
 
 class NATConfigFlags(IntEnum):
 
 
 class NATConfigFlags(IntEnum):
@@ -65,7 +65,7 @@ class NATUtil(object):
             is_add=1,
             flags=getattr(NATConfigFlags, "NAT_IS_INSIDE").value
         )
             is_add=1,
             flags=getattr(NATConfigFlags, "NAT_IS_INSIDE").value
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args_in).get_reply(err_msg)
 
         int_out_idx = InterfaceUtil.get_sw_if_index(node, int_out)
             papi_exec.add(cmd, **args_in).get_reply(err_msg)
 
         int_out_idx = InterfaceUtil.get_sw_if_index(node, int_out)
@@ -76,7 +76,7 @@ class NATUtil(object):
             is_add=1,
             flags=getattr(NATConfigFlags, "NAT_IS_OUTSIDE").value
         )
             is_add=1,
             flags=getattr(NATConfigFlags, "NAT_IS_OUTSIDE").value
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args_in).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args_in).get_reply(err_msg)
 
     @staticmethod
@@ -105,7 +105,7 @@ class NATUtil(object):
             out_addr=inet_pton(AF_INET, str(ip_out)),
             out_plen=int(subnet_out)
         )
             out_addr=inet_pton(AF_INET, str(ip_out)),
             out_plen=int(subnet_out)
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args_in).get_reply(err_msg)
 
     @staticmethod
             papi_exec.add(cmd, **args_in).get_reply(err_msg)
 
     @staticmethod
@@ -131,7 +131,7 @@ class NATUtil(object):
         cmd = 'nat_show_config'
         err_msg = 'Failed to get NAT configuration on host {host}'.\
             format(host=node['host'])
         cmd = 'nat_show_config'
         err_msg = 'Failed to get NAT configuration on host {host}'.\
             format(host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add(cmd).get_reply(err_msg)
         logger.debug("NAT Configuration:\n{reply}".format(reply=pformat(reply)))
 
             reply = papi_exec.add(cmd).get_reply(err_msg)
         logger.debug("NAT Configuration:\n{reply}".format(reply=pformat(reply)))
 
@@ -145,4 +145,4 @@ class NATUtil(object):
             "nat44_user_session_dump",
             "nat_det_map_dump"
         ]
             "nat44_user_session_dump",
             "nat_det_map_dump"
         ]
-        PapiExecutor.dump_and_log(node, cmds)
+        PapiSocketExecutor.dump_and_log(node, cmds)
index 7c8b2d0..7163d05 100644 (file)
@@ -36,19 +36,21 @@ class OptionString(object):
     the best fitting one, without much logic near the call site.
     """
 
     the best fitting one, without much logic near the call site.
     """
 
-    def __init__(self, prefix="", *args):
+    def __init__(self, parts=tuple(), prefix=""):
         """Create instance with listed strings as parts to use.
 
         Prefix will be converted to string and stripped.
         The typical (nonempty) prefix values are "-" and "--".
 
         """Create instance with listed strings as parts to use.
 
         Prefix will be converted to string and stripped.
         The typical (nonempty) prefix values are "-" and "--".
 
+        TODO: Support users calling with parts being a string?
+
+        :param parts: List of of stringifiable objects to become parts.
         :param prefix: Subtring to prepend to every parameter (not value).
         :param prefix: Subtring to prepend to every parameter (not value).
-        :param args: List of positional arguments to become parts.
+        :type parts: Iterable of object
         :type prefix: object
         :type prefix: object
-        :type args: list of object
         """
         """
+        self.parts = [str(part) for part in parts]
         self.prefix = str(prefix).strip()  # Not worth to call change_prefix.
         self.prefix = str(prefix).strip()  # Not worth to call change_prefix.
-        self.parts = list(args)
 
     def __repr__(self):
         """Return string executable as Python constructor call.
 
     def __repr__(self):
         """Return string executable as Python constructor call.
@@ -56,12 +58,11 @@ class OptionString(object):
         :returns: Executable constructor call as string.
         :rtype: str
         """
         :returns: Executable constructor call as string.
         :rtype: str
         """
-        return "".join([
-            "OptionString(prefix=", repr(self.prefix), ",",
-            repr(self.parts)[1:-1], ")"])
+        return "OptionString(parts={parts!r},prefix={prefix!r})".format(
+            parts=self.parts, prefix=self.prefix)
 
     # TODO: Would we ever need a copy() method?
 
     # TODO: Would we ever need a copy() method?
-    # Currently, supersting "master" is mutable but unique,
+    # Currently, superstring "master" is mutable but unique,
     # substring "slave" can be used to extend, but does not need to be mutated.
 
     def change_prefix(self, prefix):
     # substring "slave" can be used to extend, but does not need to be mutated.
 
     def change_prefix(self, prefix):
index 9ca34d8..0a009b3 100644 (file)
 """
 
 import binascii
 """
 
 import binascii
+import glob
 import json
 import json
+import shutil
+import subprocess
+import sys
+import tempfile
+import time
 
 from pprint import pformat
 from robot.api import logger
 
 from resources.libraries.python.Constants import Constants
 
 from pprint import pformat
 from robot.api import logger
 
 from resources.libraries.python.Constants import Constants
-from resources.libraries.python.PapiHistory import PapiHistory
+from resources.libraries.python.LocalExecution import run
+from resources.libraries.python.FilteredLogger import FilteredLogger
 from resources.libraries.python.PythonThree import raise_from
 from resources.libraries.python.PythonThree import raise_from
-from resources.libraries.python.ssh import SSH, SSHTimeout
+from resources.libraries.python.PapiHistory import PapiHistory
+from resources.libraries.python.ssh import (
+    SSH, SSHTimeout, exec_cmd_no_error, scp_node)
 
 
 
 
-__all__ = ["PapiExecutor"]
+__all__ = ["PapiExecutor", "PapiSocketExecutor"]
 
 
 
 
-class PapiExecutor(object):
-    """Contains methods for executing VPP Python API commands on DUTs.
+def dictize(obj):
+    """A helper method, to make namedtuple-like object accessible as dict.
+
+    If the object is namedtuple-like, its _asdict() form is returned,
+    but in the returned object __getitem__ method is wrapped
+    to dictize also any items returned.
+    If the object does not have _asdict, it will be returned without any change.
+    Integer keys still access the object as tuple.
+
+    A more useful version would be to keep obj mostly as a namedtuple,
+    just add getitem for string keys. Unfortunately, namedtuple inherits
+    from tuple, including its read-only __getitem__ attribute,
+    so we cannot monkey-patch it.
+
+    TODO: Create a proxy for namedtuple to allow that.
+
+    :param obj: Arbitrary object to dictize.
+    :type obj: object
+    :returns: Dictized object.
+    :rtype: same as obj type or collections.OrderedDict
+    """
+    if not hasattr(obj, "_asdict"):
+        return obj
+    ret = obj._asdict()
+    old_get = ret.__getitem__
+    new_get = lambda self, key: dictize(old_get(self, key))
+    ret.__getitem__ = new_get
+    return ret
+
+class PapiSocketExecutor(object):
+    """Methods for executing VPP Python API commands on forwarded socket.
+
+    The current implementation connects for the duration of resource manager.
+    Delay for accepting connection is 10s, and disconnect is explicit.
+    TODO: Decrease 10s to value that is long enough for creating connection
+    and short enough to not affect performance.
+
+    The current implementation downloads and parses .api.json files only once
+    and stores a VPPApiClient instance (disconnected) as a class variable.
+    Accessing multiple nodes with different APIs is therefore not supported.
+
+    The current implementation seems to run into read error occasionally.
+    Not sure if the error is in Python code on Robot side, ssh forwarding,
+    or socket handling at VPP side. Anyway, reconnect after some sleep
+    seems to help, hoping repeated command execution does not lead to surprises.
+    The reconnection is logged at WARN level, so it is prominently shown
+    in log.html, so we can see how frequently it happens.
+
+    TODO: Support sockets in NFs somehow.
+    TODO: Support handling of retval!=0 without try/except in caller.
 
     Note: Use only with "with" statement, e.g.:
 
 
     Note: Use only with "with" statement, e.g.:
 
-        with PapiExecutor(node) as papi_exec:
-            replies = papi_exec.add('show_version').get_replies(err_msg)
+        with PapiSocketExecutor(node) as papi_exec:
+            reply = papi_exec.add('show_version').get_reply(err_msg)
 
 
-    This class processes three classes of VPP PAPI methods:
-    1. simple request / reply: method='request',
-    2. dump functions: method='dump',
-    3. vpp-stats: method='stats'.
+    This class processes two classes of VPP PAPI methods:
+    1. Simple request / reply: method='request'.
+    2. Dump functions: method='dump'.
+
+    Note that access to VPP stats over socket is not supported yet.
 
     The recommended ways of use are (examples):
 
 
     The recommended ways of use are (examples):
 
@@ -48,88 +106,214 @@ class PapiExecutor(object):
 
     a. One request with no arguments:
 
 
     a. One request with no arguments:
 
-        with PapiExecutor(node) as papi_exec:
-            reply = papi_exec.add('show_version').get_reply()
+        with PapiSocketExecutor(node) as papi_exec:
+            reply = papi_exec.add('show_version').get_reply(err_msg)
 
     b. Three requests with arguments, the second and the third ones are the same
        but with different arguments.
 
 
     b. Three requests with arguments, the second and the third ones are the same
        but with different arguments.
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             replies = papi_exec.add(cmd1, **args1).add(cmd2, **args2).\
                 add(cmd2, **args3).get_replies(err_msg)
 
     2. Dump functions
 
         cmd = 'sw_interface_rx_placement_dump'
             replies = papi_exec.add(cmd1, **args1).add(cmd2, **args2).\
                 add(cmd2, **args3).get_replies(err_msg)
 
     2. Dump functions
 
         cmd = 'sw_interface_rx_placement_dump'
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index']).\
                 get_details(err_msg)
             details = papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index']).\
                 get_details(err_msg)
+    """
 
 
-    3. vpp-stats
-
-        path = ['^/if', '/err/ip4-input', '/sys/node/ip4-input']
-
-        with PapiExecutor(node) as papi_exec:
-            stats = papi_exec.add(api_name='vpp-stats', path=path).get_stats()
-
-        print('RX interface core 0, sw_if_index 0:\n{0}'.\
-            format(stats[0]['/if/rx'][0][0]))
+    # Class cache for reuse between instances.
+    cached_vpp_instance = None
 
 
-        or
+    def __init__(self, node, remote_vpp_socket="/run/vpp-api.sock"):
+        """Store the given arguments, declare managed variables.
 
 
-        path_1 = ['^/if', ]
-        path_2 = ['^/if', '/err/ip4-input', '/sys/node/ip4-input']
+        :param node: Node to connect to and forward unix domain socket from.
+        :param remote_vpp_socket: Path to remote socket to tunnel to.
+        :type node: dict
+        :type remote_vpp_socket: str
+        """
+        self._node = node
+        self._remote_vpp_socket = remote_vpp_socket
+        # The list of PAPI commands to be executed on the node.
+        self._api_command_list = list()
+        # The following values are set on enter, reset on exit.
+        self._temp_dir = None
+        self._ssh_control_socket = None
+        self._local_vpp_socket = None
 
 
-        with PapiExecutor(node) as papi_exec:
-            stats = papi_exec.add('vpp-stats', path=path_1).\
-                add('vpp-stats', path=path_2).get_stats()
+    @property
+    def vpp_instance(self):
+        """Return VPP instance with bindings to all API calls.
 
 
-        print('RX interface core 0, sw_if_index 0:\n{0}'.\
-            format(stats[1]['/if/rx'][0][0]))
+        The returned instance is initialized for unix domain socket access,
+        it has initialized all the bindings, but it is not connected
+        (to local socket) yet.
 
 
-        Note: In this case, when PapiExecutor method 'add' is used:
-        - its parameter 'csit_papi_command' is used only to keep information
-          that vpp-stats are requested. It is not further processed but it is
-          included in the PAPI history this way:
-          vpp-stats(path=['^/if', '/err/ip4-input', '/sys/node/ip4-input'])
-          Always use csit_papi_command="vpp-stats" if the VPP PAPI method
-          is "stats".
-        - the second parameter must be 'path' as it is used by PapiExecutor
-          method 'add'.
-    """
+        First invocation downloads .api.json files from self._node
+        into a temporary directory.
 
 
-    def __init__(self, node):
-        """Initialization.
+        After first invocation, the result is cached, so other calls are quick.
+        Class variable is used as the cache, but this property is defined as
+        an instance method, so that _node (for api files) is known.
 
 
-        :param node: Node to run command(s) on.
-        :type node: dict
+        :returns: Initialized but not connected VPP instance.
+        :rtype: vpp_papi.VPPApiClient
         """
         """
+        cls = self.__class__
+        if cls.cached_vpp_instance is not None:
+            return cls.cached_vpp_instance
+        tmp_dir = tempfile.mkdtemp(dir="/tmp")
+        package_path = "Not set yet."
+        try:
+            # Pack, copy and unpack Python part of VPP installation from _node.
+            # TODO: Use rsync or recursive version of ssh.scp_node instead?
+            node = self._node
+            exec_cmd_no_error(node, ["rm", "-rf", "/tmp/papi.txz"])
+            # Papi python version depends on OS (and time).
+            # Python 2.7 or 3.4, site-packages or dist-packages.
+            installed_papi_glob = "/usr/lib/python*/*-packages/vpp_papi"
+            # We need to wrap this command in bash, in order to expand globs,
+            # and as ssh does join, the inner command has to be quoted.
+            inner_cmd = " ".join([
+                "tar", "cJf", "/tmp/papi.txz", "--exclude=*.pyc",
+                installed_papi_glob, "/usr/share/vpp/api"])
+            exec_cmd_no_error(node, ["bash", "-c", "'" + inner_cmd + "'"])
+            scp_node(node, tmp_dir + "/papi.txz", "/tmp/papi.txz", get=True)
+            run(["tar", "xf", tmp_dir + "/papi.txz", "-C", tmp_dir])
+            # When present locally, we finally can find the installation path.
+            package_path = glob.glob(tmp_dir + installed_papi_glob)[0]
+            # Package path has to be one level above the vpp_papi directory.
+            package_path = package_path.rsplit('/', 1)[0]
+            sys.path.append(package_path)
+            from vpp_papi.vpp_papi import VPPApiClient as vpp_class
+            vpp_class.apidir = tmp_dir + "/usr/share/vpp/api"
+            # We need to create instance before removing from sys.path.
+            cls.cached_vpp_instance = vpp_class(
+                use_socket=True, server_address="TBD", async_thread=False,
+                read_timeout=6, logger=FilteredLogger(logger, "INFO"))
+            # Cannot use loglevel parameter, robot.api.logger lacks support.
+            # TODO: Stop overriding read_timeout when VPP-1722 is fixed.
+        finally:
+            shutil.rmtree(tmp_dir)
+            if sys.path[-1] == package_path:
+                sys.path.pop()
+        return cls.cached_vpp_instance
 
 
-        # Node to run command(s) on.
-        self._node = node
-
-        # The list of PAPI commands to be executed on the node.
-        self._api_command_list = list()
+    def __enter__(self):
+        """Create a tunnel, connect VPP instance.
 
 
-        self._ssh = SSH()
+        Only at this point a local socket names are created
+        in a temporary directory, because VIRL runs 3 pybots at once,
+        so harcoding local filenames does not work.
 
 
-    def __enter__(self):
-        try:
-            self._ssh.connect(self._node)
-        except IOError:
-            raise RuntimeError("Cannot open SSH connection to host {host} to "
-                               "execute PAPI command(s)".
-                               format(host=self._node["host"]))
+        :returns: self
+        :rtype: PapiSocketExecutor
+        """
+        # Parsing takes longer than connecting, prepare instance before tunnel.
+        vpp_instance = self.vpp_instance
+        node = self._node
+        self._temp_dir = tempfile.mkdtemp(dir="/tmp")
+        self._local_vpp_socket = self._temp_dir + "/vpp-api.sock"
+        self._ssh_control_socket = self._temp_dir + "/ssh.sock"
+        ssh_socket = self._ssh_control_socket
+        # Cleanup possibilities.
+        ret_code, _ = run(["ls", ssh_socket], check=False)
+        if ret_code != 2:
+            # This branch never seems to be hit in CI,
+            # but may be useful when testing manually.
+            run(["ssh", "-S", ssh_socket, "-O", "exit", "0.0.0.0"],
+                check=False, log=True)
+            # TODO: Is any sleep necessary? How to prove if not?
+            run(["sleep", "0.1"])
+            run(["rm", "-vrf", ssh_socket])
+        # Even if ssh can perhaps reuse this file,
+        # we need to remove it for readiness detection to work correctly.
+        run(["rm", "-rvf", self._local_vpp_socket])
+        # On VIRL, the ssh user is not added to "vpp" group,
+        # so we need to change remote socket file access rights.
+        exec_cmd_no_error(
+            node, "chmod o+rwx " + self._remote_vpp_socket, sudo=True)
+        # We use sleep command. The ssh command will exit in 10 second,
+        # unless a local socket connection is established,
+        # in which case the ssh command will exit only when
+        # the ssh connection is closed again (via control socket).
+        # The log level is to supress "Warning: Permanently added" messages.
+        ssh_cmd = [
+            "ssh", "-S", ssh_socket, "-M",
+            "-o", "LogLevel=ERROR", "-o", "UserKnownHostsFile=/dev/null",
+            "-o", "StrictHostKeyChecking=no", "-o", "ExitOnForwardFailure=yes",
+            "-L", self._local_vpp_socket + ':' + self._remote_vpp_socket,
+            "-p", str(node['port']), node['username'] + "@" + node['host'],
+            "sleep", "10"]
+        priv_key = node.get("priv_key")
+        if priv_key:
+            # This is tricky. We need a file to pass the value to ssh command.
+            # And we need ssh command, because paramiko does not suport sockets
+            # (neither ssh_socket, nor _remote_vpp_socket).
+            key_file = tempfile.NamedTemporaryFile()
+            key_file.write(priv_key)
+            # Make sure the content is written, but do not close yet.
+            key_file.flush()
+            ssh_cmd[1:1] = ["-i", key_file.name]
+        password = node.get("password")
+        if password:
+            # Prepend sshpass command to set password.
+            ssh_cmd[:0] = ["sshpass", "-p", password]
+        time_stop = time.time() + 10.0
+        # subprocess.Popen seems to be the best way to run commands
+        # on background. Other ways (shell=True with "&" and ssh with -f)
+        # seem to be too dependent on shell behavior.
+        # In particular, -f does NOT return values for run().
+        subprocess.Popen(ssh_cmd)
+        # Check socket presence on local side.
+        while time.time() < time_stop:
+            # It can take a moment for ssh to create the socket file.
+            ret_code, _ = run(["ls", "-l", self._local_vpp_socket], check=False)
+            if not ret_code:
+                break
+            time.sleep(0.1)
+        else:
+            raise RuntimeError("Local side socket has not appeared.")
+        if priv_key:
+            # Socket up means the key has been read. Delete file by closing it.
+            key_file.close()
+        # Everything is ready, set the local socket address and connect.
+        vpp_instance.transport.server_address = self._local_vpp_socket
+        # It seems we can get read error even if every preceding check passed.
+        # Single retry seems to help.
+        for _ in xrange(2):
+            try:
+                vpp_instance.connect_sync("csit_socket")
+            except IOError as err:
+                logger.warn("Got initial connect error {err!r}".format(err=err))
+                vpp_instance.disconnect()
+            else:
+                break
+        else:
+            raise RuntimeError("Failed to connect to VPP over a socket.")
         return self
 
     def __exit__(self, exc_type, exc_val, exc_tb):
         return self
 
     def __exit__(self, exc_type, exc_val, exc_tb):
-        self._ssh.disconnect(self._node)
+        """Disconnect the vpp instance, tear down the SHH tunnel.
 
 
-    def add(self, csit_papi_command="vpp-stats", history=True, **kwargs):
+        Also remove the local sockets by deleting the temporary directory.
+        Arguments related to possible exception are entirely ignored.
+        """
+        self.vpp_instance.disconnect()
+        run(["ssh", "-S", self._ssh_control_socket, "-O", "exit", "0.0.0.0"],
+            check=False)
+        shutil.rmtree(self._temp_dir)
+        return
+
+    def add(self, csit_papi_command, history=True, **kwargs):
         """Add next command to internal command list; return self.
 
         The argument name 'csit_papi_command' must be unique enough as it cannot
         be repeated in kwargs.
         """Add next command to internal command list; return self.
 
         The argument name 'csit_papi_command' must be unique enough as it cannot
         be repeated in kwargs.
+        Unless disabled, new entry to papi history is also added at this point.
 
         :param csit_papi_command: VPP API command.
         :param history: Enable/disable adding command to PAPI command history.
 
         :param csit_papi_command: VPP API command.
         :param history: Enable/disable adding command to PAPI command history.
@@ -138,51 +322,30 @@ class PapiExecutor(object):
         :type history: bool
         :type kwargs: dict
         :returns: self, so that method chaining is possible.
         :type history: bool
         :type kwargs: dict
         :returns: self, so that method chaining is possible.
-        :rtype: PapiExecutor
+        :rtype: PapiSocketExecutor
         """
         if history:
             PapiHistory.add_to_papi_history(
                 self._node, csit_papi_command, **kwargs)
         """
         if history:
             PapiHistory.add_to_papi_history(
                 self._node, csit_papi_command, **kwargs)
-        self._api_command_list.append(dict(api_name=csit_papi_command,
-                                           api_args=kwargs))
+        self._api_command_list.append(
+            dict(api_name=csit_papi_command, api_args=kwargs))
         return self
 
         return self
 
-    def get_stats(self, err_msg="Failed to get statistics.", timeout=120):
-        """Get VPP Stats from VPP Python API.
-
-        :param err_msg: The message used if the PAPI command(s) execution fails.
-        :param timeout: Timeout in seconds.
-        :type err_msg: str
-        :type timeout: int
-        :returns: Requested VPP statistics.
-        :rtype: list of dict
-        """
-
-        paths = [cmd['api_args']['path'] for cmd in self._api_command_list]
-        self._api_command_list = list()
-
-        stdout = self._execute_papi(
-            paths, method='stats', err_msg=err_msg, timeout=timeout)
-
-        return json.loads(stdout)
-
-    def get_replies(self, err_msg="Failed to get replies.", timeout=120):
+    def get_replies(self, err_msg="Failed to get replies."):
         """Get replies from VPP Python API.
 
         The replies are parsed into dict-like objects,
         "retval" field is guaranteed to be zero on success.
 
         :param err_msg: The message used if the PAPI command(s) execution fails.
         """Get replies from VPP Python API.
 
         The replies are parsed into dict-like objects,
         "retval" field is guaranteed to be zero on success.
 
         :param err_msg: The message used if the PAPI command(s) execution fails.
-        :param timeout: Timeout in seconds.
         :type err_msg: str
         :type err_msg: str
-        :type timeout: int
         :returns: Responses, dict objects with fields due to API and "retval".
         :rtype: list of dict
         :raises RuntimeError: If retval is nonzero, parsing or ssh error.
         """
         :returns: Responses, dict objects with fields due to API and "retval".
         :rtype: list of dict
         :raises RuntimeError: If retval is nonzero, parsing or ssh error.
         """
-        return self._execute(method='request', err_msg=err_msg, timeout=timeout)
+        return self._execute(err_msg=err_msg)
 
 
-    def get_reply(self, err_msg="Failed to get reply.", timeout=120):
+    def get_reply(self, err_msg="Failed to get reply."):
         """Get reply from VPP Python API.
 
         The reply is parsed into dict-like object,
         """Get reply from VPP Python API.
 
         The reply is parsed into dict-like object,
@@ -191,20 +354,18 @@ class PapiExecutor(object):
         TODO: Discuss exception types to raise, unify with inner methods.
 
         :param err_msg: The message used if the PAPI command(s) execution fails.
         TODO: Discuss exception types to raise, unify with inner methods.
 
         :param err_msg: The message used if the PAPI command(s) execution fails.
-        :param timeout: Timeout in seconds.
         :type err_msg: str
         :type err_msg: str
-        :type timeout: int
         :returns: Response, dict object with fields due to API and "retval".
         :rtype: dict
         :raises AssertionError: If retval is nonzero, parsing or ssh error.
         """
         :returns: Response, dict object with fields due to API and "retval".
         :rtype: dict
         :raises AssertionError: If retval is nonzero, parsing or ssh error.
         """
-        replies = self.get_replies(err_msg=err_msg, timeout=timeout)
+        replies = self.get_replies(err_msg=err_msg)
         if len(replies) != 1:
             raise RuntimeError("Expected single reply, got {replies!r}".format(
                 replies=replies))
         return replies[0]
 
         if len(replies) != 1:
             raise RuntimeError("Expected single reply, got {replies!r}".format(
                 replies=replies))
         return replies[0]
 
-    def get_sw_if_index(self, err_msg="Failed to get reply.", timeout=120):
+    def get_sw_if_index(self, err_msg="Failed to get reply."):
         """Get sw_if_index from reply from VPP Python API.
 
         Frequently, the caller is only interested in sw_if_index field
         """Get sw_if_index from reply from VPP Python API.
 
         Frequently, the caller is only interested in sw_if_index field
@@ -213,16 +374,16 @@ class PapiExecutor(object):
         TODO: Discuss exception types to raise, unify with inner methods.
 
         :param err_msg: The message used if the PAPI command(s) execution fails.
         TODO: Discuss exception types to raise, unify with inner methods.
 
         :param err_msg: The message used if the PAPI command(s) execution fails.
-        :param timeout: Timeout in seconds.
         :type err_msg: str
         :type err_msg: str
-        :type timeout: int
         :returns: Response, sw_if_index value of the reply.
         :rtype: int
         :raises AssertionError: If retval is nonzero, parsing or ssh error.
         """
         :returns: Response, sw_if_index value of the reply.
         :rtype: int
         :raises AssertionError: If retval is nonzero, parsing or ssh error.
         """
-        return self.get_reply(err_msg=err_msg, timeout=timeout)["sw_if_index"]
+        reply = self.get_reply(err_msg=err_msg)
+        logger.info("Getting index from {reply!r}".format(reply=reply))
+        return reply["sw_if_index"]
 
 
-    def get_details(self, err_msg="Failed to get dump details.", timeout=120):
+    def get_details(self, err_msg="Failed to get dump details."):
         """Get dump details from VPP Python API.
 
         The details are parsed into dict-like objects.
         """Get dump details from VPP Python API.
 
         The details are parsed into dict-like objects.
@@ -233,28 +394,11 @@ class PapiExecutor(object):
         it is recommended to call get_details for each dump (type) separately.
 
         :param err_msg: The message used if the PAPI command(s) execution fails.
         it is recommended to call get_details for each dump (type) separately.
 
         :param err_msg: The message used if the PAPI command(s) execution fails.
-        :param timeout: Timeout in seconds.
         :type err_msg: str
         :type err_msg: str
-        :type timeout: int
         :returns: Details, dict objects with fields due to API without "retval".
         :rtype: list of dict
         """
         :returns: Details, dict objects with fields due to API without "retval".
         :rtype: list of dict
         """
-        return self._execute(method='dump', err_msg=err_msg, timeout=timeout)
-
-    @staticmethod
-    def dump_and_log(node, cmds):
-        """Dump and log requested information, return None.
-
-        :param node: DUT node.
-        :param cmds: Dump commands to be executed.
-        :type node: dict
-        :type cmds: list of str
-        """
-        with PapiExecutor(node) as papi_exec:
-            for cmd in cmds:
-                details = papi_exec.add(cmd).get_details()
-                logger.debug("{cmd}:\n{details}".format(
-                    cmd=cmd, details=pformat(details)))
+        return self._execute(err_msg)
 
     @staticmethod
     def run_cli_cmd(node, cmd, log=True):
 
     @staticmethod
     def run_cli_cmd(node, cmd, log=True):
@@ -271,20 +415,193 @@ class PapiExecutor(object):
         :returns: CLI output.
         :rtype: str
         """
         :returns: CLI output.
         :rtype: str
         """
-
         cli = 'cli_inband'
         args = dict(cmd=cmd)
         err_msg = "Failed to run 'cli_inband {cmd}' PAPI command on host " \
                   "{host}".format(host=node['host'], cmd=cmd)
         cli = 'cli_inband'
         args = dict(cmd=cmd)
         err_msg = "Failed to run 'cli_inband {cmd}' PAPI command on host " \
                   "{host}".format(host=node['host'], cmd=cmd)
-
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add(cli, **args).get_reply(err_msg)["reply"]
             reply = papi_exec.add(cli, **args).get_reply(err_msg)["reply"]
-
         if log:
             logger.info("{cmd}:\n{reply}".format(cmd=cmd, reply=reply))
         if log:
             logger.info("{cmd}:\n{reply}".format(cmd=cmd, reply=reply))
-
         return reply
 
         return reply
 
+    @staticmethod
+    def dump_and_log(node, cmds):
+        """Dump and log requested information, return None.
+
+        :param node: DUT node.
+        :param cmds: Dump commands to be executed.
+        :type node: dict
+        :type cmds: list of str
+        """
+        with PapiSocketExecutor(node) as papi_exec:
+            for cmd in cmds:
+                dump = papi_exec.add(cmd).get_details()
+                logger.debug("{cmd}:\n{data}".format(
+                    cmd=cmd, data=pformat(dump)))
+
+    def _execute(self, err_msg="Undefined error message"):
+        """Turn internal command list into data and execute; return replies.
+
+        This method also clears the internal command list.
+
+        IMPORTANT!
+        Do not use this method in L1 keywords. Use:
+        - get_replies()
+        - get_reply()
+        - get_sw_if_index()
+        - get_details()
+
+        :param err_msg: The message used if the PAPI command(s) execution fails.
+        :type err_msg: str
+        :returns: Papi responses parsed into a dict-like object,
+            with fields due to API (possibly including retval).
+        :rtype: list of dict
+        :raises RuntimeError: If the replies are not all correct.
+        """
+        vpp_instance = self.vpp_instance
+        local_list = self._api_command_list
+        # Clear first as execution may fail.
+        self._api_command_list = list()
+        replies = list()
+        for command in local_list:
+            api_name = command["api_name"]
+            papi_fn = getattr(vpp_instance.api, api_name)
+            try:
+                reply = papi_fn(**command["api_args"])
+            except IOError as err:
+                # Ocassionally an error happens, try reconnect.
+                logger.warn("Reconnect after error: {err!r}".format(err=err))
+                self.vpp_instance.disconnect()
+                # Testing showes immediate reconnect fails.
+                time.sleep(1)
+                self.vpp_instance.connect_sync("csit_socket")
+                logger.trace("Reconnected.")
+                reply = papi_fn(**command["api_args"])
+            # *_dump commands return list of objects, convert, ordinary reply.
+            if not isinstance(reply, list):
+                reply = [reply]
+            for item in reply:
+                dict_item = dictize(item)
+                if "retval" in dict_item.keys():
+                    # *_details messages do not contain retval.
+                    retval = dict_item["retval"]
+                    if retval != 0:
+                        # TODO: What exactly to log and raise here?
+                        err = AssertionError("Retval {rv!r}".format(rv=retval))
+                        # Lowering log level, some retval!=0 calls are expected.
+                        # TODO: Expose level argument so callers can decide?
+                        raise_from(AssertionError(err_msg), err, level="DEBUG")
+                replies.append(dict_item)
+        return replies
+
+
+class PapiExecutor(object):
+    """Contains methods for executing VPP Python API commands on DUTs.
+
+    TODO: Remove .add step, make get_stats accept paths directly.
+
+    This class processes only one type of VPP PAPI methods: vpp-stats.
+
+    The recommended ways of use are (examples):
+
+    path = ['^/if', '/err/ip4-input', '/sys/node/ip4-input']
+    with PapiExecutor(node) as papi_exec:
+        stats = papi_exec.add(api_name='vpp-stats', path=path).get_stats()
+
+    print('RX interface core 0, sw_if_index 0:\n{0}'.\
+        format(stats[0]['/if/rx'][0][0]))
+
+    or
+
+    path_1 = ['^/if', ]
+    path_2 = ['^/if', '/err/ip4-input', '/sys/node/ip4-input']
+    with PapiExecutor(node) as papi_exec:
+        stats = papi_exec.add('vpp-stats', path=path_1).\
+            add('vpp-stats', path=path_2).get_stats()
+
+    print('RX interface core 0, sw_if_index 0:\n{0}'.\
+        format(stats[1]['/if/rx'][0][0]))
+
+    Note: In this case, when PapiExecutor method 'add' is used:
+    - its parameter 'csit_papi_command' is used only to keep information
+      that vpp-stats are requested. It is not further processed but it is
+      included in the PAPI history this way:
+      vpp-stats(path=['^/if', '/err/ip4-input', '/sys/node/ip4-input'])
+      Always use csit_papi_command="vpp-stats" if the VPP PAPI method
+      is "stats".
+    - the second parameter must be 'path' as it is used by PapiExecutor
+      method 'add'.
+    """
+
+    def __init__(self, node):
+        """Initialization.
+
+        :param node: Node to run command(s) on.
+        :type node: dict
+        """
+
+        # Node to run command(s) on.
+        self._node = node
+
+        # The list of PAPI commands to be executed on the node.
+        self._api_command_list = list()
+
+        self._ssh = SSH()
+
+    def __enter__(self):
+        try:
+            self._ssh.connect(self._node)
+        except IOError:
+            raise RuntimeError("Cannot open SSH connection to host {host} to "
+                               "execute PAPI command(s)".
+                               format(host=self._node["host"]))
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self._ssh.disconnect(self._node)
+
+    def add(self, csit_papi_command="vpp-stats", history=True, **kwargs):
+        """Add next command to internal command list; return self.
+
+        The argument name 'csit_papi_command' must be unique enough as it cannot
+        be repeated in kwargs.
+
+        :param csit_papi_command: VPP API command.
+        :param history: Enable/disable adding command to PAPI command history.
+        :param kwargs: Optional key-value arguments.
+        :type csit_papi_command: str
+        :type history: bool
+        :type kwargs: dict
+        :returns: self, so that method chaining is possible.
+        :rtype: PapiExecutor
+        """
+        if history:
+            PapiHistory.add_to_papi_history(
+                self._node, csit_papi_command, **kwargs)
+        self._api_command_list.append(dict(api_name=csit_papi_command,
+                                           api_args=kwargs))
+        return self
+
+    def get_stats(self, err_msg="Failed to get statistics.", timeout=120):
+        """Get VPP Stats from VPP Python API.
+
+        :param err_msg: The message used if the PAPI command(s) execution fails.
+        :param timeout: Timeout in seconds.
+        :type err_msg: str
+        :type timeout: int
+        :returns: Requested VPP statistics.
+        :rtype: list of dict
+        """
+
+        paths = [cmd['api_args']['path'] for cmd in self._api_command_list]
+        self._api_command_list = list()
+
+        stdout = self._execute_papi(
+            paths, method='stats', err_msg=err_msg, timeout=timeout)
+
+        return json.loads(stdout)
+
     @staticmethod
     def _process_api_data(api_d):
         """Process API data for smooth converting to JSON string.
     @staticmethod
     def _process_api_data(api_d):
         """Process API data for smooth converting to JSON string.
@@ -325,62 +642,6 @@ class PapiExecutor(object):
                                            api_args=api_args_processed))
         return api_data_processed
 
                                            api_args=api_args_processed))
         return api_data_processed
 
-    @staticmethod
-    def _revert_api_reply(api_r):
-        """Process API reply / a part of API reply.
-
-        Apply binascii.unhexlify() method for unicode values.
-
-        TODO: Implement complex solution to process of replies.
-
-        :param api_r: API reply.
-        :type api_r: dict
-        :returns: Processed API reply / a part of API reply.
-        :rtype: dict
-        """
-        def process_value(val):
-            """Process value.
-
-            :param val: Value to be processed.
-            :type val: object
-            :returns: Processed value.
-            :rtype: dict or str or int
-            """
-            if isinstance(val, dict):
-                for val_k, val_v in val.iteritems():
-                    val[str(val_k)] = process_value(val_v)
-                return val
-            elif isinstance(val, list):
-                for idx, val_l in enumerate(val):
-                    val[idx] = process_value(val_l)
-                return val
-            elif isinstance(val, unicode):
-                return binascii.unhexlify(val)
-            else:
-                return val
-
-        reply_dict = dict()
-        reply_value = dict()
-        for reply_key, reply_v in api_r.iteritems():
-            for a_k, a_v in reply_v.iteritems():
-                reply_value[a_k] = process_value(a_v)
-            reply_dict[reply_key] = reply_value
-        return reply_dict
-
-    def _process_reply(self, api_reply):
-        """Process API reply.
-
-        :param api_reply: API reply.
-        :type api_reply: dict or list of dict
-        :returns: Processed API reply.
-        :rtype: list or dict
-        """
-        if isinstance(api_reply, list):
-            reverted_reply = [self._revert_api_reply(a_r) for a_r in api_reply]
-        else:
-            reverted_reply = self._revert_api_reply(api_reply)
-        return reverted_reply
-
     def _execute_papi(self, api_data, method='request', err_msg="",
                       timeout=120):
         """Execute PAPI command(s) on remote node and store the result.
     def _execute_papi(self, api_data, method='request', err_msg="",
                       timeout=120):
         """Execute PAPI command(s) on remote node and store the result.
@@ -430,66 +691,3 @@ class PapiExecutor(object):
             raise AssertionError(err_msg)
 
         return stdout
             raise AssertionError(err_msg)
 
         return stdout
-
-    def _execute(self, method='request', err_msg="", timeout=120):
-        """Turn internal command list into data and execute; return replies.
-
-        This method also clears the internal command list.
-
-        IMPORTANT!
-        Do not use this method in L1 keywords. Use:
-        - get_stats()
-        - get_replies()
-        - get_details()
-
-        :param method: VPP Python API method. Supported methods are: 'request',
-            'dump' and 'stats'.
-        :param err_msg: The message used if the PAPI command(s) execution fails.
-        :param timeout: Timeout in seconds.
-        :type method: str
-        :type err_msg: str
-        :type timeout: int
-        :returns: Papi responses parsed into a dict-like object,
-            with field due to API or stats hierarchy.
-        :rtype: list of dict
-        :raises KeyError: If the reply is not correct.
-        """
-
-        local_list = self._api_command_list
-
-        # Clear first as execution may fail.
-        self._api_command_list = list()
-
-        stdout = self._execute_papi(
-            local_list, method=method, err_msg=err_msg, timeout=timeout)
-        replies = list()
-        try:
-            json_data = json.loads(stdout)
-        except ValueError as err:
-            raise_from(RuntimeError(err_msg), err)
-        for data in json_data:
-            if method == "request":
-                api_reply = self._process_reply(data["api_reply"])
-                # api_reply contains single key, *_reply.
-                obj = api_reply.values()[0]
-                retval = obj["retval"]
-                if retval != 0:
-                    # TODO: What exactly to log and raise here?
-                    err = AssertionError("Got retval {rv!r}".format(rv=retval))
-                    raise_from(AssertionError(err_msg), err, level="INFO")
-                replies.append(obj)
-            elif method == "dump":
-                api_reply = self._process_reply(data["api_reply"])
-                # api_reply is a list where item contas single key, *_details.
-                for item in api_reply:
-                    obj = item.values()[0]
-                    replies.append(obj)
-            else:
-                # TODO: Implement support for stats.
-                raise RuntimeError("Unsuported method {method}".format(
-                    method=method))
-
-        # TODO: Make logging optional?
-        logger.debug("PAPI replies: {replies}".format(replies=replies))
-
-        return replies
index a0a54fb..e05bfe3 100644 (file)
@@ -14,7 +14,7 @@
 """Proxy ARP library"""
 
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 """Proxy ARP library"""
 
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.VatExecutor import VatTerminal
 
 
 from resources.libraries.python.VatExecutor import VatTerminal
 
 
@@ -53,5 +53,5 @@ class ProxyArp(object):
             enable_disable=1)
         err_msg = 'Failed to enable proxy ARP on interface {ifc}'.format(
             ifc=interface)
             enable_disable=1)
         err_msg = 'Failed to enable proxy ARP on interface {ifc}'.format(
             ifc=interface)
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             papi_exec.add(cmd, **args).get_reply(err_msg)
             papi_exec.add(cmd, **args).get_reply(err_msg)
index 6a63798..0895f95 100644 (file)
@@ -222,6 +222,7 @@ class QemuUtils(object):
         vpp_config.add_unix_nodaemon()
         vpp_config.add_unix_cli_listen()
         vpp_config.add_unix_exec(running)
         vpp_config.add_unix_nodaemon()
         vpp_config.add_unix_cli_listen()
         vpp_config.add_unix_exec(running)
+        vpp_config.add_socksvr()
         vpp_config.add_cpu_main_core('0')
         if self._opt.get('smp') > 1:
             vpp_config.add_cpu_corelist_workers('1-{smp}'.format(
         vpp_config.add_cpu_main_core('0')
         if self._opt.get('smp') > 1:
             vpp_config.add_cpu_corelist_workers('1-{smp}'.format(
index a1e4e7a..28717dc 100644 (file)
@@ -54,7 +54,7 @@ def pack_framework_dir():
 
     run(["tar", "--sparse", "--exclude-vcs", "--exclude=output*.xml",
          "--exclude=./tmp", "-zcf", file_name, "."],
 
     run(["tar", "--sparse", "--exclude-vcs", "--exclude=output*.xml",
          "--exclude=./tmp", "-zcf", file_name, "."],
-        check=True, msg="Could not pack testing framework")
+        msg="Could not pack testing framework")
 
     return file_name
 
 
     return file_name
 
index 284b2e8..5c441df 100644 (file)
@@ -19,7 +19,7 @@ from robot.api import logger
 from resources.libraries.python.Constants import Constants
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 from resources.libraries.python.IPUtil import IPUtil
 from resources.libraries.python.Constants import Constants
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 from resources.libraries.python.IPUtil import IPUtil
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.topology import Topology
 from resources.libraries.python.VatExecutor import VatExecutor
 
 from resources.libraries.python.topology import Topology
 from resources.libraries.python.VatExecutor import VatExecutor
 
@@ -169,7 +169,7 @@ class TestConfig(object):
         err_msg = 'Failed to create VXLAN and VLAN interfaces on host {host}'.\
             format(host=node['host'])
 
         err_msg = 'Failed to create VXLAN and VLAN interfaces on host {host}'.\
             format(host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             for i in xrange(0, vxlan_count):
                 try:
                     src_ip = src_ip_addr_start + i * ip_step
             for i in xrange(0, vxlan_count):
                 try:
                     src_ip = src_ip_addr_start + i * ip_step
@@ -261,7 +261,7 @@ class TestConfig(object):
         err_msg = 'Failed to put VXLAN and VLAN interfaces up on host {host}'. \
             format(host=node['host'])
 
         err_msg = 'Failed to put VXLAN and VLAN interfaces up on host {host}'. \
             format(host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             for i in xrange(0, vxlan_count):
                 vxlan_subif_key = Topology.add_new_port(node, 'vxlan_tunnel')
                 vxlan_subif_name = 'vxlan_tunnel{nr}'.format(nr=i)
             for i in xrange(0, vxlan_count):
                 vxlan_subif_key = Topology.add_new_port(node, 'vxlan_tunnel')
                 vxlan_subif_name = 'vxlan_tunnel{nr}'.format(nr=i)
@@ -401,7 +401,7 @@ class TestConfig(object):
         err_msg = 'Failed to put VXLAN and VLAN interfaces to bridge domain ' \
                   'on host {host}'.format(host=node['host'])
 
         err_msg = 'Failed to put VXLAN and VLAN interfaces to bridge domain ' \
                   'on host {host}'.format(host=node['host'])
 
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             for i in xrange(0, vxlan_count):
                 dst_ip = dst_ip_addr_start + i * ip_step
                 args1['neighbor']['ip_address'] = str(dst_ip)
             for i in xrange(0, vxlan_count):
                 dst_ip = dst_ip_addr_start + i * ip_step
                 args1['neighbor']['ip_address'] = str(dst_ip)
index 6e3ac2a..5f885d6 100644 (file)
@@ -13,7 +13,7 @@
 
 """Packet trace library."""
 
 
 """Packet trace library."""
 
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.topology import NodeType
 
 
 from resources.libraries.python.topology import NodeType
 
 
@@ -34,8 +34,8 @@ class Trace(object):
 
         for node in nodes.values():
             if node['type'] == NodeType.DUT:
 
         for node in nodes.values():
             if node['type'] == NodeType.DUT:
-                PapiExecutor.run_cli_cmd(node, cmd="show trace {max}".
-                                         format(max=maximum))
+                PapiSocketExecutor.run_cli_cmd(
+                    node, cmd="show trace {max}".format(max=maximum))
 
     @staticmethod
     def clear_packet_trace_on_all_duts(nodes):
 
     @staticmethod
     def clear_packet_trace_on_all_duts(nodes):
@@ -46,4 +46,4 @@ class Trace(object):
         """
         for node in nodes.values():
             if node['type'] == NodeType.DUT:
         """
         for node in nodes.values():
             if node['type'] == NodeType.DUT:
-                PapiExecutor.run_cli_cmd(node, cmd="clear trace")
+                PapiSocketExecutor.run_cli_cmd(node, cmd="clear trace")
index 676671f..1ae9ca6 100644 (file)
 
 """VPP util library."""
 
 
 """VPP util library."""
 
-import binascii
-
 from robot.api import logger
 
 from resources.libraries.python.Constants import Constants
 from resources.libraries.python.DUTSetup import DUTSetup
 from resources.libraries.python.L2Util import L2Util
 from robot.api import logger
 
 from resources.libraries.python.Constants import Constants
 from resources.libraries.python.DUTSetup import DUTSetup
 from resources.libraries.python.L2Util import L2Util
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.ssh import exec_cmd_no_error
 from resources.libraries.python.topology import NodeType
 
 from resources.libraries.python.ssh import exec_cmd_no_error
 from resources.libraries.python.topology import NodeType
 
@@ -136,6 +134,7 @@ class VPPUtil(object):
             VPPUtil.verify_vpp_started(node)
             # Verify responsivness of PAPI.
             VPPUtil.show_log(node)
             VPPUtil.verify_vpp_started(node)
             # Verify responsivness of PAPI.
             VPPUtil.show_log(node)
+            VPPUtil.vpp_show_version(node)
         finally:
             DUTSetup.get_service_logs(node, Constants.VPP_UNIT)
 
         finally:
             DUTSetup.get_service_logs(node, Constants.VPP_UNIT)
 
@@ -162,7 +161,7 @@ class VPPUtil(object):
         :returns: VPP version.
         :rtype: str
         """
         :returns: VPP version.
         :rtype: str
         """
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add('show_version').get_reply()
         return_version = reply['version'].rstrip('\0x00')
         version = 'VPP version:      {ver}\n'.format(ver=return_version)
             reply = papi_exec.add('show_version').get_reply()
         return_version = reply['version'].rstrip('\0x00')
         version = 'VPP version:      {ver}\n'.format(ver=return_version)
@@ -197,7 +196,7 @@ class VPPUtil(object):
         args = dict(name_filter_valid=0, name_filter='')
         err_msg = 'Failed to get interface dump on host {host}'.format(
             host=node['host'])
         args = dict(name_filter_valid=0, name_filter='')
         err_msg = 'Failed to get interface dump on host {host}'.format(
             host=node['host'])
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         for if_dump in details:
             details = papi_exec.add(cmd, **args).get_details(err_msg)
 
         for if_dump in details:
@@ -226,7 +225,7 @@ class VPPUtil(object):
 
         for cmd in cmds:
             try:
 
         for cmd in cmds:
             try:
-                PapiExecutor.run_cli_cmd(node, cmd)
+                PapiSocketExecutor.run_cli_cmd(node, cmd)
             except AssertionError:
                 if fail_on_error:
                     raise
             except AssertionError:
                 if fail_on_error:
                     raise
@@ -252,7 +251,7 @@ class VPPUtil(object):
         :param node: DUT node to set up.
         :type node: dict
         """
         :param node: DUT node to set up.
         :type node: dict
         """
-        PapiExecutor.run_cli_cmd(node, "elog trace api cli barrier")
+        PapiSocketExecutor.run_cli_cmd(node, "elog trace api cli barrier")
 
     @staticmethod
     def vpp_enable_elog_traces_on_all_duts(nodes):
 
     @staticmethod
     def vpp_enable_elog_traces_on_all_duts(nodes):
@@ -272,7 +271,7 @@ class VPPUtil(object):
         :param node: DUT node to show traces on.
         :type node: dict
         """
         :param node: DUT node to show traces on.
         :type node: dict
         """
-        PapiExecutor.run_cli_cmd(node, "show event-logger")
+        PapiSocketExecutor.run_cli_cmd(node, "show event-logger")
 
     @staticmethod
     def show_event_logger_on_all_duts(nodes):
 
     @staticmethod
     def show_event_logger_on_all_duts(nodes):
@@ -294,7 +293,7 @@ class VPPUtil(object):
         :returns: VPP log data.
         :rtype: list
         """
         :returns: VPP log data.
         :rtype: list
         """
-        return PapiExecutor.run_cli_cmd(node, "show log")
+        return PapiSocketExecutor.run_cli_cmd(node, "show log")
 
     @staticmethod
     def vpp_show_threads(node):
 
     @staticmethod
     def vpp_show_threads(node):
@@ -305,7 +304,7 @@ class VPPUtil(object):
         :returns: VPP thread data.
         :rtype: list
         """
         :returns: VPP thread data.
         :rtype: list
         """
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             reply = papi_exec.add('show_threads').get_reply()
 
         threads_data = list()
             reply = papi_exec.add('show_threads').get_reply()
 
         threads_data = list()
index 916d082..a24bc97 100644 (file)
@@ -15,7 +15,7 @@
 
 from robot.api import logger
 
 
 from robot.api import logger
 
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.topology import NodeType, Topology
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 
 from resources.libraries.python.topology import NodeType, Topology
 from resources.libraries.python.InterfaceUtil import InterfaceUtil
 
@@ -34,7 +34,7 @@ class VhostUser(object):
         :rtype: list
         """
         cmd = "sw_interface_vhost_user_dump"
         :rtype: list
         """
         cmd = "sw_interface_vhost_user_dump"
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd).get_details()
 
         for vhost in details:
             details = papi_exec.add(cmd).get_details()
 
         for vhost in details:
@@ -62,7 +62,7 @@ class VhostUser(object):
         args = dict(
             sock_filename=str(socket)
         )
         args = dict(
             sock_filename=str(socket)
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         # Update the Topology:
             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
 
         # Update the Topology:
index e92d674..7ee3e2c 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""VPP Configuration File Generator library."""
+"""VPP Configuration File Generator library.
+
+TODO: Support initialization with default values,
+so that we do not need to have block of 6 "Add Unix" commands
+in 7 various places of CSIT code.
+"""
 
 import re
 
 
 import re
 
@@ -191,6 +196,11 @@ class VppConfigGenerator(object):
         path = ['unix', 'exec']
         self.add_config_item(self._nodeconfig, value, path)
 
         path = ['unix', 'exec']
         self.add_config_item(self._nodeconfig, value, path)
 
+    def add_socksvr(self, socket="default"):
+        """Add socksvr configuration."""
+        path = ['socksvr', socket]
+        self.add_config_item(self._nodeconfig, '', path)
+
     def add_api_segment_gid(self, value='vpp'):
         """Add API-SEGMENT gid configuration.
 
     def add_api_segment_gid(self, value='vpp'):
         """Add API-SEGMENT gid configuration.
 
index dd15535..03b40b7 100644 (file)
@@ -19,6 +19,7 @@ from pprint import pformat
 
 from robot.api import logger
 from resources.libraries.python.PapiExecutor import PapiExecutor
 
 from robot.api import logger
 from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 from resources.libraries.python.topology import NodeType, Topology
 
 
 from resources.libraries.python.topology import NodeType, Topology
 
 
@@ -46,7 +47,7 @@ class VppCounters(object):
         :param node: Node to run command on.
         :type node: dict
         """
         :param node: Node to run command on.
         :type node: dict
         """
-        PapiExecutor.run_cli_cmd(node, 'show errors')
+        PapiSocketExecutor.run_cli_cmd(node, 'show errors')
 
     @staticmethod
     def vpp_show_errors_verbose(node):
 
     @staticmethod
     def vpp_show_errors_verbose(node):
@@ -55,7 +56,7 @@ class VppCounters(object):
         :param node: Node to run command on.
         :type node: dict
         """
         :param node: Node to run command on.
         :type node: dict
         """
-        PapiExecutor.run_cli_cmd(node, 'show errors verbose')
+        PapiSocketExecutor.run_cli_cmd(node, 'show errors verbose')
 
     @staticmethod
     def vpp_show_errors_on_all_duts(nodes, verbose=False):
 
     @staticmethod
     def vpp_show_errors_on_all_duts(nodes, verbose=False):
@@ -161,7 +162,7 @@ class VppCounters(object):
         :param node: Node to run command on.
         :type node: dict
         """
         :param node: Node to run command on.
         :type node: dict
         """
-        PapiExecutor.run_cli_cmd(node, 'show hardware detail')
+        PapiSocketExecutor.run_cli_cmd(node, 'show hardware detail')
 
     @staticmethod
     def vpp_clear_runtime(node):
 
     @staticmethod
     def vpp_clear_runtime(node):
@@ -172,7 +173,7 @@ class VppCounters(object):
         :returns: Verified data from PAPI response.
         :rtype: dict
         """
         :returns: Verified data from PAPI response.
         :rtype: dict
         """
-        return PapiExecutor.run_cli_cmd(node, 'clear runtime', log=False)
+        return PapiSocketExecutor.run_cli_cmd(node, 'clear runtime', log=False)
 
     @staticmethod
     def clear_runtime_counters_on_all_duts(nodes):
 
     @staticmethod
     def clear_runtime_counters_on_all_duts(nodes):
@@ -194,7 +195,8 @@ class VppCounters(object):
         :returns: Verified data from PAPI response.
         :rtype: dict
         """
         :returns: Verified data from PAPI response.
         :rtype: dict
         """
-        return PapiExecutor.run_cli_cmd(node, 'clear interfaces', log=False)
+        return PapiSocketExecutor.run_cli_cmd(
+            node, 'clear interfaces', log=False)
 
     @staticmethod
     def clear_interface_counters_on_all_duts(nodes):
 
     @staticmethod
     def clear_interface_counters_on_all_duts(nodes):
@@ -216,7 +218,7 @@ class VppCounters(object):
         :returns: Verified data from PAPI response.
         :rtype: dict
         """
         :returns: Verified data from PAPI response.
         :rtype: dict
         """
-        return PapiExecutor.run_cli_cmd(node, 'clear hardware', log=False)
+        return PapiSocketExecutor.run_cli_cmd(node, 'clear hardware', log=False)
 
     @staticmethod
     def clear_hardware_counters_on_all_duts(nodes):
 
     @staticmethod
     def clear_hardware_counters_on_all_duts(nodes):
@@ -238,7 +240,7 @@ class VppCounters(object):
         :returns: Verified data from PAPI response.
         :rtype: dict
         """
         :returns: Verified data from PAPI response.
         :rtype: dict
         """
-        return PapiExecutor.run_cli_cmd(node, 'clear errors', log=False)
+        return PapiSocketExecutor.run_cli_cmd(node, 'clear errors', log=False)
 
     @staticmethod
     def clear_error_counters_on_all_duts(nodes):
 
     @staticmethod
     def clear_error_counters_on_all_duts(nodes):
index 2033525..268a268 100644 (file)
@@ -14,7 +14,7 @@
 """SPAN setup library"""
 
 from resources.libraries.python.topology import Topology
 """SPAN setup library"""
 
 from resources.libraries.python.topology import Topology
-from resources.libraries.python.PapiExecutor import PapiExecutor
+from resources.libraries.python.PapiExecutor import PapiSocketExecutor
 
 
 class SPAN(object):
 
 
 class SPAN(object):
@@ -41,7 +41,7 @@ class SPAN(object):
         args = dict(
             is_l2=1 if is_l2 else 0
         )
         args = dict(
             is_l2=1 if is_l2 else 0
         )
-        with PapiExecutor(node) as papi_exec:
+        with PapiSocketExecutor(node) as papi_exec:
             details = papi_exec.add(cmd, **args).get_details()
 
         return details
             details = papi_exec.add(cmd, **args).get_details()
 
         return details
index bd906e1..a4d8d10 100644 (file)
@@ -77,6 +77,7 @@
 | | Run keyword | VPP_config.Add Unix Log
 | | Run keyword | VPP_config.Add Unix CLI Listen
 | | Run keyword | VPP_config.Add Unix Nodaemon
 | | Run keyword | VPP_config.Add Unix Log
 | | Run keyword | VPP_config.Add Unix CLI Listen
 | | Run keyword | VPP_config.Add Unix Nodaemon
+| | Run keyword | VPP_config.Add Socksvr
 | | Run keyword | VPP_config.Add CPU Main Core | ${1}
 | | Run keyword | VPP_config.Apply Config
 
 | | Run keyword | VPP_config.Add CPU Main Core | ${1}
 | | Run keyword | VPP_config.Apply Config
 
index 89609f5..0bacb53 100644 (file)
 | | | Run keyword | ${dut}.Add Unix CLI Listen
 | | | Run keyword | ${dut}.Add Unix Nodaemon
 | | | Run keyword | ${dut}.Add Unix Coredump
 | | | Run keyword | ${dut}.Add Unix CLI Listen
 | | | Run keyword | ${dut}.Add Unix Nodaemon
 | | | Run keyword | ${dut}.Add Unix Coredump
+| | | Run keyword | ${dut}.Add Socksvr
 | | | Run keyword | ${dut}.Add DPDK No Tx Checksum Offload
 | | | Run keyword | ${dut}.Add DPDK Log Level | debug
 | | | Run keyword | ${dut}.Add DPDK Uio Driver
 | | | Run keyword | ${dut}.Add DPDK No Tx Checksum Offload
 | | | Run keyword | ${dut}.Add DPDK Log Level | debug
 | | | Run keyword | ${dut}.Add DPDK Uio Driver
 | | ... | ${thr_count_int}
 
 | Write startup configuration on all VPP DUTs
 | | ... | ${thr_count_int}
 
 | Write startup configuration on all VPP DUTs
-| | [Documentation] | Write VPP startup configuration on all DUTs.
+| | [Documentation] | Write VPP startup configuration without restarting VPP.
 | | ...
 | | :FOR | ${dut} | IN | @{duts}
 | | | Run keyword | ${dut}.Write Config
 | | ...
 | | :FOR | ${dut} | IN | @{duts}
 | | | Run keyword | ${dut}.Write Config