diff --git a/catalog/README.md b/catalog/README.md index 779d29f..dd9b796 100644 --- a/catalog/README.md +++ b/catalog/README.md @@ -26,9 +26,40 @@ YAML + template change here — **no bootapi code change**. ## Shipped entries - **almalinux9** — artifactapi `almalinux` remote, tree - `almalinux//BaseOS//os/`. + `almalinux//BaseOS//os/`. Generic / VM default (autopart on `sda`). - **fedora** — artifactapi `fedora` remote, tree `fedora/releases//Everything//os/`. +- **optiplex-3050 / optiplex-3060 / optiplex-7080** — AlmaLinux 9 on the Dell + OptiPlex fleet. Same install tree and `almalinux9` kickstart template, but + each passes a `storage_mode` (and, for the 7080, `vg_grow`) var so the one + template lays disks out per model. Ported from the Cobbler profiles + `almalinux9-dell_3050 / _3060 / _7080`. See "Per-model variants" below. + +## Per-model variants (Dell OptiPlex) + +Different OptiPlex models need slightly different storage handling, so each is +its own catalog entry selecting the shared `almalinux9` kickstart with a +model-specific `storage_mode` var: + +| Entry | `storage_mode` | Disk selection | Firmware | LVM PV | +|-------|----------------|----------------|----------|--------| +| `optiplex-3050` | `fixed-nvme` | static `nvme0n1` | legacy BIOS/MBR | fixed ~31G | +| `optiplex-3060` | `auto-nvme` | `%pre` picks first NVMe <512G | UEFI-aware (EFI part if booted UEFI) | fixed ~31G | +| `optiplex-7080` | `auto-nvme` + `vg_grow: "true"` | `%pre` picks first NVMe <512G | UEFI-aware | grows to fill disk | + +The kickstart (`kickstart/almalinux9.ks.tmpl`) reads `.DistroVars.storage_mode` +/ `.DistroVars.vg_grow` and renders the matching storage stanza; any entry +without those vars (e.g. plain `almalinux9`, `fedora`) gets the generic +autopart-on-`sda` path. + +**Model selection (NetBox-driven, no code change):** the entry `name` is the +OptiPlex **device_type slug**. Set the device's `provision_template` custom +field to that slug (`optiplex-3050` / `-3060` / `-7080`) and bootapi's catalog +`Select` picks the matching entry (override precedence beats platform/family). +The `match.platforms` slug on each entry is the device_type slug too — not a +real NetBox platform slug — so these entries are *only* reachable via the +override and never hijack a plain `almalinux9` host. To add another model, copy +one of these files, change the `name`/slug and `storage_mode`. ## Adding another distro (the intended path) diff --git a/catalog/optiplex-3050.yaml b/catalog/optiplex-3050.yaml new file mode 100644 index 0000000..a3cf9c6 --- /dev/null +++ b/catalog/optiplex-3050.yaml @@ -0,0 +1,23 @@ +# Distro catalog entry: AlmaLinux 9 on the Dell OptiPlex 3050. +# +# Same install tree as the generic almalinux9 entry, but with the storage layout +# the Cobbler profile almalinux9-dell_3050 used: a single known NVMe (nvme0n1), +# legacy BIOS/MBR boot, and a fixed ~31G LVM (no grow). Model selection is +# NetBox-driven: set the device's `provision_template` custom field to this +# entry's name (the OptiPlex device_type slug "optiplex-3050"). Because the +# match slug below is the device_type -- not a real platform slug -- this entry +# is never auto-selected for a plain almalinux9 host; only the override picks it. +name: optiplex-3050 +match: + platforms: [optiplex-3050] +kickstart: almalinux9 +version_default: "9" +kernel_url: "{{.ArtifactBase}}/almalinux/{{.Version}}/BaseOS/{{.Arch}}/os/images/pxeboot/vmlinuz" +initrd_url: "{{.ArtifactBase}}/almalinux/{{.Version}}/BaseOS/{{.Arch}}/os/images/pxeboot/initrd.img" +kernel_args: + - inst.text + - net.ifnames=0 +vars: + mirror: "{{.ArtifactBase}}/almalinux/{{.Version}}" + # storage_mode drives the model-specific storage stanza in almalinux9.ks.tmpl. + storage_mode: fixed-nvme diff --git a/catalog/optiplex-3060.yaml b/catalog/optiplex-3060.yaml new file mode 100644 index 0000000..0a8ad2b --- /dev/null +++ b/catalog/optiplex-3060.yaml @@ -0,0 +1,21 @@ +# Distro catalog entry: AlmaLinux 9 on the Dell OptiPlex 3060. +# +# Same install tree as the generic almalinux9 entry, with the storage behaviour +# of the Cobbler profile almalinux9-dell_3060: a %pre script auto-selects the +# internal NVMe (first under 512G), is UEFI-aware (adds an EFI System Partition +# only when booted UEFI), sets bootloader --boot-drive, and uses a fixed ~31G +# LVM (no grow). Select it by setting the device's `provision_template` custom +# field to "optiplex-3060" (the OptiPlex device_type slug). +name: optiplex-3060 +match: + platforms: [optiplex-3060] +kickstart: almalinux9 +version_default: "9" +kernel_url: "{{.ArtifactBase}}/almalinux/{{.Version}}/BaseOS/{{.Arch}}/os/images/pxeboot/vmlinuz" +initrd_url: "{{.ArtifactBase}}/almalinux/{{.Version}}/BaseOS/{{.Arch}}/os/images/pxeboot/initrd.img" +kernel_args: + - inst.text + - net.ifnames=0 +vars: + mirror: "{{.ArtifactBase}}/almalinux/{{.Version}}" + storage_mode: auto-nvme diff --git a/catalog/optiplex-7080.yaml b/catalog/optiplex-7080.yaml new file mode 100644 index 0000000..0e3ee66 --- /dev/null +++ b/catalog/optiplex-7080.yaml @@ -0,0 +1,22 @@ +# Distro catalog entry: AlmaLinux 9 on the Dell OptiPlex 7080. +# +# Identical to optiplex-3060 (auto-NVMe %pre, UEFI-aware, --boot-drive) EXCEPT +# the LVM PV grows to fill the disk -- the one real delta between the Cobbler +# profiles almalinux9-dell_3060 and almalinux9-dell_7080 (the 7080s ship larger +# NVMes and are meant to use the whole disk). vg_grow=true adds `--grow` to the +# pv partition. Select it via the device's `provision_template` custom field set +# to "optiplex-7080" (the OptiPlex device_type slug). +name: optiplex-7080 +match: + platforms: [optiplex-7080] +kickstart: almalinux9 +version_default: "9" +kernel_url: "{{.ArtifactBase}}/almalinux/{{.Version}}/BaseOS/{{.Arch}}/os/images/pxeboot/vmlinuz" +initrd_url: "{{.ArtifactBase}}/almalinux/{{.Version}}/BaseOS/{{.Arch}}/os/images/pxeboot/initrd.img" +kernel_args: + - inst.text + - net.ifnames=0 +vars: + mirror: "{{.ArtifactBase}}/almalinux/{{.Version}}" + storage_mode: auto-nvme + vg_grow: "true" diff --git a/kickstart/almalinux9.ks.tmpl b/kickstart/almalinux9.ks.tmpl index 3a09645..1316654 100644 --- a/kickstart/almalinux9.ks.tmpl +++ b/kickstart/almalinux9.ks.tmpl @@ -8,6 +8,10 @@ puppetserver (puppet.k8s.syd1.au.unkin.net / puppetca.k8s...), writes the puppet-initial PUPPETCA_URL env file, then posts back to bootapi so pxe_enabled flips off (Cobbler's netboot_enabled flow). +Storage is model-aware: the catalog entry selects a `storage_mode` (and, for +grow-to-fill, `vg_grow`) so one template serves VMs and every Dell OptiPlex +model. See the "storage" section and catalog/optiplex-*.yaml. + Data model: see docs/data-model.md. `.RootPasswordHash` and `.ProvisionToken` come from Vault/env at render time, never from NetBox. */ -}} @@ -24,7 +28,7 @@ url --url={{ $mirror }}/BaseOS/{{ .Arch }}/os/ repo --name=AppStream --baseurl={{ $mirror }}/AppStream/{{ .Arch }}/os/ # --- localization --- -keyboard --xlayouts='us' +keyboard --xlayouts='au' lang en_AU.UTF-8 timezone Australia/Sydney --utc @@ -46,17 +50,98 @@ network --bootproto=static --device={{ .MAC }} --ip={{ .IP }} --netmask={{ .Netm {{- end }} {{- end }} -# --- storage --- +# --- storage (model-aware; selected by the distro catalog's storage_mode var) --- +# storage_mode is set per-model by the catalog entry (catalog/optiplex-*.yaml): +# "" generic/VM default -> autopart on sda (unchanged legacy path) +# "fixed-nvme" OptiPlex 3050 -> static nvme0n1, BIOS/MBR, fixed 31G VG +# "auto-nvme" OptiPlex 3060 / 7080 -> %pre picks the internal NVMe, EFI-aware +# vg_grow ("true") grows the LVM PV to fill the disk (OptiPlex 7080 only). +# Layout (explicit LVM: /boot, root, swap, /home, /var/log) mirrors the Cobbler +# profiles almalinux9-dell_3050 / _3060 / _7080. Ported from the real templates +# almalinux9_dell3050 / almalinux9_dell3060 / almalinux9_dell7080. +{{- $storage := index .DistroVars "storage_mode" }} +{{- $grow := eq (index .DistroVars "vg_grow") "true" }} +{{- if eq $storage "fixed-nvme" }} +# OptiPlex 3050: single known NVMe, legacy BIOS boot (no EFI, no boot-drive). +ignoredisk --only-use=nvme0n1 +clearpart --all --initlabel --drives=nvme0n1 +part /boot --fstype="xfs" --ondisk=nvme0n1 --size=1024 +part pv.01 --fstype="lvmpv" --ondisk=nvme0n1 --size=31743 +volgroup rootvg --pesize=4096 pv.01 +logvol / --fstype="xfs" --size=10240 --name=root --vgname=rootvg +logvol swap --fstype="swap" --size=2048 --name=swap --vgname=rootvg +logvol /home --fstype="xfs" --size=9207 --name=home --vgname=rootvg +logvol /var/log --fstype="xfs" --size=10240 --name=varlog --vgname=rootvg +bootloader --location=mbr +{{- else if eq $storage "auto-nvme" }} +# OptiPlex 3060 / 7080: partition table is generated in %pre (below) into +# /tmp/bootapi-partitions and pulled in here, so the OS lands on whichever +# internal NVMe is present and the EFI partition is added only when booted UEFI. +%include /tmp/bootapi-partitions +{{- else }} +# Generic / VM default: single virtual disk, autopart. ignoredisk --only-use=sda clearpart --all --initlabel --drives=sda bootloader --location=mbr --boot-drive=sda --append="crashkernel=auto" autopart --type=lvm --nohome +{{- end }} + +# kdump: reserve crash memory + install kexec-tools (Cobbler com_redhat_kdump). +%addon com_redhat_kdump --enable --reserve-mb='auto' +%end +{{- if eq $storage "auto-nvme" }} + +# --- %pre: pick the internal NVMe and emit the partition table ----------------- +# Ported from Cobbler almalinux9_dell3060 / almalinux9_dell7080. Anaconda runs +# %pre before partitioning, so /tmp/bootapi-partitions (pulled in by the +# %include above) is populated in time. Picks the first NVMe under 512GB, wipes +# any prior rootvg so re-provisioning is idempotent, and adds an EFI System +# Partition only when the host actually booted UEFI. +%pre --interpreter=/usr/bin/bash --log=/root/bootapi-pre.log +set -x +lsblk -d -b -n -o NAME,SIZE + +# First internal NVMe under 512GB (skips large data disks and USB installers). +OSDISK=$(lsblk -d -b -n -o NAME,SIZE | awk '$1 ~ /^nvme/ && $2 < 549755813888 { print $1; exit }') + +# Tear down a pre-existing rootvg so a re-install starts from a clean disk. +if vgs rootvg >/dev/null 2>&1; then + lvchange -an rootvg || true + vgchange -an rootvg || true + vgremove -y rootvg || true + for pv in $(pvs --noheadings -o pv_name,vg_name | awk '$2 == "rootvg" { print $1 }'); do + pvremove "$pv" --force --force -y || true + done +fi + +wipefs -a "/dev/${OSDISK}" +dd if=/dev/zero of="/dev/${OSDISK}" bs=512 count=100 + +{ + echo "ignoredisk --only-use=${OSDISK}" + echo "clearpart --all --initlabel --drives=${OSDISK}" + if [ -d /sys/firmware/efi ]; then + echo "part /boot/efi --fstype=vfat --ondisk=${OSDISK} --size=200" + fi + echo "part /boot --fstype=xfs --ondisk=${OSDISK} --size=1024" + echo "part pv.01 --fstype=lvmpv --ondisk=${OSDISK} --size=31743{{ if $grow }} --grow{{ end }}" + echo "volgroup rootvg --pesize=4096 pv.01" + echo "logvol / --fstype=xfs --size=10240 --name=root --vgname=rootvg" + echo "logvol swap --fstype=swap --size=2048 --name=swap --vgname=rootvg" + echo "logvol /home --fstype=xfs --size=9207 --name=home --vgname=rootvg" + echo "logvol /var/log --fstype=xfs --size=10240 --name=varlog --vgname=rootvg" + echo "bootloader --location=mbr --boot-drive=${OSDISK}" +} > /tmp/bootapi-partitions +%end +{{- end }} # --- packages --- %packages --ignoremissing --excludedocs @^minimal-environment openssh-server chrony +kexec-tools +bind-utils vim-minimal tmux git