host.options

Connection option classes for the network protocols otto speaks.

Each protocol (SSH, Telnet, SFTP, SCP, FTP, netcat) has an *Options dataclass that holds tunable connection parameters. Default-constructed instances reproduce otto’s pre-options behavior exactly, so callers who do not care about configuration can ignore this module.

For SSH, curated fields cover the common knobs and an extra dict plus a post_connect hook together forward the full power of asyncssh (kwargs and post-connect method calls like port forwarding).

Example:

host = UnixHost(
    ip='10.0.0.1',
    creds={'admin': 'secret'},
    element='lab',
    ssh_options=SshOptions(port=2222, connect_timeout=5),
    telnet_options=TelnetOptions(auto_window_resize=True),
)
class otto.host.options.LocalPortForward(listen_host: str, listen_port: int, dest_host: str, dest_port: int)

Bases: object

An SSH local port forward: listen locally, send to host:port via the remote.

listen_host : str
listen_port : int
dest_host : str
dest_port : int
class otto.host.options.RemotePortForward(listen_host: str, listen_port: int, dest_host: str, dest_port: int)

Bases: object

An SSH remote port forward: listen on the remote, send to host:port locally.

listen_host : str
listen_port : int
dest_host : str
dest_port : int
class otto.host.options.SocksForward(listen_host: str, listen_port: int)

Bases: object

A dynamic SOCKS forward listening on the given local address.

listen_host : str
listen_port : int
class otto.host.options.SshOptions(port: int = 22, known_hosts: ~typing.Any | None = None, connect_timeout: float | None = None, keepalive_interval: float | None = None, keepalive_count_max: int | None = None, client_keys: list[str] | None = None, client_host_keys: list[str] | None = None, agent_forwarding: bool = False, preferred_auth: str | list[str] | None = None, encryption_algs: list[str] | None = None, server_host_key_algs: list[str] | None = None, compression_algs: list[str] | None = None, local_forwards: list[~otto.host.options.LocalPortForward] = <factory>, remote_forwards: list[~otto.host.options.RemotePortForward] = <factory>, socks_forwards: list[~otto.host.options.SocksForward] = <factory>, extra: dict[str, ~typing.Any] = <factory>, post_connect: ~collections.abc.Callable[[SSHClientConnection], ~collections.abc.Awaitable[None]] | None = None)

Bases: object

Connection options for asyncssh-backed SSH sessions.

Covers the common connection knobs as curated fields plus two escape hatches (extra and post_connect) that together give access to every asyncssh feature.

Default-constructed SshOptions() reproduces otto’s historical behavior: port 22 with host-key verification disabled.

port : int

TCP port for the SSH connection.

known_hosts : Any

asyncssh known_hosts. None disables host-key verification (otto’s historical default). Pass a path, a list of keys, or the string '~/.ssh/known_hosts' to enable checking.

connect_timeout : float | None

Seconds to wait for the TCP + SSH handshake. None = asyncssh default.

keepalive_interval : float | None

Seconds between SSH-level keepalives. None = asyncssh default (disabled).

keepalive_count_max : int | None

Missed keepalives before asyncssh closes the connection. None = default.

client_keys : list[str] | None

Private-key paths for public-key auth. None lets asyncssh auto-discover.

client_host_keys : list[str] | None

Host-key paths for host-based auth.

agent_forwarding : bool

Forward the local SSH agent to the remote side.

preferred_auth : str | list[str] | None

Authentication methods to attempt, in order (e.g. 'publickey,password').

encryption_algs : list[str] | None

Allowed symmetric ciphers.

server_host_key_algs : list[str] | None

Allowed server host-key algorithms.

compression_algs : list[str] | None

Allowed compression algorithms.

local_forwards : list[LocalPortForward]

Local port forwards to set up after the connection is established.

remote_forwards : list[RemotePortForward]

Remote port forwards to set up after the connection is established.

socks_forwards : list[SocksForward]

Dynamic SOCKS forwards to set up after the connection is established.

extra : dict[str, Any]

Arbitrary extra kwargs forwarded directly to asyncssh.connect().

Anything kwarg-shaped on asyncssh’s connect (config, proxy_command, x509_trusted_certs, gss_host, etc.) can be set here. Values in extra override curated fields on conflict.

post_connect : Callable[[SSHClientConnection], Awaitable[None]] | None

Optional async hook called with the freshly opened connection, after the structured forward lists have been applied. Use for anything not expressible as a kwarg or a structured forward — e.g. UNIX-socket forwards, X11, custom subsystems.

