host.embeddedHost

Embedded (bare-metal / RTOS) host class.

An EmbeddedHost is a network-reached target whose “OS” is a real-time kernel or bare-metal firmware rather than a POSIX system — Zephyr is the first concrete example. It is exposed through the same Host API as UnixHost (run/oneshot/send/ expect/put/get) so test code does not care whether a target is a Linux box or a microcontroller.

What makes an embedded target different from a Unix host:

  • One console. A Zephyr device exposes a single shell over telnet. There is no second channel and no stateless exec primitive, so oneshot shares the one persistent session with run and is therefore not concurrency-safe (it is on UnixHost).

  • No bash. No $?, no command substitution, no scp/ftp/nc. Command framing and file transfer cannot reuse the Unix machinery.

  • Telnet only. The shell is reached over telnet (optionally through an SSH hop), never SSH directly.

Command execution requires a command frame: a CommandFrame instance that frames each command for the target’s RTOS shell over the plain telnet transport and parses the output/return-code back. There is no default frame — a bare EmbeddedHost raises ValueError at construction if none is supplied (fail loud). The frame is provided by:

  • a registered OsProfile data bundle (e.g. a command_frame key in an [os_profiles.<name>] settings table), or

  • a concrete subclass that re-declares the default, or

  • an explicit constructor argument.

ZephyrHost is the in-tree concrete class: it subclasses EmbeddedHost and declares ZephyrFrame as the default command_frame (along with osType='zephyr' and osName='Zephyr'). Zephyr-specific framing and OS naming live on ZephyrHost, not on the base class.

File transfer (get/put) is delegated to EmbeddedFileTransfer, which speaks the device shell only (the console backend uses Zephyr’s fs commands). The interactive bridge (_interact) currently raises NotImplementedError.

class otto.host.embeddedHost.EmbeddedHost(ip, ne, osType='embedded', osName=None, osVersion=None, name=None, creds=<factory>, user=None, neId=None, board=None, slot=None, is_virtual=False, transfer='console', filesystem=<factory>, command_frame=None, default_dest_dir=<factory>, max_filename_len=255, telnet_options=<factory>, snmp=None, toolchain=<factory>, hop=None, resources=<factory>, log=True, log_stdout=True, _connection_factory=None)

Bases: RemoteHost

OS-agnostic bare-metal / RTOS host reached over telnet.

EmbeddedHost carries no OS-specific defaults. A command_frame must be supplied — either via a profile, a subclass (e.g. ZephyrHost), or an explicit constructor argument — or construction raises ValueError (fail loud). ZephyrHost is the in-tree concrete subclass and worked example.

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

IP address of the host’s telnet shell.

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

Network element to which this host belongs.

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

Default profile selector for a bare EmbeddedHost. Subclasses (e.g. ZephyrHost) override this to their registered name.

osName : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`str`]

Kernel/OS name, or None. A bare embedded host carries no OS name; a concrete subclass (e.g. ZephyrHost) sets it.

osVersion : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`str`]

OS/kernel version string, or None if unspecified.

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

Human readable name to represent the host. Automatically generated if not provided.

creds : --is-rst--:py:class:`dict`\ \[:py:class:`str`, :py:class:`str`]

Users and their respective passwords. Optional — the Zephyr telnet shell backend has no login step, so this is empty for a stock Zephyr target.

user : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`str`]

User with which to log in, if the shell requires one. Usually unset.

neId : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`int`]

Network element identifier to which this host belongs.

board : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`str`]

Name of the board type to which this host belongs.

slot : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`int`]

Physical slot number of the board to which this host belongs.

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

Determines whether a host is a VM/emulator (e.g. QEMU) or not.

transfer : --is-rst--:py:data:`~typing.Literal`\ \[``'console'``, ``'tftp'``]

File-transfer backend. console (default) drives the device shell’s fs commands; tftp is reserved and not yet implemented.

filesystem : --is-rst--:py:class:`~otto.host.embedded_filesystem.EmbeddedFileSystem`

On-device filesystem variant — e.g. FatRamFileSystem, LittleFsFileSystem, or NoFileSystem (the default). Carries the mount path, the optional fs mount command, and the command-formation hooks the transfer code and the embedded monitor’s disk parser drive. See otto.host.embedded_filesystem.

Lab data declares the variant by string in the filesystem field; the storage factory resolves the string to a class. Projects can register custom variants via otto.host.embedded_filesystem.register_filesystem().

command_frame : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`~otto.host.command_frame.CommandFrame`]

