Set-OPS-Public/roles/serveur_ops_site/templates/conformite-fabric.sh.j2
Daniel Allaire 6edb8ac7d2 sonde fabric : detail lisible
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:57:57 -04:00

51 lines
2.3 KiB
Django/Jinja

#!/bin/bash
# GENERE par Set-OPS (role serveur_ops_site). Ne pas editer a la main.
#
# LA FABRIC DIT-ELLE ENCORE CE QUE LE PLAN DIT ? (2026-09-28)
#
# Joue les plans de LECTURE (aucune ecriture) et consigne, pour chacun, ce qu'il resterait
# a creer, retirer, mettre a jour ou corriger. La sonde « fabric » lit ce constat.
#
# POURQUOI : le 2026-09-28, aucune des 26 VM des locataires n'avait son pare-feu Proxmox
# actif — les reconstructions clonent sans ses options —, et rien ne le signalait. Le meme
# jour, un changement du moteur a fait vouloir a la frontiere une regle qui ne
# correspondrait jamais ; seul un plan relu a la main l'a montre. Les plans existaient :
# personne ne les jouait sans raison.
#
# Pourquoi un minuteur et pas la sonde elle-meme : une sonde dispose de quelques secondes,
# et ces plans interrogent l'API du cluster et celle de la frontiere.
set -uo pipefail
RACINE={{ serveur_ops_site_racine }}
UTIL={{ serveur_ops_site_utilisateur }}
ETAT={{ serveur_ops_site_conformite_etat }}
# Le devis du pare-feu Proxmox resout les flux d'un locataire : n'importe lequel suffit,
# il est borne par `underlay.tenants`.
TENANT=$(ls -d "${RACINE}"/OPS-* 2>/dev/null | head -1)
TMP=$(mktemp); trap 'rm -f "${TMP}"' EXIT
resultats=()
for plan in {{ serveur_ops_site_conformite_plans | join(' ') }}; do
runuser -u "${UTIL}" -- bash -lc "cd '${RACINE}/Set-OPS-public' && export PATH='${RACINE}/venv/bin':\$PATH && SETOPS_INSTANCE='${TENANT}' timeout 240 make ${plan}" > "${TMP}" 2>&1
rc=$?
# La sortie passe par un FICHIER : `python3 -` lit deja son code sur l'entree standard.
resultats+=("$(python3 - "${plan}" "${rc}" "${TMP}" <<'EOF'
import json, re, sys
plan, rc, texte = sys.argv[1], int(sys.argv[2]), open(sys.argv[3], errors="replace").read()
a_faire = sum(int(n) for n in re.findall(
r"(?:a creer|a retirer|a mettre a jour|noeuds a corriger)\s*:\s*(\d+)", texte))
detail = [re.sub(r"\s+", " ", l.strip()) for l in texte.splitlines() if re.match(r"\s*[-+~]\s", l)][:6]
print(json.dumps({"plan": plan, "rc": rc, "a_faire": a_faire, "detail": detail}))
EOF
)")
done
python3 - "${ETAT}" "${resultats[@]}" <<'EOF'
import json, sys, time, os
etat, lignes = sys.argv[1], sys.argv[2:]
doc = {"horodatage": int(time.time()), "plans": [json.loads(l) for l in lignes]}
tmp = etat + ".tmp"
with open(tmp, "w") as f:
json.dump(doc, f)
os.replace(tmp, etat)
EOF