Consolider le flux operationnel des inventaires
This commit is contained in:
parent
f941b66d39
commit
72c3ac6007
11 changed files with 180 additions and 42 deletions
|
|
@ -256,7 +256,7 @@ Les cibles d'exploitation des VM doivent privilégier les groupes :
|
||||||
```text
|
```text
|
||||||
make deployer HOTE=web-01
|
make deployer HOTE=web-01
|
||||||
make deployer-groupe GROUPE=serveurs_debian
|
make deployer-groupe GROUPE=serveurs_debian
|
||||||
make appliquer GROUPE=serveurs_debian
|
make hote-planifier HOTE=obs-01 VMID=94101 GROUPES="serveurs_debian serveurs_durcis serveurs_prometheus"
|
||||||
```
|
```
|
||||||
|
|
||||||
Éviter les cibles parallèles qui réappliquent les mêmes rôles par couche, par exemple `make socle`, `make durcissement`, `make converger` ou `make deployer-vm`.
|
Éviter les cibles parallèles qui réappliquent les mêmes rôles par couche, par exemple `make socle`, `make durcissement`, `make converger` ou `make deployer-vm`.
|
||||||
|
|
@ -336,7 +336,8 @@ clients_ldap
|
||||||
clients_supervision
|
clients_supervision
|
||||||
clients_metriques
|
clients_metriques
|
||||||
serveurs_web
|
serveurs_web
|
||||||
serveurs_bases_donnees
|
serveurs_postgresql
|
||||||
|
serveurs_prometheus
|
||||||
```
|
```
|
||||||
|
|
||||||
Ne pas cibler `all` par défaut pour un playbook qui ne s'applique pas réellement à tous les hôtes.
|
Ne pas cibler `all` par défaut pour un playbook qui ne s'applique pas réellement à tous les hôtes.
|
||||||
|
|
|
||||||
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -1,5 +1,21 @@
|
||||||
# CHANGELOG — Set-OPS
|
# CHANGELOG — Set-OPS
|
||||||
|
|
||||||
|
## 2026-06-22
|
||||||
|
|
||||||
|
### Ajouté
|
||||||
|
- Ajout de `make hote-planifier` pour renseigner un hôte, son VMID et ses groupes sans création ni déploiement immédiat.
|
||||||
|
- Ajout de `scripts/inventory_rules.py` pour centraliser les règles partagées entre la gestion CLI et l'interface locale d'inventaire.
|
||||||
|
|
||||||
|
### Modifié
|
||||||
|
- Renforcement de la validation d'inventaire :
|
||||||
|
- un hôte doit être soit planifié, soit actif, mais pas les deux ;
|
||||||
|
- un hôte actif doit avoir `ansible_host` ;
|
||||||
|
- un hôte doit porter au moins un groupe opérationnel ;
|
||||||
|
- les VMID dupliqués sont refusés.
|
||||||
|
- Conservation du VMID dans l'inventaire lors de `make creer-vm` et `make hote-ajouter`.
|
||||||
|
- Alignement de l'aide Makefile et de la documentation sur le flux opérateur : planifier, activer/créer, déployer.
|
||||||
|
- Retrait des groupes génériques obsolètes `serveurs_bases_donnees` et `serveurs_supervision` au profit des groupes de services précis.
|
||||||
|
|
||||||
## 2026-06-21
|
## 2026-06-21
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|
|
||||||
21
Makefile
21
Makefile
|
|
@ -95,12 +95,14 @@ aide:
|
||||||
@printf '%s\n' ' make config'
|
@printf '%s\n' ' make config'
|
||||||
@printf '%s\n' ''
|
@printf '%s\n' ''
|
||||||
@printf '%s\n' 'Hotes'
|
@printf '%s\n' 'Hotes'
|
||||||
|
@printf '%s\n' ' Planifier un hote sans le deployer:'
|
||||||
|
@printf '%s\n' ' make hote-planifier HOTE=obs-01 VMID=94101 GROUPES="serveurs_debian serveurs_durcis serveurs_prometheus serveurs_loki"'
|
||||||
@printf '%s\n' ' Remettre un hote en conformite selon ses groupes:'
|
@printf '%s\n' ' Remettre un hote en conformite selon ses groupes:'
|
||||||
@printf '%s\n' ' make deployer HOTE=web-01'
|
@printf '%s\n' ' make deployer HOTE=web-01'
|
||||||
@printf '%s\n' ' Afficher un hote:'
|
@printf '%s\n' ' Afficher un hote:'
|
||||||
@printf '%s\n' ' make hote-afficher HOTE=web-01'
|
@printf '%s\n' ' make hote-afficher HOTE=web-01'
|
||||||
@printf '%s\n' ' Ajouter un hote existant:'
|
@printf '%s\n' ' Ajouter un hote existant:'
|
||||||
@printf '%s\n' ' make hote-ajouter HOTE=web-01 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"'
|
@printf '%s\n' ' make hote-ajouter HOTE=web-01 VMID=99101 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"'
|
||||||
@printf '%s\n' ' Modifier ses groupes:'
|
@printf '%s\n' ' Modifier ses groupes:'
|
||||||
@printf '%s\n' ' make hote-groupes HOTE=web-01 GROUPES="serveurs_debian serveurs_durcis"'
|
@printf '%s\n' ' make hote-groupes HOTE=web-01 GROUPES="serveurs_debian serveurs_durcis"'
|
||||||
@printf '%s\n' ' Diagnostiquer:'
|
@printf '%s\n' ' Diagnostiquer:'
|
||||||
|
|
@ -109,8 +111,6 @@ aide:
|
||||||
@printf '%s\n' 'Groupes'
|
@printf '%s\n' 'Groupes'
|
||||||
@printf '%s\n' ' Appliquer un groupe complet:'
|
@printf '%s\n' ' Appliquer un groupe complet:'
|
||||||
@printf '%s\n' ' make deployer-groupe GROUPE=serveurs_debian'
|
@printf '%s\n' ' make deployer-groupe GROUPE=serveurs_debian'
|
||||||
@printf '%s\n' ' Rejouer directement le playbook d un groupe:'
|
|
||||||
@printf '%s\n' ' make appliquer GROUPE=serveurs_debian'
|
|
||||||
@printf '%s\n' ' Convention:'
|
@printf '%s\n' ' Convention:'
|
||||||
@printf '%s\n' ' groupe serveurs_debian -> playbooks/groupes/serveurs_debian.yml'
|
@printf '%s\n' ' groupe serveurs_debian -> playbooks/groupes/serveurs_debian.yml'
|
||||||
@printf '%s\n' ''
|
@printf '%s\n' ''
|
||||||
|
|
@ -173,7 +173,7 @@ syntaxe-proxmox: ansible-runtime
|
||||||
.PHONY: verifier
|
.PHONY: verifier
|
||||||
verifier: lint inventaire-verifier syntaxe
|
verifier: lint inventaire-verifier syntaxe
|
||||||
|
|
||||||
.PHONY: inventaire 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
|
.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
|
||||||
inventaire: inventaire-production
|
inventaire: inventaire-production
|
||||||
|
|
||||||
config:
|
config:
|
||||||
|
|
@ -187,7 +187,16 @@ hote-ajouter: ansible-runtime
|
||||||
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote ADRESSE_IP=adresse_ip.'; \
|
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote ADRESSE_IP=adresse_ip.'; \
|
||||||
exit 2; \
|
exit 2; \
|
||||||
fi
|
fi
|
||||||
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) ajouter --hote $(HOTE) --adresse-ip $(ADRESSE_IP) --utilisateur-ansible $(UTILISATEUR_ANSIBLE) --groupes "$(GROUPES)" --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES)
|
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) ajouter --hote $(HOTE) --adresse-ip $(ADRESSE_IP) --utilisateur-ansible $(UTILISATEUR_ANSIBLE) --vmid "$(VMID)" --groupes "$(GROUPES)" --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES)
|
||||||
|
$(MAKE) inventaire-verifier
|
||||||
|
$(MAKE) hote-afficher HOTE="$(HOTE)" FICHIER_INVENTAIRE="$(FICHIER_INVENTAIRE)"
|
||||||
|
|
||||||
|
hote-planifier: ansible-runtime
|
||||||
|
@if [[ -z "$(HOTE)" ]]; then \
|
||||||
|
printf '%s\n' 'Refus: relancer avec HOTE=nom_hote GROUPES="groupe1 groupe2".'; \
|
||||||
|
exit 2; \
|
||||||
|
fi
|
||||||
|
python3 scripts/inventory_host.py --inventaire $(FICHIER_INVENTAIRE) ajouter --hote $(HOTE) --vmid "$(VMID)" --groupes "$(GROUPES)" --dossier-playbooks $(DOSSIER_PLAYBOOKS_GROUPES)
|
||||||
$(MAKE) inventaire-verifier
|
$(MAKE) inventaire-verifier
|
||||||
$(MAKE) hote-afficher HOTE="$(HOTE)" FICHIER_INVENTAIRE="$(FICHIER_INVENTAIRE)"
|
$(MAKE) hote-afficher HOTE="$(HOTE)" FICHIER_INVENTAIRE="$(FICHIER_INVENTAIRE)"
|
||||||
|
|
||||||
|
|
@ -307,7 +316,7 @@ creer-vm:
|
||||||
exit 2; \
|
exit 2; \
|
||||||
fi
|
fi
|
||||||
$(MAKE) cloner-vm HOTE="$(HOTE)" VMID_MODELE="$(VMID_MODELE)" VMID="$(VMID)" NOEUD_PROXMOX="$(NOEUD_PROXMOX)" STOCKAGE_PROXMOX="$(STOCKAGE_PROXMOX)" FORMAT_DISQUE="$(FORMAT_DISQUE)" TAILLE_DISQUE="$(TAILLE_DISQUE)" DISQUE_PROXMOX="$(DISQUE_PROXMOX)" ADRESSE_IP="$(ADRESSE_IP)" CIDR="$(CIDR)" PASSERELLE="$(PASSERELLE)" DNS="$(DNS)" DHCP="$(DHCP)" CIUSER="$(CIUSER)" CLE_SSH_PUBLIQUE="$(CLE_SSH_PUBLIQUE)" PONT_PROXMOX="$(PONT_PROXMOX)" VLAN="$(VLAN)" DEMARRER="$(DEMARRER)" CLONE_COMPLET="$(CLONE_COMPLET)"
|
$(MAKE) cloner-vm HOTE="$(HOTE)" VMID_MODELE="$(VMID_MODELE)" VMID="$(VMID)" NOEUD_PROXMOX="$(NOEUD_PROXMOX)" STOCKAGE_PROXMOX="$(STOCKAGE_PROXMOX)" FORMAT_DISQUE="$(FORMAT_DISQUE)" TAILLE_DISQUE="$(TAILLE_DISQUE)" DISQUE_PROXMOX="$(DISQUE_PROXMOX)" ADRESSE_IP="$(ADRESSE_IP)" CIDR="$(CIDR)" PASSERELLE="$(PASSERELLE)" DNS="$(DNS)" DHCP="$(DHCP)" CIUSER="$(CIUSER)" CLE_SSH_PUBLIQUE="$(CLE_SSH_PUBLIQUE)" PONT_PROXMOX="$(PONT_PROXMOX)" VLAN="$(VLAN)" DEMARRER="$(DEMARRER)" CLONE_COMPLET="$(CLONE_COMPLET)"
|
||||||
$(MAKE) hote-ajouter HOTE="$(HOTE)" ADRESSE_IP="$(ADRESSE_IP)" GROUPES="$(GROUPES)" FICHIER_INVENTAIRE="$(FICHIER_INVENTAIRE)"
|
$(MAKE) hote-ajouter HOTE="$(HOTE)" ADRESSE_IP="$(ADRESSE_IP)" VMID="$(VMID)" GROUPES="$(GROUPES)" FICHIER_INVENTAIRE="$(FICHIER_INVENTAIRE)"
|
||||||
|
|
||||||
inventaire-verifier: ansible-runtime
|
inventaire-verifier: ansible-runtime
|
||||||
ansible-inventory -i $(INVENTAIRE_LAB) --list > /dev/null
|
ansible-inventory -i $(INVENTAIRE_LAB) --list > /dev/null
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -66,10 +66,16 @@ Ouvrir l'interface locale de gestion d'inventaire :
|
||||||
make inventaire-ui
|
make inventaire-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Planifier une VM sans la créer ni la déployer :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make hote-planifier HOTE=obs-01 VMID=94101 GROUPES="serveurs_debian serveurs_durcis serveurs_prometheus serveurs_loki"
|
||||||
|
```
|
||||||
|
|
||||||
Ajouter une VM à l'inventaire production et l'associer aux groupes qui détermineront sa configuration :
|
Ajouter une VM à l'inventaire production et l'associer aux groupes qui détermineront sa configuration :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make hote-ajouter HOTE=web-01 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"
|
make hote-ajouter HOTE=web-01 VMID=99101 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"
|
||||||
make hote-groupes HOTE=web-01 GROUPES="serveurs_debian serveurs_durcis"
|
make hote-groupes HOTE=web-01 GROUPES="serveurs_debian serveurs_durcis"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -117,3 +123,5 @@ Déployer ou remettre en conformité un groupe :
|
||||||
```bash
|
```bash
|
||||||
make deployer-groupe GROUPE=serveurs_debian
|
make deployer-groupe GROUPE=serveurs_debian
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Les déploiements de groupes ciblent automatiquement les hôtes actifs seulement.
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,18 @@ Les hôtes prévus mais non encore déployés sont placés dans `hotes_planifies
|
||||||
|
|
||||||
Les hôtes réellement joignables par Ansible sont placés dans `hotes_actifs`.
|
Les hôtes réellement joignables par Ansible sont placés dans `hotes_actifs`.
|
||||||
|
|
||||||
|
Planifier un hôte :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make hote-planifier HOTE=obs-01 VMID=94101 GROUPES="serveurs_debian serveurs_durcis serveurs_prometheus serveurs_loki"
|
||||||
|
```
|
||||||
|
|
||||||
|
Activer un hôte existant ou déjà cloné :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make hote-ajouter HOTE=obs-01 VMID=94101 ADRESSE_IP=192.168.12.141 GROUPES="serveurs_debian serveurs_durcis serveurs_prometheus serveurs_loki"
|
||||||
|
```
|
||||||
|
|
||||||
Les déploiements groupés limitent automatiquement l'exécution à :
|
Les déploiements groupés limitent automatiquement l'exécution à :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,10 @@ Cloud-init ne remplace pas Ansible pour la configuration réelle du serveur.
|
||||||
|
|
||||||
Une VM déployée doit être placée dans les groupes d'inventaire qui décrivent l'état voulu.
|
Une VM déployée doit être placée dans les groupes d'inventaire qui décrivent l'état voulu.
|
||||||
|
|
||||||
|
Les hôtes prévus mais non créés restent dans `hotes_planifies`.
|
||||||
|
|
||||||
|
Les hôtes joignables par Ansible sont dans `hotes_actifs`.
|
||||||
|
|
||||||
Chaque groupe opérationnel doit avoir un playbook homonyme :
|
Chaque groupe opérationnel doit avoir un playbook homonyme :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
@ -114,14 +118,16 @@ web-01 dans serveurs_debian -> applique le socle Debian
|
||||||
web-01 dans serveurs_durcis -> applique le durcissement commun
|
web-01 dans serveurs_durcis -> applique le durcissement commun
|
||||||
```
|
```
|
||||||
|
|
||||||
Un groupe sans playbook ne doit pas être assigné à une VM de production tant que son rôle n'est pas implémenté.
|
Un groupe sans playbook ne doit pas être assigné à une VM de production.
|
||||||
|
|
||||||
|
Un playbook de groupe peut être un contrat temporaire sans rôle tant que le service n'est pas encore implémenté. Il doit rester idempotent et explicite.
|
||||||
|
|
||||||
## 6. Socle Debian
|
## 6. Socle Debian
|
||||||
|
|
||||||
Le groupe `serveurs_debian` maintient les composants de base :
|
Le groupe `serveurs_debian` maintient les composants de base :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make appliquer GROUPE=serveurs_debian
|
make deployer-groupe GROUPE=serveurs_debian
|
||||||
```
|
```
|
||||||
|
|
||||||
## 7. Hardening commun
|
## 7. Hardening commun
|
||||||
|
|
@ -129,7 +135,7 @@ make appliquer GROUPE=serveurs_debian
|
||||||
Le groupe `serveurs_durcis` maintient les couches de sécurité communes :
|
Le groupe `serveurs_durcis` maintient les couches de sécurité communes :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make appliquer GROUPE=serveurs_durcis
|
make deployer-groupe GROUPE=serveurs_durcis
|
||||||
```
|
```
|
||||||
|
|
||||||
L'accès SSH par mot de passe est désactivé dès le socle. L'activation de nftables doit rester dépendante des règles réseau propres au rôle du serveur.
|
L'accès SSH par mot de passe est désactivé dès le socle. L'activation de nftables doit rester dépendante des règles réseau propres au rôle du serveur.
|
||||||
|
|
@ -139,7 +145,7 @@ L'accès SSH par mot de passe est désactivé dès le socle. L'activation de nft
|
||||||
Pour une VM déjà clonée et démarrée :
|
Pour une VM déjà clonée et démarrée :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make hote-ajouter HOTE=web-01 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"
|
make hote-ajouter HOTE=web-01 VMID=99101 ADRESSE_IP=192.168.12.101 GROUPES="serveurs_debian serveurs_durcis"
|
||||||
make deployer HOTE=web-01
|
make deployer HOTE=web-01
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -159,6 +165,8 @@ Pour converger un groupe complet :
|
||||||
make deployer-groupe GROUPE=serveurs_debian
|
make deployer-groupe GROUPE=serveurs_debian
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Cette commande limite le playbook au croisement entre le groupe demandé et `hotes_actifs`.
|
||||||
|
|
||||||
## 9. Variables template et conformité
|
## 9. Variables template et conformité
|
||||||
|
|
||||||
Les variables du template servent à construire le golden template dans `inventories/lab/group_vars/modeles_vm.yml`.
|
Les variables du template servent à construire le golden template dans `inventories/lab/group_vars/modeles_vm.yml`.
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,5 @@ all:
|
||||||
serveurs_web:
|
serveurs_web:
|
||||||
hosts: {}
|
hosts: {}
|
||||||
|
|
||||||
serveurs_bases_donnees:
|
|
||||||
hosts: {}
|
|
||||||
|
|
||||||
serveurs_supervision:
|
|
||||||
hosts: {}
|
|
||||||
|
|
||||||
hotes_proxmox:
|
hotes_proxmox:
|
||||||
hosts: {}
|
hosts: {}
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,6 @@ all:
|
||||||
web-02:
|
web-02:
|
||||||
ansible_host: 192.168.12.102
|
ansible_host: 192.168.12.102
|
||||||
ansible_user: ansible
|
ansible_user: ansible
|
||||||
serveurs_bases_donnees:
|
|
||||||
hosts: {}
|
|
||||||
serveurs_supervision:
|
|
||||||
hosts: {}
|
|
||||||
serveurs_keycloak:
|
serveurs_keycloak:
|
||||||
hosts:
|
hosts:
|
||||||
idm-01: {}
|
idm-01: {}
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,17 @@ from urllib.parse import urlparse
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
from inventory_rules import (
|
||||||
|
GROUPE_HOTES_ACTIFS,
|
||||||
|
GROUPE_HOTES_PLANIFIES,
|
||||||
|
GROUPES_ETAT_HOTE,
|
||||||
|
est_groupe_operationnel,
|
||||||
|
groupes_operationnels_connus,
|
||||||
|
)
|
||||||
|
|
||||||
RACINE = Path(__file__).resolve().parents[1]
|
RACINE = Path(__file__).resolve().parents[1]
|
||||||
INVENTAIRE_DEFAUT = RACINE / "inventories/production/hosts.yml"
|
INVENTAIRE_DEFAUT = RACINE / "inventories/production/hosts.yml"
|
||||||
DOSSIER_PLAYBOOKS_GROUPES = RACINE / "playbooks/groupes"
|
DOSSIER_PLAYBOOKS_GROUPES = RACINE / "playbooks/groupes"
|
||||||
GROUPES_OPERATIONNELS_PREFIXES = ("serveurs_", "clients_")
|
|
||||||
GROUPES_ETAT = ("hotes_actifs", "hotes_planifies")
|
|
||||||
|
|
||||||
|
|
||||||
def charger_yaml(path: Path) -> dict:
|
def charger_yaml(path: Path) -> dict:
|
||||||
|
|
@ -41,9 +46,7 @@ def enfants(data: dict) -> dict:
|
||||||
|
|
||||||
|
|
||||||
def groupes_disponibles(data: dict) -> list[str]:
|
def groupes_disponibles(data: dict) -> list[str]:
|
||||||
groupes = set(enfants(data))
|
return groupes_operationnels_connus(enfants(data), DOSSIER_PLAYBOOKS_GROUPES)
|
||||||
groupes.update(playbook.stem for playbook in DOSSIER_PLAYBOOKS_GROUPES.glob("*.yml"))
|
|
||||||
return sorted(group for group in groupes if group.startswith(GROUPES_OPERATIONNELS_PREFIXES))
|
|
||||||
|
|
||||||
|
|
||||||
def variables_hote(data: dict, hote: str) -> dict:
|
def variables_hote(data: dict, hote: str) -> dict:
|
||||||
|
|
@ -65,15 +68,21 @@ def liste_hotes(data: dict) -> list[dict]:
|
||||||
for nom in sorted(noms):
|
for nom in sorted(noms):
|
||||||
groupes = []
|
groupes = []
|
||||||
etat = "planifie"
|
etat = "planifie"
|
||||||
|
visible = False
|
||||||
for groupe, groupe_data in enfants(data).items():
|
for groupe, groupe_data in enfants(data).items():
|
||||||
if nom not in groupe_data.get("hosts", {}):
|
if nom not in groupe_data.get("hosts", {}):
|
||||||
continue
|
continue
|
||||||
if groupe == "hotes_actifs":
|
if groupe == GROUPE_HOTES_ACTIFS:
|
||||||
etat = "actif"
|
etat = "actif"
|
||||||
elif groupe in GROUPES_ETAT:
|
visible = True
|
||||||
|
elif groupe in GROUPES_ETAT_HOTE:
|
||||||
|
visible = True
|
||||||
continue
|
continue
|
||||||
elif groupe.startswith(GROUPES_OPERATIONNELS_PREFIXES):
|
elif est_groupe_operationnel(groupe):
|
||||||
groupes.append(groupe)
|
groupes.append(groupe)
|
||||||
|
visible = True
|
||||||
|
if not visible:
|
||||||
|
continue
|
||||||
variables = variables_hote(data, nom)
|
variables = variables_hote(data, nom)
|
||||||
hotes.append(
|
hotes.append(
|
||||||
{
|
{
|
||||||
|
|
@ -114,8 +123,8 @@ def donnees_hote(hote: dict) -> dict:
|
||||||
def construire_inventaire(payload: dict, groupes_connus: list[str]) -> dict:
|
def construire_inventaire(payload: dict, groupes_connus: list[str]) -> dict:
|
||||||
enfants_data: dict = {
|
enfants_data: dict = {
|
||||||
"modeles_vm": {"hosts": {}},
|
"modeles_vm": {"hosts": {}},
|
||||||
"hotes_actifs": {"hosts": {}},
|
GROUPE_HOTES_ACTIFS: {"hosts": {}},
|
||||||
"hotes_planifies": {"hosts": {}},
|
GROUPE_HOTES_PLANIFIES: {"hosts": {}},
|
||||||
}
|
}
|
||||||
for groupe in groupes_connus:
|
for groupe in groupes_connus:
|
||||||
enfants_data.setdefault(groupe, {"hosts": {}})
|
enfants_data.setdefault(groupe, {"hosts": {}})
|
||||||
|
|
@ -126,7 +135,7 @@ def construire_inventaire(payload: dict, groupes_connus: list[str]) -> dict:
|
||||||
if not nom:
|
if not nom:
|
||||||
continue
|
continue
|
||||||
variables = donnees_hote(hote)
|
variables = donnees_hote(hote)
|
||||||
groupe_etat = "hotes_actifs" if hote.get("etat") == "actif" else "hotes_planifies"
|
groupe_etat = GROUPE_HOTES_ACTIFS if hote.get("etat") == "actif" else GROUPE_HOTES_PLANIFIES
|
||||||
enfants_data[groupe_etat]["hosts"][nom] = variables
|
enfants_data[groupe_etat]["hosts"][nom] = variables
|
||||||
for groupe in hote.get("groupes", []):
|
for groupe in hote.get("groupes", []):
|
||||||
if groupe not in groupes_connus:
|
if groupe not in groupes_connus:
|
||||||
|
|
@ -138,6 +147,7 @@ def construire_inventaire(payload: dict, groupes_connus: list[str]) -> dict:
|
||||||
|
|
||||||
def valider_payload(payload: dict, groupes_connus: list[str]) -> None:
|
def valider_payload(payload: dict, groupes_connus: list[str]) -> None:
|
||||||
noms: set[str] = set()
|
noms: set[str] = set()
|
||||||
|
vmids: dict[str, str] = {}
|
||||||
for hote in payload.get("hotes", []):
|
for hote in payload.get("hotes", []):
|
||||||
nom = str(hote.get("nom", "")).strip()
|
nom = str(hote.get("nom", "")).strip()
|
||||||
if not nom:
|
if not nom:
|
||||||
|
|
@ -145,12 +155,20 @@ def valider_payload(payload: dict, groupes_connus: list[str]) -> None:
|
||||||
if nom in noms:
|
if nom in noms:
|
||||||
raise ValueError(f"Hote en double: {nom}")
|
raise ValueError(f"Hote en double: {nom}")
|
||||||
noms.add(nom)
|
noms.add(nom)
|
||||||
|
if hote.get("etat") == "actif" and not str(hote.get("adresse_ip", "")).strip():
|
||||||
|
raise ValueError(f"Hote actif sans adresse IP: {nom}")
|
||||||
groupes = hote.get("groupes", [])
|
groupes = hote.get("groupes", [])
|
||||||
if not groupes:
|
if not groupes:
|
||||||
raise ValueError(f"Aucun groupe operationnel pour: {nom}")
|
raise ValueError(f"Aucun groupe operationnel pour: {nom}")
|
||||||
inconnus = sorted(set(groupes) - set(groupes_connus))
|
inconnus = sorted(set(groupes) - set(groupes_connus))
|
||||||
if inconnus:
|
if inconnus:
|
||||||
raise ValueError(f"Groupes inconnus pour {nom}: {', '.join(inconnus)}")
|
raise ValueError(f"Groupes inconnus pour {nom}: {', '.join(inconnus)}")
|
||||||
|
vmid = str(hote.get("vmid", "")).strip()
|
||||||
|
if not vmid:
|
||||||
|
continue
|
||||||
|
if vmid in vmids:
|
||||||
|
raise ValueError(f"VMID en double: {vmid} pour {vmids[vmid]} et {nom}")
|
||||||
|
vmids[vmid] = nom
|
||||||
|
|
||||||
|
|
||||||
HTML = r"""<!doctype html>
|
HTML = r"""<!doctype html>
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,12 @@ import sys
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
from inventory_rules import (
|
||||||
GROUPES_OPERATIONNELS_PREFIXES = ("serveurs_", "clients_")
|
GROUPE_HOTES_ACTIFS,
|
||||||
GROUPE_HOTES_ACTIFS = "hotes_actifs"
|
GROUPE_HOTES_PLANIFIES,
|
||||||
GROUPE_HOTES_PLANIFIES = "hotes_planifies"
|
GROUPES_ETAT_HOTE,
|
||||||
GROUPES_ETAT_HOTE = {GROUPE_HOTES_ACTIFS, GROUPE_HOTES_PLANIFIES}
|
est_groupe_operationnel,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def charger_inventaire(path: Path) -> dict:
|
def charger_inventaire(path: Path) -> dict:
|
||||||
|
|
@ -61,6 +62,13 @@ def variables_hote(data: dict, host: str) -> dict:
|
||||||
return merged
|
return merged
|
||||||
|
|
||||||
|
|
||||||
|
def tous_les_hotes(data: dict) -> list[str]:
|
||||||
|
hosts: set[str] = set()
|
||||||
|
for group_data in enfants(data).values():
|
||||||
|
hosts.update(group_data.get("hosts", {}))
|
||||||
|
return sorted(hosts)
|
||||||
|
|
||||||
|
|
||||||
def groupes_hote(data: dict, host: str) -> list[str]:
|
def groupes_hote(data: dict, host: str) -> list[str]:
|
||||||
groups: list[str] = []
|
groups: list[str] = []
|
||||||
for group, group_data in enfants(data).items():
|
for group, group_data in enfants(data).items():
|
||||||
|
|
@ -74,12 +82,21 @@ def retirer_hote_de_tous_les_groupes(data: dict, host: str) -> None:
|
||||||
group_data.get("hosts", {}).pop(host, None)
|
group_data.get("hosts", {}).pop(host, None)
|
||||||
|
|
||||||
|
|
||||||
def ajouter_hote(data: dict, host: str, groups: list[str], ip: str | None, ansible_user: str | None) -> None:
|
def ajouter_hote(
|
||||||
|
data: dict,
|
||||||
|
host: str,
|
||||||
|
groups: list[str],
|
||||||
|
ip: str | None,
|
||||||
|
ansible_user: str | None,
|
||||||
|
vmid: str | None,
|
||||||
|
) -> None:
|
||||||
vars_for_host = variables_hote(data, host)
|
vars_for_host = variables_hote(data, host)
|
||||||
if ip:
|
if ip:
|
||||||
vars_for_host["ansible_host"] = ip
|
vars_for_host["ansible_host"] = ip
|
||||||
if ansible_user:
|
if ansible_user:
|
||||||
vars_for_host["ansible_user"] = ansible_user
|
vars_for_host["ansible_user"] = ansible_user
|
||||||
|
if vmid:
|
||||||
|
vars_for_host["proxmox_vmid"] = int(vmid) if vmid.isdigit() else vmid
|
||||||
|
|
||||||
for group in groups:
|
for group in groups:
|
||||||
assurer_groupe(data, group)["hosts"][host] = dict(vars_for_host)
|
assurer_groupe(data, group)["hosts"][host] = dict(vars_for_host)
|
||||||
|
|
@ -87,6 +104,9 @@ def ajouter_hote(data: dict, host: str, groups: list[str], ip: str | None, ansib
|
||||||
if ip:
|
if ip:
|
||||||
assurer_groupe(data, GROUPE_HOTES_ACTIFS)["hosts"][host] = dict(vars_for_host)
|
assurer_groupe(data, GROUPE_HOTES_ACTIFS)["hosts"][host] = dict(vars_for_host)
|
||||||
assurer_groupe(data, GROUPE_HOTES_PLANIFIES)["hosts"].pop(host, None)
|
assurer_groupe(data, GROUPE_HOTES_PLANIFIES)["hosts"].pop(host, None)
|
||||||
|
else:
|
||||||
|
assurer_groupe(data, GROUPE_HOTES_PLANIFIES)["hosts"][host] = dict(vars_for_host)
|
||||||
|
assurer_groupe(data, GROUPE_HOTES_ACTIFS)["hosts"].pop(host, None)
|
||||||
|
|
||||||
|
|
||||||
def definir_groupes_hote(data: dict, host: str, groups: list[str]) -> None:
|
def definir_groupes_hote(data: dict, host: str, groups: list[str]) -> None:
|
||||||
|
|
@ -136,7 +156,7 @@ def afficher_playbooks_hote(data: dict, host: str, playbook_dir: Path) -> None:
|
||||||
playbooks: list[Path] = []
|
playbooks: list[Path] = []
|
||||||
|
|
||||||
for group in groups:
|
for group in groups:
|
||||||
if not group.startswith(GROUPES_OPERATIONNELS_PREFIXES):
|
if not est_groupe_operationnel(group):
|
||||||
continue
|
continue
|
||||||
playbook = playbook_dir / f"{group}.yml"
|
playbook = playbook_dir / f"{group}.yml"
|
||||||
if playbook.exists():
|
if playbook.exists():
|
||||||
|
|
@ -161,6 +181,36 @@ def verifier_hote_actif(data: dict, host: str) -> None:
|
||||||
raise ValueError(f"Hote non actif dans l'inventaire: {host}")
|
raise ValueError(f"Hote non actif dans l'inventaire: {host}")
|
||||||
|
|
||||||
|
|
||||||
|
def verifier_coherence_hotes(data: dict) -> None:
|
||||||
|
vmids: dict[str, str] = {}
|
||||||
|
|
||||||
|
for host in tous_les_hotes(data):
|
||||||
|
groups = groupes_hote(data, host)
|
||||||
|
state_groups = [group for group in groups if group in GROUPES_ETAT_HOTE]
|
||||||
|
operational_groups = [group for group in groups if est_groupe_operationnel(group)]
|
||||||
|
vars_for_host = variables_hote(data, host)
|
||||||
|
|
||||||
|
if not state_groups and not operational_groups:
|
||||||
|
continue
|
||||||
|
if len(state_groups) != 1:
|
||||||
|
raise ValueError(
|
||||||
|
f"{host} doit appartenir exactement a un groupe d'etat: "
|
||||||
|
f"{GROUPE_HOTES_ACTIFS} ou {GROUPE_HOTES_PLANIFIES}"
|
||||||
|
)
|
||||||
|
if not operational_groups:
|
||||||
|
raise ValueError(f"{host} doit appartenir a au moins un groupe operationnel.")
|
||||||
|
if GROUPE_HOTES_ACTIFS in state_groups and not vars_for_host.get("ansible_host"):
|
||||||
|
raise ValueError(f"{host} est actif mais n'a pas ansible_host.")
|
||||||
|
|
||||||
|
vmid = vars_for_host.get("proxmox_vmid")
|
||||||
|
if vmid in (None, ""):
|
||||||
|
continue
|
||||||
|
vmid_key = str(vmid)
|
||||||
|
if vmid_key in vmids and vmids[vmid_key] != host:
|
||||||
|
raise ValueError(f"VMID en double: {vmid_key} pour {vmids[vmid_key]} et {host}")
|
||||||
|
vmids[vmid_key] = host
|
||||||
|
|
||||||
|
|
||||||
def verifier_playbooks_groupes(data: dict, playbook_dir: Path) -> None:
|
def verifier_playbooks_groupes(data: dict, playbook_dir: Path) -> None:
|
||||||
missing_playbooks: list[str] = []
|
missing_playbooks: list[str] = []
|
||||||
|
|
||||||
|
|
@ -168,7 +218,7 @@ def verifier_playbooks_groupes(data: dict, playbook_dir: Path) -> None:
|
||||||
hosts = group_data.get("hosts", {})
|
hosts = group_data.get("hosts", {})
|
||||||
if not hosts:
|
if not hosts:
|
||||||
continue
|
continue
|
||||||
if not group.startswith(GROUPES_OPERATIONNELS_PREFIXES):
|
if not est_groupe_operationnel(group):
|
||||||
continue
|
continue
|
||||||
playbook = playbook_dir / f"{group}.yml"
|
playbook = playbook_dir / f"{group}.yml"
|
||||||
if not playbook.exists():
|
if not playbook.exists():
|
||||||
|
|
@ -178,6 +228,8 @@ def verifier_playbooks_groupes(data: dict, playbook_dir: Path) -> None:
|
||||||
missing = ", ".join(sorted(missing_playbooks))
|
missing = ", ".join(sorted(missing_playbooks))
|
||||||
raise ValueError(f"Playbook de groupe manquant pour: {missing}")
|
raise ValueError(f"Playbook de groupe manquant pour: {missing}")
|
||||||
|
|
||||||
|
verifier_coherence_hotes(data)
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(description="Gere les hotes d'un inventaire Ansible statique Set-OPS.")
|
parser = argparse.ArgumentParser(description="Gere les hotes d'un inventaire Ansible statique Set-OPS.")
|
||||||
|
|
@ -190,6 +242,7 @@ def main() -> int:
|
||||||
add_parser.add_argument("--groupes", required=True)
|
add_parser.add_argument("--groupes", required=True)
|
||||||
add_parser.add_argument("--adresse-ip")
|
add_parser.add_argument("--adresse-ip")
|
||||||
add_parser.add_argument("--utilisateur-ansible")
|
add_parser.add_argument("--utilisateur-ansible")
|
||||||
|
add_parser.add_argument("--vmid")
|
||||||
add_parser.add_argument("--dossier-playbooks", type=Path)
|
add_parser.add_argument("--dossier-playbooks", type=Path)
|
||||||
|
|
||||||
groups_parser = subparsers.add_parser("definir-groupes", help="Remplace l'appartenance aux groupes pour un hote.")
|
groups_parser = subparsers.add_parser("definir-groupes", help="Remplace l'appartenance aux groupes pour un hote.")
|
||||||
|
|
@ -225,6 +278,7 @@ def main() -> int:
|
||||||
analyser_groupes(args.groupes),
|
analyser_groupes(args.groupes),
|
||||||
args.adresse_ip,
|
args.adresse_ip,
|
||||||
args.utilisateur_ansible,
|
args.utilisateur_ansible,
|
||||||
|
args.vmid,
|
||||||
)
|
)
|
||||||
if args.dossier_playbooks:
|
if args.dossier_playbooks:
|
||||||
verifier_playbooks_groupes(data, args.dossier_playbooks)
|
verifier_playbooks_groupes(data, args.dossier_playbooks)
|
||||||
|
|
|
||||||
22
scripts/inventory_rules.py
Normal file
22
scripts/inventory_rules.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Regles partagees pour les inventaires statiques Set-OPS."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
GROUPES_OPERATIONNELS_PREFIXES = ("serveurs_", "clients_")
|
||||||
|
GROUPE_HOTES_ACTIFS = "hotes_actifs"
|
||||||
|
GROUPE_HOTES_PLANIFIES = "hotes_planifies"
|
||||||
|
GROUPES_ETAT_HOTE = {GROUPE_HOTES_ACTIFS, GROUPE_HOTES_PLANIFIES}
|
||||||
|
|
||||||
|
|
||||||
|
def est_groupe_operationnel(groupe: str) -> bool:
|
||||||
|
return groupe.startswith(GROUPES_OPERATIONNELS_PREFIXES)
|
||||||
|
|
||||||
|
|
||||||
|
def groupes_operationnels_connus(enfants: dict, dossier_playbooks: Path) -> list[str]:
|
||||||
|
groupes = set(enfants)
|
||||||
|
groupes.update(playbook.stem for playbook in dossier_playbooks.glob("*.yml"))
|
||||||
|
return sorted(groupe for groupe in groupes if est_groupe_operationnel(groupe))
|
||||||
Reference in a new issue