Hardware Control

Control and manage hardware devices including DUTs, PiKVM interfaces, and QEMU virtual machines.

DUTCTL

Run DUTCTL for the DUT. This command can power-cycle and flash the DUT as well as read the serial of the DUT.

AttributeTypeRequiredDefaultDescription
cmdconstYesdutctlCommand Name
namestringYesName to uniquely identify the teststep. This name will show up in reports, logs and the WebUI
optionsOptionsObjectNoAdditional command options
parametersDUTCTLOptionsYesOptions passed into the dutctl cmd

DUTCTL Options

AttributeTypeRequiredDefaultDescription
hoststringYesHostname or IP address of the device.
commandstringYesCommand to execute on the device. Available commands: power, flash, serial.
args[]stringNoArguments for the command. Arguments depend on the command that is executed.
inputstringNoInput for the command, used only if command is set to serial.
uartintegerNoUART number to use for serial communication.

DUTCTL Expect Options

AttributeTypeRequiredDefaultDescription
regexstringYesexpectRegular expression to match against the output of the device for the serial command.

DUTCTL Example

- cmd: dutctl
  name: Shutdown the DUT.
  parameters:
    host: "[[attributes.Flasher]]"
    command: power
    args: ["off"]

- cmd: dutctl
  name: Flash the provided binary.
  parameters:
    host: "[[attributes.Flasher]]"
    command: flash
    args: [write, "[[input.Binary]]"]

- cmd: dutctl
  name: Turn the DUT on
  parameters:
    host: "[[attributes.Flasher]]"
    command: power
    args: ["hardreset"]
  options:
    timeout: 2m

PiKVM

Manage PiKVM operations.

AttributeTypeRequiredDefaultDescription
cmdconstYespikvmCommand name.
namestringYesUnique identifier for the teststep.
optionsOptionsObjectNoAdditional command options.
parametersPiKVMOptionsYesParameters for the pikvm command.

PiKVM Options