Shell-framing dialect for this target’s console — how a command is wrapped in sentinels and how output/retcode are parsed back. There is NO default: a bare embedded host carries no dialect, so a frame is required — supplied either by a profile/subclass (e.g. ZephyrHost) or as an explicit value. A frame-less EmbeddedHost fails loud at construction.

Lab data declares the dialect by string in the command_frame field (e.g. a Zephyr 2.7 build that reports its retcode inline would name a project-registered frame); the storage factory resolves the string to an instance. Projects can register custom dialects via otto.host.command_frame.register_command_frame(). The dialect is independent of the transport, so it is handed straight to the SessionManager.

default_dest_dir : --is-rst--:py:class:`~pathlib.Path`

Default landing directory for put / get when the caller supplies an empty or relative dest_dir. When left at the default (an empty Path()), __post_init__ resolves it to filesystem.mount so generic fan-out callers like do_for_all_hosts() don’t have to branch on host type. Override in lab data to land transfers somewhere other than the FS root. See RemoteHost.default_dest_dir.

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

Upper bound on the basename length (including extension) accepted by the target’s filesystem. Defaults to 255 — the Linux NAME_MAX, also the typical LittleFS ceiling. Override per-host when the firmware enforces a tighter limit (e.g. 32 for a Zephyr build that sets CONFIG_FS_FATFS_MAX_LFN=32 / CONFIG_FS_LITTLEFS_NAME_MAX=32, or 12 for a stock FAT 8.3 build without LFN support). See RemoteHost.max_filename_len.

telnet_options : --is-rst--:py:class:`~otto.host.options.TelnetOptions`

Connection options for the telnet shell (port, cols/rows, etc.).

snmp : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`~otto.host.options.SnmpOptions`]

Optional SNMP polling config (lab snmp block). When set, otto’s monitor collects this host’s metrics over SNMP — a separate channel from the single telnet console — instead of running shell commands. See SnmpOptions.

toolchain : --is-rst--:py:class:`~otto.host.toolchain.Toolchain`

Cross-toolchain for this bed’s products. Used by the coverage pipeline to select the correct gcov/lcov. The host is the test bed, so it owns the toolchain matching its target ABI — a Zephyr 3.7 bed and a 4.4 bed declare different SDKs. Defaults to system-installed tools.

hop : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`str`]

Host ID of the intermediate SSH hop used to reach this host, or None.

resources : --is-rst--:py:class:`set`\ \[:py:class:`str`]

Names of resources required to use this host.

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

Whether this host should log its output to stdout and log files.

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

Whether this host should log its output to stdout.

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

Unique identifier for this host.

async verify_connection()

Attempt to open the telnet shell without running commands (dry-run).

Return type:

CommandStatus

async close()
Return type:

None

async oneshot(cmd, timeout=None)

Run a single command on the embedded host.

Unlike UnixHost.oneshot(), this is not concurrency-safe: an embedded target exposes a single console with no stateless exec primitive, so oneshot runs on the same persistent session as run(). It exists for API parity; use run() for stateful workflows.

Return type:

CommandStatus

async open_session(name)

Open a named persistent shell session.

Note: an embedded target has a single console. Opening a second named session opens a second telnet connection to the device, which most RTOS shell backends do not accept concurrently. Prefer the default session via run().

Return type:

HostSession

async send(text)

Send raw text to the host’s persistent session.

Return type:

None

async expect(pattern, timeout=10.0)

Wait for a pattern in the host’s session output stream.

Return type:

str

async get(src_files, dest_dir, show_progress=True)

Transfer files from the embedded host to the local machine.

Delegates to EmbeddedFileTransfer, which speaks the device shell (the console backend uses Zephyr’s fs commands). Transfers are sequential — an embedded target has a single console.

Return type:

tuple[Status, str]

async put(src_files, dest_dir, show_progress=True)

Transfer files from the local machine to the embedded host.

Delegates to EmbeddedFileTransfer (the console backend writes via Zephyr’s chunked fs write). Transfers are sequential — an embedded target has a single console.

dest_dir is resolved against default_dest_dir so a generic Path() from a fan-out caller lands on the host’s mounted filesystem (e.g. /RAM: on a FAT target) rather than on Zephyr’s bare /, which has no FS and rejects opens with -ENOENT.

Return type:

tuple[Status, str]

