From a85255dc35ccd757e2f935f7b629fb73534e97ba Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Mon, 3 Aug 2026 08:52:22 -0400 Subject: [PATCH] =?UTF-8?q?GUI=20:=20le=20panneau=20pr=C3=A9sente=20les=20?= =?UTF-8?q?deux=20devis,=20et=20son=20aide=20n'est=20plus=20p=C3=A9rim?= =?UTF-8?q?=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La vue Réseau n'affichait que le devis des commutateurs. Le devis frontière était totalement absent de l'interface, alors qu'il porte les règles de la bordure et ses avertissements — dont celui sur « Block private networks », invisible dans les règles elles-mêmes. Ajouté `/api/devis-opnsense` et son bloc d'affichage avec bouton de copie. Vérifié par HTTP que les deux points d'API servent exactement ce que le CLI produit : 181 et 125 lignes, identiques au caractère près. Import paresseux et gardé — ce module lit l'underlay et les inventaires de tous les tenants, une erreur aurait sinon vidé la vue entière. Aide corrigée sur trois points périmés : les VLAN tenants annoncés « uniques sur le trunk » (faux en SDN, aucun n'y circule) ; le dialecte renvoyé à une variable d'environnement alors que c'est un intrant de la section Fabric ; et la route par défaut dite « à adapter » alors que la section 5 l'émet. Elle dit maintenant ce qui reste à nommer à la main : les ports physiques et, en SDN, le nœud de sortie EVPN. Preuves : 24 OK, 0 échec. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 23 +++++++++++++++++++++++ scripts/inventory_gui.py | 34 +++++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9687d2c..d0df2d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # CHANGELOG — Set-OPS +## 2026-08-03 (suite 2) — le panneau présente les deux devis + +La vue *Réseau* n'affichait que le devis des commutateurs : **le devis frontière était +totalement absent de l'interface**, alors qu'il porte les règles de la bordure et ses +avertissements — dont celui sur « Block private networks », invisible dans les règles +elles-mêmes. + +Ajouté : `/api/devis-opnsense` et son bloc d'affichage, avec bouton de copie. Vérifié **par +HTTP** que les deux points d'API servent exactement ce que le CLI produit — 181 et 125 lignes, +identiques au caractère près. + +L'import est paresseux et gardé : ce module lit l'underlay et les inventaires de tous les +tenants, et une erreur y aurait sinon empêché l'affichage du reste de la vue. + +### Corrigé — le texte d'aide était périmé sur trois points +Il annonçait les VLAN tenants « uniques **sur le trunk** » — faux en SDN, où aucun ne circule ; +renvoyait le dialecte à `SETOPS_DIALECTE` alors que c'est un **intrant** de la section +*Fabric* ; et disait que « la route par défaut vers OPNsense reste à adapter » alors que la +section 5 l'émet depuis hier. + +Il dit maintenant ce qui reste réellement à nommer à la main : les **ports physiques** et, en +SDN, le **nœud de sortie EVPN**. Rien d'autre — adresses, VLAN et routes se dérivent. + ## 2026-08-03 (suite) — le devis frontière rattrape la bascule SDN Trois affirmations du devis frontière étaient devenues fausses, dont une qui cassait le diff --git a/scripts/inventory_gui.py b/scripts/inventory_gui.py index 04457d4..7564465 100644 --- a/scripts/inventory_gui.py +++ b/scripts/inventory_gui.py @@ -1403,8 +1403,9 @@ HTML = r"""
Lecture seule. Éditer les meta/flux.yml des rôles, puis make flux.
`; } else if (vuePrincipale === 'reseau') { d.innerHTML = `
Réseau & fédération
-
Chaque instance fédérée dérive son adressage de son seed index (VLAN = 1000 + index×10 + zone, unique sur le trunk). La vue liste la flotte, signale toute collision d'index, et génère le devis des switches.
-
Lecture seule, régénéré du plan — make instances / make devis-reseau. Masques d'ACL selon le dialecte (SETOPS_DIALECTE=cisco|binardat ou make devis-reseau DIALECTE=binardat). Le reste (HSRP, route par défaut vers OPNsense, port de trunk) reste à adapter à ta plateforme.
`; +
Chaque instance fédérée dérive son adressage de son seed index (VLAN/VNI = 1000 + index×10 + zone, unique dans la fédération). La vue liste la flotte, signale toute collision d'index, et génère les deux devis : commutateurs et frontière nord/sud.
+
Lecture seule, régénérés du plan — make devis-reseau / make devis-opnsense. Le dialecte de CLI et le mode de routage (commutateur ou SDN EVPN) se règlent dans la section Fabric du panneau « Intrants de base ». En mode SDN, les commutateurs ne portent ni VLAN tenant, ni SVI, ni ACL : le routage et le filtrage inter-zone vivent sur les hyperviseurs.
+
Ce qui reste à nommer à la main : les ports physiques (<PORT-VERS-…>) et, en SDN, le nœud de sortie EVPN. Rien d'autre — adresses, VLAN et routes se dérivent.
`; } else { d.innerHTML = `
Reconstruction
L'ordre part du socle et remonte : chaque couche suppose la précédente debout. Le tri intra-couche vient du graphe des dépendances.
@@ -1430,6 +1431,7 @@ HTML = r""" } let devisReseauTexte = ''; + let devisFrontiereTexte = ''; function tableauFlotte(inst, coll, fmodeles, findexPris) { if (!inst.length) return '
Aucune instance découverte (dépôts frères avec plan/nomenclature.yml).
'; const banniere = Object.keys(coll || {}).length @@ -1498,11 +1500,12 @@ HTML = r""" const cible = document.getElementById('grilles'); cible.innerHTML = '
Lecture de la fédération…
'; try { - const [ri, rd] = await Promise.all([ + const [ri, rd, rf] = await Promise.all([ fetch('/api/instances', {headers: {'X-Jeton': JETON}}), fetch('/api/devis-reseau', {headers: {'X-Jeton': JETON}}), + fetch('/api/devis-opnsense', {headers: {'X-Jeton': JETON}}), ]); - const di = await ri.json(), data = await rd.json(); + const di = await ri.json(), data = await rd.json(), dfr = await rf.json(); if (!rd.ok) { cible.innerHTML = '
' + echapper(data.erreur || 'Indisponible.') + '
'; return; } devisReseauTexte = data.config || ''; const flotte = `
Flotte d'instances ${(di.instances || []).length}
${tableauFlotte(di.instances || [], di.collisions || {}, di.modeles || [], di.index_pris || [])}
`; @@ -1511,7 +1514,13 @@ HTML = r"""
${echapper(devisReseauTexte)}
` : '
Aucune instance fédérée : pas de devis convergé à générer.
'; - cible.innerHTML = flotte + devis; + devisFrontiereTexte = dfr.config || ''; + const frontiere = devisFrontiereTexte + ? `
Devis config réseau — frontière nord/sud (OPNsense) +
+
${echapper(devisFrontiereTexte)}
` + : `
Devis frontière indisponible${dfr.erreur ? ' : ' + echapper(dfr.erreur) : ''}.
`; + cible.innerHTML = flotte + devis + frontiere; } catch (e) { cible.innerHTML = '
Erreur : ' + echapper(e.message) + '
'; } @@ -1520,6 +1529,10 @@ HTML = r""" if (navigator.clipboard) navigator.clipboard.writeText(devisReseauTexte) .then(() => message('Devis réseau copié — prêt à coller sur le switch.', 'ok'), () => {}); } + function copierDevisFrontiere() { + if (navigator.clipboard) navigator.clipboard.writeText(devisFrontiereTexte) + .then(() => message('Devis frontière copié.', 'ok'), () => {}); + } function dessinerCouches() { const cible = document.getElementById('grilles'); @@ -2740,6 +2753,17 @@ class Gestionnaire(BaseHTTPRequestHandler): "config": devis_reseau.generer(tenants) if tenants else "", "tenants": [{"nom": nom, "index": n["index"]} for nom, _, n in tenants], }) + elif chemin == "/api/devis-opnsense": + # Import paresseux : ce module lit l'underlay et les inventaires de tous + # les tenants ; une erreur ici ne doit pas empecher le reste du panneau. + try: + import devis_opnsense as _dop + tenants = _dop.decouvrir() + self.repondre_json(200, { + "config": _dop.rendre(_dop.construire(tenants)) if tenants else "", + }) + except Exception as exc: + self.repondre_json(200, {"config": "", "erreur": str(exc)}) elif chemin == "/api/instances": import instances as _inst import instance_creer as _ic