From d8d803fb489cb1a006e4fee4991a1472d95b4480 Mon Sep 17 00:00:00 2001 From: Admin MPCZ Date: Thu, 7 May 2026 20:47:32 +0200 Subject: [PATCH] feat(snapshots): support format SLPM (.exe Sanef Patch Manager) + colonne Origine Probleme: tes 51 snapshots etaient au format SLPM__YYYYMMDD_HHMM (cree par le .exe) non reconnu par PatchCenter qui n'attendait que le format _YYYY-MM-DD_avant_patch. - Service: nouveau regex SNAP_SLPM_RE + helper _detect_snap_origin retourne (origin, author) - Champs ajoutes au snapshot: origin ('patchcenter'|'slpm'|None), is_managed_format - Template: * Filtre 'Format gere uniquement' (renomme depuis 'PatchCenter uniquement') * Colonne 'Origine' avec badge: PatchCenter (bleu) / SLPM .exe (gris) / manuel (orange) * Colonne ajoutee dans header + cellules + colspan ajuste a 9 --- app/services/snapshot_mgmt_service.py | 36 +++++++++++++++++++-------- app/templates/snapshots.html | 16 ++++++++---- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/app/services/snapshot_mgmt_service.py b/app/services/snapshot_mgmt_service.py index 55c37fb..e51ecde 100644 --- a/app/services/snapshot_mgmt_service.py +++ b/app/services/snapshot_mgmt_service.py @@ -25,10 +25,27 @@ except ImportError: log.warning("pyvmomi non disponible — listing snapshots impossible") -# Format strict PatchCenter : `_YYYY-MM-DD_avant_patch` -SNAP_NAME_RE = re.compile( +# Formats gérés par les outils SecOps SANEF : +# PatchCenter (web) : `_YYYY-MM-DD_avant_patch` +# Sanef Patch Manager : `SLPM__YYYYMMDD_HHMM` +SNAP_PATCHCENTER_RE = re.compile( r"^(?P[A-Za-z0-9_\-\.]+)_(?P\d{4}-\d{2}-\d{2})_avant_patch$" ) +SNAP_SLPM_RE = re.compile( + r"^SLPM_(?P[A-Za-z0-9_\-\.]+)_\d{8}_\d{4}$" +) + + +def _detect_snap_origin(name: str): + """Renvoie (origin, author) ou (None, None) si format inconnu. + origin in {'patchcenter', 'slpm'} ; author = préfixe utilisateur.""" + m = SNAP_PATCHCENTER_RE.match(name or "") + if m: + return "patchcenter", m.group("author") + m = SNAP_SLPM_RE.match(name or "") + if m: + return "slpm", m.group("author") + return None, None def _get_vcenter_creds(db): @@ -72,14 +89,9 @@ def _walk_snapshots(snapshot_list, vm, vcenter_name, vcenter_id, vm_moid, parent created_iso = str(created) age_days = None - # Auteur déduit du préfixe du nom (format strict PatchCenter) - # `_YYYY-MM-DD_avant_patch` - author = None - is_pc_format = False - m = SNAP_NAME_RE.match(name) - if m: - author = m.group("author") - is_pc_format = True + # Détection du format géré (PatchCenter ou .exe SLPM) + origin, author = _detect_snap_origin(name) + is_managed = origin is not None yield { "vcenter_id": vcenter_id, @@ -93,7 +105,9 @@ def _walk_snapshots(snapshot_list, vm, vcenter_name, vcenter_id, vm_moid, parent "created_at": created_iso, "age_days": round(age_days, 2) if age_days is not None else None, "author": author, - "is_patchcenter_format": is_pc_format, + "origin": origin, # 'patchcenter' | 'slpm' | None + "is_managed_format": is_managed, # any des 2 formats SecOps + "is_patchcenter_format": origin == "patchcenter", "is_current": bool(getattr(s, "id", None) and vm.snapshot and vm.snapshot.currentSnapshot and vm.snapshot.currentSnapshot._moId == snap_moid), } diff --git a/app/templates/snapshots.html b/app/templates/snapshots.html index 830c920..69f7556 100644 --- a/app/templates/snapshots.html +++ b/app/templates/snapshots.html @@ -59,7 +59,7 @@ - +
@@ -97,6 +97,7 @@ vCenter VM Snapshot + Origine Auteur Créé le Âge @@ -141,8 +142,8 @@ const pcOnly = fPcFormat.checked; const minAge = parseFloat(fMinAge.value) || 0; return allSnaps.filter(s => { - // Format PatchCenter uniquement - if (pcOnly && !s.is_patchcenter_format) return false; + // Format géré (PatchCenter OU SLPM .exe) + if (pcOnly && !s.is_managed_format) return false; // Mes snapshots uniquement (auteur doit etre present et matcher) if (onlyMine) { if (!author) { @@ -186,8 +187,8 @@ if (!rows.length) { const total = allSnaps.length; tbody.innerHTML = total === 0 - ? 'Aucun snapshot dans le scan. Vérifie credentials vCenter.' - : `${total} snapshot(s) trouvé(s) mais tous filtrés. Ajuste les filtres ci-dessus.`; + ? 'Aucun snapshot dans le scan. Vérifie credentials vCenter.' + : `${total} snapshot(s) trouvé(s) mais tous filtrés. Ajuste les filtres ci-dessus.`; updateDeleteBtn(); return; } @@ -195,12 +196,17 @@ const authorCell = s.author ? escapeHTML(s.author) : 'inconnu'; // bypass escape pour ce litteral HTML + let originBadge; + if (s.origin === 'patchcenter') originBadge = 'PatchCenter'; + else if (s.origin === 'slpm') originBadge = 'SLPM (.exe)'; + else originBadge = 'manuel'; return ` ${escapeHTML(s.vcenter_name)} ${escapeHTML(s.vm_name)} ${escapeHTML(s.snap_name)} + ${originBadge} ${authorCell} ${escapeHTML(fmtDateFR(s.created_at))} ${ageBadge(s.age_days)}