Set-OPS-Public/docs/audit/schema-plan.json
Daniel Allaire 2887b57f0c GUI : les six registres ont un formulaire genere, et la sauvegarde aussi
CHAMPS_ECRITS_A_LA_MAIN est vide. Serveurs et applications, les deux plus
gros, sont passes au generateur — chargement, rendu et sauvegarde.

L EPREUVE QUI COMPTE. Ouvrir chaque vue et enregistrer sans rien toucher
doit renvoyer exactement le plan qu on vient de lire : 14 serveurs, 25
applications, 2 domaines, 4 bases, IDENTIQUE partout. C est ce qui separe
un formulaire genere d un formulaire qui en a l air — un champ visible a
l ecran et perdu en silence a l enregistrement serait le pire des deux
mondes. test_rendu_gui.py le mesure a chaque make prouver.

TROIS DEFAUTS TROUVES EN CHEMIN.

Le formulaire annoncait des defauts INVENTES : 2048 Mo, 2 coeurs, 16G. Il
n existe aucun defaut fixe — deriver_ressources calcule depuis les roles
portes (1024 et 1 pour infra-pki-01, 5632 et 4 pour collab-01). Un repere
faux fait croire qu on connait la valeur. Le schema nomme le champ derive,
et l ecran montre la valeur reelle de cet hote. L option vide d un select
dit desormais ce qu elle produira : « (defaut : asgard) ».

Une SECONDE occurrence du defaut d hier dormait dans sourceDeValeurs :
elle lisait encore data.nomenclature. Elle n avait jamais leve parce que
la vue Serveurs, seule a emprunter cette source, avait un formulaire ecrit
a la main. Elle a leve a la seconde ou le generateur l a prise. Le banc ne
voit que les chemins vivants : verifier_gui.py fait donc aussi une
verification STATIQUE, qui voit ce qui dort.

La validation client s accrochait a data-v, pose a la main sur trois
champs. Le formulaire genere l aurait perdu et la validation serait passee
au vert sur ZERO champ. Le generateur marque chaque controle, et la
sauvegarde refuse si elle n en inspecte aucun.

DEUX CHAMPS GARDENT LEUR EDITEUR, et le schema le dit (x-editeur) : la
matrice des integrations montre les universelles et les exemptions, et
l editeur de liens contraint le role a meta/liens.yml. Le generateur s
efface plutot que de remplacer un editeur qui en sait plus que lui.

LIMITE : je n ai toujours pas ouvert ces pages dans un navigateur.

make prouver : CONFORME, 61 OK, 0 echec, 1 saute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-08 18:26:33 -04:00

441 lines
14 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Registres du plan Set-OPS",
"description": "GENERE par scripts/schema_plan.py (make schema). Ne pas editer a la main. Decrit la FORME des registres ; la COHERENCE reste aux validateurs de inventory_rules.py.",
"registres": {
"serveurs": {
"title": "Serveurs (VM)",
"x-fichier": "plan/serveurs.yml",
"x-racine": "serveurs",
"entite": {
"type": "object",
"properties": {
"fonction": {
"type": "string",
"title": "Fonction",
"description": "Determine VMID, IP, VLAN et passerelle. Doit exister dans la nomenclature.",
"x-source-valeurs": "nomenclature.fonctions"
},
"etat": {
"type": "string",
"title": "État",
"description": "`planifie` = decrit mais pas deploye ; `actif` = joignable par Ansible.",
"enum": [
"actif",
"planifie"
]
},
"integrations": {
"type": "array",
"title": "Intégrations facultatives",
"description": "Seulement les facultatives. Les universelles viennent du role et sont refusees ici.",
"items": {
"type": "string"
},
"x-editeur": "matrice"
},
"noeud": {
"type": "string",
"title": "Nœud Proxmox",
"description": "Surcharge le defaut de `make config`.",
"x-defaut-intrant": "proxmox_clone_noeud",
"x-source-valeurs": "intrants.proxmox_noeuds"
},
"stockage": {
"type": "string",
"title": "Stockage",
"description": "Surcharge le defaut de `make config`.",
"x-defaut-intrant": "proxmox_clone_stockage",
"x-source-valeurs": "intrants.proxmox_stockages"
},
"disque": {
"type": "string",
"title": "Disque",
"description": "Ex. `32G`. Vide = derive des empreintes des roles.",
"x-defaut-derive": "disque_taille"
},
"memoire": {
"type": "integer",
"title": "Mémoire (Mo)",
"description": "Vide = derive des empreintes des roles.",
"x-defaut-derive": "memoire"
},
"coeurs": {
"type": "integer",
"title": "Cœurs",
"description": "Vide = derive des empreintes des roles.",
"x-defaut-derive": "coeurs"
}
},
"additionalProperties": false,
"required": [
"fonction"
]
},
"x-clef": {
"title": "Nom d'hôte",
"description": "Le nom court de la VM. Le rang final derive l'adresse."
}
},
"applications": {
"title": "Applications",
"x-fichier": "plan/applications.yml",
"x-racine": "applications",
"entite": {
"type": "object",
"properties": {
"groupe": {
"type": "string",
"title": "Groupe (rôle)",
"description": "La capacite appliquee. Doit avoir un playbook homonyme.",
"x-source-valeurs": "groupes_operationnels"
},
"hote": {
"type": "string",
"title": "Hôte",
"description": "Une VM declaree au plan. Un hote inconnu est un « hote fantome » (P06).",
"x-source-valeurs": "serveurs"
},
"port": {
"type": "integer",
"title": "Port"
},
"expose": {
"type": "array",
"title": "Exposition publique",
"description": "FQDN publies par l'edge. Derivent le vhost, les SAN et le plancher /etc/hosts.",
"items": {
"type": "string"
}
},
"requiert": {
"type": "array",
"title": "Requiert",
"description": "Dependance applicative. Indicative : l'ordre de deploiement vient des couches.",
"items": {
"type": "string"
}
},
"liens": {
"type": "array",
"title": "Liens (bindings)",
"description": "Roles acceptes par le role porteur (meta/liens.yml).",
"items": {
"type": "object",
"properties": {
"vers": {
"type": "string",
"title": "Vers",
"description": "L'application liee.",
"x-source-valeurs": "applications"
},
"role": {
"type": "string",
"title": "Rôle",
"description": "Le role du lien, accepte par meta/liens.yml."
}
},
"additionalProperties": false,
"required": [
"role",
"vers"
]
},
"x-editeur": "liens"
},
"websocket": {
"type": "boolean",
"title": "WebSocket",
"description": "L'edge doit relayer la mise a niveau de connexion."
}
},
"additionalProperties": false,
"required": [
"groupe",
"hote"
]
},
"x-clef": {
"title": "Identifiant",
"description": "Nom de l'application dans le plan."
}
},
"bases_donnees": {
"title": "Bases de donnees",
"x-fichier": "plan/bases-donnees.yml",
"x-racine": "bases_donnees",
"entite": {
"type": "object",
"properties": {
"serveur": {
"type": "string",
"title": "Serveur de BD",
"x-source-valeurs": "serveurs_bd"
},
"base": {
"type": "string",
"title": "Base"
},
"proprietaire": {
"type": "string",
"title": "Propriétaire"
},
"secret": {
"type": "string",
"title": "Secret (Vault)",
"description": "NOM d'une variable Vault, jamais une valeur. Le secret ne quitte pas le role."
},
"consommateur": {
"type": "string",
"title": "Consommateur",
"description": "Qui utilise cette base. La liste depend de la portee.",
"x-source-selon": {
"champ": "portee",
"cas": {
"hote": "serveurs",
"application": "applications"
},
"defaut": "groupes_operationnels"
}
},
"portee": {
"type": "string",
"title": "Portée",
"description": "Comment le consommateur est designe : par application, par groupe ou par hote.",
"enum": [
"application",
"groupe",
"hote"
],
"default": "groupe"
},
"usage": {
"type": "string",
"title": "Usage",
"default": "principale"
}
},
"additionalProperties": false,
"required": [
"base",
"consommateur",
"proprietaire",
"secret",
"serveur"
]
},
"x-clef": {
"title": "Identifiant",
"description": "Nom de l'entree au registre, ex. `bd_forgejo`."
}
},
"serveurs_bd": {
"title": "Serveurs de bases de donnees",
"x-fichier": "plan/bases-donnees.yml",
"x-racine": "serveurs_bd",
"entite": {
"type": "object",
"properties": {
"type": {
"type": "string",
"title": "Moteur"
},
"hote": {
"type": "string",
"title": "Hôte",
"x-source-valeurs": "serveurs"
},
"port": {
"type": "integer",
"title": "Port"
},
"groupe": {
"type": "string",
"title": "Groupe",
"x-source-valeurs": "groupes_operationnels"
}
},
"additionalProperties": false,
"required": [
"hote",
"type"
]
},
"x-clef": {
"title": "Nom",
"description": "Nom du serveur de bases, ex. `pg-principal`."
}
},
"domaines_publics": {
"title": "Domaines publics",
"x-fichier": "plan/domaines.yml",
"x-racine": "domaines_publics",
"entite": {
"type": "object",
"properties": {
"autorite": {
"type": "string",
"title": "Autorité DNS",
"enum": [
"auto-heberge",
"delegue",
"primaire-cache"
]
},
"edge": {
"type": "string",
"title": "Edge",
"description": "Le GROUPE Ansible qui sert cette zone (ex. serveur_nginx).",
"x-source-valeurs": "groupes_edge"
},
"secondaires": {
"type": "array",
"title": "Secondaires",
"description": "Serveurs DNS secondaires de la zone.",
"items": {
"type": "string"
}
},
"dnssec": {
"type": "boolean",
"title": "DNSSEC"
},
"exposition": {
"type": "array",
"title": "Expositions declarees",
"description": "FQDN publies pour cette zone, vers un groupe cible.",
"items": {
"type": "object",
"properties": {
"nom": {
"type": "string",
"title": "Nom",
"description": "Sous-domaine, ou `@` pour la zone elle-meme."
},
"cible": {
"type": "string",
"title": "Cible",
"description": "Le groupe interne qui sert ce nom.",
"x-source-valeurs": "groupes_operationnels"
},
"type": {
"type": "string",
"title": "Type",
"default": "web"
}
},
"additionalProperties": false,
"required": [
"cible",
"nom"
]
}
}
},
"additionalProperties": false,
"required": [
"autorite",
"edge"
]
},
"x-clef": {
"title": "Domaine",
"description": "Le nom public, ex. `chezlepro.ca`."
}
},
"nomenclature": {
"title": "Nomenclature",
"x-fichier": "plan/nomenclature.yml",
"x-racine": null,
"entite": {
"type": "object",
"properties": {
"index": {
"type": "integer",
"title": "Index (seed)",
"description": "LE seul champ d'adressage. Tout en derive ; P20 refuse d'en stocker un autre.",
"minimum": 0,
"maximum": 255
},
"cidr_hote": {
"type": "integer",
"title": "CIDR d'hôte",
"description": "Masque des sous-reseaux de zone. 24 = 254 hotes par zone."
},
"categories": {
"type": "object",
"title": "Catégories (zones)",
"description": "Une zone de securite par cle. Le numero derive le 3e octet et le VLAN.",
"additionalProperties": {
"type": "object",
"properties": {
"libelle": {
"type": "string",
"title": "Libellé",
"description": "Nom lisible de la zone (Frontiere, Identite...)."
}
},
"additionalProperties": false,
"required": [
"libelle"
]
}
},
"fonctions": {
"type": "object",
"title": "Fonctions",
"description": "categorie + service par fonction. VMID et IP en derivent.",
"additionalProperties": {
"type": "object",
"properties": {
"categorie": {
"type": "integer",
"title": "Catégorie",
"description": "La zone. Fixe le 3e octet (15 + categorie) et le VLAN.",
"x-source-valeurs": "nomenclature.categories"
},
"service": {
"type": "integer",
"title": "Service",
"description": "Fixe le bloc d'adresses de l'hote dans la zone."
}
},
"additionalProperties": false,
"required": [
"categorie",
"service"
]
}
},
"reservations": {
"type": "object",
"title": "Réservations",
"description": "Adresses soustraites a la derivation dans la zone.",
"properties": {
"passerelle": {
"type": "integer",
"title": "Passerelle",
"description": "Dernier octet de la passerelle. P23 refuse un SVI qui s'en ecarte."
},
"reserve_min": {
"type": "integer",
"title": "Réserve (min)",
"description": "Premier octet soustrait a la derivation."
},
"reserve_max": {
"type": "integer",
"title": "Réserve (max)",
"description": "Dernier octet soustrait a la derivation."
}
},
"additionalProperties": false
}
},
"additionalProperties": false,
"required": [
"index"
]
}
}
}
}