AttributeTypeRequiredDefaultDescription
commandstringYes (must be one of: image, screenshot, keyboard)Operation to perform via the PiKVM interface.
hoststringYesHostname or IP address of the PiKVM (format: “https://hostname”).
authobjectYes (must include username and password)Authentication details for the PiKVM API.
imageobjectConditionally required when command is imageImage configuration. Contains a path property with the image file path to upload and mount.
keyboardobjectConditionally required when command is keyboardKeyboard input configuration. Use either the keys (array of key sequences) or text (string) property.

PiKVM Examples

Image mounting:

cmd: pikvm
name: Mount OS Image via PiKVM
parameters:
  command: image
  host: "[[attributes.PiKVMHost]]"
  auth:
    username: root
    password: root
  image:
    path: "[[storage.Image]]/nixos.img"

Screenshot:

cmd: pikvm
name: Capture PiKVM Screenshot
parameters:
  command: screenshot
  host: "[[attributes.PiKVMHost]]"
  auth:
    username: root
    password: root

Keyboard input:

cmd: pikvm
name: Send Keyboard Input via PiKVM
parameters:
  command: keyboard
  host: "[[attributes.PiKVMHost]]"
  auth:
    username: root
    password: root
  keyboard:
    text: "INPUTTEXT"

or

cmd: pikvm
name: Send Keyboard Input via PiKVM
parameters:
  command: keyboard
  host: "[[attributes.PiKVMHost]]"
  auth:
    username: root
    password: root
  keyboard:
    keys: ["CTRL + C", "I", "N", "P", "U", "T", "ENTER"]

Qemu

Manage QEMU virtual machines.

AttributeTypeRequiredDefaultDescription
cmdconstYesqemuCommand Name
namestringYesName to uniquely identify the teststep. This name will show up in reports, logs and the WebUI
optionsOptionsObjectNoAdditional command options
parametersQemuOptionsYesOptions passed into the qemu cmd

Qemu Options

AttributeTypeRequiredDefaultDescription
modestringYesSupported modes are start, run and stop.
executablestringConditional (see below)The executable that should be executed.
firmwarestringConditional (see below)The firmware that should be used.
imagestringNoThe image that should be booted.
nprocintegerNoThe number of processors that should be used.
memintegerNoThe amount of memory that should be used.
variablestringConditional (see below)Name a variable to store the qemu PID for later session return.
serialQemuStepsOptionsYesSteps to interact with during test execution.

Behavior and semantics

  • The run mode can be used to attach to an already running QEMU instance. In this case, variable must be set to the PID of the QEMU process.
  • If variable is not set, the run mode will behave like the start mode, launching a new QEMU instance.
  • The stop mode will send a termination signal to the QEMU process identified by variable.

Conditional rules:

  • start: requires executable, firmware, and variable. The step launches QEMU and stores its PID in variable. If QEMU prints a PTY path it is stored in <variable>_pty.
  • run: if variable is provided the step attaches to the recorded PID/PTY and runs serial steps; if variable is empty, run requires both executable and firmware and will launch QEMU and run serial steps.
  • stop: requires variable and must not include executable, args, firmware or serial.

Qemu Serial Options

AttributeTypeRequiredDefaultDescription
sendstringNoA string that should be sent as input.
timeoutstringNoTimeout for the interaction. Format: h for hours, m for minutes, s for seconds (e.g., 1h30m20s).
expectQemuExpectOptionsNoExpectation for the interaction output.

Qemu Expect Options

AttributeTypeRequiredDefaultDescription
regexstringYesexpectRegular expression that should be matched against the output of the interaction.

BMCTest

Run BMC (Baseboard Management Controller) compliance and functionality tests using the bmc-test-go tool. This command executes automated tests to verify BMC behavior including IPMI, Redfish, and other BMC interfaces.

AttributeTypeRequiredDefaultDescription
cmdconstYesbmctestCommand Name
namestringYesName to uniquely identify the teststep. This name will show up in reports, logs and the WebUI
optionsOptionsObjectNoAdditional command options
parametersBMCTestOptionsYesOptions passed into the bmctest cmd

BMCTest Options

AttributeTypeRequiredDefaultDescription
commandstringYesbmc-test-go subcommand to execute: single, suite, all, run-from-cfg
config_pathstringYesPath to the YAML configuration file containing BMC device details
test_namestringConditional (required for single)Specific test to run (only used when command is single)
suite_namestringConditional (required for suite)Test suite to run: redfish, ipmi, ifaces, ipmi-mct, ipmi-twitter, pci
log_modestringNostructuredLogging format: default or structured
log_filestringNo/tmp/bmc-test-output.jsonPath for structured log output (only used when log_mode is structured)

BMCTest Command Details

  • single: Run a specific test by name (requires test_name)
  • suite: Run a test suite (requires suite_name)
  • all: Run all available tests
  • run-from-cfg: Run tests defined in the configuration file

BMCTest Examples

Run a complete test suite:

- cmd: bmctest
  name: Run IPMI Test Suite
  parameters:
    command: suite
    config_path: /tmp/storage/bmc_config/device.yaml
    suite_name: ipmi
  options:
    timeout: 30m

Run a single test:

- cmd: bmctest
  name: Test IPMI Power Status
  parameters:
    command: single
    config_path: /tmp/storage/bmc_config/device.yaml
    test_name: testIPMIPowerStatus
  options:
    timeout: 5m

Run all tests with custom log location:

- cmd: bmctest
  name: Run All BMC Tests
  parameters:
    command: all
    config_path: "[[storage.BMCConfig]]/bmc.yaml"
    log_mode: structured
    log_file: /tmp/bmc-results.json
  options:
    timeout: 1h

Run a suite with environment variables (e.g. for configs that reference $IMAGE or $GOLDEN_IMAGE):

- cmd: bmctest
  name: Run Redfish Test Suite
  options:
    timeout: 30m
    env:
      IMAGE: "[[input.Binary]]"
      GOLDEN_IMAGE: "[[storage.BoardImage]]"
      HOST_SSH_KEY: "[[storage.BMCConfig]]/ssh_key"
  parameters:
    command: suite
    config_path: "[[storage.BMCConfig]]/board.yaml"
    suite_name: redfish

Redfish

Run DMTF Redfish conformance testing against a BMC. Wraps the DMTF reference validators — Redfish-Service-Validator (schema, DSP8010), Redfish-Interop-Validator (profiles), and Redfish-Protocol-Validator (protocol behavior, DSP0266) — and adds a native inventory engine for board-specific value checks the validators do not perform. The validators run on the contest runner and connect to the BMC over HTTPS; their full reports (HTML/XLSX/TSV) are attached as downloadable artifacts while a per-check summary is folded into the step log.

AttributeTypeRequiredDefaultDescription
cmdconstYesredfishCommand Name
namestringYesName to uniquely identify the teststep. This name will show up in reports, logs and the WebUI
transportTransportObjectNolocalWhere the validator process runs — defaults to local (the runner). See Transport. The BMC connection itself is always HTTPS.
optionsOptionsObjectNoAdditional command options
parametersRedfishOptionsYesOptions passed into the redfish cmd

Quick start

Run the schema validator against a BMC:

- cmd: redfish
  name: Redfish schema conformance
  parameters:
    host: "[[attributes.BMC]]"
    auth: { user: root, password: "[[attributes.BMCPassword]]" }
    validate: service
  transport: { proto: local }
  options: { timeout: 20m }

Check a few board values with the native inventory engine. Most rules are four fields — a target, a property, a comparison, a value:

- cmd: redfish
  name: Board sanity
  parameters:
    host: "[[attributes.BMC]]"
    auth: { user: root, password: "[[attributes.BMCPassword]]" }
    validate: inventory
    inventory:
      rules:
        - { path: /redfish/v1, prop: RedfishVersion, op: exists }
        - { path: /redfish/v1/Systems/system, prop: PowerState, op: eq, value: "On" }
        - { path: /redfish/v1/Managers/bmc, prop: Status/Health, op: eq, value: OK }
  transport: { proto: local }

That covers most needs. The full inventory rule reference adds counts, ranges, per-component checks (forEach), multi-hop navigation (via), and composition (all/any) — reach for them only when a simple rule can’t express what you want.

Run the interop validator against a profile carried inline (no profile file needs to be staged on the runner):

- cmd: redfish
  name: Redfish interop profile
  parameters:
    host: "[[attributes.BMC]]"
    auth: { user: root, password: "[[attributes.BMCPassword]]" }
    validate: interop
    interop:
      profileDoc:
        SchemaDefinition: RedfishInteroperabilityProfile.v1_0_0
        ProfileName: BaselineManageability
        ProfileVersion: "1.0.0"
        Protocol: { MinVersion: "1.6.0" }
        Resources:
          ComputerSystem:
            PropertyRequirements:
              PowerState: { ReadRequirement: Mandatory }
              BiosVersion: { ReadRequirement: Mandatory }
  transport: { proto: local }

Alternatively set interop.profile to the path of a profile JSON already on the runner.

Redfish Options

AttributeTypeRequiredDefaultDescription
hoststringYesBMC host (and optional :port) of the Redfish service, without scheme. HTTPS is assumed.
authAuthObjectYesRedfish HTTP credentials the validators authenticate with against the BMC.
validatestring or []stringYesEngine(s) to run: service, interop, protocol, inventory. A single string or an array.
fatalstringNomustSeverity that fails the step: must (only MUST-level fails), recommended (MUST+RECOMMENDED), or all (any fail/warn).
insecurebooleanNotrueSkip TLS certificate verification (lab BMCs use self-signed certs). Enforced by the inventory and protocol engines; the service/interop validators always connect leniently.
serviceEngineOptionsNoOptions for the schema-conformance engine (Redfish-Service-Validator).
interopEngineOptionsNoOptions for the profile engine (Redfish-Interop-Validator). profile or profileDoc is required when interop is selected.
protocolEngineOptionsNoOptions for the protocol engine (Redfish-Protocol-Validator).
inventoryInventoryOptionsNoNative board-specific value checks.

Select one engine per step (with options.on-error: continue) to get an independent pass/fail per engine instead of one merged verdict.

Security

  • TLS verification is off by default (insecure: true) because lab BMCs ship self-signed certificates. Set insecure: false for any non-lab/production target. Note the wrapped service/interop validators always connect leniently regardless, so rely on the native inventory and protocol engines when strict TLS matters.
  • The BMC password is passed to the DMTF validators as a command-line argument, so it is briefly visible in the runner host’s process list (ps, /proc/<pid>/cmdline) while a validator runs. It is redacted from the step log. This is acceptable for lab BMCs with shared credentials; do not use credentials you would not want visible to anything with process-list access on the runner.

Transport

transport is optional and defaults to local — the validators run on the contest runner (where the DMTF tools are installed) and reach the BMC over HTTPS. Omit it for almost every deployment.

Use transport: { proto: ssh, options: { ... } } to run the validator processes on a remote SSH host instead. The cases where you need it:

  • The BMC is reachable only from a jump host / bastion, not from the contest runner — a segmented or air-gapped management network. Run the validators on a host that can actually reach the BMC.
  • The DMTF validators live on a different machine (an on-prem appliance or pre-provisioned host with BMC access) rather than in the runner image.

For ssh the target must have the DMTF validators on PATH (rf_service_validator, rf_interop_validator, rf_protocol_validator), and the runner needs SSH access to it (set under transport.options).

Caveat: only the service, interop, and protocol engines run over the transport. The native inventory engine is an HTTPS client that always runs on the runner, so ssh does not change where its requests originate — if the BMC is unreachable from the runner, keep inventory off ssh-only targets.

Redfish Auth Options

AttributeTypeRequiredDefaultDescription
userstringYesRedfish username.
passwordstringYesRedfish password.
modestringNobasicbasic sends credentials on every request; session creates a SessionService token, reuses it, and logs out at the end.

Redfish Engine Options

The service, interop, and protocol sub-blocks tune the wrapped DMTF validators. All accept extraArgs, an array of raw passthrough flags to the underlying tool.

AttributeApplies toTypeRequiredDescription
scopeservicestringNoPath to crawl as a subtree (--payload Tree); empty crawls the whole tree. For single-resource validation use extraArgs: ["--payload", "Single", "<uri>"].
oemCheckservicebooleanNoValidate OEM subtrees (omit to use the tool default).
profileinteropstringOne of profile/profileDocPath to an interop profile JSON already present on the runner. Use this or profileDoc.
profileDocinteropobjectOne of profile/profileDocA DMTF interoperability profile inline in the test config. It is written to a temp file and validated offline, so no profile file needs to be staged on the runner. Use this or profile.
extraArgsall[]stringNoRaw passthrough flags to the validator.

Redfish Inventory Options

The native engine fetches resources directly and checks board-specific values. Rules come from inline rules and/or an expected file.

AttributeTypeRequiredDefaultDescription
rules[]InventoryRuleNoInline value-match rules.
expectedstringNoPath to a YAML/JSON file of rules (a bare list or a {rules: [...]} object).

Inventory Rule

Each rule names a target (where to look), a value source (what to read), and a comparison (op + value).

AttributeTypeRequiredDescription
pathstringTarget (one of)Resource URI, e.g. /redfish/v1/Systems/system.
chassisstringTarget (one of)Glob selecting a chassis member by @odata.id, e.g. *S8047*.
via[]stringNoFollow these single-resource link names from the base before the check (multi-hop targets, e.g. chassis → Power → PowerSupplies). Each segment must be an @odata.id link to a single resource, not a collection.
propstringSource (one of)Property on the target. May be slash-nested to reach into objects, e.g. Status/Health.
collectionstringSource (one of)A sub-collection link whose member count is checked (use with count/ge/le/range).
forEachstringSource (one of)Iterate a collection link (members fetched individually) or an inline array of objects, applying the comparison to every member — one result each. Passes iff all members pass; an empty collection is SKIP.
whereMemberFilterNoforEach only: only members matching this predicate are checked; the rest are skipped (not failed).
all[]objectSource (one of)Composition (AND): passes iff every {prop, op, value} condition passes. Use instead of a single prop/op/value.
any[]objectSource (one of)Composition (OR): passes iff at least one {prop, op, value} condition passes.
opstringNoComparison — see below (default eq, or exists semantics for presence).
valuestringDepends on opExpected value, or min..max for range.

Comparison ops:

opMeaning
existsProperty is present.
eq / neString equality / inequality.
matchesvalue is a regex matched against the property.
containsvalue is an exact element of an array property, or a substring of a scalar.
ge / leNumeric >= / <= (inclusive), on a collection count or a numeric property.
rangeNumeric inclusive band; value is min..max (e.g. 4..8).
countCollection member count equals value.

Numeric ops parse both sides as numbers; a non-numeric operand fails the rule with a clear message rather than crashing.

Member Filter

A forEach inclusion predicate. Only members for which it holds are checked; the rest are skipped. The canonical use is ignoring empty slots, e.g. {prop: Status/State, op: ne, value: Absent}.

AttributeTypeRequiredDefaultDescription
propstringYesMember property to test (may be slash-nested).
opstringNoeqPredicate comparison (eq, ne, ge, le, matches, exists, contains).
valuestringNoPredicate value.

Multi-hop navigation and composition

via follows a chain of single-resource links from the base before the check, so a nested target works in one rule while keeping a dynamic chassis id — e.g. chassis: "*" with via: [Power] then forEach: PowerSupplies walks chassis → Power → PowerSupplies.

all / any replace a single prop/op/value with condition groups, evaluated against the target (or against each member under forEach). With both set, the rule passes iff every all condition passes and at least one any condition passes.

Note: The inventory rule grammar is complete and frozen — the fields above cover the intended use cases. Genuinely new needs are better met by a scripted check than by growing this mini-language further.

Redfish Examples

Schema conformance (full tree crawl):

- cmd: redfish
  name: Redfish schema conformance
  parameters:
    host: "[[attributes.BMC]]"
    auth:
      user: root
      password: "[[attributes.BMCPassword]]"
    validate: service
  transport:
    proto: local
  options:
    timeout: 20m

Inventory with fleet-aware checks — counts, per-member (forEach / where / range), multi-hop (via), and composition (all / any):

- cmd: redfish
  name: Redfish inventory
  parameters:
    host: "[[attributes.BMC]]"
    auth:
      user: root
      password: "[[attributes.BMCPassword]]"
    validate: inventory
    inventory:
      rules:
        # ServiceRoot advertises a version; the BMC reports health.
        - path: /redfish/v1
          prop: RedfishVersion
          op: exists
        - path: /redfish/v1/Managers/bmc
          prop: Status/Health
          op: eq
          value: OK
        # At least one DIMM is populated (exact counts vary per SKU).
        - path: /redfish/v1/Systems/system
          collection: Memory
          op: ge
          value: "1"
        # Every populated DIMM has a sane capacity; empty slots are skipped.
        - path: /redfish/v1/Systems/system
          forEach: Memory
          where:
            prop: Status/State
            op: ne
            value: Absent
          prop: CapacityMiB
          op: ge
          value: "1024"
        # Every CPU reports a plausible core count.
        - path: /redfish/v1/Systems/system
          forEach: Processors
          prop: TotalCores
          op: range
          value: "1..256"
        # Multi-hop: chassis -> Power -> every PSU healthy (one rule, dynamic id).
        - chassis: "*"
          via: [Power]
          forEach: PowerSupplies
          prop: Status/Health
          op: eq
          value: OK
        # Composition: System is On AND healthy; accept either BIOS property.
        - path: /redfish/v1/Systems/system
          all:
            - { prop: PowerState, op: eq, value: "On" }
            - { prop: Status/Health, op: eq, value: OK }
        - path: /redfish/v1/Systems/system
          any:
            - { prop: BiosVersion, op: exists }
            - { prop: BiosRevision, op: exists }
  transport:
    proto: local
  options:
    timeout: 5m