From bb72bc0158019aaa16bfbf2794c6500e6ddfbf79 Mon Sep 17 00:00:00 2001 From: Sebastian Gross Date: Sat, 8 Nov 2025 20:42:59 +0100 Subject: [PATCH 1/4] remote/client: cleanup InteractiveCommandError When handling InteractiveCommandError there is an implicit assumption that it has a field `exitcode`. This assumption holds since in all places it is raised there is the same repeated code block. Make this assumption more explicit by providing a proper constructor to InteractiveCommandError class. Signed-off-by: Sebastian Gross --- labgrid/remote/client.py | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/labgrid/remote/client.py b/labgrid/remote/client.py index 3e30f7037..07d6e694b 100755 --- a/labgrid/remote/client.py +++ b/labgrid/remote/client.py @@ -69,7 +69,9 @@ class ServerError(Error): class InteractiveCommandError(Error): - pass + def __init__(self: Error, msg: str, exitcode: int): + super(InteractiveCommandError, self).__init__(msg) + self.exitcode = exitcode @attr.s(eq=False) @@ -1049,9 +1051,7 @@ async def console(self, place, target): break if not self.args.loop: if res: - exc = InteractiveCommandError("microcom error") - exc.exitcode = res - raise exc + raise InteractiveCommandError("microcom error", res) break await asyncio.sleep(1.0) @@ -1251,27 +1251,21 @@ def ssh(self): res = drv.interact(self.args.leftover) if res: - exc = InteractiveCommandError("ssh error") - exc.exitcode = res - raise exc + raise InteractiveCommandError("ssh error", res) def scp(self): drv = self._get_ssh() res = drv.scp(src=self.args.src, dst=self.args.dst) if res: - exc = InteractiveCommandError("scp error") - exc.exitcode = res - raise exc + raise InteractiveCommandError("scp error", res) def rsync(self): drv = self._get_ssh() res = drv.rsync(src=self.args.src, dst=self.args.dst, extra=self.args.leftover) if res: - exc = InteractiveCommandError("rsync error") - exc.exitcode = res - raise exc + raise InteractiveCommandError("rsync error", res) def sshfs(self): drv = self._get_ssh() @@ -1309,9 +1303,7 @@ def telnet(self): args = ["telnet", str(ip)] res = subprocess.call(args) if res: - exc = InteractiveCommandError("telnet error") - exc.exitcode = res - raise exc + raise InteractiveCommandError("telnet error", res) def video(self): place = self.get_acquired_place() @@ -1347,9 +1339,7 @@ def video(self): else: res = drv.stream(quality, controls=controls) if res: - exc = InteractiveCommandError("gst-launch-1.0 error") - exc.exitcode = res - raise exc + raise InteractiveCommandError("gst-launch-1.0 error", res) def audio(self): place = self.get_acquired_place() @@ -1358,9 +1348,7 @@ def audio(self): drv = self._get_driver_or_new(target, "USBAudioInputDriver", name=name) res = drv.play() if res: - exc = InteractiveCommandError("gst-launch-1.0 error") - exc.exitcode = res - raise exc + raise InteractiveCommandError("gst-launch-1.0 error", res) def _get_tmc(self): place = self.get_acquired_place() From 5843a6384f003e306f0f9d385fff327e5bd6bc6f Mon Sep 17 00:00:00 2001 From: Sebastian Gross Date: Sat, 8 Nov 2025 21:18:57 +0100 Subject: [PATCH 2/4] driver/sshdriver: add multifile support to scp Users are accustomed to scp having an option for copying recursively as well as to accept multiple source files. Meet user expectation by adding well known `-r` option and support for multiple source files. Signed-off-by: Sebastian Gross --- labgrid/driver/sshdriver.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/labgrid/driver/sshdriver.py b/labgrid/driver/sshdriver.py index fdee16b72..fd3f77bfe 100644 --- a/labgrid/driver/sshdriver.py +++ b/labgrid/driver/sshdriver.py @@ -356,24 +356,31 @@ def forward_unix_socket(self, unixsocket, localport=None): yield localport @Driver.check_active - @step(args=['src', 'dst']) - def scp(self, *, src, dst): + @step(args=['src', 'dst', 'recursive']) + def scp(self, *, src, dst, recursive=False): if not self._check_keepalive(): raise ExecutionError("Keepalive no longer running") - if src.startswith(':') == dst.startswith(':'): - raise ValueError("Either source or destination must be remote (start with :)") - if src.startswith(':'): - src = '_' + src - if dst.startswith(':'): + if all([f.startswith(':') for f in src]): + if dst.startswith(':'): + raise ValueError("Either source or destination must be remote (start with :)") + src = ['_' + f for f in src] + else: + if not dst.startswith(':'): + raise ValueError("Either source or destination must be remote (start with :)") dst = '_' + dst complete_cmd = [self._scp, "-S", self._ssh, "-F", "none", "-o", f"ControlPath={self.control.replace('%', '%%')}", - src, dst, + dst, ] + for f in src: + complete_cmd.insert(-1, f) + + if recursive: + complete_cmd.insert(1, "-r") if self.explicit_sftp_mode and self._scp_supports_explicit_sftp_mode(): complete_cmd.insert(1, "-s") From 7506b154c5ebdaf800b24301952c84b8dfd6e08f Mon Sep 17 00:00:00 2001 From: Sebastian Gross Date: Sat, 8 Nov 2025 21:49:01 +0100 Subject: [PATCH 3/4] remote/client: adapt scp command Use features of multifile scp driver Signed-off-by: Sebastian Gross --- labgrid/remote/client.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/labgrid/remote/client.py b/labgrid/remote/client.py index 07d6e694b..62380f903 100755 --- a/labgrid/remote/client.py +++ b/labgrid/remote/client.py @@ -1256,7 +1256,7 @@ def ssh(self): def scp(self): drv = self._get_ssh() - res = drv.scp(src=self.args.src, dst=self.args.dst) + res = drv.scp(src=self.args.files[:-1], dst=self.args.files[-1], recursive=self.args.recursive) if res: raise InteractiveCommandError("scp error", res) @@ -1922,8 +1922,10 @@ def main(): subparser = subparsers.add_parser("scp", help="transfer file via scp") subparser.add_argument("--name", "-n", help="optional resource name") - subparser.add_argument("src", help="source path (use :dir/file for remote side)") - subparser.add_argument("dst", help="destination path (use :dir/file for remote side)") + subparser.add_argument("--recursive", "-r", action="store_true", help="copy recursive") + subparser.add_argument( + "files", nargs="+", metavar="SRC/DST", help="source and destination path (use :dir/file for remote side)" + ) subparser.set_defaults(func=ClientSession.scp) subparser = subparsers.add_parser( From 1cebc8bdd2557ba5f96328fff216b12023e1443b Mon Sep 17 00:00:00 2001 From: Sebastian Gross Date: Mon, 10 Nov 2025 10:37:45 +0100 Subject: [PATCH 4/4] man/labgrid-client: promote scp multifile option Signed-off-by: Sebastian Gross --- man/labgrid-client.1 | 2 +- man/labgrid-client.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/labgrid-client.1 b/man/labgrid-client.1 index 630511c34..fdd76019a 100644 --- a/man/labgrid-client.1 +++ b/man/labgrid-client.1 @@ -203,7 +203,7 @@ not at all. .sp \fBssh\fP \fB[command]\fP Connect via SSH. Additional arguments are passed to ssh. .sp -\fBscp\fP \fB[source]\fP \fB[destination]\fP Transfer file via scp (use \(aq:dir/file\(aq for the remote side) +\fBscp\fP \fB[sources...]\fP \fB[destination]\fP Transfer file via scp (use \(aq:dir/file\(aq for the remote side) .sp \fBrsync\fP \fB[source]\fP \fB[destination]\fP Transfer files via rsync (use \(aq:dir/file\(aq for the remote side) .sp diff --git a/man/labgrid-client.rst b/man/labgrid-client.rst index 59f43c5a6..26068af3c 100644 --- a/man/labgrid-client.rst +++ b/man/labgrid-client.rst @@ -194,7 +194,7 @@ LABGRID-CLIENT COMMANDS ``ssh`` ``[command]`` Connect via SSH. Additional arguments are passed to ssh. -``scp`` ``[source]`` ``[destination]`` Transfer file via scp (use ':dir/file' for the remote side) +``scp`` ``[sources...]`` ``[destination]`` Transfer file via scp (use ':dir/file' for the remote side) ``rsync`` ``[source]`` ``[destination]`` Transfer files via rsync (use ':dir/file' for the remote side)