DUT Configuration
Device under Test (DUT)
This configuration dut.yaml describes every aspect of a DUT. Below are the key components:
Branch scope: Each DUT belongs to a single branch of the project (see the Branches reference), alongside that branch’s workflows and storage. One physical device therefore appears once per branch that uses it, and FirmwareCI knows those entries are the same hardware — see Disabling a Device.
Required Fields
| Attribute | Type | Description |
|---|---|---|
| name | string | Unique name of the DUT. Letters, digits, -, and _, starting and ending with a letter or digit. |
| label | string | Label that identifies a group of DUTs. Workflows pick DUTs by this label with runs-on. Same characters as name. |
| reservation-system | string | How FirmwareCI reserves the DUT for a job. One of dutctl, virtual, or mock (see Reservation). |
Optional Fields
| Attribute | Type | Description |
|---|---|---|
| attributes | object | Attributes of the DUT. |
DUT Attributes
The attributes field accepts any key-value combination to describe properties of your DUT. Common attributes include host addresses, port numbers, hardware capabilities, or any device-specific configuration.
These attributes can be referenced in test files using the attributes templating syntax: [[attributes.KeyName]]. This allows tests to access DUT-specific configuration without hardcoding values.
Values must be strings, so quote numbers and booleans (Port: "22"). Attribute names are case-sensitive and must not contain - or !. Some reservation systems require certain attributes (see Reservation).
For complete details on templating syntax, see Templating and Variables.
Note: Attributes are stored and shown in plain text. Do not put passwords or tokens into attributes. Use secrets in the test instead.
Example
Reservation
Before a job starts, FirmwareCI reserves a DUT so that nothing else can use
the hardware during the job. After the post-stage, FirmwareCI releases the
reservation, whether the job passed or failed. reservation-system selects
how the DUT is reserved:
| reservation-system | Required attributes | Use for |
|---|---|---|
dutctl | Agent, Device | Real hardware connected to a dutagent. FirmwareCI locks the device on the agent. See dutctl. |
virtual | — | Virtual DUTs such as QEMU machines. No hardware is locked. See virtual. |
mock | — | Trying out a configuration without hardware. See mock. |
The reservation-system value and the attribute names are case-sensitive.
How a Job Gets a DUT
- The workflow’s
runs-onnames a label. FirmwareCI looks for DUTs with thislabelon the job’s branch. Several DUTs with the same label form a pool, and the job takes the first one that is free. - FirmwareCI reserves the DUT through its reservation system.
- The job runs: pre-stage, test stages, post-stage.
- FirmwareCI releases the reservation.
If every DUT in the pool is busy, unavailable, or disabled, the job stays Queued, and FirmwareCI tries again every few seconds. There is no time limit: the job waits until a DUT becomes free. The job does not show why it is waiting; the device status does.
If no DUT on the branch has the label, the job fails right away with
no DUT exists for label <label> on branch .... Check that runs-on matches
the label and that the DUT is defined on this branch.
dutctl
Use dutctl for hardware that is connected to a
dutagent. Two attributes are
required:
| Attribute | Description |
|---|---|
Agent | Address of the dutagent as host:port, for example lab-pi.local:2024. http:// is added if no scheme is given. |
Device | Name of the device on the agent, as the agent lists it. |
The dutctl step uses the same values. FirmwareCI does not fill them in, so template them into each step:
During a Job
- Before the job starts, FirmwareCI locks the device on the agent and holds
the lock as
<job-id>@firmwareci. - The lock does not block the job’s own dutctl steps. Manual dutctl commands against the device are refused until the job is done.
- After the post-stage, FirmwareCI unlocks the device. If unlocking fails, the lock expires after about eight hours.
Manual Locks
If someone locked the device manually with the dutctl CLI, FirmwareCI treats the device as busy, and jobs wait. The device page still shows Idle, because it only knows about FirmwareCI jobs. Release a manual lock when you are done. To keep jobs away from a device, disable it instead.
Reachability
The FirmwareCI server locks and unlocks the device, and the job’s dutctl steps
run the commands. Both must reach the agent at the address in Agent.
Health Checks
FirmwareCI checks every agent about once a minute. If an agent does not
answer three times in a row, or no longer lists the device, the device
becomes Unavailable with the reason reservation backend unreachable, and
a job that is running on it is aborted. The status clears on its own when the
agent answers again.
If locking fails when a job tries to reserve the device, the device page
shows the error, for example
dutctl lock board-1 at http://lab-pi.local:2024: agent unreachable, and the
job tries the next DUT in the pool.
virtual
Use virtual for DUTs that do not need exclusive hardware, such as QEMU
machines that the job starts itself. Nothing is locked, and one virtual DUT
can run several jobs at the same time. The FirmwareCI server limits how many
virtual jobs run at once; further jobs wait in the queue. No attributes are
required.
mock
Use mock to try out a configuration without real hardware. Nothing is
reserved. Only one mock job runs at a time on the FirmwareCI server; further
jobs wait in the queue. No attributes are required.
Device Status
A device that exists is not necessarily one a job can run on. The device list and device page show which of these applies:
| Status | What it means |
|---|---|
| Idle | Free, and the next matching job may take it. |
| Busy | A job is running on it right now. It frees itself when that job finishes. A manual dutctl lock does not appear here; the device stays Idle (see Manual Locks). |
| Unavailable | FirmwareCI cannot reach the device — usually the agent is stopped or the address is wrong. It checks periodically, so a stopped agent shows up within about a minute, and the status clears itself once the device answers again. The reason is shown on the device page. See Health Checks. |
| Disabled | Somebody deliberately took it out of the pool. See below. |
Disabling a Device
Disable a device when you do not want CI to schedule onto it — the hardware is broken and you are tired of watching jobs fail on it, or you want to use the board manually for an afternoon.
Open the device and choose Disable. You are asked for two things:
- A reason. It is shown to whoever finds the device out of the pool, so write what the next person needs to know (“PSU replaced, waiting for parts”).
- How long. Pick one of the presets, type your own (
90m,2h,1d12h), or — as an admin — leave it disabled indefinitely.
To put it back, open the device and choose Enable.
It applies to the hardware, not to one branch
The same physical device is declared once per branch that uses it. Disabling acts on the device, so every one of those entries is covered at once and you do not have to go hunting through branches. The confirmation dialog tells you how many entries are affected before you commit.
For the same reason, disabling is not part of your repository configuration:
pushing a change to dut.yaml will not quietly re-enable a device somebody
took out of the pool.
A job already running on the device is left alone — disabling is about the next job. If you need the board immediately, abort the running job as well.
Who may disable what
The duration is the permission: a device that comes back on its own is safe for anyone to take, one that does not is an admin’s call.
| Role | With a duration | Indefinitely |
|---|---|---|
| Organization admin | yes, any duration | yes |
| Organization user | yes, up to 4 hours | no |
| Viewer | no | no |
A device you disabled can be enabled again by you or by an admin — a colleague cannot pull a board out from under you mid-debug, and only an admin can return broken hardware to the pool.
Devices come back by themselves when the time is up; nothing is left holding hardware because somebody forgot. The record of who disabled it, why, and when it came back is kept.
Optional Files
Pre-Stage
This configuration pre.yaml describes the setup process of a DUT. Below are the key components:
Pre-Stage Required Fields
| Attribute | Type | Description |
|---|---|---|
| pre-stage | array | List of test step commands (see Commands) |
Pre-Stage Example
The command names come from the agent’s configuration (see Commands). For a complete pre-stage and post-stage, see Connecting Hardware with dutctl.
Post-Stage
This configuration post.yaml describes the teardown process of a DUT. Below are the key components:
Post-Stage Required Fields
| Attribute | Type | Description |
|---|---|---|
| post-stage | array | List of test step commands (see Commands) |
Post-Stage Example
Common Issues
| Problem | Solution |
|---|---|
fwci validate reports reservation-system is required | Add reservation-system to dut.yaml. |
fwci validate reports that Agent or Device is required | Add both attributes to dut.yaml, with exactly this capitalization. |
| Job stays Queued | Check the device list for the DUTs with the label: they are busy, unavailable, or disabled. Release manual dutctl locks (see Manual Locks). |
Job fails with no DUT exists for label | Set runs-on to the label of a DUT on this branch. |
Device is Unavailable with agent unreachable | Start the agent, fix the host or port in Agent, and make sure the FirmwareCI server can reach it. |
| Device is Unavailable, but the agent is running | Set Device to the device name the agent lists. |
| Your own dutctl command is refused while a job runs | Wait for the job, abort it, or disable the device to keep the next jobs away. |