ssh-handler 1.0.8__tar.gz → 1.1.0__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.
Files changed (29) hide show
  1. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/PKG-INFO +111 -10
  2. ssh_handler-1.0.8/ssh_handler.egg-info/PKG-INFO → ssh_handler-1.1.0/README.md +472 -405
  3. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/pyproject.toml +3 -2
  4. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/__init__.py +4 -1
  5. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/cli.py +57 -0
  6. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/config.py +5 -1
  7. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/core.py +105 -0
  8. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/pyqt_worker.py +25 -0
  9. ssh_handler-1.1.0/ssh_handler/serial_handler.py +200 -0
  10. ssh_handler-1.0.8/README.md → ssh_handler-1.1.0/ssh_handler.egg-info/PKG-INFO +506 -374
  11. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler.egg-info/SOURCES.txt +1 -0
  12. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler.egg-info/requires.txt +4 -0
  13. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/LICENSE +0 -0
  14. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/setup.cfg +0 -0
  15. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/__main__.py +0 -0
  16. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/credentials.py +0 -0
  17. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/exceptions.py +0 -0
  18. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/ftp.py +0 -0
  19. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/openssh/OpenSSH-ARM64.zip +0 -0
  20. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/openssh/OpenSSH-Win32.zip +0 -0
  21. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/openssh/OpenSSH-Win64.zip +0 -0
  22. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/pool.py +0 -0
  23. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/results.py +0 -0
  24. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/setup_openssh_server.ps1 +0 -0
  25. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler/winrm_bootstrap.py +0 -0
  26. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler.egg-info/dependency_links.txt +0 -0
  27. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler.egg-info/entry_points.txt +0 -0
  28. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/ssh_handler.egg-info/top_level.txt +0 -0
  29. {ssh_handler-1.0.8 → ssh_handler-1.1.0}/tests/test_offline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-handler
3
- Version: 1.0.8
3
+ Version: 1.1.0
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
@@ -22,11 +22,14 @@ Provides-Extra: gui
22
22
  Requires-Dist: PyQt5>=5.15; extra == "gui"
23
23
  Provides-Extra: winrm
24
24
  Requires-Dist: pywinrm>=0.4.3; extra == "winrm"
25
+ Provides-Extra: serial
26
+ Requires-Dist: pyserial>=3.5; extra == "serial"
25
27
  Provides-Extra: all
26
28
  Requires-Dist: keyring>=23.0; extra == "all"
27
29
  Requires-Dist: scp>=0.14; extra == "all"
28
30
  Requires-Dist: PyQt5>=5.15; extra == "all"
29
31
  Requires-Dist: pywinrm>=0.4.3; extra == "all"
32
+ Requires-Dist: pyserial>=3.5; extra == "all"
30
33
  Dynamic: license-file
31
34
 
32
35
  # ssh-handler
@@ -127,6 +130,18 @@ with SSHHandler(SSHConfig(host="10.0.0.5", username="root", password="pw")) as s
127
130
  - `open_shell()` — a persistent interactive `ShellSession` with `send` /
128
131
  `read_until` (send-expect) / `read_available`.
129
132
 
133
+ **Continuous / streaming output (logs)**
134
+ - `iter_lines(cmd)` — generator yielding a never-ending command's output **line
135
+ by line, live** (`slog2info -w`, `tail -f`, `journalctl -f`, `dmesg -w`).
136
+ - `stream(cmd, on_line=, match=, save_to=, stop_on_match=, stop_event=)` — stream
137
+ with **live regex matching**, a per-line/per-match callback, and **tee to a
138
+ local file**, all built in.
139
+
140
+ **Serial / COM ports** (`SerialHandler`, needs the `serial` extra)
141
+ - `list_serial_ports()`, `open`/`close`, `write` / `write_line`.
142
+ - `iter_lines()` and `stream(...)` — same live streaming + match + save-to-file
143
+ model as SSH, for device consoles.
144
+
130
145
  **File operations (SFTP) — full Paramiko parity**
131
146
  - Transfers: `push` / `pull` (single file **or** recursive directory, with progress
132
147
  callbacks and transfer statistics), plus `scp_push` / `scp_pull` (SCP protocol).
