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`.
Designed to run on Windows hosts, providing Ansible connectivity to Windows,
Cisco, VMware, MSSQL, and Linux targets. No systemd — OpenRC + BusyBox.
Two independent artifacts. The QEMU VM provides Docker; the Ansible container
runs the tests. Same container image runs on Docker Swarm or Kubernetes without
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
```bash
# 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)
sshpass -p ansible ssh -p 2222 ansible@localhost
# 1. Build the VM disk
./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
scripts/build-qemu.sh # Docker export → ext4 → qcow2
├── ttyS0 (serial console) ──► auto-menu driven
│ ╔══════════════════════════════════════╗
│ ║ 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)
├── output/vmlinuz-virt (kernel, direct -kernel boot)
└── output/initramfs-virt (initramfs)
├── :8080 ──► Web UI (browser-based dashboard)
│ • Run buttons for each playbook
│ • Output streaming
│ • Report downloads (JSON)
scripts/run-qemu.sh # QEMU pc-q35-10.0, KVM, virtio
┌─────────────────────────────────────────────┐
│ Alpine Linux 3.20 | Kernel 6.6 (virt) │
│ OpenRC | BusyBox | no systemd │
│ Console: ttyS0 xterm-256color │
│ Keyboard: PS/2 atkbd │
│ Docker 26.1 daemon (running) │
│ SSH: ansible@<ip> (password: ansible) │
└─────────────────────────────────────────────┘
└── :22 ──► SSH (ansible / ansible)
```
Every option in the menu and every button in the web UI runs:
```bash
docker run --rm -it \
-v /ansible/playbooks:/ansible/playbooks:ro \
-v /ansible/reports:/ansible/reports \
-v /ansible/inventory:/ansible/inventory:ro \
ansible-node site.yml
```
### 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
@@ -124,15 +173,25 @@ scripts/run-qemu.sh # QEMU pc-q35-10.0, KVM, virtio
| **MSSQL** | `microsoft.ad` 1.5 | TDS | `pymssql` 2.3 |
| **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
| Decision | Rationale |
|---|---|
| Dockerfile as build system | Dependency resolution, layer caching. Same file usable as `docker run` or QEMU boot |
| `root=/dev/vda` (not LABEL) | Alpine's `nlplug-findfs` can't resolve labels on virtio-block; device path works |
| Split: host vs. container | VM provides Docker; Ansible runs in a container. Same image on Swarm/K8s |
| 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 |
| No `apk del` purge step | Purging `util-linux-dev` cascades to removing `mount`, `umount`, `blkid` |
| Direct `-kernel` boot (no extlinux) | QEMU loads kernel/initrd directly; no bootloader needed in the disk image |
| TTY menu via `agetty -l` | Auto-launches menu instead of login prompt on serial console |
| 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.
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`
Standard Ansible inventory. Structure: