feat(patching/iexec): check espace disque (/ >= 1.5Go, /var/log >= 1Go) + fix detection subscription-manager identity FR/EN via UUID regex
This commit is contained in:
parent
11bbda5a27
commit
b07a6816d4
@ -19,6 +19,7 @@ calcule un verdict global.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
from typing import Callable, Dict, List, Any
|
from typing import Callable, Dict, List, Any
|
||||||
@ -30,6 +31,10 @@ log = logging.getLogger("patchcenter.prepatch")
|
|||||||
# Timeout par commande SSH
|
# Timeout par commande SSH
|
||||||
EXEC_TIMEOUT = 15
|
EXEC_TIMEOUT = 15
|
||||||
|
|
||||||
|
# Seuils espace disque (en Mo)
|
||||||
|
DISK_MIN_ROOT_MB = 1500 # 1.5 Go sur /
|
||||||
|
DISK_MIN_VARLOG_MB = 1000 # 1 Go sur /var/log
|
||||||
|
|
||||||
# Satellites SANEF par zone réseau
|
# Satellites SANEF par zone réseau
|
||||||
SATELLITE_LAN = "vpdsiasat2.sanef.groupe"
|
SATELLITE_LAN = "vpdsiasat2.sanef.groupe"
|
||||||
SATELLITE_DMZ = "vpdsiasat1.sanef.groupe"
|
SATELLITE_DMZ = "vpdsiasat1.sanef.groupe"
|
||||||
@ -127,6 +132,65 @@ def check_ssh(ctx: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _disk_avail_mb(client, path: str):
|
||||||
|
"""Renvoie l'espace dispo en Mo sur le FS contenant `path`, ou None si KO."""
|
||||||
|
r = _exec(client, f"sudo -n df -BM --output=avail {path} 2>&1 | tail -n +2")
|
||||||
|
out = (r["stdout"] or "").strip()
|
||||||
|
m = re.search(r"(\d+)\s*M", out)
|
||||||
|
if m:
|
||||||
|
return int(m.group(1))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_disk(ctx: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Vérifie l'espace disque dispo :
|
||||||
|
- / >= 1.5 Go
|
||||||
|
- /var/log >= 1 Go
|
||||||
|
KO si insuffisant → pas éligible au snapshot.
|
||||||
|
"""
|
||||||
|
client = ctx.get("client")
|
||||||
|
if client is None:
|
||||||
|
return {
|
||||||
|
"name": "disk",
|
||||||
|
"label": f"Espace disque (/ ≥ {DISK_MIN_ROOT_MB}M, /var/log ≥ {DISK_MIN_VARLOG_MB}M)",
|
||||||
|
"status": "ko",
|
||||||
|
"message": "SSH KO en amont",
|
||||||
|
"details": "",
|
||||||
|
}
|
||||||
|
root_mb = _disk_avail_mb(client, "/")
|
||||||
|
var_mb = _disk_avail_mb(client, "/var/log")
|
||||||
|
issues = []
|
||||||
|
parts = []
|
||||||
|
if root_mb is None:
|
||||||
|
issues.append("/ : mesure impossible")
|
||||||
|
else:
|
||||||
|
parts.append(f"/ {root_mb}M")
|
||||||
|
if root_mb < DISK_MIN_ROOT_MB:
|
||||||
|
issues.append(f"/ {root_mb}M < min {DISK_MIN_ROOT_MB}M")
|
||||||
|
if var_mb is None:
|
||||||
|
issues.append("/var/log : mesure impossible")
|
||||||
|
else:
|
||||||
|
parts.append(f"/var/log {var_mb}M")
|
||||||
|
if var_mb < DISK_MIN_VARLOG_MB:
|
||||||
|
issues.append(f"/var/log {var_mb}M < min {DISK_MIN_VARLOG_MB}M")
|
||||||
|
label = f"Espace disque (/ ≥ {DISK_MIN_ROOT_MB}M, /var/log ≥ {DISK_MIN_VARLOG_MB}M)"
|
||||||
|
details = (
|
||||||
|
f"$ sudo df -BM --output=avail / → {root_mb if root_mb is not None else 'N/A'}M\n"
|
||||||
|
f"$ sudo df -BM --output=avail /var/log → {var_mb if var_mb is not None else 'N/A'}M"
|
||||||
|
)
|
||||||
|
if issues:
|
||||||
|
return {
|
||||||
|
"name": "disk", "label": label, "status": "ko",
|
||||||
|
"message": " · ".join(issues),
|
||||||
|
"details": details,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"name": "disk", "label": label, "status": "ok",
|
||||||
|
"message": " · ".join(parts) + " (au-dessus seuils)",
|
||||||
|
"details": details,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def check_satellite(ctx: Dict[str, Any]) -> Dict[str, Any]:
|
def check_satellite(ctx: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Vérifie :
|
"""Vérifie :
|
||||||
1. la joignabilité du Satellite cible (LAN ou DMZ selon Domaine)
|
1. la joignabilité du Satellite cible (LAN ou DMZ selon Domaine)
|
||||||
@ -154,8 +218,11 @@ def check_satellite(ctx: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
sat_reachable = http_code in ("200", "301", "302", "403")
|
sat_reachable = http_code in ("200", "301", "302", "403")
|
||||||
|
|
||||||
# 2) subscription-manager identity
|
# 2) subscription-manager identity
|
||||||
|
# Locale-indépendant : on cherche un UUID dans la sortie (présent en EN comme en FR).
|
||||||
r1 = _exec(client, "sudo -n subscription-manager identity 2>&1")
|
r1 = _exec(client, "sudo -n subscription-manager identity 2>&1")
|
||||||
sub_ok = (r1["rc"] == 0 and "system identity" in r1["stdout"].lower())
|
sub_ok = (r1["rc"] == 0 and bool(re.search(
|
||||||
|
r"\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b",
|
||||||
|
r1["stdout"], re.IGNORECASE)))
|
||||||
|
|
||||||
# 3) yum repolist enabled --quiet
|
# 3) yum repolist enabled --quiet
|
||||||
r2 = _exec(client, "sudo -n yum repolist enabled --quiet 2>&1 | head -50")
|
r2 = _exec(client, "sudo -n yum repolist enabled --quiet 2>&1 | head -50")
|
||||||
@ -205,6 +272,7 @@ def check_satellite(ctx: Dict[str, Any]) -> Dict[str, Any]:
|
|||||||
CHECKS: Dict[str, Callable[[Dict[str, Any]], Dict[str, Any]]] = {
|
CHECKS: Dict[str, Callable[[Dict[str, Any]], Dict[str, Any]]] = {
|
||||||
"dns": check_dns,
|
"dns": check_dns,
|
||||||
"ssh": check_ssh,
|
"ssh": check_ssh,
|
||||||
|
"disk": check_disk,
|
||||||
"satellite": check_satellite,
|
"satellite": check_satellite,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@
|
|||||||
<th class="text-left p-1">Excludes</th>
|
<th class="text-left p-1">Excludes</th>
|
||||||
<th class="text-left p-1">DNS</th>
|
<th class="text-left p-1">DNS</th>
|
||||||
<th class="text-left p-1">SSH</th>
|
<th class="text-left p-1">SSH</th>
|
||||||
|
<th class="text-left p-1">Disque</th>
|
||||||
<th class="text-left p-1">Satellite</th>
|
<th class="text-left p-1">Satellite</th>
|
||||||
<th class="text-left p-1">Verdict</th>
|
<th class="text-left p-1">Verdict</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -61,11 +62,12 @@
|
|||||||
<td class="p-1 text-cyber-yellow">{{ r.effective_excludes or '(aucun)' }}</td>
|
<td class="p-1 text-cyber-yellow">{{ r.effective_excludes or '(aucun)' }}</td>
|
||||||
<td class="p-1 cell-dns text-gray-500">·</td>
|
<td class="p-1 cell-dns text-gray-500">·</td>
|
||||||
<td class="p-1 cell-ssh text-gray-500">·</td>
|
<td class="p-1 cell-ssh text-gray-500">·</td>
|
||||||
|
<td class="p-1 cell-disk text-gray-500">·</td>
|
||||||
<td class="p-1 cell-sat text-gray-500">·</td>
|
<td class="p-1 cell-sat text-gray-500">·</td>
|
||||||
<td class="p-1 cell-overall text-gray-500">en attente</td>
|
<td class="p-1 cell-overall text-gray-500">en attente</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr><td colspan="10" class="p-2 text-gray-500">Aucune ligne éligible.</td></tr>
|
<tr><td colspan="11" class="p-2 text-gray-500">Aucune ligne éligible.</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -93,6 +95,11 @@
|
|||||||
const detailsCard = document.getElementById('details-card');
|
const detailsCard = document.getElementById('details-card');
|
||||||
const detailsPane = document.getElementById('details-pane');
|
const detailsPane = document.getElementById('details-pane');
|
||||||
|
|
||||||
|
function escapeHTML(s){
|
||||||
|
if (s === null || s === undefined) return '';
|
||||||
|
return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||||||
|
}
|
||||||
|
|
||||||
function statusBadge(st){
|
function statusBadge(st){
|
||||||
if (st === 'ok') return '<span class="text-cyber-green">✓ OK</span>';
|
if (st === 'ok') return '<span class="text-cyber-green">✓ OK</span>';
|
||||||
if (st === 'warn')return '<span class="text-cyber-yellow">⚠ WARN</span>';
|
if (st === 'warn')return '<span class="text-cyber-yellow">⚠ WARN</span>';
|
||||||
@ -112,6 +119,7 @@
|
|||||||
if (!isLinux(osStr)) {
|
if (!isLinux(osStr)) {
|
||||||
tr.querySelector('.cell-dns').innerHTML = statusBadge('unsupported');
|
tr.querySelector('.cell-dns').innerHTML = statusBadge('unsupported');
|
||||||
tr.querySelector('.cell-ssh').innerHTML = statusBadge('unsupported');
|
tr.querySelector('.cell-ssh').innerHTML = statusBadge('unsupported');
|
||||||
|
tr.querySelector('.cell-disk').innerHTML = statusBadge('unsupported');
|
||||||
tr.querySelector('.cell-sat').innerHTML = statusBadge('unsupported');
|
tr.querySelector('.cell-sat').innerHTML = statusBadge('unsupported');
|
||||||
tr.querySelector('.cell-overall').innerHTML = '<span class="text-gray-400" title="Workflow Linux uniquement">⊘ Windows</span>';
|
tr.querySelector('.cell-overall').innerHTML = '<span class="text-gray-400" title="Workflow Linux uniquement">⊘ Windows</span>';
|
||||||
return {overall: 'unsupported'};
|
return {overall: 'unsupported'};
|
||||||
@ -127,6 +135,7 @@
|
|||||||
if (j.overall === 'unsupported') {
|
if (j.overall === 'unsupported') {
|
||||||
tr.querySelector('.cell-dns').innerHTML = statusBadge('unsupported');
|
tr.querySelector('.cell-dns').innerHTML = statusBadge('unsupported');
|
||||||
tr.querySelector('.cell-ssh').innerHTML = statusBadge('unsupported');
|
tr.querySelector('.cell-ssh').innerHTML = statusBadge('unsupported');
|
||||||
|
tr.querySelector('.cell-disk').innerHTML = statusBadge('unsupported');
|
||||||
tr.querySelector('.cell-sat').innerHTML = statusBadge('unsupported');
|
tr.querySelector('.cell-sat').innerHTML = statusBadge('unsupported');
|
||||||
tr.querySelector('.cell-overall').innerHTML = '<span class="text-gray-400" title="' + (j.skipped_reason||'') + '">⊘ N/A</span>';
|
tr.querySelector('.cell-overall').innerHTML = '<span class="text-gray-400" title="' + (j.skipped_reason||'') + '">⊘ N/A</span>';
|
||||||
return j;
|
return j;
|
||||||
@ -135,6 +144,7 @@
|
|||||||
(j.checks || []).forEach(c => byName[c.name] = c);
|
(j.checks || []).forEach(c => byName[c.name] = c);
|
||||||
tr.querySelector('.cell-dns').innerHTML = byName.dns ? statusBadge(byName.dns.status) : '–';
|
tr.querySelector('.cell-dns').innerHTML = byName.dns ? statusBadge(byName.dns.status) : '–';
|
||||||
tr.querySelector('.cell-ssh').innerHTML = byName.ssh ? statusBadge(byName.ssh.status) : '–';
|
tr.querySelector('.cell-ssh').innerHTML = byName.ssh ? statusBadge(byName.ssh.status) : '–';
|
||||||
|
tr.querySelector('.cell-disk').innerHTML = byName.disk ? statusBadge(byName.disk.status) + ' <span class="text-[10px] text-gray-400">' + escapeHTML(byName.disk.message) + '</span>' : '–';
|
||||||
tr.querySelector('.cell-sat').innerHTML = byName.satellite ? statusBadge(byName.satellite.status) : '–';
|
tr.querySelector('.cell-sat').innerHTML = byName.satellite ? statusBadge(byName.satellite.status) : '–';
|
||||||
tr.querySelector('.cell-overall').innerHTML = statusBadge(j.overall) + ' <span class="text-[10px] text-gray-500">(' + (j.duration_ms||0) + 'ms)</span>';
|
tr.querySelector('.cell-overall').innerHTML = statusBadge(j.overall) + ' <span class="text-[10px] text-gray-500">(' + (j.duration_ms||0) + 'ms)</span>';
|
||||||
tr._checkData = j;
|
tr._checkData = j;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user