site : le premier site construit depuis zero, et ce que le moteur supposait deja en place
- cloner_vm_debian / supprimer_vm_debian suivent SETOPS_UNDERLAY (le clonage visait le cluster du lien underlay.yml) ; sept cibles du Makefile aussi pour la voute. - site_machines.py transmet au clone la cle, le domaine et le resolveur du plan du site. - La machine qui porte le cache recoit artefacts_amorcage vide ; le socle retire alors le proxy herite du gabarit. P79 n'admet ce vide que sur la machine qui porte le cache. - serveur_icingaweb2 : la base et le compte d'amorcage apres l'installation des paquets. - genome_pousser : cle du runner, organisation genome et depots crees s'ils manquent. - common_packages : la lecture apt-daily s'execute en simulation (check_mode: false). 95 preuves (make verifier), make test passe. CHANGELOG 146. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f9e45b1676
commit
810670cd30
11 changed files with 290 additions and 95 deletions
45
CHANGELOG.md
45
CHANGELOG.md
|
|
@ -1,3 +1,48 @@
|
||||||
|
## 2026-10-13 (146) — Le premier site construit depuis zéro : ce que le moteur supposait déjà là
|
||||||
|
|
||||||
|
**95 preuves.** `make verifier` passe (site de Chezlepro), `make test` passe. Témoins : l'inventaire
|
||||||
|
du site de Chezlepro ne change que pour sa propre machine de cache ; P79 signale toujours une
|
||||||
|
valeur d'amorçage vide sur une machine qui ne porte pas le cache, et une valeur absente sur celle
|
||||||
|
qui le porte.
|
||||||
|
|
||||||
|
Les neuf machines du site de TechnoLibre sont nées du gabarit 9006 (`maurice`, `nvme` et
|
||||||
|
`miroir`, ~30 s chacune) et le site se déploie couche par couche. Aucun site n'avait jamais été
|
||||||
|
construit depuis zéro — celui de Chezlepro ne l'a jamais été — et chaque arrêt a montré une
|
||||||
|
chose que le moteur supposait **déjà en place** :
|
||||||
|
|
||||||
|
- **Le clonage visait le cluster du lien `underlay.yml`**, pas celui de `SETOPS_UNDERLAY` :
|
||||||
|
les VM de TechnoLibre seraient nées chez Chezlepro, avec ses jetons. `cloner_vm_debian` et
|
||||||
|
`supprimer_vm_debian` suivent la variable ; **sept cibles** du Makefile aussi pour la voûte
|
||||||
|
(`site-appliquer`, `site-deployer-tout`, `genome-pousser`, `genome-etat`, `forge-cles`,
|
||||||
|
`forge-comptes`, `cloner-vm`).
|
||||||
|
- **`site-creer` ne transmettait ni la clé, ni le domaine, ni le résolveur du plan** : les VM
|
||||||
|
ne portaient que la clé du gabarit (celle de Chezlepro), fermées à Ansible. `site_machines.py`
|
||||||
|
les tire du plan, et refuse si la clé publique manque.
|
||||||
|
- **Le cache s'amorçait sur lui-même**, et toutes les machines sur un cache déployé plus tard.
|
||||||
|
La machine qui porte le cache reçoit `artefacts_amorcage` vide ; le socle retire alors le
|
||||||
|
fichier hérité du gabarit (qui visait `10.0.33.21`, une adresse d'avant le renumérotage) et
|
||||||
|
elle va droit chez Debian. Déployée seule d'abord ; prouvée : HTTP 200 depuis une autre zone.
|
||||||
|
- **Les règles nftables du site n'avaient jamais été générées** pour ce site : le pare-feu
|
||||||
|
serait retombé sur son gabarit générique. Générées, relues, `nft -c` sur deux machines.
|
||||||
|
- **La PKI voulait restaurer son état d'un dépôt qui n'existait pas encore.** Le refus est
|
||||||
|
juste (conclure « rien à restaurer » faute de joindre le dépôt ferait naître neuf ce qui
|
||||||
|
attend ailleurs) : le dépôt de sauvegarde est déployé d'abord, il répond « rien
|
||||||
|
d'antérieur », et l'autorité naît neuve parce qu'il l'a dit. L'ordre des couches reste à
|
||||||
|
corriger pour une première naissance.
|
||||||
|
- **`serveur_icingaweb2` chargeait son schéma avant d'installer le paquet qui le fournit.**
|
||||||
|
Le bloc base et compte d'amorçage vient après les paquets.
|
||||||
|
- **Une forge neuve n'avait ni organisation ni dépôts**, et le runner ne fabriquait sa clé
|
||||||
|
qu'après avoir cloné. `genome_pousser` crée la clé si elle manque (à l'identique du rôle),
|
||||||
|
l'organisation `genome` (publique) et les dépôts manquants (moteur et modèles publics,
|
||||||
|
carte du site et plans privés) — la forme de la forge de Chezlepro, faite à la main.
|
||||||
|
- **La simulation échouait sur une lecture** (`systemctl is-active` sautée en `--check`, donc
|
||||||
|
sans `rc`) : `check_mode: false`.
|
||||||
|
|
||||||
|
**Voûte du site de TechnoLibre** : les secrets TSIG du DNS public, identiques à ceux du site de
|
||||||
|
Chezlepro (les primaires n'en connaissent qu'un par relation). **Plan** : la clé d'hôte de la
|
||||||
|
forge, relevée à la source. Un lien entre les deux sites est prévu ; d'ici là, le DNS public
|
||||||
|
de ce site ne reçoit pas les zones.
|
||||||
|
|
||||||
## 2026-10-13 (145) — La face publique d'un site sans adresse fixe : le relais
|
## 2026-10-13 (145) — La face publique d'un site sans adresse fixe : le relais
|
||||||
|
|
||||||
**95 preuves.** `make verifier` passe (site de Chezlepro), `make test` passe. Témoin : le devis de
|
**95 preuves.** `make verifier` passe (site de Chezlepro), `make test` passe. Témoin : le devis de
|
||||||
|
|
|
||||||
15
Makefile
15
Makefile
|
|
@ -1315,7 +1315,7 @@ site-deployer-tout: ansible-runtime ## Deploie TOUT le site dans l'ordre des cou
|
||||||
python3 scripts/verifier_genome_a_jour.py || exit 2; \
|
python3 scripts/verifier_genome_a_jour.py || exit 2; \
|
||||||
python3 scripts/orchestrer.py verifier; \
|
python3 scripts/orchestrer.py verifier; \
|
||||||
python3 scripts/orchestrer.py ecrire; \
|
python3 scripts/orchestrer.py ecrire; \
|
||||||
voute="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \
|
voute="$$(dirname "$$(readlink -f "$${SETOPS_UNDERLAY:-underlay.yml}")")/underlay.vault.yml"; \
|
||||||
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
||||||
mode="$$([[ -n "$(MODE_CHECK)" ]] && printf -- '--check --diff' || true)"; \
|
mode="$$([[ -n "$(MODE_CHECK)" ]] && printf -- '--check --diff' || true)"; \
|
||||||
ansible-playbook -i scripts/site_inventaire.py playbooks/site.yml \
|
ansible-playbook -i scripts/site_inventaire.py playbooks/site.yml \
|
||||||
|
|
@ -1371,7 +1371,7 @@ gabarit-etat: ## Le gabarit porte-t-il ce que le SITE declare ? — ne corrige r
|
||||||
.PHONY: genome-etat
|
.PHONY: genome-etat
|
||||||
genome-etat: ## Ce que la forge du site porte, face au poste — ne corrige rien, regarde
|
genome-etat: ## Ce que la forge du site porte, face au poste — ne corrige rien, regarde
|
||||||
@set -e; \
|
@set -e; \
|
||||||
voute="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \
|
voute="$$(dirname "$$(readlink -f "$${SETOPS_UNDERLAY:-underlay.yml}")")/underlay.vault.yml"; \
|
||||||
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
||||||
ansible-playbook -i scripts/site_inventaire.py \
|
ansible-playbook -i scripts/site_inventaire.py \
|
||||||
playbooks/maintenance/genome_etat.yml "$${supp[@]}" $(ARGS)
|
playbooks/maintenance/genome_etat.yml "$${supp[@]}" $(ARGS)
|
||||||
|
|
@ -1386,7 +1386,7 @@ publier: ## Publie le genome d'un geste : eregion PUIS la forge du site, et le v
|
||||||
.PHONY: publier
|
.PHONY: publier
|
||||||
genome-pousser: ## Le runner du site pousse le genome sur la forge du site — DEPOT=<nom> pour un seul
|
genome-pousser: ## Le runner du site pousse le genome sur la forge du site — DEPOT=<nom> pour un seul
|
||||||
@set -e; \
|
@set -e; \
|
||||||
voute="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \
|
voute="$$(dirname "$$(readlink -f "$${SETOPS_UNDERLAY:-underlay.yml}")")/underlay.vault.yml"; \
|
||||||
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
||||||
SETOPS_DEPOT="$(DEPOT)" ansible-playbook -i scripts/site_inventaire.py \
|
SETOPS_DEPOT="$(DEPOT)" ansible-playbook -i scripts/site_inventaire.py \
|
||||||
playbooks/maintenance/genome_pousser.yml "$${supp[@]}" $(ARGS)
|
playbooks/maintenance/genome_pousser.yml "$${supp[@]}" $(ARGS)
|
||||||
|
|
@ -1394,7 +1394,7 @@ genome-pousser: ## Le runner du site pousse le genome sur la forge du site — D
|
||||||
.PHONY: forge-cles
|
.PHONY: forge-cles
|
||||||
forge-cles: ## Le site enregistre sur le depot de chaque locataire la cle de lecture que sa face publie — CONFIRMER=true pour ecrire
|
forge-cles: ## Le site enregistre sur le depot de chaque locataire la cle de lecture que sa face publie — CONFIRMER=true pour ecrire
|
||||||
@set -e; \
|
@set -e; \
|
||||||
voute="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \
|
voute="$$(dirname "$$(readlink -f "$${SETOPS_UNDERLAY:-underlay.yml}")")/underlay.vault.yml"; \
|
||||||
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
||||||
CONFIRMER="$(CONFIRMER)" ansible-playbook -i scripts/site_inventaire.py \
|
CONFIRMER="$(CONFIRMER)" ansible-playbook -i scripts/site_inventaire.py \
|
||||||
playbooks/maintenance/forge_cles.yml "$${supp[@]}" $(ARGS)
|
playbooks/maintenance/forge_cles.yml "$${supp[@]}" $(ARGS)
|
||||||
|
|
@ -1402,7 +1402,7 @@ forge-cles: ## Le site enregistre sur le depot de chaque locataire la cle de lec
|
||||||
.PHONY: forge-comptes
|
.PHONY: forge-comptes
|
||||||
forge-comptes: ## Les comptes de la forge du site : un par mainteneur (plan du site), et le lecteur de la console — CONFIRMER=true pour ecrire
|
forge-comptes: ## Les comptes de la forge du site : un par mainteneur (plan du site), et le lecteur de la console — CONFIRMER=true pour ecrire
|
||||||
@set -e; \
|
@set -e; \
|
||||||
voute="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \
|
voute="$$(dirname "$$(readlink -f "$${SETOPS_UNDERLAY:-underlay.yml}")")/underlay.vault.yml"; \
|
||||||
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
supp=(); [[ -f "$$voute" ]] && supp+=( -e "@$$voute" ); \
|
||||||
CONFIRMER="$(CONFIRMER)" ansible-playbook -i scripts/site_inventaire.py \
|
CONFIRMER="$(CONFIRMER)" ansible-playbook -i scripts/site_inventaire.py \
|
||||||
playbooks/maintenance/forge_comptes.yml "$${supp[@]}" $(ARGS)
|
playbooks/maintenance/forge_comptes.yml "$${supp[@]}" $(ARGS)
|
||||||
|
|
@ -1482,6 +1482,9 @@ site-creer: ## Cree les machines du site depuis l'underlay — CONFIRMER=true
|
||||||
NOEUD_PROXMOX="$$SETOPS_NOEUD" \
|
NOEUD_PROXMOX="$$SETOPS_NOEUD" \
|
||||||
VMID_MODELE="$${SETOPS_VMID_MODELE:-$(VMID_MODELE)}" \
|
VMID_MODELE="$${SETOPS_VMID_MODELE:-$(VMID_MODELE)}" \
|
||||||
CLONE_COMPLET="$$SETOPS_CLONE_COMPLET" \
|
CLONE_COMPLET="$$SETOPS_CLONE_COMPLET" \
|
||||||
|
CLE_SSH_PUBLIQUE="$${SETOPS_CLE_PUBLIQUE:-$(CLE_SSH_PUBLIQUE)}" \
|
||||||
|
DNS="$${SETOPS_DNS:-$(DNS)}" \
|
||||||
|
DOMAINE_RECHERCHE="$${SETOPS_DOMAINE:-$(DOMAINE_RECHERCHE)}" \
|
||||||
FORMAT_DISQUE="$(FORMAT_DISQUE)"; \
|
FORMAT_DISQUE="$(FORMAT_DISQUE)"; \
|
||||||
printf '=== %s : %s s ===\n' "$$m" "$$(( $$(date +%s) - t0 ))"; \
|
printf '=== %s : %s s ===\n' "$$m" "$$(( $$(date +%s) - t0 ))"; \
|
||||||
done; \
|
done; \
|
||||||
|
|
@ -1711,7 +1714,7 @@ cloner-vm: ansible-runtime ## Clone une VM depuis le gabarit dore — HOTE=<nom>
|
||||||
done; \
|
done; \
|
||||||
done; \
|
done; \
|
||||||
if [[ -z "$$vault_file" && -L underlay.yml ]]; then \
|
if [[ -z "$$vault_file" && -L underlay.yml ]]; then \
|
||||||
vault_file="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \
|
vault_file="$$(dirname "$$(readlink -f "$${SETOPS_UNDERLAY:-underlay.yml}")")/underlay.vault.yml"; \
|
||||||
fi; \
|
fi; \
|
||||||
if [[ -n "$$vault_file" && -f "$$vault_file" ]]; then \
|
if [[ -n "$$vault_file" && -f "$$vault_file" ]]; then \
|
||||||
read -r premiere_ligne < "$$vault_file" || true; \
|
read -r premiere_ligne < "$$vault_file" || true; \
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,18 @@
|
||||||
- artefacts_amorcage is defined
|
- artefacts_amorcage is defined
|
||||||
- artefacts_amorcage | string | length > 0
|
- artefacts_amorcage | string | length > 0
|
||||||
|
|
||||||
|
# LA MACHINE QUI PORTE LE CACHE VA DROIT CHEZ DEBIAN (2026-10-13). L'inventaire lui
|
||||||
|
# donne `artefacts_amorcage` VIDE (et non absent) : le fichier herite du gabarit vise
|
||||||
|
# un cache qui n'existe pas encore — le sien — ou une adresse perimee. Absent, il
|
||||||
|
# survivrait ; vide, on le retire.
|
||||||
|
- name: Retirer l'amorcage sur la machine qui porte le cache
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/apt/apt.conf.d/00-setops-amorcage-artefacts
|
||||||
|
state: absent
|
||||||
|
when:
|
||||||
|
- artefacts_amorcage is defined
|
||||||
|
- artefacts_amorcage | string | length == 0
|
||||||
|
|
||||||
# RETIRER CE QUI DESIGNE UN CACHE QUI N'EXISTE PLUS (2026-09-10).
|
# RETIRER CE QUI DESIGNE UN CACHE QUI N'EXISTE PLUS (2026-09-10).
|
||||||
#
|
#
|
||||||
# `client_artefacts` ecrit `00-setops-artefacts`, qui trie APRES le plancher et gagne.
|
# `client_artefacts` ecrit `00-setops-artefacts`, qui trie APRES le plancher et gagne.
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,87 @@
|
||||||
# depot par depot, et l'exploitant doit aller cliquer. La cible s'en charge : la
|
# depot par depot, et l'exploitant doit aller cliquer. La cible s'en charge : la
|
||||||
# declaration est idempotente (422 = deja la), et sa portee est exactement ce qu'on
|
# declaration est idempotente (422 = deja la), et sa portee est exactement ce qu'on
|
||||||
# pousse, jamais plus large.
|
# pousse, jamais plus large.
|
||||||
|
# UNE FORGE NEUVE, UN RUNNER NEUF (2026-10-13, premier site construit depuis zero). Le
|
||||||
|
# runner du site ne fabrique sa cle qu'APRES avoir clone le genome, et il ne peut cloner
|
||||||
|
# que ce que cette commande a pousse — qui exige sa cle. Personne n'allait au bout. On
|
||||||
|
# fabrique donc la cle ici si elle manque, A L'IDENTIQUE de `serveur_ops` (meme commande,
|
||||||
|
# meme commentaire, `creates:`) : le role la retrouvera et ne la refera pas.
|
||||||
|
- name: Assurer le répertoire SSH du runner
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ genome_racine }}/.ssh"
|
||||||
|
state: directory
|
||||||
|
mode: "0700"
|
||||||
|
|
||||||
|
- name: Fabriquer la clé du runner si elle n'existe pas encore
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: >-
|
||||||
|
ssh-keygen -t {{ serveur_ops_cle_type | default('ed25519') }} -N ''
|
||||||
|
-C "setops@{{ inventory_hostname }}.{{ domaine_interne }}"
|
||||||
|
-f {{ genome_racine }}/.ssh/id_{{ serveur_ops_cle_type | default('ed25519') }}
|
||||||
|
creates: "{{ genome_racine }}/.ssh/id_{{ serveur_ops_cle_type | default('ed25519') }}"
|
||||||
|
|
||||||
|
# L'ORGANISATION ET SES DEPOTS N'ETAIENT CREES PAR PERSONNE : au site de Chezlepro, une
|
||||||
|
# fois, a la main. Meme forme qu'elle : organisation PUBLIQUE ; le moteur et les modeles
|
||||||
|
# PUBLICS (le runner les clone en anonyme) ; la carte du site et les plans des
|
||||||
|
# locataires PRIVES (`serveur_ops_roles_prives`, lus en SSH par cle de deploiement).
|
||||||
|
- name: L'organisation du génome existe-t-elle ?
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "https://forge.{{ domaine_interne }}/api/v1/orgs/{{ serveur_ops_forge_organisation | default('genome') }}"
|
||||||
|
validate_certs: false
|
||||||
|
status_code: [200, 404]
|
||||||
|
register: genome_org
|
||||||
|
|
||||||
|
- name: Créer l'organisation du génome
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "https://forge.{{ domaine_interne }}/api/v1/orgs"
|
||||||
|
method: POST
|
||||||
|
url_username: forgejo-admin
|
||||||
|
url_password: "{{ vault_forgejo_admin }}"
|
||||||
|
force_basic_auth: true
|
||||||
|
validate_certs: false
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
username: "{{ serveur_ops_forge_organisation | default('genome') }}"
|
||||||
|
visibility: public
|
||||||
|
status_code: 201
|
||||||
|
no_log: true
|
||||||
|
when: genome_org.status == 404
|
||||||
|
|
||||||
|
- name: Quels dépôts poussés existent déjà sur la forge ?
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "https://forge.{{ domaine_interne }}/api/v1/repos/{{ serveur_ops_forge_organisation | default('genome') }}/{{ item.depot }}"
|
||||||
|
url_username: forgejo-admin
|
||||||
|
url_password: "{{ vault_forgejo_admin }}"
|
||||||
|
force_basic_auth: true
|
||||||
|
validate_certs: false
|
||||||
|
status_code: [200, 404]
|
||||||
|
loop: "{{ genome_a_pousser }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.depot }}"
|
||||||
|
no_log: true
|
||||||
|
register: genome_depots_forge
|
||||||
|
|
||||||
|
- name: Créer les dépôts qui manquent
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "https://forge.{{ domaine_interne }}/api/v1/orgs/{{ serveur_ops_forge_organisation | default('genome') }}/repos"
|
||||||
|
method: POST
|
||||||
|
url_username: forgejo-admin
|
||||||
|
url_password: "{{ vault_forgejo_admin }}"
|
||||||
|
force_basic_auth: true
|
||||||
|
validate_certs: false
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: "{{ item.item.depot }}"
|
||||||
|
private: "{{ item.item.role in (serveur_ops_roles_prives | default(['instance', 'tenant', 'hebergeur'])) }}"
|
||||||
|
default_branch: "{{ item.item.branche | default('main') }}"
|
||||||
|
auto_init: false
|
||||||
|
status_code: 201
|
||||||
|
loop: "{{ genome_depots_forge.results }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.item.depot }}"
|
||||||
|
no_log: true
|
||||||
|
when: item.status == 404
|
||||||
|
|
||||||
- name: Lire la clé publique du runner
|
- name: Lire la clé publique du runner
|
||||||
ansible.builtin.slurp:
|
ansible.builtin.slurp:
|
||||||
src: "{{ genome_racine }}/.ssh/id_{{ serveur_ops_cle_type | default('ed25519') }}.pub"
|
src: "{{ genome_racine }}/.ssh/id_{{ serveur_ops_cle_type | default('ed25519') }}.pub"
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,17 @@
|
||||||
# vit dans le depot de l'hebergeur, que le symlink `underlay.yml` designe deja
|
# vit dans le depot de l'hebergeur, que le symlink `underlay.yml` designe deja
|
||||||
# (D-14/D-17) : on en DERIVE le chemin plutot que de redeclarer l'hebergeur. Absent
|
# (D-14/D-17) : on en DERIVE le chemin plutot que de redeclarer l'hebergeur. Absent
|
||||||
# (depot sans underlay monte), le `stat` echoue simplement et rien n'est charge.
|
# (depot sans underlay monte), le `stat` echoue simplement et rien n'est charge.
|
||||||
setops_proxmox_hebergeur: "{{ (playbook_dir ~ '/../../underlay.yml') | realpath | dirname }}/proxmox-hebergeur.yml"
|
# LA CARTE DESIGNEE (2026-10-13) : `SETOPS_UNDERLAY` quand il est pose, le symlink sinon.
|
||||||
|
# Lire le symlink seul aurait clone les machines du site de TechnoLibre sur le cluster
|
||||||
|
# de Chezlepro, avec ses jetons — l'inventaire, lui, suivait deja la variable.
|
||||||
|
setops_carte_underlay: "{{ lookup('ansible.builtin.env', 'SETOPS_UNDERLAY') | default(playbook_dir ~ '/../../underlay.yml', true) }}"
|
||||||
|
setops_proxmox_hebergeur: "{{ setops_carte_underlay | realpath | dirname }}/proxmox-hebergeur.yml"
|
||||||
# LA VOUTE DE L'UNDERLAY — les secrets du MONDE PHYSIQUE (jeton d'API du cluster).
|
# LA VOUTE DE L'UNDERLAY — les secrets du MONDE PHYSIQUE (jeton d'API du cluster).
|
||||||
# Meme derivation que le fichier ci-dessus : le symlink `underlay.yml` designe deja
|
# Meme derivation que le fichier ci-dessus : le symlink `underlay.yml` designe deja
|
||||||
# l'hebergeur, on n'a rien a redeclarer. Depuis le 2026-08-22 ces secrets ne vivent
|
# l'hebergeur, on n'a rien a redeclarer. Depuis le 2026-08-22 ces secrets ne vivent
|
||||||
# plus dans la voute d'un tenant : recopies chez chacun, ils ne pouvaient plus etre
|
# plus dans la voute d'un tenant : recopies chez chacun, ils ne pouvaient plus etre
|
||||||
# revoques isolement (docs/frontiere-physique-virtuel.md).
|
# revoques isolement (docs/frontiere-physique-virtuel.md).
|
||||||
setops_voute_underlay: "{{ (playbook_dir ~ '/../../underlay.yml') | realpath | dirname }}/underlay.vault.yml"
|
setops_voute_underlay: "{{ setops_carte_underlay | realpath | dirname }}/underlay.vault.yml"
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Verifier les fichiers de variables Proxmox optionnels
|
- name: Verifier les fichiers de variables Proxmox optionnels
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,11 @@
|
||||||
# vit dans le depot de l'hebergeur, que le symlink `underlay.yml` designe deja
|
# vit dans le depot de l'hebergeur, que le symlink `underlay.yml` designe deja
|
||||||
# (D-14/D-17) : on en DERIVE le chemin plutot que de redeclarer l'hebergeur. Absent
|
# (D-14/D-17) : on en DERIVE le chemin plutot que de redeclarer l'hebergeur. Absent
|
||||||
# (depot sans underlay monte), le `stat` echoue simplement et rien n'est charge.
|
# (depot sans underlay monte), le `stat` echoue simplement et rien n'est charge.
|
||||||
setops_proxmox_hebergeur: "{{ (playbook_dir ~ '/../../underlay.yml') | realpath | dirname }}/proxmox-hebergeur.yml"
|
# LA CARTE DESIGNEE (2026-10-13) : `SETOPS_UNDERLAY` quand il est pose, le symlink sinon.
|
||||||
|
# Lire le symlink seul aurait clone les machines du site de TechnoLibre sur le cluster
|
||||||
|
# de Chezlepro, avec ses jetons — l'inventaire, lui, suivait deja la variable.
|
||||||
|
setops_carte_underlay: "{{ lookup('ansible.builtin.env', 'SETOPS_UNDERLAY') | default(playbook_dir ~ '/../../underlay.yml', true) }}"
|
||||||
|
setops_proxmox_hebergeur: "{{ setops_carte_underlay | realpath | dirname }}/proxmox-hebergeur.yml"
|
||||||
# cibles_vmid : liste d'entiers, passée en -e (sinon dérivée des hôtes actifs).
|
# cibles_vmid : liste d'entiers, passée en -e (sinon dérivée des hôtes actifs).
|
||||||
cibles_vmid: "{{ groups['hotes_actifs'] | map('extract', hostvars, 'proxmox_vmid') | map('int') | list }}"
|
cibles_vmid: "{{ groups['hotes_actifs'] | map('extract', hostvars, 'proxmox_vmid') | map('int') | list }}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,14 @@
|
||||||
|
|
||||||
# UN PASSAGE DEJA PARTI se termine avant nos operations apt, plutot que de se les disputer.
|
# UN PASSAGE DEJA PARTI se termine avant nos operations apt, plutot que de se les disputer.
|
||||||
# `is-active` rend 0 si l'une au moins des unites tourne.
|
# `is-active` rend 0 si l'une au moins des unites tourne.
|
||||||
|
# `check_mode: false` : une LECTURE. Sautee en simulation, elle n'avait jamais de `rc`, la
|
||||||
|
# condition `until` ne se remplissait pas, et `--check` echouait la apres 30 essais sur
|
||||||
|
# chaque machine (mesure le 2026-10-13, premiere simulation du site de TechnoLibre).
|
||||||
- name: Attendre la fin d'un passage apt automatique deja en cours
|
- name: Attendre la fin d'un passage apt automatique deja en cours
|
||||||
ansible.builtin.command:
|
ansible.builtin.command:
|
||||||
argv: ["systemctl", "is-active", "--quiet", "apt-daily.service", "apt-daily-upgrade.service"]
|
argv: ["systemctl", "is-active", "--quiet", "apt-daily.service", "apt-daily-upgrade.service"]
|
||||||
register: common_packages_passage
|
register: common_packages_passage
|
||||||
|
check_mode: false
|
||||||
changed_when: false
|
changed_when: false
|
||||||
failed_when: false
|
failed_when: false
|
||||||
until: common_packages_passage.rc != 0
|
until: common_packages_passage.rc != 0
|
||||||
|
|
|
||||||
|
|
@ -79,89 +79,6 @@
|
||||||
passe deviné : renseigner la clé dans la voûte de cet écosystème.
|
passe deviné : renseigner la clé dans la voûte de cet écosystème.
|
||||||
when: serveur_icingaweb2_auth == 'db'
|
when: serveur_icingaweb2_auth == 'db'
|
||||||
|
|
||||||
# LE CLIENT POSTGRESQL DES MODULES `community.postgresql`, SUR CETTE MACHINE (2026-10-03).
|
|
||||||
#
|
|
||||||
# Les requetes ci-dessous s'executent sur l'hote de la vigie, pas sur le serveur de base.
|
|
||||||
# Au site, les deux sont la meme machine : `serveur_postgresql` y avait deja pose
|
|
||||||
# `python3-psycopg2`, et l'absence ne se voyait pas. Chez un locataire, la vigie vit sur
|
|
||||||
# `mon-01` et la base sur `data-sql-01` — premier passage apres que la base est devenue
|
|
||||||
# commune a tous les modes : `No module named 'psycopg2'`, masque par `no_log`.
|
|
||||||
- name: Installer le client PostgreSQL de Python (modules community.postgresql)
|
|
||||||
ansible.builtin.apt:
|
|
||||||
name: python3-psycopg2
|
|
||||||
state: present
|
|
||||||
|
|
||||||
# LE SCHEMA NE SE CHARGE QU'UNE FOIS. Le rejouer sur une base deja peuplee echouerait sur
|
|
||||||
# les objets existants — on lit donc d'abord si la table des comptes est la.
|
|
||||||
- name: La base de la console est-elle déjà en place ?
|
|
||||||
community.postgresql.postgresql_query:
|
|
||||||
login_host: "{{ serveur_icingaweb2_db_host }}"
|
|
||||||
login_user: "{{ serveur_icingaweb2_db_utilisateur }}"
|
|
||||||
login_password: "{{ serveur_icingaweb2_db_motdepasse }}"
|
|
||||||
login_db: "{{ serveur_icingaweb2_db_base }}"
|
|
||||||
ssl_mode: "{{ serveur_icingaweb2_db_sslmode | default('prefer', true) or 'prefer' }}"
|
|
||||||
ca_cert: "{{ serveur_icingaweb2_db_sslrootcert if serveur_icingaweb2_db_sslmode | default('') else omit }}"
|
|
||||||
query: "SELECT to_regclass('public.icingaweb_user') IS NOT NULL AS presente"
|
|
||||||
register: serveur_icingaweb2_schema_etat
|
|
||||||
changed_when: false
|
|
||||||
no_log: true
|
|
||||||
when:
|
|
||||||
- not ansible_check_mode
|
|
||||||
|
|
||||||
- name: Charger le schéma de la console (comptes, préférences, migrations)
|
|
||||||
community.postgresql.postgresql_script:
|
|
||||||
login_host: "{{ serveur_icingaweb2_db_host }}"
|
|
||||||
login_user: "{{ serveur_icingaweb2_db_utilisateur }}"
|
|
||||||
login_password: "{{ serveur_icingaweb2_db_motdepasse }}"
|
|
||||||
login_db: "{{ serveur_icingaweb2_db_base }}"
|
|
||||||
ssl_mode: "{{ serveur_icingaweb2_db_sslmode | default('prefer', true) or 'prefer' }}"
|
|
||||||
ca_cert: "{{ serveur_icingaweb2_db_sslrootcert if serveur_icingaweb2_db_sslmode | default('') else omit }}"
|
|
||||||
path: "{{ serveur_icingaweb2_schema }}"
|
|
||||||
no_log: true
|
|
||||||
when:
|
|
||||||
- not ansible_check_mode
|
|
||||||
- not (serveur_icingaweb2_schema_etat.query_result[0].presente | default(false))
|
|
||||||
|
|
||||||
# LE HACHAGE EST CELUI QUE L'APPLICATION VERIFIE, PAS UN QU'ON CHOISIT. `password_hash()`
|
|
||||||
# de PHP est exactement la fonction qu'Icinga Web 2 appelle a la connexion ; produire un
|
|
||||||
# hachage d'un autre outil donnerait une base valide et une connexion impossible.
|
|
||||||
- name: Hacher le mot de passe du compte d'amorçage
|
|
||||||
ansible.builtin.command:
|
|
||||||
argv:
|
|
||||||
- php
|
|
||||||
- -r
|
|
||||||
- 'echo password_hash($argv[1], PASSWORD_DEFAULT);'
|
|
||||||
- "{{ serveur_icingaweb2_admin_motdepasse }}"
|
|
||||||
register: serveur_icingaweb2_hachage
|
|
||||||
changed_when: false
|
|
||||||
no_log: true
|
|
||||||
when:
|
|
||||||
- serveur_icingaweb2_auth == 'db'
|
|
||||||
- not ansible_check_mode
|
|
||||||
|
|
||||||
# CREE S'IL MANQUE, JAMAIS ECRASE. Un mot de passe change dans l'interface appartient a
|
|
||||||
# celui qui l'a change : le moteur ne le reprend pas au deploiement suivant. Il ne pose
|
|
||||||
# que le compte qui permet d'entrer la premiere fois.
|
|
||||||
- name: Poser le compte d'amorçage s'il n'existe pas
|
|
||||||
community.postgresql.postgresql_query:
|
|
||||||
login_host: "{{ serveur_icingaweb2_db_host }}"
|
|
||||||
login_user: "{{ serveur_icingaweb2_db_utilisateur }}"
|
|
||||||
login_password: "{{ serveur_icingaweb2_db_motdepasse }}"
|
|
||||||
login_db: "{{ serveur_icingaweb2_db_base }}"
|
|
||||||
ssl_mode: "{{ serveur_icingaweb2_db_sslmode | default('prefer', true) or 'prefer' }}"
|
|
||||||
ca_cert: "{{ serveur_icingaweb2_db_sslrootcert if serveur_icingaweb2_db_sslmode | default('') else omit }}"
|
|
||||||
query: >-
|
|
||||||
INSERT INTO icingaweb_user (name, active, password_hash)
|
|
||||||
VALUES (%s, 1, %s) ON CONFLICT (name) DO NOTHING
|
|
||||||
positional_args:
|
|
||||||
- "{{ serveur_icingaweb2_admin_utilisateur }}"
|
|
||||||
- "{{ serveur_icingaweb2_hachage.stdout }}"
|
|
||||||
no_log: true
|
|
||||||
when:
|
|
||||||
- serveur_icingaweb2_auth == 'db'
|
|
||||||
- not ansible_check_mode
|
|
||||||
|
|
||||||
|
|
||||||
# ON NE RESOUT PAS UN ANNUAIRE QUAND ON N'EN A PAS (2026-09-14).
|
# ON NE RESOUT PAS UN ANNUAIRE QUAND ON N'EN A PAS (2026-09-14).
|
||||||
#
|
#
|
||||||
# `resoudre_annuaire` etait appele SANS CONDITION. Au site — qui n'a ni LDAP ni Keycloak —
|
# `resoudre_annuaire` etait appele SANS CONDITION. Au site — qui n'a ni LDAP ni Keycloak —
|
||||||
|
|
@ -259,6 +176,94 @@
|
||||||
| map('regex_replace', '^(?:Purg|Remv) (\\S+).*$', '\\1') | list)
|
| map('regex_replace', '^(?:Purg|Remv) (\\S+).*$', '\\1') | list)
|
||||||
is subset(serveur_icingaweb2_apache_restes)
|
is subset(serveur_icingaweb2_apache_restes)
|
||||||
|
|
||||||
|
# LA BASE ET LE COMPTE D'AMORCAGE APRES LES PAQUETS (2026-10-13). Ce bloc venait AVANT
|
||||||
|
# l'installation : il lit `/usr/share/icingaweb2/schema/pgsql.schema.sql` et hache par `php`,
|
||||||
|
# deux choses que les paquets apportent. Invisible la ou la console etait deja installee ;
|
||||||
|
# sur la premiere machine neuve (site de TechnoLibre), le schema n'existait pas encore.
|
||||||
|
|
||||||
|
# LE CLIENT POSTGRESQL DES MODULES `community.postgresql`, SUR CETTE MACHINE (2026-10-03).
|
||||||
|
#
|
||||||
|
# Les requetes ci-dessous s'executent sur l'hote de la vigie, pas sur le serveur de base.
|
||||||
|
# Au site, les deux sont la meme machine : `serveur_postgresql` y avait deja pose
|
||||||
|
# `python3-psycopg2`, et l'absence ne se voyait pas. Chez un locataire, la vigie vit sur
|
||||||
|
# `mon-01` et la base sur `data-sql-01` — premier passage apres que la base est devenue
|
||||||
|
# commune a tous les modes : `No module named 'psycopg2'`, masque par `no_log`.
|
||||||
|
- name: Installer le client PostgreSQL de Python (modules community.postgresql)
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: python3-psycopg2
|
||||||
|
state: present
|
||||||
|
|
||||||
|
# LE SCHEMA NE SE CHARGE QU'UNE FOIS. Le rejouer sur une base deja peuplee echouerait sur
|
||||||
|
# les objets existants — on lit donc d'abord si la table des comptes est la.
|
||||||
|
- name: La base de la console est-elle déjà en place ?
|
||||||
|
community.postgresql.postgresql_query:
|
||||||
|
login_host: "{{ serveur_icingaweb2_db_host }}"
|
||||||
|
login_user: "{{ serveur_icingaweb2_db_utilisateur }}"
|
||||||
|
login_password: "{{ serveur_icingaweb2_db_motdepasse }}"
|
||||||
|
login_db: "{{ serveur_icingaweb2_db_base }}"
|
||||||
|
ssl_mode: "{{ serveur_icingaweb2_db_sslmode | default('prefer', true) or 'prefer' }}"
|
||||||
|
ca_cert: "{{ serveur_icingaweb2_db_sslrootcert if serveur_icingaweb2_db_sslmode | default('') else omit }}"
|
||||||
|
query: "SELECT to_regclass('public.icingaweb_user') IS NOT NULL AS presente"
|
||||||
|
register: serveur_icingaweb2_schema_etat
|
||||||
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Charger le schéma de la console (comptes, préférences, migrations)
|
||||||
|
community.postgresql.postgresql_script:
|
||||||
|
login_host: "{{ serveur_icingaweb2_db_host }}"
|
||||||
|
login_user: "{{ serveur_icingaweb2_db_utilisateur }}"
|
||||||
|
login_password: "{{ serveur_icingaweb2_db_motdepasse }}"
|
||||||
|
login_db: "{{ serveur_icingaweb2_db_base }}"
|
||||||
|
ssl_mode: "{{ serveur_icingaweb2_db_sslmode | default('prefer', true) or 'prefer' }}"
|
||||||
|
ca_cert: "{{ serveur_icingaweb2_db_sslrootcert if serveur_icingaweb2_db_sslmode | default('') else omit }}"
|
||||||
|
path: "{{ serveur_icingaweb2_schema }}"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- not ansible_check_mode
|
||||||
|
- not (serveur_icingaweb2_schema_etat.query_result[0].presente | default(false))
|
||||||
|
|
||||||
|
# LE HACHAGE EST CELUI QUE L'APPLICATION VERIFIE, PAS UN QU'ON CHOISIT. `password_hash()`
|
||||||
|
# de PHP est exactement la fonction qu'Icinga Web 2 appelle a la connexion ; produire un
|
||||||
|
# hachage d'un autre outil donnerait une base valide et une connexion impossible.
|
||||||
|
- name: Hacher le mot de passe du compte d'amorçage
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv:
|
||||||
|
- php
|
||||||
|
- -r
|
||||||
|
- 'echo password_hash($argv[1], PASSWORD_DEFAULT);'
|
||||||
|
- "{{ serveur_icingaweb2_admin_motdepasse }}"
|
||||||
|
register: serveur_icingaweb2_hachage
|
||||||
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- serveur_icingaweb2_auth == 'db'
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
# CREE S'IL MANQUE, JAMAIS ECRASE. Un mot de passe change dans l'interface appartient a
|
||||||
|
# celui qui l'a change : le moteur ne le reprend pas au deploiement suivant. Il ne pose
|
||||||
|
# que le compte qui permet d'entrer la premiere fois.
|
||||||
|
- name: Poser le compte d'amorçage s'il n'existe pas
|
||||||
|
community.postgresql.postgresql_query:
|
||||||
|
login_host: "{{ serveur_icingaweb2_db_host }}"
|
||||||
|
login_user: "{{ serveur_icingaweb2_db_utilisateur }}"
|
||||||
|
login_password: "{{ serveur_icingaweb2_db_motdepasse }}"
|
||||||
|
login_db: "{{ serveur_icingaweb2_db_base }}"
|
||||||
|
ssl_mode: "{{ serveur_icingaweb2_db_sslmode | default('prefer', true) or 'prefer' }}"
|
||||||
|
ca_cert: "{{ serveur_icingaweb2_db_sslrootcert if serveur_icingaweb2_db_sslmode | default('') else omit }}"
|
||||||
|
query: >-
|
||||||
|
INSERT INTO icingaweb_user (name, active, password_hash)
|
||||||
|
VALUES (%s, 1, %s) ON CONFLICT (name) DO NOTHING
|
||||||
|
positional_args:
|
||||||
|
- "{{ serveur_icingaweb2_admin_utilisateur }}"
|
||||||
|
- "{{ serveur_icingaweb2_hachage.stdout }}"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- serveur_icingaweb2_auth == 'db'
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
|
||||||
- name: Ajouter www-data au groupe icingaweb2 (lecture de la config)
|
- name: Ajouter www-data au groupe icingaweb2 (lecture de la config)
|
||||||
ansible.builtin.user:
|
ansible.builtin.user:
|
||||||
name: www-data
|
name: www-data
|
||||||
|
|
|
||||||
|
|
@ -4079,8 +4079,17 @@ def _repli_hotes_du_site(enfants: dict, hv: dict) -> list[str]:
|
||||||
fautes.append(f"rechargement : {h} renouvelle son certificat sans recharger "
|
fautes.append(f"rechargement : {h} renouvelle son certificat sans recharger "
|
||||||
f"nginx — le nouveau reste sur disque, l'ancien est servi")
|
f"nginx — le nouveau reste sur disque, l'ancien est servi")
|
||||||
if membres("serveur_artefacts"):
|
if membres("serveur_artefacts"):
|
||||||
|
porteurs = set(membres("serveur_artefacts"))
|
||||||
for h, v in sorted(hv.items()):
|
for h, v in sorted(hv.items()):
|
||||||
if (v or {}).get("dns_amorcage") and not (v or {}).get("artefacts_amorcage"):
|
v = v or {}
|
||||||
|
if not v.get("dns_amorcage") or v.get("artefacts_amorcage"):
|
||||||
|
continue
|
||||||
|
# VIDE SUR LA MACHINE QUI PORTE LE CACHE, ET SEULEMENT LA (2026-10-13). Elle ne
|
||||||
|
# peut pas s'amorcer sur un cache qu'elle n'a pas encore : le socle RETIRE alors
|
||||||
|
# le fichier du gabarit (`artefacts_amorcage` defini et vide). Absent, ou vide
|
||||||
|
# ailleurs, c'est toujours le repli silencieux que cette garde existe pour voir.
|
||||||
|
if h in porteurs and "artefacts_amorcage" in v:
|
||||||
|
continue
|
||||||
fautes.append(f"jumeaux : {h} recoit `dns_amorcage` sans "
|
fautes.append(f"jumeaux : {h} recoit `dns_amorcage` sans "
|
||||||
f"`artefacts_amorcage` — le gabarit garde son ancien proxy apt")
|
f"`artefacts_amorcage` — le gabarit garde son ancien proxy apt")
|
||||||
return fautes
|
return fautes
|
||||||
|
|
|
||||||
|
|
@ -978,6 +978,16 @@ def inventaire() -> dict:
|
||||||
if _frontiere_metriques else [])}
|
if _frontiere_metriques else [])}
|
||||||
if (_fabric_metriques and "serveur_prometheus" in _groupes_de(nom)) else {}),
|
if (_fabric_metriques and "serveur_prometheus" in _groupes_de(nom)) else {}),
|
||||||
**communes,
|
**communes,
|
||||||
|
# LE CACHE NE S'AMORCE PAS SUR LUI-MEME (2026-10-13). Toute machine du site
|
||||||
|
# recevait `artefacts_amorcage` = le cache — y compris le cache, et des le
|
||||||
|
# socle, alors qu'il n'est deploye qu'a la couche `services`. Le premier `apt`
|
||||||
|
# d'un site construit depuis zero aurait echoue partout ; celui de Chezlepro
|
||||||
|
# n'a jamais ete reconstruit, son cache existait deja. VIDE ici, et non absent :
|
||||||
|
# le socle RETIRE alors le fichier du gabarit (qui vise `10.0.33.21`, une adresse
|
||||||
|
# d'avant le renumerotage) et la machine va droit chez Debian, ce que la
|
||||||
|
# frontiere permet au cache du site.
|
||||||
|
**({"artefacts_amorcage": ""}
|
||||||
|
if _cache_site and str(srv.get("ip")) == _cache_site else {}),
|
||||||
# En dernier : ce qu'une machine declare d'elle-meme prime sur ce que le
|
# En dernier : ce qu'une machine declare d'elle-meme prime sur ce que le
|
||||||
# plan declare pour toutes.
|
# plan declare pour toutes.
|
||||||
**(srv.get("variables") or {}),
|
**(srv.get("variables") or {}),
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ validee, et `make prouver` s'en assure avant.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -174,6 +175,23 @@ def parametres(nom: str) -> dict[str, str]:
|
||||||
# un autre stockage sans que tout le site bascule.
|
# un autre stockage sans que tout le site bascule.
|
||||||
p["SETOPS_STOCKAGE"] = str(m.get("stockage") or mat.get("stockage") or "")
|
p["SETOPS_STOCKAGE"] = str(m.get("stockage") or mat.get("stockage") or "")
|
||||||
p["SETOPS_CLONE_COMPLET"] = "true" if mat.get("clone_complet", True) else "false"
|
p["SETOPS_CLONE_COMPLET"] = "true" if mat.get("clone_complet", True) else "false"
|
||||||
|
# CE QUE LE PLAN DU SITE DECLARE DEJA, TRANSMIS AU CLONE (2026-10-13). `site-creer` ne
|
||||||
|
# passait ni cle, ni domaine, ni resolveur : la VM recevait ceux du GABARIT, ne au site de
|
||||||
|
# Chezlepro — sa cle (`royaume chezlepro`) et `searchdomain: chezlepro.ca`. Au site de
|
||||||
|
# TechnoLibre, dont le plan dit `cle_ssh: …technolibre`, les neuf VM seraient nees
|
||||||
|
# FERMEES a Ansible. La cle manque : refus, plutot qu'une VM qu'on ne peut plus joindre.
|
||||||
|
intr = U.lire_plan_site("10-intrants.yml") or {}
|
||||||
|
cle = str(intr.get("cle_ssh") or "").strip()
|
||||||
|
if cle:
|
||||||
|
pub = Path(os.path.expanduser(cle + ".pub"))
|
||||||
|
if not pub.is_file():
|
||||||
|
raise SystemExit(f"machine '{nom}': `cle_ssh: {cle}` au plan du site, mais "
|
||||||
|
f"{pub} est absente — la VM naitrait sans cle qu'Ansible possede")
|
||||||
|
p["SETOPS_CLE_PUBLIQUE"] = str(pub)
|
||||||
|
if str(intr.get("domaine_interne") or "").strip():
|
||||||
|
p["SETOPS_DOMAINE"] = str(intr["domaine_interne"]).strip()
|
||||||
|
if str(intr.get("dns_amorcage") or "").strip():
|
||||||
|
p["SETOPS_DNS"] = str(intr["dns_amorcage"]).strip()
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue