From 10b669df38e5c1b542d653ad9ae06763b23499bd Mon Sep 17 00:00:00 2001 From: oval Date: Tue, 7 Jul 2026 17:24:13 +0000 Subject: [PATCH] docs: split architecture (host + ansible), TTY menu, web UI --- README.md | 170 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 139 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index a5e907d..4a4807b 100644 --- a/README.md +++ b/README.md @@ -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@ (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/` for file downloads. + ### `inventory.ini` Standard Ansible inventory. Structure: