ssh-handler 1.3.0__tar.gz → 1.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ssh_handler-1.3.0/ssh_handler.egg-info → ssh_handler-1.4.1}/PKG-INFO +28 -15
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/README.md +27 -14
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/pyproject.toml +1 -1
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/__init__.py +1 -1
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/core.py +63 -14
- {ssh_handler-1.3.0 → ssh_handler-1.4.1/ssh_handler.egg-info}/PKG-INFO +28 -15
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/LICENSE +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/setup.cfg +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/__main__.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/cli.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/config.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/credentials.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/exceptions.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/ftp.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/openssh/OpenSSH-ARM64.zip +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/openssh/OpenSSH-Win32.zip +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/openssh/OpenSSH-Win64.zip +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/pool.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/pyqt_worker.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/results.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/serial_handler.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/setup_openssh_server.ps1 +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler/winrm_bootstrap.py +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler.egg-info/SOURCES.txt +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler.egg-info/dependency_links.txt +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler.egg-info/entry_points.txt +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler.egg-info/requires.txt +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/ssh_handler.egg-info/top_level.txt +0 -0
- {ssh_handler-1.3.0 → ssh_handler-1.4.1}/tests/test_offline.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssh-handler
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools.
|
|
5
5
|
Author: ssh-handler contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -304,26 +304,39 @@ with SerialHandler("COM5", baudrate=115200, quiet=True) as ser:
|
|
|
304
304
|
ser.stream(on_line=print, match=r"login:", stop_on_match=True, save_to="console.log")
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
-
### Serial via RDP /
|
|
308
|
-
`pyserial` only opens a *local* port, so when the serial
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
### Serial via RDP / SSH (port on a *remote* machine)
|
|
308
|
+
`pyserial` only opens a *local* port, so when the serial port is on a remote
|
|
309
|
+
machine, stream it **over SSH** with `serial_stream()` — same live match + save.
|
|
310
|
+
It auto-detects the OS from the device name: `COM*` → Windows (PowerShell
|
|
311
|
+
SerialPort reader), `/dev/tty*` → Linux (`stty` + `cat`).
|
|
312
|
+
|
|
313
|
+
**Windows COM port on the remote machine** (connect SSH straight to that machine
|
|
314
|
+
— it has sshd from `ssh-handler-setup`):
|
|
311
315
|
|
|
312
316
|
```python
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
317
|
+
cfg = SSHConfig(host="10.232.9.22", domain="CORP", username="myuser",
|
|
318
|
+
password="pw", host_key_policy="ignore")
|
|
319
|
+
with SSHHandler(cfg, quiet=True) as ssh:
|
|
320
|
+
ssh.serial_write("COM5", "version", baudrate=115200) # write a line
|
|
321
|
+
ssh.serial_stream("COM5", baudrate=115200, # read it live
|
|
322
|
+
on_line=print, match=r"login:|ERROR",
|
|
323
|
+
save_to="com5.log", timeout=120)
|
|
324
|
+
```
|
|
316
325
|
|
|
326
|
+
**Linux device file on a target reached through the jump:**
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
target = SSHConfig(host="10.120.1.91", username="root", password="pw",
|
|
330
|
+
jump_host=rdp_box, host_key_policy="ignore")
|
|
317
331
|
with SSHHandler(target, quiet=True) as ssh:
|
|
318
|
-
ssh.
|
|
319
|
-
|
|
320
|
-
on_line=print, match=r"login:|ERROR",
|
|
321
|
-
save_to="device_console.log", timeout=120)
|
|
332
|
+
ssh.serial_stream("/dev/ttyUSB0", baudrate=115200,
|
|
333
|
+
on_line=print, match=r"login:", save_to="ttyusb0.log")
|
|
322
334
|
```
|
|
323
335
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
336
|
+
> Note: on Windows a COM port can't be shared — don't run `serial_write` while a
|
|
337
|
+
> `serial_stream` on the same port is open (`serial_write` opens/writes/closes).
|
|
338
|
+
> If the port is on **your own laptop**, use the local `SerialHandler("COM5")`
|
|
339
|
+
> above instead — no SSH needed.
|
|
327
340
|
|
|
328
341
|
## File transfer (SFTP / SCP / FTP) via RDP
|
|
329
342
|
|
|
@@ -278,26 +278,39 @@ with SerialHandler("COM5", baudrate=115200, quiet=True) as ser:
|
|
|
278
278
|
ser.stream(on_line=print, match=r"login:", stop_on_match=True, save_to="console.log")
|
|
279
279
|
```
|
|
280
280
|
|
|
281
|
-
### Serial via RDP /
|
|
282
|
-
`pyserial` only opens a *local* port, so when the serial
|
|
283
|
-
|
|
284
|
-
|
|
281
|
+
### Serial via RDP / SSH (port on a *remote* machine)
|
|
282
|
+
`pyserial` only opens a *local* port, so when the serial port is on a remote
|
|
283
|
+
machine, stream it **over SSH** with `serial_stream()` — same live match + save.
|
|
284
|
+
It auto-detects the OS from the device name: `COM*` → Windows (PowerShell
|
|
285
|
+
SerialPort reader), `/dev/tty*` → Linux (`stty` + `cat`).
|
|
286
|
+
|
|
287
|
+
**Windows COM port on the remote machine** (connect SSH straight to that machine
|
|
288
|
+
— it has sshd from `ssh-handler-setup`):
|
|
285
289
|
|
|
286
290
|
```python
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
291
|
+
cfg = SSHConfig(host="10.232.9.22", domain="CORP", username="myuser",
|
|
292
|
+
password="pw", host_key_policy="ignore")
|
|
293
|
+
with SSHHandler(cfg, quiet=True) as ssh:
|
|
294
|
+
ssh.serial_write("COM5", "version", baudrate=115200) # write a line
|
|
295
|
+
ssh.serial_stream("COM5", baudrate=115200, # read it live
|
|
296
|
+
on_line=print, match=r"login:|ERROR",
|
|
297
|
+
save_to="com5.log", timeout=120)
|
|
298
|
+
```
|
|
290
299
|
|
|
300
|
+
**Linux device file on a target reached through the jump:**
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
target = SSHConfig(host="10.120.1.91", username="root", password="pw",
|
|
304
|
+
jump_host=rdp_box, host_key_policy="ignore")
|
|
291
305
|
with SSHHandler(target, quiet=True) as ssh:
|
|
292
|
-
ssh.
|
|
293
|
-
|
|
294
|
-
on_line=print, match=r"login:|ERROR",
|
|
295
|
-
save_to="device_console.log", timeout=120)
|
|
306
|
+
ssh.serial_stream("/dev/ttyUSB0", baudrate=115200,
|
|
307
|
+
on_line=print, match=r"login:", save_to="ttyusb0.log")
|
|
296
308
|
```
|
|
297
309
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
310
|
+
> Note: on Windows a COM port can't be shared — don't run `serial_write` while a
|
|
311
|
+
> `serial_stream` on the same port is open (`serial_write` opens/writes/closes).
|
|
312
|
+
> If the port is on **your own laptop**, use the local `SerialHandler("COM5")`
|
|
313
|
+
> above instead — no SSH needed.
|
|
301
314
|
|
|
302
315
|
## File transfer (SFTP / SCP / FTP) via RDP
|
|
303
316
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ssh-handler"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.1"
|
|
8
8
|
description = "Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -698,36 +698,85 @@ class SSHHandler:
|
|
|
698
698
|
|
|
699
699
|
return self._guard("stream", _do, safe=safe)
|
|
700
700
|
|
|
701
|
+
@staticmethod
|
|
702
|
+
def _ps_encode(script: str) -> str:
|
|
703
|
+
"""Encode a PowerShell script for `powershell -EncodedCommand` (UTF-16LE
|
|
704
|
+
base64) - sidesteps all quoting issues over SSH -> cmd -> powershell."""
|
|
705
|
+
import base64
|
|
706
|
+
return base64.b64encode(script.encode("utf-16-le")).decode("ascii")
|
|
707
|
+
|
|
701
708
|
def serial_stream(self, device: str = "/dev/ttyUSB0", *, baudrate: int = 115200,
|
|
702
|
-
on_line=None, on_match=None, match=None,
|
|
709
|
+
mode: str = "auto", on_line=None, on_match=None, match=None,
|
|
703
710
|
stop_on_match: bool = False, save_to: Optional[str] = None,
|
|
704
711
|
timeout: Optional[float] = None, stop_event=None,
|
|
705
712
|
configure: bool = True, safe: Optional[bool] = None):
|
|
706
713
|
"""
|
|
707
|
-
Stream a serial
|
|
714
|
+
Stream a serial port attached to the **remote** host, over SSH — so it
|
|
708
715
|
works through a jump host (laptop -> RDP machine -> target). Same live
|
|
709
716
|
match + save-to-file model as :meth:`stream`.
|
|
710
717
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
718
|
+
``mode``:
|
|
719
|
+
* "auto" - "COMx" -> windows, otherwise linux (default)
|
|
720
|
+
* "windows" - read a COM port via a PowerShell SerialPort reader
|
|
721
|
+
* "linux" - set speed with ``stty`` then ``cat`` the device file
|
|
714
722
|
|
|
715
|
-
>>>
|
|
716
|
-
|
|
723
|
+
>>> # Windows COM port on the remote machine:
|
|
724
|
+
>>> ssh.serial_stream("COM5", baudrate=115200, match="login:",
|
|
725
|
+
... save_to="console.log", on_line=print)
|
|
726
|
+
>>> # Linux device file on the remote machine:
|
|
727
|
+
>>> ssh.serial_stream("/dev/ttyUSB0", baudrate=115200, on_line=print)
|
|
717
728
|
"""
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
729
|
+
is_windows = (mode == "windows" or
|
|
730
|
+
(mode == "auto" and device.upper().startswith("COM")))
|
|
731
|
+
if is_windows:
|
|
732
|
+
# Force UTF-8 console output so our UTF-8 reader doesn't get mojibake,
|
|
733
|
+
# and read whatever bytes are available (robust to any line ending)
|
|
734
|
+
# rather than ReadLine (which hangs if the device's EOL differs).
|
|
735
|
+
ps = (
|
|
736
|
+
"$ErrorActionPreference='SilentlyContinue';"
|
|
737
|
+
"[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;"
|
|
738
|
+
f"$p=New-Object System.IO.Ports.SerialPort '{device}',{int(baudrate)},"
|
|
739
|
+
"'None',8,'One';"
|
|
740
|
+
"$p.Encoding=[System.Text.Encoding]::UTF8;"
|
|
741
|
+
"$p.Open();"
|
|
742
|
+
"while($true){"
|
|
743
|
+
"if($p.BytesToRead -gt 0){"
|
|
744
|
+
"$s=$p.ReadExisting();[Console]::Out.Write($s);[Console]::Out.Flush()}"
|
|
745
|
+
"else{Start-Sleep -Milliseconds 30}}"
|
|
746
|
+
)
|
|
747
|
+
cmd = f"powershell -NoProfile -EncodedCommand {self._ps_encode(ps)}"
|
|
722
748
|
else:
|
|
723
|
-
|
|
749
|
+
dev = shlex.quote(device)
|
|
750
|
+
if configure:
|
|
751
|
+
cmd = (f"stty -F {dev} {int(baudrate)} raw -echo -echoe -echok "
|
|
752
|
+
f"2>/dev/null; cat {dev}")
|
|
753
|
+
else:
|
|
754
|
+
cmd = f"cat {dev}"
|
|
724
755
|
return self.stream(cmd, on_line=on_line, on_match=on_match, match=match,
|
|
725
756
|
stop_on_match=stop_on_match, save_to=save_to,
|
|
726
757
|
timeout=timeout, stop_event=stop_event, safe=safe)
|
|
727
758
|
|
|
728
|
-
def serial_write(self, device: str, data: str, *,
|
|
759
|
+
def serial_write(self, device: str, data: str, *, baudrate: int = 115200,
|
|
760
|
+
mode: str = "auto", newline: bool = True,
|
|
729
761
|
safe: Optional[bool] = None):
|
|
730
|
-
"""
|
|
762
|
+
"""
|
|
763
|
+
Write a line to a serial port on the remote host (over SSH/jump).
|
|
764
|
+
Opens, writes, and closes the port (don't run while serial_stream holds
|
|
765
|
+
it open on Windows - the port can't be shared).
|
|
766
|
+
"""
|
|
767
|
+
is_windows = (mode == "windows" or
|
|
768
|
+
(mode == "auto" and device.upper().startswith("COM")))
|
|
769
|
+
if is_windows:
|
|
770
|
+
payload = data.replace("'", "''")
|
|
771
|
+
meth = "WriteLine" if newline else "Write"
|
|
772
|
+
ps = (
|
|
773
|
+
f"$p=New-Object System.IO.Ports.SerialPort '{device}',{int(baudrate)},"
|
|
774
|
+
"'None',8,'One';"
|
|
775
|
+
f"$p.Open();$p.{meth}('{payload}');Start-Sleep -Milliseconds 200;"
|
|
776
|
+
"$p.Close()"
|
|
777
|
+
)
|
|
778
|
+
cmd = f"powershell -NoProfile -EncodedCommand {self._ps_encode(ps)}"
|
|
779
|
+
return self.run(cmd, safe=safe)
|
|
731
780
|
payload = data + ("\n" if newline else "")
|
|
732
781
|
dev = shlex.quote(device)
|
|
733
782
|
return self.run(f"printf %s {shlex.quote(payload)} > {dev}", safe=safe)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssh-handler
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
4
4
|
Summary: Extensive SSH/SFTP/SCP/FTP handler built on Paramiko, for test automation, CLIs and PyQt5 tools.
|
|
5
5
|
Author: ssh-handler contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -304,26 +304,39 @@ with SerialHandler("COM5", baudrate=115200, quiet=True) as ser:
|
|
|
304
304
|
ser.stream(on_line=print, match=r"login:", stop_on_match=True, save_to="console.log")
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
-
### Serial via RDP /
|
|
308
|
-
`pyserial` only opens a *local* port, so when the serial
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
### Serial via RDP / SSH (port on a *remote* machine)
|
|
308
|
+
`pyserial` only opens a *local* port, so when the serial port is on a remote
|
|
309
|
+
machine, stream it **over SSH** with `serial_stream()` — same live match + save.
|
|
310
|
+
It auto-detects the OS from the device name: `COM*` → Windows (PowerShell
|
|
311
|
+
SerialPort reader), `/dev/tty*` → Linux (`stty` + `cat`).
|
|
312
|
+
|
|
313
|
+
**Windows COM port on the remote machine** (connect SSH straight to that machine
|
|
314
|
+
— it has sshd from `ssh-handler-setup`):
|
|
311
315
|
|
|
312
316
|
```python
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
317
|
+
cfg = SSHConfig(host="10.232.9.22", domain="CORP", username="myuser",
|
|
318
|
+
password="pw", host_key_policy="ignore")
|
|
319
|
+
with SSHHandler(cfg, quiet=True) as ssh:
|
|
320
|
+
ssh.serial_write("COM5", "version", baudrate=115200) # write a line
|
|
321
|
+
ssh.serial_stream("COM5", baudrate=115200, # read it live
|
|
322
|
+
on_line=print, match=r"login:|ERROR",
|
|
323
|
+
save_to="com5.log", timeout=120)
|
|
324
|
+
```
|
|
316
325
|
|
|
326
|
+
**Linux device file on a target reached through the jump:**
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
target = SSHConfig(host="10.120.1.91", username="root", password="pw",
|
|
330
|
+
jump_host=rdp_box, host_key_policy="ignore")
|
|
317
331
|
with SSHHandler(target, quiet=True) as ssh:
|
|
318
|
-
ssh.
|
|
319
|
-
|
|
320
|
-
on_line=print, match=r"login:|ERROR",
|
|
321
|
-
save_to="device_console.log", timeout=120)
|
|
332
|
+
ssh.serial_stream("/dev/ttyUSB0", baudrate=115200,
|
|
333
|
+
on_line=print, match=r"login:", save_to="ttyusb0.log")
|
|
322
334
|
```
|
|
323
335
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
336
|
+
> Note: on Windows a COM port can't be shared — don't run `serial_write` while a
|
|
337
|
+
> `serial_stream` on the same port is open (`serial_write` opens/writes/closes).
|
|
338
|
+
> If the port is on **your own laptop**, use the local `SerialHandler("COM5")`
|
|
339
|
+
> above instead — no SSH needed.
|
|
327
340
|
|
|
328
341
|
## File transfer (SFTP / SCP / FTP) via RDP
|
|
329
342
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|