« Pourquoi pas make myDay ? » — la cible existe (alias strict de reconstruire) mais n'avait aucun texte d'aide, et P31 la declarait conforme. Le motif etait ^[a-z][a-z0-9_-]*: — toute majuscule echappait au controle. myDay est citee dans l'aide du Makefile et dans la GUI, et n'apparaissait dans aucun recensement. Une preuve ne vaut que ce que vaut son motif. Celle-ci a ete ecrite avec la conviction d'etre rigoureuse et testee dans les deux sens le jour meme. Le trou a ete trouve par une question, pas par un test. Troisieme fois sur la meme preuve en une journee, apres le rapport genere qui se citait lui-meme et l'inventaire genere qui l'aurait satisfaite par construction. La difficulte n'est pas d'ecrire un test, c'est de delimiter honnetement ce qu'il regarde. 87 cibles documentees, 36 scripts, 54 roles. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
891 lines
44 KiB
Makefile
891 lines
44 KiB
Makefile
SHELL := /usr/bin/env bash
|
|
|
|
export ANSIBLE_HOME ?= $(CURDIR)/.ansible
|
|
export ANSIBLE_LOCAL_TEMP ?= $(CURDIR)/.ansible/tmp
|
|
export ANSIBLE_SSH_CONTROL_PATH_DIR ?= $(CURDIR)/.ansible/cp
|
|
export ANSIBLE_SSH_ARGS ?= -F /dev/null -o ControlMaster=no
|
|
|
|
export SETOPS_INSTANCE ?= instance
|
|
# Inventaire de l'instance : un seul par instance dans le modèle « séparation par
|
|
# instance ». Détection rétro-compatible : principal > production > lab.
|
|
# Surchargeable : make … SETOPS_INVENTAIRE=chemin/hosts.yml
|
|
SETOPS_INVENTAIRE ?= $(firstword $(wildcard $(SETOPS_INSTANCE)/inventories/principal/hosts.yml) $(wildcard $(SETOPS_INSTANCE)/inventories/production/hosts.yml) $(SETOPS_INSTANCE)/inventories/principal/hosts.yml)
|
|
export SETOPS_INVENTAIRE
|
|
# Inventaire « modèle » (construction du golden template) : lab > principal > production.
|
|
INVENTAIRE_LAB ?= $(firstword $(wildcard $(SETOPS_INSTANCE)/inventories/lab/hosts.yml) $(wildcard $(SETOPS_INSTANCE)/inventories/principal/hosts.yml) $(SETOPS_INSTANCE)/inventories/production/hosts.yml)
|
|
INVENTAIRE_PRODUCTION ?= $(SETOPS_INVENTAIRE)
|
|
FICHIER_INVENTAIRE ?= $(SETOPS_INVENTAIRE)
|
|
FICHIER_DEPENDANCES ?= docs/dependances-groupes.yml
|
|
GROUPE_MODELE ?= modeles_vm
|
|
GROUPE_DEBIAN ?= serveur_debian
|
|
GROUPE_HOTES_ACTIFS ?= hotes_actifs
|
|
LIMITE ?= $(GROUPE_DEBIAN)
|
|
HOTE ?=
|
|
ADRESSE_IP ?=
|
|
GROUPES ?= $(GROUPE_DEBIAN)
|
|
GROUPE ?= $(GROUPE_DEBIAN)
|
|
UTILISATEUR_ANSIBLE ?= ansible
|
|
VMID_MODELE ?=
|
|
VMID ?=
|
|
NOEUD_PROXMOX ?=
|
|
STOCKAGE_PROXMOX ?=
|
|
FORMAT_DISQUE ?=
|
|
TAILLE_DISQUE ?=
|
|
DISQUE_PROXMOX ?=
|
|
CIDR ?= 24
|
|
PASSERELLE ?=
|
|
DNS ?=
|
|
DHCP ?= false
|
|
CIUSER ?=
|
|
CLE_SSH_PUBLIQUE ?=
|
|
PONT_PROXMOX ?=
|
|
VLAN ?=
|
|
DEMARRER ?=
|
|
CLONE_COMPLET ?=
|
|
|
|
CONFIRMER ?= false
|
|
VERIFICATION ?= false
|
|
DIFF ?= false
|
|
ETIQUETTES ?=
|
|
SAUTER_ETIQUETTES ?=
|
|
VARIABLES ?=
|
|
|
|
OPTIONS_PLAYBOOK :=
|
|
ifneq ($(LIMITE),)
|
|
OPTIONS_PLAYBOOK += --limit $(LIMITE)
|
|
endif
|
|
ifeq ($(VERIFICATION),true)
|
|
OPTIONS_PLAYBOOK += --check
|
|
endif
|
|
ifeq ($(DIFF),true)
|
|
OPTIONS_PLAYBOOK += --diff
|
|
endif
|
|
ifneq ($(ETIQUETTES),)
|
|
OPTIONS_PLAYBOOK += --tags $(ETIQUETTES)
|
|
endif
|
|
ifneq ($(SAUTER_ETIQUETTES),)
|
|
OPTIONS_PLAYBOOK += --skip-tags $(SAUTER_ETIQUETTES)
|
|
endif
|
|
ifneq ($(VARIABLES),)
|
|
OPTIONS_PLAYBOOK += -e $(VARIABLES)
|
|
endif
|
|
|
|
PLAYBOOK_PREPARER_MODELE := playbooks/modeles_vm/debian13_proxmox_preparer.yml
|
|
PLAYBOOK_VERIFIER_MODELE := playbooks/modeles_vm/debian13_proxmox_verifier.yml
|
|
PLAYBOOK_NETTOYER_MODELE := playbooks/modeles_vm/debian13_proxmox_nettoyer.yml
|
|
PLAYBOOK_VERIFIER_HOTE := playbooks/maintenance/verifier_hote_debian.yml
|
|
PLAYBOOK_PROXMOX_CLONER_VM := playbooks/proxmox/cloner_vm_debian.yml
|
|
DOSSIER_PLAYBOOKS_GROUPES := playbooks/groupes
|
|
|
|
.DEFAULT_GOAL := aide
|
|
|
|
.PHONY: ansible-runtime
|
|
ansible-runtime: ## Prepare le repertoire temporaire local d'Ansible (prerequis interne des cibles qui deploient)
|
|
@mkdir -p "$(ANSIBLE_LOCAL_TEMP)"
|
|
@mkdir -p "$(ANSIBLE_SSH_CONTROL_PATH_DIR)"
|
|
|
|
.PHONY: _instance-requise
|
|
_instance-requise:
|
|
@if [[ ! -f "$(SETOPS_INSTANCE)/plan/serveurs.yml" ]]; then \
|
|
printf '%s\n' "Aucune instance configuree : '$(SETOPS_INSTANCE)/plan' introuvable."; \
|
|
printf '%s\n' "Demarre avec QUICKSTART.md. En bref :"; \
|
|
printf '%s\n' " cp -r exemples/modeles/<modele> ../mon-instance && ln -s ../mon-instance instance"; \
|
|
printf '%s\n' " modeles disponibles : $$(ls exemples/modeles 2>/dev/null | grep -v '\.md' | tr '\n' ' ')"; \
|
|
exit 2; \
|
|
fi
|
|
|
|
.PHONY: aide
|
|
aide: ## Affiche l'aide detaillee du moteur (au-dela de cette liste)
|
|
@printf '%s\n' 'Set-OPS — moteur d ecosystemes numeriques souverains'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Nouveau ? -> QUICKSTART.md (de zero a ton ecosysteme sur Proxmox)'
|
|
@printf '%s\n' 'Flux: editer le plan -> make instancier -> make instancier-appliquer -> make deployer'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'VM'
|
|
@printf '%s\n' ' Creer une VM (VMID/IP/VLAN/passerelle lus dans le plan):'
|
|
@printf '%s\n' ' make creer-vm HOTE=web-frontal-01'
|
|
@printf '%s\n' ' Cloner seulement, sans passer par le plan:'
|
|
@printf '%s\n' ' make cloner-vm HOTE=web-frontal-01 VMID=95301 VLAN=15 ADRESSE_IP=10.0.2.31 PASSERELLE=10.0.2.1'
|
|
@printf '%s\n' ' Configurer Proxmox et le Vault API (parametres: docs/config-proxmox.md):'
|
|
@printf '%s\n' ' make config'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Hotes'
|
|
@printf '%s\n' ' Planifier/modifier un hote: editer le plan, puis regenerer:'
|
|
@printf '%s\n' ' editer instance/plan/serveurs.yml (ou la vue Serveurs du GUI)'
|
|
@printf '%s\n' ' make instancier-appliquer'
|
|
@printf '%s\n' ' Verifier un deploiement a blanc (dry-run):'
|
|
@printf '%s\n' ' make verifier-deploiement HOTE=web-frontal-01'
|
|
@printf '%s\n' ' Remettre un hote en conformite selon ses groupes:'
|
|
@printf '%s\n' ' make deployer HOTE=web-frontal-01'
|
|
@printf '%s\n' ' Afficher un hote:'
|
|
@printf '%s\n' ' make hote-afficher HOTE=web-frontal-01'
|
|
@printf '%s\n' ' Diagnostiquer:'
|
|
@printf '%s\n' ' make verifier-hote LIMITE=web-frontal-01'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Groupes'
|
|
@printf '%s\n' ' Appliquer un groupe complet:'
|
|
@printf '%s\n' ' make deployer-groupe GROUPE=serveur_debian'
|
|
@printf '%s\n' ' Convention:'
|
|
@printf '%s\n' ' groupe serveur_debian -> playbooks/groupes/serveur_debian.yml'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Ecosysteme complet (orchestrateur)'
|
|
@printf '%s\n' ' Ordre de deploiement (couches + graphe):'
|
|
@printf '%s\n' ' make site-verifier # valide la coherence couches/graphe'
|
|
@printf '%s\n' ' python3 scripts/orchestrer.py ordre'
|
|
@printf '%s\n' ' (Re)generer playbooks/site.yml ordonne:'
|
|
@printf '%s\n' ' make site'
|
|
@printf '%s\n' ' CONFIGURER la flotte existante, couche par couche (2b, VM deja creees):'
|
|
@printf '%s\n' ' make deployer-tout CONFIRMER=true # (MODE_CHECK=1 pour un essai a blanc idempotent)'
|
|
@printf '%s\n' ' CREER toutes les VM du plan (2a, clone Proxmox):'
|
|
@printf '%s\n' ' make flotte-creer CONFIRMER=true'
|
|
@printf '%s\n' ' RECONSTRUIRE from-zero = creer les VM PUIS deployer (2a+2b, VM inexistantes):'
|
|
@printf '%s\n' ' make reconstruire CONFIRMER=true # alias: make myDay CONFIRMER=true'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Flux reseau (pare-feu / audit)'
|
|
@printf '%s\n' ' Matrice d audit + apercus nftables resolus (NON actives):'
|
|
@printf '%s\n' ' make flux # -> docs/registre-flux.md + instance/flux-genere/*.nft'
|
|
@printf '%s\n' ' make flux-verifier # valide schema + coherence de matrice'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Wiki pedagogique'
|
|
@printf '%s\n' ' Publier wiki/ dans le wiki Forgejo (source versionnee -> vue browsable):'
|
|
@printf '%s\n' ' make wiki-publier WIKI_REMOTE=https://forge.<domaine>/<proprio>/<depot>.wiki.git'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Inventaires'
|
|
@printf '%s\n' ' Graphe de production:'
|
|
@printf '%s\n' ' make inventaire'
|
|
@printf '%s\n' ' Graphe explicite:'
|
|
@printf '%s\n' ' make inventaire-graphe FICHIER_INVENTAIRE=$(SETOPS_INSTANCE)/inventories/production/hosts.yml'
|
|
@printf '%s\n' ' Verifier les inventaires:'
|
|
@printf '%s\n' ' make inventaire-verifier'
|
|
@printf '%s\n' ' Lister les donnees brutes:'
|
|
@printf '%s\n' ' make inventaire-lister'
|
|
@printf '%s\n' ' Interface locale de gestion:'
|
|
@printf '%s\n' ' make inventaire-ui'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Modele Debian 13 Proxmox'
|
|
@printf '%s\n' ' Construire et verifier:'
|
|
@printf '%s\n' ' make preparer-modele'
|
|
@printf '%s\n' ' make verifier-modele'
|
|
@printf '%s\n' ' Nettoyage final protege:'
|
|
@printf '%s\n' ' make nettoyer-modele CONFIRMER=true'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Validation'
|
|
@printf '%s\n' ' make syntaxe'
|
|
@printf '%s\n' ' make lint'
|
|
@printf '%s\n' ' make verifier'
|
|
@printf '%s\n' ''
|
|
@printf '%s\n' 'Variables frequentes'
|
|
@printf '%s\n' ' HOTE=web-frontal-01 GROUPE=serveur_debian GROUPES="serveur_debian serveur_durci"'
|
|
@printf '%s\n' ' VMID=95301 VLAN=15 ADRESSE_IP=10.0.2.31 PASSERELLE=10.0.2.1'
|
|
@printf '%s\n' ' FICHIER_INVENTAIRE=$(SETOPS_INSTANCE)/inventories/production/hosts.yml FICHIER_DEPENDANCES=docs/dependances-groupes.yml CONFIRMER=true'
|
|
|
|
.PHONY: lint
|
|
lint: ansible-runtime ## Passe ansible-lint sur tout le depot
|
|
ansible-lint
|
|
|
|
.PHONY: syntaxe syntaxe-modele syntaxe-nettoyage syntaxe-verification-modele syntaxe-verification-hote syntaxe-groupes syntaxe-proxmox
|
|
syntaxe: syntaxe-modele syntaxe-verification-modele syntaxe-nettoyage syntaxe-verification-hote syntaxe-groupes syntaxe-proxmox ## Verifie la syntaxe de TOUS les playbooks (modele, hote, groupes, proxmox)
|
|
|
|
syntaxe-modele: ansible-runtime ## Verifie la syntaxe du playbook de preparation du gabarit dore
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_PREPARER_MODELE) --syntax-check
|
|
|
|
syntaxe-verification-modele: ansible-runtime ## Verifie la syntaxe du playbook de verification du gabarit
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_VERIFIER_MODELE) --syntax-check
|
|
|
|
syntaxe-nettoyage: ansible-runtime ## Verifie la syntaxe du playbook de nettoyage du gabarit
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_NETTOYER_MODELE) --syntax-check
|
|
|
|
syntaxe-verification-hote: ansible-runtime ## Verifie la syntaxe du playbook de verification d'hote
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) $(PLAYBOOK_VERIFIER_HOTE) --syntax-check
|
|
|
|
syntaxe-groupes: ansible-runtime ## Verifie la syntaxe des 30 playbooks de groupe
|
|
@for playbook in $(DOSSIER_PLAYBOOKS_GROUPES)/*.yml; do \
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --syntax-check; \
|
|
done
|
|
|
|
syntaxe-proxmox: ansible-runtime ## Verifie la syntaxe du playbook de clonage de VM
|
|
ansible-playbook -i localhost, $(PLAYBOOK_PROXMOX_CLONER_VM) --syntax-check
|
|
|
|
.PHONY: test
|
|
test: ## Lance les tests unitaires (derivation de nomenclature et d'inventaire)
|
|
python3 scripts/tests/test_inventory_host.py
|
|
python3 scripts/tests/test_raser.py
|
|
|
|
.PHONY: verifier
|
|
verifier: lint test inventaire-verifier site-verifier flux-verifier syntaxe ## Rejoue les preuves SANS reecrire le rapport (verification rapide)
|
|
python3 scripts/prouver.py --verifier
|
|
|
|
# Harnais de preuve : rejoue les preuves automatisables du registre et ecrit
|
|
# docs/audit/preuve-<date>.md (piece justificative horodatee, rejouable).
|
|
# `make verifier` l'appelle en mode --verifier (preuves seules, aucun rapport ecrit).
|
|
.PHONY: prouver
|
|
prouver: ansible-runtime _instance-requise ## Execute les preuves et ecrit docs/audit/preuve-<date>.md
|
|
python3 scripts/prouver.py
|
|
|
|
.PHONY: inventaire hote-planifier hote-ajouter hote-groupes hote-afficher appliquer deployer deployer-groupe cloner-vm creer-vm config inventaire-ui inventaire-verifier inventaire-lister inventaire-graphe inventaire-hote inventaire-lab inventaire-production instance-utiliser instance-courante
|
|
inventaire: inventaire-production ## Verifie l'inventaire et en affiche le graphe (lab puis production)
|
|
|
|
# Bascule le symlink 'instance' vers un autre dépôt d'instance (séparation par
|
|
# instance : prod vs bac à sable). Ex. : make instance-utiliser NOM=OPS-Chezlepro-lab
|
|
instance-utiliser: ## Bascule l'instance active (symlink instance/) vers un dossier frere — NOM=<dossier>
|
|
@if [[ -z "$(NOM)" ]]; then printf '%s\n' "Usage: make instance-utiliser NOM=<dossier-frère> (ex. OPS-Chezlepro-lab)"; exit 2; fi
|
|
@if [[ ! -d "../$(NOM)" ]]; then printf '%s\n' "Introuvable: ../$(NOM)"; exit 2; fi
|
|
@if [[ -e instance && ! -L instance ]]; then printf '%s\n' "Refus: 'instance' existe et n'est pas un symlink."; exit 2; fi
|
|
@rm -f instance && ln -s "../$(NOM)" instance
|
|
@printf 'instance -> %s\n' "$$(readlink instance)"
|
|
|
|
instance-courante: ## Affiche vers quel ecosysteme pointe l'instance active
|
|
@printf 'instance -> %s\n' "$$(readlink instance 2>/dev/null || echo '(non monté)')"
|
|
|
|
# Vue d'ensemble : toutes les instances de la fédération, l'active (*), leur index,
|
|
# plage VLAN, statut fédéré/prod ; signale les collisions d'index. Lecture seule.
|
|
instances: ## Liste les ecosystemes decouverts (dossiers freres) et signale les collisions d'index
|
|
@python3 scripts/instances.py
|
|
|
|
# (Re)génère le plan de recette (docs/audit/plan-de-recette.md) depuis les exercices
|
|
# du wiki. La preuve P22 vérifie qu'il reste à jour.
|
|
plan-recette: ## Regenere docs/audit/plan-de-recette.md depuis le wiki
|
|
@python3 scripts/plan_recette.py
|
|
|
|
# Liste les modèles disponibles (socle + SETOPS_MODELES) pour créer une instance.
|
|
instance-modeles: ## Liste les modeles d'ecosysteme disponibles
|
|
@python3 scripts/instance_creer.py --lister-modeles
|
|
|
|
# Crée un dépôt d'instance frère depuis un modèle. Ne bascule pas le symlink.
|
|
# Ex. : make instance-creer NOM=OPS-ClientX MODELE=socle INDEX=4
|
|
instance-creer: ## Cree un nouvel ecosysteme depuis un modele — NOM=<nom> MODELE=<modele>
|
|
@python3 scripts/instance_creer.py --nom "$(NOM)" --modele "$(MODELE)" \
|
|
$(if $(INDEX),--index $(INDEX),)
|
|
|
|
# Crée un MODÈLE (dépôt privé). Deux modes :
|
|
# base : copier un modèle générique -> make model-creer MODE=base BASE=identite NOM=maison-obnl
|
|
# instance : promouvoir une instance -> make model-creer MODE=instance SOURCE=OPS-Chezlepro NOM=cabinet
|
|
model-creer: ## Cree un modele d'ecosysteme — MODE=<mode> NOM=<nom>
|
|
@python3 scripts/model_creer.py --mode "$(MODE)" --nom "$(NOM)" \
|
|
$(if $(BASE),--base $(BASE),) $(if $(SOURCE),--source $(SOURCE),) $(if $(DEST),--dest $(DEST),)
|
|
|
|
config: ## Affiche la configuration Proxmox lue par le moteur
|
|
python3 scripts/config_proxmox.py
|
|
|
|
inventaire-ui: _instance-requise ## Ouvre la console d'exploitation (GUI web) sur l'inventaire actif
|
|
python3 scripts/inventory_gui.py --inventaire $(FICHIER_INVENTAIRE)
|
|
|
|
hote-ajouter hote-planifier hote-groupes:
|
|
@printf '%s\n' 'Cible depreciee: l inventaire est GENERE depuis le plan, il ne s edite plus a la main.'
|
|
@printf '%s\n' 'Declare ou modifie l hote dans instance/plan/serveurs.yml (ou la vue Serveurs du GUI), puis :'
|
|
@printf '%s\n' ' make instancier-appliquer'
|
|
@printf '%s\n' '(creer-vm lit desormais VMID/IP/VLAN/passerelle directement dans l inventaire genere.)'
|
|
@exit 2
|
|
|
|
hote-afficher: ansible-runtime ## Affiche tout ce que le plan derive pour un hote — HOTE=<nom>
|
|
@if [[ -z "$(HOTE)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
|
|
exit 2; \
|
|
fi
|
|
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) afficher --hote $(HOTE)
|
|
|
|
appliquer: ansible-runtime ## Applique un groupe a la flotte — GROUPE=<groupe>
|
|
@if [[ -z "$(GROUPE)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec GROUPE=nom_groupe.'; \
|
|
exit 2; \
|
|
fi
|
|
@if [[ ! -f "$(DOSSIER_PLAYBOOKS_GROUPES)/$(GROUPE).yml" ]]; then \
|
|
printf '%s\n' 'Refus: aucun playbook pour ce groupe: $(DOSSIER_PLAYBOOKS_GROUPES)/$(GROUPE).yml'; \
|
|
exit 2; \
|
|
fi
|
|
python3 scripts/inventory_host.py --inventaire $(INVENTAIRE_PRODUCTION) --dependances $(FICHIER_DEPENDANCES) verifier-dependances-groupe --groupe $(GROUPE)
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$(DOSSIER_PLAYBOOKS_GROUPES)/$(GROUPE).yml" --limit '$(GROUPE):&$(GROUPE_HOTES_ACTIFS)'
|
|
|
|
deployer: _instance-requise ## Deploie un hote, couche par couche, dans l'ordre du graphe — HOTE=<nom>
|
|
@set -e; \
|
|
if [[ -z "$(HOTE)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
|
|
exit 2; \
|
|
fi; \
|
|
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) verifier-actif --hote $(HOTE); \
|
|
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) --dependances $(FICHIER_DEPENDANCES) verifier-dependances-hote --hote $(HOTE); \
|
|
playbooks="$$(python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) playbooks --hote $(HOTE) --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES))"; \
|
|
if [[ -z "$$playbooks" ]]; then \
|
|
printf '%s\n' 'Refus: aucun playbook applicable pour HOTE=$(HOTE).'; \
|
|
exit 2; \
|
|
fi; \
|
|
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' $(dir $(INVENTAIRE_PRODUCTION))group_vars 2>/dev/null | head -1 || true)"; \
|
|
if [[ -n "$$vault_chiffre" && -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
|
|
if [[ -t 0 ]]; then \
|
|
read -r -s -p 'Mot de passe du vault Ansible: ' mdp; echo; \
|
|
vf="$$(mktemp)"; printf '%s' "$$mdp" > "$$vf"; chmod 600 "$$vf"; \
|
|
export ANSIBLE_VAULT_PASSWORD_FILE="$$vf"; \
|
|
trap 'rm -f "$$vf"' EXIT; \
|
|
else \
|
|
printf '%s\n' 'Refus: vault chiffre detecte mais aucun mot de passe (entree non interactive). Fournir ANSIBLE_VAULT_PASSWORD_FILE ou le champ vault de la GUI.'; \
|
|
exit 2; \
|
|
fi; \
|
|
fi; \
|
|
$(MAKE) _verifier-acces-hote LIMITE="$(HOTE)"; \
|
|
$(MAKE) _verifier-privileges-hote LIMITE="$(HOTE)"; \
|
|
for playbook in $$playbooks; do \
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --limit "$(HOTE)"; \
|
|
done; \
|
|
$(MAKE) verifier-hote LIMITE="$(HOTE)"
|
|
|
|
.PHONY: site site-verifier deployer-tout
|
|
site: ansible-runtime ## Regenere playbooks/site.yml depuis les couches et le graphe de dependances
|
|
python3 scripts/orchestrer.py ecrire
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) playbooks/site.yml --syntax-check
|
|
|
|
site-verifier: ## Verifie que playbooks/site.yml correspond aux couches declarees
|
|
python3 scripts/orchestrer.py verifier
|
|
|
|
.PHONY: flux flux-verifier
|
|
flux: ansible-runtime ## Regenere le registre des flux et les regles nftables depuis les meta/flux.yml
|
|
python3 scripts/resoudre_flux.py registre
|
|
python3 scripts/resoudre_flux.py nftables
|
|
|
|
.PHONY: devis-reseau
|
|
devis-reseau: ansible-runtime ## Devis switch (VLANs/SVIs/ACLs) du reseau converge, derive des nomenclatures. DIALECTE=cisco|binardat
|
|
python3 scripts/devis_reseau.py $(if $(DIALECTE),--dialecte $(DIALECTE),)
|
|
|
|
.PHONY: devis-opnsense devis-opnsense-verifier
|
|
devis-opnsense: ansible-runtime ## Devis OPNsense (frontiere nord/sud), derive du registre des flux (pair: externe)
|
|
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-appliquer: ansible-runtime ## Reconcilie le pare-feu est-ouest : IPSets, groupes, affectations. CONFIRMER=true
|
|
@if [[ "$(CONFIRMER)" != "true" ]]; then \
|
|
printf '%s\n' "Refus: cette cible ECRIT sur le pare-feu du cluster (IPSets, groupes, VM)" \
|
|
"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
|
|
|
|
.PHONY: sdn-plan sdn-appliquer
|
|
sdn-plan: ansible-runtime ## Ecart entre le SDN EVPN (+ sortie des VRF) et son devis (aucune ecriture)
|
|
python3 scripts/appliquer_sdn.py
|
|
|
|
sdn-appliquer: ansible-runtime ## Reconcilie le SDN : cree ce qui manque, RETIRE ce qui est perime. CONFIRMER=true
|
|
@if [[ "$(CONFIRMER)" != "true" ]]; then \
|
|
printf '%s\n' "Refus: cette cible ECRIT sur le cluster (zones, VNets, sous-reseaux) et sur" \
|
|
"les noeuds de sortie (/etc/frr/frr.conf.local), et RETIRE ce qui est perime." \
|
|
"Relire d'abord 'make sdn-plan', puis: make sdn-appliquer CONFIRMER=true"; \
|
|
exit 2; fi
|
|
CONFIRMER=true python3 scripts/appliquer_sdn.py
|
|
|
|
.PHONY: ca-racine
|
|
# Ou deposer la racine. Par defaut le repertoire courant : c'est un certificat
|
|
# PUBLIC, pas un secret — il n'a rien a faire dans la voute, et tout a faire dans
|
|
# le magasin de confiance de qui administre.
|
|
CA_RACINE_DEST ?= ./root_ca.crt
|
|
|
|
ca-racine: ansible-runtime _instance-requise ## Recupere la racine de l'AC interne + son empreinte (a verifier AVANT de l'installer)
|
|
@set -e; \
|
|
hote="$$(python3 -c "import yaml,sys; d=yaml.safe_load(open('$(FICHIER_INVENTAIRE)')); \
|
|
import itertools; \
|
|
g=d['all']['children']; \
|
|
print(next(iter((g.get('serveur_step_ca') or {}).get('hosts') or {}), ''))" 2>/dev/null || true)"; \
|
|
if [[ -z "$$hote" ]]; then \
|
|
printf '%s\n' "Refus: aucun hote ne porte 'serveur_step_ca' dans $(FICHIER_INVENTAIRE)." \
|
|
"Cette instance n'a pas d'autorite de certification interne."; exit 2; fi; \
|
|
ansible -i $(FICHIER_INVENTAIRE) "$$hote" --become \
|
|
-m fetch -a "src=/etc/step-ca/certs/root_ca.crt dest=$(CA_RACINE_DEST) flat=yes" >/dev/null; \
|
|
printf '%s\n' "Racine de l'AC ecrite dans $(CA_RACINE_DEST) (depuis $$hote)."; \
|
|
printf '%s\n' "" " sujet : $$(openssl x509 -in $(CA_RACINE_DEST) -noout -subject | sed 's/^subject=//')"; \
|
|
printf '%s\n' " valide : $$(openssl x509 -in $(CA_RACINE_DEST) -noout -enddate | sed 's/^notAfter=//')"; \
|
|
printf '%s\n' " empreinte: $$(openssl x509 -in $(CA_RACINE_DEST) -noout -fingerprint -sha256 | sed 's/^.*=//' | tr -d ':' | tr 'A-Z' 'a-z')"; \
|
|
printf '%s\n' "" \
|
|
"COMPARE l'empreinte avec celle de l'AC avant de l'installer :" \
|
|
" make ca-empreinte" \
|
|
"" \
|
|
"Installer une AC, c'est lui donner le droit de signer N'IMPORTE QUEL nom" \
|
|
"pour ton navigateur. La comparaison est ce qui distingue ta racine d'une" \
|
|
"racine interceptee — ce n'est pas une formalite." \
|
|
"" \
|
|
" sudo cp $(CA_RACINE_DEST) /usr/local/share/ca-certificates/setops-root.crt" \
|
|
" sudo update-ca-certificates" \
|
|
" (Firefox a son propre magasin : Parametres > Certificats > Autorites)"
|
|
|
|
.PHONY: ca-empreinte
|
|
ca-empreinte: ansible-runtime _instance-requise ## Empreinte de la racine, lue SUR l'AC (le temoin de comparaison)
|
|
@set -e; \
|
|
hote="$$(python3 -c "import yaml; d=yaml.safe_load(open('$(FICHIER_INVENTAIRE)')); \
|
|
g=d['all']['children']; \
|
|
print(next(iter((g.get('serveur_step_ca') or {}).get('hosts') or {}), ''))" 2>/dev/null || true)"; \
|
|
if [[ -z "$$hote" ]]; then \
|
|
printf '%s\n' "Refus: aucun hote ne porte 'serveur_step_ca'."; exit 2; fi; \
|
|
ansible -i $(FICHIER_INVENTAIRE) "$$hote" --become -m command \
|
|
-a "step certificate fingerprint /etc/step-ca/certs/root_ca.crt" 2>/dev/null \
|
|
| tail -1 | tr -d ' \r'
|
|
|
|
.PHONY: frontiere-plan frontiere-appliquer
|
|
courriel-plan: ansible-runtime ## Chaine Postfix -> LDAP -> Dovecot -> IMAP (aucune ecriture)
|
|
@rm -f $(SETOPS_INSTANCE)/devis-courriel.json.*
|
|
@ansible-playbook -i $(SETOPS_INVENTAIRE) playbooks/maintenance/devis-courriel.yml >/dev/null
|
|
@python3 scripts/devis_courriel.py
|
|
|
|
postgresql-plan: ansible-runtime ## Chiffrement impose et portee des acces PostgreSQL (aucune ecriture)
|
|
@rm -f $(SETOPS_INSTANCE)/devis-postgresql.json.*
|
|
@ansible-playbook -i $(SETOPS_INVENTAIRE) playbooks/maintenance/devis-postgresql.yml >/dev/null
|
|
@python3 scripts/devis_postgresql.py
|
|
|
|
expositions-plan: ansible-runtime ## Chaque exposition du plan repond-elle ? (edge et poste, aucune ecriture)
|
|
@ansible-playbook -i $(SETOPS_INVENTAIRE) playbooks/maintenance/devis-expositions.yml >/dev/null
|
|
@python3 scripts/devis_expositions.py
|
|
|
|
certificats-plan: ansible-runtime ## Ecart entre les certificats sur disque et ceux reellement servis (aucune ecriture)
|
|
@rm -f $(SETOPS_INSTANCE)/devis-certificats.json.*
|
|
@ansible-playbook -i $(SETOPS_INVENTAIRE) playbooks/maintenance/devis-certificats.yml >/dev/null
|
|
@python3 scripts/devis_certificats.py
|
|
|
|
identite-plan: ansible-runtime ## Ecart entre l'identite deployee et ce que le plan derive (aucune ecriture)
|
|
@ansible-playbook -i $(SETOPS_INVENTAIRE) playbooks/maintenance/devis-identite.yml >/dev/null
|
|
@python3 scripts/devis_identite.py
|
|
|
|
frontiere-plan: ansible-runtime ## Ecart entre la frontiere OPNsense et son devis (aucune ecriture)
|
|
python3 scripts/appliquer_opnsense.py
|
|
|
|
frontiere-appliquer: ansible-runtime ## Reconcilie la frontiere : cree ce qui manque, RETIRE ce qui est perime. CONFIRMER=true
|
|
@if [[ "$(CONFIRMER)" != "true" ]]; then \
|
|
printf '%s\n' "Refus: cette cible ECRIT sur le pare-feu de bordure et RETIRE les regles perimees." \
|
|
"Relire d'abord 'make frontiere-plan', puis: make frontiere-appliquer CONFIRMER=true"; \
|
|
exit 2; fi
|
|
CONFIRMER=true python3 scripts/appliquer_opnsense.py
|
|
|
|
.PHONY: devis-proxmox-fw devis-proxmox-fw-verifier
|
|
devis-proxmox-fw: ansible-runtime ## Devis pare-feu Proxmox (est-ouest intra-tenant), derive du registre des flux
|
|
python3 scripts/devis_proxmox_fw.py $(if $(JSON),--json,)
|
|
|
|
devis-proxmox-fw-verifier: ## Verifie le devis du pare-feu est-ouest Proxmox (aucune ecriture)
|
|
python3 scripts/devis_proxmox_fw.py --verifier
|
|
|
|
.PHONY: devis-proxmox-pools devis-proxmox-pools-verifier
|
|
devis-proxmox-pools: ansible-runtime ## Devis des pools Proxmox (un par tenant), derive du plan
|
|
python3 scripts/devis_proxmox_pools.py $(if $(JSON),--json,)
|
|
|
|
devis-proxmox-pools-verifier: ## Verifie le devis des pools Proxmox (aucune ecriture)
|
|
python3 scripts/devis_proxmox_pools.py --verifier
|
|
|
|
.PHONY: devis-sdn devis-sdn-verifier
|
|
devis-sdn: ansible-runtime ## Devis SDN EVPN (zone + VNets + sous-reseaux par tenant), derive du seed
|
|
python3 scripts/devis_sdn.py $(if $(JSON),--json,)
|
|
|
|
devis-sdn-verifier: ## Verifie le devis SDN EVPN — zones, VNets, sous-reseaux (aucune ecriture)
|
|
python3 scripts/devis_sdn.py --verifier
|
|
|
|
devis-opnsense-verifier: ## Verifie le devis de la frontiere nord/sud (aucune ecriture)
|
|
python3 scripts/devis_opnsense.py --verifier
|
|
|
|
.PHONY: underlay
|
|
underlay: ## Underlay (fabric physique cluster-global : mgmt/iSCSI/Ceph) : affiche + valide (P23)
|
|
python3 scripts/underlay.py
|
|
|
|
flux-verifier: ## Verifie que le registre des flux correspond aux meta/flux.yml des roles
|
|
python3 scripts/resoudre_flux.py verifier
|
|
|
|
.PHONY: valider
|
|
valider: ansible-runtime ## Passe la recette de validation sur la flotte
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) playbooks/valider.yml
|
|
|
|
.PHONY: wiki-publier
|
|
wiki-publier: ## Publie le wiki (wiki/) vers la forge
|
|
@set -e; \
|
|
if [[ -z "$(WIKI_REMOTE)" ]]; then \
|
|
printf '%s\n' 'Refus: URL du wiki Forgejo requise.'; \
|
|
printf '%s\n' 'Ex: make wiki-publier WIKI_REMOTE=https://forge.<domaine>/<proprio>/<depot>.wiki.git'; \
|
|
exit 2; \
|
|
fi; \
|
|
src="$(CURDIR)/wiki"; \
|
|
tmp="$$(mktemp -d)"; \
|
|
trap 'rm -rf "$$tmp"' EXIT; \
|
|
printf '%s\n' "Clonage du wiki: $(WIKI_REMOTE)"; \
|
|
if ! git clone --quiet --depth 1 "$(WIKI_REMOTE)" "$$tmp/wiki"; then \
|
|
printf '%s\n' 'Echec du clone (URL ou acces ?). Le wiki doit exister (creer une 1re page dans Forgejo).'; \
|
|
exit 1; \
|
|
fi; \
|
|
find "$$tmp/wiki" -maxdepth 1 -name '*.md' -delete; \
|
|
for f in "$$src"/*.md; do \
|
|
bn="$$(basename "$$f")"; \
|
|
[[ "$$bn" == "README.md" ]] && continue; \
|
|
cp "$$f" "$$tmp/wiki/$$bn"; \
|
|
done; \
|
|
rm -rf "$$tmp/wiki/img"; \
|
|
if compgen -G "$$src/img/*-annote.svg" > /dev/null; then \
|
|
mkdir -p "$$tmp/wiki/img"; \
|
|
cp -L "$$src"/img/*-annote.svg "$$tmp/wiki/img/"; \
|
|
fi; \
|
|
cd "$$tmp/wiki"; \
|
|
if [[ -z "$$(git status --porcelain)" ]]; then \
|
|
printf '%s\n' 'Wiki deja a jour (aucun changement).'; \
|
|
exit 0; \
|
|
fi; \
|
|
git add -A; \
|
|
sha="$$(git -C "$(CURDIR)" rev-parse --short HEAD 2>/dev/null || echo inconnu)"; \
|
|
git commit --quiet -m "Publication du wiki depuis le depot (source: $$sha)"; \
|
|
git push --quiet; \
|
|
printf '%s\n' 'Wiki publie.'
|
|
|
|
deployer-tout: _instance-requise ## Deploie TOUTE la flotte dans l'ordre des couches
|
|
@set -e; \
|
|
if [[ "$(CONFIRMER)" != "true" ]]; then \
|
|
printf '%s\n' 'Refus: deploiement ORCHESTRE de TOUTE la flotte (action impactante).'; \
|
|
printf '%s\n' 'Relancer avec CONFIRMER=true. Astuce: tester d abord en idempotent avec MODE_CHECK=1.'; \
|
|
exit 2; \
|
|
fi; \
|
|
python3 scripts/orchestrer.py verifier; \
|
|
python3 scripts/orchestrer.py ecrire; \
|
|
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' $(dir $(INVENTAIRE_PRODUCTION))group_vars 2>/dev/null | head -1 || true)"; \
|
|
if [[ -n "$$vault_chiffre" && -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
|
|
if [[ -t 0 ]]; then \
|
|
read -r -s -p 'Mot de passe du vault Ansible: ' mdp; echo; \
|
|
vf="$$(mktemp)"; printf '%s' "$$mdp" > "$$vf"; chmod 600 "$$vf"; \
|
|
export ANSIBLE_VAULT_PASSWORD_FILE="$$vf"; \
|
|
trap 'rm -f "$$vf"' EXIT; \
|
|
else \
|
|
printf '%s\n' 'Refus: vault chiffre detecte mais aucun mot de passe (entree non interactive). Fournir ANSIBLE_VAULT_PASSWORD_FILE ou le champ vault de la GUI.'; \
|
|
exit 2; \
|
|
fi; \
|
|
fi; \
|
|
$(MAKE) _verifier-acces-hote LIMITE="$(GROUPE_HOTES_ACTIFS)"; \
|
|
$(MAKE) _verifier-privileges-hote LIMITE="$(GROUPE_HOTES_ACTIFS)"; \
|
|
mode="$$([[ -n "$(MODE_CHECK)" ]] && printf -- '--check --diff' || true)"; \
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) playbooks/site.yml --limit "$(GROUPE_HOTES_ACTIFS)" $$mode
|
|
|
|
# --- Reconstruction from-zero : creer TOUTES les VM (2a) puis deployer (2b) ---
|
|
|
|
.PHONY: flotte-creer
|
|
flotte-creer: _instance-requise ## Cree les VM manquantes de la flotte depuis le plan
|
|
@set -e; \
|
|
if [[ "$(CONFIRMER)" != "true" ]]; then \
|
|
printf '%s\n' 'Refus: creation de TOUTES les VM actives du plan (clone Proxmox).'; \
|
|
printf '%s\n' 'Relancer avec CONFIRMER=true.'; \
|
|
exit 2; \
|
|
fi; \
|
|
hotes="$$(python3 scripts/inventory_host.py --inventaire $(INVENTAIRE_PRODUCTION) lister-actifs)"; \
|
|
if [[ -z "$$hotes" ]]; then printf '%s\n' 'Refus: aucun hote actif dans le plan.'; exit 2; fi; \
|
|
for h in $$hotes; do \
|
|
printf '\n=== Creation VM: %s ===\n' "$$h"; \
|
|
$(MAKE) creer-vm HOTE="$$h"; \
|
|
done; \
|
|
printf '\nToutes les VM actives sont creees.\n'
|
|
|
|
.PHONY: _attendre-flotte
|
|
_attendre-flotte: ansible-runtime
|
|
@set -e; \
|
|
max="$${ATTENTE_MAX:-600}"; deadline=$$(( $$(date +%s) + max )); \
|
|
printf 'Attente que la flotte reponde en SSH (max %ss)...\n' "$$max"; \
|
|
until ansible -i $(INVENTAIRE_PRODUCTION) $(GROUPE_HOTES_ACTIFS) -m ping -e ansible_become=false >/dev/null 2>&1; do \
|
|
if (( $$(date +%s) > deadline )); then printf 'Timeout: flotte injoignable apres %ss.\n' "$$max"; exit 1; fi; \
|
|
sleep 10; \
|
|
done; \
|
|
printf 'Flotte joignable.\n'
|
|
|
|
.PHONY: reconstruire
|
|
raser: ansible-runtime _instance-requise ## DESTRUCTIF : detruit les VM derivees du plan — exige CONFIRMER=true ET INSTANCE=<nom>
|
|
@python3 scripts/raser.py $(if $(INSTANCE),--instance $(INSTANCE)) $(if $(filter true,$(CONFIRMER)),--confirmer)
|
|
|
|
reconstruire: _instance-requise ## Reconstruit un ecosysteme depuis zero : VM puis deploiement complet
|
|
@set -e; \
|
|
if [[ "$(CONFIRMER)" != "true" ]]; then \
|
|
printf '%s\n' 'Refus: RECONSTRUCTION — cree les VM manquantes (2a) PUIS deploie tout (2b).'; \
|
|
printf '%s\n' 'Idempotent : une VM deja presente est sautee (clone par nom), le deploiement converge.'; \
|
|
printf '%s\n' 'Relancer avec CONFIRMER=true.'; \
|
|
exit 2; \
|
|
fi; \
|
|
$(MAKE) flotte-creer CONFIRMER=true; \
|
|
$(MAKE) _attendre-flotte; \
|
|
$(MAKE) deployer-tout CONFIRMER=true
|
|
|
|
# « Go ahead, make my day. » — LE bouton rouge : alias de reconstruire (Clint Eastwood).
|
|
# Cree toutes les VM puis deploie tout, en une commande. Garde CONFIRMER=true.
|
|
.PHONY: myDay
|
|
myDay: reconstruire ## Alias strict de `reconstruire` (meme cible, memes gardes)
|
|
|
|
deployer-groupe: ## Deploie un seul groupe sur toute la flotte — GROUPE=<groupe>
|
|
@if [[ -z "$(GROUPE)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec GROUPE=nom_groupe.'; \
|
|
exit 2; \
|
|
fi
|
|
$(MAKE) appliquer GROUPE="$(GROUPE)"
|
|
|
|
.PHONY: verifier-deploiement
|
|
verifier-deploiement: ansible-runtime ## Verifie l'etat de la flotte apres deploiement
|
|
@set -e; \
|
|
if [[ -z "$(HOTE)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
|
|
exit 2; \
|
|
fi; \
|
|
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) verifier-actif --hote $(HOTE); \
|
|
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) --dependances $(FICHIER_DEPENDANCES) verifier-dependances-hote --hote $(HOTE); \
|
|
playbooks="$$(python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) playbooks --hote $(HOTE) --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES))"; \
|
|
if [[ -z "$$playbooks" ]]; then \
|
|
printf '%s\n' 'Refus: aucun playbook applicable pour HOTE=$(HOTE).'; \
|
|
exit 2; \
|
|
fi; \
|
|
vault_chiffre="$$(grep -rlsIF '$$ANSIBLE_VAULT' $(dir $(INVENTAIRE_PRODUCTION))group_vars 2>/dev/null | head -1 || true)"; \
|
|
if [[ -n "$$vault_chiffre" && -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
|
|
if [[ -t 0 ]]; then \
|
|
read -r -s -p 'Mot de passe du vault Ansible: ' mdp; echo; \
|
|
vf="$$(mktemp)"; printf '%s' "$$mdp" > "$$vf"; chmod 600 "$$vf"; \
|
|
export ANSIBLE_VAULT_PASSWORD_FILE="$$vf"; \
|
|
trap 'rm -f "$$vf"' EXIT; \
|
|
else \
|
|
printf '%s\n' 'Refus: vault chiffre detecte mais aucun mot de passe (entree non interactive). Fournir ANSIBLE_VAULT_PASSWORD_FILE ou le champ vault de la GUI.'; \
|
|
exit 2; \
|
|
fi; \
|
|
fi; \
|
|
for playbook in $$playbooks; do \
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --limit "$(HOTE)" --check --diff; \
|
|
done
|
|
|
|
cloner-vm: ansible-runtime ## Clone une VM depuis le gabarit dore — HOTE=<nom> VMID=<id>
|
|
@if [[ -z "$(HOTE)" || -z "$(VMID)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec HOTE=nom VMID=id_clone.'; \
|
|
exit 2; \
|
|
fi
|
|
@# En SDN EVPN, l'etiquette est portee par le VNet : `instancier` emet donc un VLAN
|
|
@# VIDE et un pont derive (t17serv). Exiger un VLAN ici rejetait toute VM de tenant.
|
|
@# Un VLAN vide n'est accepte QUE si un pont est fourni — sinon la VM n'aurait ni
|
|
@# etiquette ni VNet, et se retrouverait branchee nulle part.
|
|
@if [[ -z "$(VLAN)" && -z "$(PONT_PROXMOX)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec VLAN=id_vlan, ou PONT_PROXMOX=<vnet> en SDN.'; \
|
|
exit 2; \
|
|
fi
|
|
@if [[ -n "$(VLAN)" ]] && { ! [[ "$(VLAN)" =~ ^[0-9]+$$ ]] || (( 10#$(VLAN) < 1 || 10#$(VLAN) > 4094 )); }; then \
|
|
printf '%s\n' 'Refus: VLAN doit etre un nombre entre 1 et 4094.'; \
|
|
exit 2; \
|
|
fi
|
|
@if [[ -n "$(CLE_SSH_PUBLIQUE)" && ! -f "$(CLE_SSH_PUBLIQUE)" ]]; then \
|
|
printf '%s\n' 'Refus: cle publique SSH introuvable: $(CLE_SSH_PUBLIQUE)'; \
|
|
exit 2; \
|
|
fi
|
|
@if [[ "$(DHCP)" != "true" && ( -z "$(ADRESSE_IP)" || -z "$(CIDR)" || -z "$(PASSERELLE)" ) ]]; then \
|
|
printf '%s\n' 'Refus: fournir ADRESSE_IP, CIDR et PASSERELLE, ou utiliser DHCP=true.'; \
|
|
exit 2; \
|
|
fi
|
|
@ipconfig='ip=dhcp'; \
|
|
if [[ "$(DHCP)" != "true" ]]; then \
|
|
ipconfig='ip=$(ADRESSE_IP)/$(CIDR),gw=$(PASSERELLE)'; \
|
|
fi; \
|
|
extra_vars=( \
|
|
-e proxmox_clone_nom="$(HOTE)" \
|
|
-e proxmox_clone_vmid="$(VMID)" \
|
|
-e proxmox_clone_ipconfig0="$$ipconfig" \
|
|
-e proxmox_clone_pool="$$(python3 scripts/devis_proxmox_pools.py --pool-actif)" \
|
|
); \
|
|
[[ -n "$(VMID_MODELE)" ]] && extra_vars+=( -e proxmox_clone_vmid_modele="$(VMID_MODELE)" ); \
|
|
[[ -n "$(NOEUD_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_noeud="$(NOEUD_PROXMOX)" ); \
|
|
[[ -n "$(STOCKAGE_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_stockage="$(STOCKAGE_PROXMOX)" ); \
|
|
[[ -n "$(FORMAT_DISQUE)" ]] && extra_vars+=( -e proxmox_clone_format="$(FORMAT_DISQUE)" ); \
|
|
[[ -n "$(CLONE_COMPLET)" ]] && extra_vars+=( -e proxmox_clone_complet="$(CLONE_COMPLET)" ); \
|
|
[[ -n "$(TAILLE_DISQUE)" ]] && extra_vars+=( -e proxmox_clone_taille_disque="$(TAILLE_DISQUE)" ); \
|
|
[[ -n "$(COEURS)" ]] && extra_vars+=( -e proxmox_clone_coeurs="$(COEURS)" ); \
|
|
[[ -n "$(MEMOIRE)" ]] && extra_vars+=( -e proxmox_clone_memoire="$(MEMOIRE)" ); \
|
|
[[ -n "$(DISQUE_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_disque="$(DISQUE_PROXMOX)" ); \
|
|
[[ -n "$(DNS)" ]] && extra_vars+=( -e proxmox_clone_dns="$(DNS)" ); \
|
|
[[ -n "$(CIUSER)" ]] && extra_vars+=( -e proxmox_clone_ciuser="$(CIUSER)" ); \
|
|
[[ -n "$(CLE_SSH_PUBLIQUE)" ]] && extra_vars+=( -e proxmox_clone_cle_publique_fichier="$(CLE_SSH_PUBLIQUE)" ); \
|
|
[[ -n "$(PONT_PROXMOX)" ]] && extra_vars+=( -e proxmox_clone_pont="$(PONT_PROXMOX)" ); \
|
|
extra_vars+=( -e proxmox_clone_vlan="$(VLAN)" ); \
|
|
[[ -n "$(DEMARRER)" ]] && extra_vars+=( -e proxmox_clone_demarrer="$(DEMARRER)" ); \
|
|
vault_args=(); \
|
|
vault_file=""; \
|
|
for d in lab principal production; do \
|
|
for gv in group_vars/all/vault.yml group_vars/proxmox.vault.yml; do \
|
|
f="$(SETOPS_INSTANCE)/inventories/$$d/$$gv"; \
|
|
[[ -f "$$f" ]] && vault_file="$$f" && break 2; \
|
|
done; \
|
|
done; \
|
|
if [[ -n "$$vault_file" && -f "$$vault_file" ]]; then \
|
|
read -r premiere_ligne < "$$vault_file" || true; \
|
|
case "$$premiere_ligne" in \
|
|
'$$ANSIBLE_VAULT'*) \
|
|
if [[ -z "$${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then \
|
|
vault_args+=( --ask-vault-pass ); \
|
|
fi; \
|
|
;; \
|
|
esac; \
|
|
fi; \
|
|
ansible-playbook -i localhost, $(PLAYBOOK_PROXMOX_CLONER_VM) "$${vault_args[@]}" "$${extra_vars[@]}"
|
|
|
|
creer-vm: _instance-requise ## Cree une VM et attend qu'elle soit joignable — HOTE=<nom>
|
|
@set -e; \
|
|
if [[ -z "$(HOTE)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote (declare dans le plan).'; \
|
|
exit 2; \
|
|
fi; \
|
|
params="$$(python3 scripts/inventory_host.py --inventaire $(INVENTAIRE_PRODUCTION) parametres-proxmox --hote $(HOTE))"; \
|
|
eval "$$params"; \
|
|
$(MAKE) cloner-vm \
|
|
HOTE="$(HOTE)" \
|
|
VMID="$$SETOPS_VMID" \
|
|
ADRESSE_IP="$$SETOPS_IP" \
|
|
CIDR="$$SETOPS_CIDR" \
|
|
PASSERELLE="$$SETOPS_PASSERELLE" \
|
|
VLAN="$$SETOPS_VLAN" \
|
|
PONT_PROXMOX="$${SETOPS_PONT:-$(PONT_PROXMOX)}" \
|
|
STOCKAGE_PROXMOX="$${SETOPS_STOCKAGE:-$(STOCKAGE_PROXMOX)}" \
|
|
TAILLE_DISQUE="$${SETOPS_DISQUE:-$(TAILLE_DISQUE)}" \
|
|
COEURS="$${SETOPS_COEURS:-$(COEURS)}" \
|
|
MEMOIRE="$${SETOPS_MEMOIRE:-$(MEMOIRE)}" \
|
|
NOEUD_PROXMOX="$${SETOPS_NOEUD:-$(NOEUD_PROXMOX)}" \
|
|
VMID_MODELE="$(VMID_MODELE)" \
|
|
FORMAT_DISQUE="$(FORMAT_DISQUE)" \
|
|
DISQUE_PROXMOX="$(DISQUE_PROXMOX)" \
|
|
DNS="$${SETOPS_DNS:-$(DNS)}" \
|
|
DHCP="$(DHCP)" \
|
|
CIUSER="$(CIUSER)" \
|
|
CLE_SSH_PUBLIQUE="$(CLE_SSH_PUBLIQUE)" \
|
|
DEMARRER="$(DEMARRER)" \
|
|
CLONE_COMPLET="$(CLONE_COMPLET)"
|
|
@# `creer-vm` rend une VM PRETE, pas seulement demarree : sans cette attente,
|
|
@# enchainer `creer-vm` puis `deployer` echoue presque toujours sur une machine
|
|
@# neuve. C'est ce qui separe une suite de commandes d'une reconstruction.
|
|
@if [[ "$(ATTENDRE)" != "false" ]]; then \
|
|
$(MAKE) --no-print-directory _attendre-hote LIMITE="$(HOTE)"; \
|
|
fi
|
|
|
|
inventaire-verifier: ansible-runtime _instance-requise ## Verifie que l'inventaire se parse (voute dechiffree)
|
|
ansible-inventory -i $(INVENTAIRE_LAB) --list > /dev/null
|
|
ansible-inventory -i $(INVENTAIRE_PRODUCTION) --list > /dev/null
|
|
python3 scripts/inventory_host.py --inventaire $(INVENTAIRE_PRODUCTION) verifier-playbooks --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES)
|
|
python3 scripts/inventory_host.py --inventaire $(INVENTAIRE_PRODUCTION) --dependances $(FICHIER_DEPENDANCES) verifier-dependances --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES)
|
|
python3 scripts/verifier_gui.py
|
|
python3 scripts/serveurs.py verifier
|
|
python3 scripts/applications.py verifier
|
|
python3 scripts/bases_donnees.py verifier
|
|
python3 scripts/domaines.py verifier
|
|
|
|
.PHONY: bases bases-verifier domaines domaines-verifier applications applications-verifier applications-bootstrap serveurs serveurs-verifier serveurs-bootstrap
|
|
serveurs: ## Liste les serveurs declares au plan
|
|
python3 scripts/serveurs.py lister
|
|
|
|
serveurs-verifier: ## Valide le registre des serveurs
|
|
python3 scripts/serveurs.py verifier
|
|
|
|
serveurs-bootstrap: ## Amorce l'acces SSH aux serveurs neufs
|
|
python3 scripts/serveurs.py bootstrap
|
|
|
|
.PHONY: instancier instancier-appliquer
|
|
instancier: _instance-requise ## Genere hosts.yml depuis le plan (sans l'appliquer)
|
|
python3 scripts/instancier.py generer
|
|
python3 scripts/instancier.py comparer
|
|
|
|
instancier-appliquer: _instance-requise ## Applique l'inventaire genere — FORCE=1 pour passer outre le diff
|
|
python3 scripts/instancier.py appliquer $(if $(FORCE),--force)
|
|
|
|
bases: ## Liste les bases de donnees declarees au plan
|
|
python3 scripts/bases_donnees.py lister
|
|
|
|
bases-verifier: ## Valide le registre des bases de donnees
|
|
python3 scripts/bases_donnees.py verifier
|
|
|
|
domaines: ## Liste les domaines declares au plan
|
|
python3 scripts/domaines.py lister
|
|
|
|
domaines-verifier: ## Valide le registre des domaines
|
|
python3 scripts/domaines.py verifier
|
|
|
|
applications: ## Liste les applications declarees au plan
|
|
python3 scripts/applications.py lister
|
|
|
|
applications-verifier: ## Valide le registre des applications
|
|
python3 scripts/applications.py verifier
|
|
|
|
applications-bootstrap: ## Amorce les applications declarees au plan
|
|
python3 scripts/applications.py bootstrap
|
|
|
|
inventaire-lister: ansible-runtime ## Affiche l'inventaire complet (JSON)
|
|
ansible-inventory -i $(FICHIER_INVENTAIRE) --list
|
|
|
|
inventaire-graphe: ansible-runtime ## Affiche le graphe des groupes de l'inventaire
|
|
ansible-inventory -i $(FICHIER_INVENTAIRE) --graph
|
|
|
|
inventaire-hote: ansible-runtime ## Affiche les variables derivees d'un hote — HOTE=<nom>
|
|
@if [[ -z "$(HOTE)" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote.'; \
|
|
exit 2; \
|
|
fi
|
|
ansible-inventory -i $(FICHIER_INVENTAIRE) --host $(HOTE)
|
|
|
|
inventaire-lab: ## Affiche le graphe de l'inventaire de laboratoire
|
|
$(MAKE) inventaire-graphe FICHIER_INVENTAIRE="$(INVENTAIRE_LAB)"
|
|
|
|
inventaire-production: ## Affiche le graphe de l'inventaire de production
|
|
$(MAKE) inventaire-graphe FICHIER_INVENTAIRE="$(INVENTAIRE_PRODUCTION)"
|
|
|
|
.PHONY: _verifier-acces-modele _verifier-privileges-modele preparer-modele verifier-modele nettoyer-modele
|
|
_verifier-acces-modele: ansible-runtime
|
|
ansible -i $(INVENTAIRE_LAB) $(GROUPE_MODELE) -m ping -e ansible_become=false
|
|
|
|
_verifier-privileges-modele: ansible-runtime
|
|
ansible -i $(INVENTAIRE_LAB) $(GROUPE_MODELE) -b -m command -a "whoami"
|
|
|
|
preparer-modele: ansible-runtime _verifier-acces-modele _verifier-privileges-modele ## Prepare le gabarit dore (VM de reference clonee pour chaque hote)
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_PREPARER_MODELE)
|
|
|
|
verifier-modele: ansible-runtime ## Verifie le gabarit dore
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_VERIFIER_MODELE)
|
|
|
|
nettoyer-modele: ansible-runtime ## Nettoie le gabarit avant capture — exige CONFIRMER=true
|
|
@if [[ "$(CONFIRMER)" != "true" ]]; then \
|
|
printf '%s\n' 'Refus: relancer avec CONFIRMER=true pour le nettoyage final du modele.'; \
|
|
exit 2; \
|
|
fi
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_NETTOYER_MODELE) -e template_cleanup_confirm=true
|
|
|
|
.PHONY: _verifier-acces-hote _verifier-privileges-hote _attendre-hote faits verifier-hote
|
|
|
|
# Deux ATTENTES ACTIVES, sans lesquelles on ne peut pas enchainer creation et
|
|
# deploiement — donc sans lesquelles `make myDay` ne peut pas reconstruire seul.
|
|
#
|
|
# 1. SSH. `make creer-vm` rend la main des que Proxmox a DEMARRE la VM, pas quand elle
|
|
# repond. Verifier l'acces aussitot echoue presque toujours sur une machine neuve —
|
|
# et le symptome trompe : a travers la frontiere, le TCP s'etablit (SYN proxy) et
|
|
# l'echec se lit « Connection timed out during banner exchange ».
|
|
#
|
|
# 2. Le verrou dpkg. L'image Debian lance ses propres mises a jour au premier
|
|
# demarrage et tient `/var/lib/dpkg/lock-frontend` plusieurs minutes. Le premier
|
|
# `apt` d'Ansible echoue alors sur un verrou, pas sur une vraie erreur.
|
|
#
|
|
# `unattended-upgrades.service` est volontairement ABSENT de la condition : c'est un
|
|
# DEMON (Type=simple), toujours `active`. L'y inclure rendait l'attente impossible a
|
|
# satisfaire — elle echouait au bout du delai, systematiquement. Seules `apt-daily*`
|
|
# sont des one-shot, et c'est le VERROU qui dit si dpkg est reellement occupe.
|
|
#
|
|
# Les deux sont des COURSES de premier demarrage, pas des defauts de conception. On les
|
|
# attend au lieu de les subir. ATTENTE_HOTE (secondes) borne chacune : depasser le
|
|
# delai reste un echec, pour ne pas transformer une panne en attente infinie.
|
|
ATTENTE_HOTE ?= 600
|
|
|
|
_attendre-hote: ansible-runtime
|
|
@set -e; \
|
|
if [[ -z "$(LIMITE)" ]]; then printf '%s\n' 'Refus: LIMITE requis.'; exit 2; fi; \
|
|
fin=$$(( SECONDS + $(ATTENTE_HOTE) )); \
|
|
printf '%s' "Attente de SSH sur $(LIMITE) "; \
|
|
until ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -m ping -e ansible_become=false >/dev/null 2>&1; do \
|
|
if (( SECONDS > fin )); then printf '%s\n' " ECHEC: injoignable apres $(ATTENTE_HOTE)s."; exit 4; fi; \
|
|
printf '.'; sleep 5; \
|
|
done; \
|
|
printf '%s\n' " ok"; \
|
|
printf '%s' "Attente de cloud-init et des maj automatiques "; \
|
|
until ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -b -m shell -a \
|
|
'cloud-init status --wait >/dev/null 2>&1; \
|
|
! systemctl is-active --quiet apt-daily.service apt-daily-upgrade.service \
|
|
&& ! fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1' >/dev/null 2>&1; do \
|
|
if (( SECONDS > fin )); then printf '%s\n' " ECHEC: dpkg toujours occupe apres $(ATTENTE_HOTE)s."; exit 4; fi; \
|
|
printf '.'; sleep 10; \
|
|
done; \
|
|
printf '%s\n' " libere"
|
|
|
|
_verifier-acces-hote: ansible-runtime
|
|
ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -m ping -e ansible_become=false
|
|
|
|
_verifier-privileges-hote: ansible-runtime
|
|
ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -b -m command -a "whoami"
|
|
|
|
faits: ansible-runtime ## Interroge les faits Ansible de la flotte — LIMITE=<motif>
|
|
ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -m setup -a "filter=ansible_distribution*"
|
|
|
|
verifier-hote: ansible-runtime ## Passe le playbook de verification sur un hote
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) $(PLAYBOOK_VERIFIER_HOTE) $(OPTIONS_PLAYBOOK)
|