SSH#

SSH transport: one reusable session per batch, resolved through ~/.ssh/config.

class remote_compression.ssh.ExecResult(exit_status: int, stdout_tail: str = '', stderr_tail: str = '')[source]#

Outcome of a remote command.

exit_status#

Remote exit status; -1 when the server never sent one (treated as failure).

Type:

int

stdout_tail#

Last TAIL_BYTES bytes of stdout, decoded with replacement.

Type:

str

stderr_tail#

Last TAIL_BYTES bytes of stderr, decoded with replacement.

Type:

str

property ok#

True iff the exit status is 0.

Type:

bool

exception remote_compression.ssh.RemoteExecError[source]#

A remote command stopped producing output (inactivity timeout).

exception remote_compression.ssh.SSHConnectionError[source]#

Host unreachable, authentication failure, host key mismatch, or connection lost for good.

exception remote_compression.ssh.SSHError[source]#

Base class for errors of the ssh layer.

class remote_compression.ssh.SSHSession(hostname, config_path=None, attempts=3, connect_timeout=20.0, keepalive=30)[source]#

A reusable SSH connection (plus SFTP) to one destination.

Composition over inheritance: the session owns up to two paramiko.SSHClient (an optional ProxyJump gateway and the destination) and a lazy SFTP channel. Use as a context manager; it returns itself.

Parameters:
  • hostname (str) – Destination alias; extra parameters (HostName, User, Port, IdentityFile, ProxyJump) come from the openSSH config file.

  • config_path (str, optional) – Alternative openSSH config file.

  • attempts (int) – Connection attempts before giving up.

  • connect_timeout (float) – Timeout (seconds) applied to TCP, banner and auth phases.

  • keepalive (int) – Keepalive interval (seconds), applied to both transports.

close()[source]#

Close everything (sftp, destination, gateway), never raising.

ensure_connected()[source]#

Reconnect if the transport died.

is_alive()[source]#

bool: True iff the destination transport is up.

open()[source]#

Connect (idempotent). Returns the session itself.

reconnect()[source]#

Tear everything down and connect again.

run(command, inactivity_timeout=300.0, on_stdout=None)[source]#

Execute a remote command, draining its output continuously.

Continuous draining is not cosmetic: paramiko’s flow-control window (~2 MB) fills up otherwise and the remote process blocks on write. The timeout is an inactivity timeout, not a total duration: any output (e.g. the ffmpeg -progress stream) resets it.

Parameters:
  • command (str) – Command line (ASCII; syntax must suit both sh and cmd.exe).

  • inactivity_timeout (float) – Seconds without any output before giving up.

  • on_stdout (callable, optional) – Called with each decoded stdout chunk (progress feed).

Returns:

Exit status and bounded output tails.

Return type:

ExecResult

Raises:
property sftp#

lazy SFTP channel, reopened when dead.

Raises:
  • SSHError – When the server refuses the SFTP subsystem (e.g. Synology DSM ships with the SFTP service disabled) — with the remedy spelled out.

  • SSHConnectionError – When the connection died.

Type:

paramiko.SFTPClient

remote_compression.ssh.TAIL_BYTES = 4096#

Bytes of output kept per channel when reporting a command result.

remote_compression.ssh.get_config(hostname, config_path=None)[source]#
Parameters:
  • hostname (str) – Destination alias.

  • config_path (str, optional) – Location of the openSSH config file if different from ~/.ssh/config.

Returns:

Parameters for the destination (hostname, user, port, identityfile, proxyjump…).

Return type:

dict

remote_compression.ssh.is_localhost(hostname, config_path=None)[source]#

Tell whether an SSH destination is in fact this very machine.

Parameters:
  • hostname (str) – Destination alias (local is the explicit sentinel).

  • config_path (str, optional) – Alternative openSSH config file (mostly for tests).

Returns:

True when the effective HostName designates this machine. Guards: a ProxyJump or a non-standard port always means “remote” (a tunnel like HostName localhost / Port 2222 usually leads to another machine).

Return type:

bool

Notes

Known false negative, by design: a DNS name resolving to the public IP of a NAT that loops back to this machine (hairpin) is treated as remote — the public address matches no local interface. Everything still works, only through a pointless SSH round-trip. When it matters, use -D local or an alias whose HostName is the LAN name of the machine. Detecting this reliably would require comparing SSH host keys after connecting, which is not worth the machinery.

remote_compression.ssh.split_user_host(destination)[source]#

Split an [user@]alias destination.

Parameters:

destination (str) – e.g. nas or admin@nas.

Returns:

  • str or None – Explicit user, when given (it beats the ssh config User).

  • str – The alias to look up in the ssh config.