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'},
    ne='lab',
    ssh_options=SshOptions(port=2222, connect_timeout=5),
    telnet_options=TelnetOptions(auto_window_resize=True),
)
class otto.host.options.LocalPortForward(listen_host, listen_port, dest_host, dest_port)

Bases: object

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

listen_host : --is-rst--:py:class:`str`
listen_port : --is-rst--:py:class:`int`
dest_host : --is-rst--:py:class:`str`
dest_port : --is-rst--:py:class:`int`
class otto.host.options.RemotePortForward(listen_host, listen_port, dest_host, dest_port)

Bases: object

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

listen_host : --is-rst--:py:class:`str`
listen_port : --is-rst--:py:class:`int`
dest_host : --is-rst--:py:class:`str`
dest_port : --is-rst--:py:class:`int`
class otto.host.options.SocksForward(listen_host, listen_port)

Bases: object

A dynamic SOCKS forward listening on the given local address.

listen_host : --is-rst--:py:class:`str`
listen_port : --is-rst--:py:class:`int`
class otto.host.options.SshOptions(port=22, known_hosts=None, connect_timeout=None, keepalive_interval=None, keepalive_count_max=None, client_keys=None, client_host_keys=None, agent_forwarding=False, preferred_auth=None, encryption_algs=None, server_host_key_algs=None, compression_algs=None, local_forwards=<factory>, remote_forwards=<factory>, socks_forwards=<factory>, extra=<factory>, post_connect=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 : --is-rst--:py:class:`int`

TCP port for the SSH connection.

known_hosts : --is-rst--:py:data:`~typing.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 : --is-rst--:py:class:`float` | :py:obj:`None`

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

keepalive_interval : --is-rst--:py:class:`float` | :py:obj:`None`

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

keepalive_count_max : --is-rst--:py:class:`int` | :py:obj:`None`

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

client_keys : --is-rst--:py:class:`list`\ \[:py:class:`str`] | :py:obj:`None`

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

client_host_keys : --is-rst--:py:class:`list`\ \[:py:class:`str`] | :py:obj:`None`

Host-key paths for host-based auth.

agent_forwarding : --is-rst--:py:class:`bool`

Forward the local SSH agent to the remote side.

preferred_auth : --is-rst--:py:class:`str` | :py:class:`list`\ \[:py:class:`str`] | :py:obj:`None`

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

encryption_algs : --is-rst--:py:class:`list`\ \[:py:class:`str`] | :py:obj:`None`

Allowed symmetric ciphers.

server_host_key_algs : --is-rst--:py:class:`list`\ \[:py:class:`str`] | :py:obj:`None`

Allowed server host-key algorithms.

compression_algs : --is-rst--:py:class:`list`\ \[:py:class:`str`] | :py:obj:`None`

Allowed compression algorithms.

local_forwards : --is-rst--:py:class:`list`\ \[:py:class:`~otto.host.options.LocalPortForward`]

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

remote_forwards : --is-rst--:py:class:`list`\ \[:py:class:`~otto.host.options.RemotePortForward`]

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

socks_forwards : --is-rst--:py:class:`list`\ \[:py:class:`~otto.host.options.SocksForward`]

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

extra : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:data:`~typing.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 : --is-rst--:py:class:`~collections.abc.Callable`\ \[\[SSHClientConnection], :py:class:`~collections.abc.Awaitable`\ \[:py:obj:`None`]] | :py:obj:`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=23, write_chunk_size=0, write_chunk_delay=0.0, cols=400, rows=24, encoding=False, connect_timeout=None, echo_negotiation_timeout=3.0, login_prompt=b':', login=True, single_client_console=False, auto_window_resize=False, extra=<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 : --is-rst--:py:class:`int`

TCP port for the telnet connection.

write_chunk_size : --is-rst--:py:class:`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 : --is-rst--:py:class:`float`

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

cols : --is-rst--:py:class:`int`

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

rows : --is-rst--:py:class:`int`

Initial terminal height reported to the remote side.

encoding : --is-rst--:py:class:`str` | :py:class:`bool`

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

connect_timeout : --is-rst--:py:class:`float` | :py:obj:`None`

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

echo_negotiation_timeout : --is-rst--:py:class:`float`

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

login_prompt : --is-rst--:py:class:`bytes`

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

login : --is-rst--:py:class:`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 : --is-rst--:py:class:`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 : --is-rst--:py:class:`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 : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:data:`~typing.Any`]

Extra kwargs forwarded to telnetlib3.open_connection().

class otto.host.options.SftpOptions(env=None, send_env=None, extra=<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 : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:class:`str`] | :py:obj:`None`

Environment variables to set in the remote SFTP process.

send_env : --is-rst--:py:class:`list`\ \[:py:class:`str`] | :py:obj:`None`

Local env vars to forward to the remote SFTP process.

extra : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:data:`~typing.Any`]

Extra kwargs forwarded to SSHClientConnection.start_sftp_client().

class otto.host.options.ScpOptions(preserve=False, recurse=True, block_size=16384, extra=<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 : --is-rst--:py:class:`bool`

Preserve mtime/atime/mode on transferred files.

recurse : --is-rst--:py:class:`bool`

Recurse into directories.

block_size : --is-rst--:py:class:`int`

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

extra : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:data:`~typing.Any`]

Extra kwargs forwarded to asyncssh.scp().

class otto.host.options.FtpOptions(port=21, encoding='utf-8', socket_timeout=None, connection_timeout=None, path_timeout=None, read_speed_limit=None, write_speed_limit=None, ssl=None, passive_commands=('epsv', 'pasv'), extra=<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 : --is-rst--:py:class:`int`

TCP port for the FTP control connection.

encoding : --is-rst--:py:class:`str`

Text encoding for FTP commands and paths.

socket_timeout : --is-rst--:py:class:`float` | :py:obj:`None`

Socket-level read/write timeout.

connection_timeout : --is-rst--:py:class:`float` | :py:obj:`None`

Handshake timeout.

path_timeout : --is-rst--:py:class:`float` | :py:obj:`None`

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

read_speed_limit : --is-rst--:py:class:`int` | :py:obj:`None`

Bytes/sec cap on downloads. None = unlimited.

write_speed_limit : --is-rst--:py:class:`int` | :py:obj:`None`

Bytes/sec cap on uploads. None = unlimited.

ssl : --is-rst--:py:data:`~typing.Any`

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

passive_commands : --is-rst--:py:class:`tuple`\ \[:py:class:`str`, :py:data:`...<Ellipsis>`]

Passive-mode commands to attempt, in order.

extra : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:data:`~typing.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='nc', port=9000, port_strategy='auto', port_cmd=None, listener_check='auto', listener_cmd=None, listener_timeout=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 : --is-rst--:py:class:`str`

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

port : --is-rst--:py:class:`int`

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

port_strategy : --is-rst--:py:data:`~typing.Literal`\ \[``'auto'``, ``'ss'``, ``'netstat'``, ``'python'``, ``'proc'``, ``'custom'``]

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

port_cmd : --is-rst--:py:class:`str` | :py:obj:`None`

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

listener_check : --is-rst--:py:data:`~typing.Literal`\ \[``'auto'``, ``'ss'``, ``'netstat'``, ``'proc'``, ``'custom'``]

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

listener_cmd : --is-rst--:py:class:`str` | :py:obj:`None`

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

listener_timeout : --is-rst--:py:class:`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=69, server_ip=None, block_size=512, timeout=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 : --is-rst--:py:class:`int`

UDP port of the TFTP server.

server_ip : --is-rst--:py:class:`str` | :py:obj:`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 : --is-rst--:py:class:`int`

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

timeout : --is-rst--:py:class:`float`

Per-block retransmit timeout, in seconds.

class otto.host.options.SnmpOptions(oids=(), community='public', port=161, version='2c', address=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 : --is-rst--:py:class:`tuple`\ \[:py:class:`str`, :py:data:`...<Ellipsis>`]

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

community : --is-rst--:py:class:`str`

SNMP v2c community string.

port : --is-rst--:py:class:`int`

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

version : --is-rst--:py:class:`str`

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

address : --is-rst--:py:class:`str` | :py:obj:`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.