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
oneshotshares the one persistent session withrunand is therefore not concurrency-safe (it is onUnixHost).No bash. No
$?, no command substitution, noscp/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
OsProfiledata bundle (e.g. acommand_framekey in an[os_profiles.<name>]settings table), ora 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, loader=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:
RemoteHostOS-agnostic bare-metal / RTOS host reached over telnet.
EmbeddedHostcarries no OS-specific defaults. Acommand_framemust be supplied — either via a profile, a subclass (e.g.ZephyrHost), or an explicit constructor argument — or construction raisesValueError(fail loud).ZephyrHostis 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
embeddedhost 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’sfscommands;tftpis reserved and not yet implemented.
- filesystem : --is-rst--:py:class:`~otto.host.embedded_filesystem.EmbeddedFileSystem`¶
On-device filesystem variant — e.g.
FatRamFileSystem,LittleFsFileSystem, orNoFileSystem(the default). Carries the mount path, the optionalfs mountcommand, and the command-formation hooks the transfer code and the embedded monitor’s disk parser drive. Seeotto.host.embedded_filesystem.Lab data declares the variant by string in the
filesystemfield; the storage factory resolves the string to a class. Projects can register custom variants viaotto.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
embeddedhost carries no dialect, so a frame is required — supplied either by a profile/subclass (e.g.ZephyrHost) or as an explicit value. A frame-lessEmbeddedHostfails loud at construction.Lab data declares the dialect by string in the
command_framefield (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 viaotto.host.command_frame.register_command_frame(). The dialect is independent of the transport, so it is handed straight to theSessionManager.
- loader : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`~otto.host.binary_loader.BinaryLoader`]¶
Binary-load strategy for this target’s runtime (e.g. Zephyr LLEXT). Unlike
command_frameit is optional — many embedded hosts never load binaries. Lab data declares it by string in theloaderfield (e.g."llext-hex");__post_init__resolves the string to an instance.load()/unload()fail loud (ValueError) when it is None. Projects register custom loaders viaotto.host.binary_loader.register_binary_loader().
- default_dest_dir : --is-rst--:py:class:`~pathlib.Path`¶
Default landing directory for
put/getwhen the caller supplies an empty or relativedest_dir. When left at the default (an emptyPath()),__post_init__resolves it tofilesystem.mountso generic fan-out callers likedo_for_all_hosts()don’t have to branch on host type. Override in lab data to land transfers somewhere other than the FS root. SeeRemoteHost.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 LinuxNAME_MAX, also the typical LittleFS ceiling. Override per-host when the firmware enforces a tighter limit (e.g.32for a Zephyr build that setsCONFIG_FS_FATFS_MAX_LFN=32/CONFIG_FS_LITTLEFS_NAME_MAX=32, or12for a stock FAT 8.3 build without LFN support). SeeRemoteHost.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
snmpblock). 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. SeeSnmpOptions.
- 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:¶
-
async oneshot(cmd, timeout=
None, log=True)¶ 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, sooneshotruns on the same persistent session asrun(). It exists for API parity; userun()for stateful workflows.- Return type:¶
- 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:¶
-
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 (theconsolebackend uses Zephyr’sfscommands). Transfers are sequential — an embedded target has a single console.
-
async put(src_files, dest_dir, show_progress=
True)¶ Transfer files from the local machine to the embedded host.
Delegates to
EmbeddedFileTransfer(theconsolebackend writes via Zephyr’s chunkedfs write). Transfers are sequential — an embedded target has a single console.dest_diris resolved againstdefault_dest_dirso a genericPath()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.
-
async load(file, name, show_progress=
False, timeout=120.0)¶ Load a binary into the device runtime via the host’s binary loader.
Distinct from
put()(a file transfer to a mounted filesystem):loadpushes a binary into the target’s loader (e.g. Zephyr LLEXT’sllext load_hex), with no destination file. The payload is read from file, formatted into the device command by the loader, and sent withlog=Falseso the (large) encoded payload never reaches the console or log. Returns(Status, str)likeput()/get(); thestrcarries the device’s failure text on error.show_progressis off by default (the bar only renders in interactive /otto run; underotto testoutput is captured). When enabled it drives a transfer-style Rich bar from the paced telnet write of the payload — the only measurable progress (the device’s relocation emits no incremental signal). Fails loud (ValueError) if the host declares no loader.
-
async unload(name, timeout=
20.0)¶ Unload name from the device runtime, draining to full eviction.
Some loaders (LLEXT) refcount a resident binary, so one unload may only decrement it.
unloadloops the loader’s unload command untilis_fully_unloaded()reports the binary gone (bounded byloader.max_unload_rounds). Idempotent: unloading something not loaded succeeds on the first round. Returns(Status, str); fails loud (ValueError) if no loader is declared.
- 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>, loader=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:
EmbeddedHostA 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
EmbeddedHostno longer assumes, and is registered underosType: "zephyr"viaotto.host.os_profile.register_host_class(). External repositories register their ownEmbeddedHost/UnixHostsubclasses the same way (from an init module listed in.otto/settings.toml), and may layer per-buildOsProfiledata 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’sfscommands;tftpis reserved and not yet implemented.
- filesystem : --is-rst--EmbeddedFileSystem¶
On-device filesystem variant — e.g.
FatRamFileSystem,LittleFsFileSystem, orNoFileSystem(the default). Carries the mount path, the optionalfs mountcommand, and the command-formation hooks the transfer code and the embedded monitor’s disk parser drive. Seeotto.host.embedded_filesystem.Lab data declares the variant by string in the
filesystemfield; the storage factory resolves the string to a class. Projects can register custom variants viaotto.host.embedded_filesystem.register_filesystem().
- loader : --is-rst--Optional[BinaryLoader]¶
Binary-load strategy for this target’s runtime (e.g. Zephyr LLEXT). Unlike
command_frameit is optional — many embedded hosts never load binaries. Lab data declares it by string in theloaderfield (e.g."llext-hex");__post_init__resolves the string to an instance.load()/unload()fail loud (ValueError) when it is None. Projects register custom loaders viaotto.host.binary_loader.register_binary_loader().
- default_dest_dir : --is-rst--Path¶
Default landing directory for
put/getwhen the caller supplies an empty or relativedest_dir. When left at the default (an emptyPath()),__post_init__resolves it tofilesystem.mountso generic fan-out callers likedo_for_all_hosts()don’t have to branch on host type. Override in lab data to land transfers somewhere other than the FS root. SeeRemoteHost.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 LinuxNAME_MAX, also the typical LittleFS ceiling. Override per-host when the firmware enforces a tighter limit (e.g.32for a Zephyr build that setsCONFIG_FS_FATFS_MAX_LFN=32/CONFIG_FS_LITTLEFS_NAME_MAX=32, or12for a stock FAT 8.3 build without LFN support). SeeRemoteHost.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
snmpblock). 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. SeeSnmpOptions.
- 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.
zephyrfor this class.
- osName : --is-rst--:py:data:`~typing.Optional`\ \[:py:class:`str`]¶
Kernel/OS name —
Zephyrfor this class.
- command_frame : --is-rst--:py:class:`~otto.host.command_frame.CommandFrame`¶
Stock Zephyr
retvalshell framing (3.7 / 4.4 LTS).