import glob
import json
import shutil
+import struct # vpp-papi can raise struct.error
import subprocess
import sys
import tempfile
from resources.libraries.python.PapiHistory import PapiHistory
from resources.libraries.python.ssh import (
SSH, SSHTimeout, exec_cmd_no_error, scp_node)
+from resources.libraries.python.topology import Topology, SocketType
from resources.libraries.python.VppApiCrc import VppApiCrcChecker
Note: Use only with "with" statement, e.g.:
+ cmd = 'show_version'
with PapiSocketExecutor(node) as papi_exec:
- reply = papi_exec.add('show_version').get_reply(err_msg)
+ reply = papi_exec.add(cmd).get_reply(err_msg)
This class processes two classes of VPP PAPI methods:
1. Simple request / reply: method='request'.
a. One request with no arguments:
+ cmd = 'show_version'
with PapiSocketExecutor(node) as papi_exec:
- reply = papi_exec.add('show_version').get_reply(err_msg)
+ reply = papi_exec.add(cmd).get_reply(err_msg)
b. Three requests with arguments, the second and the third ones are the same
but with different arguments.
if self.vpp_instance:
return
cls = self.__class__ # Shorthand for setting class fields.
- tmp_dir = tempfile.mkdtemp(dir="/tmp")
package_path = None
+ tmp_dir = tempfile.mkdtemp(dir="/tmp")
try:
# Pack, copy and unpack Python part of VPP installation from _node.
# TODO: Use rsync or recursive version of ssh.scp_node instead?
# Package path has to be one level above the vpp_papi directory.
package_path = package_path.rsplit('/', 1)[0]
sys.path.append(package_path)
+ # pylint: disable=import-error
from vpp_papi.vpp_papi import VPPApiClient as vpp_class
vpp_class.apidir = api_json_directory
# We need to create instance before removing from sys.path.
for _ in xrange(2):
try:
vpp_instance.connect_sync("csit_socket")
- except IOError as err:
+ except (IOError, struct.error) as err:
logger.warn("Got initial connect error {err!r}".format(err=err))
vpp_instance.disconnect()
else:
:raises AssertionError: If retval is nonzero, parsing or ssh error.
"""
reply = self.get_reply(err_msg=err_msg)
- logger.info("Getting index from {reply!r}".format(reply=reply))
+ logger.trace("Getting index from {reply!r}".format(reply=reply))
return reply["sw_if_index"]
def get_details(self, err_msg="Failed to get dump details."):
return self._execute(err_msg)
@staticmethod
- def run_cli_cmd(node, cmd, log=True):
+ def run_cli_cmd(node, cli_cmd, log=True,
+ remote_vpp_socket=Constants.SOCKSVR_PATH):
"""Run a CLI command as cli_inband, return the "reply" field of reply.
Optionally, log the field value.
:param node: Node to run command on.
- :param cmd: The CLI command to be run on the node.
+ :param cli_cmd: The CLI command to be run on the node.
+ :param remote_vpp_socket: Path to remote socket to tunnel to.
:param log: If True, the response is logged.
:type node: dict
- :type cmd: str
+ :type remote_vpp_socket: str
+ :type cli_cmd: str
:type log: bool
:returns: CLI output.
:rtype: str
"""
- cli = 'cli_inband'
- args = dict(cmd=cmd)
+ cmd = 'cli_inband'
+ args = dict(cmd=cli_cmd)
err_msg = "Failed to run 'cli_inband {cmd}' PAPI command on host " \
"{host}".format(host=node['host'], cmd=cmd)
- with PapiSocketExecutor(node) as papi_exec:
- reply = papi_exec.add(cli, **args).get_reply(err_msg)["reply"]
+ with PapiSocketExecutor(node, remote_vpp_socket) as papi_exec:
+ reply = papi_exec.add(cmd, **args).get_reply(err_msg)["reply"]
if log:
- logger.info("{cmd}:\n{reply}".format(cmd=cmd, reply=reply))
+ logger.info(
+ "{cmd} ({host} - {remote_vpp_socket}):\n{reply}".
+ format(cmd=cmd, reply=reply,
+ remote_vpp_socket=remote_vpp_socket, host=node['host']))
return reply
+ @staticmethod
+ def run_cli_cmd_on_all_sockets(node, cli_cmd, log=True):
+ """Run a CLI command as cli_inband, on all sockets in topology file.
+
+ :param node: Node to run command on.
+ :param cli_cmd: The CLI command to be run on the node.
+ :param log: If True, the response is logged.
+ :type node: dict
+ :type cli_cmd: str
+ :type log: bool
+ """
+ sockets = Topology.get_node_sockets(node, socket_type=SocketType.PAPI)
+ if sockets:
+ for socket in sockets.values():
+ PapiSocketExecutor.run_cli_cmd(
+ node, cli_cmd, log=log, remote_vpp_socket=socket)
+
@staticmethod
def dump_and_log(node, cmds):
"""Dump and log requested information, return None.
try:
try:
reply = papi_fn(**command["api_args"])
- except IOError as err:
+ except (IOError, struct.error) as err:
# Ocassionally an error happens, try reconnect.
logger.warn("Reconnect after error: {err!r}".format(
err=err))
self.vpp_instance.connect_sync("csit_socket")
logger.trace("Reconnected.")
reply = papi_fn(**command["api_args"])
- except (AttributeError, IOError) as err:
+ except (AttributeError, IOError, struct.error) as err:
raise_from(AssertionError(err_msg), err, level="INFO")
# *_dump commands return list of objects, convert, ordinary reply.
if not isinstance(reply, list):
api_name=csit_papi_command, api_args=copy.deepcopy(kwargs)))
return self
- def get_stats(self, err_msg="Failed to get statistics.", timeout=120):
+ def get_stats(self, err_msg="Failed to get statistics.", timeout=120,
+ socket=Constants.SOCKSTAT_PATH):
"""Get VPP Stats from VPP Python API.
:param err_msg: The message used if the PAPI command(s) execution fails.
: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)
+ paths, method='stats', err_msg=err_msg, timeout=timeout,
+ socket=socket)
return json.loads(stdout)
return api_data_processed
def _execute_papi(self, api_data, method='request', err_msg="",
- timeout=120):
+ timeout=120, socket=None):
"""Execute PAPI command(s) on remote node and store the result.
:param api_data: List of APIs with their arguments.
:raises RuntimeError: If PAPI executor failed due to another reason.
:raises AssertionError: If PAPI command(s) execution has failed.
"""
-
if not api_data:
raise RuntimeError("No API data provided.")
if method in ("stats", "stats_request") \
else json.dumps(self._process_api_data(api_data))
- cmd = "{fw_dir}/{papi_provider} --method {method} --data '{json}'".\
- format(
- fw_dir=Constants.REMOTE_FW_DIR, method=method, json=json_data,
- papi_provider=Constants.RESOURCES_PAPI_PROVIDER)
+ sock = " --socket {socket}".format(socket=socket) if socket else ""
+ cmd = (
+ "{fw_dir}/{papi_provider} --method {method} --data '{json}'{socket}"
+ .format(fw_dir=Constants.REMOTE_FW_DIR,
+ papi_provider=Constants.RESOURCES_PAPI_PROVIDER,
+ method=method, json=json_data, socket=sock))
try:
ret_code, stdout, _ = self._ssh.exec_command_sudo(
cmd=cmd, timeout=timeout, log_stdout_err=False)