From 12f2cd11cbe50a181add4b2e66b196131b15f471 Mon Sep 17 00:00:00 2001 From: Markus Fensterer Date: Fri, 18 Sep 2026 16:38:44 +0200 Subject: [PATCH 1/8] Add an Ansible export for the metal-roles partition roles A new Ansible tab generates an inventory, group and host variables and playbooks for the metal-roles partition roles (sonic-config, metal-core, mgmt-server, dhcp, metal-bmc, pixiecore, image-cache), laid out like a metal-stack deployment repository, previewed per file and downloadable as a zip. - derive/devices.ts: named devices per partition, the single source of hostnames and ASNs (leaves unique, spines and exits shared, per the metal-stack network docs). - derive/ip/deviceAddresses.ts: loopbacks, management addresses and gateways, per-leaf PXE networks (metal-core CIDRs), DHCP ranges for the BMCs and transfer network addresses from the infra subnets. - IP plan: infra subnets carry a kind; L3 management networks get a management loopback pool; the PXE subnet is sized as one slice per leaf plus one for the exits, as every leaf routes its own PXE network. - model/deployment.ts: Plan.deployment for the few settings the export needs (additive with defaults, no format bump). - model/ansibleRoles.ts mirrors the role variables of metal-roles v0.17.26; tests fail on any generated variable not listed there. - Values the plan cannot know are written as CHANGE_ME and listed. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 1 + README.md | 7 + THIRD-PARTY-NOTICES.md | 23 +- package-lock.json | 18 ++ package.json | 2 + src/App.tsx | 11 +- src/derive/ansible/ansible.test.ts | 161 +++++++++ src/derive/ansible/index.ts | 88 +++++ src/derive/ansible/inventory.ts | 80 +++++ src/derive/ansible/playbooks.ts | 177 ++++++++++ src/derive/ansible/vars.ts | 448 ++++++++++++++++++++++++++ src/derive/ansible/yaml.test.ts | 46 +++ src/derive/ansible/yaml.ts | 97 ++++++ src/derive/devices.test.ts | 83 +++++ src/derive/devices.ts | 204 ++++++++++++ src/derive/ip/deviceAddresses.test.ts | 81 +++++ src/derive/ip/deviceAddresses.ts | 258 +++++++++++++++ src/derive/ip/ipPlan.test.ts | 20 +- src/derive/ip/ipPlan.ts | 100 ++++-- src/derive/ip/validateIp.test.ts | 2 +- src/io/ansibleZip.test.ts | 21 ++ src/io/ansibleZip.ts | 13 + src/model/ansibleRoles.ts | 106 ++++++ src/model/defaults.ts | 2 + src/model/deployment.ts | 30 ++ src/model/plan.ts | 3 + src/store/planStore.ts | 8 +- src/views/AnsibleView.tsx | 126 ++++++++ src/views/ansible/FileBrowser.tsx | 157 +++++++++ src/views/ansible/SettingsCard.tsx | 143 ++++++++ src/views/ansible/infos.ts | 15 + src/views/icons/index.tsx | 14 + src/views/ips/infos.ts | 2 +- 33 files changed, 2515 insertions(+), 32 deletions(-) create mode 100644 src/derive/ansible/ansible.test.ts create mode 100644 src/derive/ansible/index.ts create mode 100644 src/derive/ansible/inventory.ts create mode 100644 src/derive/ansible/playbooks.ts create mode 100644 src/derive/ansible/vars.ts create mode 100644 src/derive/ansible/yaml.test.ts create mode 100644 src/derive/ansible/yaml.ts create mode 100644 src/derive/devices.test.ts create mode 100644 src/derive/devices.ts create mode 100644 src/derive/ip/deviceAddresses.test.ts create mode 100644 src/derive/ip/deviceAddresses.ts create mode 100644 src/io/ansibleZip.test.ts create mode 100644 src/io/ansibleZip.ts create mode 100644 src/model/ansibleRoles.ts create mode 100644 src/model/deployment.ts create mode 100644 src/views/AnsibleView.tsx create mode 100644 src/views/ansible/FileBrowser.tsx create mode 100644 src/views/ansible/SettingsCard.tsx create mode 100644 src/views/ansible/infos.ts diff --git a/CLAUDE.md b/CLAUDE.md index 9ea3cde..1df3b4d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,6 +28,7 @@ A client-only single-page app (React + Vite + TypeScript) to plan a [metal-stack - **Rack defaults and power budget.** `Partition.rackDefaults` (`heightUnits` 42, `maxPowerWatts` 12000 — five 3U eight-node MicroCloud chassis at ~2 kW plus a leaf pair no longer fit in 10 kW) seed racks added to the partition and apply to the central rack; each rack keeps its own `heightUnits` / `maxPowerWatts` in its Advanced section, which also holds the rarely changed leaf model and leaf count and a rack group's member names. The central rack's Advanced section likewise holds the internet router and storage leaf counts, its hardware models (spine, superspine, storage leaf, mgmt spine/leaf/server) and mgmt leaves per rack, grouped with the network OS, fabric links and rack defaults; the section header shows the chosen models read-only. Issues that can only be fixed in either Advanced section carry `target.field: 'advanced'`, and `navigateTo()` unfolds that section (`details[data-advanced]`) when revealing them. Validation reports an error per physical rack whose estimated draw exceeds its budget. - **Info bubbles.** `views/plan/InfoBubble.tsx` renders the "i" bubbles; `SelectField` / `NumberField` take an `info` prop. Texts must match the metal-stack docs, and links come from `views/plan/docs.ts` — only pages verified to exist on docs.metal-stack.io. The partition's central components are edited in `CentralRackSection.tsx` (the UI says "Central rack", the schema still calls it `fabric`). - **IP plan.** Inputs live in `Plan.ipPlan` (`src/model/ipPlan.ts`: per-family schema, infrastructure inputs, presets). `src/derive/ip/cidr.ts` is the BigInt CIDR library (no dependency); `src/derive/ip/ipPlan.ts` derives limits (each with a formula string), the partition allocation (sequential, skipping slots that overlap Kubernetes, reserve, internet or infrastructure ranges — an internet range containing the whole project CIDR is by design), the example cluster and the infrastructure subnets; `src/derive/ip/validateIp.ts` reports issues with `target: { section: 'ips', field }`, which `navigateTo()` opens in the IPs tab. Infrastructure sizing rules are in the header of `ipPlan.ts`. Presets mirror the layouts of the untracked `ip_calc.ods`; their public ranges must be documentation ranges (RFC 5737 / RFC 3849), never real allocations. `ipPlanToCsv()` in `src/io/ipCsv.ts` exports the address plan. +- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`. - **Icons.** Icons come from `src/views/icons/` only: `index.tsx` is the registry (Lucide icons plus the custom `NetworkSwitch`, `Rack`, `RackGroup` glyphs from `custom.ts`, drawn on Lucide's 24 px grid) with typed maps per tab, node kind, BOM category, rack slot kind and severity, and an `` helper (decorative, `aria-hidden`). An ESLint rule forbids importing `lucide-react` anywhere else. Sizes: 16 px in tabs and buttons, 14 px inline and in topology nodes, 12 px in rack slots; color follows the text. - **Templates.** `src/model/templates.ts` holds the starting plans offered by the Templates menu. They must order only hardware that is current at the vendor — `templates.test.ts` fails on any `availability` of `eol` / `withdrawn`, for the templates and for the default plan. Each `build()` returns a fresh Plan with new ids; `templates.test.ts` asserts every template parses and has no validation errors, so keep them valid when capacity rules change. - **Persistence and format version.** Zustand store (`src/store/planStore.ts`) with `persist` middleware → localStorage key `metal-stack-planner/plan`. JSON export/import in `src/io/json.ts`; every import runs through `migrateRawPlan()` (`src/model/migrate.ts`) and is then validated with `PlanSchema.parse`, and the same path runs on rehydrate. `SCHEMA_VERSION` in `migrate.ts` is the plan file format version — currently 1 with no migrations. Purely additive fields only need a Zod `.default()`; anything that breaks older files bumps `SCHEMA_VERSION` and adds a function to `MIGRATIONS`, keyed by the version it upgrades _from_. Files from a newer version are refused with a readable message. diff --git a/README.md b/README.md index 3c08c3e..40eac87 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,13 @@ consistent — you edit the plan, everything else follows from it. loopbacks, PXE, management and transfer networks — from the plan's switches and servers. Overlapping or misaligned ranges are reported like any other issue, and the address plan exports as CSV. +- **Ansible export** — an inventory, group and host variables for the + [metal-roles](https://github.com/metal-stack/metal-roles) partition roles (sonic-config, + metal-core, mgmt-server, dhcp, metal-bmc, pixiecore, image-cache) and the playbooks that apply + them, laid out like a metal-stack deployment repository. Hostnames, ASNs, loopbacks, management + addresses, per-leaf PXE networks, DHCP ranges and transfer networks come from the plan and the + IP plan; secrets, endpoints and switch ports are marked `CHANGE_ME` and listed. Previewed file + by file and downloaded as a zip. - **Prices** — an optional price book (kept in the browser, separate from the plan, importable and exportable as JSON) turns the BOM into a cost estimate with line totals, category subtotals and a grand total. diff --git a/THIRD-PARTY-NOTICES.md b/THIRD-PARTY-NOTICES.md index f2dafcc..51ef6e6 100644 --- a/THIRD-PARTY-NOTICES.md +++ b/THIRD-PARTY-NOTICES.md @@ -8,7 +8,7 @@ Build-time tooling is not listed: it never reaches a user. Regenerate with `npm run notices`; CI checks that it stays current. -88 packages. +89 packages. > **Note.** The following declare no license and ship no license file: > `buffers`. @@ -3558,6 +3558,27 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` +## yaml 2.9.1 + +License: ISC +Homepage: https://eemeli.org/yaml/ + +```text +Copyright Eemeli Aro + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +``` + ## zip-stream 4.1.1 License: MIT diff --git a/package-lock.json b/package-lock.json index a04871b..3af711e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,11 +7,14 @@ "": { "name": "metal-stack-planner", "version": "0.0.0", + "license": "MIT", "dependencies": { "exceljs": "^4.4.0", + "jszip": "^3.10.2", "lucide-react": "^1.45.0", "react": "^19.3.0", "react-dom": "^19.3.0", + "yaml": "^2.9.1", "zod": "^4.6.1", "zundo": "^2.3.0", "zustand": "^5.0.15" @@ -5079,6 +5082,21 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index d53db59..90ddf05 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,11 @@ }, "dependencies": { "exceljs": "^4.4.0", + "jszip": "^3.10.2", "lucide-react": "^1.45.0", "react": "^19.3.0", "react-dom": "^19.3.0", + "yaml": "^2.9.1", "zod": "^4.6.1", "zundo": "^2.3.0", "zustand": "^5.0.15" diff --git a/src/App.tsx b/src/App.tsx index 05ab68d..06cd0b5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo } from 'react' +import { lazy, Suspense, useEffect, useMemo } from 'react' import { countIssues, validatePlan } from './derive/validate' import { useHistory, usePlanStore, type View } from './store/planStore' import BomView from './views/BomView' @@ -12,12 +12,16 @@ import TopologyView from './views/TopologyView' import logoUrl from './assets/metal-stack-logo.svg' import { ACTION_ICON, Icon, TAB_ICON } from './views/icons' +// The Ansible tab (and its YAML writer) loads on first use. +const AnsibleView = lazy(() => import('./views/AnsibleView')) + const tabs: { view: View; label: string }[] = [ { view: 'plan', label: 'Plan' }, { view: 'topology', label: 'Topology' }, { view: 'racks', label: 'Racks' }, { view: 'ips', label: 'IPs' }, { view: 'bom', label: 'BOM' }, + { view: 'ansible', label: 'Ansible' }, ] const isMac = typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform) @@ -128,6 +132,11 @@ export default function App() { {activeView === 'racks' && } {activeView === 'ips' && } {activeView === 'bom' && } + {activeView === 'ansible' && ( + + + + )} diff --git a/src/derive/ansible/ansible.test.ts b/src/derive/ansible/ansible.test.ts new file mode 100644 index 0000000..954b55b --- /dev/null +++ b/src/derive/ansible/ansible.test.ts @@ -0,0 +1,161 @@ +import { describe, expect, it } from 'vitest' +import { parse } from 'yaml' +import { KNOWN_VARIABLES } from '../../model/ansibleRoles' +import { createEmptyPlan } from '../../model/defaults' +import { templates } from '../../model/templates' +import { CHANGE_ME, deriveAnsible } from './index' + +const plans = () => [ + { id: 'default', plan: createEmptyPlan() }, + ...templates.map((t) => ({ id: t.id, plan: t.build() })), +] + +/** Hosts of an inventory tree, depth first. */ +function inventoryHosts(group: Record | null): string[] { + if (!group) return [] + const hosts = Object.keys((group.hosts as Record) ?? {}) + const children = Object.values((group.children as Record) ?? {}) + return [...hosts, ...children.flatMap((c) => inventoryHosts(c as Record))] +} + +describe('Ansible export', () => { + it.each(plans())('writes valid YAML with known role variables ($id)', ({ plan }) => { + const { files } = deriveAnsible(plan) + const paths = files.map((f) => f.path) + expect(new Set(paths).size).toBe(paths.length) + for (const f of files.filter((f) => f.path.endsWith('.yaml'))) { + const doc = parse(f.content) + if (!/\/(group|host)_vars\//.test(f.path) || doc === null) continue + const unknown = Object.keys(doc).filter((k) => !KNOWN_VARIABLES.has(k)) + expect(unknown, f.path).toEqual([]) + } + }) + + it.each(plans())('gives every inventory host its host_vars ($id)', ({ plan }) => { + const { files, devices } = deriveAnsible(plan) + const inventory = parse(files.find((f) => f.path.endsWith('inventory.yaml'))!.content) + const hosts = [...new Set(inventoryHosts(inventory.all))].sort() + const expected = devices.flatMap((p) => p.devices.map((d) => d.hostname)).sort() + expect(hosts).toEqual(expected) + for (const h of hosts) { + expect(files.some((f) => f.path === `inventories/prod/host_vars/${h}.yaml`)).toBe(true) + } + }) + + it('derives every address of the templates, leaving only what the plan cannot know', () => { + for (const { plan } of plans()) { + const { placeholders, notes } = deriveAnsible(plan) + expect(notes).toEqual([]) + const derivable = placeholders.filter((p) => + /ansible_host|loopback|metal_core_cidr|router_id|mgmt_gateway/.test(p.key), + ) + expect(derivable).toEqual([]) + } + }) + + it('lists each CHANGE_ME as a placeholder', () => { + const { files, placeholders } = deriveAnsible(createEmptyPlan()) + for (const f of files.filter((f) => f.path.endsWith('.yaml'))) { + const marks = f.content.split(CHANGE_ME).length - 1 + expect( + placeholders.filter((p) => p.file === f.path), + f.path, + ).toHaveLength(marks) + } + }) + + it('uses the deployment settings', () => { + const plan = createEmptyPlan() + plan.deployment = { + ...plan.deployment, + environment: 'Staging', + metalStackRelease: 'v0.21.6', + nameservers: ['192.0.2.53'], + ntpServers: ['192.0.2.123'], + } + const { files, placeholders } = deriveAnsible(plan) + const file = (suffix: string) => parse(files.find((f) => f.path.endsWith(suffix))!.content) + expect(file('staging/group_vars/all/release_vector.yaml').metal_stack_release_version).toBe( + 'v0.21.6', + ) + expect(file('group_vars/partition/switches.yaml')).toMatchObject({ + sonic_config_nameservers: ['192.0.2.53'], + sonic_config_ntp: { servers: ['192.0.2.123'] }, + }) + expect(placeholders.some((p) => p.key === 'metal_stack_release_version')).toBe(false) + expect(files.find((f) => f.path === 'ansible.cfg')!.content).toContain( + 'inventory = inventories/staging/inventory.yaml', + ) + }) + + it('lays out the default inventory', () => { + const { files } = deriveAnsible(createEmptyPlan()) + expect(files.find((f) => f.path === 'inventories/prod/inventory.yaml')!.content) + .toMatchInlineSnapshot(` + "--- + # Generated by the metal-stack planner. Hosts are named after the plan; + # ansible_host comes from host_vars (the management addresses). + all: + children: + partition: + children: + mgmtservers: + children: + partition_1_mgmtservers: + mgmtleaves: + children: + partition_1_mgmtleaves: + mgmtspines: + children: + partition_1_mgmtspines: + inet: + children: + partition_1_inet: + leaves: + children: + partition_1_leaves: + spines: + children: + partition_1_spines: + superspines: {} + exits: + children: + partition_1_exits: + storageleaves: {} + # Partition 1 + partition_1: + children: + partition_1_mgmtservers: + hosts: + partition-1-mgmtserver01: + partition-1-mgmtserver02: + partition_1_mgmtleaves: + hosts: + partition-1-r01mgmtleaf: + partition_1_mgmtspines: + hosts: + partition-1-mgmtspine01: + partition-1-mgmtspine02: + partition_1_inet: + hosts: + partition-1-inet01: + partition-1-inet02: + partition_1_leaves: + children: + # Rack 1 + partition_1_r01: + hosts: + partition-1-r01leaf01: + partition-1-r01leaf02: + partition_1_spines: + hosts: + partition-1-spine01: + partition-1-spine02: + partition_1_exits: + hosts: + partition-1-exit01: + partition-1-exit02: + " + `) + }) +}) diff --git a/src/derive/ansible/index.ts b/src/derive/ansible/index.ts new file mode 100644 index 0000000..2b542b8 --- /dev/null +++ b/src/derive/ansible/index.ts @@ -0,0 +1,88 @@ +import type { Plan } from '../../model/plan' +import { deriveDevices, slugify, type PartitionDevices } from '../devices' +import { deriveDeviceAddresses, type PartitionAddresses } from '../ip/deviceAddresses' +import { deriveIpPlan } from '../ip/ipPlan' +import { inventoryFile } from './inventory' +import { staticFiles, readme } from './playbooks' +import { groupVarFiles, hostVarFiles } from './vars' + +// The Ansible export: an inventory, group and host variables for the +// metal-roles partition roles, and the playbooks that apply them, laid out +// like a metal-stack deployment repository: +// +// README.md, ansible.cfg, requirements.yaml, deploy_*.yaml +// inventories//inventory.yaml +// inventories//group_vars//.yaml +// inventories//host_vars/.yaml +// +// Everything is derived from the plan (devices, addresses, ASNs, DHCP +// ranges); what the plan cannot know (secrets, API endpoints, interface +// names, switch port layouts) is written as CHANGE_ME and listed as a +// placeholder, in the view and in the generated README. + +export const CHANGE_ME = 'CHANGE_ME' + +export interface AnsibleFile { + path: string + content: string +} + +export interface Placeholder { + file: string + key: string + reason: string +} + +export interface AnsibleExport { + files: AnsibleFile[] + placeholders: Placeholder[] + /** Addresses that could not be handed out, and why. */ + notes: string[] + devices: PartitionDevices[] + addresses: PartitionAddresses[] +} + +/** Collects files and placeholders while the generators run. */ +export class AnsibleOut { + readonly files: AnsibleFile[] = [] + readonly placeholders: Placeholder[] = [] + /** inventories/ */ + readonly inventory: string + + constructor(environment: string) { + this.inventory = `inventories/${slugify(environment) || 'prod'}` + } + + /** Records a placeholder and returns the marker to write in its place. */ + todo(file: string, key: string, reason: string): string { + this.placeholders.push({ file, key, reason }) + return CHANGE_ME + } + + add(path: string, content: string): void { + this.files.push({ path, content }) + } +} + +export function deriveAnsible(plan: Plan): AnsibleExport { + const devices = deriveDevices(plan) + const addresses = deriveDeviceAddresses(plan, deriveIpPlan(plan), devices) + const out = new AnsibleOut(plan.deployment.environment) + const ctx = { plan, devices, addresses, out } + + staticFiles(ctx) + inventoryFile(ctx) + groupVarFiles(ctx) + hostVarFiles(ctx) + const notes = addresses.flatMap((a) => a.notes) + // The README lists the placeholders, so it comes last and goes first. + out.files.unshift({ path: 'README.md', content: readme(ctx, notes) }) + return { files: out.files, placeholders: out.placeholders, notes, devices, addresses } +} + +export interface AnsibleContext { + plan: Plan + devices: PartitionDevices[] + addresses: PartitionAddresses[] + out: AnsibleOut +} diff --git a/src/derive/ansible/inventory.ts b/src/derive/ansible/inventory.ts new file mode 100644 index 0000000..91d1f76 --- /dev/null +++ b/src/derive/ansible/inventory.ts @@ -0,0 +1,80 @@ +import type { DeviceRole, PartitionDevices } from '../devices' +import type { AnsibleContext } from './index' +import { kv, toYaml, ymap, type YEntry, type YMap } from './yaml' + +// inventory.yaml: functional groups (leaves, spines …) across partitions, +// each with one child group per partition, and per partition a group +// holding those children, the leaves split into one group per rack. This +// is the layout of metal-stack deployment repositories: group_vars can +// target a device kind everywhere, one partition, or one rack. + +/** Functional group per device role, in inventory order. */ +export const ROLE_GROUP: Record = { + 'mgmt-server': 'mgmtservers', + 'mgmt-leaf': 'mgmtleaves', + 'mgmt-spine': 'mgmtspines', + router: 'inet', + leaf: 'leaves', + spine: 'spines', + superspine: 'superspines', + exit: 'exits', + 'storage-leaf': 'storageleaves', +} + +const ROLES = Object.keys(ROLE_GROUP) as DeviceRole[] + +export const partitionGroup = (p: PartitionDevices, role: DeviceRole) => + `${p.group}_${ROLE_GROUP[role]}` + +export const rackGroup = (p: PartitionDevices, tag: string) => `${p.group}_${tag}` + +const hosts = (names: string[]): YMap => ymap(...names.map((n) => kv(n, null))) + +function partitionEntry(p: PartitionDevices): YEntry { + const children = ROLES.flatMap((role): YEntry[] => { + const list = p.devices.filter((d) => d.role === role) + if (list.length === 0) return [] + if (role !== 'leaf') { + return [kv(partitionGroup(p, role), ymap(kv('hosts', hosts(list.map((d) => d.hostname)))))] + } + const racks = p.racks + .map((r) => ({ r, names: list.filter((d) => d.rack?.rackId === r.rackId) })) + .filter(({ names }) => names.length > 0) + .map(({ r, names }) => + kv(rackGroup(p, r.tag), ymap(kv('hosts', hosts(names.map((d) => d.hostname)))), r.name), + ) + return [kv(partitionGroup(p, role), ymap(kv('children', ymap(...racks))))] + }) + return kv(p.group, ymap(kv('children', ymap(...children))), p.partitionName) +} + +export function inventoryFile({ devices, out }: AnsibleContext): void { + const functional = ROLES.map((role) => { + const members = devices + .filter((p) => p.devices.some((d) => d.role === role)) + .map((p) => kv(partitionGroup(p, role), null)) + return kv(ROLE_GROUP[role], members.length ? ymap(kv('children', ymap(...members))) : ymap()) + }) + out.add( + `${out.inventory}/inventory.yaml`, + toYaml( + ymap( + kv( + 'all', + ymap( + kv( + 'children', + ymap( + kv( + 'partition', + ymap(kv('children', ymap(...functional, ...devices.map(partitionEntry)))), + ), + ), + ), + ), + ), + ), + 'Generated by the metal-stack planner. Hosts are named after the plan;\nansible_host comes from host_vars (the management addresses).', + ), + ) +} diff --git a/src/derive/ansible/playbooks.ts b/src/derive/ansible/playbooks.ts new file mode 100644 index 0000000..62e814c --- /dev/null +++ b/src/derive/ansible/playbooks.ts @@ -0,0 +1,177 @@ +import { ANSIBLE_COMMON_REPO, METAL_ROLES_REPO } from '../../model/ansibleRoles' +import { formatCidr, formatIp } from '../ip/cidr' +import type { AnsibleContext } from './index' +import { fromObject, kv, toYaml, toYamlSeq, ymap, type YValue } from './yaml' + +// Playbooks, role requirements, ansible.cfg and the README of the Ansible +// export. The plays follow mini-lab's deploy_partition.yaml and the +// deployment repositories: sonic-config on every switch, metal-core on the +// leaves, the management server roles on the mgmt servers. + +const role = (name: string, tags?: string): YValue => fromObject(tags ? { name, tags } : { name }) + +/** Collections the targeted metal-roles and ansible-common need. */ +const COLLECTIONS = ['community.docker', 'community.general', 'ansible.posix', 'ansible.utils'] + +const INSTALL = [ + 'ansible-galaxy role install -r requirements.yaml -p roles', + 'ansible-galaxy collection install -r requirements.yaml', +] + +const COMMON = role('ansible-common', 'always') +const SONIC = role('metal-roles/partition/roles/sonic-config', 'sonic-config') + +function play(name: string, hosts: string, roles: YValue[], extra: Record = {}) { + return fromObject({ name, hosts, ...extra, roles }) +} + +export function staticFiles({ plan, out }: AnsibleContext): void { + const dep = plan.deployment + const header = 'Generated by the metal-stack planner.' + out.add( + 'ansible.cfg', + [ + '# Generated by the metal-stack planner.', + '[defaults]', + `inventory = ${out.inventory}/inventory.yaml`, + 'roles_path = roles', + '', + ].join('\n'), + ) + out.add( + 'requirements.yaml', + toYaml( + ymap( + kv('roles', [ + fromObject({ + src: ANSIBLE_COMMON_REPO, + name: 'ansible-common', + version: dep.ansibleCommonVersion, + }), + fromObject({ + src: METAL_ROLES_REPO, + name: 'metal-roles', + version: dep.metalRolesVersion, + }), + ]), + kv( + 'collections', + COLLECTIONS.map((name) => fromObject({ name })), + 'Modules the roles call (docker containers, timezone, sysctl, IP filters).', + ), + ), + `${header}\n${INSTALL.join('\n')}`, + ), + ) + out.add( + 'deploy_prod_network.yaml', + toYamlSeq( + [ + play( + 'Configure superspines, spines, exits and storage leaves', + 'superspines:spines:exits:storageleaves', + [COMMON, SONIC], + { serial: 1 }, + ), + play('Configure leaves', 'leaves', [COMMON, SONIC], { serial: 1 }), + play( + 'Deploy metal-core', + 'leaves', + [COMMON, role('metal-roles/partition/roles/metal-core', 'metal-core')], + { serial: 1, become: true }, + ), + ], + header, + ), + ) + out.add( + 'deploy_mgmt_network.yaml', + toYamlSeq( + [play('Configure management switches', 'mgmtspines:mgmtleaves', [COMMON, SONIC])], + header, + ), + ) + out.add( + 'deploy_mgmt_server.yaml', + toYamlSeq( + [ + play( + 'Deploy management servers', + 'mgmtservers', + [ + COMMON, + role('metal-roles/partition/roles/mgmt-server', 'mgmt-server'), + role('metal-roles/partition/roles/dhcp', 'dhcp'), + role('metal-roles/partition/roles/metal-bmc', 'metal-bmc'), + role('metal-roles/partition/roles/pixiecore', 'pixiecore'), + role('metal-roles/partition/roles/image-cache', 'image-cache'), + ], + { become: true }, + ), + ], + header, + ), + ) +} + +const row = (cells: (string | number)[]) => `| ${cells.join(' | ')} |` + +export function readme({ plan, devices, addresses, out }: AnsibleContext, notes: string[]): string { + const lines: string[] = [ + `# ${plan.name}: metal-stack partition deployment`, + '', + 'Generated by the metal-stack planner from the plan. Hostnames, ASNs, loopbacks,', + 'management addresses, PXE networks and DHCP ranges are derived; everything the', + 'plan cannot know is marked `CHANGE_ME` and listed below.', + '', + '## Usage', + '', + '```sh', + ...INSTALL, + 'ansible-playbook deploy_mgmt_network.yaml', + 'ansible-playbook deploy_mgmt_server.yaml', + 'ansible-playbook deploy_prod_network.yaml', + '```', + '', + `The inventory is \`${out.inventory}/inventory.yaml\` (set in \`ansible.cfg\`).`, + 'Encrypt `group_vars/partition/secrets.yaml` with `ansible-vault` once filled in.', + '', + ] + if (out.placeholders.length > 0) { + lines.push('## To fill in', '', row(['File', 'Variable', 'What']), row(['---', '---', '---'])) + for (const p of out.placeholders) lines.push(row([`\`${p.file}\``, `\`${p.key}\``, p.reason])) + lines.push('') + } + if (notes.length > 0) { + lines.push('## Address plan warnings', '', ...notes.map((n) => `- ${n}`), '') + } + devices.forEach((p, i) => { + const addr = addresses[i] + lines.push( + `## ${p.partitionName} (\`${p.slug}\`)`, + '', + row(['Host', 'Role', 'ASN', 'Loopback', 'Management']), + row(['---', '---', '---', '---', '---']), + ) + for (const d of p.devices) { + const a = addr.byHost.get(d.hostname)! + lines.push( + row([ + d.hostname, + d.role, + d.asn ?? '–', + a.loopback ?? '–', + a.mgmt ? `${a.mgmt.ip}/${a.mgmt.prefix}` : '–', + ]), + ) + } + if (addr.mgmtSubnets.length > 0) { + lines.push('', row(['Management subnet', 'Scope', 'Gateway']), row(['---', '---', '---'])) + for (const s of addr.mgmtSubnets) { + lines.push(row([formatCidr(s.cidr), s.scope, formatIp(4, s.gateway)])) + } + } + lines.push('') + }) + return lines.join('\n') +} diff --git a/src/derive/ansible/vars.ts b/src/derive/ansible/vars.ts new file mode 100644 index 0000000..d06f3f3 --- /dev/null +++ b/src/derive/ansible/vars.ts @@ -0,0 +1,448 @@ +import { devicesOf, type Device, type PartitionDevices } from '../devices' +import { formatCidr, formatIp, type Cidr } from '../ip/cidr' +import type { PartitionAddresses } from '../ip/deviceAddresses' +import type { AnsibleContext } from './index' +import { partitionGroup, rackGroup } from './inventory' +import { kv, note, toYaml, ymap, type YEntry, type YValue } from './yaml' + +// group_vars and host_vars for the metal-roles partition roles +// (sonic-config on every switch, metal-core on the leaves, mgmt-server, +// dhcp, metal-bmc, pixiecore and image-cache on the management servers). +// Variable names are checked against model/ansibleRoles.ts by the tests. + +const PXE_VLAN = 4000 +const PORTS_NOTE = + 'Port layout (sonic_config_breakouts, sonic_config_ports) depends on the\nswitch model and the cabling, which the planner does not model yet.' + +function netmask(prefix: number): string { + const all = (1n << 32n) - 1n + return formatIp(4, all ^ ((1n << BigInt(32 - prefix)) - 1n)) +} + +const cidrs = (list: Cidr[]) => list.map(formatCidr) + +/** A central-rack or rack switch that runs BGP (loopback and ASN). */ +function isBgpSpeaker(d: Device, l3: boolean): boolean { + if (d.role === 'mgmt-spine' || d.role === 'mgmt-leaf') return l3 + return d.role !== 'router' && d.role !== 'mgmt-server' +} + +export function groupVarFiles({ plan, devices, addresses, out }: AnsibleContext): void { + const inv = out.inventory + const dep = plan.deployment + const file = (group: string, topic: string, entries: (YEntry | false)[], header?: string) => + out.add(`${inv}/group_vars/${group}/${topic}.yaml`, toYaml(ymap(...entries), header)) + + // Release vector (mirrors mini-lab's group_vars/all/release_vector.yaml). + { + const f = `${inv}/group_vars/all/release_vector.yaml` + out.add( + f, + toYaml( + ymap( + kv( + 'metal_stack_release_version', + dep.metalStackRelease || + out.todo( + f, + 'metal_stack_release_version', + 'metal-stack release to deploy (Ansible tab)', + ), + ), + kv('metal_stack_release_vectors', [ + ymap( + kv('url', 'oci://ghcr.io/metal-stack/releases:{{ metal_stack_release_version }}'), + kv('variable_mapping_path', 'metal_stack_release.mapping'), + kv('include_role_defaults', 'metal-roles/common/roles/defaults'), + ), + ]), + ), + 'Image names and tags of the roles come from the metal-stack release vector.\nAdd oci_cosign_verify_key to the vector entry to verify its signature.', + ), + ) + } + + // Settings shared by the whole partition inventory. + { + const f = `${inv}/group_vars/partition/switches.yaml` + const list = (key: string, values: string[], what: string) => + values.length ? values : [out.todo(f, key, `${what} (Ansible tab)`)] + file('partition', 'switches', [ + kv('sonic_config_mgmt_vrf', true), + kv('sonic_config_timezone', dep.timezone), + kv( + 'sonic_config_nameservers', + list('sonic_config_nameservers', dep.nameservers, 'name servers'), + ), + kv( + 'sonic_config_ntp', + ymap(kv('servers', list('sonic_config_ntp', dep.ntpServers, 'NTP servers'))), + ), + kv('sonic_config_ssh_sourceranges', dep.sshSourceRanges), + ]) + } + { + const f = `${inv}/group_vars/partition/metal.yaml` + file( + 'partition', + 'metal', + [ + kv('metal_partition_timezone', dep.timezone), + kv( + 'metal_partition_metal_api_addr', + out.todo(f, 'metal_partition_metal_api_addr', 'address of the control plane metal-api'), + ), + ], + 'Connection to the metal-stack control plane (partition/roles/defaults).', + ) + } + { + const f = `${inv}/group_vars/partition/secrets.yaml` + const secret = (key: string, what: string) => kv(key, out.todo(f, key, what)) + file( + 'partition', + 'secrets', + [ + secret('metal_partition_metal_api_hmac_edit_key', 'metal-api HMAC edit key'), + secret('metal_partition_metal_api_hmac_view_key', 'metal-api HMAC view key'), + secret('metal_partition_metal_api_grpc_ca_cert', 'metal-api gRPC CA certificate'), + secret('metal_partition_metal_api_grpc_client_cert', 'metal-api gRPC client certificate'), + secret('metal_partition_metal_api_grpc_client_key', 'metal-api gRPC client key'), + secret('metal_bmc_bmc_superuser', 'BMC superuser name'), + secret('metal_bmc_bmc_superuser_pwd', 'BMC superuser password'), + secret('metal_bmc_nsqd_ca_cert', 'NSQ CA certificate'), + secret('metal_bmc_nsqd_client_cert', 'NSQ client certificate'), + secret('metal_bmc_nsqd_client_cert_key', 'NSQ client key'), + secret('metal_bmc_console_ca_cert', 'console CA certificate'), + secret('metal_bmc_console_cert', 'console certificate'), + secret('metal_bmc_console_key', 'console key'), + secret('mgmt_server_metal_ssh_privkey', 'SSH private key of the metal admin user'), + secret('mgmt_server_metal_ssh_pubkey', 'SSH public key of the metal admin user'), + ], + 'Secrets: fill in and encrypt this file with ansible-vault before committing it.', + ) + } + + devices.forEach((p, i) => + partitionGroupVars( + p, + addresses[i], + plan.partitions[i].fabric.mgmt.layer === 'l3', + file, + out, + dep, + ), + ) +} + +function partitionGroupVars( + p: PartitionDevices, + addr: PartitionAddresses, + l3: boolean, + file: (group: string, topic: string, entries: (YEntry | false)[], header?: string) => void, + out: AnsibleContext['out'], + dep: AnsibleContext['plan']['deployment'], +): void { + const inv = out.inventory + const has = (role: Device['role']) => devicesOf(p, role).length > 0 + const central = addr.mgmtSubnets.find((s) => !s.rackId) + const gw = (s: { gateway: bigint } | undefined, f: string) => + s ? formatIp(4, s.gateway) : out.todo(f, 'metal_partition_mgmt_gateway', 'management gateway') + + { + const f = `${inv}/group_vars/${p.group}/partition.yaml` + file( + p.group, + 'partition', + [ + kv('metal_partition_id', p.slug), + kv( + 'metal_partition_mgmt_gateway', + gw(central, f), + l3 ? 'Gateway of the central management network.' : 'Gateway of the management network.', + ), + ], + p.partitionName, + ) + } + + const bgpPorts = (group: string, what: string): YEntry => { + const f = `${inv}/group_vars/${group}/sonic.yaml` + return kv('sonic_config_bgp_ports', [out.todo(f, 'sonic_config_bgp_ports', what)]) + } + + if (has('leaf')) { + const g = partitionGroup(p, 'leaf') + file(g, 'sonic', [ + kv( + 'lo', + '{{ sonic_config_loopback_address }}', + 'metal-core reads the loopback and the ASN as lo / asn.', + ), + kv('asn', '{{ sonic_config_asn }}'), + kv( + 'sonic_config_frr_render', + false, + 'metal-core writes frr.conf, which needs the split routing mode.', + ), + kv('sonic_config_docker_routing_config_mode', 'split'), + kv('sonic_config_frr_l2vpn_evpn', true), + bgpPorts(g, 'spine-facing ports of the leaves'), + note(PORTS_NOTE), + ]) + for (const rack of p.racks) { + if (!p.devices.some((d) => d.role === 'leaf' && d.rack?.rackId === rack.rackId)) continue + const rg = rackGroup(p, rack.tag) + const f = `${inv}/group_vars/${rg}/rack.yaml` + const rackSubnet = addr.mgmtSubnets.find((s) => s.rackId === rack.rackId) + file( + rg, + 'rack', + [ + kv('metal_core_rack_id', `${p.slug}-rack${String(rack.number).padStart(2, '0')}`), + kv('metal_core_spine_uplinks', [ + out.todo( + f, + 'metal_core_spine_uplinks', + `spine-facing ports of the leaves in ${rack.name}`, + ), + ]), + l3 && + kv( + 'metal_partition_mgmt_gateway', + gw(rackSubnet, f), + "The rack's management network, routed by its mgmt leaf.", + ), + ], + rack.name, + ) + } + } + + for (const role of ['spine', 'superspine', 'storage-leaf'] as const) { + if (!has(role)) continue + const g = partitionGroup(p, role) + file(g, 'sonic', [ + kv('sonic_config_frr_l2vpn_evpn', true), + bgpPorts( + g, + role === 'spine' + ? 'leaf- and exit-facing ports of the spines' + : `spine-facing ports of the ${role}s`, + ), + note(PORTS_NOTE), + ]) + } + if (has('exit')) { + const g = partitionGroup(p, 'exit') + file(g, 'sonic', [ + kv('sonic_config_frr_l2vpn_evpn', true), + kv( + 'sonic_config_vtep', + ymap(kv('enabled', true)), + 'Exits terminate the external networks as VTEPs.', + ), + bgpPorts(g, 'spine-facing ports of the exits'), + note( + "Upstream peerings to the internet routers (sonic_config_interconnects) are\nindividual; the transfer networks are listed in each exit's host_vars.", + ), + note(PORTS_NOTE), + ]) + } + for (const role of ['mgmt-spine', 'mgmt-leaf'] as const) { + if (!has(role)) continue + const g = partitionGroup(p, role) + file( + g, + 'sonic', + l3 + ? [ + kv('sonic_config_frr_l2vpn_evpn', false), + bgpPorts(g, `uplink ports of the ${role}s`), + note(PORTS_NOTE), + ] + : [ + kv('sonic_config_frr_render', false, 'L2 management network: switching only, no BGP.'), + note(PORTS_NOTE), + ], + ) + } + + if (has('mgmt-server')) { + const g = partitionGroup(p, 'mgmt-server') + const f = (topic: string) => `${inv}/group_vars/${g}/${topic}.yaml` + file(g, 'mgmt-server', [ + kv( + 'mgmt_server_spine_facing_interface', + out.todo( + f('mgmt-server'), + 'mgmt_server_spine_facing_interface', + 'NIC of the mgmt server towards the mgmt spines', + ), + ), + kv( + 'mgmt_server_firewall_facing_interface', + out.todo( + f('mgmt-server'), + 'mgmt_server_firewall_facing_interface', + 'NIC of the mgmt server towards the mgmt firewall', + ), + ), + dep.nameservers.length > 0 && kv('mgmt_server_nameservers', dep.nameservers), + ]) + const subnets: YValue[] = addr.mgmtSubnets + .filter((s) => s.dhcpRange) + .map((s) => + ymap( + kv('comment', `BMCs, ${s.scope}`), + kv('network', formatIp(4, s.cidr.addr)), + kv('netmask', netmask(s.cidr.prefix)), + kv( + 'range', + ymap( + kv('begin', formatIp(4, s.dhcpRange![0])), + kv('end', formatIp(4, s.dhcpRange![1])), + ), + ), + kv('options', [`routers ${formatIp(4, s.gateway)}`]), + ), + ) + file( + g, + 'dhcp', + [kv('dhcp_subnets', subnets)], + 'BMC addresses: the management subnets after the gateway and the switches.', + ) + file(g, 'metal-bmc', [ + kv('metal_bmc_allowed_cidrs', cidrs(addr.mgmtSubnets.map((s) => s.cidr))), + kv( + 'metal_bmc_nsqd_addr', + out.todo( + f('metal-bmc'), + 'metal_bmc_nsqd_addr', + 'NSQ daemon of the control plane (host:port)', + ), + ), + ]) + file(g, 'pixiecore', [ + kv( + 'pixiecore_api_host', + out.todo(f('pixiecore'), 'pixiecore_api_host', 'address pixiecore serves the PXE API on'), + ), + dep.nameservers.length > 0 && kv('pixiecore_dns_servers', dep.nameservers), + dep.ntpServers.length > 0 && kv('pixiecore_metal_hammer_ntp_servers', dep.ntpServers), + ]) + file(g, 'image-cache', [ + kv('image_cache_sync_metal_api_view_hmac', '{{ metal_partition_metal_api_hmac_view_key }}'), + ]) + } +} + +export function hostVarFiles({ plan, devices, addresses, out }: AnsibleContext): void { + devices.forEach((p, i) => { + const addr = addresses[i] + const l3 = plan.partitions[i].fabric.mgmt.layer === 'l3' + for (const d of p.devices) { + const f = `${out.inventory}/host_vars/${d.hostname}.yaml` + const a = addr.byHost.get(d.hostname)! + const transfers = addr.transfers.filter( + (t) => t.exit === d.hostname || t.router === d.hostname, + ) + const transferNote = + transfers.length > 0 && + note( + 'Transfer networks (router ↔ exit):\n' + + transfers + .map( + (t) => + ` ${formatCidr(t.cidr)} ${t.exit} ${t.exitIp}, ${t.router} ${t.routerIp} (link ${t.link})`, + ) + .join('\n'), + ) + const entries: (YEntry | false)[] = [] + + if (d.role === 'router') { + entries.push( + note('Internet router: no metal-roles role configures it; listed for reference.'), + transferNote, + ) + } else { + entries.push( + kv( + 'ansible_host', + a.mgmt?.ip ?? + out.todo(f, 'ansible_host', 'management address (the subnet is not placed)'), + ), + ) + if (d.role === 'mgmt-server') { + entries.push( + kv('mgmt_server_asn', d.asn ?? 0), + kv( + 'mgmt_server_router_id', + (l3 ? a.loopback : a.mgmt?.ip) ?? + out.todo(f, 'mgmt_server_router_id', 'router id of the mgmt server'), + ), + ) + } else { + if (isBgpSpeaker(d, l3)) { + entries.push( + kv('sonic_config_asn', d.asn ?? 0), + kv( + 'sonic_config_loopback_address', + a.loopback ?? + out.todo(f, 'sonic_config_loopback_address', 'loopback (the pool is not placed)'), + ), + ) + } + entries.push( + a.mgmt + ? kv( + 'sonic_config_mgmt_interface', + ymap( + kv('ip', `${a.mgmt.ip}/${a.mgmt.prefix}`), + kv('gateway_address', a.mgmt.gateway), + ), + ) + : note('No management address: the management subnet is not placed.'), + ) + } + if (d.role === 'leaf') { + entries.push( + kv( + 'metal_core_cidr', + a.pxe ?? out.todo(f, 'metal_core_cidr', 'PXE network of the leaf (see the IPs tab)'), + "The leaf's address in its own PXE network.", + ), + ) + } + if (d.role === 'exit' && a.pxe) { + entries.push( + kv( + 'sonic_config_vlans', + [ymap(kv('id', PXE_VLAN), kv('ip', a.pxe))], + 'PXE network of the exits.', + ), + ) + } + if (d.role === 'mgmt-leaf' && l3) { + const s = addr.mgmtSubnets.find((m) => m.rackId === d.rack?.rackId) + if (s) { + entries.push( + note( + `Routes ${formatCidr(s.cidr)} for ${d.rack!.name}: gateway ${formatIp(4, s.gateway)}` + + (s.dhcpRange + ? `, BMC range ${formatIp(4, s.dhcpRange[0])} to ${formatIp(4, s.dhcpRange[1])}.` + : '.'), + ), + ) + } + } + entries.push(transferNote) + } + out.add( + f, + toYaml(ymap(...entries.filter((e): e is YEntry => !!e)), `${d.hostname} (${d.role})`), + ) + } + }) +} diff --git a/src/derive/ansible/yaml.test.ts b/src/derive/ansible/yaml.test.ts new file mode 100644 index 0000000..9a9063f --- /dev/null +++ b/src/derive/ansible/yaml.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest' +import { parse } from 'yaml' +import { fromObject, kv, note, toYaml, toYamlSeq, ymap } from './yaml' + +describe('yaml writer', () => { + it('writes nested maps, sequences and comments', () => { + const text = toYaml( + ymap( + note('derived from the plan'), + kv('asn', 4200000001), + kv('lo', '{{ sonic_config_loopback_address }}'), + kv('mgmt', ymap(kv('ip', '10.0.0.2/24'), kv('gateway_address', '10.0.0.1'))), + kv('servers', ['192.0.2.1', '192.0.2.2']), + kv('vlans', [ymap(kv('id', 4000), kv('ip', '10.1.0.1/27'))]), + kv('empty', []), + kv('flag', 'yes', 'a string, not a boolean'), + ), + 'header', + ) + expect(text.startsWith('---\n# header\n# derived from the plan\nasn: 4200000001\n')).toBe(true) + expect(parse(text)).toEqual({ + asn: 4200000001, + lo: '{{ sonic_config_loopback_address }}', + mgmt: { ip: '10.0.0.2/24', gateway_address: '10.0.0.1' }, + servers: ['192.0.2.1', '192.0.2.2'], + vlans: [{ id: 4000, ip: '10.1.0.1/27' }], + empty: [], + flag: 'yes', + }) + }) + + it('writes bare keys for null values (inventory hosts)', () => { + const text = toYaml(ymap(kv('hosts', ymap(kv('leaf01', null), kv('leaf02', null))))) + expect(text).toContain(' leaf01:\n') + expect(parse(text)).toEqual({ hosts: { leaf01: null, leaf02: null } }) + }) + + it('writes sequence documents', () => { + const text = toYamlSeq([fromObject({ name: 'play', hosts: 'leaves', roles: ['a', 'b'] })]) + expect(parse(text)).toEqual([{ name: 'play', hosts: 'leaves', roles: ['a', 'b'] }]) + }) + + it('refuses multi-line strings', () => { + expect(() => toYaml(ymap(kv('a', 'x\ny')))).toThrow() + }) +}) diff --git a/src/derive/ansible/yaml.ts b/src/derive/ansible/yaml.ts new file mode 100644 index 0000000..06334ab --- /dev/null +++ b/src/derive/ansible/yaml.ts @@ -0,0 +1,97 @@ +import { stringify } from 'yaml' + +// A small block-style YAML writer for the Ansible export. The generated +// files carry comments (placeholders, derived facts worth knowing), which +// plain object serialization cannot express, so the generator builds an +// ordered tree of entries; scalars go through the `yaml` package so that +// quoting ("{{ jinja }}", "10.0.0.1/24", "yes") is always right. + +export type YScalar = string | number | boolean +export type YValue = YScalar | null | YValue[] | YMap + +export interface YEntry { + /** Comment lines above the entry; an entry may be a comment only. */ + comment?: string + key?: string + value?: YValue +} + +export class YMap { + constructor(readonly entries: YEntry[]) {} +} + +type EntryLike = YEntry | false | null | undefined + +export const ymap = (...entries: EntryLike[]): YMap => + new YMap(entries.filter((e): e is YEntry => !!e)) + +/** `key: value`; a null value writes a bare `key:` (an inventory host). */ +export const kv = (key: string, value: YValue, comment?: string): YEntry => ({ + key, + value, + comment, +}) + +export const note = (comment: string): YEntry => ({ comment }) + +/** An ordered map from a plain object (insertion order is kept). */ +export function fromObject(o: Record): YMap { + return new YMap(Object.entries(o).map(([key, value]) => ({ key, value }))) +} + +function scalar(v: YScalar): string { + if (typeof v === 'string' && v.includes('\n')) { + throw new Error('multi-line strings are not supported') + } + return stringify(v, { lineWidth: 0 }).trimEnd() +} + +function commentLines(comment: string | undefined, indent: string): string[] { + if (!comment) return [] + return comment.split('\n').map((line) => (line ? `${indent}# ${line}` : `${indent}#`)) +} + +function writeValue(key: string, value: YValue, indent: string): string[] { + const head = `${indent}${scalar(key)}:` + if (value === null) return [head] + if (Array.isArray(value)) { + return value.length === 0 ? [`${head} []`] : [head, ...writeSeq(value, `${indent} `)] + } + if (value instanceof YMap) { + return value.entries.length === 0 ? [`${head} {}`] : [head, ...writeMap(value, `${indent} `)] + } + return [`${head} ${scalar(value)}`] +} + +function writeMap(map: YMap, indent: string): string[] { + return map.entries.flatMap((e) => [ + ...commentLines(e.comment, indent), + ...(e.key === undefined ? [] : writeValue(e.key, e.value ?? null, indent)), + ]) +} + +function writeSeq(items: YValue[], indent: string): string[] { + return items.flatMap((item) => { + if (item instanceof YMap) { + if (item.entries.length === 0) return [`${indent}- {}`] + if (item.entries[0].comment || item.entries[0].key === undefined) { + throw new Error('a sequence map must start with a key') + } + const lines = writeMap(item, `${indent} `) + return [`${indent}- ${lines[0].trimStart()}`, ...lines.slice(1)] + } + if (Array.isArray(item)) throw new Error('nested sequences are not supported') + if (item === null) return [`${indent}-`] + return [`${indent}- ${scalar(item)}`] + }) +} + +/** A YAML document: `---`, optional header comment, then the map. */ +export function toYaml(doc: YMap, header?: string): string { + return ['---', ...commentLines(header, ''), ...writeMap(doc, '')].join('\n') + '\n' +} + +/** A top-level sequence document (playbooks). */ +export function toYamlSeq(items: YValue[], header?: string): string { + return ['---', ...commentLines(header, ''), ...writeSeq(items, '')].join('\n') + '\n' +} diff --git a/src/derive/devices.test.ts b/src/derive/devices.test.ts new file mode 100644 index 0000000..ec28ef8 --- /dev/null +++ b/src/derive/devices.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, it } from 'vitest' +import { createEmptyPlan, defaultPartition } from '../model/defaults' +import { templates } from '../model/templates' +import { deriveDevices, devicesOf, groupName, slugify } from './devices' + +describe('devices', () => { + it('names the default plan like a deployment repository', () => { + const [p] = deriveDevices(createEmptyPlan()) + expect(p.slug).toBe('partition-1') + expect(p.group).toBe('partition_1') + expect(p.devices.map((d) => d.hostname)).toEqual([ + 'partition-1-inet01', + 'partition-1-inet02', + 'partition-1-spine01', + 'partition-1-spine02', + 'partition-1-exit01', + 'partition-1-exit02', + 'partition-1-mgmtspine01', + 'partition-1-mgmtspine02', + 'partition-1-mgmtserver01', + 'partition-1-mgmtserver02', + 'partition-1-r01mgmtleaf', + 'partition-1-r01leaf01', + 'partition-1-r01leaf02', + ]) + }) + + it('numbers ASNs per the metal-stack model: leaves unique, spines and exits shared', () => { + const plan = createEmptyPlan() + plan.partitions.push(defaultPartition('Partition 2')) + const [p1, p2] = deriveDevices(plan) + const asns = (role: Parameters[1], p = p1) => + devicesOf(p, role).map((d) => d.asn) + expect(asns('leaf')).toEqual([4200000001, 4200000002]) + expect(asns('spine')).toEqual([4200020000, 4200020000]) + expect(asns('exit')).toEqual([4200030000, 4200030000]) + expect(asns('mgmt-spine')).toEqual([4200040000, 4200040000]) + expect(asns('mgmt-leaf')).toEqual([4200041001]) + expect(asns('mgmt-server')).toEqual([4200045001, 4200045002]) + expect(asns('router')).toEqual([null, null]) + expect(asns('leaf', p2)).toEqual([4200100001, 4200100002]) + }) + + it('counts every switch the BOM counts, with unique hostnames', () => { + for (const t of templates) { + const plan = t.build() + const devices = deriveDevices(plan) + const names = devices.flatMap((p) => p.devices.map((d) => d.hostname)) + expect(new Set(names).size).toBe(names.length) + plan.partitions.forEach((partition, i) => { + const leaves = partition.racks.reduce((n, r) => n + r.leafCount, 0) + expect(devicesOf(devices[i], 'leaf')).toHaveLength(leaves) + expect(devicesOf(devices[i], 'mgmt-leaf')).toHaveLength( + partition.racks.length * partition.fabric.mgmt.leafPerRack, + ) + }) + } + }) + + it('keeps a rack group as one rack', () => { + const plan = templates.find((t) => t.id === 'redundant')!.build() + const [p] = deriveDevices(plan) + expect(p.racks.map((r) => r.tag)).toEqual(['r01', 'r02']) + expect(devicesOf(p, 'leaf').map((d) => d.hostname)).toEqual([ + 'partition-1-r01leaf01', + 'partition-1-r01leaf02', + 'partition-1-r02leaf01', + 'partition-1-r02leaf02', + ]) + }) + + it('makes unique slugs and valid group names', () => { + expect(slugify(' Frankfurt / Room 2 ')).toBe('frankfurt-room-2') + expect(groupName('1st-floor')).toBe('p_1st_floor') + const plan = createEmptyPlan() + plan.partitions.push(defaultPartition('partition 1'), defaultPartition('***')) + expect(deriveDevices(plan).map((p) => p.slug)).toEqual([ + 'partition-1', + 'partition-1-2', + 'partition-3', + ]) + }) +}) diff --git a/src/derive/devices.ts b/src/derive/devices.ts new file mode 100644 index 0000000..11363b0 --- /dev/null +++ b/src/derive/devices.ts @@ -0,0 +1,204 @@ +import { mgmtDeviceCount, type Partition, type Plan } from '../model/plan' + +// Every named device of the plan: the single source of hostnames and ASNs +// for the Ansible export. Counts come from the same plan fields the BOM +// and the topology read; like them, this is derived, never stored. +// +// Hostnames follow the deployment repositories metal-stack operators keep +// (`-spine01`, `-r01leaf01`, `-r01mgmtleaf` +// …): the partition slug, then the device, with racks numbered in plan +// order (a rack group once, its leaves sit in the middle rack). +// +// ASNs follow the numbering model of the metal-stack network docs +// (docs.metal-stack.io, Concepts › Network › Theory, "ASN Numbering"): +// private 4-byte ASNs (RFC 6996), leaves unique, spines share one, exits +// share one. The same holds per tier here: superspines and mgmt spines +// share one, storage leaves, mgmt leaves and mgmt servers are unique. Each +// partition gets a block of 100000 above `Plan.deployment.asnBase`: +// +// leaves base + p·100000 + 1 + i +// storage leaves + 5001 + i +// superspines + 10000 +// spines + 20000 +// exits + 30000 +// mgmt spines + 40000 +// mgmt leaves + 41001 + i +// mgmt servers + 45001 + i +// +// Internet routers get no ASN: they peer with the provider, and no +// metal-roles role configures them. + +export type DeviceRole = + | 'router' + | 'superspine' + | 'spine' + | 'exit' + | 'storage-leaf' + | 'mgmt-spine' + | 'mgmt-server' + | 'mgmt-leaf' + | 'leaf' + +export interface Device { + hostname: string + role: DeviceRole + modelId: string + /** 1-based within its role (and rack, for rack devices). */ + index: number + asn: number | null + /** Set on leaves and mgmt leaves. */ + rack?: DeviceRack +} + +export interface DeviceRack { + rackId: string + /** Plan rack name (a rack group's name). */ + name: string + /** 1-based position in the partition's rack list. */ + number: number + /** "r01" */ + tag: string +} + +export interface PartitionDevices { + partitionId: string + partitionName: string + /** Hostname prefix and metal-stack partition id, e.g. "partition-1". */ + slug: string + /** Ansible group name prefix (slug with underscores). */ + group: string + racks: DeviceRack[] + devices: Device[] +} + +const PARTITION_STRIDE = 100000 +const ASN_OFFSET = { + leaf: 1, + 'storage-leaf': 5001, + superspine: 10000, + spine: 20000, + exit: 30000, + 'mgmt-spine': 40000, + 'mgmt-leaf': 41001, + 'mgmt-server': 45001, +} as const +/** Roles whose devices share one ASN per partition. */ +const SHARED_ASN = new Set(['superspine', 'spine', 'exit', 'mgmt-spine']) + +const pad = (n: number) => String(n).padStart(2, '0') + +/** Lowercase DNS label from a display name ("Partition 1" → "partition-1"). */ +export function slugify(name: string): string { + return name + .toLowerCase() + .normalize('NFKD') + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') +} + +/** Ansible group names take letters, digits and underscores only. */ +export function groupName(slug: string): string { + const g = slug.replace(/-/g, '_') + return /^[a-z_]/.test(g) ? g : `p_${g}` +} + +function uniqueSlugs(partitions: Partition[]): string[] { + const used = new Set() + return partitions.map((p, i) => { + const base = slugify(p.name) || `partition-${i + 1}` + let slug = base + for (let n = 2; used.has(slug); n++) slug = `${base}-${n}` + used.add(slug) + return slug + }) +} + +function partitionDevices( + partition: Partition, + partitionIndex: number, + slug: string, + asnBase: number, +): PartitionDevices { + const { fabric } = partition + const { mgmt } = fabric + const base = asnBase + partitionIndex * PARTITION_STRIDE + const devices: Device[] = [] + const counters = new Map() + + const asnFor = (role: DeviceRole, ordinal: number): number | null => { + if (role === 'router') return null + const offset = ASN_OFFSET[role] + return SHARED_ASN.has(role) ? base + offset : base + offset + ordinal + } + const add = ( + role: DeviceRole, + modelId: string, + name: string, + index: number, + rack?: DeviceRack, + ) => { + // Unique ASNs count across the partition (leaves of all racks). + const ordinal = counters.get(role) ?? 0 + counters.set(role, ordinal + 1) + devices.push({ + hostname: `${slug}-${name}`, + role, + modelId, + index, + asn: asnFor(role, ordinal), + rack, + }) + } + const tier = (role: DeviceRole, name: string, modelId: string, count: number) => { + for (let i = 1; i <= count; i++) add(role, modelId, `${name}${pad(i)}`, i) + } + + tier('router', 'inet', 'router-internet', fabric.routerCount) + if (fabric.fabricType === 'leaf-spine-superspine') { + tier('superspine', 'superspine', fabric.superspineModelId, fabric.superspineCount) + } + tier('spine', 'spine', fabric.spineModelId, fabric.spineCount) + tier('exit', 'exit', fabric.exitModelId, fabric.exitSwitchCount) + tier('storage-leaf', 'storageleaf', fabric.storageLeafModelId, fabric.storageLeafCount) + tier('mgmt-spine', 'mgmtspine', mgmt.spineModelId, mgmtDeviceCount(mgmt)) + tier('mgmt-server', 'mgmtserver', mgmt.serverModelId, mgmtDeviceCount(mgmt)) + + const racks = partition.racks.map((rack, i): DeviceRack => ({ + rackId: rack.id, + name: rack.name, + number: i + 1, + tag: `r${pad(i + 1)}`, + })) + partition.racks.forEach((rack, i) => { + const r = racks[i] + for (let m = 1; m <= mgmt.leafPerRack; m++) { + add( + 'mgmt-leaf', + mgmt.leafModelId, + `${r.tag}mgmtleaf${mgmt.leafPerRack > 1 ? pad(m) : ''}`, + m, + r, + ) + } + for (let l = 1; l <= rack.leafCount; l++) { + add('leaf', rack.leafModelId, `${r.tag}leaf${pad(l)}`, l, r) + } + }) + + return { + partitionId: partition.id, + partitionName: partition.name, + slug, + group: groupName(slug), + racks, + devices, + } +} + +export function deriveDevices(plan: Plan): PartitionDevices[] { + const slugs = uniqueSlugs(plan.partitions) + return plan.partitions.map((p, i) => partitionDevices(p, i, slugs[i], plan.deployment.asnBase)) +} + +export const devicesOf = (p: PartitionDevices, role: DeviceRole): Device[] => + p.devices.filter((d) => d.role === role) diff --git a/src/derive/ip/deviceAddresses.test.ts b/src/derive/ip/deviceAddresses.test.ts new file mode 100644 index 0000000..d42707a --- /dev/null +++ b/src/derive/ip/deviceAddresses.test.ts @@ -0,0 +1,81 @@ +import { describe, expect, it } from 'vitest' +import { createEmptyPlan } from '../../model/defaults' +import { templates } from '../../model/templates' +import { deriveDevices } from '../devices' +import { contains, formatCidr, parseIp, type Cidr } from './cidr' +import { deriveDeviceAddresses } from './deviceAddresses' +import { deriveIpPlan } from './ipPlan' + +function derive(plan = createEmptyPlan()) { + const devices = deriveDevices(plan) + const ip = deriveIpPlan(plan) + return { devices, ip, addresses: deriveDeviceAddresses(plan, ip, devices) } +} + +const inside = (cidr: Cidr, ip: string) => + contains(cidr, { family: 4, addr: parseIp(ip)!.addr, prefix: 32 }) + +describe('device addresses', () => { + it('addresses every device of the default plan', () => { + const { addresses, ip } = derive() + const [a] = addresses + expect(a.notes).toEqual([]) + const subnet = (kind: string, rackId?: string) => + ip.infra.partitions[0].subnets.find((s) => s.kind === kind && s.rackId === rackId)!.cidr! + const host = (name: string) => a.byHost.get(`partition-1-${name}`)! + expect(host('spine01').loopback).toBe('172.16.0.129') + expect(inside(subnet('underlay'), host('r01leaf02').loopback!)).toBe(true) + expect(inside(subnet('mgmt-loopbacks'), host('mgmtspine01').loopback!)).toBe(true) + // Central switches in the central management subnet, gateway first. + expect(host('spine01').mgmt).toEqual({ + ip: '172.16.0.162', + prefix: 27, + gateway: '172.16.0.161', + }) + // Rack switches in the rack's subnet. + expect(host('r01leaf01').mgmt!.gateway).toBe(host('r01mgmtleaf').mgmt!.gateway) + // PXE: 4 /27 slices of the /25, exits first, then one per leaf. + expect(formatCidr(a.pxe!)).toBe('172.16.0.0/25') + expect(host('exit01').pxe).toBe('172.16.0.1/27') + expect(host('exit02').pxe).toBe('172.16.0.2/27') + expect(host('r01leaf01').pxe).toBe('172.16.0.33/27') + expect(host('r01leaf02').pxe).toBe('172.16.0.65/27') + // 2 routers × 2 exits × 2 links. + expect(a.transfers).toHaveLength(8) + expect(a.transfers[0]).toMatchObject({ + exit: 'partition-1-exit01', + router: 'partition-1-inet01', + link: 1, + }) + }) + + it('never hands out an address twice and leaves room for BMCs', () => { + for (const t of templates) { + const { addresses } = derive(t.build()) + for (const a of addresses) { + expect(a.notes).toEqual([]) + const all = [...a.byHost.values()].flatMap((d) => [d.loopback, d.mgmt?.ip, d.pxe]) + const used = all.filter((x): x is string => !!x).map((x) => x.split('/')[0]) + expect(new Set(used).size).toBe(used.length) + for (const s of a.mgmtSubnets) expect(s.dhcpRange).not.toBeNull() + } + } + }) + + it('uses one management subnet and no management loopbacks for L2', () => { + const plan = createEmptyPlan() + plan.partitions[0].fabric.mgmt.layer = 'l2' + const [a] = derive(plan).addresses + expect(a.mgmtSubnets.map((s) => s.scope)).toEqual(['Partition']) + expect(a.byHost.get('partition-1-mgmtspine01')!.loopback).toBeNull() + expect(a.byHost.get('partition-1-r01leaf01')!.mgmt).not.toBeNull() + }) + + it('explains what cannot be addressed', () => { + const plan = createEmptyPlan() + plan.ipPlan.infra.cidr = 'nonsense' + const [a] = derive(plan).addresses + expect(a.notes[0]).toContain('no infrastructure block') + expect(a.byHost.get('partition-1-spine01')!.loopback).toBeNull() + }) +}) diff --git a/src/derive/ip/deviceAddresses.ts b/src/derive/ip/deviceAddresses.ts new file mode 100644 index 0000000..e79c0d1 --- /dev/null +++ b/src/derive/ip/deviceAddresses.ts @@ -0,0 +1,258 @@ +import type { Plan } from '../../model/plan' +import { devicesOf, type Device, type PartitionDevices } from '../devices' +import { rackNodes } from '../nodes' +import { formatCidr, formatIp, lastAddr, size, subnet, type Cidr } from './cidr' +import { pxeSliceCount, type InfraPartition, type InfraSubnet, type IpPlanResult } from './ipPlan' + +// Hands out single addresses from the infrastructure subnets that +// deriveIpPlan() sized and placed, one per device of derive/devices.ts. +// Only consumed by the Ansible export; the subnets themselves don't change. +// +// - Loopbacks: the underlay pool in order spines, superspines, exits, +// storage leaves, leaves; with an L3 management network the management +// loopback pool in order mgmt spines, mgmt leaves, mgmt servers. Pools +// start at their second address. +// - Management: in every management subnet the first host is the gateway, +// the switch management interfaces (and the mgmt servers' own +// interfaces, in the central or partition subnet) follow in device +// order, and the rest up to the broadcast address is the DHCP range for +// the BMCs. L2: one subnet for the partition; L3: the central subnet +// plus one per rack, whose gateway is the rack's mgmt leaf. +// - PXE: every leaf routes its own PXE network (the metal-core CIDR); the +// partition's PXE subnet is sized as equal slices for that (see +// ipPlan.ts): the first for the exit switch SVIs, then one per leaf in +// rack order, whose first host is the leaf's address. +// - Transfer networks: router r, exit e, link l (two per pair) take the +// subnet (r · exits + e) · 2 + l; the exit takes the first address, the +// router the second. +// Whatever does not fit is left null and explained in `notes`. + +export interface MgmtAddress { + ip: string + prefix: number + gateway: string +} + +export interface DeviceAddresses { + loopback: string | null + mgmt: MgmtAddress | null + /** Leaves: the metal-core CIDR ("10.1.2.1/27"); exits: the PXE SVI. */ + pxe: string | null +} + +export interface MgmtSubnet { + cidr: Cidr + gateway: bigint + /** "Central rack", "Partition" or the rack name. */ + scope: string + rackId?: string + /** First and last address handed out by DHCP (BMCs), if any are left. */ + dhcpRange: [bigint, bigint] | null +} + +export interface TransferLink { + exit: string + router: string + link: number + cidr: Cidr + exitIp: string + routerIp: string +} + +export interface PartitionAddresses { + partitionId: string + byHost: Map + mgmtSubnets: MgmtSubnet[] + pxe: Cidr | null + transfers: TransferLink[] + notes: string[] +} + +const EMPTY: DeviceAddresses = { loopback: null, mgmt: null, pxe: null } + +function partitionAddresses( + plan: Plan, + devices: PartitionDevices, + infra: InfraPartition | undefined, +): PartitionAddresses { + const partition = plan.partitions.find((p) => p.id === devices.partitionId)! + const byHost = new Map( + devices.devices.map((d) => [d.hostname, { ...EMPTY }]), + ) + const notes: string[] = [] + const name = devices.partitionName + const set = (d: Device, patch: Partial) => + byHost.set(d.hostname, { ...byHost.get(d.hostname)!, ...patch }) + const find = (kind: InfraSubnet['kind'], rackId?: string): Cidr | null => + infra?.subnets.find((s) => s.kind === kind && s.rackId === rackId)?.cidr ?? null + const role = (r: Device['role']) => devicesOf(devices, r) + const result: PartitionAddresses = { + partitionId: devices.partitionId, + byHost, + mgmtSubnets: [], + pxe: find('pxe'), + transfers: [], + notes, + } + if (!infra?.block) { + notes.push(`${name}: no infrastructure block, so no device addresses (see the IPs tab).`) + return result + } + + // Loopbacks. + const pool = (cidr: Cidr | null, label: string, list: Device[]) => { + if (list.length === 0) return + if (!cidr) { + notes.push(`${name}: the ${label} pool is not placed, loopbacks are left open.`) + return + } + list.forEach((d, i) => { + const addr = cidr.addr + 1n + BigInt(i) + if (addr <= lastAddr(cidr)) set(d, { loopback: formatIp(4, addr) }) + }) + if (BigInt(list.length) + 1n > size(cidr)) { + notes.push(`${name}: ${formatCidr(cidr)} is too small for ${list.length} ${label}.`) + } + } + pool(find('underlay'), 'underlay loopbacks', [ + ...role('spine'), + ...role('superspine'), + ...role('exit'), + ...role('storage-leaf'), + ...role('leaf'), + ]) + const l3 = partition.fabric.mgmt.layer === 'l3' + if (l3) { + pool(find('mgmt-loopbacks'), 'management loopbacks', [ + ...role('mgmt-spine'), + ...role('mgmt-leaf'), + ...role('mgmt-server'), + ]) + } + + // Management subnets. + const mgmtSubnet = (cidr: Cidr | null, scope: string, list: Device[], rackId?: string) => { + if (!cidr) { + if (list.length) notes.push(`${name}: the ${scope} management subnet is not placed.`) + return + } + const gateway = cidr.addr + 1n + const broadcast = lastAddr(cidr) + list.forEach((d, i) => { + const addr = gateway + 1n + BigInt(i) + if (addr < broadcast) { + set(d, { + mgmt: { ip: formatIp(4, addr), prefix: cidr.prefix, gateway: formatIp(4, gateway) }, + }) + } + }) + const first = gateway + 1n + BigInt(list.length) + const last = broadcast - 1n + if (first > last) + notes.push(`${name}: ${formatCidr(cidr)} (${scope}) has no room left for BMCs.`) + result.mgmtSubnets.push({ + cidr, + gateway, + scope, + rackId, + dhcpRange: first <= last ? [first, last] : null, + }) + } + const central = [ + ...role('superspine'), + ...role('spine'), + ...role('exit'), + ...role('storage-leaf'), + ...role('mgmt-spine'), + ...role('mgmt-server'), + ] + const rackDevices = (rackId: string) => + devices.devices.filter( + (d) => d.rack?.rackId === rackId && (d.role === 'mgmt-leaf' || d.role === 'leaf'), + ) + if (l3) { + mgmtSubnet(find('mgmt'), 'Central rack', central) + for (const rack of devices.racks) { + mgmtSubnet(find('mgmt', rack.rackId), rack.name, rackDevices(rack.rackId), rack.rackId) + } + } else { + mgmtSubnet(find('mgmt'), 'Partition', [ + ...central, + ...devices.racks.flatMap((r) => rackDevices(r.rackId)), + ]) + } + + // PXE: one slice for the exits, one per leaf. + const pxe = result.pxe + const leaves = role('leaf') + const exits = role('exit') + if (pxe && leaves.length + exits.length > 0) { + const slices = pxeSliceCount(leaves.length) + const slicePrefix = pxe.prefix + Math.log2(slices) + if (slicePrefix > 30) { + notes.push( + `${name}: ${formatCidr(pxe)} cannot be split into ${slices} PXE networks (one per leaf plus the exits); raise the headroom in the IPs tab.`, + ) + } else { + const exitSlice = subnet(pxe, slicePrefix, 0n) + exits.forEach((d, i) => { + const addr = exitSlice.addr + 1n + BigInt(i) + if (addr < lastAddr(exitSlice)) set(d, { pxe: `${formatIp(4, addr)}/${slicePrefix}` }) + }) + const hosts = size(exitSlice) - 3n + const reported = new Set() + leaves.forEach((d, i) => { + const slice = subnet(pxe, slicePrefix, BigInt(i + 1)) + set(d, { pxe: `${formatIp(4, slice.addr + 1n)}/${slicePrefix}` }) + const rack = partition.racks.find((r) => r.id === d.rack?.rackId) + const nodes = rack ? rackNodes(rack).total : 0 + if (rack && BigInt(nodes) > hosts && !reported.has(rack.id)) { + reported.add(rack.id) + notes.push( + `${name}: the PXE network per leaf (/${slicePrefix}, ${hosts} hosts) is too small for the ${nodes} nodes of ${rack.name}; raise the headroom in the IPs tab.`, + ) + } + }) + } + } else if (!pxe && leaves.length > 0) { + notes.push(`${name}: the PXE subnet is not placed, metal-core CIDRs are left open.`) + } + + // Transfer networks. + const transfer = find('transfer') + const prefix = plan.ipPlan.infra.transferPrefix + const routers = role('router') + if (transfer && prefix <= 31) { + routers.forEach((router, r) => + exits.forEach((exit, e) => { + for (let l = 0; l < 2; l++) { + const cidr = subnet(transfer, prefix, BigInt((r * exits.length + e) * 2 + l)) + const exitAddr = prefix === 31 ? cidr.addr : cidr.addr + 1n + result.transfers.push({ + exit: exit.hostname, + router: router.hostname, + link: l + 1, + cidr, + exitIp: formatIp(4, exitAddr), + routerIp: formatIp(4, exitAddr + 1n), + }) + } + }), + ) + } + return result +} + +export function deriveDeviceAddresses( + plan: Plan, + ip: IpPlanResult, + devices: PartitionDevices[], +): PartitionAddresses[] { + return devices.map((d) => + partitionAddresses( + plan, + d, + ip.infra.partitions.find((p) => p.partitionId === d.partitionId), + ), + ) +} diff --git a/src/derive/ip/ipPlan.test.ts b/src/derive/ip/ipPlan.test.ts index 1ef3a68..18862f9 100644 --- a/src/derive/ip/ipPlan.test.ts +++ b/src/derive/ip/ipPlan.test.ts @@ -138,11 +138,12 @@ describe('infrastructure ranges', () => { const byPurpose = Object.fromEntries(p.subnets.map((s) => [`${s.purpose}|${s.scope}`, s])) // 2 leaves + 2 spines + 2 exits + 4 firewalls = 10, doubled = 20 → /27 expect(byPurpose['Underlay loopbacks|Partition']).toMatchObject({ needed: 10, prefix: 27 }) - // 8 nodes + 2 exit SVIs = 10, doubled + 3 reserved = 23 → /27 + // One PXE network per leaf plus one for the exits: 4 slices, each for + // the rack's 8 nodes, doubled + 3 reserved = 19 → /27, so 4 × 32 → /25 expect(byPurpose['PXE (vlan4000)|Partition']).toMatchObject({ needed: 10, - sized: 23, - prefix: 27, + sized: 128, + prefix: 25, }) // L3 management: central 2 spines + 2 exits + 2 mgmt spines + 2 mgmt servers × 2 + 2 routers expect(byPurpose['Management|Central rack']).toMatchObject({ needed: 12, prefix: 27 }) @@ -150,9 +151,12 @@ describe('infrastructure ranges', () => { expect(byPurpose['Management|Rack 1']).toMatchObject({ needed: 11, prefix: 27 }) // 2 routers × 2 exits × 2 links × /30 = 32 addresses expect(byPurpose['Transfer networks|Partition']).toMatchObject({ needed: 8, prefix: 27 }) + // L3 management: 2 mgmt spines + 1 mgmt leaf + 2 mgmt servers, doubled → /28 + expect(byPurpose['Management loopbacks|Partition']).toMatchObject({ needed: 5, prefix: 28 }) expect(formatCidr(p.block!)).toBe('172.16.0.0/20') expect(p.overflow).toBe(false) - expect(p.requiredPrefix).toBe(24) + // 128 PXE + 4 × 32 + 16 = 272 → /23 + expect(p.requiredPrefix).toBe(23) expect(p.subnets.every((s) => s.cidr !== null)).toBe(true) }) @@ -163,13 +167,15 @@ describe('infrastructure ranges', () => { expect(p.subnets.filter((s) => s.purpose === 'Management').map((s) => s.scope)).toEqual([ 'Partition', ]) + expect(p.subnets.some((s) => s.kind === 'mgmt-loopbacks')).toBe(false) }) it('sizes the Redundant template per rack group and overflows a small block', () => { const plan = templates.find((t) => t.id === 'redundant')!.build() const p = deriveIpPlan(plan).infra.partitions[0] const pxe = p.subnets.find((s) => s.purpose === 'PXE (vlan4000)')! - expect(pxe).toMatchObject({ needed: 229, prefix: 23 }) + // 4 leaves + exits → 8 slices, each for the 115 nodes of rack group 1 → /24 + expect(pxe).toMatchObject({ needed: 229, prefix: 21 }) const racks = p.subnets.filter((s) => s.purpose === 'Management' && s.scope !== 'Central rack') expect(racks.map((s) => [s.scope, s.needed, s.prefix])).toEqual([ ['Rack group 1', 118, 24], @@ -178,7 +184,7 @@ describe('infrastructure ranges', () => { plan.ipPlan.infra.partitionPrefix = 23 const small = deriveIpPlan(plan).infra.partitions[0] expect(small.overflow).toBe(true) - // 512 PXE + 2 × 256 rack mgmt + 3 × 32 (underlay, central mgmt, transfer) = 1120 - expect(small.requiredPrefix).toBe(21) + // 2048 PXE + 2 × 256 rack mgmt + 3 × 32 (underlay, central mgmt, transfer) + 16 = 2672 + expect(small.requiredPrefix).toBe(20) }) }) diff --git a/src/derive/ip/ipPlan.ts b/src/derive/ip/ipPlan.ts index a864d0d..24ad82c 100644 --- a/src/derive/ip/ipPlan.ts +++ b/src/derive/ip/ipPlan.ts @@ -29,13 +29,18 @@ import { // - Underlay: one loopback per BGP speaker — used as router ID and VTEP // address — for leaves, spines, exits, superspines, storage leaves and // firewalls (EVPN-to-the-host VTEPs). -// - PXE (vlan4000): one address per server node plus one per exit switch, -// whose SVI runs the PXE DHCP server. +// - PXE (vlan4000): every leaf routes its own PXE network (its metal-core +// CIDR), and the exit switches' SVIs share one more. The subnet is a +// power of two of equal slices (at least leaves + 1), each sized for the +// largest rack's server nodes (or the exits) with headroom; the Ansible +// export hands the slices out in derive/ip/deviceAddresses.ts. // - Management (mgmt VRF / out-of-band): one per switch management // interface (production and management switches), one BMC per server // node, management server and internet router, and each management // server's own interface. L2 management: one subnet per partition; L3: // a central subnet plus one per compute rack (a rack group once). +// - Management loopbacks (L3 management only): one per BGP speaker of the +// management network, i.e. mgmt spines, mgmt leaves and mgmt servers. // - Transfer networks: one per router ↔ exit link (2 × routers × exits). // Host subnets reserve 3 addresses (network, broadcast, gateway), the // loopback pool none. Every size except the transfer networks gets the @@ -134,7 +139,14 @@ export interface FamilyResult { example: ExampleCluster | null } +/** What an infrastructure subnet is for, so consumers (the Ansible + * export) need not match display strings. */ +export type InfraSubnetKind = 'underlay' | 'pxe' | 'mgmt' | 'mgmt-loopbacks' | 'transfer' + export interface InfraSubnet { + kind: InfraSubnetKind + /** Set on the per-rack management subnets of an L3 management network. */ + rackId?: string purpose: string scope: string /** Devices / addresses needed today. */ @@ -424,6 +436,7 @@ function withHeadroom(needed: number, headroomPercent: number): number { } function hostSubnet( + kind: InfraSubnetKind, purpose: string, scope: string, needed: number, @@ -433,7 +446,47 @@ function hostSubnet( ): InfraSubnet | null { if (needed <= 0) return null const sized = withHeadroom(needed, infra.headroomPercent) + reserved - return { purpose, scope, needed, sized, prefix: fitPrefix(4, sized), cidr: null, detail } + return { kind, purpose, scope, needed, sized, prefix: fitPrefix(4, sized), cidr: null, detail } +} + +/** Number of equal PXE slices: one per leaf plus one for the exits. */ +export function pxeSliceCount(leaves: number): number { + let n = 1 + while (n < leaves + 1) n *= 2 + return n +} + +function pxeSubnet( + partition: Partition, + leaves: number, + nodes: number, + infra: IpInfra, +): InfraSubnet | null { + const exits = partition.fabric.exitSwitchCount + const needed = nodes + exits + if (needed <= 0) return null + const largestRack = Math.max( + 0, + ...partition.racks.filter((r) => r.leafCount > 0).map((r) => rackNodes(r).total), + ) + const slices = pxeSliceCount(leaves) + const slicePrefix = fitPrefix( + 4, + withHeadroom(Math.max(largestRack, exits), infra.headroomPercent) + HOST_RESERVED, + ) + const sized = slices * 2 ** (32 - slicePrefix) + return { + kind: 'pxe', + purpose: 'PXE (vlan4000)', + scope: 'Partition', + needed, + sized, + prefix: fitPrefix(4, sized), + cidr: null, + detail: + `${slices} × /${slicePrefix}, one per leaf (${leaves}) and one for the ${exits} exit SVIs, ` + + `each for up to ${largestRack} server nodes`, + } } /** Subnets a partition needs, in display order, not yet placed. */ @@ -450,6 +503,7 @@ export function partitionInfraNeeds(partition: Partition, infra: IpInfra): Infra leaves + fabric.spineCount + fabric.exitSwitchCount + superspines + fabric.storageLeafCount out.push( hostSubnet( + 'underlay', 'Underlay loopbacks', 'Partition', speakers + infra.firewallsPerPartition, @@ -461,15 +515,7 @@ export function partitionInfraNeeds(partition: Partition, infra: IpInfra): Infra 0, ), ) - out.push( - hostSubnet( - 'PXE (vlan4000)', - 'Partition', - nodes + fabric.exitSwitchCount, - `${nodes} server nodes + ${fabric.exitSwitchCount} exit SVIs`, - infra, - ), - ) + out.push(pxeSubnet(partition, leaves, nodes, infra)) const centralSwitches = fabric.spineCount + fabric.exitSwitchCount + superspines + fabric.storageLeafCount + mgmtCount @@ -480,6 +526,7 @@ export function partitionInfraNeeds(partition: Partition, infra: IpInfra): Infra if (fabric.mgmt.layer === 'l2') { out.push( hostSubnet( + 'mgmt', 'Management', 'Partition', centralSwitches + centralServers + leaves + mgmtLeaves + nodes, @@ -490,6 +537,7 @@ export function partitionInfraNeeds(partition: Partition, infra: IpInfra): Infra } else { out.push( hostSubnet( + 'mgmt', 'Management', 'Central rack', centralSwitches + centralServers, @@ -499,16 +547,27 @@ export function partitionInfraNeeds(partition: Partition, infra: IpInfra): Infra ) for (const rack of partition.racks) { const rackNodeCount = rackNodes(rack).total - out.push( - hostSubnet( - 'Management', - rack.name, - rack.leafCount + fabric.mgmt.leafPerRack + rackNodeCount, - `${rack.leafCount} leaves + ${fabric.mgmt.leafPerRack} mgmt leaf + ${rackNodeCount} BMCs`, - infra, - ), + const rackSubnet = hostSubnet( + 'mgmt', + 'Management', + rack.name, + rack.leafCount + fabric.mgmt.leafPerRack + rackNodeCount, + `${rack.leafCount} leaves + ${fabric.mgmt.leafPerRack} mgmt leaf + ${rackNodeCount} BMCs`, + infra, ) + out.push(rackSubnet && { ...rackSubnet, rackId: rack.id }) } + out.push( + hostSubnet( + 'mgmt-loopbacks', + 'Management loopbacks', + 'Partition', + 2 * mgmtCount + mgmtLeaves, + `${mgmtCount} mgmt spines + ${mgmtLeaves} mgmt leaves + ${mgmtCount} mgmt servers`, + infra, + 0, + ), + ) } const links = 2 * fabric.routerCount * fabric.exitSwitchCount @@ -516,6 +575,7 @@ export function partitionInfraNeeds(partition: Partition, infra: IpInfra): Infra const perLink = 2 ** (32 - infra.transferPrefix) const total = links * perLink out.push({ + kind: 'transfer', purpose: 'Transfer networks', scope: 'Partition', needed: links, diff --git a/src/derive/ip/validateIp.test.ts b/src/derive/ip/validateIp.test.ts index ec12293..01866b1 100644 --- a/src/derive/ip/validateIp.test.ts +++ b/src/derive/ip/validateIp.test.ts @@ -82,6 +82,6 @@ describe('validateIpPlan', () => { const small = createEmptyPlan() small.ipPlan.infra.partitionPrefix = 25 - expect(messages(small, 'error').some((m) => m.includes('needs a /24'))).toBe(true) + expect(messages(small, 'error').some((m) => m.includes('needs a /23'))).toBe(true) }) }) diff --git a/src/io/ansibleZip.test.ts b/src/io/ansibleZip.test.ts new file mode 100644 index 0000000..9e6c0bf --- /dev/null +++ b/src/io/ansibleZip.test.ts @@ -0,0 +1,21 @@ +import JSZip from 'jszip' +import { describe, expect, it } from 'vitest' +import { deriveAnsible } from '../derive/ansible' +import { createEmptyPlan } from '../model/defaults' +import { ansibleZip } from './ansibleZip' + +describe('Ansible zip', () => { + it('packs every file under one folder', async () => { + const { files } = deriveAnsible(createEmptyPlan()) + const bytes = await ansibleZip(files, 'plan-ansible') + const zip = await JSZip.loadAsync(bytes) + const names = Object.values(zip.files) + .filter((f) => !f.dir) + .map((f) => f.name) + expect(names.sort()).toEqual(files.map((f) => `plan-ansible/${f.path}`).sort()) + const inventory = files.find((f) => f.path.endsWith('inventory.yaml'))! + expect(await zip.file(`plan-ansible/${inventory.path}`)!.async('string')).toBe( + inventory.content, + ) + }) +}) diff --git a/src/io/ansibleZip.ts b/src/io/ansibleZip.ts new file mode 100644 index 0000000..13b2f93 --- /dev/null +++ b/src/io/ansibleZip.ts @@ -0,0 +1,13 @@ +import type { AnsibleFile } from '../derive/ansible' + +/** The Ansible export as a zip archive with every file under one folder. + * JSZip is loaded on demand, like exceljs for the xlsx export. */ +export async function ansibleZip(files: AnsibleFile[], folder: string): Promise { + const { default: JSZip } = await import('jszip') + const zip = new JSZip() + const root = zip.folder(folder)! + // A fixed date keeps the archive identical for an unchanged plan. + const date = new Date(Date.UTC(2020, 0, 1)) + for (const f of files) root.file(f.path, f.content, { date }) + return zip.generateAsync({ type: 'uint8array', compression: 'DEFLATE' }) +} diff --git a/src/model/ansibleRoles.ts b/src/model/ansibleRoles.ts new file mode 100644 index 0000000..3fe9335 --- /dev/null +++ b/src/model/ansibleRoles.ts @@ -0,0 +1,106 @@ +// Variables of the metal-stack Ansible roles the Ansible export may set, +// per source. Like the hardware catalog mirrors the official compatibility +// list, this mirrors the role READMEs and defaults of metal-stack/metal-roles +// (checked against v0.17.26, commit 4a9a436, 2026-09-18) plus the release +// vector variables of mini-lab's inventory. Update it from there, don't +// invent entries: ansible.test.ts fails on any generated variable that is +// not listed here. + +export const METAL_ROLES_REPO = 'https://github.com/metal-stack/metal-roles' +export const ANSIBLE_COMMON_REPO = 'https://github.com/metal-stack/ansible-common' + +export const ROLE_VARIABLES: Record = { + // Ansible connection variables. + ansible: ['ansible_host', 'ansible_user', 'ansible_become', 'ansible_python_interpreter'], + // Release vector mapping (common/roles/defaults, mini-lab group_vars/all). + 'common/defaults': ['metal_stack_release_version', 'metal_stack_release_vectors'], + // partition/README.md, partition/roles/defaults. + 'partition/defaults': [ + 'metal_partition_id', + 'metal_partition_mgmt_gateway', + 'metal_partition_timezone', + 'metal_partition_metal_api_addr', + 'metal_partition_metal_api_port', + 'metal_partition_metal_api_protocol', + 'metal_partition_metal_api_basepath', + 'metal_partition_metal_api_hmac_edit_key', + 'metal_partition_metal_api_hmac_view_key', + 'metal_partition_metal_api_grpc_address', + 'metal_partition_metal_api_grpc_cert_dir', + 'metal_partition_metal_api_grpc_ca_cert', + 'metal_partition_metal_api_grpc_client_cert', + 'metal_partition_metal_api_grpc_client_key', + ], + // partition/roles/sonic-config (replaces the deprecated sonic role). + 'sonic-config': [ + 'sonic_config_asn', + 'sonic_config_bgp_ports', + 'sonic_config_breakouts', + 'sonic_config_docker_routing_config_mode', + 'sonic_config_extended_cacl', + 'sonic_config_features', + 'sonic_config_frr_l2vpn_evpn', + 'sonic_config_frr_mgmt_framework_config', + 'sonic_config_frr_render', + 'sonic_config_frr_route_map', + 'sonic_config_frr_static_routes', + 'sonic_config_frr_static_routes_mgmt', + 'sonic_config_frr_syslog_level', + 'sonic_config_interconnects', + 'sonic_config_lldp_hello_timer', + 'sonic_config_loopback_address', + 'sonic_config_mclag', + 'sonic_config_mgmt_interface', + 'sonic_config_mgmt_vrf', + 'sonic_config_nameservers', + 'sonic_config_ntp', + 'sonic_config_portchannels', + 'sonic_config_ports', + 'sonic_config_reload_config', + 'sonic_config_sag', + 'sonic_config_ssh_sourceranges', + 'sonic_config_timezone', + 'sonic_config_vlan_subinterfaces', + 'sonic_config_vlans', + 'sonic_config_vtep', + ], + // partition/roles/metal-core; `lo` and `asn` are read by its env template. + 'metal-core': [ + 'lo', + 'asn', + 'metal_core_cidr', + 'metal_core_rack_id', + 'metal_core_spine_uplinks', + 'metal_core_log_level', + 'metal_core_pxe_vlan_id', + 'metal_core_additional_bridge_vids', + 'metal_core_additional_bridge_ports', + ], + 'mgmt-server': [ + 'mgmt_server_asn', + 'mgmt_server_router_id', + 'mgmt_server_spine_facing_interface', + 'mgmt_server_firewall_facing_interface', + 'mgmt_server_firewall_ip', + 'mgmt_server_nameservers', + 'mgmt_server_metal_ssh_privkey', + 'mgmt_server_metal_ssh_pubkey', + ], + dhcp: ['dhcp_subnets', 'dhcp_global_options', 'dhcp_static_hosts'], + pixiecore: ['pixiecore_api_host', 'pixiecore_dns_servers', 'pixiecore_metal_hammer_ntp_servers'], + 'metal-bmc': [ + 'metal_bmc_allowed_cidrs', + 'metal_bmc_bmc_superuser', + 'metal_bmc_bmc_superuser_pwd', + 'metal_bmc_nsqd_addr', + 'metal_bmc_nsqd_ca_cert', + 'metal_bmc_nsqd_client_cert', + 'metal_bmc_nsqd_client_cert_key', + 'metal_bmc_console_ca_cert', + 'metal_bmc_console_cert', + 'metal_bmc_console_key', + ], + 'image-cache': ['image_cache_sync_metal_api_endpoint', 'image_cache_sync_metal_api_view_hmac'], +} + +export const KNOWN_VARIABLES: ReadonlySet = new Set(Object.values(ROLE_VARIABLES).flat()) diff --git a/src/model/defaults.ts b/src/model/defaults.ts index 35bbd85..2a44cb8 100644 --- a/src/model/defaults.ts +++ b/src/model/defaults.ts @@ -1,3 +1,4 @@ +import { defaultDeployment } from './deployment' import { defaultIpPlan } from './ipPlan' import { SCHEMA_VERSION } from './migrate' import type { FabricConfig, Partition, Plan, Rack, RackDefaults } from './plan' @@ -145,6 +146,7 @@ export function createEmptyPlan(): Plan { partitions: [defaultPartition('Partition 1')], sparesPerLine: 2, ipPlan: defaultIpPlan(), + deployment: defaultDeployment(), externalNetworks: [ { id: id(), diff --git a/src/model/deployment.ts b/src/model/deployment.ts new file mode 100644 index 0000000..a09a51b --- /dev/null +++ b/src/model/deployment.ts @@ -0,0 +1,30 @@ +import { z } from 'zod' + +// Inputs of the Ansible export (Ansible tab) that the plan cannot derive. +// Everything else in the generated inventory (hostnames, ASNs, addresses, +// DHCP ranges) comes from the plan and the IP plan in src/derive/ansible/. +// +// Role versions default to the pins in metal-stack/mini-lab's +// requirements.yaml (checked 2026-09-18); the release vector version is +// left for the operator, the export marks it as a placeholder. + +export const DeploymentSchema = z.object({ + /** Inventory directory name (inventories//). */ + environment: z.string().default('prod'), + /** First ASN of the private 4-byte range (RFC 6996); see derive/devices.ts. */ + asnBase: z.number().int().min(4200000000).max(4294967294).default(4200000000), + timezone: z.string().default('Europe/Berlin'), + nameservers: z.array(z.string()).default([]), + ntpServers: z.array(z.string()).default([]), + /** Ranges the switches accept SSH from on their production addresses. */ + sshSourceRanges: z.array(z.string()).default([]), + /** metal-stack release (tag of github.com/metal-stack/releases). */ + metalStackRelease: z.string().default(''), + ansibleCommonVersion: z.string().default('v0.7.4'), + metalRolesVersion: z.string().default('v0.17.26'), +}) +export type Deployment = z.infer + +export function defaultDeployment(): Deployment { + return DeploymentSchema.parse({}) +} diff --git a/src/model/plan.ts b/src/model/plan.ts index dd85050..ab794db 100644 --- a/src/model/plan.ts +++ b/src/model/plan.ts @@ -1,4 +1,5 @@ import { z } from 'zod' +import { defaultDeployment, DeploymentSchema } from './deployment' import { defaultIpPlan, IpPlanSchema } from './ipPlan' import { SCHEMA_VERSION } from './migrate' @@ -165,5 +166,7 @@ export const PlanSchema = z.object({ sparesPerLine: z.number().int().min(0).default(2), /** IPv4/IPv6 address plan (IPs tab). */ ipPlan: IpPlanSchema.default(defaultIpPlan), + /** Settings of the Ansible export (Ansible tab). */ + deployment: DeploymentSchema.default(defaultDeployment), }) export type Plan = z.infer diff --git a/src/store/planStore.ts b/src/store/planStore.ts index 41beb26..71f0ded 100644 --- a/src/store/planStore.ts +++ b/src/store/planStore.ts @@ -3,6 +3,7 @@ import { useShallow } from 'zustand/react/shallow' import { persist } from 'zustand/middleware' import { temporal } from 'zundo' import { createEmptyPlan, defaultPartition, newRack, withRackKind } from '../model/defaults' +import type { Deployment } from '../model/deployment' import { ipPresets, type IpFamily, type IpFamilyKey, type IpInfra } from '../model/ipPlan' import { migrateRawPlan, SCHEMA_VERSION } from '../model/migrate' import { normalizePlan } from '../model/normalize' @@ -17,7 +18,7 @@ import { type TopologyVariant, } from '../model/plan' -export type View = 'plan' | 'topology' | 'racks' | 'ips' | 'bom' +export type View = 'plan' | 'topology' | 'racks' | 'ips' | 'bom' | 'ansible' interface PlannerState { plan: Plan @@ -30,6 +31,7 @@ interface PlannerState { patchIpInfra: (patch: Partial) => void setIpv6Enabled: (enabled: boolean) => void applyIpPreset: (presetId: string) => void + patchDeployment: (patch: Partial) => void addPartition: () => void removePartition: (partitionId: string) => void renamePartition: (partitionId: string, name: string) => void @@ -127,6 +129,10 @@ export const usePlanStore = create()( }), } }), + patchDeployment: (patch) => + set((s) => ({ + plan: touched(s.plan, { deployment: { ...s.plan.deployment, ...patch } }), + })), addPartition: () => set((s) => ({ plan: touched(s.plan, { diff --git a/src/views/AnsibleView.tsx b/src/views/AnsibleView.tsx new file mode 100644 index 0000000..b581144 --- /dev/null +++ b/src/views/AnsibleView.tsx @@ -0,0 +1,126 @@ +import { useMemo, useState } from 'react' +import { deriveAnsible, type Placeholder } from '../derive/ansible' +import { slugify } from '../derive/devices' +import { ansibleZip } from '../io/ansibleZip' +import { downloadBlob } from '../io/download' +import { usePlanStore } from '../store/planStore' +import { useToastStore } from '../store/toastStore' +import FileBrowser from './ansible/FileBrowser' +import { ANSIBLE_INFO } from './ansible/infos' +import SettingsCard from './ansible/SettingsCard' +import InfoBubble from './plan/InfoBubble' +import { navigateTo } from './plan/navigate' +import { ACTION_ICON, Icon, SECTION_ICON, SEVERITY_ICON } from './icons' + +/** Ansible tab: inventory, variables and playbooks for the metal-roles + * partition roles, derived in src/derive/ansible/, previewed per file and + * downloaded as a zip. */ +export default function AnsibleView() { + const plan = usePlanStore((s) => s.plan) + const notify = useToastStore((s) => s.notify) + const result = useMemo(() => deriveAnsible(plan), [plan]) + const [selected, setSelected] = useState('README.md') + const folder = `${slugify(plan.name) || 'metal-stack'}-ansible` + + const byFile = new Map() + for (const p of result.placeholders) byFile.set(p.file, [...(byFile.get(p.file) ?? []), p]) + + const download = async () => { + try { + const bytes = await ansibleZip(result.files, folder) + downloadBlob(new Blob([bytes as BlobPart], { type: 'application/zip' }), `${folder}.zip`) + } catch { + notify('Creating the zip failed.', { kind: 'error' }) + } + } + + return ( +
+
+

