(use-case-ci)= # Hardware in the CI loop In this use case nobody types the commands: a pipeline or a scheduler does. The appliance becomes a permanent part of your CI infrastructure, with one DUT permanently attached to it, and jobs are dispatched to it automatically. The LAA is designed for this. Each appliance drives exactly one DUT, on its own private network, with its own worker process and its own power and USB control. A crash, a misbehaving test image or a reboot affects that one DUT and nothing else in the lab. The reasoning behind that design is in [Design goals](/generality/goals.md). ## Choosing a runtime The appliance can run one of several runtimes, selected remotely from [LMS](/software/fleet.md): | Runtime | Good fit for | Documentation | |---|---|---| | [GitLab runner](#gitlab-and-github-ci) | Single developers to mid-size teams that want hardware in an existing pipeline | this page | | [LAVA worker](#lava) | Teams running a lab with many DUTs and many users | [LAVA integration](/software/fleet/lava.md) | | [KernelCI runner](#kernelci) | Contributing a board to KernelCI while keeping it in a closed network | [KernelCI runner](/software/fleet/kci-runner.md) | | [Labgrid](#labgrid) | Teams already standardised on Labgrid | this page | :::{admonition} One runtime per appliance :class: warning The `Select Kind` options in LMS (`GitLab runner`, `KernelCI runner`, `LAVA worker`) are mutually exclusive: an appliance runs a single runtime at a time. Select `None` to disable the runner for using the board interactively. ::: ## Before anything else: enable the DUT Whichever runtime you pick, the appliance first has to know how to boot your board reliably and unattended. If your DUT is already in the {ref}`devices-index` list, this is done for you. Otherwise, work through [Device-Type enablement](/guides/device_type_enablement.md) first: MIB selection, power sequencing, reset method, serial console, and the recovery path for when a job leaves the board bricked. A CI setup is only as reliable as its recovery story. Make sure `laam dut test` boots the board repeatedly before you point a pipeline at it. ## GitLab and GitHub CI The appliance can run a GitLab runner that has full access to the attached DUT. The pipeline job then tests any part of the software stack directly on your hardware: no separate lab infrastructure, no automation framework to learn. ![LAA as Gitlab runner](/_images/generality/gitlab-pipeline.png){align=center} Select `GitLab runner` in the `Select Kind` dialog on the LAA page in LMS, the same way as for the [KernelCI runner](/software/fleet/kci-runner.md#set-the-kernelci-runner). Inside the job, the DUT is reachable exactly as in the [developer use case](/use-cases/developer.md): power and USB control through [`laacli`](/software/laacli.md), the serial console through [ser2net](/guides/device_type_enablement.md#ser2net-configuration), and artifacts served to the DUT over [TFTP and NFS](/software/dut-services/automation.md). Anything you scripted by hand works unchanged in the pipeline. :::{admonition} Gitlab use case :class: tip Using the LAA as a Gitlab runner is a good fit for single developers to mid-size teams that want to add Hardware in the CI loop without having to setup a proper automation service like LAVA. ::: This has been used successfully to test IOT devices directly from a gitlab pipeline: check the [FOSDEM 26 presentation](https://fosdem.org/2026/schedule/event/JQMEBE-testing_esphome_in_the_real_world/). ## LAVA The appliance is a fully integrated [LAVA](https://lava.readthedocs.io/) worker, able to run any LAVA job on the attached DUT. ![LAA as LAVA worker](/_images/generality/lava.png){align=center} You do not configure the worker by hand. In LMS you select the LAVA instance and the device-type attached to the appliance; LMS creates the worker on the LAVA server, generates the device dictionary and pushes the configuration down to the appliance, which starts a worker running the matching LAVA version. If the server is later upgraded, the worker follows automatically, waiting for the running job to finish first. The full procedure is in [LAVA integration](/software/fleet/lava.md), and `laam workers` lets you inspect and debug the worker from your laptop: ```console $ laam workers list $ laam workers test $ laam workers logs ``` :::{admonition} LAVA use case :class: tip Using the LAA as a LAVA worker is a good fit for teams that want to have a solid and versatile automation framework that allows to manage a pull of DUTs in their lab. Linaro can help you to setup such lab. ::: ## KernelCI Configured as a KernelCI runner, the appliance polls the [KernelCI pull-lab](https://docs.kernelci.org/components/maestro/pipeline/connecting-pull-lab/) API endpoint for jobs targeting the attached DUT, runs them and reports the results back. Because the appliance pulls, the DUT can stay inside a closed network with no inbound connectivity. ![LAA as KernelCI runner](/_images/generality/kernelci.png){align=center} The setup (selecting the runner kind, selecting the device, and requesting the KernelCI events that route jobs to your lab) is described in [KernelCI runner (pull-lab)](/software/fleet/kci-runner.md). ## Labgrid The appliance also fits a [Labgrid](https://labgrid.readthedocs.io/) setup, though there is no LMS-managed Labgrid runtime: you drive the appliance from your own exporter or from the test host. Everything Labgrid needs from lab infrastructure is exposed by the appliance over the network: * **power control**: `laacli power on|off|reset`, run remotely with [`LAAPowerDriver`](https://labgrid.readthedocs.io/en/latest/configuration.html#laapowerdriver) * **reset and boot-mode buttons**: [`LAAButtonDriver`](https://labgrid.readthedocs.io/en/latest/configuration.html#laabuttondriver) * **USB port power**: [`LAAUSBDriver`](https://labgrid.readthedocs.io/en/latest/configuration.html#laausbdriver), for boards that uses USB * **serial console**: a plain TCP socket per console, published by [`LAASerialDriver`](https://labgrid.readthedocs.io/en/latest/configuration.html#laaserialdriver) * **USB mass-storage emulation**: [`LAAUSBGadgetMassStorage`](https://labgrid.readthedocs.io/en/latest/configuration.html#laausbgadgetmassstorage) exposes an image to the DUT as a USB mass storage device * **artifact serving**: TFTP and NFS on the DUT private network, see [`LAAProvider`](https://labgrid.readthedocs.io/en/latest/configuration.html#laaprovider) :::{admonition} Labgrid example :class: tip An example labgrid configuration and pytest script is available at [labgrid-laa-examples](https://gitlab.com/Linaro/lava/appliance/labgrid-laa-examples). ::: ## Operating a fleet Once more than one appliance is in service, the day-to-day management happens in LMS rather than on individual appliances: * [OTA upgrades with automatic rollback](/software/fleet/ota.md) keep the fleet on a known software version * [`lmscli`](/software/lmscli.md) scripts LMS itself: instances, subscriptions, deployments and releases * [`laam services`](/software/laam_console.md#services) and [`laam system logs`](/software/laam_console.md#system) give you the logs of a single appliance without needing a shell on it * [remote access](/software/fleet/remote-access.md) lets Linaro engineers help debug an appliance, with your permission For what your IT department will want to know before appliances land in the lab (protocols, ports, outbound destinations, DUT isolation) point them at [LAA Overview for IT Departments](/support_faq/it_overview.md) and [LAA & Security](/support_faq/it_config.md).