docs: split architecture (host + ansible), TTY menu, web UI

This commit is contained in:
2026-07-07 17:24:13 +00:00
parent 893de6d430
commit 10b669df38
+139 -31
View File
@@ -69,47 +69,96 @@ README.md # This file
--- ---
## Ansible Control Node — QEMU-Bootable (Alpine Linux) ## Ansible Control Node — Split Architecture
A minimal, portable Ansible control node that boots on QEMU `pc-q35-10.0`. Two independent artifacts. The QEMU VM provides Docker; the Ansible container
Designed to run on Windows hosts, providing Ansible connectivity to Windows, runs the tests. Same container image runs on Docker Swarm or Kubernetes without
Cisco, VMware, MSSQL, and Linux targets. No systemd — OpenRC + BusyBox. the VM layer.
### The Two Artifacts
| Artifact | Defined by | Built with | Result |
|---|---|---|---|
| **Alpine Docker Host** | `Dockerfile.alpine-host` | `./scripts/build-qemu.sh` | `output/ansible-node.qcow2` (~470MB) |
| **Ansible Control Node** | `Dockerfile.ansible` | `./scripts/build-ansible.sh` | `ansible-node` Docker image (~793MB) |
### Build & Launch ### Build & Launch
```bash ```bash
# Prerequisites: Docker, QEMU (qemu-full), passwordless sudo for mount # Prerequisites: Docker, QEMU (qemu-full), passwordless sudo for mount
./scripts/build-qemu.sh # Dockerfile → qcow2 disk (2GB)
./scripts/run-qemu.sh # Boot VM (KVM, 1GB RAM, 2 vCPUs)
# Access the VM (password: ansible) # 1. Build the VM disk
sshpass -p ansible ssh -p 2222 ansible@localhost ./scripts/build-qemu.sh # Alpine + Docker + SSH → qcow2
# 2. Build the Ansible image
./scripts/build-ansible.sh # Ansible + 10 collections → Docker image
# Or push to a registry:
REGISTRY=my-registry ./scripts/build-ansible.sh --push
# 3. Boot the VM
./scripts/run-qemu.sh # QEMU pc-q35-10.0, KVM, 1GB, 2 vCPUs
``` ```
### Architecture ### How the User Interacts
``` ```
Dockerfile # Complete rootfs definition QEMU VM boots in ~6 seconds
├── ttyS0 (serial console) ──► auto-menu driven
scripts/build-qemu.sh # Docker export → ext4 → qcow2 │ ╔══════════════════════════════════════╗
│ ║ 1) Run all tests ║
│ ║ 2) Run FR1 — Auth ║
│ ║ 3) Run FR2 — Use Control ║
│ ║ 4) Run FR5 — Data Flow ║
│ ║ 5) Download reports (tar.gz) ║
│ ║ 6) View latest report ║
│ ║ 7) Shell (Ansible container) ║
│ ║ 8) Shell (Docker host) ║
│ ║ 0) Shutdown ║
│ ╚══════════════════════════════════════╝
├── output/ansible-node.qcow2 (bootable disk, ext4) ├── :8080 ──► Web UI (browser-based dashboard)
├── output/vmlinuz-virt (kernel, direct -kernel boot) │ • Run buttons for each playbook
└── output/initramfs-virt (initramfs) │ • Output streaming
│ • Report downloads (JSON)
└── :22 ──► SSH (ansible / ansible)
scripts/run-qemu.sh # QEMU pc-q35-10.0, KVM, virtio ```
Every option in the menu and every button in the web UI runs:
┌─────────────────────────────────────────────┐ ```bash
│ Alpine Linux 3.20 | Kernel 6.6 (virt) │ docker run --rm -it \
│ OpenRC | BusyBox | no systemd │ -v /ansible/playbooks:/ansible/playbooks:ro \
│ Console: ttyS0 xterm-256color │ -v /ansible/reports:/ansible/reports \
│ Keyboard: PS/2 atkbd │ -v /ansible/inventory:/ansible/inventory:ro \
│ Docker 26.1 daemon (running) │ ansible-node site.yml
│ SSH: ansible@<ip> (password: ansible) │ ```
└─────────────────────────────────────────────┘
### Architecture Diagram
```
Dockerfile.alpine-host Dockerfile.ansible
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ Alpine 3.20 │ │ Alpine 3.20 │
│ Docker 26.1 daemon │ │ Ansible 2.17 │
│ OpenRC (no systemd) │ │ 10 collections │
│ tty-menu.sh │ │ pywinrm, pyvmomi, │
│ webui.py (:8080) │ │ pymssql, paramiko, │
│ sshd (:22) │ │ netmiko, ncclient │
│ ~470MB qcow2 │ │ ~793MB Docker image │
└─────────────────────┘ └─────────────────────┘
│ │
│ docker run ansible-node │
└───────────────────────────────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌─────────┐
│Windows │ │ Cisco │ │ VMware │
│ WinRM │ │ SSH/CLI │ │ SOAP │
└────────┘ └──────────┘ └─────────┘
``` ```
### Target Integrations ### Target Integrations
@@ -124,15 +173,25 @@ scripts/run-qemu.sh # QEMU pc-q35-10.0, KVM, virtio
| **MSSQL** | `microsoft.ad` 1.5 | TDS | `pymssql` 2.3 | | **MSSQL** | `microsoft.ad` 1.5 | TDS | `pymssql` 2.3 |
| **Linux** | built-in SSH | SSH | — | | **Linux** | built-in SSH | SSH | — |
### Deployment Targets for the Ansible Image
| Environment | How |
|---|---|
| **QEMU VM on Windows** | `docker run ansible-node` inside the Alpine Docker Host |
| **Docker Swarm** | `docker stack deploy` the `ansible-node` image directly |
| **Kubernetes** | `kubectl run` a Job or Pod with the `ansible-node` image |
### Key Design Decisions ### Key Design Decisions
| Decision | Rationale | | Decision | Rationale |
|---|---| |---|---|
| Dockerfile as build system | Dependency resolution, layer caching. Same file usable as `docker run` or QEMU boot | | Split: host vs. container | VM provides Docker; Ansible runs in a container. Same image on Swarm/K8s |
| `root=/dev/vda` (not LABEL) | Alpine's `nlplug-findfs` can't resolve labels on virtio-block; device path works | | Dockerfile as build system | Dependency resolution, layer caching. Dockerfile → `docker export` → qcow2 |
| `root=/dev/vda` (not LABEL) | Alpine's `nlplug-findfs` can't resolve labels on virtio-block |
| `modules=virtio_blk,ext4` on cmdline | `CONFIG_VIRTIO_BLK=m` — module not auto-detected by initramfs | | `modules=virtio_blk,ext4` on cmdline | `CONFIG_VIRTIO_BLK=m` — module not auto-detected by initramfs |
| No `apk del` purge step | Purging `util-linux-dev` cascades to removing `mount`, `umount`, `blkid` | | TTY menu via `agetty -l` | Auto-launches menu instead of login prompt on serial console |
| Direct `-kernel` boot (no extlinux) | QEMU loads kernel/initrd directly; no bootloader needed in the disk image | | Web UI: Python stdlib only | Zero dependencies; `http.server` + inline HTML + async JS |
| Direct `-kernel` boot (no extlinux) | QEMU loads kernel/initrd directly; no bootloader in disk image |
--- ---
@@ -540,6 +599,55 @@ A hand-crafted example JSON report for testing the renderers without running
Ansible. Contains 15 results (10 passed, 4 failed, 1 review) across FR1 and FR2. Ansible. Contains 15 results (10 passed, 4 failed, 1 review) across FR1 and FR2.
Useful for CI validation of the report rendering pipeline. Useful for CI validation of the report rendering pipeline.
### `Dockerfile.alpine-host`
Alpine Linux 3.20 with Docker daemon, SSH, TTY menu, and web UI. Used as the
blueprint for the QEMU-bootable VM disk. Built via `scripts/build-qemu.sh`.
Installs: `alpine-base`, `linux-virt`, `docker`, `openssh-server`, `python3`.
Provides: serial console menu (`scripts/tty-menu.sh`) and web UI (`webui/app.py`).
### `Dockerfile.ansible`
Ansible control node container image. Installs `ansible` + `sshpass` + 10
Ansible collections + Python packages for Windows (pywinrm), Cisco (paramiko,
netmiko, ncclient), VMware (pyvmomi), and MSSQL (pymssql). Built via
`scripts/build-ansible.sh`. Deployable on Docker Swarm, Kubernetes, or inside
the Alpine Docker Host VM.
### `scripts/build-qemu.sh`
Converts `Dockerfile.alpine-host` into a bootable QEMU disk. Steps: Docker build
→ export rootfs → extract kernel + initramfs → create ext4 disk → convert to
qcow2. Outputs: `output/ansible-node.qcow2`, `output/vmlinuz-virt`,
`output/initramfs-virt`.
### `scripts/build-ansible.sh`
Builds the `ansible-node` Docker image from `Dockerfile.ansible`. Reports the
image size and layers. With `--push` and `REGISTRY` set, pushes to a container
registry for deployment.
### `scripts/run-qemu.sh`
Launches the Alpine Docker Host VM on QEMU `pc-q35-10.0`. Uses direct kernel
boot (`-kernel` / `-initrd`), virtio devices, user-mode networking with port
forwards (SSH on `:2222`, web UI on `:8090`). Supports `--gui`, `--vnc`, and
`--debug` modes.
### `scripts/tty-menu.sh`
Serial console menu script. Launched automatically by `agetty -l` on `ttyS0`.
Options: run individual playbooks, view reports, shell into the Ansible
container, shell into the Docker host, shutdown VM. All test options run
`docker run ansible-node` with shared volumes.
### `webui/app.py`
Minimal web dashboard (Python 3 stdlib only, zero dependencies). Serves on
port 8080. Features: dark-themed UI with run buttons per playbook, async output
streaming via `fetch`, JSON report downloads. Handles `POST /api/run` to execute
playbooks via `docker run` and `GET /reports/<name>` for file downloads.
### `inventory.ini` ### `inventory.ini`
Standard Ansible inventory. Structure: Standard Ansible inventory. Structure: