An experiment on COSMOS is a description: one file that says what the
experiment needs, not how to obtain it. cosmos-run reads it, resolves it
against the live testbed, and carries it out.
cosmos-run sdr/ofdm-ber-sb5.description.yml
That is the whole user interface. This page explains what happens behind it and
what the pieces are called.
¶ 👋 New here? Start with Your First Experiment
Fifteen lines of YAML, three commands, and a file brought home from a node you
have never logged into. No Ansible knowledge assumed, and the first step
needs no reservation and touches no hardware. About fifteen minutes.The rest of this section is reference. It is written for someone who already
knows what a description is — which that page will make you in one sitting.
Start here
| Page | Covers |
|---|---|
| Your First Experiment | A hello-world that runs, and the Ansible you actually need — nothing assumed |
| Ansible in ten minutes | The five ideas the framework exposes — and the ones you can ignore |
| Writing an experiment | The real workflow, from an idea to a working description |
Learn the language
| Page | Covers |
|---|---|
| Description language | The file format, field by field |
| Step kinds | role, run, wait, barrier, assert, service, collect, external |
| Resources and feasibility | How nodes are chosen, and what the refusal messages mean |
Reference
| Page | Covers |
|---|---|
cosmos-run |
Every option, what each does, and how to reach the underlying tool |
| Tutorials that use the framework | Worked examples to copy — and which tutorials are not framework-based yet |
| Role library | The reusable roles a description composes |
| Modules and plugins | The inventory plugin, the omf wrappers, the device modules |
| Artifacts and reproducibility | What a run leaves behind, and how to reproduce it |
| Troubleshooting | Real failure messages and their causes |
Enough of one to read, from the packaged set:
apiVersion: cosmos/v1
name: ofdm-ber-sb5
description: OFDM BER against injected noise on the sb5 cabled pair
parameters:
sub: {type: string, default: bpsk}
amplitude: {type: float, default: 0.15}
snr_db: {type: string, default: "8:24:1"}
resources:
tx:
pin: [node1-1.sb5.cosmos-lab.org]
rx:
pin: [node1-2.sb5.cosmos-lab.org]
steps:
- role: cosmos_lab.orchestration.fr3_ofdm_ber
vars:
cosmos_fr3_sub: "{{ sub }}"
cosmos_fr3_amplitude: "{{ amplitude }}"
cosmos_fr3_snr_db: "{{ snr_db }}"
collect: [/run/cosmos/fr3-ofdm]
Six keys carry the whole thing: what it is (apiVersion, name,
description), what the user may change (parameters), what hardware it
needs (resources), what to do (steps), what to keep (collect).
Anything in parameters can be overridden without editing the file:
cosmos-run sdr/ofdm-ber-sb5.description.yml -e sub=qpsk -e amplitude=0.06
Four phases, in order. Each is a separate play, and the boundaries matter
because of what is true on either side of them.
1 — Resolve. Read the description, query the live inventory service, and
choose actual nodes for each resource. Nothing is touched. This phase alone
runs under --resolve-only, and needs no reservation.
2 — Prepare. Bring the chosen nodes to the state the description asked for:
image them if the requested image is not what they are running, power them on,
verify the radio or device is present. This is the first phase with side
effects.
3 — Execute. Run the steps. Whatever the experiment actually does happens
here.
4 — Record. Collect the artifacts named in collect, and write a manifest
naming the description's hash, the collection version, the nodes chosen and the
time.
Teardown is not a fifth phase — it is an always: block around phase 3, so a
device that was configured gets restored even when the experiment fails.
Nothing here is something you install.
| Path | What |
|---|---|
/usr/bin/cosmos-run |
The command |
/usr/share/ansible/collections/…/cosmos_lab/orchestration |
The framework: roles, modules, plugins, playbooks |
/usr/share/cosmos-experiments/ |
The packaged descriptions, named by the tutorials |
/etc/cosmos/inventory/cosmos.yml |
Inventory config (a conffile: local edits survive upgrades) |
~/cosmos-experiments/ |
Optional. Your own copy, searched first, so you can edit an experiment without root |
~/cosmos-artifacts/<run>/ |
Where results and the run manifest land |
cosmos-run --version names the copy that will actually be used, which matters
when there is more than one:
seskar@console-grid:~$ cosmos-run --version
cosmos-run (cosmos_lab.orchestration 0.1.0)
loaded from: /usr/share/ansible/collections/ansible_collections/cosmos_lab/orchestration
ansible-playbook [core 2.16.3]
Users install nothing. Both packages are already on every console, and
console users have no need for root.
For operators, the framework ships as two Debian packages, deliberately
separate because they change at different rates:
| Package | Contents | Changes when |
|---|---|---|
cosmos-orchestration |
the collection, cosmos-run, the inventory conffile |
the engine changes |
cosmos-experiments |
the experiment descriptions | a tutorial is written or corrected |
cosmos-experiments depends on cosmos-orchestration — descriptions do
nothing without the runner — while the framework only recommends the
experiments, so either can be upgraded or removed alone. Each carries a version
derived from the commits touching its own files, so a tutorial fix does not
drag an engine version with it.
sudo apt-get install ./cosmos-orchestration_*.deb ./cosmos-experiments_*.deb
A per-user copy in
~/.ansible/collectionsoverrides the packaged one.
That is how a build is tested before it ships, andcosmos-run --version
printsshadowing:when it happens. Note also that COSMOS consoles share
one NFS home directory, so such a copy is fleet-wide rather than per
console — and removing it while an experiment is running anywhere will break
that experiment.
Last verified: 2026-08-03 on console.grid.cosmos-lab.org.