Working with hosts¶
otto host provides direct access to host operations from the command line –
running commands, transferring files, opening an interactive shell, and invoking
host capabilities – without writing a test suite or instruction.
Syntax¶
The host ID comes before the subcommand, so all host-level options apply to every action:
otto host <host_id> <command> [ARGS...] [OPTIONS]
The host verb model¶
Every otto host action is a verb on the host. The four core verbs –
run, put, get, and login – are built in (see Core commands).
Every other verb is a capability verb: a host method marked @cli_exposed
that otto turns into a subcommand automatically, scoped to that host’s class.
otto host <host_id> --help lists exactly the verbs the chosen host supports.
See Capability verbs for the capability verbs and Netcat transfers,
Connections, and Configuration for transport and tuning.
Listing hosts¶
Use --list-hosts to see which host IDs are available in the loaded lab:
otto --lab my_lab host --list-hosts
This is the same --list-hosts option available on the top-level otto command.
Dry run¶
Like all otto commands, --dry-run (or -n) previews what would happen without
executing commands or transferring files:
otto --lab my_lab --dry-run host router1 run "make install"
From Python¶
The otto host subcommands map directly to methods on the
BaseHost class. Everything otto host does from the CLI
can also be done inside instructions and test suites:
>>> host = LocalHost()
>>> result = run(host.run(["echo hello", "echo world"]))
>>> result.status
<Status.Success: 0>
>>> [cr.value.strip() for cr in result]
['hello', 'world']
File transfers work the same way – put and get map to
put() and
get():
from pathlib import Path
# Upload
res = await host.put(
src_files=[Path("firmware.bin")],
dest_dir=Path("/tmp"),
)
if not res:
logger.error(f"upload failed: {res.msg}")
# Download
res = await host.get(
src_files=[Path("/var/log/syslog")],
dest_dir=Path("./logs"),
)
if not res:
logger.error(f"download failed: {res.msg}")
Note
put and get are available on all host types, with per-class semantics:
LocalHost copies files within the local
filesystem, UnixHost transfers between the
local machine and the remote host, and EmbeddedHost provides its own
console/tftp transfer path; see Embedded Hosts.
Exit codes¶
Every otto host <name> <verb> invocation derives its exit code from the
verb’s returned Result family, via Result.exit_code.
Command results are ssh-like: the shell’s retcode when the command ran,
255 when it never ran. (oneshot is Python-only — it is not a CLI verb,
so these rows apply to run.)
Situation |
Exit code |
|---|---|
Verb succeeded (incl. |
0 |
|
that command’s shell retcode (ssh-like: |
|
255 (matches ssh’s convention) |
Any other verb: |
1 |
Any other verb: |
2 (note: Click also uses 2 for CLI usage errors) |
Any other verb: |
3 |
Custom verbs on third-party host classes may return plain values instead of a
Result; the CLI prints them as-is and exits 0.