+ Ansible deployment + +

+ + {result.files.length} files, {result.placeholders.length} values to fill in + + +
+ + + +
+ + +
+ {result.notes.length > 0 && ( +
+
+

Addresses left open

+
+
    + {result.notes.map((n, i) => ( +
  • + +
  • + ))} +
+
+ )} + +
+
+

+ + To fill in{' '} + + · {result.placeholders.length} CHANGE_ME + + +

+
+
    + {[...byFile].map(([file, list]) => ( +
  • + +
  • + ))} +
+
+
+
+
+ ) +} diff --git a/src/views/ansible/FileBrowser.tsx b/src/views/ansible/FileBrowser.tsx new file mode 100644 index 0000000..bb9ed3a --- /dev/null +++ b/src/views/ansible/FileBrowser.tsx @@ -0,0 +1,157 @@ +import { Fragment } from 'react' +import { CHANGE_ME, type AnsibleFile } from '../../derive/ansible' +import { useToastStore } from '../../store/toastStore' +import { ACTION_ICON, FILE_ICON, Icon } from '../icons' + +interface Dir { + name: string + path: string + dirs: Dir[] + files: AnsibleFile[] +} + +/** Folder tree of the export; folders first, both in export order. */ +function buildTree(files: AnsibleFile[]): Dir { + const root: Dir = { name: '', path: '', dirs: [], files: [] } + for (const f of files) { + const parts = f.path.split('/') + let dir = root + for (const part of parts.slice(0, -1)) { + const path = dir.path ? `${dir.path}/${part}` : part + let next = dir.dirs.find((d) => d.name === part) + if (!next) { + next = { name: part, path, dirs: [], files: [] } + dir.dirs.push(next) + } + dir = next + } + dir.files.push(f) + } + return root +} + +const placeholderCount = (f: AnsibleFile) => + f.path.endsWith('.yaml') ? f.content.split(CHANGE_ME).length - 1 : 0 + +function DirView({ + dir, + depth, + selected, + onSelect, +}: { + dir: Dir + depth: number + selected: string + onSelect: (path: string) => void +}) { + const pad = { paddingLeft: `${0.5 + depth * 0.875}rem` } + return ( + <> + {dir.dirs.map((d) => ( +
+ + + {d.name} + + +
+ ))} + {dir.files.map((f) => { + const todo = placeholderCount(f) + return ( + + ) + })} + + ) +} + +/** One line of a file: comments dimmed, CHANGE_ME highlighted. */ +function Line({ text }: { text: string }) { + const comment = /^\s*#/.test(text) + const parts = text.split(CHANGE_ME) + return ( + + {parts.map((part, i) => ( + + {i > 0 && {CHANGE_ME}} + {part} + + ))} + {'\n'} + + ) +} + +export default function FileBrowser({ + files, + selected, + onSelect, +}: { + files: AnsibleFile[] + selected: string + onSelect: (path: string) => void +}) { + const notify = useToastStore((s) => s.notify) + const file = files.find((f) => f.path === selected) ?? files[0] + const tree = buildTree(files) + + return ( +
+ +
+
+ {file.path} + +
+
+          {file.content
+            .replace(/\n$/, '')
+            .split('\n')
+            .map((line, i) => (
+              
+            ))}
+        
+
+
+ ) +} diff --git a/src/views/ansible/SettingsCard.tsx b/src/views/ansible/SettingsCard.tsx new file mode 100644 index 0000000..3148be3 --- /dev/null +++ b/src/views/ansible/SettingsCard.tsx @@ -0,0 +1,143 @@ +import { useState } from 'react' +import type { Deployment } from '../../model/deployment' +import { usePlanStore } from '../../store/planStore' +import { NumberField } from '../plan/fields' +import { ANSIBLE_INFO } from './infos' +import { Icon, SECTION_ICON } from '../icons' + +function TextField({ + label, + value, + placeholder, + onChange, +}: { + label: string + value: string + placeholder?: string + onChange: (value: string) => void +}) { + return ( + + ) +} + +const parseList = (text: string) => text.split(/[\s,]+/).filter(Boolean) + +/** Comma or space separated list. The draft keeps what is typed (a + * trailing comma) and follows the plan when it changes elsewhere (undo). */ +function ListField({ + label, + values, + placeholder, + onChange, +}: { + label: string + values: string[] + placeholder?: string + onChange: (values: string[]) => void +}) { + const [draft, setDraft] = useState(values.join(', ')) + const shown = parseList(draft).join(',') === values.join(',') ? draft : values.join(', ') + return ( + { + setDraft(text) + onChange(parseList(text)) + }} + /> + ) +} + +/** Inputs of the export the plan cannot derive (Plan.deployment). */ +export default function SettingsCard({ deployment: d }: { deployment: Deployment }) { + const patch = usePlanStore((s) => s.patchDeployment) + return ( +
+

+ + Deployment settings{' '} + · what the plan cannot know +

+
+ patch({ environment })} + /> + patch({ metalStackRelease })} + /> + patch({ metalRolesVersion })} + /> + patch({ ansibleCommonVersion })} + /> + patch({ timezone })} + /> + { + if (asnBase <= 4294967294) patch({ asnBase }) + }} + /> +
+ patch({ nameservers })} + /> +
+
+ patch({ ntpServers })} + /> +
+
+ patch({ sshSourceRanges })} + /> +
+
+
+ ) +} diff --git a/src/views/ansible/infos.ts b/src/views/ansible/infos.ts new file mode 100644 index 0000000..ed8b32b --- /dev/null +++ b/src/views/ansible/infos.ts @@ -0,0 +1,15 @@ +import { DOCS, type Info } from '../plan/docs' + +export const ANSIBLE_INFO = { + tab: { + text: 'An Ansible inventory, group and host variables for the metal-roles partition roles (sonic-config on every switch, metal-core on the leaves, dhcp, metal-bmc, pixiecore and image-cache on the management servers) and the playbooks that apply them. Hostnames, ASNs, loopbacks, management addresses, PXE networks and DHCP ranges come from the plan and the IPs tab; secrets, endpoints and switch ports are left as CHANGE_ME.', + }, + asn: { + text: 'Private 4-byte ASNs (4200000000 to 4294967294). Leaves get unique ASNs, spines share one and exit switches share one, as the metal-stack network docs suggest against BGP path hunting. Each partition takes a block of 100000 above this number.', + href: DOCS.networking, + linkLabel: 'Networking', + }, + placeholders: { + text: 'Values the plan cannot know: secrets and certificates (group_vars/partition/secrets.yaml, encrypt it with ansible-vault), the control plane endpoints, NIC names of the management servers and the switch ports, which depend on the cabling.', + }, +} satisfies Record diff --git a/src/views/icons/index.tsx b/src/views/icons/index.tsx index f4ed1f2..7a997c2 100644 --- a/src/views/icons/index.tsx +++ b/src/views/icons/index.tsx @@ -17,6 +17,10 @@ import { Download, EthernetPort, ExternalLink, + FileCode, + FileText, + Folder, + Copy, Globe, HardDrive, Info, @@ -69,6 +73,7 @@ export const TAB_ICON: Record = { racks: Rack, ips: Binary, bom: ListChecks, + ansible: FileCode, } export const ACTION_ICON = { @@ -89,6 +94,7 @@ export const ACTION_ICON = { random: Dices, fit: Scan, fill: RefreshCw, + copy: Copy, } satisfies Record export const SECTION_ICON = { @@ -105,6 +111,14 @@ export const SECTION_ICON = { example: Boxes, infrastructure: Cable, bom: ListChecks, + deployment: SlidersHorizontal, + files: FileCode, + placeholders: PencilRuler, +} satisfies Record + +export const FILE_ICON = { + folder: Folder, + file: FileText, } satisfies Record export const SEVERITY_ICON = { diff --git a/src/views/ips/infos.ts b/src/views/ips/infos.ts index 406baeb..563c151 100644 --- a/src/views/ips/infos.ts +++ b/src/views/ips/infos.ts @@ -42,7 +42,7 @@ export const IP_INFO = { text: 'Unique local addresses (RFC 4193) for IPv6 pods and services. A random 40-bit global ID keeps the /48 unique; the presets encode metal-stack in fd8e:7a15:7ac6::/48.', }, infra: { - text: 'Per-partition ranges of the switch plane: underlay loopbacks (BGP router ID and VTEP address of every switch and firewall), the PXE VLAN whose DHCP server runs on the exit switches, the management network (mgmt VRF with every switch management interface and every BMC), and the transfer networks to the upstream routers.', + text: 'Per-partition ranges of the switch plane: underlay loopbacks (BGP router ID and VTEP address of every switch and firewall), the PXE VLAN (one network per leaf, its metal-core CIDR, plus one for the exit switch SVIs), the management network (mgmt VRF with every switch management interface and every BMC), the management loopbacks of an L3 management network, and the transfer networks to the upstream routers.', href: DOCS.networking, }, headroom: { From da9b40b37a893caa8a2c950780b38c9df44d470b Mon Sep 17 00:00:00 2001 From: Markus Fensterer Date: Fri, 18 Sep 2026 16:52:07 +0200 Subject: [PATCH 2/8] Generate CI/CD pipelines for the Ansible export The export now adds pipelines that check and deploy the playbooks: GitLab CI (.gitlab-ci.yml, the default) and GitHub Actions (check and deploy workflows), either or both. - Every push: inventory parse, --syntax-check of all playbooks, and a guard that fails while CHANGE_ME values are left in the inventory. - Deploys run by hand from the deploy branch, per partition, in the order mgmt servers, mgmt network, prod network, on a runner tagged per partition (only a runner inside it reaches the management network), one resource group / concurrency group per partition. An optional dry run shows the changes with --check --diff. - Credentials stay CI secrets; Ansible reads ANSIBLE_PRIVATE_KEY_FILE and ANSIBLE_VAULT_PASSWORD_FILE from the environment. The settings card shows what differs for every installation (release, metal-api address, name and NTP servers, pipeline platform) and keeps everything with a default in a folded Advanced section. The YAML writer gains literal blocks and YAML 1.1 quoting ("on", "yes"), as Ansible and GitHub parse it. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 +- README.md | 5 +- src/derive/ansible/ansible.test.ts | 5 +- src/derive/ansible/ci.test.ts | 105 +++++++++ src/derive/ansible/ci.ts | 362 +++++++++++++++++++++++++++++ src/derive/ansible/index.ts | 3 + src/derive/ansible/playbooks.ts | 2 + src/derive/ansible/vars.ts | 7 +- src/derive/ansible/yaml.test.ts | 15 +- src/derive/ansible/yaml.ts | 17 +- src/model/deployment.ts | 36 ++- src/views/AnsibleView.tsx | 9 +- src/views/ansible/FileBrowser.tsx | 23 +- src/views/ansible/SettingsCard.tsx | 222 ++++++++++++++---- src/views/ansible/infos.ts | 8 +- 15 files changed, 755 insertions(+), 66 deletions(-) create mode 100644 src/derive/ansible/ci.test.ts create mode 100644 src/derive/ansible/ci.ts diff --git a/CLAUDE.md b/CLAUDE.md index 1df3b4d..8abf98e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,7 +28,7 @@ A client-only single-page app (React + Vite + TypeScript) to plan a [metal-stack - **Rack defaults and power budget.** `Partition.rackDefaults` (`heightUnits` 42, `maxPowerWatts` 12000 — five 3U eight-node MicroCloud chassis at ~2 kW plus a leaf pair no longer fit in 10 kW) seed racks added to the partition and apply to the central rack; each rack keeps its own `heightUnits` / `maxPowerWatts` in its Advanced section, which also holds the rarely changed leaf model and leaf count and a rack group's member names. The central rack's Advanced section likewise holds the internet router and storage leaf counts, its hardware models (spine, superspine, storage leaf, mgmt spine/leaf/server) and mgmt leaves per rack, grouped with the network OS, fabric links and rack defaults; the section header shows the chosen models read-only. Issues that can only be fixed in either Advanced section carry `target.field: 'advanced'`, and `navigateTo()` unfolds that section (`details[data-advanced]`) when revealing them. Validation reports an error per physical rack whose estimated draw exceeds its budget. - **Info bubbles.** `views/plan/InfoBubble.tsx` renders the "i" bubbles; `SelectField` / `NumberField` take an `info` prop. Texts must match the metal-stack docs, and links come from `views/plan/docs.ts` — only pages verified to exist on docs.metal-stack.io. The partition's central components are edited in `CentralRackSection.tsx` (the UI says "Central rack", the schema still calls it `fabric`). - **IP plan.** Inputs live in `Plan.ipPlan` (`src/model/ipPlan.ts`: per-family schema, infrastructure inputs, presets). `src/derive/ip/cidr.ts` is the BigInt CIDR library (no dependency); `src/derive/ip/ipPlan.ts` derives limits (each with a formula string), the partition allocation (sequential, skipping slots that overlap Kubernetes, reserve, internet or infrastructure ranges — an internet range containing the whole project CIDR is by design), the example cluster and the infrastructure subnets; `src/derive/ip/validateIp.ts` reports issues with `target: { section: 'ips', field }`, which `navigateTo()` opens in the IPs tab. Infrastructure sizing rules are in the header of `ipPlan.ts`. Presets mirror the layouts of the untracked `ip_calc.ods`; their public ranges must be documentation ranges (RFC 5737 / RFC 3849), never real allocations. `ipPlanToCsv()` in `src/io/ipCsv.ts` exports the address plan. -- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`. +- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`, which follows YAML 1.1 quoting (`"on"`, `"yes"`), as Ansible and GitHub read it. `derive/ansible/ci.ts` adds the pipelines (`Plan.deployment.ci.platform`: GitLab CI by default, GitHub Actions, both or none): a check on every push (inventory, `--syntax-check`, no `CHANGE_ME` left in `inventories/`) and manual deploys per partition in `DEPLOY_STEPS` order (mgmt servers, mgmt network, prod network) on a runner tagged per partition, one resource group / concurrency group each. Credentials stay CI secrets that Ansible reads through `ANSIBLE_PRIVATE_KEY_FILE` / `ANSIBLE_VAULT_PASSWORD_FILE`. The settings card shows what differs per installation (release, metal-api address, name and NTP servers, pipeline platform) and keeps everything with a default in its Advanced section. - **Icons.** Icons come from `src/views/icons/` only: `index.tsx` is the registry (Lucide icons plus the custom `NetworkSwitch`, `Rack`, `RackGroup` glyphs from `custom.ts`, drawn on Lucide's 24 px grid) with typed maps per tab, node kind, BOM category, rack slot kind and severity, and an `` helper (decorative, `aria-hidden`). An ESLint rule forbids importing `lucide-react` anywhere else. Sizes: 16 px in tabs and buttons, 14 px inline and in topology nodes, 12 px in rack slots; color follows the text. - **Templates.** `src/model/templates.ts` holds the starting plans offered by the Templates menu. They must order only hardware that is current at the vendor — `templates.test.ts` fails on any `availability` of `eol` / `withdrawn`, for the templates and for the default plan. Each `build()` returns a fresh Plan with new ids; `templates.test.ts` asserts every template parses and has no validation errors, so keep them valid when capacity rules change. - **Persistence and format version.** Zustand store (`src/store/planStore.ts`) with `persist` middleware → localStorage key `metal-stack-planner/plan`. JSON export/import in `src/io/json.ts`; every import runs through `migrateRawPlan()` (`src/model/migrate.ts`) and is then validated with `PlanSchema.parse`, and the same path runs on rehydrate. `SCHEMA_VERSION` in `migrate.ts` is the plan file format version — currently 1 with no migrations. Purely additive fields only need a Zod `.default()`; anything that breaks older files bumps `SCHEMA_VERSION` and adds a function to `MIGRATIONS`, keyed by the version it upgrades _from_. Files from a newer version are refused with a readable message. diff --git a/README.md b/README.md index 40eac87..fc3ee8d 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,9 @@ consistent — you edit the plan, everything else follows from it. metal-core, mgmt-server, dhcp, metal-bmc, pixiecore, image-cache) and the playbooks that apply them, laid out like a metal-stack deployment repository. Hostnames, ASNs, loopbacks, management addresses, per-leaf PXE networks, DHCP ranges and transfer networks come from the plan and the - IP plan; secrets, endpoints and switch ports are marked `CHANGE_ME` and listed. Previewed file - by file and downloaded as a zip. + IP plan; secrets, endpoints and switch ports are marked `CHANGE_ME` and listed. A GitLab CI + pipeline (or GitHub Actions workflows) checks every push and deploys each partition by hand, + in order, on a runner inside the partition. Previewed file by file and downloaded as a zip. - **Prices** — an optional price book (kept in the browser, separate from the plan, importable and exportable as JSON) turns the BOM into a cost estimate with line totals, category subtotals and a grand total. diff --git a/src/derive/ansible/ansible.test.ts b/src/derive/ansible/ansible.test.ts index 954b55b..0465dd8 100644 --- a/src/derive/ansible/ansible.test.ts +++ b/src/derive/ansible/ansible.test.ts @@ -53,9 +53,10 @@ describe('Ansible export', () => { } }) - it('lists each CHANGE_ME as a placeholder', () => { + it('lists each CHANGE_ME of the inventory as a placeholder', () => { const { files, placeholders } = deriveAnsible(createEmptyPlan()) - for (const f of files.filter((f) => f.path.endsWith('.yaml'))) { + // The pipelines mention CHANGE_ME in their check, not as a value. + for (const f of files.filter((f) => f.path.startsWith('inventories/'))) { const marks = f.content.split(CHANGE_ME).length - 1 expect( placeholders.filter((p) => p.file === f.path), diff --git a/src/derive/ansible/ci.test.ts b/src/derive/ansible/ci.test.ts new file mode 100644 index 0000000..34073d2 --- /dev/null +++ b/src/derive/ansible/ci.test.ts @@ -0,0 +1,105 @@ +import { describe, expect, it } from 'vitest' +import { parse } from 'yaml' +import { createEmptyPlan, defaultPartition } from '../../model/defaults' +import type { Plan } from '../../model/plan' +import { deriveAnsible } from './index' + +function plan(ci: Partial = {}): Plan { + const p = createEmptyPlan() + p.partitions.push(defaultPartition('Partition 2')) + p.deployment.ci = { ...p.deployment.ci, ...ci } + return p +} + +const file = (p: Plan, path: string) => { + const f = deriveAnsible(p).files.find((f) => f.path === path) + return f && parse(f.content) +} + +describe('CI/CD pipelines', () => { + it('generates GitLab CI by default, GitHub Actions on request, nothing for none', () => { + const paths = (p: Plan) => deriveAnsible(p).files.map((f) => f.path) + const ciPaths = (p: Plan) => paths(p).filter((f) => /gitlab-ci|\.github/.test(f)) + expect(ciPaths(plan())).toEqual(['.gitlab-ci.yml']) + expect(ciPaths(plan({ platform: 'github' }))).toEqual([ + '.github/workflows/check.yaml', + '.github/workflows/deploy.yaml', + ]) + expect(ciPaths(plan({ platform: 'both' }))).toHaveLength(3) + expect(ciPaths(plan({ platform: 'none' }))).toEqual([]) + }) + + it('deploys each partition by hand, in order, on its own runner', () => { + const gl = file(plan({ runnerTags: { unknown: 'x' } }), '.gitlab-ci.yml') + expect(gl.stages).toEqual(['check', 'deploy']) + expect(gl.check.script.join('\n')).toContain('--syntax-check') + expect(gl['.deploy'].rules).toEqual([{ if: '$CI_COMMIT_BRANCH == "main"', when: 'manual' }]) + expect(gl['partition-2:1-mgmt-server']).toMatchObject({ + extends: '.deploy', + tags: ['partition-2'], + resource_group: 'partition-2', + environment: { name: 'prod/partition-2' }, + needs: ['check'], + script: ['ansible-playbook deploy_mgmt_server.yaml --limit partition_2'], + }) + expect(gl['partition-2:2-mgmt-network'].needs).toEqual(['partition-2:1-mgmt-server']) + expect(gl['partition-2:3-prod-network'].needs).toEqual(['partition-2:2-mgmt-network']) + expect(gl['partition-1:dry-run'].script[0]).toContain('--check --diff') + }) + + it('takes runner tags, branch, SSH user and the dry run from the settings', () => { + const p = plan({ + platform: 'both', + branch: 'production', + sshUser: 'deploy', + hostKeyChecking: false, + dryRun: false, + }) + p.deployment.ci.runnerTags = { [p.partitions[0].id]: 'dc1-mgmt' } + const gl = file(p, '.gitlab-ci.yml') + expect(gl.variables).toEqual({ + ANSIBLE_FORCE_COLOR: '1', + ANSIBLE_HOST_KEY_CHECKING: 'False', + ANSIBLE_REMOTE_USER: 'deploy', + }) + expect(gl['partition-1:1-mgmt-server'].tags).toEqual(['dc1-mgmt']) + expect(gl['.deploy'].rules[0].if).toBe('$CI_COMMIT_BRANCH == "production"') + expect(Object.keys(gl).some((k) => k.endsWith(':dry-run'))).toBe(false) + + const gh = file(p, '.github/workflows/deploy.yaml') + expect(Object.keys(gh.on.workflow_dispatch.inputs)).toEqual(['partition', 'playbook']) + expect(gh.on.workflow_dispatch.inputs.partition.options).toEqual(['partition-1', 'partition-2']) + expect(gh.jobs['partition-1']).toMatchObject({ + if: "inputs.partition == 'partition-1' && github.ref_name == 'production'", + 'runs-on': ['self-hosted', 'dc1-mgmt'], + environment: 'prod-partition-1', + }) + const steps = gh.jobs['partition-1'].steps.map((s: { name?: string }) => s.name) + expect(steps).toEqual([ + undefined, + 'Install credentials', + 'Install roles and collections', + 'Management servers', + 'Management network', + 'Production network', + ]) + expect(file(p, '.github/workflows/check.yaml').on.push.branches).toEqual(['production']) + }) + + it('offers a dry run input on GitHub that also works off the deploy branch', () => { + const gh = file(plan({ platform: 'github' }), '.github/workflows/deploy.yaml') + expect(gh.on.workflow_dispatch.inputs.dry_run).toMatchObject({ type: 'boolean', default: true }) + expect(gh.jobs['partition-1'].if).toContain("(inputs.dry_run || github.ref_name == 'main')") + expect(gh.jobs['partition-1'].steps[3].run).toContain("inputs.dry_run && '--check --diff'") + }) + + it('fills the metal-api address from the settings', () => { + const p = plan() + expect(deriveAnsible(p).placeholders.some((x) => x.key.endsWith('api_addr'))).toBe(true) + p.deployment.metalApiAddress = 'api.example.com' + expect(deriveAnsible(p).placeholders.some((x) => x.key.endsWith('api_addr'))).toBe(false) + expect( + file(p, 'inventories/prod/group_vars/partition/metal.yaml').metal_partition_metal_api_addr, + ).toBe('api.example.com') + }) +}) diff --git a/src/derive/ansible/ci.ts b/src/derive/ansible/ci.ts new file mode 100644 index 0000000..d7d93f0 --- /dev/null +++ b/src/derive/ansible/ci.ts @@ -0,0 +1,362 @@ +import { runnerTag, type Ci } from '../../model/deployment' +import { devicesOf, type DeviceRole, type PartitionDevices } from '../devices' +import type { AnsibleContext } from './index' +import { fromObject, kv, toYaml, ymap, type YEntry, type YMap, type YValue } from './yaml' + +// CI/CD pipelines that check and deploy the generated playbooks. GitLab is +// the first-class target (the layout of metal-stack deployment +// repositories: one image with Ansible, manual deploy jobs per partition +// in the order mgmt servers, mgmt network, prod network, one resource +// group per partition, runners tagged per partition because only a runner +// inside the partition reaches its management network); GitHub Actions +// gets the same as a check workflow and a dispatch-triggered deploy. +// +// Credentials never enter the repository: Ansible reads +// ANSIBLE_PRIVATE_KEY_FILE and ANSIBLE_VAULT_PASSWORD_FILE from the +// environment, which GitLab file variables and a GitHub step fill in. + +export interface DeployStep { + id: string + label: string + playbook: string + roles: DeviceRole[] +} + +/** Deploy order, as in the deployment repositories. */ +export const DEPLOY_STEPS: DeployStep[] = [ + { + id: 'mgmt-server', + label: 'Management servers', + playbook: 'deploy_mgmt_server.yaml', + roles: ['mgmt-server'], + }, + { + id: 'mgmt-network', + label: 'Management network', + playbook: 'deploy_mgmt_network.yaml', + roles: ['mgmt-spine', 'mgmt-leaf'], + }, + { + id: 'prod-network', + label: 'Production network', + playbook: 'deploy_prod_network.yaml', + roles: ['superspine', 'spine', 'exit', 'storage-leaf', 'leaf'], + }, +] + +/** Steps with devices in the partition. */ +export function stepsFor(p: PartitionDevices): DeployStep[] { + return DEPLOY_STEPS.filter((s) => s.roles.some((r) => devicesOf(p, r).length > 0)) +} + +const INSTALL = [ + 'ansible-galaxy role install -r requirements.yaml -p roles', + 'ansible-galaxy collection install -r requirements.yaml', +] + +const SYNTAX_CHECK = `ansible-inventory --graph > /dev/null +for playbook in deploy_*.yaml; do + ansible-playbook --syntax-check "$playbook" +done +` + +const NO_PLACEHOLDERS = `if grep -rn CHANGE_ME inventories; then + echo "Fill in the CHANGE_ME values above before deploying." + exit 1 +fi +` + +function ansibleEnv(ci: Ci): YEntry[] { + return [ + kv('ANSIBLE_FORCE_COLOR', '1'), + kv('ANSIBLE_HOST_KEY_CHECKING', ci.hostKeyChecking ? 'True' : 'False'), + ...(ci.sshUser.trim() ? [kv('ANSIBLE_REMOTE_USER', ci.sshUser.trim())] : []), + ] +} + +const HEADER = 'Generated by the metal-stack planner.' + +function gitlab({ plan, devices, out }: AnsibleContext): string { + const ci = plan.deployment.ci + const env = out.inventory.split('/')[1] + const branchRule = `$CI_COMMIT_BRANCH == "${ci.branch}"` + const jobs: YEntry[] = [] + for (const p of devices) { + const tag = runnerTag(ci, p.partitionId, p.slug) + const steps = stepsFor(p) + if (steps.length === 0) continue + const common = [kv('tags', [tag])] + if (ci.dryRun) { + jobs.push( + kv( + `${p.slug}:dry-run`, + ymap( + kv('extends', '.dry-run'), + ...common, + kv( + 'script', + steps.map((s) => `ansible-playbook ${s.playbook} --limit ${p.group} --check --diff`), + ), + ), + `${p.partitionName}: runners tagged "${tag}" must reach its management network.`, + ), + ) + } + steps.forEach((s, i) => { + const name = `${p.slug}:${i + 1}-${s.id}` + jobs.push( + kv( + name, + ymap( + kv('extends', '.deploy'), + ...common, + kv('resource_group', p.slug), + kv('environment', ymap(kv('name', `${env}/${p.slug}`))), + kv('needs', [i === 0 ? 'check' : `${p.slug}:${i}-${steps[i - 1].id}`]), + kv('script', [`ansible-playbook ${s.playbook} --limit ${p.group}`]), + ), + !ci.dryRun && i === 0 + ? `${p.partitionName}: runners tagged "${tag}" must reach its management network.` + : undefined, + ), + ) + }) + } + + return toYaml( + ymap( + kv('image', ci.image), + kv('stages', ['check', 'deploy']), + kv('variables', ymap(...ansibleEnv(ci))), + kv( + 'default', + ymap( + kv('before_script', [ + ...INSTALL, + `if [ -n "\${ANSIBLE_PRIVATE_KEY_FILE:-}" ]; then chmod 600 "$ANSIBLE_PRIVATE_KEY_FILE"; fi +if [ -n "\${SSH_KNOWN_HOSTS:-}" ]; then mkdir -p ~/.ssh && cp "$SSH_KNOWN_HOSTS" ~/.ssh/known_hosts; fi +`, + ]), + ), + ), + kv( + 'check', + ymap(kv('stage', 'check'), kv('script', [SYNTAX_CHECK, NO_PLACEHOLDERS])), + 'Inventory, playbook syntax and leftover placeholders, on every push.', + ), + kv( + '.deploy', + ymap( + kv('stage', 'deploy'), + kv('rules', [ymap(kv('if', branchRule), kv('when', 'manual'))]), + ), + `Deploys run by hand from ${ci.branch}, in order, one at a time per partition.`, + ), + ci.dryRun && + kv( + '.dry-run', + ymap( + kv('stage', 'deploy'), + kv('needs', ['check']), + kv('rules', [ymap(kv('when', 'manual'))]), + kv('allow_failure', true), + ), + 'Shows what a deploy would change (--check --diff), from any branch.', + ), + ...jobs, + ), + `${HEADER} +CI/CD variables (Settings > CI/CD > Variables), type File, protected: + ANSIBLE_PRIVATE_KEY_FILE SSH private key for all hosts + ANSIBLE_VAULT_PASSWORD_FILE password of the ansible-vault encrypted secrets + SSH_KNOWN_HOSTS known_hosts entries of all hosts`, + ) +} + +const expr = (e: string) => '${{ ' + e + ' }}' + +function githubCheck({ plan }: AnsibleContext): string { + const ci = plan.deployment.ci + return toYaml( + ymap( + kv('name', 'Check'), + kv('on', ymap(kv('push', ymap(kv('branches', [ci.branch]))), kv('pull_request', null))), + kv( + 'jobs', + ymap( + kv( + 'check', + ymap( + kv('runs-on', 'ubuntu-latest'), + kv('container', ci.image), + kv('env', ymap(kv('ANSIBLE_FORCE_COLOR', '1'))), + kv('steps', [ + fromObject({ uses: 'actions/checkout@v4' }), + fromObject({ + name: 'Install roles and collections', + run: INSTALL.join('\n') + '\n', + }), + fromObject({ name: 'Check inventory and playbooks', run: SYNTAX_CHECK }), + fromObject({ name: 'No placeholders left', run: NO_PLACEHOLDERS }), + ]), + ), + ), + ), + ), + ), + `${HEADER}\nInventory, playbook syntax and leftover placeholders, on every push.`, + ) +} + +const CREDENTIALS = `umask 077 +dir="$RUNNER_TEMP/ansible" +mkdir -p "$dir" ~/.ssh +printf '%s\\n' "$SSH_PRIVATE_KEY" > "$dir/id" +echo "ANSIBLE_PRIVATE_KEY_FILE=$dir/id" >> "$GITHUB_ENV" +if [ -n "$VAULT_PASSWORD" ]; then + printf '%s\\n' "$VAULT_PASSWORD" > "$dir/vault" + echo "ANSIBLE_VAULT_PASSWORD_FILE=$dir/vault" >> "$GITHUB_ENV" +fi +if [ -n "$KNOWN_HOSTS" ]; then + printf '%s\\n' "$KNOWN_HOSTS" > ~/.ssh/known_hosts +fi +` + +function githubDeploy({ plan, devices, out }: AnsibleContext): string { + const ci = plan.deployment.ci + const env = out.inventory.split('/')[1] + const partitions = devices.filter((p) => stepsFor(p).length > 0) + const inputs: YEntry[] = [ + kv( + 'partition', + ymap( + kv('description', 'Partition to deploy'), + kv('type', 'choice'), + kv('required', true), + kv( + 'options', + partitions.map((p) => p.slug), + ), + ), + ), + kv( + 'playbook', + ymap( + kv('description', 'Playbooks to run'), + kv('type', 'choice'), + kv('default', 'all'), + kv('options', ['all', ...DEPLOY_STEPS.map((s) => s.id)]), + ), + ), + ] + if (ci.dryRun) { + inputs.push( + kv( + 'dry_run', + ymap( + kv('description', 'Dry run only (--check --diff)'), + kv('type', 'boolean'), + kv('default', true), + ), + ), + ) + } + const onBranch = `github.ref_name == '${ci.branch}'` + const flags = ci.dryRun ? ` ${expr("inputs.dry_run && '--check --diff' || ''")}` : '' + + const job = (p: PartitionDevices): YMap => { + const tag = runnerTag(ci, p.partitionId, p.slug) + const allowed = ci.dryRun ? `(inputs.dry_run || ${onBranch})` : onBranch + return ymap( + kv('name', `Deploy ${p.slug}`), + kv('if', `inputs.partition == '${p.slug}' && ${allowed}`), + kv( + 'runs-on', + ['self-hosted', tag], + `Runners labeled "${tag}" must reach its management network.`, + ), + kv('environment', `${env}-${p.slug}`), + kv('concurrency', ymap(kv('group', `deploy-${p.slug}`), kv('cancel-in-progress', false))), + kv('container', ci.image), + kv('steps', [ + fromObject({ uses: 'actions/checkout@v4' }), + fromObject({ + name: 'Install credentials', + env: fromObject({ + SSH_PRIVATE_KEY: expr('secrets.ANSIBLE_SSH_PRIVATE_KEY'), + VAULT_PASSWORD: expr('secrets.ANSIBLE_VAULT_PASSWORD'), + KNOWN_HOSTS: expr('secrets.SSH_KNOWN_HOSTS'), + }), + run: CREDENTIALS, + }), + fromObject({ name: 'Install roles and collections', run: INSTALL.join('\n') + '\n' }), + ...stepsFor(p).map((s): YValue => + fromObject({ + name: s.label, + if: `inputs.playbook == 'all' || inputs.playbook == '${s.id}'`, + run: `ansible-playbook ${s.playbook} --limit ${p.group}${flags}`, + }), + ), + ]), + ) + } + + return toYaml( + ymap( + kv('name', 'Deploy'), + kv('on', ymap(kv('workflow_dispatch', ymap(kv('inputs', ymap(...inputs)))))), + kv('env', ymap(...ansibleEnv(ci))), + kv('jobs', ymap(...partitions.map((p) => kv(p.slug.replace(/[^a-z0-9_-]/g, '-'), job(p))))), + ), + `${HEADER} +Run from Actions > Deploy. Deploys run from ${ci.branch}${ci.dryRun ? ', dry runs from any branch' : ''}. +Secrets (per environment ${partitions.map((p) => `${env}-${p.slug}`).join(', ')}): + ANSIBLE_SSH_PRIVATE_KEY SSH private key for all hosts + ANSIBLE_VAULT_PASSWORD password of the ansible-vault encrypted secrets + SSH_KNOWN_HOSTS known_hosts entries of all hosts`, + ) +} + +/** Adds the pipeline files for the chosen platform(s). */ +export function ciFiles(ctx: AnsibleContext): void { + const { platform } = ctx.plan.deployment.ci + if (platform === 'gitlab' || platform === 'both') ctx.out.add('.gitlab-ci.yml', gitlab(ctx)) + if (platform === 'github' || platform === 'both') { + ctx.out.add('.github/workflows/check.yaml', githubCheck(ctx)) + ctx.out.add('.github/workflows/deploy.yaml', githubDeploy(ctx)) + } +} + +/** README section on the pipelines. */ +export function ciReadme({ plan, devices }: AnsibleContext): string[] { + const ci = plan.deployment.ci + if (ci.platform === 'none') return [] + const lines = ['## CI/CD', ''] + const runners = devices + .filter((p) => stepsFor(p).length > 0) + .map((p) => `\`${runnerTag(ci, p.partitionId, p.slug)}\` (${p.partitionName})`) + lines.push( + `Every push runs the check (inventory, playbook syntax, no \`CHANGE_ME\` left). Deploys are`, + `started by hand from \`${ci.branch}\` and run per partition in the order management servers,`, + 'management network, production network. They need a runner inside each partition, one that', + `reaches its management network, tagged ${runners.join(', ')}. Bootstrap the first`, + 'management server from a workstation, as its runner usually lives there.', + '', + ) + if (ci.platform !== 'github') { + lines.push( + '**GitLab** (`.gitlab-ci.yml`): add the CI/CD variables `ANSIBLE_PRIVATE_KEY_FILE`,', + '`ANSIBLE_VAULT_PASSWORD_FILE` and `SSH_KNOWN_HOSTS` as protected variables of type File.', + '', + ) + } + if (ci.platform !== 'gitlab') { + lines.push( + '**GitHub Actions** (`.github/workflows/`): add the secrets `ANSIBLE_SSH_PRIVATE_KEY`,', + '`ANSIBLE_VAULT_PASSWORD` and `SSH_KNOWN_HOSTS` to each deploy environment and register', + 'self-hosted runners with the labels above; run Actions > Deploy.', + '', + ) + } + return lines +} diff --git a/src/derive/ansible/index.ts b/src/derive/ansible/index.ts index 2b542b8..de8d29e 100644 --- a/src/derive/ansible/index.ts +++ b/src/derive/ansible/index.ts @@ -2,6 +2,7 @@ import type { Plan } from '../../model/plan' import { deriveDevices, slugify, type PartitionDevices } from '../devices' import { deriveDeviceAddresses, type PartitionAddresses } from '../ip/deviceAddresses' import { deriveIpPlan } from '../ip/ipPlan' +import { ciFiles } from './ci' import { inventoryFile } from './inventory' import { staticFiles, readme } from './playbooks' import { groupVarFiles, hostVarFiles } from './vars' @@ -11,6 +12,7 @@ import { groupVarFiles, hostVarFiles } from './vars' // like a metal-stack deployment repository: // // README.md, ansible.cfg, requirements.yaml, deploy_*.yaml +// .gitlab-ci.yml and/or .github/workflows/ (check and deploy pipelines) // inventories//inventory.yaml // inventories//group_vars//.yaml // inventories//host_vars/.yaml @@ -71,6 +73,7 @@ export function deriveAnsible(plan: Plan): AnsibleExport { const ctx = { plan, devices, addresses, out } staticFiles(ctx) + ciFiles(ctx) inventoryFile(ctx) groupVarFiles(ctx) hostVarFiles(ctx) diff --git a/src/derive/ansible/playbooks.ts b/src/derive/ansible/playbooks.ts index 62e814c..c48061a 100644 --- a/src/derive/ansible/playbooks.ts +++ b/src/derive/ansible/playbooks.ts @@ -1,5 +1,6 @@ import { ANSIBLE_COMMON_REPO, METAL_ROLES_REPO } from '../../model/ansibleRoles' import { formatCidr, formatIp } from '../ip/cidr' +import { ciReadme } from './ci' import type { AnsibleContext } from './index' import { fromObject, kv, toYaml, toYamlSeq, ymap, type YValue } from './yaml' @@ -137,6 +138,7 @@ export function readme({ plan, devices, addresses, out }: AnsibleContext, notes: 'Encrypt `group_vars/partition/secrets.yaml` with `ansible-vault` once filled in.', '', ] + lines.push(...ciReadme({ plan, devices, addresses, out })) if (out.placeholders.length > 0) { lines.push('## To fill in', '', row(['File', 'Variable', 'What']), row(['---', '---', '---'])) for (const p of out.placeholders) lines.push(row([`\`${p.file}\``, `\`${p.key}\``, p.reason])) diff --git a/src/derive/ansible/vars.ts b/src/derive/ansible/vars.ts index d06f3f3..607eb39 100644 --- a/src/derive/ansible/vars.ts +++ b/src/derive/ansible/vars.ts @@ -90,7 +90,12 @@ export function groupVarFiles({ plan, devices, addresses, out }: AnsibleContext) kv('metal_partition_timezone', dep.timezone), kv( 'metal_partition_metal_api_addr', - out.todo(f, 'metal_partition_metal_api_addr', 'address of the control plane metal-api'), + dep.metalApiAddress.trim() || + out.todo( + f, + 'metal_partition_metal_api_addr', + 'address of the control plane metal-api (Ansible tab)', + ), ), ], 'Connection to the metal-stack control plane (partition/roles/defaults).', diff --git a/src/derive/ansible/yaml.test.ts b/src/derive/ansible/yaml.test.ts index 9a9063f..9250d7e 100644 --- a/src/derive/ansible/yaml.test.ts +++ b/src/derive/ansible/yaml.test.ts @@ -29,6 +29,10 @@ describe('yaml writer', () => { }) }) + it('quotes YAML 1.1 booleans, which Ansible and GitHub read as such', () => { + expect(toYaml(ymap(kv('on', 'off'), kv('a', 'yes')))).toBe('---\n"on": "off"\na: "yes"\n') + }) + it('writes bare keys for null values (inventory hosts)', () => { const text = toYaml(ymap(kv('hosts', ymap(kv('leaf01', null), kv('leaf02', null))))) expect(text).toContain(' leaf01:\n') @@ -40,7 +44,14 @@ describe('yaml writer', () => { expect(parse(text)).toEqual([{ name: 'play', hosts: 'leaves', roles: ['a', 'b'] }]) }) - it('refuses multi-line strings', () => { - expect(() => toYaml(ymap(kv('a', 'x\ny')))).toThrow() + it('writes multi-line strings as literal blocks', () => { + const run = 'set -eu\nfor p in deploy_*.yaml; do\n ansible-playbook "$p"\ndone\n' + const text = toYaml(ymap(kv('run', run), kv('script', ['echo a', 'x\ny']))) + expect(text).toContain('run: |\n set -eu\n') + expect(parse(text)).toEqual({ run, script: ['echo a', 'x\ny\n'] }) + }) + + it('refuses multi-line keys', () => { + expect(() => toYaml(ymap(kv('a\nb', 1)))).toThrow() }) }) diff --git a/src/derive/ansible/yaml.ts b/src/derive/ansible/yaml.ts index 06334ab..e1c6855 100644 --- a/src/derive/ansible/yaml.ts +++ b/src/derive/ansible/yaml.ts @@ -41,11 +41,22 @@ export function fromObject(o: Record): YMap { function scalar(v: YScalar): string { if (typeof v === 'string' && v.includes('\n')) { - throw new Error('multi-line strings are not supported') + throw new Error('multi-line strings only go in block scalars') } - return stringify(v, { lineWidth: 0 }).trimEnd() + // YAML 1.1 rules, as Ansible and GitHub parse it: `on`, `yes`, `off` … + // are booleans there, so they get quoted. + return stringify(v, { lineWidth: 0, version: '1.1' }).trimEnd() } +/** A multi-line string as a literal block (`|`, keeping one final newline). */ +function block(v: string, indent: string): string[] { + const body = v.replace(/\n+$/, '') + if (/^\s/.test(body)) throw new Error('a block must not start with whitespace') + return body.split('\n').map((line) => (line ? `${indent}${line}` : '')) +} + +const isBlock = (v: YValue): v is string => typeof v === 'string' && v.includes('\n') + function commentLines(comment: string | undefined, indent: string): string[] { if (!comment) return [] return comment.split('\n').map((line) => (line ? `${indent}# ${line}` : `${indent}#`)) @@ -54,6 +65,7 @@ function commentLines(comment: string | undefined, indent: string): string[] { function writeValue(key: string, value: YValue, indent: string): string[] { const head = `${indent}${scalar(key)}:` if (value === null) return [head] + if (isBlock(value)) return [`${head} |`, ...block(value, `${indent} `)] if (Array.isArray(value)) { return value.length === 0 ? [`${head} []`] : [head, ...writeSeq(value, `${indent} `)] } @@ -82,6 +94,7 @@ function writeSeq(items: YValue[], indent: string): string[] { } if (Array.isArray(item)) throw new Error('nested sequences are not supported') if (item === null) return [`${indent}-`] + if (isBlock(item)) return [`${indent}- |`, ...block(item, `${indent} `)] return [`${indent}- ${scalar(item)}`] }) } diff --git a/src/model/deployment.ts b/src/model/deployment.ts index a09a51b..1f53cfd 100644 --- a/src/model/deployment.ts +++ b/src/model/deployment.ts @@ -5,8 +5,32 @@ import { z } from 'zod' // DHCP ranges) comes from the plan and the IP plan in src/derive/ansible/. // // Role versions default to the pins in metal-stack/mini-lab's -// requirements.yaml (checked 2026-09-18); the release vector version is -// left for the operator, the export marks it as a placeholder. +// requirements.yaml, the CI image to the one metal-stack deployment +// repositories use (checked 2026-09-18); the release vector version and +// the metal-api address are left for the operator, the export marks them +// as placeholders. + +export const CiPlatformSchema = z.enum(['gitlab', 'github', 'both', 'none']) +export type CiPlatform = z.infer + +/** CI/CD pipelines that check and deploy the playbooks (derive/ansible/ci.ts). */ +export const CiSchema = z.object({ + platform: CiPlatformSchema.default('gitlab'), + /** Container image with Ansible, used by every job. */ + image: z.string().default('ghcr.io/metal-stack/metal-deployment-base:v0.7.7'), + /** Deploys run from this branch only. */ + branch: z.string().default('main'), + /** Runner tag (GitLab) / label (GitHub) per partition id; empty uses the + * partition's slug. Runners must reach the partition's management network. */ + runnerTags: z.record(z.string(), z.string()).default({}), + /** SSH user for all hosts (ANSIBLE_REMOTE_USER); empty keeps Ansible's default. */ + sshUser: z.string().default(''), + /** Verify SSH host keys (SSH_KNOWN_HOSTS then holds them). */ + hostKeyChecking: z.boolean().default(true), + /** Offer a dry run (--check --diff) before deploying. */ + dryRun: z.boolean().default(true), +}) +export type Ci = z.infer export const DeploymentSchema = z.object({ /** Inventory directory name (inventories//). */ @@ -20,11 +44,19 @@ export const DeploymentSchema = z.object({ sshSourceRanges: z.array(z.string()).default([]), /** metal-stack release (tag of github.com/metal-stack/releases). */ metalStackRelease: z.string().default(''), + /** Address of the control plane's metal-api (metal_partition_metal_api_addr). */ + metalApiAddress: z.string().default(''), ansibleCommonVersion: z.string().default('v0.7.4'), metalRolesVersion: z.string().default('v0.17.26'), + ci: CiSchema.default(() => CiSchema.parse({})), }) export type Deployment = z.infer export function defaultDeployment(): Deployment { return DeploymentSchema.parse({}) } + +/** Runner tag of a partition: the override, else its slug. */ +export function runnerTag(ci: Ci, partitionId: string, slug: string): string { + return ci.runnerTags[partitionId]?.trim() || slug +} diff --git a/src/views/AnsibleView.tsx b/src/views/AnsibleView.tsx index b581144..417140a 100644 --- a/src/views/AnsibleView.tsx +++ b/src/views/AnsibleView.tsx @@ -50,10 +50,15 @@ export default function AnsibleView() { - +
- +
{result.notes.length > 0 && ( diff --git a/src/views/ansible/FileBrowser.tsx b/src/views/ansible/FileBrowser.tsx index bb9ed3a..34ebdf3 100644 --- a/src/views/ansible/FileBrowser.tsx +++ b/src/views/ansible/FileBrowser.tsx @@ -1,5 +1,5 @@ import { Fragment } from 'react' -import { CHANGE_ME, type AnsibleFile } from '../../derive/ansible' +import { CHANGE_ME, type AnsibleFile, type Placeholder } from '../../derive/ansible' import { useToastStore } from '../../store/toastStore' import { ACTION_ICON, FILE_ICON, Icon } from '../icons' @@ -30,18 +30,17 @@ function buildTree(files: AnsibleFile[]): Dir { return root } -const placeholderCount = (f: AnsibleFile) => - f.path.endsWith('.yaml') ? f.content.split(CHANGE_ME).length - 1 : 0 - function DirView({ dir, depth, selected, + todos, onSelect, }: { dir: Dir depth: number selected: string + todos: Map onSelect: (path: string) => void }) { const pad = { paddingLeft: `${0.5 + depth * 0.875}rem` } @@ -56,11 +55,17 @@ function DirView({ {d.name} - + ))} {dir.files.map((f) => { - const todo = placeholderCount(f) + const todo = todos.get(f.path) ?? 0 return (
diff --git a/src/views/ansible/infos.ts b/src/views/ansible/infos.ts index dbc96da..7b6f45d 100644 --- a/src/views/ansible/infos.ts +++ b/src/views/ansible/infos.ts @@ -9,6 +9,9 @@ export const ANSIBLE_INFO = { href: DOCS.networking, linkLabel: 'Networking', }, + controlPlane: { + text: 'DNS name of the control plane ingress (metal_control_plane_ingress_dns). The partition reaches metal-api at api. and NSQ at :4150, as the control plane roles serve them by default.', + }, ci: { text: 'Pipelines that check every push (inventory, playbook syntax, no CHANGE_ME left) and deploy per partition by hand, in the order management servers, management network, production network. GitLab CI gets one .gitlab-ci.yml, GitHub Actions a check and a deploy workflow. SSH key, vault password and known hosts are CI secrets, never files in the repository.', }, From 41c62ed6d6f3e6ee362fb18e7b34945be486ecfc Mon Sep 17 00:00:00 2001 From: Markus Fensterer Date: Fri, 18 Sep 2026 17:09:56 +0200 Subject: [PATCH 5/8] Highlight YAML and render markdown in the Ansible file preview YAML files show keys, strings, numbers, booleans, Jinja expressions, comments and CHANGE_ME in color (block scalars stay plain script text), ansible.cfg gets INI highlighting, and the README renders headings, tables, lists and code blocks, with a toggle to its source. The tokenizers (views/ansible/syntax.ts) cover what the export writes and render as React elements, never as HTML strings, so names from the plan cannot inject markup. Syntax colors are theme tokens. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 +- src/index.css | 7 + src/views/ansible/FileBrowser.tsx | 51 ++++---- src/views/ansible/Preview.tsx | 168 ++++++++++++++++++++++++ src/views/ansible/syntax.test.ts | 119 +++++++++++++++++ src/views/ansible/syntax.ts | 210 ++++++++++++++++++++++++++++++ 6 files changed, 528 insertions(+), 29 deletions(-) create mode 100644 src/views/ansible/Preview.tsx create mode 100644 src/views/ansible/syntax.test.ts create mode 100644 src/views/ansible/syntax.ts diff --git a/CLAUDE.md b/CLAUDE.md index a85d689..d726f3f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,7 +28,7 @@ A client-only single-page app (React + Vite + TypeScript) to plan a [metal-stack - **Rack defaults and power budget.** `Partition.rackDefaults` (`heightUnits` 42, `maxPowerWatts` 12000 — five 3U eight-node MicroCloud chassis at ~2 kW plus a leaf pair no longer fit in 10 kW) seed racks added to the partition and apply to the central rack; each rack keeps its own `heightUnits` / `maxPowerWatts` in its Advanced section, which also holds the rarely changed leaf model and leaf count and a rack group's member names. The central rack's Advanced section likewise holds the internet router and storage leaf counts, its hardware models (spine, superspine, storage leaf, mgmt spine/leaf/server) and mgmt leaves per rack, grouped with the network OS, fabric links and rack defaults; the section header shows the chosen models read-only. Issues that can only be fixed in either Advanced section carry `target.field: 'advanced'`, and `navigateTo()` unfolds that section (`details[data-advanced]`) when revealing them. Validation reports an error per physical rack whose estimated draw exceeds its budget. - **Info bubbles.** `views/plan/InfoBubble.tsx` renders the "i" bubbles; `SelectField` / `NumberField` take an `info` prop. Texts must match the metal-stack docs, and links come from `views/plan/docs.ts` — only pages verified to exist on docs.metal-stack.io. The partition's central components are edited in `CentralRackSection.tsx` (the UI says "Central rack", the schema still calls it `fabric`). - **IP plan.** Inputs live in `Plan.ipPlan` (`src/model/ipPlan.ts`: per-family schema, infrastructure inputs, presets). `src/derive/ip/cidr.ts` is the BigInt CIDR library (no dependency); `src/derive/ip/ipPlan.ts` derives limits (each with a formula string), the partition allocation (sequential, skipping slots that overlap Kubernetes, reserve, internet or infrastructure ranges — an internet range containing the whole project CIDR is by design), the example cluster and the infrastructure subnets; `src/derive/ip/validateIp.ts` reports issues with `target: { section: 'ips', field }`, which `navigateTo()` opens in the IPs tab. Infrastructure sizing rules are in the header of `ipPlan.ts`. Presets mirror the layouts of the untracked `ip_calc.ods`; their public ranges must be documentation ranges (RFC 5737 / RFC 3849), never real allocations. `ipPlanToCsv()` in `src/io/ipCsv.ts` exports the address plan. -- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. `src/derive/ports.ts` lays out each switch's BGP ports (uplinks on the last ports of their speed, downlinks from the first, in plan order) from the catalog's SONiC port maps (`ports[].sonic`, from sonic-buildimage's `port_config.ini`; only add maps from there, `sonicPortNames()` returns null otherwise). metal-api and NSQ are addressed under `metal_control_plane_ingress_dns` (`api.`, `:4150`, the control plane role defaults), the pixiecore API at the mgmt server's router ID, and the BMC superuser follows go-hal's per-vendor name (`BMC_SUPERUSER` in the catalog). What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`, which follows YAML 1.1 quoting (`"on"`, `"yes"`), as Ansible and GitHub read it. `derive/ansible/ci.ts` adds the pipelines (`Plan.deployment.ci.platform`: GitLab CI by default, GitHub Actions, both or none): a check on every push (inventory, `--syntax-check`, no `CHANGE_ME` left in `inventories/`) and manual deploys per partition in `DEPLOY_STEPS` order (mgmt servers, mgmt network, prod network) on a runner tagged per partition, one resource group / concurrency group each. Credentials stay CI secrets that Ansible reads through `ANSIBLE_PRIVATE_KEY_FILE` / `ANSIBLE_VAULT_PASSWORD_FILE`. The settings card shows only what differs per installation and has no default (release, control plane domain); everything with a default (public name and NTP servers as in mini-lab / metal-roles, inventory, role versions, the pipeline platform and its settings) sits in its Advanced section. +- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews (YAML/INI highlighting and README rendering from the tokenizers in `views/ansible/syntax.ts`, rendered as React elements, never as HTML strings; colors are the `--color-code-*` theme tokens) and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. `src/derive/ports.ts` lays out each switch's BGP ports (uplinks on the last ports of their speed, downlinks from the first, in plan order) from the catalog's SONiC port maps (`ports[].sonic`, from sonic-buildimage's `port_config.ini`; only add maps from there, `sonicPortNames()` returns null otherwise). metal-api and NSQ are addressed under `metal_control_plane_ingress_dns` (`api.`, `:4150`, the control plane role defaults), the pixiecore API at the mgmt server's router ID, and the BMC superuser follows go-hal's per-vendor name (`BMC_SUPERUSER` in the catalog). What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`, which follows YAML 1.1 quoting (`"on"`, `"yes"`), as Ansible and GitHub read it. `derive/ansible/ci.ts` adds the pipelines (`Plan.deployment.ci.platform`: GitLab CI by default, GitHub Actions, both or none): a check on every push (inventory, `--syntax-check`, no `CHANGE_ME` left in `inventories/`) and manual deploys per partition in `DEPLOY_STEPS` order (mgmt servers, mgmt network, prod network) on a runner tagged per partition, one resource group / concurrency group each. Credentials stay CI secrets that Ansible reads through `ANSIBLE_PRIVATE_KEY_FILE` / `ANSIBLE_VAULT_PASSWORD_FILE`. The settings card shows only what differs per installation and has no default (release, control plane domain); everything with a default (public name and NTP servers as in mini-lab / metal-roles, inventory, role versions, the pipeline platform and its settings) sits in its Advanced section. - **Icons.** Icons come from `src/views/icons/` only: `index.tsx` is the registry (Lucide icons plus the custom `NetworkSwitch`, `Rack`, `RackGroup` glyphs from `custom.ts`, drawn on Lucide's 24 px grid) with typed maps per tab, node kind, BOM category, rack slot kind and severity, and an `` helper (decorative, `aria-hidden`). An ESLint rule forbids importing `lucide-react` anywhere else. Sizes: 16 px in tabs and buttons, 14 px inline and in topology nodes, 12 px in rack slots; color follows the text. - **Templates.** `src/model/templates.ts` holds the starting plans offered by the Templates menu. They must order only hardware that is current at the vendor — `templates.test.ts` fails on any `availability` of `eol` / `withdrawn`, for the templates and for the default plan. Each `build()` returns a fresh Plan with new ids; `templates.test.ts` asserts every template parses and has no validation errors, so keep them valid when capacity rules change. - **Persistence and format version.** Zustand store (`src/store/planStore.ts`) with `persist` middleware → localStorage key `metal-stack-planner/plan`. JSON export/import in `src/io/json.ts`; every import runs through `migrateRawPlan()` (`src/model/migrate.ts`) and is then validated with `PlanSchema.parse`, and the same path runs on rehydrate. `SCHEMA_VERSION` in `migrate.ts` is the plan file format version — currently 1 with no migrations. Purely additive fields only need a Zod `.default()`; anything that breaks older files bumps `SCHEMA_VERSION` and adds a function to `MIGRATIONS`, keyed by the version it upgrades _from_. Files from a newer version are refused with a readable message. diff --git a/src/index.css b/src/index.css index 3086feb..59a1cf9 100644 --- a/src/index.css +++ b/src/index.css @@ -14,6 +14,13 @@ --color-brand-strong: #d97706; --color-brand-soft: #fcd094; --color-brand-tint: #fff8e6; + /* Syntax colors of the file previews (Ansible tab); CHANGE_ME uses the brand. */ + --color-code-key: #1e40af; + --color-code-string: #15803d; + --color-code-number: #b45309; + --color-code-literal: #7e22ce; + --color-code-template: #0e7490; + --color-code-comment: #6b7280; } @layer base { diff --git a/src/views/ansible/FileBrowser.tsx b/src/views/ansible/FileBrowser.tsx index 34ebdf3..b1fb4e7 100644 --- a/src/views/ansible/FileBrowser.tsx +++ b/src/views/ansible/FileBrowser.tsx @@ -1,7 +1,8 @@ -import { Fragment } from 'react' -import { CHANGE_ME, type AnsibleFile, type Placeholder } from '../../derive/ansible' +import { useState } from 'react' +import type { AnsibleFile, Placeholder } from '../../derive/ansible' import { useToastStore } from '../../store/toastStore' import { ACTION_ICON, FILE_ICON, Icon } from '../icons' +import Preview, { type PreviewMode } from './Preview' interface Dir { name: string @@ -93,23 +94,6 @@ function DirView({ ) } -/** One line of a file: comments dimmed, CHANGE_ME highlighted. */ -function Line({ text }: { text: string }) { - const comment = /^\s*#/.test(text) - const parts = text.split(CHANGE_ME) - return ( - - {parts.map((part, i) => ( - - {i > 0 && {CHANGE_ME}} - {part} - - ))} - {'\n'} - - ) -} - export default function FileBrowser({ files, placeholders, @@ -122,6 +106,7 @@ export default function FileBrowser({ onSelect: (path: string) => void }) { const notify = useToastStore((s) => s.notify) + const [mode, setMode] = useState('rendered') const file = files.find((f) => f.path === selected) ?? files[0] const tree = buildTree(files) const todos = new Map() @@ -138,6 +123,23 @@ export default function FileBrowser({
{file.path} + {file.path.endsWith('.md') && ( +
+ {(['rendered', 'source'] as const).map((m) => ( + + ))} +
+ )}
-
-          {file.content
-            .replace(/\n$/, '')
-            .split('\n')
-            .map((line, i) => (
-              
-            ))}
-        
+
) diff --git a/src/views/ansible/Preview.tsx b/src/views/ansible/Preview.tsx new file mode 100644 index 0000000..36ee297 --- /dev/null +++ b/src/views/ansible/Preview.tsx @@ -0,0 +1,168 @@ +import { Fragment } from 'react' +import { CHANGE_ME, type AnsibleFile } from '../../derive/ansible' +import { + iniTokens, + inlineSpans, + markdownBlocks, + yamlTokens, + type MdBlock, + type Token, + type TokenKind, +} from './syntax' + +const TOKEN_CLASS: Record = { + plain: '', + key: 'text-code-key', + punct: 'text-code-comment', + string: 'text-code-string', + number: 'text-code-number', + literal: 'text-code-literal', + template: 'text-code-template', + comment: 'text-code-comment italic', + placeholder: 'rounded bg-brand-soft px-0.5 font-semibold text-ink', +} + +function Lines({ lines }: { lines: Token[][] }) { + return ( +
+      {lines.map((line, i) => (
+        
+          {line.map((t, j) =>
+            TOKEN_CLASS[t.kind] ? (
+              
+                {t.text}
+              
+            ) : (
+              {t.text}
+            ),
+          )}
+          {'\n'}
+        
+      ))}
+    
+ ) +} + +/** Inline code and bold; CHANGE_ME stands out like in the YAML. */ +function InlineText({ text }: { text: string }) { + return ( + <> + {inlineSpans(text).map((s, i) => + s.kind === 'code' ? ( + + {s.text} + + ) : s.kind === 'strong' ? ( + {s.text} + ) : ( + {s.text} + ), + )} + + ) +} + +const HEADING_CLASS = [ + 'text-lg font-semibold', + 'mt-2 text-base font-semibold', + 'text-sm font-semibold', +] + +function Block({ block }: { block: MdBlock }) { + switch (block.kind) { + case 'heading': { + const Tag = `h${Math.min(block.level + 1, 6)}` as 'h2' + return ( + + + + ) + } + case 'paragraph': + return ( +

+ +

+ ) + case 'list': + return ( +
    + {block.items.map((item, i) => ( +
  • + +
  • + ))} +
+ ) + case 'code': + return ( +
+          {block.text}
+        
+ ) + case 'table': + return ( +
+ + + + {block.header.map((h, i) => ( + + ))} + + + + {block.rows.map((row, i) => ( + + {row.map((cell, j) => ( + + ))} + + ))} + +
+ +
+ +
+
+ ) + } +} + +function Markdown({ text }: { text: string }) { + return ( +
+ {markdownBlocks(text).map((block, i) => ( + + ))} +
+ ) +} + +const plainLines = (text: string): Token[][] => + text + .replace(/\n$/, '') + .split('\n') + .map((line) => [{ kind: 'plain', text: line }]) + +export type PreviewMode = 'rendered' | 'source' + +/** A generated file, highlighted by type; markdown rendered or as source. */ +export default function Preview({ file, mode }: { file: AnsibleFile; mode: PreviewMode }) { + if (/\.md$/.test(file.path)) { + return mode === 'rendered' ? ( + + ) : ( + + ) + } + if (/\.ya?ml$/.test(file.path)) return + if (/\.cfg$/.test(file.path)) return + return +} diff --git a/src/views/ansible/syntax.test.ts b/src/views/ansible/syntax.test.ts new file mode 100644 index 0000000..d657d40 --- /dev/null +++ b/src/views/ansible/syntax.test.ts @@ -0,0 +1,119 @@ +import { describe, expect, it } from 'vitest' +import { deriveAnsible } from '../../derive/ansible' +import { createEmptyPlan } from '../../model/defaults' +import { iniTokens, inlineSpans, markdownBlocks, yamlTokens, type Token } from './syntax' + +const kinds = (line: Token[]) => line.filter((t) => t.text.trim()).map((t) => [t.kind, t.text]) + +describe('YAML tokens', () => { + it('colors keys, values, comments and placeholders', () => { + const lines = yamlTokens( + [ + '---', + '# header', + 'sonic_config_asn: 4200000001', + 'lo: "{{ sonic_config_loopback_address }}"', + 'flag: false # note', + 'ports:', + ' - Ethernet120', + 'key: CHANGE_ME', + 'partition-1:1-mgmt-server:', + ' url: oci://ghcr.io/x:{{ v }}', + ].join('\n'), + ) + expect(kinds(lines[0])).toEqual([['punct', '---']]) + expect(kinds(lines[1])).toEqual([['comment', '# header']]) + expect(kinds(lines[2])).toEqual([ + ['key', 'sonic_config_asn'], + ['punct', ':'], + ['number', '4200000001'], + ]) + expect(kinds(lines[3])).toEqual([ + ['key', 'lo'], + ['punct', ':'], + ['string', '"'], + ['template', '{{ sonic_config_loopback_address }}'], + ['string', '"'], + ]) + expect(kinds(lines[4])).toEqual([ + ['key', 'flag'], + ['punct', ':'], + ['literal', 'false'], + ['comment', '# note'], + ]) + expect(kinds(lines[6])).toEqual([ + ['punct', '- '], + ['plain', 'Ethernet120'], + ]) + expect(kinds(lines[7])).toEqual([ + ['key', 'key'], + ['punct', ':'], + ['placeholder', 'CHANGE_ME'], + ]) + expect(kinds(lines[8])[0]).toEqual(['key', 'partition-1:1-mgmt-server']) + expect(kinds(lines[9]).slice(2)).toEqual([ + ['plain', 'oci://ghcr.io/x:'], + ['template', '{{ v }}'], + ]) + }) + + it('keeps block scalars as text', () => { + const lines = yamlTokens('run: |\n if [ -n "$x" ]; then\n echo a: b\n fi\nnext: 1') + expect(kinds(lines[0]).at(-1)).toEqual(['punct', '|']) + expect(kinds(lines[2])).toEqual([['plain', ' echo a: b']]) + expect(kinds(lines[4])[0]).toEqual(['key', 'next']) + expect(kinds(yamlTokens('s: |\n grep CHANGE_ME x')[1])).toEqual([ + ['plain', ' grep CHANGE_ME x'], + ]) + }) + + it('loses no text on any generated file', () => { + for (const f of deriveAnsible(createEmptyPlan()).files) { + if (!/\.ya?ml$/.test(f.path)) continue + const back = yamlTokens(f.content) + .map((line) => line.map((t) => t.text).join('')) + .join('\n') + expect(back, f.path).toBe(f.content.replace(/\n$/, '')) + } + }) +}) + +describe('INI tokens', () => { + it('colors sections, keys and comments', () => { + const [comment, section, entry] = iniTokens('# x\n[defaults]\nroles_path = roles\n') + expect(comment[0].kind).toBe('comment') + expect(section[0].kind).toBe('literal') + expect(entry.map((t) => t.kind)).toEqual(['key', 'punct', 'string']) + }) +}) + +describe('markdown', () => { + it('splits the README into blocks', () => { + const readme = deriveAnsible(createEmptyPlan()).files.find((f) => f.path === 'README.md')! + const blocks = markdownBlocks(readme.content) + expect(blocks[0]).toMatchObject({ kind: 'heading', level: 1 }) + expect(blocks.some((b) => b.kind === 'code' && b.lang === 'sh')).toBe(true) + const table = blocks.find((b) => b.kind === 'table') + expect(table && table.kind === 'table' && table.header).toEqual(['File', 'Variable', 'What']) + }) + + it('treats lines no block takes as text', () => { + expect(markdownBlocks('| not a table\n```sh extra')).toEqual([ + { kind: 'paragraph', text: '| not a table' }, + { kind: 'paragraph', text: '```sh extra' }, + ]) + }) + + it('parses lists, paragraphs and inline spans', () => { + expect(markdownBlocks('- a\n- b\n\nsome\ntext')).toEqual([ + { kind: 'list', items: ['a', 'b'] }, + { kind: 'paragraph', text: 'some text' }, + ]) + expect(inlineSpans('run `make` **now**')).toEqual([ + { kind: 'text', text: 'run ' }, + { kind: 'code', text: 'make' }, + { kind: 'text', text: ' ' }, + { kind: 'strong', text: 'now' }, + ]) + }) +}) diff --git a/src/views/ansible/syntax.ts b/src/views/ansible/syntax.ts new file mode 100644 index 0000000..bd2f19b --- /dev/null +++ b/src/views/ansible/syntax.ts @@ -0,0 +1,210 @@ +import { CHANGE_ME } from '../../derive/ansible' + +// Tokenizers for the file previews of the Ansible tab: YAML and INI lines +// into colored tokens, markdown into blocks. They cover what the export +// writes (block-style YAML, the README's headings, tables, lists and code +// fences), not the full languages. Views render tokens as React elements, +// never as HTML strings, so plan names can't inject markup. + +export type TokenKind = + | 'plain' + | 'key' + | 'punct' + | 'string' + | 'number' + | 'literal' + | 'template' + | 'comment' + | 'placeholder' + +export interface Token { + kind: TokenKind + text: string +} + +/** Splits Jinja expressions and CHANGE_ME out of a scalar. */ +function scalarTokens(text: string, kind: TokenKind): Token[] { + return text + .split(new RegExp(`(\\{\\{.*?\\}\\}|${CHANGE_ME})`)) + .filter((part) => part !== '') + .map((part) => ({ + kind: part === CHANGE_ME ? 'placeholder' : /^\{\{.*\}\}$/.test(part) ? 'template' : kind, + text: part, + })) +} + +/** Index of a ` #` comment outside quotes, or -1. */ +function commentStart(text: string): number { + let quote = '' + for (let i = 0; i < text.length; i++) { + const c = text[i] + if (quote) { + if (c === quote) quote = '' + } else if (c === '"' || c === "'") quote = c + else if (c === '#' && (i === 0 || /\s/.test(text[i - 1]))) return i + } + return -1 +} + +function valueTokens(value: string): Token[] { + const hash = commentStart(value) + const body = hash < 0 ? value : value.slice(0, hash) + const trimmed = body.trimEnd() + const tail = body.slice(trimmed.length) + const out: Token[] = [] + if (/^(["']).*\1$/.test(trimmed)) out.push(...scalarTokens(trimmed, 'string')) + else if (/^-?\d+(\.\d+)?$/.test(trimmed)) out.push({ kind: 'number', text: trimmed }) + else if (/^(true|false|null|~)$/i.test(trimmed)) out.push({ kind: 'literal', text: trimmed }) + else if (/^(\[\]|\{\}|[|>][-+]?)$/.test(trimmed)) out.push({ kind: 'punct', text: trimmed }) + else out.push(...scalarTokens(trimmed, 'plain')) + if (tail) out.push({ kind: 'plain', text: tail }) + if (hash >= 0) out.push({ kind: 'comment', text: value.slice(hash) }) + return out +} + +// A key: quoted, or plain up to the first colon followed by a space or the +// end of the line (so "partition-1:1-mgmt-server:" is one key). +const KEY = /^("(?:[^"\\]|\\.)*"|'[^']*'|[^\s#'"](?:[^:]|:(?!\s|$))*?)(:)(?=\s|$)/ + +export function yamlTokens(text: string): Token[][] { + let blockIndent: number | null = null + return text + .replace(/\n$/, '') + .split('\n') + .map((line): Token[] => { + const indent = line.length - line.trimStart().length + if (blockIndent !== null) { + // Block scalars are scripts here; a CHANGE_ME in them is text. + if (line.trim() === '' || indent > blockIndent) return [{ kind: 'plain', text: line }] + blockIndent = null + } + if (/^\s*#/.test(line) || line === '---') { + return [{ kind: line === '---' ? 'punct' : 'comment', text: line }] + } + const out: Token[] = [] + const lead = /^(\s*)(- )?/.exec(line)! + if (lead[1]) out.push({ kind: 'plain', text: lead[1] }) + if (lead[2]) out.push({ kind: 'punct', text: lead[2] }) + let rest = line.slice(lead[0].length) + const key = KEY.exec(rest) + if (key) { + out.push(...scalarTokens(key[1], 'key'), { kind: 'punct', text: ':' }) + rest = rest.slice(key[0].length) + const space = /^\s*/.exec(rest)![0] + if (space) out.push({ kind: 'plain', text: space }) + rest = rest.slice(space.length) + } + if (rest) { + const value = valueTokens(rest) + if (value[0]?.kind === 'punct' && /^[|>]/.test(value[0].text)) blockIndent = indent + out.push(...value) + } + return out + }) +} + +/** ansible.cfg: comments, [sections], key = value. */ +export function iniTokens(text: string): Token[][] { + return text + .replace(/\n$/, '') + .split('\n') + .map((line): Token[] => { + if (/^\s*[#;]/.test(line)) return [{ kind: 'comment', text: line }] + if (/^\s*\[.*\]\s*$/.test(line)) return [{ kind: 'literal', text: line }] + const m = /^(\s*[^=]+?)(\s*=\s*)(.*)$/.exec(line) + if (!m) return [{ kind: 'plain', text: line }] + return [ + { kind: 'key', text: m[1] }, + { kind: 'punct', text: m[2] }, + { kind: 'string', text: m[3] }, + ] + }) +} + +// --- Markdown ---------------------------------------------------------- + +export type MdBlock = + | { kind: 'heading'; level: number; text: string } + | { kind: 'paragraph'; text: string } + | { kind: 'list'; items: string[] } + | { kind: 'table'; header: string[]; rows: string[][] } + | { kind: 'code'; lang: string; text: string } + +const cells = (row: string) => + row + .trim() + .replace(/^\||\|$/g, '') + .split('|') + .map((c) => c.trim()) + +export function markdownBlocks(text: string): MdBlock[] { + const lines = text.split('\n') + const blocks: MdBlock[] = [] + let i = 0 + while (i < lines.length) { + const line = lines[i] + const fence = /^```(\w*)\s*$/.exec(line) + if (fence) { + const body: string[] = [] + for (i++; i < lines.length && !/^```\s*$/.test(lines[i]); i++) body.push(lines[i]) + blocks.push({ kind: 'code', lang: fence[1], text: body.join('\n') }) + i++ + continue + } + const heading = /^(#{1,6})\s+(.*)$/.exec(line) + if (heading) { + blocks.push({ kind: 'heading', level: heading[1].length, text: heading[2] }) + i++ + continue + } + if (/^\s*\|/.test(line) && /^\s*\|\s*:?-/.test(lines[i + 1] ?? '')) { + const header = cells(line) + const rows: string[][] = [] + for (i += 2; i < lines.length && /^\s*\|/.test(lines[i]); i++) rows.push(cells(lines[i])) + blocks.push({ kind: 'table', header, rows }) + continue + } + if (/^\s*[-*]\s+/.test(line)) { + const items: string[] = [] + for (; i < lines.length && /^\s*[-*]\s+/.test(lines[i]); i++) { + items.push(lines[i].replace(/^\s*[-*]\s+/, '')) + } + blocks.push({ kind: 'list', items }) + continue + } + if (line.trim() === '') { + i++ + continue + } + const para: string[] = [] + for ( + ; + i < lines.length && + lines[i].trim() !== '' && + !/^(#{1,6}\s|```|\s*\||\s*[-*]\s)/.test(lines[i]); + i++ + ) { + para.push(lines[i].trim()) + } + // A line no block takes (a lone "| x", "```sh extra") is text. + if (para.length === 0) para.push(lines[i++].trim()) + blocks.push({ kind: 'paragraph', text: para.join(' ') }) + } + return blocks +} + +export type Inline = { kind: 'text' | 'code' | 'strong'; text: string } + +/** `code` and **strong** spans of a markdown line. */ +export function inlineSpans(text: string): Inline[] { + return text + .split(/(`[^`]+`|\*\*[^*]+\*\*)/) + .filter((part) => part !== '') + .map((part) => + part.startsWith('`') + ? { kind: 'code', text: part.slice(1, -1) } + : part.startsWith('**') + ? { kind: 'strong', text: part.slice(2, -2) } + : { kind: 'text', text: part }, + ) +} From c5d6e20fae7952b2d0a1cf2fc7b533e617e35cb8 Mon Sep 17 00:00:00 2001 From: Markus Fensterer Date: Fri, 18 Sep 2026 17:30:07 +0200 Subject: [PATCH 6/8] Update the README for the Ansible export The README now covers the current Ansible export (derived switch ports, control plane domain, public server defaults, the file preview), the new modules and the yaml and JSZip dependencies, and has an Ansible screenshot and a fresh plan editor one. The export computes the rack ids in one place in derive/devices.ts. Co-Authored-By: Claude Opus 5 --- README.md | 34 +++++++++++++++++++++++++--------- docs/screenshots/ansible.png | Bin 0 -> 224097 bytes docs/screenshots/plan.png | Bin 172423 -> 173246 bytes src/derive/ansible/vars.ts | 2 +- src/derive/devices.ts | 14 ++++++++++++-- 5 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 docs/screenshots/ansible.png diff --git a/README.md b/README.md index fc3ee8d..8d844d2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

Plan a metal-stack installation in the browser:
configure the production and management networks, see the resulting topology and rack elevations,
- and get an orderable hardware bill of materials. + get an orderable hardware bill of materials and an Ansible deployment with CI/CD pipelines.

@@ -77,11 +77,16 @@ consistent — you edit the plan, everything else follows from it. - **Ansible export** — an inventory, group and host variables for the [metal-roles](https://github.com/metal-stack/metal-roles) partition roles (sonic-config, metal-core, mgmt-server, dhcp, metal-bmc, pixiecore, image-cache) and the playbooks that apply - them, laid out like a metal-stack deployment repository. Hostnames, ASNs, loopbacks, management - addresses, per-leaf PXE networks, DHCP ranges and transfer networks come from the plan and the - IP plan; secrets, endpoints and switch ports are marked `CHANGE_ME` and listed. A GitLab CI - pipeline (or GitHub Actions workflows) checks every push and deploys each partition by hand, - in order, on a runner inside the partition. Previewed file by file and downloaded as a zip. + them, laid out like a metal-stack deployment repository. Derived from the plan and the IP plan: + hostnames, ASNs (leaves unique, spines and exits shared), loopbacks, management addresses, + per-leaf PXE networks, DHCP ranges, transfer networks and each switch's BGP ports (uplinks on + the last ports, from the SONiC port maps in the catalog). Only the metal-stack release and the + control plane domain need entering (metal-api and NSQ are reached under it); name and NTP + servers default to public ones, and everything else with a default sits under Advanced. What + the plan cannot know, the secrets and certificates, is marked `CHANGE_ME` and listed. A GitLab + CI pipeline (or GitHub Actions workflows) checks every push, refusing leftover placeholders, + and deploys each partition by hand, in order, on a runner inside the partition. Every file is + previewed with YAML highlighting (the README rendered) and the whole set downloads as a zip. - **Prices** — an optional price book (kept in the browser, separate from the plan, importable and exportable as JSON) turns the BOM into a cost estimate with line totals, category subtotals and a grand total. @@ -131,6 +136,13 @@ With derivation shown: every quantity broken down by central rack and rack group ![BOM](docs/screenshots/bom.png) +### Ansible deployment + +The Redundant template with a release and control plane domain entered: a leaf's host variables +with its ASN, loopback, BGP ports and PXE network, and the secrets still to fill in on the right. + +![Ansible deployment](docs/screenshots/ansible.png) + ## Getting started The planner runs at **[https://metal-stack.github.io/planner/](https://metal-stack.github.io/planner/)** — no install, no account, nothing leaves your browser. @@ -175,9 +187,12 @@ The whole app operates on a single `Plan` document, described by Zod schemas in | `src/derive/validate.ts` | validation issues | | `src/derive/nodes.ts` | node tallies per rack, partition and plan | | `src/derive/ip/` | CIDR arithmetic, the IP address plan and its validation | +| `src/derive/devices.ts` | named devices with hostnames and ASNs | +| `src/derive/ports.ts` | the BGP ports of every switch | +| `src/derive/ansible/` | the Ansible inventory, variables, playbooks and CI/CD | -Hardware facts — part numbers, port counts, height units, nodes per chassis, and metal-stack -compatibility — live in `src/model/catalog.ts`. The compatibility data mirrors the official +Hardware facts — part numbers, port counts and SONiC port names, height units, nodes per chassis, +and metal-stack compatibility — live in `src/model/catalog.ts`. The compatibility data mirrors the official [hardware list](https://docs.metal-stack.io/docs/hardware). State lives in a Zustand store with undo history and localStorage persistence @@ -194,7 +209,8 @@ diagrams are plain SVG. ## Dependencies and advisories Runtime dependencies are deliberately few: React, Zustand (with Zundo for undo), Zod, Lucide for -icons and ExcelJS for the xlsx export. Everything else is build tooling. +icons, ExcelJS for the xlsx export, and yaml and JSZip for the Ansible export (both loaded only +when the Ansible tab opens). Everything else is build tooling. `npm audit` reports two moderate advisories against `uuid <11.1.1` ([GHSA-w5hq-g745-h8pq](https://github.com/advisories/GHSA-w5hq-g745-h8pq)), reached through diff --git a/docs/screenshots/ansible.png b/docs/screenshots/ansible.png new file mode 100644 index 0000000000000000000000000000000000000000..809523194f2cfd30cc351fd4c4347e96c40d2927 GIT binary patch literal 224097 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�dKy$(;dD@4To1 z$H1nd)@mIb&E({cST4Nu#opXG^EJhwV%?ajkd0fHray@=5qr%ejZ7s+k2#AuGDUD` zXoyw!3v4?WQn-CTTdKe#{QVpZhSNwxDQC6YMdq?S{x5;Cu}rWOR}j)(8#@7mu|Z)` zr9cUe0C5ZssF*VWY?i9Ih1I}1JXXw;IeZ757`&A=jpv)gOVH%Nob*9(Q zp`Pccg$eZ-L_F7CjK%_iRTvt>l&NG1K{vFgJ1VzRhoS zRrscXY%~e?C2lSXihFnNM)~@p3d>?u;&RwtozB+GWxsP5VU>mJ!04%b6Ioc_9M$#f zs|T9Av9Yn0hF|IO-e~6EKvPtD(dr*0Xt=w|r#Xh~YzxQl3Lf4<=5T?UYWTREk9R5g z6>mf>Z-JYkkBm|xBR-msOrOI11vdgazj!YwxJfA=f_w7Q_?GR3?!5fe=VYE-6>E$H zCK-I1NBZp+?g(C9Vo(tfc$=py*uQN}=_s(ANn;`@J{=c}kjua~(ldPCFjE`rHA%{I zW;;~Vp&z5kcVuOEZJ9dd@;F3UNG`I4gnZDdAVMNV-Tb6?#Xzvl?AuZxenm|iOc`(d zf~VSs_bz*h>4*nX3@d6V*0za_hQZ3;qBnUI>r;aK=~~PF`h3yX5^@6`Zb65nS$Uz0 z6bjO`2(ZAJKe5gpuy0T+ZxU)pU0>#^upR$h> zQ?EWs%ghMI!Z9!~@HUhb11FLob&TP}?4-LwR?ho`kdPjswZ2X(yZtr>wGnL#6dfIB zzVa{Y>zB9YuJz;-arN}}l8^}VjCR%3xEy;jtVWWIT_YsqPSZ2R{BgmH)PuC-q7j|c z)@>fXr_1OfTDBMgpFb}{#0B~xPrqo7kK^QhYw!CQ7+CW4m9OsxwE5zBUYCvKm8I(G zRmxT`e(>U9wq`lp*w)5m2qpJS)K2Z>mR=P_- z>F$tFN9eaRDd`t62el3+3Bw9Rrvo`~knk z-x_h=#F?9z7#Q9{VZ_z08jP8St!5fUdvx^;sW7g*@fKeH*&zygDr!(5I-P$cd7K=a zEs?8+t25fo)9dTk5O=b$!Aulz%D77DOD{%D*F9*S_G0ti`DQhIKSq&8w(kTj*j3-} z3HUO9KFhqb7C#m1DZ&B6W|QN!?ET#lMi2{UmKU}*NdF|nWuhjRp^HyR0dqA{J{X-H zUqFWTJh8EHGl2Cf!Ps2>_1m|}Et?59jMvxN(rz~^$Lj;2cTkK*cy%v@(`9$|o|94( zM5~Q;V*^u0l=sd?7BNXWp8!8Lc0e73F+d(mHGRKFJ~ddR@b-vby1~!iuM-Z=3Ls`` z1A2N_oF+d%`6q2=wTYAec!q~(pl>;rJUs&{5V2ZWfL1pxsTmXZ7I=- zh+5NYt%x{;l~i#PQMCG~z*3q#Ocjz+CrZkA5_GZJAKy8ovE;mMpLI7dDnRxYdEsx8 z)KqD=T$Y?XwYb#{yJ9FM)zj!9>+P*)uW$@jvc9AEpq+_=EJk(<2lt;L1mnv2@ZjK! ziel7eYc@r8sEwW7-r0#nP~>cd?kwcgY6XG^Qqs6Yd@5PlI`!AjEUS(L+|Mi&eB-_v zy12lF)dH%hYZK7NCs-@NHXK%!nlon4n8SkH2s)GWh z$FMiS8F~f=8{T94Rx_3o?mI^>*bq?gY_3)gJ0eLowznA^R7D&}c4hqhGCz?qKz#++ zoOTj>(Mh$SuJ&xK!{ck)OIRSyzBq#UT(N?)y?z`!raNmbUn< z85&i=P-=`y;U*m}55S*Plt_BJhP+UpFL^Fv1vN$?zzxA&@L^erddKY{KX@dCJTIv? zO=(?c+)tK-hkNqYj2{0PezJt7F#KC;trhae=e?sYZZ463`A@o^f=3)=s1<@H7!|UyEdJM!osAua9&q95o{W{G%K@gS@=U9hr~mQH0AiUr{{a(9W&YCwc?v5 zG(w(dOFyM00PSHhR~f#6ME&cnO4(IHe7p!RE$`I58cfnaOAA^O=yN*Y5Y#}kn``uf zo!{)~lo0Jd&CCQQ2{0pB4rW{yGx_PPg5rObA}<&W?&fkuEt;3F6~kwXQAW6Pe* z=VIe5EcY?JWMnF*XDG(AwoHvre?%javizLHa%z*92uGCc$scg&C=B>T{rp2E=&7evBT4=I`*UFu zmE$}<+xM%h0gS}Vu^e89jc~%ngQw%I-2rpe0p@7FKL&xSty+AOnnoyXz7j4tGm|W} z!Q&?_ra~w&AO8%O6&bIbw%mmI{3ABxVz8ptlVndqH0w5`y?Pm6y&4)MdK#R$fN?|x zayAcwQg9g1F{{n;eD15iS1&Fv3oPumWj1`Oj$1wbLB^*(J^p=jC%d()rP;BmIIQYo zGjyWHg~aWS)iR{XzqFowIe}MNK}J>LllSyqn+j~3WiM)8MD0$eYECt9QXZRLPo3>_ z)i``s7#>N8I93+!ZJqg;p=>uQf%?cR{VAK~{*27Z^6F&G9LJs4@Km1`-x&#U5qqa= zvH%!2^&c+d5D}2PYho?zR=&218#oWy=brP6KW60skTpVf;@B8R-g4DPRd?RlixdEd-p>F!B8gA#ZC&8e3VMD2%D6w>*Cu#^L~bd;Eh=%N+W8*jpayGO9y5}HNv2*FGYC2$ zcOxh~o0EcrZwc15xiM;@Eu>9Pk2K)zTs14c%>E4Q90KTf1#x)%&hD!~z~9~x5`0=* zUOr#xIKAA_g&K}JLY&X@d-@rJx|TH<@W>9cGCzF+ag2_Rr;L%r^LNxfI$Js7k_H6X zt#TOxzG6$a4OMAnyj~2u@7*Af9ILsoWVY$a^O%g}qOEa;J+YpEF&k`ZYU)1Sw((NU zzTV!{m>8r(eOTXbwbmWV6N=?(^*%E#=6>)c+l%Kod##5`|nXT~Sei6vvT`Nbf*bcQL;|wX~oh>Pyt`8b`MF z1y9dgtJ(Tkpfpr`<8PvhKYb137|Qf_!*mRtcGmOsKuhDd-MPK)H6F-twvmzgFtt4a z-~h|m$sB~x>%8!Grd9h5=K8IUS`?ID;Ur&09YQM(f`0$5VbfSyUCEQnczXkTvKM-R z3Pw576CQrx^Lk7^n!Y#p<|_ycEGkkKOUwvNVdH{cT-5)F-&yP3;Scs|nilvvD^NQ< zJ5TuQY#<$qh>BKHR`%_iJsz)OlCX61d*@6~+`~LRx{R;NkCURDrYl(Z`0}-(>I8B3Xcz(?&<3fF)uXVv!07 z!I=Go1F>`va=*$2B8|o;j2Ny-T+V1sx4)nMnRiDS-l9x|MMBNjnIARhP3<&)1Fmyo zp;~C;4LCxB<_FNZ-rm;Y;}du^Y+I^iIcpy@y*iX`(aw4rn-@l$b8kJvA>tiigW1+U@UW4_Jr%`6oGF;OC_t^q#CLRPfpzeacW8 zt(t`2ZmoZN1WrC^(q207G@m;Df)gU;O47-b$J>z}-AkX|-P_kxQ=?^?hlD&QCEb^U z;|5Q5Hrus3&{2EUD}crbczD!L6>qe(=DL;4$u#tg&0(XzkO)j^r@_%CL`4OKhlAn` zC??_dH+#M$sNwb*Xmj|2WrnCf+&)gTG&LJZ{O8ODFIV~w&Yvim={cbXn7iW7$i0wr zNQH9GZ~KSA2!z|8Mov1Ixwy7gR#Na{go^5`!Q;mmRedHsJ>9{K5ak99>U_6TfR{ryR7&R$TjTe4o^^4!u=M;nQkevT-pC?HaqZ2fSV<`8t`Hn=6l z#vTu5Q6`ZJV*I^3llu*7XQtEye;pe8z(Jh


l|$-5}|?EE|cPfX0fKvYyzRn^tu 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 z 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

uvLNb+0tvp?9poL<@7 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

7k)eme-^Wn)lf%>S~`DM}nRH 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%0Au&zT%EXk$Y&_08_jYNg;ahCj~i~c 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 I7jNDF7iyP@bpQYW diff --git a/src/derive/ansible/vars.ts b/src/derive/ansible/vars.ts index 7ca5d8a..54f58ea 100644 --- a/src/derive/ansible/vars.ts +++ b/src/derive/ansible/vars.ts @@ -255,7 +255,7 @@ function partitionGroupVars( rg, 'rack', [ - kv('metal_core_rack_id', `${p.slug}-rack${String(rack.number).padStart(2, '0')}`), + kv('metal_core_rack_id', rack.metalId), l3 && kv( 'metal_partition_mgmt_gateway', diff --git a/src/derive/devices.ts b/src/derive/devices.ts index 11363b0..049c9d6 100644 --- a/src/derive/devices.ts +++ b/src/derive/devices.ts @@ -7,7 +7,9 @@ import { mgmtDeviceCount, type Partition, type Plan } from '../model/plan' // Hostnames follow the deployment repositories metal-stack operators keep // (`-spine01`, `-r01leaf01`, `-r01mgmtleaf` // …): the partition slug, then the device, with racks numbered in plan -// order (a rack group once, its leaves sit in the middle rack). +// order (a rack group once, its leaves sit in the middle rack). Rack names +// are display only; identifiers are always the slug and the rack number, +// unique within the plan. // // ASNs follow the numbering model of the metal-stack network docs // (docs.metal-stack.io, Concepts › Network › Theory, "ASN Numbering"): @@ -58,6 +60,8 @@ export interface DeviceRack { number: number /** "r01" */ tag: string + /** metal-stack's rack id (metal_core_rack_id), "partition-1-rack01". */ + metalId: string } export interface PartitionDevices { @@ -102,6 +106,12 @@ export function groupName(slug: string): string { return /^[a-z_]/.test(g) ? g : `p_${g}` } +/** Identifiers of the n-th rack (1-based) of a partition: the hostname + * tag and metal-stack's rack id. */ +function rackIds(slug: string, n: number): { tag: string; metalId: string } { + return { tag: `r${pad(n)}`, metalId: `${slug}-rack${pad(n)}` } +} + function uniqueSlugs(partitions: Partition[]): string[] { const used = new Set() return partitions.map((p, i) => { @@ -167,7 +177,7 @@ function partitionDevices( rackId: rack.id, name: rack.name, number: i + 1, - tag: `r${pad(i + 1)}`, + ...rackIds(slug, i + 1), })) partition.racks.forEach((rack, i) => { const r = racks[i] From 21a9fdd0598637d5d56ee89bfc1d557a5f3d28d2 Mon Sep 17 00:00:00 2001 From: Markus Fensterer Date: Fri, 18 Sep 2026 22:02:15 +0200 Subject: [PATCH 7/8] Port and breakout config; Broadcom SONiC via dellemc.enterprise_sonic Port plan (derive/ports.ts), shared by both NOS paths: 4x25G breakouts for 2x25G server ports on the leaves' first 100G ports, uplinks on the last ports of their speed, downlinks from the first, with speed, MTU (9216 fabric, 9000 servers and management) and RS-FEC on server ports. The catalog gains front-panel numbers and breakout lanes. Switches follow their partition's NOS, grouped as edgecore_sonic and broadcom_sonic in the inventory: - Edgecore SONiC: metal-roles' sonic-config, now with sonic_config_breakouts and sonic_config_ports. - Broadcom Enterprise SONiC: sonic-config only supports Edgecore, so the Dell collection from Ansible Galaxy configures these switches over httpapi, one generated task per module (breakouts, system, LLDP, interfaces, VLANs, L3, DHCP relay, route maps, BGP, VXLAN, NTP, mgmt VRF) with one sonic_ variable each, structured like an existing Enterprise SONiC deployment. An SSH play sets DNS and the leaves' split routing mode for metal-core. PXE, missing so far: every leaf carries VLAN 4000 on its metal-core CIDR with DHCP relayed to the mgmt servers, whose dhcp_subnets now lease each leaf's PXE network. Leaves run without the mgmt VRF, as metal-stack deployments do. Checked: all 662 generated module configs pass the collection's own argument specs, every playbook passes --syntax-check, sonic-config's templates render with the generated variables, every BGP switch gets its FRR from exactly one source and every leaf relays PXE DHCP (tests). Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 +- README.md | 7 +- src/derive/ansible/ansible.test.ts | 16 +- src/derive/ansible/enterpriseSonic.ts | 429 ++++++++++++++++++++++++++ src/derive/ansible/inventory.ts | 40 ++- src/derive/ansible/nos.test.ts | 284 +++++++++++++++++ src/derive/ansible/playbooks.ts | 98 +++++- src/derive/ansible/vars.ts | 266 ++++++++++++---- src/derive/ip/deviceAddresses.ts | 3 + src/derive/ports.test.ts | 169 ++++++++-- src/derive/ports.ts | 299 +++++++++++++++--- src/model/ansibleRoles.ts | 35 ++- src/model/catalog.ts | 23 ++ 13 files changed, 1513 insertions(+), 158 deletions(-) create mode 100644 src/derive/ansible/enterpriseSonic.ts create mode 100644 src/derive/ansible/nos.test.ts diff --git a/CLAUDE.md b/CLAUDE.md index d726f3f..045e08e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,7 +28,7 @@ A client-only single-page app (React + Vite + TypeScript) to plan a [metal-stack - **Rack defaults and power budget.** `Partition.rackDefaults` (`heightUnits` 42, `maxPowerWatts` 12000 — five 3U eight-node MicroCloud chassis at ~2 kW plus a leaf pair no longer fit in 10 kW) seed racks added to the partition and apply to the central rack; each rack keeps its own `heightUnits` / `maxPowerWatts` in its Advanced section, which also holds the rarely changed leaf model and leaf count and a rack group's member names. The central rack's Advanced section likewise holds the internet router and storage leaf counts, its hardware models (spine, superspine, storage leaf, mgmt spine/leaf/server) and mgmt leaves per rack, grouped with the network OS, fabric links and rack defaults; the section header shows the chosen models read-only. Issues that can only be fixed in either Advanced section carry `target.field: 'advanced'`, and `navigateTo()` unfolds that section (`details[data-advanced]`) when revealing them. Validation reports an error per physical rack whose estimated draw exceeds its budget. - **Info bubbles.** `views/plan/InfoBubble.tsx` renders the "i" bubbles; `SelectField` / `NumberField` take an `info` prop. Texts must match the metal-stack docs, and links come from `views/plan/docs.ts` — only pages verified to exist on docs.metal-stack.io. The partition's central components are edited in `CentralRackSection.tsx` (the UI says "Central rack", the schema still calls it `fabric`). - **IP plan.** Inputs live in `Plan.ipPlan` (`src/model/ipPlan.ts`: per-family schema, infrastructure inputs, presets). `src/derive/ip/cidr.ts` is the BigInt CIDR library (no dependency); `src/derive/ip/ipPlan.ts` derives limits (each with a formula string), the partition allocation (sequential, skipping slots that overlap Kubernetes, reserve, internet or infrastructure ranges — an internet range containing the whole project CIDR is by design), the example cluster and the infrastructure subnets; `src/derive/ip/validateIp.ts` reports issues with `target: { section: 'ips', field }`, which `navigateTo()` opens in the IPs tab. Infrastructure sizing rules are in the header of `ipPlan.ts`. Presets mirror the layouts of the untracked `ip_calc.ods`; their public ranges must be documentation ranges (RFC 5737 / RFC 3849), never real allocations. `ipPlanToCsv()` in `src/io/ipCsv.ts` exports the address plan. -- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews (YAML/INI highlighting and README rendering from the tokenizers in `views/ansible/syntax.ts`, rendered as React elements, never as HTML strings; colors are the `--color-code-*` theme tokens) and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. `src/derive/ports.ts` lays out each switch's BGP ports (uplinks on the last ports of their speed, downlinks from the first, in plan order) from the catalog's SONiC port maps (`ports[].sonic`, from sonic-buildimage's `port_config.ini`; only add maps from there, `sonicPortNames()` returns null otherwise). metal-api and NSQ are addressed under `metal_control_plane_ingress_dns` (`api.`, `:4150`, the control plane role defaults), the pixiecore API at the mgmt server's router ID, and the BMC superuser follows go-hal's per-vendor name (`BMC_SUPERUSER` in the catalog). What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`, which follows YAML 1.1 quoting (`"on"`, `"yes"`), as Ansible and GitHub read it. `derive/ansible/ci.ts` adds the pipelines (`Plan.deployment.ci.platform`: GitLab CI by default, GitHub Actions, both or none): a check on every push (inventory, `--syntax-check`, no `CHANGE_ME` left in `inventories/`) and manual deploys per partition in `DEPLOY_STEPS` order (mgmt servers, mgmt network, prod network) on a runner tagged per partition, one resource group / concurrency group each. Credentials stay CI secrets that Ansible reads through `ANSIBLE_PRIVATE_KEY_FILE` / `ANSIBLE_VAULT_PASSWORD_FILE`. The settings card shows only what differs per installation and has no default (release, control plane domain); everything with a default (public name and NTP servers as in mini-lab / metal-roles, inventory, role versions, the pipeline platform and its settings) sits in its Advanced section. +- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews (YAML/INI highlighting and README rendering from the tokenizers in `views/ansible/syntax.ts`, rendered as React elements, never as HTML strings; colors are the `--color-code-*` theme tokens) and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. `src/derive/ports.ts` lays out each switch's ports once for both NOS paths: 4x25G breakouts for 2x25G server ports on the leaves' first 100G ports, uplinks on the last ports of their speed, downlinks from the first, in plan order, with speed, MTU (9216 fabric, 9000 servers and mgmt) and RS-FEC on server ports, and the BGP ports; from the catalog's SONiC port maps (`ports[].sonic`, from sonic-buildimage's `port_config.ini`; only add maps from there, `sonicPortNames()` returns null otherwise). metal-api and NSQ are addressed under `metal_control_plane_ingress_dns` (`api.`, `:4150`, the control plane role defaults), the pixiecore API at the mgmt server's router ID, and the BMC superuser follows go-hal's per-vendor name (`BMC_SUPERUSER` in the catalog). Switches follow their partition's NOS (`FabricConfig.nos`, inventory groups `edgecore_sonic` / `broadcom_sonic`): Edgecore SONiC through metal-roles' `sonic-config` (`sonic_config_*`), Broadcom Enterprise SONiC through the Dell collection `dellemc.enterprise_sonic` over httpapi (`derive/ansible/enterpriseSonic.ts`): one `sonic_` variable per module holding its `config`, one generated task per module under `tasks/enterprise_sonic/`, `replaced` for plan-described topics, checked against the collection's argspecs, plus an SSH play for DNS and the leaves' split routing mode. The leaves' FRR is always metal-core's; every other BGP switch gets FRR from exactly one source (`nos.test.ts`), and every leaf carries PXE VLAN 4000 with DHCP relayed to the mgmt servers, whose `dhcp_subnets` lease each leaf's PXE network. What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`, which follows YAML 1.1 quoting (`"on"`, `"yes"`), as Ansible and GitHub read it. `derive/ansible/ci.ts` adds the pipelines (`Plan.deployment.ci.platform`: GitLab CI by default, GitHub Actions, both or none): a check on every push (inventory, `--syntax-check`, no `CHANGE_ME` left in `inventories/`) and manual deploys per partition in `DEPLOY_STEPS` order (mgmt servers, mgmt network, prod network) on a runner tagged per partition, one resource group / concurrency group each. Credentials stay CI secrets that Ansible reads through `ANSIBLE_PRIVATE_KEY_FILE` / `ANSIBLE_VAULT_PASSWORD_FILE`. The settings card shows only what differs per installation and has no default (release, control plane domain); everything with a default (public name and NTP servers as in mini-lab / metal-roles, inventory, role versions, the pipeline platform and its settings) sits in its Advanced section. - **Icons.** Icons come from `src/views/icons/` only: `index.tsx` is the registry (Lucide icons plus the custom `NetworkSwitch`, `Rack`, `RackGroup` glyphs from `custom.ts`, drawn on Lucide's 24 px grid) with typed maps per tab, node kind, BOM category, rack slot kind and severity, and an `` helper (decorative, `aria-hidden`). An ESLint rule forbids importing `lucide-react` anywhere else. Sizes: 16 px in tabs and buttons, 14 px inline and in topology nodes, 12 px in rack slots; color follows the text. - **Templates.** `src/model/templates.ts` holds the starting plans offered by the Templates menu. They must order only hardware that is current at the vendor — `templates.test.ts` fails on any `availability` of `eol` / `withdrawn`, for the templates and for the default plan. Each `build()` returns a fresh Plan with new ids; `templates.test.ts` asserts every template parses and has no validation errors, so keep them valid when capacity rules change. - **Persistence and format version.** Zustand store (`src/store/planStore.ts`) with `persist` middleware → localStorage key `metal-stack-planner/plan`. JSON export/import in `src/io/json.ts`; every import runs through `migrateRawPlan()` (`src/model/migrate.ts`) and is then validated with `PlanSchema.parse`, and the same path runs on rehydrate. `SCHEMA_VERSION` in `migrate.ts` is the plan file format version — currently 1 with no migrations. Purely additive fields only need a Zod `.default()`; anything that breaks older files bumps `SCHEMA_VERSION` and adds a function to `MIGRATIONS`, keyed by the version it upgrades _from_. Files from a newer version are refused with a readable message. diff --git a/README.md b/README.md index 8d844d2..8eb83c4 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,11 @@ consistent — you edit the plan, everything else follows from it. metal-core, mgmt-server, dhcp, metal-bmc, pixiecore, image-cache) and the playbooks that apply them, laid out like a metal-stack deployment repository. Derived from the plan and the IP plan: hostnames, ASNs (leaves unique, spines and exits shared), loopbacks, management addresses, - per-leaf PXE networks, DHCP ranges, transfer networks and each switch's BGP ports (uplinks on - the last ports, from the SONiC port maps in the catalog). Only the metal-stack release and the + per-leaf PXE networks with DHCP relay, DHCP ranges, transfer networks and each switch's ports: + 4x25G breakouts for 25G servers, speed, MTU and FEC, and the BGP ports (uplinks on the last + ports, from the SONiC port maps in the catalog). Switches follow their partition's NOS: + Edgecore SONiC through metal-roles' sonic-config, Broadcom Enterprise SONiC through the + `dellemc.enterprise_sonic` collection, one module call per topic. Only the metal-stack release and the control plane domain need entering (metal-api and NSQ are reached under it); name and NTP servers default to public ones, and everything else with a default sits under Advanced. What the plan cannot know, the secrets and certificates, is marked `CHANGE_ME` and listed. A GitLab diff --git a/src/derive/ansible/ansible.test.ts b/src/derive/ansible/ansible.test.ts index 493e4d0..c0b1738 100644 --- a/src/derive/ansible/ansible.test.ts +++ b/src/derive/ansible/ansible.test.ts @@ -68,9 +68,11 @@ describe('Ansible export', () => { }) it('defaults to public name and NTP servers, leaving release and metal-api open', () => { - const { files, placeholders } = deriveAnsible(createEmptyPlan()) + const plan = createEmptyPlan() + plan.partitions[0].fabric.nos = 'edgecore-sonic' + const { files, placeholders } = deriveAnsible(plan) const switches = parse( - files.find((f) => f.path.endsWith('group_vars/partition/switches.yaml'))!.content, + files.find((f) => f.path.endsWith('group_vars/edgecore_sonic/sonic-config.yaml'))!.content, ) expect(switches.sonic_config_nameservers).toEqual(['1.1.1.1', '8.8.8.8']) expect(switches.sonic_config_ntp.servers[0]).toBe('0.europe.pool.ntp.org') @@ -83,6 +85,7 @@ describe('Ansible export', () => { it('uses the deployment settings', () => { const plan = createEmptyPlan() + plan.partitions[0].fabric.nos = 'edgecore-sonic' plan.deployment = { ...plan.deployment, environment: 'Staging', @@ -95,7 +98,7 @@ describe('Ansible export', () => { expect(file('staging/group_vars/all/release_vector.yaml').metal_stack_release_version).toBe( 'v0.21.6', ) - expect(file('group_vars/partition/switches.yaml')).toMatchObject({ + expect(file('group_vars/edgecore_sonic/sonic-config.yaml')).toMatchObject({ sonic_config_nameservers: ['192.0.2.53'], sonic_config_ntp: { servers: ['192.0.2.123'] }, }) @@ -139,6 +142,13 @@ describe('Ansible export', () => { children: partition_1_exits: storageleaves: {} + broadcom_sonic: + children: + partition_1_mgmtleaves: + partition_1_mgmtspines: + partition_1_leaves: + partition_1_spines: + partition_1_exits: # Partition 1 partition_1: children: diff --git a/src/derive/ansible/enterpriseSonic.ts b/src/derive/ansible/enterpriseSonic.ts new file mode 100644 index 0000000..048a016 --- /dev/null +++ b/src/derive/ansible/enterpriseSonic.ts @@ -0,0 +1,429 @@ +import type { Device } from '../devices' +import type { DeviceAddresses } from '../ip/deviceAddresses' +import type { SwitchPorts } from '../ports' +import type { AnsibleOut } from './index' +import { fromObject, kv, note, toYaml, toYamlSeq, ymap, type YEntry, type YValue } from './yaml' + +// Broadcom Enterprise SONiC: metal-roles' sonic-config only supports +// Edgecore SONiC, so these switches are configured with the Dell collection +// from Ansible Galaxy (dellemc.enterprise_sonic), whose resource modules +// talk to the SONiC management framework over httpapi. Argument specs were +// checked against collection 4.1.0. +// +// The structure follows a deployment running this collection on Enterprise +// SONiC: one variable per module named after it (sonic_, holding +// exactly that module's `config`), one task per module in the order the +// config builds up, `replaced` for what the plan fully describes and +// `merged` for global settings. The route map LOOPBACKS limits what BGP +// redistributes to the loopback; peers use metal-stack's timers (1/3/10). +// +// What the modules don't cover, DNS servers and the routing mode metal-core +// needs on leaves, runs over SSH in config_db, as mini-lab does. The +// leaves' FRR is written by metal-core, so they get no BGP here. The port +// plan is the same as on the Edgecore path (derive/ports.ts); interface +// naming is set to native so its EthernetN names hold. + +export const ENTERPRISE_SONIC_COLLECTION = 'dellemc.enterprise_sonic' +const PEER_GROUP = 'FABRIC' +const ROUTE_MAP = 'LOOPBACKS' + +const SPEED: Record = { + 1000: 'SPEED_1GB', + 10000: 'SPEED_10GB', + 25000: 'SPEED_25GB', + 100000: 'SPEED_100GB', +} + +interface Step { + module: string + state: 'replaced' | 'merged' + /** One call per list item (route maps, as the collection needs it). */ + perItem?: boolean +} + +/** One task per module, in the order the configuration builds up. */ +export const TOPICS: { topic: string; label: string; steps: Step[] }[] = [ + { + topic: 'breakouts', + label: 'Port breakouts', + steps: [{ module: 'port_breakout', state: 'replaced' }], + }, + { + topic: 'system', + label: 'Hostname, native interface naming, no auto-breakout', + steps: [{ module: 'system', state: 'merged' }], + }, + { topic: 'lldp', label: 'LLDP', steps: [{ module: 'lldp_global', state: 'merged' }] }, + { + topic: 'interfaces', + label: 'Port speed, FEC and MTU; loopback', + steps: [{ module: 'interfaces', state: 'replaced' }], + }, + { topic: 'vlans', label: 'VLANs', steps: [{ module: 'vlans', state: 'replaced' }] }, + { + topic: 'l3', + label: 'Loopback, VLAN and BGP port addresses', + steps: [{ module: 'l3_interfaces', state: 'replaced' }], + }, + { + topic: 'dhcp-relay', + label: 'DHCP relay of the PXE VLAN', + steps: [{ module: 'dhcp_relay', state: 'replaced' }], + }, + { + topic: 'route-maps', + label: 'Route maps', + steps: [{ module: 'route_maps', state: 'merged', perItem: true }], + }, + { + topic: 'bgp', + label: 'BGP', + steps: [ + { module: 'bgp', state: 'replaced' }, + { module: 'bgp_af', state: 'merged' }, + { module: 'bgp_neighbors', state: 'replaced' }, + ], + }, + { topic: 'vxlans', label: 'VTEP', steps: [{ module: 'vxlans', state: 'replaced' }] }, + { topic: 'ntp', label: 'NTP', steps: [{ module: 'ntp', state: 'merged' }] }, + // Last: switching to the mgmt VRF restarts the management services. + { topic: 'vrfs', label: 'Management VRF', steps: [{ module: 'vrfs', state: 'merged' }] }, +] + +export const moduleVar = (module: string) => `sonic_${module}` + +export interface EnterpriseSonicHost { + device: Device + addresses: DeviceAddresses + ports: SwitchPorts | undefined + /** Runs BGP (production switches; mgmt switches of an L3 mgmt network). */ + bgp: boolean + /** Activates EVPN (spines, superspines, exits, storage leaves). */ + evpn: boolean + /** Mgmt VRF on (every switch but the leaves, as metal-stack runs them). */ + mgmtVrf: boolean + /** Mgmt servers' router ids, the leaves relay PXE DHCP to them. */ + dhcpServers: string[] + ntpServers: string[] +} + +const cfg = (module: string, value: YValue, comment?: string) => + kv(moduleVar(module), value, comment) + +/** The sonic_ host variables of a Broadcom SONiC switch. */ +export function enterpriseSonicHostVars(h: EnterpriseSonicHost): YEntry[] { + const { device: d, addresses: a, ports: sp } = h + const leaf = d.role === 'leaf' + const vtep = leaf || d.role === 'exit' + const planned = sp && !sp.reason ? sp : undefined + const out: YEntry[] = [] + + if (planned && planned.breakouts.length > 0) { + out.push( + cfg( + 'port_breakout', + planned.breakouts.map((b) => ymap(kv('name', `1/${b.frontPanel}`), kv('mode', b.mode))), + `Front-panel ports ${planned.breakouts.map((b) => b.port).join(', ')}.`, + ), + ) + } + if (sp?.reason) out.push(note(`Port settings left open: ${sp.reason}.`)) + out.push( + cfg( + 'system', + ymap( + kv('hostname', d.hostname), + kv('interface_naming', 'native'), + kv('auto_breakout', 'DISABLE'), + ), + ), + ) + + const interfaces: YValue[] = [] + if (h.bgp && a.loopback) { + interfaces.push( + ymap(kv('name', 'Loopback0'), kv('description', 'Router-ID'), kv('enabled', true)), + ) + } + for (const p of planned?.ports ?? []) { + interfaces.push( + ymap( + kv('name', p.name), + kv('description', p.use), + kv('enabled', true), + kv('speed', SPEED[p.speed] ?? `SPEED_${p.speed / 1000}GB`), + ...(p.fec === 'rs' ? [kv('fec', 'FEC_RS')] : []), + kv('mtu', p.mtu), + ), + ) + } + if (interfaces.length > 0) out.push(cfg('interfaces', interfaces)) + + if (vtep && a.pxe) { + out.push(cfg('vlans', [ymap(kv('vlan_id', 4000), kv('description', 'metal-stack PXE'))])) + } + + const l3: YValue[] = [] + if (h.bgp && a.loopback) { + l3.push( + ymap( + kv('name', 'Loopback0'), + kv('ipv4', ymap(kv('addresses', [ymap(kv('address', `${a.loopback}/32`))]))), + ), + ) + } + if (vtep && a.pxe) { + l3.push( + ymap(kv('name', 'Vlan4000'), kv('ipv4', ymap(kv('addresses', [ymap(kv('address', a.pxe))])))), + ) + } + // BGP unnumbered peers over IPv6 link-local addresses. + const bgpPorts = h.bgp && !leaf ? (planned?.bgp ?? []) : [] + for (const port of bgpPorts) + l3.push(ymap(kv('name', port), kv('ipv6', ymap(kv('enabled', true))))) + if (l3.length > 0) out.push(cfg('l3_interfaces', l3)) + + if (leaf && a.pxe && h.dhcpServers.length > 0) { + out.push( + cfg( + 'dhcp_relay', + [ + ymap( + kv('name', 'Vlan4000'), + kv( + 'ipv4', + ymap( + kv('link_select', true), + kv( + 'server_addresses', + h.dhcpServers.map((s) => ymap(kv('address', s))), + ), + kv('source_interface', 'Loopback0'), + ), + ), + ), + ], + 'PXE DHCP goes to the mgmt servers, relayed from the leaf loopback.', + ), + ) + } + + // metal-core writes the leaves' FRR. + if (h.bgp && !leaf && d.asn && a.loopback && planned) { + const asn = String(d.asn) + const afis = [ + ymap(kv('activate', true), kv('afi', 'ipv4'), kv('safi', 'unicast')), + ...(h.evpn ? [ymap(kv('activate', true), kv('afi', 'l2vpn'), kv('safi', 'evpn'))] : []), + ] + out.push( + cfg('route_maps', [ + ymap( + kv('map_name', ROUTE_MAP), + kv('action', 'permit'), + kv('match', ymap(kv('interface', 'Loopback0'))), + kv('sequence_num', 10), + ), + ]), + cfg('bgp', [ + ymap( + kv('bgp_as', asn), + kv('bestpath', ymap(kv('as_path', ymap(kv('multipath_relax', true))))), + kv('log_neighbor_changes', true), + kv('router_id', a.loopback), + ), + ]), + cfg('bgp_af', [ + ymap( + kv('bgp_as', asn), + kv( + 'address_family', + ymap( + kv('afis', [ + ymap( + kv('afi', 'ipv4'), + kv('safi', 'unicast'), + kv('redistribute', [ + ymap(kv('protocol', 'connected'), kv('route_map', ROUTE_MAP)), + ]), + ), + ...(h.evpn + ? [ + ymap( + kv('afi', 'l2vpn'), + kv('safi', 'evpn'), + // Only a VTEP advertises its VNIs. + ...(vtep + ? [kv('advertise_all_vni', true), kv('advertise_default_gw', true)] + : []), + ), + ] + : []), + ]), + ), + ), + ), + ]), + cfg( + 'bgp_neighbors', + [ + ymap( + kv('bgp_as', asn), + kv( + 'neighbors', + bgpPorts.map((port) => + ymap( + kv('neighbor', port), + kv('capability', ymap(kv('extended_nexthop', true))), + kv('peer_group', PEER_GROUP), + kv('remote_as', ymap(kv('peer_type', 'external'))), + ), + ), + ), + kv('peer_group', [ + ymap( + kv('name', PEER_GROUP), + kv('address_family', ymap(kv('afis', afis))), + kv('remote_as', ymap(kv('peer_type', 'external'))), + kv('timers', ymap(kv('connect_retry', 10), kv('holdtime', 3), kv('keepalive', 1))), + ), + ]), + ), + ], + 'BGP unnumbered on the fabric ports.', + ), + ) + } + + if (vtep && a.loopback) { + out.push( + cfg('vxlans', [ + ymap(kv('name', 'vtep1'), kv('source_ip', a.loopback), kv('evpn_nvo', 'nvo1')), + ]), + ) + } + if (h.ntpServers.length > 0) { + out.push( + cfg( + 'ntp', + ymap( + kv( + 'servers', + h.ntpServers.map((s) => ymap(kv('address', s))), + ), + ...(h.mgmtVrf ? [kv('vrf', 'mgmt')] : []), + ), + ), + ) + } + if (h.mgmtVrf) out.push(cfg('vrfs', [ymap(kv('name', 'mgmt'))])) + return out +} + +function task(label: string, many: boolean, step: Step): YValue { + const v = moduleVar(step.module) + return fromObject({ + name: many ? `${label}: ${step.module}` : label, + [`${ENTERPRISE_SONIC_COLLECTION}.sonic_${step.module}`]: fromObject({ + config: step.perItem ? ['{{ item }}'] : `{{ ${v} }}`, + state: step.state, + }), + ...(step.perItem ? { loop: `{{ ${v} }}` } : {}), + when: `${v} is defined`, + }) +} + +/** Task files, one per topic, one module call per task. */ +export function enterpriseSonicTaskFiles(out: AnsibleOut): void { + for (const { topic, label, steps } of TOPICS) { + out.add( + `tasks/enterprise_sonic/${topic}.yaml`, + toYamlSeq( + steps.map((s) => task(label, steps.length > 1, s)), + `Generated by the metal-stack planner. ${label} with ${ENTERPRISE_SONIC_COLLECTION}.`, + ), + ) + } + // What the modules don't cover, over SSH in config_db (as mini-lab does). + out.add( + 'tasks/enterprise_sonic/ssh.yaml', + toYamlSeq( + [ + fromObject({ + name: 'DNS servers', + 'ansible.builtin.command': `sonic-db-cli CONFIG_DB hset "DNS_SERVER|{{ item }}" NULL NULL`, + loop: '{{ sonic_nameservers }}', + changed_when: false, + }), + fromObject({ + name: 'Routing config mode of a leaf', + 'ansible.builtin.command': + 'sonic-db-cli CONFIG_DB hget "DEVICE_METADATA|localhost" docker_routing_config_mode', + register: 'routing_mode', + changed_when: false, + when: "'leaves' in group_names", + }), + fromObject({ + name: 'Split routing config mode, metal-core writes frr.conf', + 'ansible.builtin.command': + 'sonic-db-cli CONFIG_DB hset "DEVICE_METADATA|localhost" docker_routing_config_mode split', + when: "'leaves' in group_names and routing_mode.stdout != 'split'", + notify: 'Restart bgp', + }), + fromObject({ + name: 'Save the configuration', + 'ansible.builtin.command': 'config save -y', + changed_when: false, + }), + ], + 'Generated by the metal-stack planner. Settings the Enterprise SONiC modules do not cover.', + ), + ) +} + +/** group_vars/broadcom_sonic: connection of the modules and plan-wide values. */ +export function enterpriseSonicGroupVars(out: AnsibleOut, nameservers: string[]): void { + const dir = `${out.inventory}/group_vars/broadcom_sonic` + out.add( + `${dir}/connection.yaml`, + toYaml( + ymap( + kv('ansible_connection', 'ansible.netcommon.httpapi'), + kv('ansible_network_os', `${ENTERPRISE_SONIC_COLLECTION}.sonic`), + kv('ansible_user', 'admin', 'The switch user of the management REST API.'), + kv('ansible_httpapi_use_ssl', true), + kv('ansible_httpapi_validate_certs', false, 'The switches serve self-signed certificates.'), + ), + `Broadcom Enterprise SONiC, configured with ${ENTERPRISE_SONIC_COLLECTION} over httpapi.`, + ), + ) + const f = `${dir}/secrets.yaml` + out.add( + f, + toYaml( + ymap( + kv( + 'ansible_password', + out.todo(f, 'ansible_password', 'password of the Broadcom SONiC switches (REST API)'), + ), + ), + 'Secrets: fill in and encrypt this file with ansible-vault before committing it.', + ), + ) + out.add( + `${dir}/system.yaml`, + toYaml( + ymap( + kv( + moduleVar('lldp_global'), + ymap(kv('enable', true), kv('hello_time', 10)), + 'metal-stack learns which machine port sits on which switch port over LLDP.', + ), + kv( + 'sonic_nameservers', + nameservers, + 'Written to config_db over SSH (tasks/enterprise_sonic/ssh.yaml).', + ), + ), + ), + ) +} diff --git a/src/derive/ansible/inventory.ts b/src/derive/ansible/inventory.ts index 91d1f76..4755d76 100644 --- a/src/derive/ansible/inventory.ts +++ b/src/derive/ansible/inventory.ts @@ -1,3 +1,4 @@ +import type { Nos } from '../../model/plan' import type { DeviceRole, PartitionDevices } from '../devices' import type { AnsibleContext } from './index' import { kv, toYaml, ymap, type YEntry, type YMap } from './yaml' @@ -48,13 +49,43 @@ function partitionEntry(p: PartitionDevices): YEntry { return kv(p.group, ymap(kv('children', ymap(...children))), p.partitionName) } -export function inventoryFile({ devices, out }: AnsibleContext): void { +/** Switch roles, grouped by the partition's NOS. */ +export const SWITCH_ROLES: DeviceRole[] = [ + 'mgmt-leaf', + 'mgmt-spine', + 'leaf', + 'spine', + 'superspine', + 'exit', + 'storage-leaf', +] + +/** NOS groups: which switches sonic-config configures (Edgecore SONiC) and + * which the Dell collection (Broadcom SONiC). */ +export const NOS_GROUP: Record = { + 'edgecore-sonic': 'edgecore_sonic', + 'broadcom-sonic': 'broadcom_sonic', +} + +export function inventoryFile({ plan, devices, out }: AnsibleContext): void { const functional = ROLES.map((role) => { const members = devices .filter((p) => p.devices.some((d) => d.role === role)) .map((p) => kv(partitionGroup(p, role), null)) return kv(ROLE_GROUP[role], members.length ? ymap(kv('children', ymap(...members))) : ymap()) }) + const nosGroups = (Object.keys(NOS_GROUP) as Nos[]) + .map((nos) => { + const members = devices + .filter((_, i) => plan.partitions[i].fabric.nos === nos) + .flatMap((p) => + SWITCH_ROLES.filter((role) => p.devices.some((d) => d.role === role)).map((role) => + kv(partitionGroup(p, role), null), + ), + ) + return members.length > 0 && kv(NOS_GROUP[nos], ymap(kv('children', ymap(...members)))) + }) + .filter((e): e is YEntry => !!e) out.add( `${out.inventory}/inventory.yaml`, toYaml( @@ -67,7 +98,12 @@ export function inventoryFile({ devices, out }: AnsibleContext): void { ymap( kv( 'partition', - ymap(kv('children', ymap(...functional, ...devices.map(partitionEntry)))), + ymap( + kv( + 'children', + ymap(...functional, ...nosGroups, ...devices.map(partitionEntry)), + ), + ), ), ), ), diff --git a/src/derive/ansible/nos.test.ts b/src/derive/ansible/nos.test.ts new file mode 100644 index 0000000..be181a0 --- /dev/null +++ b/src/derive/ansible/nos.test.ts @@ -0,0 +1,284 @@ +import { describe, expect, it } from 'vitest' +import { parse } from 'yaml' +import { createEmptyPlan, defaultPartition } from '../../model/defaults' +import type { Nos, Plan } from '../../model/plan' +import { deriveAnsible } from './index' + +/** Two partitions, one per NOS. */ +function mixed(): Plan { + const plan = createEmptyPlan() + plan.partitions[0].fabric.nos = 'edgecore-sonic' + const second = defaultPartition('Partition 2') + second.fabric.nos = 'broadcom-sonic' + plan.partitions.push(second) + return plan +} + +function withNos(nos: Nos): Plan { + const plan = createEmptyPlan() + plan.partitions[0].fabric.nos = nos + return plan +} + +const read = (plan: Plan, path: string) => { + const f = deriveAnsible(plan).files.find((f) => f.path === path) + return f ? parse(f.content) : undefined +} +const host = (plan: Plan, name: string) => read(plan, `inventories/prod/host_vars/${name}.yaml`) + +describe('Edgecore SONiC path (sonic-config)', () => { + const plan = withNos('edgecore-sonic') + + it('breaks out and sets the ports like the reference deployment', () => { + const leaf = host(plan, 'partition-1-r01leaf01') + expect(leaf.sonic_config_breakouts).toEqual({ Ethernet0: '4x25G', Ethernet4: '4x25G' }) + expect(leaf.sonic_config_ports).toEqual({ + default_mtu: 9216, + list: [ + { name: 'Ethernet120', speed: 100000 }, + { name: 'Ethernet124', speed: 100000 }, + ], + }) + const mgmtLeaf = host(plan, 'partition-1-r01mgmtleaf') + expect(mgmtLeaf.sonic_config_ports.default_mtu).toBe(9000) + expect(mgmtLeaf.sonic_config_ports.list.at(-1)).toEqual({ name: 'Ethernet51', speed: 25000 }) + }) + + it('puts the PXE VLAN on the leaves, relayed to the mgmt servers', () => { + const leaf = host(plan, 'partition-1-r01leaf01') + const servers = ['mgmtserver01', 'mgmtserver02'].map( + (m) => host(plan, `partition-1-${m}`).mgmt_server_router_id, + ) + expect(leaf.sonic_config_vlans).toEqual([ + { id: 4000, ip: leaf.metal_core_cidr, dhcp_servers: servers }, + ]) + const leaves = read(plan, 'inventories/prod/group_vars/partition_1_leaves/sonic.yaml') + expect(leaves).toMatchObject({ + sonic_config_vtep: { enabled: true }, + sonic_config_mgmt_vrf: false, + }) + }) + + it('uses only sonic-config plays', () => { + const prod = read(plan, 'deploy_prod_network.yaml') + expect(prod.map((p: { hosts: string }) => p.hosts)).toEqual([ + 'superspines:spines:exits:storageleaves:&edgecore_sonic', + 'leaves:&edgecore_sonic', + 'leaves', + ]) + expect(deriveAnsible(plan).files.some((f) => f.path.startsWith('tasks/'))).toBe(false) + }) +}) + +describe('Broadcom SONiC path (dellemc.enterprise_sonic)', () => { + const plan = withNos('broadcom-sonic') + + it('configures a spine topic by topic', () => { + const spine = host(plan, 'partition-1-spine01') + expect(Object.keys(spine).filter((k) => k.startsWith('sonic_config_'))).toEqual([]) + expect(spine.sonic_system).toEqual({ + hostname: 'partition-1-spine01', + interface_naming: 'native', + auto_breakout: 'DISABLE', + }) + expect(spine.sonic_interfaces[0]).toEqual({ + name: 'Loopback0', + description: 'Router-ID', + enabled: true, + }) + expect(spine.sonic_interfaces[1]).toMatchObject({ + name: 'Ethernet0', + speed: 'SPEED_100GB', + mtu: 9216, + enabled: true, + }) + const [neighbors] = spine.sonic_bgp_neighbors + expect(neighbors.bgp_as).toBe('4200020000') + expect(neighbors.neighbors.map((n: { neighbor: string }) => n.neighbor)).toEqual([ + 'Ethernet0', + 'Ethernet4', + 'Ethernet8', + 'Ethernet12', + ]) + expect(neighbors.peer_group[0].address_family.afis.map((a: { afi: string }) => a.afi)).toEqual([ + 'ipv4', + 'l2vpn', + ]) + expect(neighbors.peer_group[0].timers).toEqual({ connect_retry: 10, holdtime: 3, keepalive: 1 }) + expect(neighbors.neighbors[0].capability).toEqual({ extended_nexthop: true }) + // Only the loopback is redistributed; a spine is no VTEP. + const [af] = spine.sonic_bgp_af + expect(af.address_family.afis[0].redistribute).toEqual([ + { protocol: 'connected', route_map: 'LOOPBACKS' }, + ]) + expect(af.address_family.afis[1]).toEqual({ afi: 'l2vpn', safi: 'evpn' }) + expect(spine.sonic_route_maps[0]).toMatchObject({ + map_name: 'LOOPBACKS', + match: { interface: 'Loopback0' }, + }) + expect(spine.sonic_vrfs).toEqual([{ name: 'mgmt' }]) + expect(spine.sonic_ntp.vrf).toBe('mgmt') + }) + + it('prepares the leaves for metal-core', () => { + const leaf = host(plan, 'partition-1-r01leaf01') + expect(leaf.sonic_port_breakout).toEqual([ + { name: '1/1', mode: '4x25G' }, + { name: '1/2', mode: '4x25G' }, + ]) + expect(leaf.sonic_vlans).toEqual([{ vlan_id: 4000, description: 'metal-stack PXE' }]) + // Server ports per breakout lane, RS-FEC and MTU 9000. + const servers = leaf.sonic_interfaces.filter( + (i: { description: string }) => i.description === 'servers', + ) + expect(servers).toHaveLength(8) + expect(servers[0]).toEqual({ + name: 'Ethernet0', + description: 'servers', + enabled: true, + speed: 'SPEED_25GB', + fec: 'FEC_RS', + mtu: 9000, + }) + expect(leaf.sonic_dhcp_relay[0]).toMatchObject({ + name: 'Vlan4000', + ipv4: { source_interface: 'Loopback0', link_select: true }, + }) + expect(leaf.sonic_vxlans[0]).toMatchObject({ + name: 'vtep1', + source_ip: leaf.lo, + evpn_nvo: 'nvo1', + }) + // metal-core renders the leaves' FRR, the mgmt VRF stays off. + expect(leaf.sonic_bgp).toBeUndefined() + expect(leaf.sonic_vrfs).toBeUndefined() + expect(leaf.metal_core_spine_uplinks).toEqual(['Ethernet120', 'Ethernet124']) + }) + + it('writes the topic tasks, the SSH play and the collection requirement', () => { + const { files, placeholders } = deriveAnsible(plan) + const bgp = read(plan, 'tasks/enterprise_sonic/bgp.yaml') + expect( + bgp.map((t: Record) => { + const module = Object.keys(t)[1] + return [module, t[module].state] + }), + ).toEqual([ + ['dellemc.enterprise_sonic.sonic_bgp', 'replaced'], + ['dellemc.enterprise_sonic.sonic_bgp_af', 'merged'], + ['dellemc.enterprise_sonic.sonic_bgp_neighbors', 'replaced'], + ]) + const prodTopics = read(plan, 'deploy_prod_network.yaml')[0].tasks.map( + (t: { tags: string[] }) => t.tags[0], + ) + expect(prodTopics).toEqual([ + 'breakouts', + 'system', + 'lldp', + 'interfaces', + 'vlans', + 'l3', + 'dhcp-relay', + 'route-maps', + 'bgp', + 'vxlans', + 'ntp', + 'vrfs', + ]) + const prod = read(plan, 'deploy_prod_network.yaml') + expect(prod.map((p: { hosts: string }) => p.hosts)).toEqual([ + 'superspines:spines:exits:storageleaves:leaves:&broadcom_sonic', + 'superspines:spines:exits:storageleaves:leaves:&broadcom_sonic', + 'leaves', + ]) + expect(prod[1].vars).toEqual({ ansible_connection: 'ansible.builtin.ssh' }) + expect(prod[2].vars).toEqual({ ansible_connection: 'ansible.builtin.ssh' }) + const req = read(plan, 'requirements.yaml') + expect(req.collections.map((c: { name: string }) => c.name)).toContain( + 'dellemc.enterprise_sonic', + ) + expect(files.some((f) => f.path.endsWith('group_vars/broadcom_sonic/connection.yaml'))).toBe( + true, + ) + expect(placeholders.filter((p) => p.key === 'ansible_password')).toHaveLength(1) + }) +}) + +describe('mixed NOS and PXE DHCP', () => { + it('groups each partition by its NOS', () => { + const inventory = read(mixed(), 'inventories/prod/inventory.yaml') + const groups = inventory.all.children.partition.children + expect(Object.keys(groups.edgecore_sonic.children)).toContain('partition_1_leaves') + expect(Object.keys(groups.broadcom_sonic.children)).toContain('partition_2_leaves') + expect(Object.keys(groups.broadcom_sonic.children)).not.toContain('partition_1_leaves') + }) + + it('leases every leaf PXE network from the mgmt servers', () => { + const dhcp = read( + createEmptyPlan(), + 'inventories/prod/group_vars/partition_1_mgmtservers/dhcp.yaml', + ) + const pxe = dhcp.dhcp_subnets.filter((s: { comment: string }) => s.comment.startsWith('PXE')) + expect(pxe).toHaveLength(2) + expect(pxe[0]).toEqual({ + comment: 'PXE network of partition-1-r01leaf01', + network: '172.16.0.32', + netmask: '255.255.255.224', + range: { begin: '172.16.0.34', end: '172.16.0.62' }, + options: ['routers 172.16.0.33'], + }) + // The mgmt loopback network dhcpd listens on, without leases. + expect(dhcp.dhcp_subnets[0].range).toBeUndefined() + }) +}) + +const TEMPLATE_IDS = ['starter', 'redundant', 'three-partitions'] as const + +describe('every switch and leaf, both NOS', () => { + it.each( + TEMPLATE_IDS.flatMap((id) => + (['edgecore-sonic', 'broadcom-sonic'] as const).map((nos) => ({ id, nos })), + ), + )( + 'gets FRR from exactly one source and relays PXE DHCP on every leaf ($id, $nos)', + async ({ id, nos }) => { + const { templates } = await import('../../model/templates') + const plan = templates.find((t) => t.id === id)!.build() + for (const p of plan.partitions) p.fabric.nos = nos + const { files, devices } = deriveAnsible(plan) + const vars = (h: string) => + parse(files.find((f) => f.path === `inventories/prod/host_vars/${h}.yaml`)!.content) + const group = (g: string) => { + const f = files.find((f) => f.path === `inventories/prod/group_vars/${g}/sonic.yaml`) + return f ? parse(f.content) : {} + } + devices.forEach((p, i) => { + const l3 = plan.partitions[i].fabric.mgmt.layer === 'l3' + for (const d of p.devices) { + if (d.role === 'router' || d.role === 'mgmt-server') continue + const v = vars(d.hostname) + const bgpSpeaker = !d.role.startsWith('mgmt') || l3 + const g = group( + `${p.group}_${{ leaf: 'leaves', spine: 'spines', superspine: 'superspines', exit: 'exits', 'storage-leaf': 'storageleaves', 'mgmt-spine': 'mgmtspines', 'mgmt-leaf': 'mgmtleaves' }[d.role]}`, + ) + const sources = [ + // sonic-config renders frr.conf unless told not to. + nos === 'edgecore-sonic' && bgpSpeaker && g.sonic_config_frr_render !== false, + // The collection's BGP modules. + v.sonic_bgp !== undefined, + // metal-core writes the leaves' frr.conf. + d.role === 'leaf', + ].filter(Boolean) + expect(sources, d.hostname).toHaveLength(bgpSpeaker ? 1 : 0) + if (d.role === 'leaf') { + const relayed = + nos === 'edgecore-sonic' + ? v.sonic_config_vlans?.[0]?.dhcp_servers + : v.sonic_dhcp_relay?.[0]?.ipv4?.server_addresses + expect(relayed?.length, d.hostname).toBeGreaterThan(0) + } + } + }) + }, + ) +}) diff --git a/src/derive/ansible/playbooks.ts b/src/derive/ansible/playbooks.ts index c48061a..4b735c3 100644 --- a/src/derive/ansible/playbooks.ts +++ b/src/derive/ansible/playbooks.ts @@ -1,13 +1,16 @@ import { ANSIBLE_COMMON_REPO, METAL_ROLES_REPO } from '../../model/ansibleRoles' import { formatCidr, formatIp } from '../ip/cidr' import { ciReadme } from './ci' +import { ENTERPRISE_SONIC_COLLECTION, enterpriseSonicTaskFiles, TOPICS } from './enterpriseSonic' import type { AnsibleContext } from './index' import { fromObject, kv, toYaml, toYamlSeq, ymap, type YValue } from './yaml' // Playbooks, role requirements, ansible.cfg and the README of the Ansible // export. The plays follow mini-lab's deploy_partition.yaml and the -// deployment repositories: sonic-config on every switch, metal-core on the -// leaves, the management server roles on the mgmt servers. +// deployment repositories: sonic-config on Edgecore SONiC switches, the +// Dell collection tasks on Broadcom SONiC switches (enterpriseSonic.ts), +// metal-core on the leaves, the management server roles on the mgmt +// servers. Plays exist only for the NOS the plan uses. const role = (name: string, tags?: string): YValue => fromObject(tags ? { name, tags } : { name }) @@ -26,9 +29,49 @@ function play(name: string, hosts: string, roles: YValue[], extra: Record + fromObject({ + 'ansible.builtin.import_tasks': `tasks/enterprise_sonic/${t.topic}.yaml`, + tags: [t.topic], + }), + ), + }), + fromObject({ + name: `${name}: DNS and routing mode over SSH (Broadcom SONiC)`, + hosts: target, + gather_facts: false, + become: true, + vars: fromObject(SSH), + tasks: [fromObject({ 'ansible.builtin.import_tasks': 'tasks/enterprise_sonic/ssh.yaml' })], + handlers: [ + fromObject({ + name: 'Restart bgp', + 'ansible.builtin.service': fromObject({ name: 'bgp', state: 'restarted' }), + }), + ], + }), + ] +} + export function staticFiles({ plan, out }: AnsibleContext): void { const dep = plan.deployment const header = 'Generated by the metal-stack planner.' + const noses = new Set(plan.partitions.map((p) => p.fabric.nos)) + const edgecore = noses.has('edgecore-sonic') + const broadcom = noses.has('broadcom-sonic') + if (broadcom) enterpriseSonicTaskFiles(out) out.add( 'ansible.cfg', [ @@ -57,8 +100,12 @@ export function staticFiles({ plan, out }: AnsibleContext): void { ]), kv( 'collections', - COLLECTIONS.map((name) => fromObject({ name })), - 'Modules the roles call (docker containers, timezone, sysctl, IP filters).', + [...COLLECTIONS, ...(broadcom ? [ENTERPRISE_SONIC_COLLECTION] : [])].map((name) => + fromObject({ name }), + ), + broadcom + ? 'Modules the roles call; dellemc.enterprise_sonic configures Broadcom SONiC.' + : 'Modules the roles call (docker containers, timezone, sysctl, IP filters).', ), ), `${header}\n${INSTALL.join('\n')}`, @@ -68,18 +115,30 @@ export function staticFiles({ plan, out }: AnsibleContext): void { 'deploy_prod_network.yaml', toYamlSeq( [ - play( - 'Configure superspines, spines, exits and storage leaves', - 'superspines:spines:exits:storageleaves', - [COMMON, SONIC], - { serial: 1 }, - ), - play('Configure leaves', 'leaves', [COMMON, SONIC], { serial: 1 }), + ...(edgecore + ? [ + play( + 'Configure superspines, spines, exits and storage leaves (Edgecore SONiC)', + 'superspines:spines:exits:storageleaves:&edgecore_sonic', + [COMMON, SONIC], + { serial: 1 }, + ), + play('Configure leaves (Edgecore SONiC)', 'leaves:&edgecore_sonic', [COMMON, SONIC], { + serial: 1, + }), + ] + : []), + ...(broadcom + ? enterpriseSonicPlays( + 'Configure production switches', + 'superspines:spines:exits:storageleaves:leaves', + ) + : []), play( 'Deploy metal-core', 'leaves', [COMMON, role('metal-roles/partition/roles/metal-core', 'metal-core')], - { serial: 1, become: true }, + { serial: 1, become: true, ...(broadcom ? { vars: fromObject(SSH) } : {}) }, ), ], header, @@ -88,7 +147,20 @@ export function staticFiles({ plan, out }: AnsibleContext): void { out.add( 'deploy_mgmt_network.yaml', toYamlSeq( - [play('Configure management switches', 'mgmtspines:mgmtleaves', [COMMON, SONIC])], + [ + ...(edgecore + ? [ + play( + 'Configure management switches (Edgecore SONiC)', + 'mgmtspines:mgmtleaves:&edgecore_sonic', + [COMMON, SONIC], + ), + ] + : []), + ...(broadcom + ? enterpriseSonicPlays('Configure management switches', 'mgmtspines:mgmtleaves') + : []), + ], header, ), ) diff --git a/src/derive/ansible/vars.ts b/src/derive/ansible/vars.ts index 54f58ea..b8557d4 100644 --- a/src/derive/ansible/vars.ts +++ b/src/derive/ansible/vars.ts @@ -1,16 +1,20 @@ import { BMC_SUPERUSER, catalog } from '../../model/catalog' +import type { Nos } from '../../model/plan' import { devicesOf, type Device, type PartitionDevices } from '../devices' -import { derivePorts } from '../ports' -import { formatCidr, formatIp, type Cidr } from '../ip/cidr' +import { derivePorts, type SwitchPorts } from '../ports' +import { formatCidr, formatIp, lastAddr, parseIp, type Cidr } from '../ip/cidr' import type { PartitionAddresses } from '../ip/deviceAddresses' +import { enterpriseSonicGroupVars, enterpriseSonicHostVars } from './enterpriseSonic' import type { AnsibleContext } from './index' import { partitionGroup, rackGroup } from './inventory' import { kv, note, toYaml, ymap, type YEntry, type YValue } from './yaml' // group_vars and host_vars for the metal-roles partition roles -// (sonic-config on every switch, metal-core on the leaves, mgmt-server, -// dhcp, metal-bmc, pixiecore and image-cache on the management servers). -// Variable names are checked against model/ansibleRoles.ts by the tests. +// (sonic-config on Edgecore SONiC switches, metal-core on the leaves, +// mgmt-server, dhcp, metal-bmc, pixiecore and image-cache on the +// management servers). Broadcom SONiC switches get the variables of the +// export's Dell collection tasks instead (enterpriseSonic.ts). Variable +// names are checked against model/ansibleRoles.ts by the tests. const PXE_VLAN = 4000 @@ -24,8 +28,9 @@ const PORT_COMMENT: Partial> = { 'mgmt-leaf': 'Mgmt spine uplinks on the last ports.', 'mgmt-spine': 'Mgmt servers on the first copper ports, mgmt leaves on the first fiber ports.', } -const PORTS_NOTE = - 'BGP ports are set per switch in host_vars. Breakouts and port settings\n(sonic_config_breakouts, sonic_config_ports) are left to the cabling.' +const PORTS_NOTE = 'BGP ports, breakouts and port settings are set per switch in host_vars.' + +const edgecore = (nos: Nos) => nos === 'edgecore-sonic' function netmask(prefix: number): string { const all = (1n << 32n) - 1n @@ -75,12 +80,13 @@ export function groupVarFiles({ plan, devices, addresses, out }: AnsibleContext) ) } - // Settings shared by the whole partition inventory. - { - const f = `${inv}/group_vars/partition/switches.yaml` + // Settings of every Edgecore SONiC switch (sonic-config). + const noses = new Set(plan.partitions.map((p) => p.fabric.nos)) + if (noses.has('edgecore-sonic')) { + const f = `${inv}/group_vars/edgecore_sonic/sonic-config.yaml` const list = (key: string, values: string[], what: string) => values.length ? values : [out.todo(f, key, `${what} (Ansible tab)`)] - file('partition', 'switches', [ + file('edgecore_sonic', 'sonic-config', [ kv('sonic_config_mgmt_vrf', true), kv('sonic_config_timezone', dep.timezone), kv( @@ -94,6 +100,7 @@ export function groupVarFiles({ plan, devices, addresses, out }: AnsibleContext) kv('sonic_config_ssh_sourceranges', dep.sshSourceRanges), ]) } + if (noses.has('broadcom-sonic')) enterpriseSonicGroupVars(out, dep.nameservers) { const f = `${inv}/group_vars/partition/metal.yaml` file( @@ -190,6 +197,7 @@ export function groupVarFiles({ plan, devices, addresses, out }: AnsibleContext) p, addresses[i], plan.partitions[i].fabric.mgmt.layer === 'l3', + edgecore(plan.partitions[i].fabric.nos), file, out, dep, @@ -197,10 +205,32 @@ export function groupVarFiles({ plan, devices, addresses, out }: AnsibleContext) ) } +/** The mgmt servers' router ids, where leaves relay PXE DHCP to. */ +function dhcpServers(p: PartitionDevices, addr: PartitionAddresses, l3: boolean): string[] { + return devicesOf(p, 'mgmt-server') + .map((d) => { + const a = addr.byHost.get(d.hostname) + return (l3 ? a?.loopback : a?.mgmt?.ip) ?? '' + }) + .filter(Boolean) +} + +/** Network, first host and broadcast of an "a.b.c.d/p" host address. */ +function hostCidr(value: string): { net: Cidr; host: bigint } | null { + const [ip, prefix] = value.split('/') + const parsed = parseIp(ip) + if (!parsed || parsed.family !== 4) return null + const p = Number(prefix) + const mask = ((1n << 32n) - 1n) ^ ((1n << BigInt(32 - p)) - 1n) + return { net: { family: 4, addr: parsed.addr & mask, prefix: p }, host: parsed.addr } +} + function partitionGroupVars( p: PartitionDevices, addr: PartitionAddresses, l3: boolean, + /** sonic-config variables only for Edgecore SONiC partitions. */ + ec: boolean, file: (group: string, topic: string, entries: (YEntry | false)[], header?: string) => void, out: AnsibleContext['out'], dep: AnsibleContext['plan']['deployment'], @@ -230,22 +260,30 @@ function partitionGroupVars( if (has('leaf')) { const g = partitionGroup(p, 'leaf') - file(g, 'sonic', [ - kv( - 'lo', - '{{ sonic_config_loopback_address }}', - 'metal-core reads the loopback and the ASN as lo / asn.', - ), - kv('asn', '{{ sonic_config_asn }}'), - kv( - 'sonic_config_frr_render', - false, - 'metal-core writes frr.conf, which needs the split routing mode.', - ), - kv('sonic_config_docker_routing_config_mode', 'split'), - kv('sonic_config_frr_l2vpn_evpn', true), - note(PORTS_NOTE), - ]) + if (ec) { + file(g, 'sonic', [ + kv( + 'lo', + '{{ sonic_config_loopback_address }}', + 'metal-core reads the loopback and the ASN as lo / asn.', + ), + kv('asn', '{{ sonic_config_asn }}'), + kv( + 'sonic_config_frr_render', + false, + 'metal-core writes frr.conf, which needs the split routing mode.', + ), + kv('sonic_config_docker_routing_config_mode', 'split'), + kv('sonic_config_frr_l2vpn_evpn', true), + kv('sonic_config_vtep', ymap(kv('enabled', true))), + kv( + 'sonic_config_mgmt_vrf', + false, + 'Leaves run without the mgmt VRF: metal-core reaches metal-api over eth0.', + ), + note(PORTS_NOTE), + ]) + } for (const rack of p.racks) { if (!p.devices.some((d) => d.role === 'leaf' && d.rack?.rackId === rack.rackId)) continue const rg = rackGroup(p, rack.tag) @@ -268,12 +306,13 @@ function partitionGroupVars( } } + // The rest configures sonic-config; Broadcom SONiC switches get host_vars. for (const role of ['spine', 'superspine', 'storage-leaf'] as const) { - if (!has(role)) continue + if (!has(role) || !ec) continue const g = partitionGroup(p, role) file(g, 'sonic', [kv('sonic_config_frr_l2vpn_evpn', true), note(PORTS_NOTE)]) } - if (has('exit')) { + if (has('exit') && ec) { const g = partitionGroup(p, 'exit') file(g, 'sonic', [ kv('sonic_config_frr_l2vpn_evpn', true), @@ -289,7 +328,7 @@ function partitionGroupVars( ]) } for (const role of ['mgmt-spine', 'mgmt-leaf'] as const) { - if (!has(role)) continue + if (!has(role) || !ec) continue const g = partitionGroup(p, role) file( g, @@ -313,7 +352,7 @@ function partitionGroupVars( ), dep.nameservers.length > 0 && kv('mgmt_server_nameservers', dep.nameservers), ]) - const subnets: YValue[] = addr.mgmtSubnets + const bmcs: YValue[] = addr.mgmtSubnets .filter((s) => s.dhcpRange) .map((s) => ymap( @@ -330,11 +369,41 @@ function partitionGroupVars( kv('options', [`routers ${formatIp(4, s.gateway)}`]), ), ) + // Every leaf routes its own PXE network and relays DHCP here. + const pxe: YValue[] = devicesOf(p, 'leaf').flatMap((leaf) => { + const c = hostCidr(addr.byHost.get(leaf.hostname)?.pxe ?? '') + if (!c) return [] + return [ + ymap( + kv('comment', `PXE network of ${leaf.hostname}`), + kv('network', formatIp(4, c.net.addr)), + kv('netmask', netmask(c.net.prefix)), + kv( + 'range', + ymap( + kv('begin', formatIp(4, c.host + 1n)), + kv('end', formatIp(4, lastAddr(c.net) - 1n)), + ), + ), + kv('options', [`routers ${formatIp(4, c.host)}`]), + ), + ] + }) + // dhcpd needs a declaration for the network it listens on. + const own: YValue[] = addr.mgmtLoopbacks + ? [ + ymap( + kv('comment', 'Mgmt loopbacks: the network dhcpd listens on, no leases'), + kv('network', formatIp(4, addr.mgmtLoopbacks.addr)), + kv('netmask', netmask(addr.mgmtLoopbacks.prefix)), + ), + ] + : [] file( g, 'dhcp', - [kv('dhcp_subnets', subnets)], - 'BMC addresses: the management subnets after the gateway and the switches.', + [kv('dhcp_subnets', [...own, ...bmcs, ...pxe])], + 'BMC addresses after the gateway and the switches; one PXE network per leaf.', ) file(g, 'metal-bmc', [ kv('metal_bmc_allowed_cidrs', cidrs(addr.mgmtSubnets.map((s) => s.cidr))), @@ -354,11 +423,53 @@ function partitionGroupVars( } } +/** sonic-config port settings from the port plan. */ +function sonicConfigPorts(sp: SwitchPorts): YEntry[] { + if (sp.reason) return [note(`Port settings left open: ${sp.reason}.`)] + const out: YEntry[] = [] + // As the reference deployments: metal-core owns the leaves' server ports. + const ports = sp.ports.filter((p) => p.use !== 'servers') + if (sp.breakouts.length > 0) { + out.push( + kv( + 'sonic_config_breakouts', + ymap(...sp.breakouts.map((b) => kv(b.port, b.mode))), + 'Server ports: 4x25G breakouts on the first ports.', + ), + ) + } + if (ports.length > 0) { + const mtu = ports[0].mtu + out.push( + kv( + 'sonic_config_ports', + ymap( + kv('default_mtu', mtu), + kv( + 'list', + ports.map((p) => + ymap( + kv('name', p.name), + kv('speed', p.speed), + ...(p.mtu !== mtu ? [kv('mtu', p.mtu)] : []), + ), + ), + ), + ), + `Used ports: ${[...new Set(ports.map((p) => p.use))].join(', ')}.`, + ), + ) + } + return out +} + export function hostVarFiles({ plan, devices, addresses, out }: AnsibleContext): void { const ports = derivePorts(plan, devices) devices.forEach((p, i) => { const addr = addresses[i] const l3 = plan.partitions[i].fabric.mgmt.layer === 'l3' + const ec = edgecore(plan.partitions[i].fabric.nos) + const relay = dhcpServers(p, addr, l3) for (const d of p.devices) { const f = `${out.inventory}/host_vars/${d.hostname}.yaml` const a = addr.byHost.get(d.hostname)! @@ -400,7 +511,8 @@ export function hostVarFiles({ plan, devices, addresses, out }: AnsibleContext): out.todo(f, 'mgmt_server_router_id', 'router id of the mgmt server'), ), ) - } else { + } else if (ec) { + const sp = ports.get(d.hostname) if (isBgpSpeaker(d, l3)) { entries.push( kv('sonic_config_asn', d.asn ?? 0), @@ -409,9 +521,6 @@ export function hostVarFiles({ plan, devices, addresses, out }: AnsibleContext): a.loopback ?? out.todo(f, 'sonic_config_loopback_address', 'loopback (the pool is not placed)'), ), - ) - const sp = ports.get(d.hostname) - entries.push( kv( 'sonic_config_bgp_ports', sp?.bgp ?? [ @@ -420,17 +529,8 @@ export function hostVarFiles({ plan, devices, addresses, out }: AnsibleContext): PORT_COMMENT[d.role], ), ) - if (d.role === 'leaf') { - entries.push( - kv( - 'metal_core_spine_uplinks', - sp?.uplinks ?? [ - out.todo(f, 'metal_core_spine_uplinks', `spine uplinks (${sp?.reason})`), - ], - ), - ) - } } + if (sp) entries.push(...sonicConfigPorts(sp)) entries.push( a.mgmt ? kv( @@ -442,9 +542,72 @@ export function hostVarFiles({ plan, devices, addresses, out }: AnsibleContext): ) : note('No management address: the management subnet is not placed.'), ) + if (d.role === 'leaf' && a.pxe) { + entries.push( + kv( + 'sonic_config_vlans', + [ymap(kv('id', PXE_VLAN), kv('ip', a.pxe), kv('dhcp_servers', relay))], + 'PXE network of the leaf, DHCP relayed to the mgmt servers.', + ), + ) + } + if (d.role === 'exit' && a.pxe) { + entries.push( + kv( + 'sonic_config_vlans', + [ymap(kv('id', PXE_VLAN), kv('ip', a.pxe))], + 'PXE network of the exits.', + ), + ) + } + } else { + // Broadcom SONiC: the variables of the Dell collection tasks. + if (d.role === 'leaf') { + entries.push( + kv( + 'lo', + a.loopback ?? out.todo(f, 'lo', 'loopback (the pool is not placed)'), + 'metal-core reads the loopback and the ASN as lo / asn.', + ), + kv('asn', d.asn ?? 0), + ) + } + entries.push( + ...enterpriseSonicHostVars({ + device: d, + addresses: a, + ports: ports.get(d.hostname), + bgp: isBgpSpeaker(d, l3), + evpn: ['spine', 'superspine', 'exit', 'storage-leaf'].includes(d.role), + mgmtVrf: d.role !== 'leaf', + dhcpServers: relay, + ntpServers: plan.deployment.ntpServers, + }), + ) + const sp = ports.get(d.hostname) + if (isBgpSpeaker(d, l3) && !sp?.bgp) { + // No port plan: the BGP neighbors are the fabric ports, cabled by hand. + entries.push( + kv( + 'sonic_bgp_neighbors', + out.todo( + f, + 'sonic_bgp_neighbors', + `BGP neighbors on the fabric ports (${sp?.reason ?? 'unknown'})`, + ), + ), + ) + } } if (d.role === 'leaf') { + const sp = ports.get(d.hostname) entries.push( + kv( + 'metal_core_spine_uplinks', + sp?.uplinks ?? [ + out.todo(f, 'metal_core_spine_uplinks', `spine uplinks (${sp?.reason})`), + ], + ), kv( 'metal_core_cidr', a.pxe ?? out.todo(f, 'metal_core_cidr', 'PXE network of the leaf (see the IPs tab)'), @@ -452,15 +615,6 @@ export function hostVarFiles({ plan, devices, addresses, out }: AnsibleContext): ), ) } - if (d.role === 'exit' && a.pxe) { - entries.push( - kv( - 'sonic_config_vlans', - [ymap(kv('id', PXE_VLAN), kv('ip', a.pxe))], - 'PXE network of the exits.', - ), - ) - } if (d.role === 'mgmt-leaf' && l3) { const s = addr.mgmtSubnets.find((m) => m.rackId === d.rack?.rackId) if (s) { diff --git a/src/derive/ip/deviceAddresses.ts b/src/derive/ip/deviceAddresses.ts index e79c0d1..cfe62f0 100644 --- a/src/derive/ip/deviceAddresses.ts +++ b/src/derive/ip/deviceAddresses.ts @@ -63,6 +63,8 @@ export interface PartitionAddresses { partitionId: string byHost: Map mgmtSubnets: MgmtSubnet[] + /** L3 management: the mgmt loopback pool (the mgmt servers' own network). */ + mgmtLoopbacks: Cidr | null pxe: Cidr | null transfers: TransferLink[] notes: string[] @@ -90,6 +92,7 @@ function partitionAddresses( partitionId: devices.partitionId, byHost, mgmtSubnets: [], + mgmtLoopbacks: find('mgmt-loopbacks'), pxe: find('pxe'), transfers: [], notes, diff --git a/src/derive/ports.test.ts b/src/derive/ports.test.ts index d098b1d..8d6b7be 100644 --- a/src/derive/ports.test.ts +++ b/src/derive/ports.test.ts @@ -1,15 +1,18 @@ import { describe, expect, it } from 'vitest' -import { sonicPortNames } from '../model/catalog' +import { breakoutChildren, frontPanelIndex, sonicPortNames } from '../model/catalog' import { createEmptyPlan, defaultRack } from '../model/defaults' import type { Plan } from '../model/plan' -import { deriveDevices } from './devices' +import { templates } from '../model/templates' +import { rackBmcPorts } from './bom' +import { deriveDevices, devicesOf } from './devices' import { derivePorts } from './ports' +import { leafPortsNeeded } from './validate' const ports = (plan: Plan) => derivePorts(plan, deriveDevices(plan)) -const host = (plan: Plan, name: string) => ports(plan).get(`partition-1-${name}`) +const host = (plan: Plan, name: string) => ports(plan).get(`partition-1-${name}`)! -describe('switch ports', () => { - it('names SONiC ports after the platform port maps', () => { +describe('SONiC port names', () => { + it('follow the platform port maps', () => { expect(sonicPortNames('switch-as7726', '100G')!.slice(-2)).toEqual([ 'Ethernet120', 'Ethernet124', @@ -24,55 +27,153 @@ describe('switch ports', () => { expect(sonicPortNames('switch-as4625', '10G')).toBeNull() }) - it('puts uplinks on the last ports and downlinks on the first', () => { - const plan = createEmptyPlan() - // One link per leaf and spine: the spine uplinks of the reference deployment. - expect(host(plan, 'r01leaf01')).toEqual({ - bgp: ['Ethernet120', 'Ethernet124'], - uplinks: ['Ethernet120', 'Ethernet124'], - }) - expect(host(plan, 'exit01')!.bgp).toEqual(['Ethernet120', 'Ethernet124']) - // 2 leaves + 2 exits from the first port. - expect(host(plan, 'spine01')!.bgp).toEqual([ + it('number front panels across port groups and name breakout lanes', () => { + expect(frontPanelIndex('switch-as7726', 'Ethernet0')).toBe(1) + expect(frontPanelIndex('switch-as7726', 'Ethernet124')).toBe(32) + expect(frontPanelIndex('switch-as4630', 'Ethernet48')).toBe(49) + expect(frontPanelIndex('switch-as4630', 'Ethernet56')).toBe(54) + expect(frontPanelIndex('switch-as4625', 'Ethernet0')).toBeNull() + expect(breakoutChildren('Ethernet8', '4x25G')).toEqual([ + 'Ethernet8', + 'Ethernet9', + 'Ethernet10', + 'Ethernet11', + ]) + }) +}) + +describe('switch ports', () => { + it('breaks out server ports first and puts uplinks last on a leaf', () => { + // 8 nodes with 2x25G on a leaf pair: 8 × 25G per leaf → two 4x25G ports. + const leaf = host(createEmptyPlan(), 'r01leaf01') + expect(leaf.breakouts).toEqual([ + { port: 'Ethernet0', frontPanel: 1, mode: '4x25G' }, + { port: 'Ethernet4', frontPanel: 2, mode: '4x25G' }, + ]) + expect(leaf.bgp).toEqual(['Ethernet120', 'Ethernet124']) + expect(leaf.uplinks).toEqual(['Ethernet120', 'Ethernet124']) + // Server ports per breakout lane with RS-FEC and MTU 9000, uplinks at 9216. + const servers = leaf.ports.filter((p) => p.use === 'servers') + expect(servers.map((p) => p.name)).toEqual([ 'Ethernet0', + 'Ethernet1', + 'Ethernet2', + 'Ethernet3', 'Ethernet4', - 'Ethernet8', - 'Ethernet12', + 'Ethernet5', + 'Ethernet6', + 'Ethernet7', + ]) + expect(servers[0]).toEqual({ + name: 'Ethernet0', + speed: 25000, + mtu: 9000, + fec: 'rs', + use: 'servers', + }) + expect(leaf.ports.filter((p) => p.use === 'spines')).toEqual([ + { name: 'Ethernet120', speed: 100000, mtu: 9216, use: 'spines' }, + { name: 'Ethernet124', speed: 100000, mtu: 9216, use: 'spines' }, ]) - // L3 management over 25G: mgmt leaf uplinks last, mgmt servers on copper. - expect(host(plan, 'r01mgmtleaf')!.bgp).toEqual(['Ethernet50', 'Ethernet51']) - expect(host(plan, 'mgmtspine01')!.bgp).toEqual(['Ethernet0', 'Ethernet1', 'Ethernet48']) }) - it('follows the number of leaf to spine links and superspines', () => { + it('matches the leaf port budget of the validation for every template', () => { + for (const t of templates) { + const plan = t.build() + const all = ports(plan) + deriveDevices(plan).forEach((p, i) => { + for (const rack of plan.partitions[i].racks) { + const leaves = devicesOf(p, 'leaf').filter((d) => d.rack?.rackId === rack.id) + const serverPorts = leaves.reduce((n, d) => n + all.get(d.hostname)!.breakouts.length, 0) + const native = rack.servers + .filter((g) => g.uplink === '2x100G') + .reduce((n, g) => n + 2 * g.count, 0) + // Per leaf rounding may add one breakout port per leaf. + expect(serverPorts + native).toBeGreaterThanOrEqual(leafPortsNeeded(rack)) + expect(serverPorts + native).toBeLessThanOrEqual(leafPortsNeeded(rack) + leaves.length) + } + }) + } + }) + + it('lays out exits, spines and superspines', () => { const plan = createEmptyPlan() + const exit = host(plan, 'exit01') + // Two links per internet router first, spine uplinks last. + expect(exit.ports.map((p) => [p.name, p.use])).toEqual([ + ['Ethernet0', 'internet routers'], + ['Ethernet4', 'internet routers'], + ['Ethernet8', 'internet routers'], + ['Ethernet12', 'internet routers'], + ['Ethernet120', 'spines'], + ['Ethernet124', 'spines'], + ]) + expect(exit.bgp).toEqual(['Ethernet120', 'Ethernet124']) + // 2 leaves + 2 exits from the first port. + expect(host(plan, 'spine01').bgp).toEqual(['Ethernet0', 'Ethernet4', 'Ethernet8', 'Ethernet12']) + const fabric = plan.partitions[0].fabric fabric.leafSpineLinks = 2 fabric.fabricType = 'leaf-spine-superspine' fabric.superspineCount = 2 - expect(host(plan, 'r01leaf01')!.uplinks).toHaveLength(4) - const spine = host(plan, 'spine01')!.bgp! - expect(spine.slice(0, 1)).toEqual(['Ethernet0']) + expect(host(plan, 'r01leaf01').uplinks).toHaveLength(4) + const spine = host(plan, 'spine01').bgp! + expect(spine[0]).toBe('Ethernet0') expect(spine.slice(-2)).toEqual(['Ethernet120', 'Ethernet124']) - expect(host(plan, 'superspine01')!.bgp).toEqual(['Ethernet0', 'Ethernet4']) + expect(host(plan, 'superspine01').bgp).toEqual(['Ethernet0', 'Ethernet4']) + }) + + it('lays out the management switches like the BOM cables them', () => { + const plan = createEmptyPlan() + const rack = plan.partitions[0].racks[0] + const mgmtLeaf = host(plan, 'r01mgmtleaf') + // BMCs and leaf mgmt interfaces on copper, uplinks on the last 25G ports. + const copper = mgmtLeaf.ports.filter((p) => p.speed === 1000) + expect(copper).toHaveLength(rackBmcPorts(rack) + rack.leafCount) + expect(mgmtLeaf.bgp).toEqual(['Ethernet50', 'Ethernet51']) + expect(mgmtLeaf.ports.at(-1)).toEqual({ + name: 'Ethernet51', + speed: 25000, + mtu: 9000, + use: 'mgmt spines', + }) + // Mgmt servers on the first copper ports, then half of the 6 central + // mgmt interfaces (2 spines, 2 exits, 2 routers), the mgmt leaf on fiber. + const spine = host(plan, 'mgmtspine01') + expect(spine.bgp).toEqual(['Ethernet0', 'Ethernet1', 'Ethernet48']) + expect(spine.ports.filter((p) => p.use === 'central mgmt interfaces')).toHaveLength(3) }) - it('leaves ports open without a port map or with too few ports', () => { + it('keeps management ports but no BGP ports for an L2 management network', () => { + const plan = createEmptyPlan() + plan.partitions[0].fabric.mgmt.layer = 'l2' + expect(host(plan, 'r01mgmtleaf').bgp).toEqual([]) + expect(host(plan, 'r01mgmtleaf').ports.length).toBeGreaterThan(0) + expect(host(plan, 'mgmtspine01').bgp).toEqual([]) + }) + + it('explains what cannot be laid out', () => { const noMap = createEmptyPlan() noMap.partitions[0].fabric.mgmt.leafModelId = 'switch-as4625' - expect(host(noMap, 'r01mgmtleaf')).toMatchObject({ bgp: null }) - expect(host(noMap, 'r01mgmtleaf')!.reason).toContain('switch-as4625') + expect(host(noMap, 'r01mgmtleaf')).toMatchObject({ bgp: null, breakouts: [], ports: [] }) + expect(host(noMap, 'r01mgmtleaf').reason).toContain('switch-as4625') const full = createEmptyPlan() full.partitions[0].racks = Array.from({ length: 16 }, (_, i) => defaultRack(`Rack ${i + 1}`)) expect(host(full, 'spine01')).toMatchObject({ bgp: null }) - expect(host(full, 'spine01')!.reason).toContain('needs 34 100G ports, has 32') + expect(host(full, 'spine01').reason).toContain('needs 34 100G ports, has 32') }) - it('assigns no management BGP ports for an L2 management network', () => { - const plan = createEmptyPlan() - plan.partitions[0].fabric.mgmt.layer = 'l2' - expect(host(plan, 'r01mgmtleaf')).toBeUndefined() - expect(host(plan, 'mgmtspine01')).toBeUndefined() + it('never uses a port twice', () => { + for (const t of templates) { + for (const [name, sp] of ports(t.build())) { + // A broken-out port's first lane keeps its name, so lanes and + // ports are one list of interface names. + const used = sp.ports.map((p) => p.name) + expect(new Set(used).size, name).toBe(used.length) + const parents = sp.breakouts.map((b) => b.port) + expect(new Set(parents).size, name).toBe(parents.length) + } + } }) }) diff --git a/src/derive/ports.ts b/src/derive/ports.ts index d50b328..018da27 100644 --- a/src/derive/ports.ts +++ b/src/derive/ports.ts @@ -1,94 +1,303 @@ -import { sonicPortNames, type PortSpeed } from '../model/catalog' +import { breakoutChildren, frontPanelIndex, sonicPortNames, type PortSpeed } from '../model/catalog' import { mgmtDeviceCount, type Plan } from '../model/plan' -import { mgmtUplinkSpeed } from './bom' +import { mgmtUplinkSpeed, rackBmcPorts } from './bom' import { devicesOf, type Device, type PartitionDevices } from './devices' -// Which switch ports carry the BGP underlay, derived from the cabling -// model of derive/bom.ts and the SONiC port maps in the catalog. Uplinks -// take a switch's last ports of their speed, downlinks its first ports, in -// plan order: +// Every switch's front-panel ports, derived from the cabling model of +// derive/bom.ts and the SONiC port maps in the catalog: which ports are +// broken out, which carry the BGP underlay, and the speed and MTU of each +// used port. Uplinks take a switch's last ports of their speed, everything +// else its first ports, in plan order. Both NOS paths of the Ansible export +// render this one plan. // -// - leaves, exits, storage leaves: leafSpineLinks (leaves) or one link to -// every spine on their last 100G ports -// - spines: leaves (in rack order, leafSpineLinks each), storage leaves and -// exits from the first 100G port, superspines on the last ports -// - superspines: one link per spine from the first 100G port -// - L3 management: mgmt leaves reach every mgmt spine on their last ports -// of the uplink speed (mgmtUplinkSpeed(); SFP28 ports also run 10G), the -// mgmt spines take the mgmt leaves on their first such ports and the -// mgmt servers on their first copper ports +// - leaves: per leaf, 2 × nodes / leaves server ports: 25G ones on the +// first 100G ports in 4x25G breakout (four per port), 100G ones natively +// after them, both at MTU 9000 with RS-FEC (as Enterprise SONiC +// deployments set them); leafSpineLinks to every spine on the last ports. +// - exits: two links per internet router first, one to every spine last +// - storage leaves: one link to every spine on the last ports +// - spines: leaves (rack order, leafSpineLinks each), storage leaves and +// exits from the first port, superspines on the last ports +// - superspines: one link per spine from the first port +// - mgmt leaves: the rack's BMCs and leaf mgmt interfaces on the first +// copper ports, one uplink to every mgmt spine on the last ports of the +// uplink speed (mgmtUplinkSpeed(); SFP28 ports also run 10G) +// - mgmt spines: every mgmt server, then their share of the central +// switches' and routers' mgmt interfaces on the first copper ports, the +// mgmt leaves on the first fiber ports // -// Where the catalog has no SONiC map or the ports don't suffice, the -// result is null with a reason, and the export writes a placeholder. +// Production ports run MTU 9216, management ports 9000 (as metal-stack +// deployments do). BGP runs on the fabric links, and in an L3 management +// network on the mgmt uplinks and mgmt server ports. Where the catalog has +// no SONiC map or the ports don't suffice, the plan carries a reason and +// the export writes placeholders. + +export const PROD_MTU = 9216 +export const MGMT_MTU = 9000 +export const SERVER_MTU = 9000 +export const BREAKOUT_MODE = '4x25G' + +export interface PortSetting { + name: string + /** Mbit/s, as SONiC's config_db writes it (100000 = 100G). */ + speed: number + mtu: number + /** Forward error correction, where it is set explicitly. */ + fec?: 'rs' + /** What the port connects to, for comments ("servers" on leaves). */ + use: string +} + +export interface Breakout { + port: string + /** Front-panel number, Enterprise SONiC's breakout name is "1/". */ + frontPanel: number + mode: string +} export interface SwitchPorts { - /** sonic_config_bgp_ports */ + /** Ports running BGP (sonic_config_bgp_ports); null with a reason. */ bgp: string[] | null /** Leaves: the spine uplinks (metal_core_spine_uplinks). */ uplinks?: string[] | null - /** Why bgp is null. */ + breakouts: Breakout[] + /** Port settings of the used ports. */ + ports: PortSetting[] + /** Why the plan could not be made. */ reason?: string } -function names(modelId: string, speed: PortSpeed): string[] | null { +interface Need { + speed: PortSpeed + count: number + end: 'first' | 'last' + use: string + bgp?: boolean + /** List the ports as port settings (default true). */ + list?: boolean + breakout?: boolean + /** Speed the ports run at when it differs from the group's. */ + runAt?: number + mtu?: number + fec?: 'rs' + uplink?: boolean +} + +const SPEED_MBPS: Record = { + '1G': 1000, + '10G': 10000, + '25G': 25000, + '100G': 100000, +} + +function groupNames(modelId: string, speed: PortSpeed): string[] | null { return sonicPortNames(modelId, speed) ?? (speed === '10G' ? sonicPortNames(modelId, '25G') : null) } -/** Ports for the device: `down` from the first port, `up` on the last. */ -function assign(d: Device, speed: PortSpeed, down: number, up: number): SwitchPorts { - const all = names(d.modelId, speed) - if (!all) return { bgp: null, reason: `no SONiC port map for ${d.modelId}` } - if (down + up > all.length) { - return { bgp: null, reason: `needs ${down + up} ${speed} ports, has ${all.length}` } +/** Lays the needs out on the device's ports: `first` needs from the first + * port upwards, `last` needs from the last port downwards, per speed. */ +function layout(d: Device, mtu: number, needs: Need[]): SwitchPorts { + const out: SwitchPorts = { bgp: [], uplinks: [], breakouts: [], ports: [] } + const fail = (reason: string): SwitchPorts => ({ + bgp: null, + uplinks: null, + breakouts: [], + ports: [], + reason, + }) + const taken = new Map() + const firsts: string[] = [] + const lasts: string[] = [] + for (const need of needs.filter((n) => n.count > 0)) { + const names = groupNames(d.modelId, need.speed) + if (!names) return fail(`no SONiC port map for ${d.modelId}`) + const t = taken.get(need.speed) ?? { first: 0, last: 0, total: names.length } + taken.set(need.speed, t) + if (t.first + t.last + need.count > t.total) { + const wanted = needs.filter((n) => n.speed === need.speed).reduce((s, n) => s + n.count, 0) + return fail(`needs ${wanted} ${need.speed} ports, has ${t.total}`) + } + const ports = + need.end === 'first' + ? names.slice(t.first, t.first + need.count) + : names.slice(t.total - t.last - need.count, t.total - t.last) + if (need.end === 'first') t.first += need.count + else t.last += need.count + if (need.breakout) { + for (const port of ports) { + out.breakouts.push({ + port, + frontPanel: frontPanelIndex(d.modelId, port)!, + mode: BREAKOUT_MODE, + }) + } + } + if (need.list !== false) { + const speed = need.runAt ?? SPEED_MBPS[need.speed] + // A broken-out port is set per lane. + const names = need.breakout ? ports.flatMap((p) => breakoutChildren(p, BREAKOUT_MODE)) : ports + for (const name of names) { + out.ports.push({ + name, + speed, + mtu: need.mtu ?? mtu, + ...(need.fec ? { fec: need.fec } : {}), + use: need.use, + }) + } + } + if (need.bgp) (need.end === 'first' ? firsts : lasts).push(...ports) + if (need.uplink) out.uplinks!.push(...ports) } - const uplinks = up > 0 ? all.slice(all.length - up) : [] - return { bgp: [...all.slice(0, down), ...uplinks], uplinks } + out.bgp = [...firsts, ...lasts] + // Port settings in port order, as a switch lists them. + const order = (name: string) => Number(/(\d+)$/.exec(name)?.[1] ?? 0) + out.ports.sort((a, b) => order(a.name) - order(b.name)) + return out } export function derivePorts(plan: Plan, devices: PartitionDevices[]): Map { const out = new Map() devices.forEach((p, i) => { - const { fabric } = plan.partitions[i] + const partition = plan.partitions[i] + const { fabric } = partition const count = (role: Device['role']) => devicesOf(p, role).length const spines = count('spine') const superspines = count('superspine') - const spineDown = count('leaf') * fabric.leafSpineLinks + count('storage-leaf') + count('exit') + const links = fabric.leafSpineLinks + const fabricUp = (n: number, use: string): Need => ({ + speed: '100G', + count: n, + end: 'last', + use, + bgp: true, + uplink: true, + }) for (const d of p.devices) { switch (d.role) { - case 'leaf': - out.set(d.hostname, assign(d, '100G', 0, spines * fabric.leafSpineLinks)) + case 'leaf': { + const rack = partition.racks.find((r) => r.id === d.rack?.rackId)! + const perLeaf = (uplink: '2x25G' | '2x100G') => + Math.ceil( + (2 * + rack.servers.filter((g) => g.uplink === uplink).reduce((n, g) => n + g.count, 0)) / + Math.max(rack.leafCount, 1), + ) + out.set( + d.hostname, + layout(d, PROD_MTU, [ + { + speed: '100G', + count: Math.ceil(perLeaf('2x25G') / 4), + end: 'first', + use: 'servers', + breakout: true, + runAt: 25000, + mtu: SERVER_MTU, + fec: 'rs', + }, + { + speed: '100G', + count: perLeaf('2x100G'), + end: 'first', + use: 'servers', + mtu: SERVER_MTU, + fec: 'rs', + }, + fabricUp(spines * links, 'spines'), + ]), + ) break + } case 'exit': + out.set( + d.hostname, + layout(d, PROD_MTU, [ + { speed: '100G', count: 2 * count('router'), end: 'first', use: 'internet routers' }, + fabricUp(spines, 'spines'), + ]), + ) + break case 'storage-leaf': - out.set(d.hostname, assign(d, '100G', 0, spines)) + out.set(d.hostname, layout(d, PROD_MTU, [fabricUp(spines, 'spines')])) break case 'spine': - out.set(d.hostname, assign(d, '100G', spineDown, superspines)) + out.set( + d.hostname, + layout(d, PROD_MTU, [ + { + speed: '100G', + count: count('leaf') * links, + end: 'first', + use: 'leaves', + bgp: true, + }, + { + speed: '100G', + count: count('storage-leaf'), + end: 'first', + use: 'storage leaves', + bgp: true, + }, + { speed: '100G', count: count('exit'), end: 'first', use: 'exits', bgp: true }, + { ...fabricUp(superspines, 'superspines'), uplink: false }, + ]), + ) break case 'superspine': - out.set(d.hostname, assign(d, '100G', spines, 0)) + out.set( + d.hostname, + layout(d, PROD_MTU, [ + { speed: '100G', count: spines, end: 'first', use: 'spines', bgp: true }, + ]), + ) break } } const { mgmt } = fabric - if (mgmt.layer !== 'l3') return - const speed = mgmtUplinkSpeed(mgmt.leafModelId, mgmt.spineModelId) + const l3 = mgmt.layer === 'l3' + const uplinkSpeed = mgmtUplinkSpeed(mgmt.leafModelId, mgmt.spineModelId) + // SFP28 ports run 10G when the other end has no 25G. + const fiber = (count: number, end: 'first' | 'last', use: string, uplink = false): Need => ({ + speed: uplinkSpeed, + count, + end, + use, + bgp: l3, + uplink, + runAt: SPEED_MBPS[uplinkSpeed], + }) const mgmtSpines = mgmtDeviceCount(mgmt) for (const d of devicesOf(p, 'mgmt-leaf')) { - out.set(d.hostname, assign(d, speed, 0, mgmtSpines)) - } - for (const d of devicesOf(p, 'mgmt-spine')) { - const fiber = assign(d, speed, count('mgmt-leaf'), 0) - const copper = assign(d, '1G', count('mgmt-server'), 0) + const rack = partition.racks.find((r) => r.id === d.rack?.rackId)! + const copper = Math.ceil( + (rackBmcPorts(rack) + rack.leafCount) / Math.max(mgmt.leafPerRack, 1), + ) out.set( d.hostname, - fiber.bgp && copper.bgp - ? { bgp: [...copper.bgp, ...fiber.bgp] } - : { bgp: null, reason: fiber.reason ?? copper.reason }, + layout(d, MGMT_MTU, [ + { speed: '1G', count: copper, end: 'first', use: 'BMCs and leaf mgmt interfaces' }, + fiber(mgmtSpines, 'last', 'mgmt spines', true), + ]), ) } + // As the BOM: one mgmt interface per central switch and router. + const centralMgmt = + count('spine') + count('exit') + count('superspine') + count('storage-leaf') + count('router') + devicesOf(p, 'mgmt-spine').forEach((d, s) => { + // Central mgmt interfaces are spread over the mgmt spines. + const share = Math.floor(centralMgmt / mgmtSpines) + (s < centralMgmt % mgmtSpines ? 1 : 0) + out.set( + d.hostname, + layout(d, MGMT_MTU, [ + { speed: '1G', count: count('mgmt-server'), end: 'first', use: 'mgmt servers', bgp: l3 }, + { speed: '1G', count: share, end: 'first', use: 'central mgmt interfaces' }, + fiber(count('mgmt-leaf'), 'first', 'mgmt leaves'), + ]), + ) + }) }) return out } diff --git a/src/model/ansibleRoles.ts b/src/model/ansibleRoles.ts index 5be30f5..3d99915 100644 --- a/src/model/ansibleRoles.ts +++ b/src/model/ansibleRoles.ts @@ -10,8 +10,18 @@ export const METAL_ROLES_REPO = 'https://github.com/metal-stack/metal-roles' export const ANSIBLE_COMMON_REPO = 'https://github.com/metal-stack/ansible-common' export const ROLE_VARIABLES: Record = { - // Ansible connection variables. - ansible: ['ansible_host', 'ansible_user', 'ansible_become', 'ansible_python_interpreter'], + // Ansible connection variables; httpapi ones for the Dell collection. + ansible: [ + 'ansible_host', + 'ansible_user', + 'ansible_password', + 'ansible_become', + 'ansible_python_interpreter', + 'ansible_connection', + 'ansible_network_os', + 'ansible_httpapi_use_ssl', + 'ansible_httpapi_validate_certs', + ], // Release vector mapping (common/roles/defaults, mini-lab group_vars/all). 'common/defaults': ['metal_stack_release_version', 'metal_stack_release_vectors'], // control-plane/roles/defaults: the ingress domain metal-api (api.) @@ -106,6 +116,27 @@ export const ROLE_VARIABLES: Record = { 'metal_bmc_console_key', ], 'image-cache': ['image_cache_sync_metal_api_endpoint', 'image_cache_sync_metal_api_view_hmac'], + // Not metal-roles: the export's own task files for Broadcom SONiC + // (derive/ansible/enterpriseSonic.ts), one variable per module of the + // dellemc.enterprise_sonic collection, named after it and holding that + // module's config. + 'export/enterprise-sonic': [ + 'sonic_port_breakout', + 'sonic_system', + 'sonic_lldp_global', + 'sonic_interfaces', + 'sonic_vlans', + 'sonic_l3_interfaces', + 'sonic_dhcp_relay', + 'sonic_route_maps', + 'sonic_bgp', + 'sonic_bgp_af', + 'sonic_bgp_neighbors', + 'sonic_vxlans', + 'sonic_ntp', + 'sonic_vrfs', + 'sonic_nameservers', + ], } export const KNOWN_VARIABLES: ReadonlySet = new Set(Object.values(ROLE_VARIABLES).flat()) diff --git a/src/model/catalog.ts b/src/model/catalog.ts index f88cd9d..2d722e4 100644 --- a/src/model/catalog.ts +++ b/src/model/catalog.ts @@ -629,6 +629,29 @@ export function portCount(item: CatalogItem, speed: PortSpeed): number { return item.ports?.find((p) => p.speed === speed)?.count ?? 0 } +/** Front-panel number of a SONiC port (1-based, counting the `ports` + * groups in order), as Enterprise SONiC names breakout ports ("1/"); + * null without a SONiC port map. */ +export function frontPanelIndex(modelId: string, portName: string): number | null { + let offset = 0 + for (const group of catalog[modelId]?.ports ?? []) { + const names = sonicPortNames(modelId, group.speed) + const i = names?.indexOf(portName) ?? -1 + if (i >= 0) return offset + i + 1 + offset += group.count + } + return null +} + +/** Interfaces a port becomes in an `x` breakout: its lanes as + * consecutive native names, "Ethernet0" in 4x25G → Ethernet0…Ethernet3. */ +export function breakoutChildren(portName: string, mode: string): string[] { + const n = Number(/^(\d+)x/.exec(mode)?.[1] ?? 1) + const base = Number(/(\d+)$/.exec(portName)?.[1] ?? NaN) + if (Number.isNaN(base)) return [portName] + return Array.from({ length: n }, (_, i) => `Ethernet${base + i}`) +} + /** The BMC superuser metal-hammer creates on a server, per vendor * (go-hal's SuperUser() of each vendor, metal-stack/go-hal * internal/vendors/); metal-bmc logs in with it. */ From 96757da0a6d49a6b95140f4313f77b010ae1b858 Mon Sep 17 00:00:00 2001 From: Markus Fensterer Date: Fri, 18 Sep 2026 22:11:24 +0200 Subject: [PATCH 8/8] Call the Dell collection modules directly in the deploy playbooks The Broadcom SONiC plays list the dellemc.enterprise_sonic module calls themselves instead of importing generated task files; each call keeps its topic as a tag, so single topics still run with --tags. The SSH tasks for DNS and the leaves' routing mode sit in their play the same way. No tasks/ directory is generated any more. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 +- src/derive/ansible/enterpriseSonic.ts | 92 ++++++++++++--------------- src/derive/ansible/nos.test.ts | 47 ++++++-------- src/derive/ansible/playbooks.ts | 16 ++--- 4 files changed, 71 insertions(+), 86 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 045e08e..5f05460 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,7 +28,7 @@ A client-only single-page app (React + Vite + TypeScript) to plan a [metal-stack - **Rack defaults and power budget.** `Partition.rackDefaults` (`heightUnits` 42, `maxPowerWatts` 12000 — five 3U eight-node MicroCloud chassis at ~2 kW plus a leaf pair no longer fit in 10 kW) seed racks added to the partition and apply to the central rack; each rack keeps its own `heightUnits` / `maxPowerWatts` in its Advanced section, which also holds the rarely changed leaf model and leaf count and a rack group's member names. The central rack's Advanced section likewise holds the internet router and storage leaf counts, its hardware models (spine, superspine, storage leaf, mgmt spine/leaf/server) and mgmt leaves per rack, grouped with the network OS, fabric links and rack defaults; the section header shows the chosen models read-only. Issues that can only be fixed in either Advanced section carry `target.field: 'advanced'`, and `navigateTo()` unfolds that section (`details[data-advanced]`) when revealing them. Validation reports an error per physical rack whose estimated draw exceeds its budget. - **Info bubbles.** `views/plan/InfoBubble.tsx` renders the "i" bubbles; `SelectField` / `NumberField` take an `info` prop. Texts must match the metal-stack docs, and links come from `views/plan/docs.ts` — only pages verified to exist on docs.metal-stack.io. The partition's central components are edited in `CentralRackSection.tsx` (the UI says "Central rack", the schema still calls it `fabric`). - **IP plan.** Inputs live in `Plan.ipPlan` (`src/model/ipPlan.ts`: per-family schema, infrastructure inputs, presets). `src/derive/ip/cidr.ts` is the BigInt CIDR library (no dependency); `src/derive/ip/ipPlan.ts` derives limits (each with a formula string), the partition allocation (sequential, skipping slots that overlap Kubernetes, reserve, internet or infrastructure ranges — an internet range containing the whole project CIDR is by design), the example cluster and the infrastructure subnets; `src/derive/ip/validateIp.ts` reports issues with `target: { section: 'ips', field }`, which `navigateTo()` opens in the IPs tab. Infrastructure sizing rules are in the header of `ipPlan.ts`. Presets mirror the layouts of the untracked `ip_calc.ods`; their public ranges must be documentation ranges (RFC 5737 / RFC 3849), never real allocations. `ipPlanToCsv()` in `src/io/ipCsv.ts` exports the address plan. -- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews (YAML/INI highlighting and README rendering from the tokenizers in `views/ansible/syntax.ts`, rendered as React elements, never as HTML strings; colors are the `--color-code-*` theme tokens) and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. `src/derive/ports.ts` lays out each switch's ports once for both NOS paths: 4x25G breakouts for 2x25G server ports on the leaves' first 100G ports, uplinks on the last ports of their speed, downlinks from the first, in plan order, with speed, MTU (9216 fabric, 9000 servers and mgmt) and RS-FEC on server ports, and the BGP ports; from the catalog's SONiC port maps (`ports[].sonic`, from sonic-buildimage's `port_config.ini`; only add maps from there, `sonicPortNames()` returns null otherwise). metal-api and NSQ are addressed under `metal_control_plane_ingress_dns` (`api.`, `:4150`, the control plane role defaults), the pixiecore API at the mgmt server's router ID, and the BMC superuser follows go-hal's per-vendor name (`BMC_SUPERUSER` in the catalog). Switches follow their partition's NOS (`FabricConfig.nos`, inventory groups `edgecore_sonic` / `broadcom_sonic`): Edgecore SONiC through metal-roles' `sonic-config` (`sonic_config_*`), Broadcom Enterprise SONiC through the Dell collection `dellemc.enterprise_sonic` over httpapi (`derive/ansible/enterpriseSonic.ts`): one `sonic_` variable per module holding its `config`, one generated task per module under `tasks/enterprise_sonic/`, `replaced` for plan-described topics, checked against the collection's argspecs, plus an SSH play for DNS and the leaves' split routing mode. The leaves' FRR is always metal-core's; every other BGP switch gets FRR from exactly one source (`nos.test.ts`), and every leaf carries PXE VLAN 4000 with DHCP relayed to the mgmt servers, whose `dhcp_subnets` lease each leaf's PXE network. What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`, which follows YAML 1.1 quoting (`"on"`, `"yes"`), as Ansible and GitHub read it. `derive/ansible/ci.ts` adds the pipelines (`Plan.deployment.ci.platform`: GitLab CI by default, GitHub Actions, both or none): a check on every push (inventory, `--syntax-check`, no `CHANGE_ME` left in `inventories/`) and manual deploys per partition in `DEPLOY_STEPS` order (mgmt servers, mgmt network, prod network) on a runner tagged per partition, one resource group / concurrency group each. Credentials stay CI secrets that Ansible reads through `ANSIBLE_PRIVATE_KEY_FILE` / `ANSIBLE_VAULT_PASSWORD_FILE`. The settings card shows only what differs per installation and has no default (release, control plane domain); everything with a default (public name and NTP servers as in mini-lab / metal-roles, inventory, role versions, the pipeline platform and its settings) sits in its Advanced section. +- **Ansible export.** The Ansible tab (`views/AnsibleView.tsx`, lazy-loaded) previews (YAML/INI highlighting and README rendering from the tokenizers in `views/ansible/syntax.ts`, rendered as React elements, never as HTML strings; colors are the `--color-code-*` theme tokens) and zips (`io/ansibleZip.ts`, JSZip on demand) what `deriveAnsible(plan)` in `src/derive/ansible/` generates: `inventory.yaml` (functional groups `leaves`, `spines`, `mgmtservers` … with a child group per partition, leaves split into one group per rack), `group_vars` / `host_vars` for the metal-roles partition roles (`sonic-config`, not the deprecated `sonic`; `metal-core`, which reads `lo` / `asn`; `mgmt-server`, `dhcp`, `metal-bmc`, `pixiecore`, `image-cache`), the playbooks, `requirements.yaml` (roles and collections) and a README. `src/derive/devices.ts` is the single source of hostnames and ASNs (numbering model in its header, from the metal-stack network docs: leaves unique, spines and exits shared); `src/derive/ip/deviceAddresses.ts` hands out loopbacks, management addresses, per-leaf PXE networks (metal-core CIDRs) and transfer addresses from the infra subnets, which carry a `kind` for that. `src/derive/ports.ts` lays out each switch's ports once for both NOS paths: 4x25G breakouts for 2x25G server ports on the leaves' first 100G ports, uplinks on the last ports of their speed, downlinks from the first, in plan order, with speed, MTU (9216 fabric, 9000 servers and mgmt) and RS-FEC on server ports, and the BGP ports; from the catalog's SONiC port maps (`ports[].sonic`, from sonic-buildimage's `port_config.ini`; only add maps from there, `sonicPortNames()` returns null otherwise). metal-api and NSQ are addressed under `metal_control_plane_ingress_dns` (`api.`, `:4150`, the control plane role defaults), the pixiecore API at the mgmt server's router ID, and the BMC superuser follows go-hal's per-vendor name (`BMC_SUPERUSER` in the catalog). Switches follow their partition's NOS (`FabricConfig.nos`, inventory groups `edgecore_sonic` / `broadcom_sonic`): Edgecore SONiC through metal-roles' `sonic-config` (`sonic_config_*`), Broadcom Enterprise SONiC through the Dell collection `dellemc.enterprise_sonic` over httpapi (`derive/ansible/enterpriseSonic.ts`): one `sonic_` variable per module holding its `config`, one task per module called directly in the deploy playbooks (tagged with its topic), `replaced` for plan-described topics, checked against the collection's argspecs, plus an SSH play for DNS and the leaves' split routing mode. The leaves' FRR is always metal-core's; every other BGP switch gets FRR from exactly one source (`nos.test.ts`), and every leaf carries PXE VLAN 4000 with DHCP relayed to the mgmt servers, whose `dhcp_subnets` lease each leaf's PXE network. What the plan cannot know is written as `CHANGE_ME` via `AnsibleOut.todo()` and listed as a placeholder; `Plan.deployment` (`model/deployment.ts`) holds the few settings the export needs. Every generated variable must be listed in `model/ansibleRoles.ts`, which mirrors the metal-roles READMEs at a pinned version (`ansible.test.ts` enforces it); update it from there, don't invent variables. YAML goes through the comment-capable writer in `derive/ansible/yaml.ts`, which follows YAML 1.1 quoting (`"on"`, `"yes"`), as Ansible and GitHub read it. `derive/ansible/ci.ts` adds the pipelines (`Plan.deployment.ci.platform`: GitLab CI by default, GitHub Actions, both or none): a check on every push (inventory, `--syntax-check`, no `CHANGE_ME` left in `inventories/`) and manual deploys per partition in `DEPLOY_STEPS` order (mgmt servers, mgmt network, prod network) on a runner tagged per partition, one resource group / concurrency group each. Credentials stay CI secrets that Ansible reads through `ANSIBLE_PRIVATE_KEY_FILE` / `ANSIBLE_VAULT_PASSWORD_FILE`. The settings card shows only what differs per installation and has no default (release, control plane domain); everything with a default (public name and NTP servers as in mini-lab / metal-roles, inventory, role versions, the pipeline platform and its settings) sits in its Advanced section. - **Icons.** Icons come from `src/views/icons/` only: `index.tsx` is the registry (Lucide icons plus the custom `NetworkSwitch`, `Rack`, `RackGroup` glyphs from `custom.ts`, drawn on Lucide's 24 px grid) with typed maps per tab, node kind, BOM category, rack slot kind and severity, and an `` helper (decorative, `aria-hidden`). An ESLint rule forbids importing `lucide-react` anywhere else. Sizes: 16 px in tabs and buttons, 14 px inline and in topology nodes, 12 px in rack slots; color follows the text. - **Templates.** `src/model/templates.ts` holds the starting plans offered by the Templates menu. They must order only hardware that is current at the vendor — `templates.test.ts` fails on any `availability` of `eol` / `withdrawn`, for the templates and for the default plan. Each `build()` returns a fresh Plan with new ids; `templates.test.ts` asserts every template parses and has no validation errors, so keep them valid when capacity rules change. - **Persistence and format version.** Zustand store (`src/store/planStore.ts`) with `persist` middleware → localStorage key `metal-stack-planner/plan`. JSON export/import in `src/io/json.ts`; every import runs through `migrateRawPlan()` (`src/model/migrate.ts`) and is then validated with `PlanSchema.parse`, and the same path runs on rehydrate. `SCHEMA_VERSION` in `migrate.ts` is the plan file format version — currently 1 with no migrations. Purely additive fields only need a Zod `.default()`; anything that breaks older files bumps `SCHEMA_VERSION` and adds a function to `MIGRATIONS`, keyed by the version it upgrades _from_. Files from a newer version are refused with a readable message. diff --git a/src/derive/ansible/enterpriseSonic.ts b/src/derive/ansible/enterpriseSonic.ts index 048a016..6453c13 100644 --- a/src/derive/ansible/enterpriseSonic.ts +++ b/src/derive/ansible/enterpriseSonic.ts @@ -2,7 +2,7 @@ import type { Device } from '../devices' import type { DeviceAddresses } from '../ip/deviceAddresses' import type { SwitchPorts } from '../ports' import type { AnsibleOut } from './index' -import { fromObject, kv, note, toYaml, toYamlSeq, ymap, type YEntry, type YValue } from './yaml' +import { fromObject, kv, note, toYaml, ymap, type YEntry, type YValue } from './yaml' // Broadcom Enterprise SONiC: metal-roles' sonic-config only supports // Edgecore SONiC, so these switches are configured with the Dell collection @@ -12,8 +12,8 @@ import { fromObject, kv, note, toYaml, toYamlSeq, ymap, type YEntry, type YValue // // The structure follows a deployment running this collection on Enterprise // SONiC: one variable per module named after it (sonic_, holding -// exactly that module's `config`), one task per module in the order the -// config builds up, `replaced` for what the plan fully describes and +// exactly that module's `config`), one task per module, called directly +// in the deploy playbooks in the order the config builds up, `replaced` for what the plan fully describes and // `merged` for global settings. The route map LOOPBACKS limits what BGP // redistributes to the loopback; peers use metal-stack's timers (1/3/10). // @@ -319,7 +319,7 @@ export function enterpriseSonicHostVars(h: EnterpriseSonicHost): YEntry[] { return out } -function task(label: string, many: boolean, step: Step): YValue { +function task(topic: string, label: string, many: boolean, step: Step): YValue { const v = moduleVar(step.module) return fromObject({ name: many ? `${label}: ${step.module}` : label, @@ -329,57 +329,49 @@ function task(label: string, many: boolean, step: Step): YValue { }), ...(step.perItem ? { loop: `{{ ${v} }}` } : {}), when: `${v} is defined`, + tags: [topic], }) } -/** Task files, one per topic, one module call per task. */ -export function enterpriseSonicTaskFiles(out: AnsibleOut): void { - for (const { topic, label, steps } of TOPICS) { - out.add( - `tasks/enterprise_sonic/${topic}.yaml`, - toYamlSeq( - steps.map((s) => task(label, steps.length > 1, s)), - `Generated by the metal-stack planner. ${label} with ${ENTERPRISE_SONIC_COLLECTION}.`, - ), - ) - } - // What the modules don't cover, over SSH in config_db (as mini-lab does). - out.add( - 'tasks/enterprise_sonic/ssh.yaml', - toYamlSeq( - [ - fromObject({ - name: 'DNS servers', - 'ansible.builtin.command': `sonic-db-cli CONFIG_DB hset "DNS_SERVER|{{ item }}" NULL NULL`, - loop: '{{ sonic_nameservers }}', - changed_when: false, - }), - fromObject({ - name: 'Routing config mode of a leaf', - 'ansible.builtin.command': - 'sonic-db-cli CONFIG_DB hget "DEVICE_METADATA|localhost" docker_routing_config_mode', - register: 'routing_mode', - changed_when: false, - when: "'leaves' in group_names", - }), - fromObject({ - name: 'Split routing config mode, metal-core writes frr.conf', - 'ansible.builtin.command': - 'sonic-db-cli CONFIG_DB hset "DEVICE_METADATA|localhost" docker_routing_config_mode split', - when: "'leaves' in group_names and routing_mode.stdout != 'split'", - notify: 'Restart bgp', - }), - fromObject({ - name: 'Save the configuration', - 'ansible.builtin.command': 'config save -y', - changed_when: false, - }), - ], - 'Generated by the metal-stack planner. Settings the Enterprise SONiC modules do not cover.', - ), +/** The module calls, one task per module, tagged with their topic. */ +export function enterpriseSonicTasks(): YValue[] { + return TOPICS.flatMap(({ topic, label, steps }) => + steps.map((s) => task(topic, label, steps.length > 1, s)), ) } +/** What the modules don't cover, over SSH in config_db (as mini-lab does). */ +export function enterpriseSonicSshTasks(): YValue[] { + return [ + fromObject({ + name: 'DNS servers', + 'ansible.builtin.command': `sonic-db-cli CONFIG_DB hset "DNS_SERVER|{{ item }}" NULL NULL`, + loop: '{{ sonic_nameservers }}', + changed_when: false, + }), + fromObject({ + name: 'Routing config mode of a leaf', + 'ansible.builtin.command': + 'sonic-db-cli CONFIG_DB hget "DEVICE_METADATA|localhost" docker_routing_config_mode', + register: 'routing_mode', + changed_when: false, + when: "'leaves' in group_names", + }), + fromObject({ + name: 'Split routing config mode, metal-core writes frr.conf', + 'ansible.builtin.command': + 'sonic-db-cli CONFIG_DB hset "DEVICE_METADATA|localhost" docker_routing_config_mode split', + when: "'leaves' in group_names and routing_mode.stdout != 'split'", + notify: 'Restart bgp', + }), + fromObject({ + name: 'Save the configuration', + 'ansible.builtin.command': 'config save -y', + changed_when: false, + }), + ] +} + /** group_vars/broadcom_sonic: connection of the modules and plan-wide values. */ export function enterpriseSonicGroupVars(out: AnsibleOut, nameservers: string[]): void { const dir = `${out.inventory}/group_vars/broadcom_sonic` @@ -421,7 +413,7 @@ export function enterpriseSonicGroupVars(out: AnsibleOut, nameservers: string[]) kv( 'sonic_nameservers', nameservers, - 'Written to config_db over SSH (tasks/enterprise_sonic/ssh.yaml).', + 'Written to config_db over SSH by the Broadcom SONiC plays.', ), ), ), diff --git a/src/derive/ansible/nos.test.ts b/src/derive/ansible/nos.test.ts index be181a0..1c4531b 100644 --- a/src/derive/ansible/nos.test.ts +++ b/src/derive/ansible/nos.test.ts @@ -155,35 +155,30 @@ describe('Broadcom SONiC path (dellemc.enterprise_sonic)', () => { expect(leaf.metal_core_spine_uplinks).toEqual(['Ethernet120', 'Ethernet124']) }) - it('writes the topic tasks, the SSH play and the collection requirement', () => { + it('calls the modules in the playbook, then the SSH play, and requires the collection', () => { const { files, placeholders } = deriveAnsible(plan) - const bgp = read(plan, 'tasks/enterprise_sonic/bgp.yaml') - expect( - bgp.map((t: Record) => { + expect(files.some((f) => f.path.startsWith('tasks/'))).toBe(false) + const calls = read(plan, 'deploy_prod_network.yaml')[0].tasks.map( + (t: Record & { tags: string[] }) => { const module = Object.keys(t)[1] - return [module, t[module].state] - }), - ).toEqual([ - ['dellemc.enterprise_sonic.sonic_bgp', 'replaced'], - ['dellemc.enterprise_sonic.sonic_bgp_af', 'merged'], - ['dellemc.enterprise_sonic.sonic_bgp_neighbors', 'replaced'], - ]) - const prodTopics = read(plan, 'deploy_prod_network.yaml')[0].tasks.map( - (t: { tags: string[] }) => t.tags[0], + return [t.tags[0], module.replace('dellemc.enterprise_sonic.', ''), t[module].state] + }, ) - expect(prodTopics).toEqual([ - 'breakouts', - 'system', - 'lldp', - 'interfaces', - 'vlans', - 'l3', - 'dhcp-relay', - 'route-maps', - 'bgp', - 'vxlans', - 'ntp', - 'vrfs', + expect(calls).toEqual([ + ['breakouts', 'sonic_port_breakout', 'replaced'], + ['system', 'sonic_system', 'merged'], + ['lldp', 'sonic_lldp_global', 'merged'], + ['interfaces', 'sonic_interfaces', 'replaced'], + ['vlans', 'sonic_vlans', 'replaced'], + ['l3', 'sonic_l3_interfaces', 'replaced'], + ['dhcp-relay', 'sonic_dhcp_relay', 'replaced'], + ['route-maps', 'sonic_route_maps', 'merged'], + ['bgp', 'sonic_bgp', 'replaced'], + ['bgp', 'sonic_bgp_af', 'merged'], + ['bgp', 'sonic_bgp_neighbors', 'replaced'], + ['vxlans', 'sonic_vxlans', 'replaced'], + ['ntp', 'sonic_ntp', 'merged'], + ['vrfs', 'sonic_vrfs', 'merged'], ]) const prod = read(plan, 'deploy_prod_network.yaml') expect(prod.map((p: { hosts: string }) => p.hosts)).toEqual([ diff --git a/src/derive/ansible/playbooks.ts b/src/derive/ansible/playbooks.ts index 4b735c3..46810b3 100644 --- a/src/derive/ansible/playbooks.ts +++ b/src/derive/ansible/playbooks.ts @@ -1,7 +1,11 @@ import { ANSIBLE_COMMON_REPO, METAL_ROLES_REPO } from '../../model/ansibleRoles' import { formatCidr, formatIp } from '../ip/cidr' import { ciReadme } from './ci' -import { ENTERPRISE_SONIC_COLLECTION, enterpriseSonicTaskFiles, TOPICS } from './enterpriseSonic' +import { + ENTERPRISE_SONIC_COLLECTION, + enterpriseSonicSshTasks, + enterpriseSonicTasks, +} from './enterpriseSonic' import type { AnsibleContext } from './index' import { fromObject, kv, toYaml, toYamlSeq, ymap, type YValue } from './yaml' @@ -41,12 +45,7 @@ function enterpriseSonicPlays(name: string, hosts: string): YValue[] { hosts: target, gather_facts: false, serial: 1, - tasks: TOPICS.map((t) => - fromObject({ - 'ansible.builtin.import_tasks': `tasks/enterprise_sonic/${t.topic}.yaml`, - tags: [t.topic], - }), - ), + tasks: enterpriseSonicTasks(), }), fromObject({ name: `${name}: DNS and routing mode over SSH (Broadcom SONiC)`, @@ -54,7 +53,7 @@ function enterpriseSonicPlays(name: string, hosts: string): YValue[] { gather_facts: false, become: true, vars: fromObject(SSH), - tasks: [fromObject({ 'ansible.builtin.import_tasks': 'tasks/enterprise_sonic/ssh.yaml' })], + tasks: enterpriseSonicSshTasks(), handlers: [ fromObject({ name: 'Restart bgp', @@ -71,7 +70,6 @@ export function staticFiles({ plan, out }: AnsibleContext): void { const noses = new Set(plan.partitions.map((p) => p.fabric.nos)) const edgecore = noses.has('edgecore-sonic') const broadcom = noses.has('broadcom-sonic') - if (broadcom) enterpriseSonicTaskFiles(out) out.add( 'ansible.cfg', [

o>^qMW7zva+)3UtEtG5eXF(GRS3Q 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¬NfWC@6ZURZ1Fcw{W@Afn=(?^QUW) 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 literal 172423 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