class otto.host.embeddedHost.ZephyrHost(ip, ne, osType='zephyr', osName='Zephyr', osVersion=None, name=None, creds=<factory>, user=None, neId=None, board=None, slot=None, is_virtual=False, transfer='console', filesystem=<factory>, command_frame=<factory>, default_dest_dir=<factory>, max_filename_len=255, telnet_options=<factory>, snmp=None, toolchain=<factory>, hop=None, resources=<factory>, log=True, log_stdout=True, _connection_factory=None)

Bases: EmbeddedHost

A Zephyr RTOS host — the concrete, registered embedded host.

This is the worked example for shipping a host subclass: it re-declares the Zephyr-specific field defaults that EmbeddedHost no longer assumes, and is registered under osType: "zephyr" via otto.host.os_profile.register_host_class(). External repositories register their own EmbeddedHost/UnixHost subclasses the same way (from an init module listed in .otto/settings.toml), and may layer per-build OsProfile data bundles over them.

ip : --is-rst--str

IP address of the host’s telnet shell.

ne : --is-rst--str

Network element to which this host belongs.

osVersion : --is-rst--Optional[str]

OS/kernel version string, or None if unspecified.

name : --is-rst--str

Human readable name to represent the host. Automatically generated if not provided.

creds : --is-rst--dict[str, str]

Users and their respective passwords. Optional — the Zephyr telnet shell backend has no login step, so this is empty for a stock Zephyr target.

user : --is-rst--Optional[str]

User with which to log in, if the shell requires one. Usually unset.

neId : --is-rst--Optional[int]

Network element identifier to which this host belongs.

board : --is-rst--Optional[str]

Name of the board type to which this host belongs.

slot : --is-rst--Optional[int]

Physical slot number of the board to which this host belongs.

is_virtual : --is-rst--bool

Determines whether a host is a VM/emulator (e.g. QEMU) or not.

transfer : --is-rst--EmbeddedTransferType

File-transfer backend. console (default) drives the device shell’s fs commands; tftp is reserved and not yet implemented.

filesystem : --is-rst--EmbeddedFileSystem

On-device filesystem variant — e.g. FatRamFileSystem, LittleFsFileSystem, or NoFileSystem (the default). Carries the mount path, the optional fs mount command, and the command-formation hooks the transfer code and the embedded monitor’s disk parser drive. See otto.host.embedded_filesystem.

Lab data declares the variant by string in the filesystem field; the storage factory resolves the string to a class. Projects can register custom variants via otto.host.embedded_filesystem.register_filesystem().

default_dest_dir : --is-rst--Path

Default landing directory for put / get when the caller supplies an empty or relative dest_dir. When left at the default (an empty Path()), __post_init__ resolves it to filesystem.mount so generic fan-out callers like do_for_all_hosts() don’t have to branch on host type. Override in lab data to land transfers somewhere other than the FS root. See RemoteHost.default_dest_dir.

max_filename_len : --is-rst--int

Upper bound on the basename length (including extension) accepted by the target’s filesystem. Defaults to 255 — the Linux NAME_MAX, also the typical LittleFS ceiling. Override per-host when the firmware enforces a tighter limit (e.g. 32 for a Zephyr build that sets CONFIG_FS_FATFS_MAX_LFN=32 / CONFIG_FS_LITTLEFS_NAME_MAX=32, or 12 for a stock FAT 8.3 build without LFN support). See RemoteHost.max_filename_len.

telnet_options : --is-rst--TelnetOptions

Connection options for the telnet shell (port, cols/rows, etc.).

snmp : --is-rst--Optional[SnmpOptions]

Optional SNMP polling config (lab snmp block). When set, otto’s monitor collects this host’s metrics over SNMP — a separate channel from the single telnet console — instead of running shell commands. See SnmpOptions.

toolchain : --is-rst--Toolchain

Cross-toolchain for this bed’s products. Used by the coverage pipeline to select the correct gcov/lcov. The host is the test bed, so it owns the toolchain matching its target ABI — a Zephyr 3.7 bed and a 4.4 bed declare different SDKs. Defaults to system-installed tools.

hop : --is-rst--Optional[str]

Host ID of the intermediate SSH hop used to reach this host, or None.

resources : --is-rst--set[str]

Names of resources required to use this host.

log : --is-rst--bool

Whether this host should log its output to stdout and log files.

log_stdout : --is-rst--bool

Whether this host should log its output to stdout.

id : --is-rst--str

Unique identifier for this host.

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

Profile selector recorded on the host. zephyr for this class.

osName : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`str`]

Kernel/OS name — Zephyr for this class.

command_frame : --is-rst--:py:class:`~otto.host.command_frame.CommandFrame`

Stock Zephyr retval shell framing (3.7 / 4.4 LTS).