Repository Setup

Otto discovers your project through a .otto/settings.toml file at the repository root. This page explains every setting and what happens during project initialization.

The settings file

Create .otto/settings.toml in your repo root:

name = "my_project"
version = "1.0.0"

labs  = ["${sut_dir}/../lab_data"]
libs  = ["${sut_dir}/pylib"]
tests = ["${sut_dir}/tests"]
init  = ["my_instructions", "my_shared_options"]

# Optional: product preferences applied to every host this repo touches.
# Selector = Python regex matched against the host id; ".*" = all hosts.
# Values win over hosts.json; CLI --term/--transfer win over everything.
[host_preferences.".*"]
ssh_options = { connect_timeout = 5.0, keepalive_interval = 30 }

Variable expansion

${sut_dir} is replaced with the absolute path to the repo root at load time. Use it to keep paths relative and portable. Expansion runs inside every settings table, including string values nested under [host_preferences].

Field reference

name

Required. Product or repository name. Displayed in CLI panels and log output.

version

Required. Semantic version string (e.g. "1.0.0").

labs

List of directory paths to search for lab JSON files. When you pass --lab my_lab, otto looks in these directories for a file matching that name. Defaults to [].

[lab]

Optional table selecting the host-source backend — where otto’s hosts come from. backend names a registered source (defaults to "json", which reads hosts.json from the labs directories); a [lab.<name>] sub-table holds that backend’s keyword arguments. See Host Database for the full treatment.

libs

List of Python package directories to add to sys.path at startup. This is where you put your instruction modules, shared options, and helper libraries. Defaults to [].

tests

List of directories to scan for test_*.py files. Each matching file is imported at startup, which triggers @register_suite() decorators and makes suites available as otto test subcommands. Defaults to [].

init

List of Python module names (dot-separated) to import at startup. Use this to register instructions (@command()) and shared option classes. These modules must be importable from one of the libs directories. Defaults to [].

[host_preferences]

Optional table of product-wide selector-scoped preferences. Each sub-table key is a Python regex matched against host ids; inner keys are term, transfer (ordered backend lists) or *_options tables (per-key option values that win over hosts.json). See Product host preferences in Lab Configuration for the full schema and precedence rules.

Migration note: [host_defaults] was removed; its option tables move under [host_preferences."<selector>".<opt>].

[os_profiles]

Optional table of named OS-profile bundles. Each [os_profiles.<name>] sub-table must contain a base key naming a registered host class (e.g. "unix", "zephyr", or a class registered by an init module) and may contain any default field values to bundle with that profile. Profiles are registered into the global OS-profile registry so lab-data entries can select them by os_type name. See OS Profiles & Custom Host Classes for the full treatment.

[reservations]

Optional table enabling the reservation gate — otto refuses to start live-lab commands against resources the current user doesn’t hold. backend names a registered scheduler source ("none" — the default — disables the gate; "json" reads a reservation file). See Lab Reservations for backends, the file format, and the --as-user / -R break-glass overrides.

What happens at startup

When you run any otto command, the following initialization sequence occurs:

  1. Environment parsing – Otto reads OTTO_SUT_DIRS to find repo root directories.

  2. Repo discovery – For each path in OTTO_SUT_DIRS, otto creates a Repo object and reads its .otto/settings.toml.

  3. Apply settings – For each repo, otto:

    • Adds libs directories to sys.path

    • Imports modules listed in init (this registers instructions)

    • Auto-imports all test_*.py files from tests directories (this registers suites)

  4. Lab loading – Otto builds the host source via build_lab_repository (selected by [lab] backend, defaulting to the built-in json source over the merged labs search paths) and loads the lab(s) named by --lab or OTTO_LAB. Multiple labs are merged, combining their hosts. The host source is pluggable — see Host Database.

  5. Context creation – The global OttoContext is created with the loaded repos and lab and installed via set_context(), making hosts available to the zero-argument accessors (get_host, all_hosts) in all commands.

Multiple repos

Otto supports multiple repos simultaneously. Set OTTO_SUT_DIRS to a comma-separated list:

export OTTO_SUT_DIRS=/path/to/repo1,/path/to/repo2

Each repo has its own settings, libs, tests, and lab search paths. They are all merged at startup – instructions and suites from every repo appear in the CLI, and lab search paths from all repos are combined.

Lab files

Each directory listed under labs holds a hosts.json file describing the hosts at that location. The full per-host schema — every field, the connection-option tables, repo-level host defaults, and how labs merge — lives in Lab Configuration.

Team setup checklist

Most of otto’s configuration is a one-time, team-level decision. New contributors then just clone and run. Work through this map once when adopting otto for a team:

  1. Create .otto/settings.tomlname, version, and the labs / libs / tests / init paths (this page, above).

  2. Choose a host source — the built-in json source (commit hosts.json under a labs directory) is the default; point [lab] backend at a CMDB or inventory API if you have one. See Host Database.

  3. Decide on reservation gating — leave it off (backend = "none", the default) for sandbox labs, or wire [reservations] to your scheduler so otto refuses to clobber a held rack. Tell the team about the --as-user and -R / --skip-reservation-check break-glass overrides before they need them. See Lab Reservations.

  4. Register shared code — put instruction/option modules under libs and list them in init; auto-import test suites from tests. See otto run and otto test.

  5. Set per-product preferences — optional [host_preferences] / [os_profiles] (this page, above, and Lab Configuration / OS Profiles & Custom Host Classes).

  6. Enable tab completion — see Getting Started.

Each backend choice is verifiable: otto ships conformance helpers (otto.testing.assert_lab_repository_conforms / assert_reservation_backend_conforms) so a custom host source or reservation backend can be checked against otto’s contract in your own test suite.