# Verification suite

A small Ansible-based test framework that checks whether a lab was completed
correctly:

- **for the trainer**, to verify an environment in seconds instead of reading YAML
- **for participants**, as an optional self-check after each lab

Every check is an assertion against the *real environment*: files on the control
node, state on the Proxmox VE nodes, and objects in the cluster read through the API.
Nothing is mocked.

## Usage

Run from the project root (`~/ansible-proxmox` for a participant, this directory's
parent for the reference project):

```bash
./tests/run.sh 01                    # one lab
./tests/run.sh all                   # everything
./tests/run.sh 03                    # extra flags go to ansible-playbook
./tests/run.sh 05 --limit pve02
```

Exit code `0` means everything passed. Each suite prints `PASS` or `FAIL`, and the
runner prints a summary.

For exercise 06 the token secret has to be in the environment:

```bash
export PVE_TOKEN_SECRET='...'        # from ~/pve-token.json
./tests/run.sh 06
```

## Layout

| File | Checks |
|---|---|
| `vars.yml` | **all expected values live here**, the only file to adapt |
| `verify-01.yml` | project skeleton, `ansible.cfg`, inventory groups, reachability |
| `verify-02.yml` | repositories, packages, name resolution, time sync |
| `verify-03.yml` | cluster exists, quorate, all nodes members, `link0`, pmxcfs |
| `verify-04.yml` | role layout, `site.yml`, no duplicated values, MOTD template |
| `verify-05.yml` | storage, service account, VM, backup artefact, manual bridge |
| `verify-06.yml` | dynamic inventory parses and returns the expected groups |
| `run.sh` | runner and summary |

`verify-02.yml` and `verify-04.yml` both describe the node base state, so
`verify-02.yml` also passes after the module 04 refactoring. That is intentional: it
is the same regression check the refactoring relies on.

## Adapting it

Everything environment-specific is in `vars.yml`. Change the node names, addresses,
cluster name, storage names and VM id there; no check playbook should need editing.

## Verified against the real environment

Run on 15 September 2026 against the `workshop200` set (`pve01`-`pve03`, Proxmox VE
9.2.2, Debian 13). All six suites pass. The values below are confirmed, not assumed:

- [x] **Node names and addresses** — `pve01`, `pve02`, `pve03` at `192.168.0.1-3`.
      The PVE node names are the short names, which is what the fingerprint lookup in
      `20-cluster.yml` filters on.
- [x] **Storage layout** — `local` (dir) and `local-lvm` (lvmthin) on every node.
      `pve_vm_storage: local-lvm` is correct.
- [x] **Time synchronisation** — `NTPSynchronized=yes` on all three nodes.
- [x] **Keyring path** — `/usr/share/keyrings/proxmox-archive-keyring.gpg` exists.
- [x] **Proxmox VE version** — 9.2.2 on Debian 13 ("trixie").
- [x] **Control node** — the VDI desktop, reached over the jump host. It is the
      machine participants work on.

## Open points

- [ ] **Container template name** — `pve_ct_template` in `group_vars/all.yml` is a
      plausible Debian 13 file name, not a verified one. Check with
      `pveam available --section system | grep debian-13`, or drop the optional LXC
      exercise.
- [ ] **Per-participant addressing** — the test used `workshop200`. The internal
      addresses `192.168.0.1-3` look identical for every participant set, which would
      make one inventory valid for the whole room. Confirm before relying on it.
- [ ] **A pre-flight suite** (`verify-00.yml`) that checks a whole delivery
      environment before the session, rather than one participant set at a time.

## Desktop image: software the workshop needs

Verified against the image on 15/16 September 2026 by checking `dpkg.log`, so this
separates what the image ships from what was installed during testing.

**Missing from the image, and required:**

| Package | Why it is needed | What breaks without it |
|---|---|---|
| `sshpass` | Ansible logs into the nodes with a password | every playbook, from exercise 01 on |
| `python3-venv` | `deploy.sh` builds the Ansible environment | nothing can be set up at all |
| `jq` | formats the API output in the handouts | the `curl` examples in modules 01, 02, 03, 05, 06 |

```bash
sudo apt-get install -y sshpass python3-venv jq ansible-lint httpie
```

`deploy.sh` checks for `sshpass`, `python3-venv` and `jq` before it does anything and
prints that command if one is absent. `ansible-lint` and `httpie` are not checked,
because nothing in the material stops working without them.

A note on `ansible-lint`: the virtualenv carries its own copy, and that is the one on
`PATH`. That is deliberate, because ansible-lint has to match the ansible-core it
lints against, and the system one would be built for 2.19 while the venv runs 2.21.
The system package does no harm.

**Present and sufficient:** `ansible-core` 2.19.11 (the venv supersedes it), `git`,
`curl`, `vim`, `nano`, `tmux`, `less`, `man`, `chromium`, VS Code (`code`),
`python3-requests`, `nfs-common`.

**Deliberately not needed:**

- `python3-proxmoxer` — Debian ships 2.2.0, and `community.proxmox` 2.x wants 2.3.
  The venv provides 2.3.0, so the system package would only be confusing.
- `ansible-lint` — comes with the venv bundle, and the venv copy is the one that
  runs. Note that `ansible-lint --version` crashes with a traceback on
  ansible-lint 26.8 with Python 3.13; linting itself is unaffected.
- `rsync` — absent, and not needed on the desktop. `publish.sh` uses it on the host
  cluster node, where it is present.

## Ansible on the desktop

The image ships `community.proxmox` 1.3.0 as a Debian package: 29 modules, missing
`proxmox_cluster_status_info` and `proxmox_node_network`, both of which this material
uses. `deploy.sh` therefore builds a virtualenv from `venv-bundle/` on the share and
puts it on `PATH` in `.bashrc` **and** `.profile`.

Both files on purpose: Debian's `.bashrc` returns early for non-interactive shells,
so a line there alone is invisible to `ssh host command` and to scripts.

Baking the venv into the image is still the better option, and then `deploy.sh`
simply finds it and skips that step. The lab mount under `~/Desktop/workshop` is
already part of the image.
