Deuxieme application du patron devis/applicateur aux services. Trouve a la premiere execution : sur infra-pki-01 — l'autorite elle-meme — le certificat etait expire depuis plus de 8 h et le renouvellement echouait toutes les 14 minutes sur « 'step ca renew' requires the '--ca-url' flag ». Rien ne le signalait. Cause : sur l'hote de l'AC, /etc/step est le STEPPATH du SERVEUR, pas un amorcage client — pas de defaults.json, et l'unite de renouvellement en dependait. La lecon etait deja ecrite dans le commentaire de la tache d'emission (« l'autorite ne bootstrape pas »), jamais reportee sur l'unite. Le role ne pouvait pas non plus se soigner : la re-emission ne regardait que la FORME (cert absent ou SAN manquant), jamais la validite. client_pki verifie desormais l'echeance (client_pki_marge_renouvellement). Ce qu'il a fallu desapprendre : les certificats vivent 24 h et se renouvellent toutes les ~14 min ; « empreinte servie != empreinte disque » est l'etat NORMAL. Comparer les empreintes aurait donne un verificateur qui crie en permanence. Le signal est l'echeance de ce qui est SERVI, plus l'absence de client_pki_reload_services. Le devis a d'abord menti, du defaut meme qu'il traque : include_vars au niveau du play prime sur les group_vars. Et le premier correctif a PARU marcher — set_fact accepte un dictionnaire entier en argument libre sans erreur et n'en fait rien. Il faut reimposer cle par cle. Les deux devis sont corriges et le piege est consigne dans docs/devis-services.md avant d'ecrire le prochain. Verifie dans les deux sens : CONFORME sur 14 hotes ; sur un releve ou l'on rejoue une copie perimee en memoire, 2 ecarts et code de sortie 1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
873 lines
39 KiB
Makefile
873 lines
39 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:
|
|
@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:
|
|
@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
|
|
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
|
|
|
|
syntaxe-modele: ansible-runtime
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_PREPARER_MODELE) --syntax-check
|
|
|
|
syntaxe-verification-modele: ansible-runtime
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_VERIFIER_MODELE) --syntax-check
|
|
|
|
syntaxe-nettoyage: ansible-runtime
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_NETTOYER_MODELE) --syntax-check
|
|
|
|
syntaxe-verification-hote: ansible-runtime
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) $(PLAYBOOK_VERIFIER_HOTE) --syntax-check
|
|
|
|
syntaxe-groupes: ansible-runtime
|
|
@for playbook in $(DOSSIER_PLAYBOOKS_GROUPES)/*.yml; do \
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) "$$playbook" --syntax-check; \
|
|
done
|
|
|
|
syntaxe-proxmox: ansible-runtime
|
|
ansible-playbook -i localhost, $(PLAYBOOK_PROXMOX_CLONER_VM) --syntax-check
|
|
|
|
.PHONY: test
|
|
test:
|
|
python3 scripts/tests/test_inventory_host.py
|
|
|
|
.PHONY: verifier
|
|
verifier: lint test inventaire-verifier site-verifier flux-verifier syntaxe
|
|
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
|
|
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
|
|
|
|
# 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:
|
|
@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:
|
|
@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:
|
|
@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:
|
|
@python3 scripts/plan_recette.py
|
|
|
|
# Liste les modèles disponibles (socle + SETOPS_MODELES) pour créer une instance.
|
|
instance-modeles:
|
|
@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:
|
|
@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:
|
|
@python3 scripts/model_creer.py --mode "$(MODE)" --nom "$(NOM)" \
|
|
$(if $(BASE),--base $(BASE),) $(if $(SOURCE),--source $(SOURCE),) $(if $(DEST),--dest $(DEST),)
|
|
|
|
config:
|
|
python3 scripts/config_proxmox.py
|
|
|
|
inventaire-ui: _instance-requise
|
|
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
|
|
@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
|
|
@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
|
|
@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
|
|
python3 scripts/orchestrer.py ecrire
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) playbooks/site.yml --syntax-check
|
|
|
|
site-verifier:
|
|
python3 scripts/orchestrer.py verifier
|
|
|
|
.PHONY: flux flux-verifier
|
|
flux: ansible-runtime
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
python3 scripts/devis_sdn.py --verifier
|
|
|
|
devis-opnsense-verifier:
|
|
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:
|
|
python3 scripts/resoudre_flux.py verifier
|
|
|
|
.PHONY: valider
|
|
valider: ansible-runtime
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) playbooks/valider.yml
|
|
|
|
.PHONY: wiki-publier
|
|
wiki-publier:
|
|
@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
|
|
@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
|
|
@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
|
|
reconstruire: _instance-requise
|
|
@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
|
|
|
|
deployer-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
|
|
@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
|
|
@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
|
|
@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
|
|
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:
|
|
python3 scripts/serveurs.py lister
|
|
|
|
serveurs-verifier:
|
|
python3 scripts/serveurs.py verifier
|
|
|
|
serveurs-bootstrap:
|
|
python3 scripts/serveurs.py bootstrap
|
|
|
|
.PHONY: instancier instancier-appliquer
|
|
instancier: _instance-requise
|
|
python3 scripts/instancier.py generer
|
|
python3 scripts/instancier.py comparer
|
|
|
|
instancier-appliquer: _instance-requise
|
|
python3 scripts/instancier.py appliquer $(if $(FORCE),--force)
|
|
|
|
bases:
|
|
python3 scripts/bases_donnees.py lister
|
|
|
|
bases-verifier:
|
|
python3 scripts/bases_donnees.py verifier
|
|
|
|
domaines:
|
|
python3 scripts/domaines.py lister
|
|
|
|
domaines-verifier:
|
|
python3 scripts/domaines.py verifier
|
|
|
|
applications:
|
|
python3 scripts/applications.py lister
|
|
|
|
applications-verifier:
|
|
python3 scripts/applications.py verifier
|
|
|
|
applications-bootstrap:
|
|
python3 scripts/applications.py bootstrap
|
|
|
|
inventaire-lister: ansible-runtime
|
|
ansible-inventory -i $(FICHIER_INVENTAIRE) --list
|
|
|
|
inventaire-graphe: ansible-runtime
|
|
ansible-inventory -i $(FICHIER_INVENTAIRE) --graph
|
|
|
|
inventaire-hote: ansible-runtime
|
|
@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:
|
|
$(MAKE) inventaire-graphe FICHIER_INVENTAIRE="$(INVENTAIRE_LAB)"
|
|
|
|
inventaire-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
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_PREPARER_MODELE)
|
|
|
|
verifier-modele: ansible-runtime
|
|
ansible-playbook -i $(INVENTAIRE_LAB) $(PLAYBOOK_VERIFIER_MODELE)
|
|
|
|
nettoyer-modele: ansible-runtime
|
|
@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
|
|
ansible -i $(INVENTAIRE_PRODUCTION) $(LIMITE) -m setup -a "filter=ansible_distribution*"
|
|
|
|
verifier-hote: ansible-runtime
|
|
ansible-playbook -i $(INVENTAIRE_PRODUCTION) $(PLAYBOOK_VERIFIER_HOTE) $(OPTIONS_PLAYBOOK)
|