Add VIO FC Status monitoring page and enhance server inventory filtering

- Introduced `Storage/VIO.php` for monitoring VIO Fibre Channel status with client balancing checks and table sorting.
- Updated `/include/en.php` and `/include/fr.php` to include new translations for ServiceNow and VIO-related navigation elements.
- Improved SQL query and filtering logic in `/X/Inventory.php` to exclude VIO entries and add XenSam version data.
- Enhanced `/reports/heartbeat.php` with a new mechanism for handling server heartbeat status, allowing better granularity and readability.
This commit is contained in:
2026-09-14 10:32:15 +02:00
parent 1f1b7dcd22
commit c185d099d6
25 changed files with 6646 additions and 437 deletions

View File

@@ -0,0 +1,221 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Web Infra Reports OT</title>
<script src="/js/jquery-3.6.1.min.js"></script>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/bootstrap-icons/bootstrap-icons.css">
<link rel="stylesheet" href="/css/preloader.css">
<script src="/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="/css/bootstrap-table.min.css">
<script src="/js/bootstrap-table.min.js"></script>
<script src="/js/bootstrap-table-fr-FR.min.js"></script>
<script src="/js/tableExport.min.js"></script>
<script src="/js/bootstrap-table-export.min.js"></script>
<script src="/js/libs/js-xlsx/xlsx.core.min.js"></script>
<style>
code.sig { font-size: .85em; }
.table td { vertical-align: middle; }
.pivot th, .pivot td { text-align: center; }
.pivot td.comp { text-align: left; font-weight: 500; }
</style>
</head>
<body>
<?php include $_SERVER['DOCUMENT_ROOT']."/include/all.php" ; ?>
<div class="container-fluid">
<div class="row flex-nowrap">
<div class="col-auto col-md-2 col-xl-2 px-sm-2 px-0 bg-dark" style="-ms-flex: 0 0 230px;flex: 0 0 230px;">
<?php include $_SERVER['DOCUMENT_ROOT']."/navbar.html" ; ?>
</div>
<div class="col py-3">
<?php
// -----------------------------------------------------------
// Helpers
// -----------------------------------------------------------
function majority($arr) {
if (count($arr) == 0) { return null; }
$c = array_count_values($arr);
arsort($c);
return key($c);
}
$cluster = isset($_GET['cluster']) ? $_GET['cluster'] : '';
$run = isset($_GET['run']) ? $_GET['run'] : '';
$conn = DB_INFRA();
$nodes = array();
$delta = array();
$runDate = '-';
if ($cluster !== '' && $run !== '') {
// --- Node facts (parameterized to avoid injection) -------
$sqlNodes = "SELECT NodeName, OsName, MsPatchLevel, LastHotfix,
CONVERT(VARCHAR(10), LastHotfixOn, 120) AS LastHotfixOn,
SppSignature, Reachable, MsDrift, SppDrift, ErrorMessage,
CONVERT(VARCHAR(16), RunDate, 120) AS RunDate
FROM dbo.ClusterComplianceResults
WHERE ClusterName = ? AND RunId = ?
ORDER BY NodeName";
$stmt = odbc_prepare($conn, $sqlNodes);
if ($stmt && odbc_execute($stmt, array($cluster, $run))) {
while ($r = odbc_fetch_array($stmt)) {
if ($runDate === '-' && !empty($r['RunDate'])) { $runDate = $r['RunDate']; }
$nodes[] = $r;
}
}
// --- SPP component delta ---------------------------------
$sqlDelta = "SELECT ComponentName, NodeName, ComponentVersion
FROM dbo.ClusterComplianceSppDelta
WHERE ClusterName = ? AND RunId = ?
ORDER BY ComponentName, NodeName";
$stmt2 = odbc_prepare($conn, $sqlDelta);
if ($stmt2 && odbc_execute($stmt2, array($cluster, $run))) {
while ($r = odbc_fetch_array($stmt2)) { $delta[] = $r; }
}
}
// Build the delta pivot : component x node
$deltaNodes = array();
$comps = array();
$matrix = array();
foreach ($delta as $d) {
$cp = $d['ComponentName'];
$nd = $d['NodeName'];
if (!in_array($nd, $deltaNodes)) { $deltaNodes[] = $nd; }
$comps[$cp] = true;
$matrix[$cp][$nd] = $d['ComponentVersion'];
}
sort($deltaNodes);
$comps = array_keys($comps);
sort($comps);
// MS majority for highlighting
$msVals = array();
foreach ($nodes as $n) {
if ($n['Reachable'] == 1 && $n['MsPatchLevel'] !== '' && $n['MsPatchLevel'] !== null) {
$msVals[] = $n['MsPatchLevel'];
}
}
$msMajority = majority($msVals);
?>
<h1>
<span class="badge text-bg-secondary" style="width:100%;">
D&eacute;tail cluster : <?php echo htmlspecialchars($cluster); ?>
<small><small>(<?php echo htmlspecialchars($runDate); ?>)</small></small>
</span>
</h1>
<a href="cluster_compliance.php" class="btn btn-sm btn-outline-secondary mb-3">
<i class="bi bi-arrow-left"></i> Retour au tableau de bord
</a>
<?php if (count($nodes) == 0): ?>
<div class="alert alert-warning">Aucune donn&eacute;e pour ce cluster / ce run.</div>
<?php else: ?>
<!-- ============ Node facts ============ -->
<h4 class="mt-3">&Eacute;tat des n&oelig;uds</h4>
<table class="table table-bordered table-sm">
<thead class="table-light">
<tr>
<th>N&oelig;ud</th>
<th>OS</th>
<th>Niveau MS</th>
<th>Signature SPP</th>
<th>&Eacute;tat</th>
<th>Erreur</th>
</tr>
</thead>
<tbody>
<?php foreach ($nodes as $n): ?>
<?php
$msCls = '';
if ($n['Reachable'] == 1 && $msMajority !== null && $n['MsPatchLevel'] !== $msMajority) {
$msCls = 'table-warning';
}
?>
<tr>
<td><?php echo htmlspecialchars($n['NodeName']); ?></td>
<td><?php echo htmlspecialchars($n['OsName']); ?></td>
<td class="<?php echo $msCls; ?>"><?php echo htmlspecialchars($n['MsPatchLevel']); ?></td>
<td><code class="sig"><?php echo htmlspecialchars($n['SppSignature']); ?></code></td>
<td>
<?php if ($n['Reachable'] == 1): ?>
<span class="badge text-bg-success">Joignable</span>
<?php else: ?>
<span class="badge text-bg-danger">Injoignable</span>
<?php endif; ?>
</td>
<td><small class="text-muted"><?php echo htmlspecialchars($n['ErrorMessage']); ?></small></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- ============ SPP delta pivot ============ -->
<h4 class="mt-4">Delta SPP HP (composants divergents)</h4>
<?php if (count($comps) == 0): ?>
<div class="alert alert-info mb-0">
Aucun &eacute;cart de composant HP relev&eacute; (drift Microsoft uniquement, ou signatures identiques).
</div>
<?php else: ?>
<div class="table-responsive">
<table class="table table-bordered table-sm pivot">
<thead class="table-light">
<tr>
<th class="comp">Composant</th>
<?php foreach ($deltaNodes as $nd): ?>
<th><?php echo htmlspecialchars($nd); ?></th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($comps as $cp): ?>
<?php
$rowVals = array();
foreach ($deltaNodes as $nd) {
$rowVals[] = isset($matrix[$cp][$nd]) ? $matrix[$cp][$nd] : '<absent>';
}
$maj = majority($rowVals);
?>
<tr>
<td class="comp"><?php echo htmlspecialchars($cp); ?></td>
<?php foreach ($deltaNodes as $nd): ?>
<?php
$v = isset($matrix[$cp][$nd]) ? $matrix[$cp][$nd] : '<absent>';
$cls = '';
if ($v === '<absent>') { $cls = 'table-danger'; }
elseif ($v !== $maj) { $cls = 'table-warning'; }
?>
<td class="<?php echo $cls; ?>"><?php echo htmlspecialchars($v); ?></td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="small text-muted mt-1">
<span class="badge text-bg-warning text-dark">jaune</span> version diff&eacute;rente de la majorit&eacute; &nbsp;
<span class="badge text-bg-danger">rouge</span> composant absent du n&oelig;ud
</div>
<?php endif; ?>
<?php endif; /* nodes exist */ ?>
</div>
</div>
</div>
<script src="/js/switch.js"></script>
</body>
</html>