diff --git a/Makefile b/Makefile index 6fc0fd6..a97edc6 100644 --- a/Makefile +++ b/Makefile @@ -698,8 +698,8 @@ devis-opnsense: ansible-runtime ## Devis OPNsense (frontiere nord/sud), derive d python3 scripts/devis_opnsense.py $(if $(JSON),--json,) .PHONY: proxmox-fw-plan proxmox-fw-appliquer -proxmox-fw-plan: ansible-runtime ## Ecart entre le pare-feu est-ouest Proxmox et son devis (aucune ecriture) - python3 scripts/appliquer_proxmox_fw.py +proxmox-fw-plan: ansible-runtime ## Ecart entre le pare-feu est-ouest Proxmox et son devis (aucune ecriture) — ARGS="--objets-seulement" ou "--vm " + python3 scripts/appliquer_proxmox_fw.py $(ARGS) proxmox-fw-appliquer: ansible-runtime ## Reconcilie le pare-feu est-ouest : IPSets, groupes, affectations. CONFIRMER=true @if [[ "$(CONFIRMER)" != "true" ]]; then \ @@ -707,7 +707,7 @@ proxmox-fw-appliquer: ansible-runtime ## Reconcilie le pare-feu est-ouest : IPSe "et RETIRE ce qui est perime. Elle n'active JAMAIS le pare-feu du datacenter." \ "Relire d'abord 'make proxmox-fw-plan', puis: make proxmox-fw-appliquer CONFIRMER=true"; \ exit 2; fi - CONFIRMER=true python3 scripts/appliquer_proxmox_fw.py + CONFIRMER=true python3 scripts/appliquer_proxmox_fw.py $(ARGS) .PHONY: placement-plan sdn-plan sdn-appliquer placement-plan: ansible-runtime ## Le noeud, stockage, pont et gabarit du tenant existent-ils sur ce cluster ? (aucune ecriture) diff --git a/scripts/appliquer_proxmox_fw.py b/scripts/appliquer_proxmox_fw.py index 2325a1a..ebb09ce 100644 --- a/scripts/appliquer_proxmox_fw.py +++ b/scripts/appliquer_proxmox_fw.py @@ -332,8 +332,32 @@ def main() -> int: raise SystemExit("Le devis ne se genere pas :\n" + r.stderr.strip()[:400]) devis = json.loads(r.stdout) + # PAR ETAPES, ET NON D'UN BLOC (2026-09-28). Les objets (IPSets, groupes) sont INERTES + # tant qu'aucune VM ne les porte ; l'affectation, elle, active le pare-feu de la VM en + # `REJECT` — le filtrage demarre, et une regle manquante coupe un service. Mesure du + # jour : aucune des 26 VM des locataires n'avait son pare-feu actif (les reconstructions + # clonent sans les options). Tout activer d'un coup, c'etait ouvrir 26 pannes possibles + # en meme temps. On pose donc les objets, puis on active VM par VM. + # --objets-seulement IPSets et groupes, aucune VM touchee + # --vm n'affecter QUE ces VM (repetable) ; les objets suivent + import argparse + ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + ap.add_argument("--objets-seulement", action="store_true") + ap.add_argument("--vm", action="append", type=int, default=[]) + args = ap.parse_args() + if args.objets_seulement and args.vm: + raise SystemExit("REFUS : --objets-seulement et --vm s'excluent.") + print(f"Cluster {api.hote} — {len(devis['blocs'])} tenant(s) au devis\n") p = plan(api, devis) + if args.objets_seulement or args.vm: + inconnues = sorted(set(args.vm) - set(p["affect_majer"]) - set(p["affect_posees"])) + if inconnues: + raise SystemExit(f"REFUS : VM hors du devis ou du cluster : {inconnues}") + ecartees = len(p["affect_majer"]) + p["affect_majer"] = {v: a for v, a in p["affect_majer"].items() if v in args.vm} + ecartees -= len(p["affect_majer"]) + print(f" (portee restreinte : {ecartees} affectation(s) de VM laissee(s) pour plus tard)\n") quelque_chose = afficher(p) if not quelque_chose: print("\n Le cluster dit deja ce que le devis dit. Rien a faire.")