feat(snapshots): support format SLPM (.exe Sanef Patch Manager) + colonne Origine
Probleme: tes 51 snapshots etaient au format SLPM_<auteur>_YYYYMMDD_HHMM (cree par le .exe)
non reconnu par PatchCenter qui n'attendait que le format <auteur>_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
This commit is contained in:
parent
0a3fde36b7
commit
d8d803fb48
@ -25,10 +25,27 @@ except ImportError:
|
|||||||
log.warning("pyvmomi non disponible — listing snapshots impossible")
|
log.warning("pyvmomi non disponible — listing snapshots impossible")
|
||||||
|
|
||||||
|
|
||||||
# Format strict PatchCenter : `<auteur>_YYYY-MM-DD_avant_patch`
|
# Formats gérés par les outils SecOps SANEF :
|
||||||
SNAP_NAME_RE = re.compile(
|
# PatchCenter (web) : `<auteur>_YYYY-MM-DD_avant_patch`
|
||||||
|
# Sanef Patch Manager : `SLPM_<auteur>_YYYYMMDD_HHMM`
|
||||||
|
SNAP_PATCHCENTER_RE = re.compile(
|
||||||
r"^(?P<author>[A-Za-z0-9_\-\.]+)_(?P<date>\d{4}-\d{2}-\d{2})_avant_patch$"
|
r"^(?P<author>[A-Za-z0-9_\-\.]+)_(?P<date>\d{4}-\d{2}-\d{2})_avant_patch$"
|
||||||
)
|
)
|
||||||
|
SNAP_SLPM_RE = re.compile(
|
||||||
|
r"^SLPM_(?P<author>[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):
|
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)
|
created_iso = str(created)
|
||||||
age_days = None
|
age_days = None
|
||||||
|
|
||||||
# Auteur déduit du préfixe du nom (format strict PatchCenter)
|
# Détection du format géré (PatchCenter ou .exe SLPM)
|
||||||
# `<auteur>_YYYY-MM-DD_avant_patch`
|
origin, author = _detect_snap_origin(name)
|
||||||
author = None
|
is_managed = origin is not None
|
||||||
is_pc_format = False
|
|
||||||
m = SNAP_NAME_RE.match(name)
|
|
||||||
if m:
|
|
||||||
author = m.group("author")
|
|
||||||
is_pc_format = True
|
|
||||||
|
|
||||||
yield {
|
yield {
|
||||||
"vcenter_id": vcenter_id,
|
"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,
|
"created_at": created_iso,
|
||||||
"age_days": round(age_days, 2) if age_days is not None else None,
|
"age_days": round(age_days, 2) if age_days is not None else None,
|
||||||
"author": author,
|
"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
|
"is_current": bool(getattr(s, "id", None) and vm.snapshot and vm.snapshot.currentSnapshot
|
||||||
and vm.snapshot.currentSnapshot._moId == snap_moid),
|
and vm.snapshot.currentSnapshot._moId == snap_moid),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
<label class="text-xs text-gray-500">Auteur (préfixe nom)</label>
|
<label class="text-xs text-gray-500">Auteur (préfixe nom)</label>
|
||||||
<input type="text" id="f-author" value="{{ intervenant_default }}" placeholder="ex: khalid" class="w-full">
|
<input type="text" id="f-author" value="{{ intervenant_default }}" placeholder="ex: khalid" class="w-full">
|
||||||
<label class="text-xs text-gray-400 mt-1"><input type="checkbox" id="f-only-mine" checked> Mes snapshots uniquement</label>
|
<label class="text-xs text-gray-400 mt-1"><input type="checkbox" id="f-only-mine" checked> Mes snapshots uniquement</label>
|
||||||
<label class="text-xs text-gray-400 mt-1"><input type="checkbox" id="f-pc-format" checked> Format PatchCenter uniquement (<code><auteur>_YYYY-MM-DD_avant_patch</code>)</label>
|
<label class="text-xs text-gray-400 mt-1"><input type="checkbox" id="f-pc-format" checked> Formats gérés uniquement (PatchCenter <code><auteur>_YYYY-MM-DD_avant_patch</code> ou .exe <code>SLPM_<auteur>_YYYYMMDD_HHMM</code>)</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-3">
|
<div class="col-span-3">
|
||||||
<label class="text-xs text-gray-500">Âge minimum (jours)</label>
|
<label class="text-xs text-gray-500">Âge minimum (jours)</label>
|
||||||
@ -97,6 +97,7 @@
|
|||||||
<th class="p-2 text-left">vCenter</th>
|
<th class="p-2 text-left">vCenter</th>
|
||||||
<th class="p-2 text-left">VM</th>
|
<th class="p-2 text-left">VM</th>
|
||||||
<th class="p-2 text-left">Snapshot</th>
|
<th class="p-2 text-left">Snapshot</th>
|
||||||
|
<th class="p-2 text-left">Origine</th>
|
||||||
<th class="p-2 text-left">Auteur</th>
|
<th class="p-2 text-left">Auteur</th>
|
||||||
<th class="p-2 text-left">Créé le</th>
|
<th class="p-2 text-left">Créé le</th>
|
||||||
<th class="p-2 text-left">Âge</th>
|
<th class="p-2 text-left">Âge</th>
|
||||||
@ -141,8 +142,8 @@
|
|||||||
const pcOnly = fPcFormat.checked;
|
const pcOnly = fPcFormat.checked;
|
||||||
const minAge = parseFloat(fMinAge.value) || 0;
|
const minAge = parseFloat(fMinAge.value) || 0;
|
||||||
return allSnaps.filter(s => {
|
return allSnaps.filter(s => {
|
||||||
// Format PatchCenter uniquement
|
// Format géré (PatchCenter OU SLPM .exe)
|
||||||
if (pcOnly && !s.is_patchcenter_format) return false;
|
if (pcOnly && !s.is_managed_format) return false;
|
||||||
// Mes snapshots uniquement (auteur doit etre present et matcher)
|
// Mes snapshots uniquement (auteur doit etre present et matcher)
|
||||||
if (onlyMine) {
|
if (onlyMine) {
|
||||||
if (!author) {
|
if (!author) {
|
||||||
@ -186,8 +187,8 @@
|
|||||||
if (!rows.length) {
|
if (!rows.length) {
|
||||||
const total = allSnaps.length;
|
const total = allSnaps.length;
|
||||||
tbody.innerHTML = total === 0
|
tbody.innerHTML = total === 0
|
||||||
? '<tr><td colspan="8" class="p-4 text-center text-gray-500">Aucun snapshot dans le scan. Vérifie credentials vCenter.</td></tr>'
|
? '<tr><td colspan="9" class="p-4 text-center text-gray-500">Aucun snapshot dans le scan. Vérifie credentials vCenter.</td></tr>'
|
||||||
: `<tr><td colspan="8" class="p-4 text-center text-cyber-yellow">${total} snapshot(s) trouvé(s) mais tous filtrés. Ajuste les filtres ci-dessus.</td></tr>`;
|
: `<tr><td colspan="9" class="p-4 text-center text-cyber-yellow">${total} snapshot(s) trouvé(s) mais tous filtrés. Ajuste les filtres ci-dessus.</td></tr>`;
|
||||||
updateDeleteBtn();
|
updateDeleteBtn();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -195,12 +196,17 @@
|
|||||||
const authorCell = s.author
|
const authorCell = s.author
|
||||||
? escapeHTML(s.author)
|
? escapeHTML(s.author)
|
||||||
: '<i class="text-gray-500">inconnu</i>'; // bypass escape pour ce litteral HTML
|
: '<i class="text-gray-500">inconnu</i>'; // bypass escape pour ce litteral HTML
|
||||||
|
let originBadge;
|
||||||
|
if (s.origin === 'patchcenter') originBadge = '<span class="badge badge-blue">PatchCenter</span>';
|
||||||
|
else if (s.origin === 'slpm') originBadge = '<span class="badge badge-gray">SLPM (.exe)</span>';
|
||||||
|
else originBadge = '<span class="badge badge-orange">manuel</span>';
|
||||||
return `
|
return `
|
||||||
<tr class="ss-row border-b border-cyber-border/30" data-idx="${allSnaps.indexOf(s)}">
|
<tr class="ss-row border-b border-cyber-border/30" data-idx="${allSnaps.indexOf(s)}">
|
||||||
<td class="p-2"><input type="checkbox" class="row-cb"></td>
|
<td class="p-2"><input type="checkbox" class="row-cb"></td>
|
||||||
<td class="p-2 font-mono">${escapeHTML(s.vcenter_name)}</td>
|
<td class="p-2 font-mono">${escapeHTML(s.vcenter_name)}</td>
|
||||||
<td class="p-2 font-mono">${escapeHTML(s.vm_name)}</td>
|
<td class="p-2 font-mono">${escapeHTML(s.vm_name)}</td>
|
||||||
<td class="p-2 font-mono">${escapeHTML(s.snap_name)}</td>
|
<td class="p-2 font-mono">${escapeHTML(s.snap_name)}</td>
|
||||||
|
<td class="p-2">${originBadge}</td>
|
||||||
<td class="p-2">${authorCell}</td>
|
<td class="p-2">${authorCell}</td>
|
||||||
<td class="p-2 font-mono text-[10px]">${escapeHTML(fmtDateFR(s.created_at))}</td>
|
<td class="p-2 font-mono text-[10px]">${escapeHTML(fmtDateFR(s.created_at))}</td>
|
||||||
<td class="p-2">${ageBadge(s.age_days)}</td>
|
<td class="p-2">${ageBadge(s.age_days)}</td>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user