@@ -247,6 +262,65 @@ When a connection just fails, the error now self-diagnoses — it probes the SSH
247
262
  RDP ports and tells you *why* (e.g. "Port 22 is closed but RDP (3389) is open … no
248
263
  SSH server listening"). Call `ssh.diagnose()` for a pre-flight reachability check.
249
264
 
265
+ ## Continuous logs & live pattern matching
266
+
267
+ Stream a long-running remote command and react to lines as they arrive — match a
268
+ pattern, save to a file, or stop when something appears. Works through the jump
269
+ host too.
270
+
271
+ ```python
272
+ from ssh_handler import SSHHandler, SSHConfig
273
+
274
+ with SSHHandler(SSHConfig(host="10.120.1.91", username="root", password="pw",
275
+ jump_host=rdp_box), quiet=True) as ssh:
276
+
277
+ # (a) simplest: iterate lines live
278
+ for line in ssh.iter_lines("slog2info -w"):
279
+ print(line)
280
+ if "FATAL" in line:
281
+ break
282
+
283
+ # (b) full: match + tee to a local file + callback, stop on a pattern
284
+ result = ssh.stream(
285
+ "tail -f /var/log/messages",
286
+ on_line=print, # called for every line
287
+ match=r"error|fail", # regex; matching lines collected
288
+ on_match=lambda l: print("HIT:", l),
289
+ save_to="device.log", # tee every line to this local file
290
+ stop_on_match=False, # set True to stop at the first match
291
+ timeout=60, # optional overall time limit
292
+ )
293
+ print(result["lines"], "lines,", len(result["matches"]), "matched")
294
+ ```
295
+
296
+ To stop a stream from another thread (e.g. a GUI Stop button), pass a
297
+ `threading.Event` as `stop_event=` and `.set()` it.
298
+
299
+ ## Serial / COM ports
300
+
301
+ Same streaming + match + save model for device serial consoles (needs
302
+ `pip install "ssh-handler[serial]"`).
303
+
304
+ ```python
305
+ from ssh_handler import SerialHandler, list_serial_ports
306
+
307
+ print(list_serial_ports()) # [{'device':'COM5','description':...}, ...]
308
+
309
+ with SerialHandler("COM5", baudrate=115200, quiet=True) as ser:
310
+ ser.write_line("version") # send a command
311
+ res = ser.stream(
312
+ on_line=print,
313
+ match=r"login:", # wait for the login prompt
314
+ stop_on_match=True,
315
+ save_to="serial_console.log", # tee to file
316
+ timeout=120,
317
+ )
318
+ print("matched:", res["matched"])
319
+ ```
320
+
321
+ `write_line(..., eol="\r\n")` for consoles that need CRLF. Everything returns the
322
+ same `OperationResult` in safe mode and raises `SerialError` otherwise.
323
+
250
324
  ## Confidential credentials
251
325
 
252
326
  | Mechanism | What it does |
@@ -306,17 +380,29 @@ Every action returns structured data, not bare strings:
306
380
  ## CLI reference
307
381
 
308
382
  ```bash
309
- python -m ssh_handler run --host H --user U --domain CORP uptime
310
- python -m ssh_handler push --host H --user U ./build /tmp/build --recursive
311
- python -m ssh_handler pull --host H --user U /var/log ./logs --recursive
312
- python -m ssh_handler info --host H --user U --json
383
+ python -m ssh_handler run --host H --user U --domain CORP uptime
384
+ python -m ssh_handler push --host H --user U ./build /tmp/build --recursive
385
+ python -m ssh_handler pull --host H --user U /var/log ./logs --recursive
386
+ python -m ssh_handler info --host H --user U --json
313
387
  python -m ssh_handler store-credential --user U --domain CORP --service my_test_lab
388
+
389
+ # continuous logs over SSH, with live matching + save:
390
+ python -m ssh_handler stream --host H --user U --match "error|fail" \
391
+ --save run.log -- slog2info -w
392
+
393
+ # serial / COM ports:
394
+ python -m ssh_handler list-serial
395
+ python -m ssh_handler serial-monitor --port COM5 --baud 115200 \
396
+ --match "login:" --stop-on-match --save console.log
397
+
398
+ # install OpenSSH Server on THIS Windows machine (offline, self-elevates):
399
+ ssh-handler-setup
314
400
  ```
315
401
 
316
402
  Password options: `--password` (hidden prompt), `--use-stored` (read from the OS
317
403
  vault), `--key FILE` (private key). Add `--json` for machine-readable output.
318
- After `pip install`, a `ssh-handler` console script is also available
319
- (`ssh-handler run --host …`).
404
+ Put `--match`/`--save` *before* the streamed command. After `pip install`, the
405
+ `ssh-handler` and `ssh-handler-setup` console scripts are also available.
320
406
 
321
407
  ## PyQt5 integration
322
408
 
@@ -337,8 +423,22 @@ thread.started.connect(lambda: worker.run_command("uptime"))
337
423
  thread.start()
338
424
  ```
339
425
 
340
- Signals: `log`, `connected`, `command_done`, `transfer_done`, `progress`, `error`,
341
- `finished`. The import is lazy, so the rest of the package works where PyQt5 isn't installed.
426
+ Signals: `log`, `connected`, `command_done`, `transfer_done`, `progress`,
427
+ `stream_line`, `stream_match`, `stream_done`, `error`, `finished`. The import is
428
+ lazy, so the rest of the package works where PyQt5 isn't installed.
429
+
430
+ **Streaming logs into the GUI** — drive `start_stream` in the worker thread and
431
+ wire the per-line signals to your widgets; `stop_stream()` ends it cleanly:
432
+
433
+ ```python
434
+ worker.stream_line.connect(log_view.append) # every live line
435
+ worker.stream_match.connect(lambda l: alerts.append(l)) # only matching lines
436
+ thread.started.connect(lambda: worker.start_stream("slog2info -w",
437
+ match="error|fail",
438
+ save_to="device.log"))
439
+ # later, from a Stop button:
440
+ worker.stop_stream()
441
+ ```
342
442
 
343
443
  ## Parallel fleet operations
344
444
 
@@ -372,8 +472,9 @@ with FTPHandler(FTPConfig(host="ftp.example.com", username="u",
372
472
  ssh_handler/
373
473
  config.py SSHConfig, FTPConfig
374
474
  credentials.py Secret, CredentialStore, mask, prompt_password
375
- core.py SSHHandler, ShellSession (SSH + SFTP + SCP + diagnose)
475
+ core.py SSHHandler, ShellSession (SSH + SFTP + SCP + stream + diagnose)
376
476
  ftp.py FTPHandler (FTP / FTPS)
477
+ serial_handler.py SerialHandler, list_serial_ports (serial / COM ports)
377
478
  winrm_bootstrap.py enable_openssh_via_winrm (one-time sshd enable over WinRM)
378
479
  pool.py SSHPool (parallel multi-host)
379
480
  cli.py argparse entry point (python -m ssh_handler / ssh-handler)