From: jspewock@iol.unh.edu
To: Luca.Vizzarro@arm.com, wathsala.vithanage@arm.com,
yoan.picchi@foss.arm.com, juraj.linkes@pantheon.tech,
paul.szczepanek@arm.com, probb@iol.unh.edu, thomas@monjalon.net,
Honnappa.Nagarahalli@arm.com
Cc: dev@dpdk.org, Jeremy Spewock <jspewock@iol.unh.edu>
Subject: [PATCH v2 1/3] dts: Improve output gathering in interactive shells
Date: Wed, 1 May 2024 12:16:21 -0400 [thread overview]
Message-ID: <20240501161623.26672-2-jspewock@iol.unh.edu> (raw)
In-Reply-To: <20240501161623.26672-1-jspewock@iol.unh.edu>
From: Jeremy Spewock <jspewock@iol.unh.edu>
The current implementation of consuming output from interactive shells
relies on being able to find an expected prompt somewhere within the
output buffer after sending the command. This is useful in situations
where the prompt does not appear in the output itself, but in some
practical cases (such as the starting of an XML-RPC server for scapy)
the prompt exists in one of the commands sent to the shell and this can
cause the command to exit early and creates a race condition between the
server starting and the first command being sent to the server.
This patch addresses this problem by searching for a line that strictly
ends with the provided prompt, rather than one that simply contains it,
so that the detection that a command is finished is more consistent. It
also adds a catch to detect when a command times out before finding the
prompt or the underlying SSH session dies so that the exception can be
wrapped into a more explicit one and be more consistent with the
non-interactive shells.
Bugzilla ID: 1359
Fixes: 88489c0501af ("dts: add smoke tests")
Signed-off-by: Jeremy Spewock <jspewock@iol.unh.edu>
---
dts/framework/exception.py | 66 ++++++++++++-------
.../remote_session/interactive_shell.py | 46 +++++++++----
2 files changed, 77 insertions(+), 35 deletions(-)
diff --git a/dts/framework/exception.py b/dts/framework/exception.py
index cce1e0231a..627190c781 100644
--- a/dts/framework/exception.py
+++ b/dts/framework/exception.py
@@ -48,26 +48,6 @@ class DTSError(Exception):
severity: ClassVar[ErrorSeverity] = ErrorSeverity.GENERIC_ERR
-class SSHTimeoutError(DTSError):
- """The SSH execution of a command timed out."""
-
- #:
- severity: ClassVar[ErrorSeverity] = ErrorSeverity.SSH_ERR
- _command: str
-
- def __init__(self, command: str):
- """Define the meaning of the first argument.
-
- Args:
- command: The executed command.
- """
- self._command = command
-
- def __str__(self) -> str:
- """Add some context to the string representation."""
- return f"{self._command} execution timed out."
-
-
class SSHConnectionError(DTSError):
"""An unsuccessful SSH connection."""
@@ -95,8 +75,42 @@ def __str__(self) -> str:
return message
-class SSHSessionDeadError(DTSError):
- """The SSH session is no longer alive."""
+class _SSHTimeoutError(DTSError):
+ """The execution of a command via SSH timed out.
+
+ This class is private and meant to be raised as its interactive and non-interactive variants.
+ """
+
+ #:
+ severity: ClassVar[ErrorSeverity] = ErrorSeverity.SSH_ERR
+ _command: str
+
+ def __init__(self, command: str):
+ """Define the meaning of the first argument.
+
+ Args:
+ command: The executed command.
+ """
+ self._command = command
+
+ def __str__(self) -> str:
+ """Add some context to the string representation."""
+ return f"{self._command} execution timed out."
+
+
+class SSHTimeoutError(_SSHTimeoutError):
+ """The execution of a command on a non-interactive SSH session timed out."""
+
+
+class InteractiveSSHTimeoutError(_SSHTimeoutError):
+ """The execution of a command on an interactive SSH session timed out."""
+
+
+class _SSHSessionDeadError(DTSError):
+ """The SSH session is no longer alive.
+
+ This class is private and meant to be raised as its interactive and non-interactive variants.
+ """
#:
severity: ClassVar[ErrorSeverity] = ErrorSeverity.SSH_ERR
@@ -115,6 +129,14 @@ def __str__(self) -> str:
return f"SSH session with {self._host} has died."
+class SSHSessionDeadError(_SSHSessionDeadError):
+ """Non-interactive SSH session has died."""
+
+
+class InteractiveSSHSessionDeadError(_SSHSessionDeadError):
+ """Interactive SSH session as died."""
+
+
class ConfigurationError(DTSError):
"""An invalid configuration."""
diff --git a/dts/framework/remote_session/interactive_shell.py b/dts/framework/remote_session/interactive_shell.py
index 5cfe202e15..0b0ccdb545 100644
--- a/dts/framework/remote_session/interactive_shell.py
+++ b/dts/framework/remote_session/interactive_shell.py
@@ -18,11 +18,17 @@
from pathlib import PurePath
from typing import Callable, ClassVar
-from paramiko import Channel, SSHClient, channel # type: ignore[import]
+from paramiko import Channel, channel # type: ignore[import]
+from framework.exception import (
+ InteractiveSSHSessionDeadError,
+ InteractiveSSHTimeoutError,
+)
from framework.logger import DTSLogger
from framework.settings import SETTINGS
+from .interactive_remote_session import InteractiveRemoteSession
+
class InteractiveShell(ABC):
"""The base class for managing interactive shells.
@@ -34,7 +40,7 @@ class InteractiveShell(ABC):
session.
"""
- _interactive_session: SSHClient
+ _interactive_session: InteractiveRemoteSession
_stdin: channel.ChannelStdinFile
_stdout: channel.ChannelFile
_ssh_channel: Channel
@@ -60,7 +66,7 @@ class InteractiveShell(ABC):
def __init__(
self,
- interactive_session: SSHClient,
+ interactive_session: InteractiveRemoteSession,
logger: DTSLogger,
get_privileged_command: Callable[[str], str] | None,
app_args: str = "",
@@ -80,7 +86,7 @@ def __init__(
and no output is gathered within the timeout, an exception is thrown.
"""
self._interactive_session = interactive_session
- self._ssh_channel = self._interactive_session.invoke_shell()
+ self._ssh_channel = self._interactive_session.session.invoke_shell()
self._stdin = self._ssh_channel.makefile_stdin("w")
self._stdout = self._ssh_channel.makefile("r")
self._ssh_channel.settimeout(timeout)
@@ -124,20 +130,34 @@ def send_command(self, command: str, prompt: str | None = None) -> str:
Returns:
All output in the buffer before expected string.
+
+ Raises:
+ InteractiveSSHSessionDeadError: The session died while executing the command.
+ InteractiveSSHTimeoutError: If command was sent but prompt could not be found in
+ the output before the timeout.
"""
self._logger.info(f"Sending: '{command}'")
if prompt is None:
prompt = self._default_prompt
- self._stdin.write(f"{command}{self._command_extra_chars}\n")
- self._stdin.flush()
out: str = ""
- for line in self._stdout:
- out += line
- if prompt in line and not line.rstrip().endswith(
- command.rstrip()
- ): # ignore line that sent command
- break
- self._logger.debug(f"Got output: {out}")
+ try:
+ self._stdin.write(f"{command}{self._command_extra_chars}\n")
+ self._stdin.flush()
+ for line in self._stdout:
+ out += line
+ if line.rstrip().endswith(prompt):
+ break
+ except TimeoutError as e:
+ self._logger.exception(e)
+ self._logger.debug(
+ f"Prompt ({prompt}) was not found in output from command before timeout."
+ )
+ raise InteractiveSSHTimeoutError(command) from e
+ except OSError as e:
+ self._logger.exception(e)
+ raise InteractiveSSHSessionDeadError(self._interactive_session.hostname) from e
+ finally:
+ self._logger.debug(f"Got output: {out}")
return out
def close(self) -> None:
--
2.44.0
next prev parent reply other threads:[~2024-05-01 16:17 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-12 17:25 [PATCH v1 0/2] Improve interactive shell output gathering jspewock
2024-03-12 17:25 ` [PATCH v1 1/2] dts: Improve output gathering in interactive shells jspewock
2024-04-03 9:00 ` Juraj Linkeš
2024-04-08 16:20 ` Jeremy Spewock
2024-04-10 10:20 ` Juraj Linkeš
2024-03-12 17:25 ` [PATCH v1 2/2] dts: Add missing docstring from XML-RPC server jspewock
2024-04-24 13:42 ` Patrick Robb
2024-05-01 16:16 ` [PATCH v2 0/3] Improve interactive shell output gathering and logging jspewock
2024-05-01 16:16 ` jspewock [this message]
2024-05-09 9:57 ` [PATCH v2 1/3] dts: Improve output gathering in interactive shells Luca Vizzarro
2024-05-13 14:58 ` Juraj Linkeš
2024-05-15 19:13 ` Jeremy Spewock
2024-05-01 16:16 ` [PATCH v2 2/3] dts: Add missing docstring from XML-RPC server jspewock
2024-05-09 9:57 ` Luca Vizzarro
2024-05-13 14:58 ` Juraj Linkeš
2024-05-01 16:16 ` [PATCH v2 3/3] dts: Improve logging for interactive shells jspewock
2024-05-09 9:57 ` Luca Vizzarro
2024-05-13 15:02 ` Juraj Linkeš
2024-05-15 19:23 ` Jeremy Spewock
2024-05-09 9:59 ` [PATCH v2 0/3] Improve interactive shell output gathering and logging Luca Vizzarro
2024-05-20 15:08 ` Nicholas Pratte
2024-05-29 19:49 ` [PATCH v3 " jspewock
2024-05-29 19:49 ` [PATCH v3 1/3] dts: Improve output gathering in interactive shells jspewock
2024-05-31 16:49 ` Luca Vizzarro
2024-06-07 13:37 ` Juraj Linkeš
2024-06-14 20:58 ` Nicholas Pratte
2024-06-17 15:00 ` Luca Vizzarro
2024-05-29 19:49 ` [PATCH v3 2/3] dts: Add missing docstring from XML-RPC server jspewock
2024-05-31 16:50 ` Luca Vizzarro
2024-06-07 13:37 ` Juraj Linkeš
2024-06-14 20:48 ` Nicholas Pratte
2024-06-17 15:06 ` Jeremy Spewock
2024-06-17 15:00 ` Luca Vizzarro
2024-05-29 19:49 ` [PATCH v3 3/3] dts: Improve logging for interactive shells jspewock
2024-05-31 16:50 ` Luca Vizzarro
2024-06-07 13:38 ` Juraj Linkeš
2024-06-14 20:26 ` Nicholas Pratte
2024-06-17 15:01 ` Luca Vizzarro
2024-06-20 17:36 ` [PATCH v4 0/3] Improve interactive shell output gathering and logging jspewock
2024-06-20 17:36 ` [PATCH v4 1/3] dts: Improve output gathering in interactive shells jspewock
2024-06-21 9:10 ` Juraj Linkeš
2024-06-20 17:36 ` [PATCH v4 2/3] dts: Add missing docstring from XML-RPC server jspewock
2024-06-21 9:12 ` Juraj Linkeš
2024-06-20 17:36 ` [PATCH v4 3/3] dts: Improve logging for interactive shells jspewock
2024-06-21 9:23 ` Juraj Linkeš
2024-06-21 15:31 ` Patrick Robb
2024-07-23 22:17 ` [PATCH v4 0/3] Improve interactive shell output gathering and logging Thomas Monjalon
2024-07-24 14:08 ` [PATCH v5 " jspewock
2024-07-24 14:08 ` [PATCH v5 1/3] dts: Improve output gathering in interactive shells jspewock
2024-07-24 14:08 ` [PATCH v5 2/3] dts: Add missing docstring from XML-RPC server jspewock
2024-07-24 14:08 ` [PATCH v5 3/3] dts: Improve logging for interactive shells jspewock
2024-07-24 18:39 ` [PATCH v6 0/3] Improve interactive shell output gathering and logging jspewock
2024-07-24 18:39 ` [PATCH v6 1/3] dts: Improve output gathering in interactive shells jspewock
2024-07-24 18:39 ` [PATCH v6 2/3] dts: Add missing docstring from XML-RPC server jspewock
2024-07-24 18:39 ` [PATCH v6 3/3] dts: Improve logging for interactive shells jspewock
2024-07-26 11:01 ` [PATCH v6 0/3] Improve interactive shell output gathering and logging Juraj Linkeš
2024-07-29 16:56 ` Thomas Monjalon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240501161623.26672-2-jspewock@iol.unh.edu \
--to=jspewock@iol.unh.edu \
--cc=Honnappa.Nagarahalli@arm.com \
--cc=Luca.Vizzarro@arm.com \
--cc=dev@dpdk.org \
--cc=juraj.linkes@pantheon.tech \
--cc=paul.szczepanek@arm.com \
--cc=probb@iol.unh.edu \
--cc=thomas@monjalon.net \
--cc=wathsala.vithanage@arm.com \
--cc=yoan.picchi@foss.arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).