From c8e704b5957cc3ac7adb1540390fdb5a17695bee Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 12 Jun 2026 08:47:21 -0400 Subject: [PATCH] CMM: add backup staging (sync-cmm-backups.sh) + write cmmid.txt at resolve sync-cmm-backups.sh pushes per-CMM backup sets (goCMM + PC-DMIS zips produced by Backup-CMM) from pxe-images/cmm-bk// to the PXE share at installers-post/cmm/backups//, atomic-swap with a timestamped prior copy. Distinct from sync-cmm.sh (which stages the CMM installer bundle). resolve-cmm-bay-config.ps1 now also writes cmmid.txt alongside version/doda/ partgroup, so 09-Setup-CMM can locate this bay's staged backup for restore-by-machine-number. The 09-Setup-CMM restore block + startnet staging line are intentionally NOT added yet - the restore needs manual end-to-end validation on a real CMM before auto-running at imaging (per the live-bay restore issues we hit). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../resolve-cmm-bay-config.ps1 | 3 + playbook/sync-cmm-backups.sh | 67 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100755 playbook/sync-cmm-backups.sh diff --git a/playbook/shopfloor-setup/gea-shopfloor-cmm/resolve-cmm-bay-config.ps1 b/playbook/shopfloor-setup/gea-shopfloor-cmm/resolve-cmm-bay-config.ps1 index 22bb2a3..3d0e7df 100644 --- a/playbook/shopfloor-setup/gea-shopfloor-cmm/resolve-cmm-bay-config.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-cmm/resolve-cmm-bay-config.ps1 @@ -51,6 +51,9 @@ if ($match.PSObject.Properties['part_group'] -and $match.part_group) { [System.IO.File]::WriteAllText((Join-Path $OutDir 'version.txt'), $version) [System.IO.File]::WriteAllText((Join-Path $OutDir 'doda.txt'), $doda) +# cmmid.txt: the resolved CMM id, so 09-Setup-CMM can locate this bay's staged +# backup set (installers-post\cmm\backups\\) for restore-by-machine-#. +[System.IO.File]::WriteAllText((Join-Path $OutDir 'cmmid.txt'), $CmmId) if ($partGroup) { [System.IO.File]::WriteAllText((Join-Path $OutDir 'partgroup.txt'), $partGroup) } diff --git a/playbook/sync-cmm-backups.sh b/playbook/sync-cmm-backups.sh new file mode 100755 index 0000000..ff20368 --- /dev/null +++ b/playbook/sync-cmm-backups.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# sync-cmm-backups.sh - stage per-CMM backups (goCMM + PC-DMIS) on the PXE +# enrollment share so 09-Setup-CMM can restore them by CMM machine-# at imaging. +# (Distinct from sync-cmm.sh, which stages the CMM *installers* bundle.) +# +# Source layout - one folder per CMM, named by the cmm_id in cmm-bay-config.csv, +# produced by Backup-CMM.ps1 and mirrored here from the live bays: +# /home/camp/pxe-images/cmm-bk// +# gocmm_backup__.zip +# pcdmis_backup___.zip (one per installed PC-DMIS version) +# cmm-backup-index.json +# +# Pushes the NEWEST backup set per cmm_id to: +# /srv/samba/enrollment/installers-post/cmm/backups// +# +# DODA bays: do NOT back them up (don't put them under cmm-bk/). 09-Setup-CMM +# also gates restore on cmm-bay-config doda=no as a second guard. +# +# Usage: ./playbook/sync-cmm-backups.sh (all cmm_id folders) +# CMM_ID=CMM3 ./playbook/sync-cmm-backups.sh (just one) +set -euo pipefail + +PXE_HOST="${PXE_HOST:-172.16.9.1}" +PXE_USER="${PXE_USER:-pxe}" +PXE_PASS="${PXE_PASS:-pxe}" +BACKUP_SRC_DIR="${CMM_BACKUP_DIR:-/home/camp/pxe-images/cmm-bk}" +REMOTE_BASE="/srv/samba/enrollment/installers-post/cmm/backups" +ONLY_ID="${CMM_ID:-}" + +ssh_run() { sshpass -p "$PXE_PASS" ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR "$PXE_USER@$PXE_HOST" "$@"; } +scp_to() { sshpass -p "$PXE_PASS" scp -o StrictHostKeyChecking=no -o LogLevel=ERROR "$@"; } + +command -v sshpass >/dev/null || { echo "sshpass required (apt install sshpass)"; exit 1; } +[ -d "$BACKUP_SRC_DIR" ] || { echo "No backup source dir: $BACKUP_SRC_DIR"; echo "Run Backup-CMM on the bays and mirror each cmm-backup\\\\ folder here."; exit 1; } +ping -c1 -W2 "$PXE_HOST" >/dev/null 2>&1 || { echo "PXE host $PXE_HOST unreachable"; exit 1; } + +count=0 +for cmm_dir in "$BACKUP_SRC_DIR"/*/; do + [ -d "$cmm_dir" ] || continue + cmm_id="$(basename "$cmm_dir")" + [ -n "$ONLY_ID" ] && [ "$cmm_id" != "$ONLY_ID" ] && continue + + # newest goCMM zip + every pcdmis zip (one per version) + the index + mapfile -t zips < <(ls -1t "$cmm_dir"/gocmm_backup_*.zip 2>/dev/null | head -1; \ + ls -1t "$cmm_dir"/pcdmis_backup_*.zip 2>/dev/null) + if [ ${#zips[@]} -eq 0 ]; then echo " [$cmm_id] no backup zips - skipping"; continue; fi + + echo "==> [$cmm_id] staging ${#zips[@]} zip(s)" + REMOTE_TMP="/tmp/cmm-bk-stage-$cmm_id-$$" + ssh_run "rm -rf '$REMOTE_TMP' && mkdir -p '$REMOTE_TMP'" + for z in "${zips[@]}"; do scp_to "$z" "$PXE_USER@$PXE_HOST:$REMOTE_TMP/"; done + [ -f "$cmm_dir/cmm-backup-index.json" ] && scp_to "$cmm_dir/cmm-backup-index.json" "$PXE_USER@$PXE_HOST:$REMOTE_TMP/" + + # atomic swap into the share (root-owned -> sudo); keep a timestamped copy of the prior set + ssh_run "echo $PXE_PASS | sudo -S bash -c ' + mkdir -p \"$REMOTE_BASE\" + dst=\"$REMOTE_BASE/$cmm_id\" + if [ -d \"\$dst\" ]; then mv \"\$dst\" \"\${dst}.pre-\$(date +%Y%m%d-%H%M%S)\"; fi + mv \"$REMOTE_TMP\" \"\$dst\" + chown -R pxe:pxe \"\$dst\" + ls -la \"\$dst\" + '" + count=$((count+1)) +done + +echo "==> Done. Staged $count CMM backup set(s) to $PXE_USER@$PXE_HOST:$REMOTE_BASE" +echo " 09-Setup-CMM restores at imaging when that CMM is picked (and doda=no)."