- 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.
233 lines
8.0 KiB
PHP
233 lines
8.0 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<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 IT</title>
|
|
<link rel="shortcut icon" type="image/png" href="/include/favicon-32x32.png">
|
|
|
|
<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>
|
|
.cell-multiline {
|
|
white-space: nowrap;
|
|
}
|
|
/* Custom style for VIOErr badges */
|
|
.badge-err {
|
|
background-color: #dc3545 !important;
|
|
color: white;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body class="bg-light text-dark">
|
|
<?php include $_SERVER['DOCUMENT_ROOT'] . "/include/all.php"; ?>
|
|
|
|
<?php
|
|
// ==========================================
|
|
// 1. DATA PROCESSING (PHP)
|
|
// ==========================================
|
|
|
|
// Parity of a physical fc port (fcname): "fcs0" -> even, "fcs1" -> odd, ...
|
|
function fcs_parity($fcs) {
|
|
if (preg_match('/(\d+)\s*$/', strtolower(trim((string)$fcs)), $m)) {
|
|
return (intval($m[1]) % 2 === 0) ? 'even' : 'odd';
|
|
}
|
|
return 'unknown';
|
|
}
|
|
|
|
$rawRows = Invoke_Infra("SELECT * FROM x_cmdb_VIO");
|
|
if (!$rawRows) $rawRows = [];
|
|
|
|
// Grouping
|
|
$groupedData = [];
|
|
foreach ($rawRows as $row) {
|
|
$key = $row['clintname'];
|
|
if (empty($key)) $key = "[No Client Name]";
|
|
if (!isset($groupedData[$key])) $groupedData[$key] = [];
|
|
$groupedData[$key][] = $row;
|
|
}
|
|
|
|
// Flattening for Display (1 Row per Client)
|
|
$displayRows = [];
|
|
|
|
foreach ($groupedData as $clientKey => $rows) {
|
|
// 1. Sort by date desc
|
|
usort($rows, function($a, $b) {
|
|
return strcmp($b['ts'], $a['ts']);
|
|
});
|
|
|
|
// 2. Keep max 2
|
|
if (count($rows) > 2) {
|
|
$rows = array_slice($rows, 0, 2);
|
|
}
|
|
|
|
$names = []; $inerrs = []; $vfcs = []; $vios = [];
|
|
$clintids = []; $fcnames = []; $tss = []; $vioerrs = [];
|
|
$parities = [];
|
|
|
|
$hasError = false;
|
|
$maxVioErr = 0;
|
|
|
|
foreach ($rows as $r) {
|
|
if ($r['inerr'] != 0 && $r['inerr'] != '0') $hasError = true;
|
|
|
|
// Track VIOErr
|
|
$errVal = (int)($r['VIOErr'] ?? 0);
|
|
if ($errVal > $maxVioErr) $maxVioErr = $errVal;
|
|
|
|
// Format VIOErr for display (Red badge if > 0)
|
|
$vioerrs[] = ($errVal > 0) ? "<span class='badge badge-err'>$errVal</span>" : $errVal;
|
|
|
|
$names[] = $r['name'] ?? '-';
|
|
$inerrs[] = $r['inerr'] ?? '-';
|
|
$vfcs[] = $r['vfcclientname'] ?? '-';
|
|
$vios[] = $r['vio'] ?? '-';
|
|
$clintids[] = $r['clintid'] ?? '-';
|
|
$fcnames[] = $r['fcname'] ?? '-';
|
|
$tss[] = $r['ts'] ?? '-';
|
|
|
|
// Redundancy check is on the physical fc port (fcname).
|
|
$parities[] = fcs_parity($r['fcname'] ?? '');
|
|
}
|
|
|
|
// Parity issue = exactly 2 known fcs parities and they are identical
|
|
// (both even or both odd -> no fabric redundancy).
|
|
$hasParityIssue = false;
|
|
$knownParities = array_values(array_filter($parities, fn($p) => $p !== 'unknown'));
|
|
if (count($knownParities) === 2 && $knownParities[0] === $knownParities[1]) {
|
|
$hasParityIssue = true;
|
|
}
|
|
|
|
$displayRows[] = [
|
|
'clintname' => $clientKey,
|
|
'hasError' => $hasError,
|
|
'hasParityIssue' => $hasParityIssue,
|
|
'maxVioErr' => $maxVioErr,
|
|
'name' => implode('<br>', $names),
|
|
'inerr' => implode('<br>', $inerrs),
|
|
'vfc' => implode('<br>', $vfcs),
|
|
'vio' => implode('<br>', $vios),
|
|
'clintid' => implode('<br>', $clintids),
|
|
'fcname' => implode('<br>', $fcnames),
|
|
'ts' => implode('<br>', $tss),
|
|
'vioerr' => implode('<br>', $vioerrs),
|
|
];
|
|
}
|
|
|
|
// 4. Final Sort:
|
|
// 1. Top tier: InErr (table-warning) OR parity issue (table-danger)
|
|
// 2. VIOErr > 0
|
|
// 3. Alpha
|
|
usort($displayRows, function($a, $b) {
|
|
// Priority 1: warning OR parity issue -> floated to the top, same standing
|
|
$aTop = $a['hasError'] || $a['hasParityIssue'];
|
|
$bTop = $b['hasError'] || $b['hasParityIssue'];
|
|
if ($aTop && !$bTop) return -1;
|
|
if (!$aTop && $bTop) return 1;
|
|
|
|
// Priority 2: VIOErr count
|
|
if ($a['maxVioErr'] > 0 && $b['maxVioErr'] == 0) return -1;
|
|
if ($a['maxVioErr'] == 0 && $b['maxVioErr'] > 0) return 1;
|
|
if ($a['maxVioErr'] != $b['maxVioErr']) return $b['maxVioErr'] - $a['maxVioErr'];
|
|
|
|
// Priority 3: Alphabetical
|
|
return strcasecmp($a['clintname'], $b['clintname']);
|
|
});
|
|
?>
|
|
|
|
<div class="container-fluid" id="content">
|
|
<div class="row flex-nowrap">
|
|
<div class="col-auto col-md-2 col-xl-2 px-sm-2 px-0 bg-dark vh-100 position-sticky top-0" style="flex: 0 0 230px;">
|
|
<?php include $_SERVER['DOCUMENT_ROOT'] . "/navbar.html"; ?>
|
|
</div>
|
|
|
|
<div class="col py-3">
|
|
<h1><span class="badge text-bg-secondary w-100"><?php echo $ti_13;?></span></h1>
|
|
|
|
<div class="container-fluid">
|
|
<div>
|
|
<table class='table table-bordered table-hover table-sm' id='t1'
|
|
data-toggle="table"
|
|
data-search="true"
|
|
data-show-columns="true"
|
|
data-export-types="['xlsx','csv','json']"
|
|
data-show-export="true"
|
|
data-sortable="true"> <thead>
|
|
<tr>
|
|
<th data-field="clintname" data-sortable="true">Clint Name</th>
|
|
<th data-field="name"><?php echo $w_name;?></th>
|
|
<th data-field="vioerr">VIOErr</th>
|
|
<th data-field="inerr">InErr</th>
|
|
<th data-field="vfcclientname">VFC Client Name</th>
|
|
<th data-field="vio">VIO</th>
|
|
<th data-field="clintid">ClintID</th>
|
|
<th data-field="fcname">FC Name</th>
|
|
<th data-field="ts"><?php echo $w_lastUpdate;?></th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<?php foreach ($displayRows as $row): ?>
|
|
<?php
|
|
// Parity issue (red) takes precedence over inerr warning (yellow);
|
|
// both are floated to the top by the sort above.
|
|
if ($row['hasParityIssue']) {
|
|
$class = 'table-danger';
|
|
} elseif ($row['hasError']) {
|
|
$class = 'table-warning';
|
|
} else {
|
|
$class = '';
|
|
}
|
|
?>
|
|
<tr class="<?php echo $class; ?>">
|
|
<td class="fw-bold align-middle"><?php echo htmlspecialchars($row['clintname']); ?></td>
|
|
<td class="cell-multiline"><?php echo $row['name']; ?></td>
|
|
<td class="cell-multiline text-center fw-bold"><?php echo $row['vioerr']; ?></td>
|
|
<td class="cell-multiline"><?php echo $row['inerr']; ?></td>
|
|
<td class="cell-multiline"><?php echo $row['vfc']; ?></td>
|
|
<td class="cell-multiline"><?php echo $row['vio']; ?></td>
|
|
<td class="cell-multiline"><?php echo $row['clintid']; ?></td>
|
|
<td class="cell-multiline">
|
|
<?php echo $row['fcname']; ?>
|
|
<?php if ($row['hasParityIssue']): ?>
|
|
<br><span class="badge bg-danger">même parité</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td class="cell-multiline small"><?php echo $row['ts']; ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
<script src="/js/switch.js"></script>
|
|
<script>
|
|
var $table = $('#t1');
|
|
function adjustTableHeight() {
|
|
let height = $(window).height() - 170;
|
|
$table.bootstrapTable('resetView', { height: height });
|
|
}
|
|
$(function () {
|
|
adjustTableHeight();
|
|
$(window).resize(function () { adjustTableHeight(); });
|
|
});
|
|
</script>
|
|
</html>
|