$ORIGIN {{ serveur_powerdns_zone }}. $TTL {{ serveur_powerdns_ttl }} @ IN SOA {{ serveur_powerdns_nameserver }}.{{ serveur_powerdns_zone }}. {{ serveur_powerdns_contact }}. ( {{ serveur_powerdns_serial }} {{ serveur_powerdns_soa_refresh }} {{ serveur_powerdns_soa_retry }} {{ serveur_powerdns_soa_expire }} {{ serveur_powerdns_soa_minimum }} ) @ IN NS {{ serveur_powerdns_nameserver }}.{{ serveur_powerdns_zone }}. {{ serveur_powerdns_nameserver }} IN A {{ hostvars[groups['serveur_powerdns'][0]].ansible_host | default('127.0.0.1') }} {# LE PLAN L'EMPORTE SUR LE DEFAUT DU ROLE (2026-08-25). `serveur_powerdns_records` porte un defaut de commodite : `dns IN CNAME ns1`. Le plan du site, lui, expose `dns.genese.internal` pour `serveur_resolveur` — ce qui produit un `dns IN A`. Un nom ne peut pas porter a la fois un CNAME et un A : `named-checkzone` a refuse la zone (« CNAME and other data »), et PowerDNS ne l'a pas servie du tout. La garde a bien fonctionne — elle a arrete une zone cassee avant qu'elle soit servie. Mais elle laissait le DNS du site sans zone tant que la collision durait. On ecarte donc le record statique quand le plan expose le meme nom. C'est le sens de marche du depot : ce qui est DECLARE AU PLAN prime sur ce qu'un role suppose par defaut. Les deux pointaient de toute facon vers la meme machine. #} {% set _exposes = [] %} {% if serveur_powerdns_publier_expositions | default(false) %} {% for expo in serveur_powerdns_expositions | default([]) if expo.domaine == serveur_powerdns_zone %} {% set _ = _exposes.append(expo.fqdn[:-(serveur_powerdns_zone | length + 1)]) %} {% endfor %} {% endif %} {% for record in serveur_powerdns_records if record.name not in _exposes %} {{ record.name }} IN {{ record.type }} {{ record.value }} {% endfor %} {% set emitted = [] %} {% for group_name in serveur_powerdns_inventory_groups %} {% for host_name in groups.get(group_name, []) | sort %} {% if hostvars[host_name].ansible_host is defined and host_name not in emitted %} {{ host_name }} IN A {{ hostvars[host_name].ansible_host }} {% set _ = emitted.append(host_name) %} {% endif %} {% endfor %} {% endfor %} {% if serveur_powerdns_publier_expositions | default(false) %} {% for expo in serveur_powerdns_expositions | default([]) if expo.domaine == serveur_powerdns_zone %} {% set cibles = [expo.hote] if expo.resout_vers | default('edge') == 'service' else groups.get(expo.edge, []) %} {% if cibles and cibles[0] in hostvars and hostvars[cibles[0]].ansible_host is defined %} {{ expo.fqdn[:-(serveur_powerdns_zone | length + 1)] }} IN A {{ hostvars[cibles[0]].ansible_host }} {% endif %} {% endfor %} {% endif %} {# L'EMPREINTE DU PLANCHER (2026-09-29). Le plancher `/etc/hosts` de chaque machine et cette zone derivent du meme plan, mais pas au meme moment : chacun suit le plan quand son role est rejoue. Le 2026-09-29, la console manquait aux planchers des deux locataires pendant que la zone la portait. Pour que chaque machine puisse comparer SANS transfert de zone (AXFR reste ferme), la zone publie ce que le plancher DOIT contenir pour ce domaine : le nombre de paires « nom adresse » et leur empreinte sha256. LE MEME ENSEMBLE QUE `hosts_statiques/templates/hosts.j2`, pas celui de cette zone : la flotte ACTIVE ET PLANIFIEE (le plancher resout un clone avant qu'il soit actif ; la zone non), plus les expositions de ce domaine. `test_empreinte_plancher.py` rend les deux gabarits sur un meme inventaire et exige la meme empreinte : si l'un change seul, le test le dit. La sonde `plancher` (role `client_sante`) calcule le cote machine. #} {% set _plancher = [] %} {% for h in (groups.get('hotes_actifs', []) + groups.get('hotes_planifies', [])) | unique | sort %} {% if hostvars[h].ansible_host is defined %} {% set _ = _plancher.append(h ~ '.' ~ serveur_powerdns_zone ~ ' ' ~ hostvars[h].ansible_host) %} {% endif %} {% endfor %} {% if serveur_powerdns_publier_expositions | default(false) %} {% for expo in serveur_powerdns_expositions | default([]) if expo.domaine == serveur_powerdns_zone %} {% set cibles = [expo.hote] if expo.resout_vers | default('edge') == 'service' else groups.get(expo.edge, []) %} {% if cibles and cibles[0] in hostvars and hostvars[cibles[0]].ansible_host is defined %} {% set _ = _plancher.append(expo.fqdn ~ ' ' ~ hostvars[cibles[0]].ansible_host) %} {% endif %} {% endfor %} {% endif %} {% set _paires = _plancher | unique | sort %} _plancher IN TXT "n={{ _paires | length }} sha256={{ _paires | join('\n') | hash('sha256') }}"