class otto.host.options.TelnetOptions(port: int = 23, write_chunk_size: int = 0, write_chunk_delay: float = 0.0, cols: int = 400, rows: int = 24, encoding: str | bool = False, connect_timeout: float | None = None, echo_negotiation_timeout: float = 3.0, login_prompt: bytes = b':', login: bool = True, single_client_console: bool = False, auto_window_resize: bool = False, extra: dict[str, ~typing.Any] = <factory>)

Bases: object

Connection options for telnetlib3-backed telnet sessions.

Default-constructed TelnetOptions() reproduces otto’s historical behavior: port 23, bytes mode, cols=400, and a 3-second ECHO negotiation timeout.

port : int

TCP port for the telnet connection.

write_chunk_size : int

Split each command write into chunks of at most this many bytes. 0 (default) writes the whole payload in one call — correct for a host-terminated telnet shell (x86 + E1000). A positive value paces the write so a UART-backed RTOS shell behind a -serial telnet: bridge doesn’t overrun its console RX FIFO on a multi-KB llext load_hex line.

write_chunk_delay : float

Seconds to pause between chunked writes (see write_chunk_size). Ignored when write_chunk_size is 0.

cols : int

Initial terminal width reported to the remote side. otto historically used 400 to avoid line-wrap artifacts in automation output.

rows : int

Initial terminal height reported to the remote side.

encoding : str | bool

Text encoding. False = bytes mode (otto default).

connect_timeout : float | None

Seconds to wait for the telnet TCP handshake. None = no timeout.

echo_negotiation_timeout : float

Seconds to wait for the remote to honor DONT ECHO during connect.

login_prompt : bytes

Byte delimiter that terminates the login/password prompts. Anything ending in a colon matches login:, Username:, Password:, etc.

login : bool

Whether connect() performs a telnet login (waits for the login/password prompts and sends credentials). Set False for a shell with no login step — e.g. a bare-metal/RTOS telnet shell — where waiting for a login: prompt that never arrives would hang the connection.

single_client_console : bool

When True, this connection targets a single-client console — an RTOS telnet shell that serves one client at a time (e.g. Zephyr shell_telnet reached over a -serial telnet: bridge). The transport is registered in a process-local set so the embedded test teardown can force-release the slot if a timed-out test left it half-open (see otto.host.telnet.abort_console_transports()). Unix telnet (multi-session telnetd) leaves this False, so it is never registered or aborted.

auto_window_resize : bool

When True and stdin is a TTY, install a SIGWINCH handler that sends a NAWS update on every resize so remote TUIs reflow. Off by default; opt in for interactive telnet sessions.

extra : dict[str, Any]

Extra kwargs forwarded to telnetlib3.open_connection().

class otto.host.options.SftpOptions(env: dict[str, str] | None = None, send_env: list[str] | None = None, extra: dict[str, ~typing.Any] = <factory>)

Bases: object

Connection options for asyncssh-backed SFTP clients.

SFTP rides on the underlying SSH connection, so connection-level tuning belongs in SshOptions. These knobs configure the SFTP subsystem itself.

env : dict[str, str] | None

Environment variables to set in the remote SFTP process.

send_env : list[str] | None

Local env vars to forward to the remote SFTP process.

extra : dict[str, Any]

Extra kwargs forwarded to SSHClientConnection.start_sftp_client().

class otto.host.options.ScpOptions(preserve: bool = False, recurse: bool = True, block_size: int = 16384, extra: dict[str, ~typing.Any] = <factory>)

Bases: object

Connection options for asyncssh.scp file transfers.

Only protocol-level knobs live here; the SSH connection itself is configured via SshOptions.

preserve : bool

Preserve mtime/atime/mode on transferred files.

recurse : bool

Recurse into directories.

block_size : int

Chunk size for SCP transfers. Larger = faster on fast links, more RAM.

extra : dict[str, Any]

Extra kwargs forwarded to asyncssh.scp().

class otto.host.options.FtpOptions(port: int = 21, encoding: str = 'utf-8', socket_timeout: float | None = None, connection_timeout: float | None = None, path_timeout: float | None = None, read_speed_limit: int | None = None, write_speed_limit: int | None = None, ssl: ~typing.Any | None = None, passive_commands: tuple[str, ...] = ('epsv', 'pasv'), extra: dict[str, ~typing.Any] = <factory>)

Bases: object

Connection options for aioftp-backed FTP clients.

Default-constructed FtpOptions() reproduces otto’s historical behavior: port 21, UTF-8 encoding, aioftp defaults for everything else.

port : int

TCP port for the FTP control connection.

encoding : str

Text encoding for FTP commands and paths.

socket_timeout : float | None

Socket-level read/write timeout.

connection_timeout : float | None

Handshake timeout.

path_timeout : float | None

Timeout for path-level operations (list/stat/etc.).

read_speed_limit : int | None

Bytes/sec cap on downloads. None = unlimited.

write_speed_limit : int | None

Bytes/sec cap on uploads. None = unlimited.

ssl : Any

ssl.SSLContext, True, or None. Use an SSLContext for FTPS.

passive_commands : tuple[str, ...]

Passive-mode commands to attempt, in order.

extra : dict[str, Any]

Extra kwargs forwarded to aioftp.Client(). aioftp also accepts arbitrary siosocks_asyncio_kwargs which can be routed through here.

class otto.host.options.NcOptions(exec_name: str = 'nc', port: int = 9000, port_strategy: NcPortStrategy = 'auto', port_cmd: str | None = None, listener_check: NcListenerCheck = 'auto', listener_cmd: str | None = None, listener_timeout: float = 30.0)

Bases: object

Connection options for netcat-based file transfers.

Bundles all nc-specific knobs that previously lived on UnixHost into a single object so that NcOptions() (with defaults) produces the same behavior as the old individual fields.

exec_name : str

Netcat executable on both sides (e.g. nc, ncat, netcat). Listener syntax is assumed to be OpenBSD-style (nc -l PORT).

port : int

Base port for netcat transfers. Used as the scan-start for the ss/netstat/python/proc port-finding strategies.

port_strategy : NcPortStrategy

Strategy for finding free ports on the remote host. 'auto' probes ss → netstat → python → proc and caches the first that works.

port_cmd : str | None

Shell command that prints a free port to stdout. Only used when port_strategy == 'custom'.

listener_check : NcListenerCheck

Strategy for verifying that a remote nc listener is ready before sending data. 'auto' probes ss → netstat → proc.

listener_cmd : str | None

Shell command (using {port} as placeholder) that exits 0 when a port is listening. Only used when listener_check == 'custom'.

listener_timeout : float

Seconds a remote nc -l listener may wait for a client before it self-terminates (passed as nc -w), and the ceiling on the post-transfer wait for that listener to exit. Bounds the orphaned-listener hang: if a concurrent process wins a port-collision race, our sender’s bytes land in its listener and ours never gets a client — without this it would block forever. A transfer whose connection is established stays unaffected; this only caps the wait for a client that never arrives.

class otto.host.options.TftpOptions(port: int = 69, server_ip: str | None = None, block_size: int = 512, timeout: float = 5.0)

Bases: object

Connection options for TFTP file transfer to an embedded host.

Reserved. TFTP is the deferred tftp embedded-transfer backend — a faster alternative to console fs transfer for targets whose firmware has a TFTP client. The backend itself is not yet implemented (EmbeddedFileTransfer raises NotImplementedError for tftp); this dataclass exists so lab data and the host API can name the option table now, without a later breaking change when the backend lands.

port : int

UDP port of the TFTP server.

server_ip : str | None

IP address otto’s TFTP server binds to and the target transfers against. None auto-detects the local IP, as the netcat path does.

block_size : int

TFTP block size (RFC 2348 blksize option). 512 is the protocol default; larger blocks reduce round-trips on a reliable link.

timeout : float

Per-block retransmit timeout, in seconds.

class otto.host.options.SnmpOptions(oids: tuple[str, ...] = (), community: str = 'public', port: int = 161, version: str = '2c', address: str | None = None)

Bases: object

Per-host SNMP polling config — the acquisition half of SNMP monitoring.

Declared in lab data as a host’s snmp block; carries only what to poll and how to reach the agent. Presentation for each OID (chart/unit/scale) lives in the monitor module’s descriptor registry, never here. The monitor factory turns this into a live SnmpClient + SnmpSource.

A host carrying an snmp block is monitored over SNMP instead of by running shell commands — the way otto reaches an embedded target’s metrics without contending for its single shell session. It is not embedded-only; a Unix host may declare one too.

oids : tuple[str, ...]

OIDs to GET each tick (e.g. ("1.3.6.1.2.1.1.3.0", ...)).

community : str

SNMP v2c community string.

port : int

UDP port of the agent (or of a relay standing in for it).

version : str

SNMP version — "2c" (default) or "1".

address : str | None

Address otto sends SNMP to. None (the default) means use the host’s own ip; set it when the agent is reached at a different address than the host’s primary interface — e.g. a relay endpoint, or (future) a named interface from a per-host interface map. See todo/multi_interface_hosts.md.