Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
139 commits
Select commit Hold shift + click to select a range
7936dca
feat(demo_lab_network): new scenario — demo_lab + inter-bridge iptabl…
t0kubetsu May 15, 2026
fa1c75f
fix(demo_lab_network): use proxmox-cli (SSH) host for iptables tasks
t0kubetsu May 15, 2026
8f506e0
fix(demo_lab_network): pin python3 interpreter on proxmox-cli to supp…
t0kubetsu May 15, 2026
7c88272
fix(demo_lab_network): lift CTF air-gap during setup so cloud-init an…
t0kubetsu May 15, 2026
e4b7658
fix(demo_lab_network): enable network utils on vuln-boxes (ping, nmap…
t0kubetsu May 15, 2026
594fc95
fix(demo_lab_network): lift_ctf_airgap — handle duplicate/malformed a…
t0kubetsu May 15, 2026
1937784
feat(r42topo): P1 foundation — topology core models, IO, plan
w1ld3r Jun 3, 2026
3ff5b45
feat(r42topo): P2 catalog loader + reservation checks
w1ld3r Jun 3, 2026
e9c132a
feat(r42topo): P3 compiler + adapter + segmentation linter
w1ld3r Jun 3, 2026
89f342e
feat(r42topo): P4 Typer CLI + P5 Textual TUI
w1ld3r Jun 3, 2026
0d8ddbb
fix(r42topo): address ECC python + security review findings
w1ld3r Jun 3, 2026
855273d
docs(r42playbooks): construction blueprint (multi-session handoff)
w1ld3r Jun 3, 2026
7210234
refactor: rename r42topo package to r42playbooks
w1ld3r Jun 3, 2026
17bc6dc
feat(spec): add scenario.r42.yml composition spec model
w1ld3r Jun 3, 2026
93b70c5
feat(catalog): add role/container enumeration + spec ref validation
w1ld3r Jun 3, 2026
c0e786c
feat(allocate): deterministic VM allocation + scenario_vms manifest
w1ld3r Jun 3, 2026
6332bb2
docs: mark r42playbooks plan S1-S4 done, S5b next
w1ld3r Jun 3, 2026
bb30762
chore: ignore scratch flat-workspace compiler artifacts
w1ld3r Jun 3, 2026
0a82521
test(golden): add demo_lab file-list baseline for the S5 renderer
w1ld3r Jun 3, 2026
0c54759
feat(render): S5b verbatim-with-param scenario renderer (class B)
w1ld3r Jun 3, 2026
cd854d9
docs(r42playbooks): mark S5b done, S5a next
w1ld3r Jun 3, 2026
72ee9cd
feat(render): S5a manifest-derived artifacts + freeze public API (cla…
w1ld3r Jun 3, 2026
49a2567
docs(r42playbooks): mark S5a done + API frozen, S6∥S7 next
w1ld3r Jun 3, 2026
5469c7f
feat(cli): S6 re-point CLI to msfvenom-style generator (list/show/new)
w1ld3r Jun 3, 2026
fd82657
docs(r42playbooks): mark S6 done, S7 next
w1ld3r Jun 3, 2026
89d7375
feat(tui): S7 re-point TUI to scenario composer over frozen API
w1ld3r Jun 3, 2026
f4ed7d1
docs(r42playbooks): mark S7 done, S8 next
w1ld3r Jun 3, 2026
f0c2030
feat(api): S8 importable package surface + generator docs
w1ld3r Jun 3, 2026
7e7d2c4
docs(r42playbooks): mark S8 done, S9 next
w1ld3r Jun 3, 2026
443cc5b
fix(s9): address ECC review — renderer/allocator/catalog hardening
w1ld3r Jun 3, 2026
8572a41
fix(tui,allocate): real-catalog template gap + TUI preview crash
w1ld3r Jun 3, 2026
ca405d5
docs(r42playbooks): mark S9 (ECC review) done; only Step 0 housekeepi…
w1ld3r Jun 3, 2026
22f8cf7
feat(render): warn on existing scenario dir + harden TUI against sile…
w1ld3r Jun 3, 2026
d376aa5
fix(tui): Add now gives visible feedback; Selects default to a real v…
w1ld3r Jun 3, 2026
a1ca733
chore: untrack generated scenario my_lab (accidentally staged by git …
w1ld3r Jun 3, 2026
b4b2a1e
fix(tui): keyboard bindings so the composer works without a mouse
w1ld3r Jun 3, 2026
e53547b
fix(tui): make composer fully operable by click, Enter-on-button, and…
w1ld3r Jun 3, 2026
891d55f
fix(tui): side-by-side layout + compact buttons; remove custom key bi…
w1ld3r Jun 3, 2026
74b6200
feat(os): OS-aware box templates + clone-image selection
w1ld3r Jun 3, 2026
2370bec
feat(os): Debian 13 (trixie) image set + OS-aware main.yml imports
w1ld3r Jun 3, 2026
5e54ef8
feat(render): OS-selective 01_init_proxmox (only the image families u…
w1ld3r Jun 3, 2026
2f5a13a
refactor(render): staged 01_init_proxmox (_init_lab layout) + debian …
w1ld3r Jun 3, 2026
e1bfdbb
feat(init): richer idempotent template content in the staged layout (…
w1ld3r Jun 3, 2026
de6f05f
refactor(generator): drop network_policy from the generator path (opt…
w1ld3r Jun 3, 2026
29e1f5e
refactor(image): versioned base-image model (os family -> <distro>_<c…
w1ld3r Jun 3, 2026
f170932
feat(render): emit attachment params into stage_01 (firewall_rules, d…
w1ld3r Jun 3, 2026
c6fa58c
docs: session handoff (continue on another PC) — state, decisions, ne…
w1ld3r Jun 3, 2026
106b913
docs(handoff): profiles belong in the catalog, not bundles (preserve …
w1ld3r Jun 4, 2026
61d5739
docs(handoff): note base-images-in-catalog debt (new 01_image_layer, …
w1ld3r Jun 4, 2026
ad08d80
feat(catalog): add 01_image_layer support — ImageDef model, loader, v…
t0kubetsu Jun 4, 2026
e996905
fix(stage_00): wait for async Proxmox download before stage_01 import…
t0kubetsu Jun 4, 2026
87a9d75
refactor(stage_00): render cloudinit download plays from catalog inst…
t0kubetsu Jun 4, 2026
9067554
refactor(stage_01): render template VM create plays from catalog inst…
t0kubetsu Jun 4, 2026
42ba4b2
refactor(assets): convert 01_init_proxmox/_main.yml to render_assets …
t0kubetsu Jun 4, 2026
f9199f6
refactor(assets): eliminate assets/ entirely — all content now in ren…
t0kubetsu Jun 4, 2026
715cf9b
refactor(box-template): replace spec+image with template_vm direct re…
t0kubetsu Jun 4, 2026
c29ef39
docs(handoff): update session handoff — template_vm refactor, selecti…
t0kubetsu Jun 4, 2026
9a40501
style(render): linter reformatting + fix _DEFAULT_PROXMOX_NODE to pve
t0kubetsu Jun 4, 2026
d413b42
fix(render): clean scenario tree before overwrite (--force)
t0kubetsu Jun 4, 2026
7a597b8
feat(cli): enrich `show` output with description and resolved templat…
t0kubetsu Jun 4, 2026
699d55b
feat(cli): print manifest summary after `new` generates a scenario
t0kubetsu Jun 4, 2026
5ca077f
feat(cli): add `list images` and `show <image>` commands
t0kubetsu Jun 4, 2026
72ffd46
feat(cli): enrich `list scenarios` + fix dead code in allocate
t0kubetsu Jun 4, 2026
49a88a5
feat(cli): add `validate` command for spec typo-checking
t0kubetsu Jun 4, 2026
bfc17d3
feat(render): render notes field into generated README.md
t0kubetsu Jun 4, 2026
7aa566d
style(cli): linter formatting pass
t0kubetsu Jun 4, 2026
0ca3523
docs(handoff): update session handoff — CLI polish pass, 197 tests
t0kubetsu Jun 4, 2026
6cf9a51
style(cli): align list boxes/images columns instead of tab-separated
t0kubetsu Jun 4, 2026
637fecb
test(allocate): cover missing template_subnet raises CompileError path
t0kubetsu Jun 4, 2026
87206e6
feat(cli): auto-detect _reserved.json in output dir for `new`
t0kubetsu Jun 4, 2026
fab2f5a
style(cli): update --reserved help text to mention auto-detection
t0kubetsu Jun 4, 2026
712967a
docs: remove all msfvenom-style references
t0kubetsu Jun 4, 2026
74ab917
test(api): add list_images to frozen surface assertion
t0kubetsu Jun 4, 2026
eb3ec6a
feat(tui): auto-detect _reserved.json in generate() dest dir
t0kubetsu Jun 4, 2026
cdac284
test(tui): add reserved.json auto-detection test for controller.gener…
t0kubetsu Jun 4, 2026
179d281
docs(handoff): update — 200 tests, full CLI polish, auto-detect reserved
t0kubetsu Jun 4, 2026
9cd485c
refactor(generator): remove role from BoxTemplate, subnet placement n…
t0kubetsu Jun 4, 2026
4b6d724
refactor(generator): derive inventory_group from subnet, remove defau…
t0kubetsu Jun 4, 2026
c0429c1
feat(generator): add explicit octet to BoxSpec, remove base_octet fro…
t0kubetsu Jun 4, 2026
d28c7df
feat(tui+cli): add octet field for explicit IP start per box
t0kubetsu Jun 4, 2026
0f2bbb0
feat(tui): show subnet CIDR next to name in subnet dropdown
t0kubetsu Jun 4, 2026
a164b64
fix(tui): widen form panel to 60, pin count/octet to narrow fixed widths
t0kubetsu Jun 4, 2026
50b79ce
refactor(generator): compute section/label from subnet list position …
t0kubetsu Jun 4, 2026
02c35d3
fix(allocate): guard against octet/gateway IP conflicts
t0kubetsu Jun 4, 2026
8e04ef7
fix(allocate): detect duplicate vm_names across subnets
t0kubetsu Jun 4, 2026
571851f
feat(allocate): scope VM names to subnet — {subnet}-{template}-{index…
t0kubetsu Jun 4, 2026
32f8f9f
feat(render): add 05_network_isolation render stage
t0kubetsu Jun 4, 2026
4bfd233
fix(render): remove become: true from network isolation play
t0kubetsu Jun 4, 2026
e362bb2
feat(compiler): add W_EGRESS_ACCEPT=700 weight band
t0kubetsu Jun 4, 2026
1edbaf3
fix(review): apply 11 code-review findings (security + correctness)
t0kubetsu Jun 5, 2026
ba93a67
refactor(render): eliminate _IMAGE_SETS static registry
t0kubetsu Jun 5, 2026
bb28bd8
fix(scaffold): default proxmox_node to pve
t0kubetsu Jun 5, 2026
e712692
feat(render): emit 05_network_isolation/teardown.yml alongside _main.yml
t0kubetsu Jun 5, 2026
8feef35
feat(tui): add network policy selector to the TUI composer
t0kubetsu Jun 5, 2026
bb20906
chore: remove session docs from repo, add .claude/ to .gitignore
t0kubetsu Jun 5, 2026
e2a3ef4
feat(catalog,services): remove ip_octet from ProxmoxTemplateSpec; add…
t0kubetsu Jun 8, 2026
50f51e3
feat(spec): auto-inject services.apt when a single apt-provider box i…
t0kubetsu Jun 8, 2026
2edb632
chore: align misp_lab (additionnal typo + delete_all canonical patter…
hyde-repo Jun 8, 2026
d5868e4
fix: align kunai_lab with project convention (SSH alice, single in-VM…
hyde-repo Jun 8, 2026
d3c55e0
chore: add Python build artifact ignores and .claude to .gitignore
t0kubetsu Jun 8, 2026
85f30eb
feat(scenarios): add test_apt_cacher and test_apt_mirror dual-lan sce…
t0kubetsu Jun 8, 2026
81291cc
chore(ci): add CI workflow and Dependabot hardening
t0kubetsu Jun 9, 2026
707e99b
fix(ci): split workflow into ansible-lint + conditional python jobs
t0kubetsu Jun 9, 2026
443de07
fix(ci): fix python job condition and relax ansible-lint profile
t0kubetsu Jun 9, 2026
55e6c92
fix(ci): expand ansible-lint skip_list for bundles (syntax-check, nam…
t0kubetsu Jun 9, 2026
413fa3d
fix(ci): replace unskippable syntax-check with mock_roles for cross-r…
t0kubetsu Jun 9, 2026
18e15f9
fix(ci): add missing catalog roles to mock_roles + skip jinja[spacing…
t0kubetsu Jun 9, 2026
0582420
fix(ci): add software.install.wazuh-agent to mock_roles
t0kubetsu Jun 9, 2026
4d7d973
ci(workflow): fix push branch triggers — feat/** + fix/** replace fea…
t0kubetsu Jun 9, 2026
f9f5f51
chore(ci): bump actions/checkout to v6
t0kubetsu Jun 9, 2026
cde1440
chore(ci): upgrade ruff check to --select ALL
t0kubetsu Jun 9, 2026
90d37d8
chore(ci): scope ruff to r42playbooks/ — exclude tests from --select ALL
t0kubetsu Jun 9, 2026
8f7171c
fix(ci): correct ansible-lint violations in bundles
t0kubetsu Jun 9, 2026
80977c4
merge: PR #48 (feat/demo_lab_network-scenario) into dev_ada
t0kubetsu Jun 10, 2026
aa3cc30
merge: PR #77 (feat/r42playbooks-generator) into dev_ada
t0kubetsu Jun 10, 2026
06d0ff3
merge: PR #81 (feat/ci-hardening) into dev_ada
t0kubetsu Jun 10, 2026
cea7527
merge: PR #84 (feat/scenarios-wave03) into dev_ada
t0kubetsu Jun 10, 2026
271132d
merge: PR #79 (feat/local-apt-mirror) into dev_ada
t0kubetsu Jun 10, 2026
bcde207
fix(scenarios): parameterize cloud-init DNS instead of hardcoding 1.1…
t0kubetsu Jun 10, 2026
025aa1f
feat(scenarios): add default_vm_ci_dns_ips to scenario templates
t0kubetsu Jun 10, 2026
19db86c
feat(scenarios): add test_rocketchat scenario
t0kubetsu Jun 10, 2026
37b9325
merge: feature/test-rocketchat-scenario into dev_ada
t0kubetsu Jun 10, 2026
716d9e9
chore(scenarios): regenerate _reserved.json
t0kubetsu Jun 10, 2026
b551780
merge: feature/test-rocketchat-scenario (_reserved.json regen) into d…
t0kubetsu Jun 10, 2026
07eda18
fix(generator): emit default_vm_ci_dns_ips in generated scenario temp…
t0kubetsu Jun 10, 2026
c195b78
merge: fix/generator-ci-dns-default into dev_ada
t0kubetsu Jun 10, 2026
fa37b7a
fix(allocate): give each template a unique build IP (was box-octet mi…
t0kubetsu Jun 10, 2026
a6eef10
merge: fix/template-build-ip-collision into dev_ada
t0kubetsu Jun 10, 2026
a9bf3aa
chore(scenarios): regenerate test_apt_cacher + test_apt_mirror (disti…
t0kubetsu Jun 10, 2026
b06e939
merge: fix/regen-apt-scenarios into dev_ada
t0kubetsu Jun 10, 2026
bd6e4fe
fix(test_rocketchat): drop NTP-and-update-time (no reachable NTP in a…
t0kubetsu Jun 10, 2026
042af0d
merge: fix/rocketchat-drop-ntp into dev_ada
t0kubetsu Jun 10, 2026
3f0363a
chore(bundles): remove debian bundles from PR scope
t0kubetsu Jun 11, 2026
356baf2
chore(bundles): revert proxmox/configure/default/vms to main
t0kubetsu Jun 11, 2026
ae8c367
chore(roles): remove .ansible/roles additions from PR scope
t0kubetsu Jun 11, 2026
bd0bd6b
chore(scenarios): revert scenarios/ to main
t0kubetsu Jun 11, 2026
16366c2
chore(scenarios): align scenarios/ with origin/main
t0kubetsu Jun 11, 2026
fb3b7ac
chore(scenarios): sync scenarios/ with origin/dev
t0kubetsu Jun 11, 2026
d59d944
chore(scenarios): restore scenarios/ to merge base state
t0kubetsu Jun 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .ansible-lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
profile: basic

skip_list:
# SCREAMING_SNAKE is the documented convention for infrastructure-wide vars
- var-naming[pattern]
# noun.verb role naming is the documented convention
- role-name
# generated scenario playbooks do not require play-level names
- name[play]
# pre-existing task naming convention: lowercase names throughout bundles
- name[casing]
# generated bundles include tasks without explicit names
- name[missing]
# Jinja template syntax in task names is intentional (loop items, vars)
- name[template]
# pre-existing {{ item.ID}} spacing in generated bundles — cosmetic only
- jinja[spacing]

warn_list:
- yaml[line-length]
- yaml[empty-lines]
- yaml[trailing-spaces]
- risky-shell-pipe

# catalog and cross-repo roles not checked out in CI — stub so syntax-check passes
mock_roles:
- range42-ansible_roles-proxmox_controller
- ansible.utils
- indexer
- software.configure.docker-compose
- software.configure.firewalls
- software.configure.tailscale_disable_nftables
- software.install.nodejs_app_systemd
- software.install.symlinks_farm
- software.install.tailscale
- software.install.warmup.basic_packages
- software.install.warmup.dot_files
- software.install.wazuh-dashboard
- software.install.wazuh-filebeat-oss
- software.install.wazuh-indexer
- software.install.wazuh-manager
- systems.configure.add_user
- software.install.wazuh-agent

# vars defined at runtime via inventory/group_vars — stub for CI syntax-check
extra_vars:
global_vm_ssh_name: ci_mock
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
labels: [dependencies]
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
labels: [dependencies, ci]
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI
on:
push:
branches: [main, dev, 'feat/**', 'fix/**']
pull_request:
branches: [main, dev]
jobs:
ansible-lint:
name: Ansible Lint
runs-on: ubuntu-latest
container:
image: python:3.13-slim
steps:
- run: apt-get update && apt-get install -y --no-install-recommends git
- uses: actions/checkout@v6
- run: pip install --no-cache-dir ansible-lint
- run: ansible-lint bundles/

python:
name: Ruff + Pytest
runs-on: ubuntu-latest
container:
image: python:3.13-slim
steps:
- run: apt-get update && apt-get install -y --no-install-recommends git
- uses: actions/checkout@v6
- id: pycheck
run: test -f pyproject.toml && echo "exists=true" >> "$GITHUB_OUTPUT" || echo "exists=false" >> "$GITHUB_OUTPUT"
- if: "steps.pycheck.outputs.exists == 'true'"
run: pip install --no-cache-dir -e ".[dev]"
- if: "steps.pycheck.outputs.exists == 'true'"
run: ruff check --select ALL r42playbooks/
- if: "steps.pycheck.outputs.exists == 'true'"
run: pytest tests/ --tb=short -q
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,23 @@
**/secret/
CLAUDE.md
**/CLAUDE.md

# Python (r42playbooks module)
.venv/
__pycache__/
*.py[cod]
*.egg-info/
.pytest_cache/
.ruff_cache/
.coverage
htmlcov/
dist/
build/

# Claude session files (local only — handoffs, design notes)
.claude/

# Scratch compiler artifacts (flat-workspace outputs, never committed)
/topology.json
/hosts.yml
/expanded.json
36 changes: 36 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"

[project]
name = "r42playbooks"
version = "0.1.0"
description = "range42 scenario generator — compose labs from the catalog into deployable scenarios/"
readme = "r42playbooks/README.md"
requires-python = ">=3.11"
license = { text = "GPL-3.0-only" }
dependencies = [
"pydantic>=2,<3",
"pyyaml>=6",
]

[project.optional-dependencies]
cli = ["typer>=0.12"]
tui = ["textual>=0.60"]
dev = ["pytest>=8.3", "pytest-cov"]

[project.scripts]
r42playbooks = "r42playbooks.cli:app"
r42playbooks-tui = "r42playbooks.tui.app:main"

[tool.setuptools.packages.find]
include = ["r42playbooks*"]

[tool.setuptools.package-data]
# Verbatim scenario boilerplate copied into every generated tree (e.g. the
# 01_init_proxmox/ template-creation subtree — see render.py / plan H3).
r42playbooks = ["assets/**/*"]

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q"
104 changes: 104 additions & 0 deletions r42playbooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# r42playbooks

**Scenario generator for range42 — compose labs from the catalog.**

`r42playbooks` lists the composable modules in `range42-catalog`, lets you compose a lab
(CLI flags, a `scenario.r42.yml` spec, or the TUI), and **generates a real
`scenarios/<name>/` directory** in the existing `demo_lab` format — deployable through the
normal range42 flow (`range42-context deploy`) with no changes elsewhere.

It is **not** the canonical topology engine for the gamenet "universal" playbook (that
convergence is parked on a separate branch, issue #67). A generated scenario references
catalog content **by name only** — Ansible roles resolve via `ANSIBLE_ROLES_PATH`, container
stacks via `RANGE42_INVENTORY__DOCKER__*` — so the generated tree stays a thin recipe of names,
never a copy of role code.

## Install

```bash
pip install r42playbooks # pure core only (pydantic + pyyaml + stdlib)
pip install "r42playbooks[cli]" # + Typer CLI
pip install "r42playbooks[tui]" # + Textual TUI
```

## CLI (`list` / `show` / `new` / `validate`)

```bash
# explore the catalog
r42playbooks list boxes --catalog ../range42-catalog
r42playbooks list subnets --catalog ../range42-catalog
r42playbooks list policies --catalog ../range42-catalog
r42playbooks list roles --catalog ../range42-catalog
r42playbooks list containers --catalog ../range42-catalog
r42playbooks show vuln-box --catalog ../range42-catalog

# compose + generate a scenario from flags
r42playbooks new my_lab \
--subnet default-3zone --policy air-gap-ctf \
--box admin-wazuh --box vuln-box:count=5 \
--catalog ../range42-catalog -o scenarios/

# or regenerate from a saved composition (written into every scenario)
r42playbooks new my_lab --spec scenarios/my_lab/scenario.r42.yml \
--catalog ../range42-catalog -o scenarios/
```

## TUI

```bash
r42playbooks-tui ../range42-catalog
```

Pick a name, subnet layout, and network policy; add boxes (with a count); preview the
allocation; generate.

## Library (`import r42playbooks`)

The stable surface is re-exported at the package root so a downstream tool
(`r42deploy`/`r42runtime`, the range42 deployment CLI/TUI) can drive generation by import alone:

```python
import r42playbooks as r

catalog = r.load_catalog("/path/to/range42-catalog")
spec = r.load_spec("scenario.r42.yml") # or r.ScenarioSpec.model_validate({...})

problems = r.validate_refs(spec, catalog) # typo guard — [] means every ref resolves
if not problems:
root = r.render_scenario(spec, catalog=catalog, dest="scenarios/")
print(f"generated {root}")
```

Everything raises the `r42playbooks.core.errors` hierarchy (`TopologyError` and subclasses) —
never a framework type. Frozen entry points: `load_catalog`, `list_roles`, `list_containers`,
`validate_refs`, `load_spec`, `allocate`, `render_scenario(spec, *, catalog, dest, reserved=None)`.

## What gets generated

```
scenarios/<name>/
scenario.r42.yml # the composition (re-runnable)
main.yml / main_vms_only.yml # import skeleton (only the emitted sections)
01_init_proxmox/templates/ # 9xxx Proxmox template creation (vendored, verbatim)
NN_<section>/ # 02_admin / 03_student / 04_ctf, per composed box role
_main.yml # stage imports + per-VM global_* overrides
stage_00/<vm>.yml # clone Proxmox template -> create VM
stage_01/<vm>.yml # roles: [<catalog role NAMES>] + <vm>.devkit/
manifest/scenario_vms.json # allocated vm_id / ip (octet rule, _reserved.json aware)
templates/ # ansible-inventory.j2, ssh-config.j2, ansible-vars.yml, vault-example.yml
<name>.setup.sh / .delete_all.sh / reset scripts
```

`secrets/` is **not** generated — it is a deploy-time symlink created by `range42-context use`.

## Develop

```bash
python3 -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
.venv/bin/python -m pytest -q
```

See [`docs/r42playbooks-plan.md`](../docs/r42playbooks-plan.md) for the full build plan and status.

GPL-3.0.
61 changes: 61 additions & 0 deletions r42playbooks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""r42playbooks — range42 scenario generator.

Lists composable ``range42-catalog`` modules, lets a user compose a lab (flags,
a ``scenario.r42.yml`` spec, the CLI, or the TUI), and generates a real
``scenarios/<name>/`` directory in the existing demo_lab format — deployable
through the normal range42 flow with no changes elsewhere.

The pure core (``r42playbooks.core``) is framework-agnostic; the stable surface
(``r42playbooks.api``) is re-exported here so a downstream tool can drive
generation by import alone::

import r42playbooks as r

catalog = r.load_catalog("/path/to/range42-catalog")
spec = r.load_spec("scenario.r42.yml") # or r.ScenarioSpec.model_validate(...)
if not r.validate_refs(spec, catalog):
root = r.render_scenario(spec, catalog=catalog, dest="scenarios/")

Everything raises the ``r42playbooks.core.errors`` hierarchy — never a framework
type. CLI/TUI deps are optional extras (``pip install r42playbooks[cli]``, ``[tui]``).
"""

from r42playbooks.api import (
Allocation,
Catalog,
ReservedIndex,
ScenarioExistsError,
ScenarioSpec,
allocate,
dump_spec_atomic,
list_containers,
list_images,
list_roles,
load_catalog,
load_spec,
render_scenario,
validate_refs,
)
from r42playbooks.core.models import Topology

__all__ = [
# generator surface (the stable import-only contract)
"load_catalog",
"list_images",
"list_roles",
"list_containers",
"validate_refs",
"load_spec",
"dump_spec_atomic",
"allocate",
"render_scenario",
"ScenarioSpec",
"Allocation",
"Catalog",
"ReservedIndex",
"ScenarioExistsError",
# legacy topology model (pre-pivot, kept for back-compat)
"Topology",
"__version__",
]
__version__ = "0.1.0"
Loading
Loading