Refactor GSP stats dashboard for better clarity
This commit is contained in:
parent
1afea9cec8
commit
06804419a2
1 changed files with 225 additions and 142 deletions
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
// stats_aggregate.php — call: ?machine=HOSTNAME_OR_ID[&format=html]
|
||||
// gsp_stats_dashboard.php
|
||||
// Visual dashboard for GSP metrics (uses gsp_ tables in your PANEL DB).
|
||||
|
||||
/********** CONFIG (panel DB) **********/
|
||||
$db = [
|
||||
|
|
@ -9,19 +10,14 @@ $db = [
|
|||
'name' => 'panel_database'
|
||||
];
|
||||
$TABLE_PREFIX = 'gsp_';
|
||||
$DISCORD_WEBHOOK = 'https://discord.com/api/webhooks/REPLACE_ME';
|
||||
$ALERT_THRESHOLD = 80.0;
|
||||
$AUTO_REFRESH_SECONDS = 30; // set 0 to disable auto-refresh
|
||||
/***************************************/
|
||||
|
||||
$format = $_GET['format'] ?? 'json';
|
||||
$machine = $_GET['machine'] ?? null;
|
||||
|
||||
if ($format === 'html') header('Content-Type: text/html'); else header('Content-Type: application/json');
|
||||
|
||||
$mysqli = new mysqli($db['host'], $db['user'], $db['pass'], $db['name']);
|
||||
$mysqli = @new mysqli($db['host'], $db['user'], $db['pass'], $db['name']);
|
||||
if ($mysqli->connect_errno) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'DB connect failed', 'detail' => $mysqli->connect_error]); exit;
|
||||
echo "<pre>DB connect failed: " . htmlspecialchars($mysqli->connect_error) . "</pre>";
|
||||
exit;
|
||||
}
|
||||
$mysqli->set_charset("utf8mb4");
|
||||
|
||||
|
|
@ -29,6 +25,7 @@ function q($mysqli, $sql, $params=[]) {
|
|||
$stmt = $mysqli->prepare($sql);
|
||||
if(!$stmt){ throw new Exception($mysqli->error); }
|
||||
if(!empty($params)) {
|
||||
// infer types (all strings for simplicity)
|
||||
$types = str_repeat('s', count($params));
|
||||
$stmt->bind_param($types, ...$params);
|
||||
}
|
||||
|
|
@ -38,155 +35,241 @@ function q($mysqli, $sql, $params=[]) {
|
|||
$stmt->close();
|
||||
return $rows;
|
||||
}
|
||||
function send_discord($url, $content) {
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['content' => $content]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$resp = curl_exec($ch); $err = curl_error($ch); curl_close($ch);
|
||||
return [$resp, $err];
|
||||
function fmt_bytes($b) {
|
||||
if ($b===null) return '—';
|
||||
$u = ['B','KB','MB','GB','TB','PB']; $i=0;
|
||||
while ($b>=1024 && $i<count($u)-1) { $b/=1024; $i++; }
|
||||
return sprintf('%.1f %s', $b, $u[$i]);
|
||||
}
|
||||
function window_clause($hours) {
|
||||
return "ts >= (NOW() - INTERVAL {$hours} HOUR)";
|
||||
function pct_class($p) {
|
||||
if ($p===null) return 'bar';
|
||||
if ($p>=80) return 'bar danger';
|
||||
if ($p>=60) return 'bar warn';
|
||||
return 'bar ok';
|
||||
}
|
||||
function pct($v) { return $v===null ? '—' : sprintf('%.1f%%', $v); }
|
||||
function num0($v) { return $v===null ? '—' : number_format($v,0); }
|
||||
|
||||
$machine = isset($_GET['machine']) ? trim($_GET['machine']) : '';
|
||||
$windows = ['1h'=>1, '24h'=>24, '7d'=>168];
|
||||
|
||||
?><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>GSP Stats Dashboard<?= $machine ? " – ".htmlspecialchars($machine) : "" ?></title>
|
||||
<?php if ($AUTO_REFRESH_SECONDS>0): ?>
|
||||
<meta http-equiv="refresh" content="<?= (int)$AUTO_REFRESH_SECONDS ?>">
|
||||
<?php endif; ?>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root { --bg:#0b0f14; --panel:#121821; --muted:#9bb0c3; --text:#e6eef6; --ok:#1f9d55; --warn:#d4a017; --danger:#d9534f; --accent:#4ea1ff; }
|
||||
body{background:var(--bg);color:var(--text);font:14px/1.45 system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif;margin:0;padding:24px;}
|
||||
a{color:var(--accent);text-decoration:none}
|
||||
h1,h2,h3{margin:0 0 12px 0}
|
||||
.wrap{max-width:1200px;margin:0 auto}
|
||||
.topbar{display:flex;align-items:center;gap:12px;margin-bottom:16px}
|
||||
.card{background:var(--panel);border-radius:14px;padding:16px;box-shadow:0 2px 12px rgba(0,0,0,.25)}
|
||||
.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}
|
||||
.col-3{grid-column:span 3} .col-4{grid-column:span 4} .col-6{grid-column:span 6} .col-12{grid-column:span 12}
|
||||
.muted{color:var(--muted)}
|
||||
.pill{display:inline-block;padding:2px 8px;border-radius:999px;background:#1a2330;color:#cfe2ff;border:1px solid #2a3a52}
|
||||
.kpi{font-size:22px;font-weight:700}
|
||||
.barwrap{background:#0e141d;border-radius:10px;height:12px;overflow:hidden}
|
||||
.bar{height:100%;display:block;width:0%;transition:width .5s ease;background:var(--ok)}
|
||||
.bar.warn{background:var(--warn)} .bar.danger{background:var(--danger)}
|
||||
table{width:100%;border-collapse:separate;border-spacing:0 8px}
|
||||
thead th{font-weight:600;color:#bcd; text-align:left; padding:8px}
|
||||
tbody td{padding:8px;background:var(--panel)}
|
||||
tbody tr{border-radius:10px}
|
||||
tbody tr td:first-child{border-top-left-radius:10px;border-bottom-left-radius:10px}
|
||||
tbody tr td:last-child{border-top-right-radius:10px;border-bottom-right-radius:10px}
|
||||
.right{text-align:right}
|
||||
.small{font-size:12px}
|
||||
.row{display:flex;gap:10px;flex-wrap:wrap}
|
||||
.btn{display:inline-block;padding:8px 10px;border:1px solid #2a3a52;border-radius:10px;background:#0e141d;color:#dfeaff}
|
||||
.btn.active{background:#1a2330}
|
||||
.split{display:flex;gap:16px;flex-wrap:wrap}
|
||||
.split > div{flex:1 1 280px}
|
||||
.subtle{opacity:.8}
|
||||
.hdr{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
|
||||
.sep{height:1px;background:#223143;margin:16px 0}
|
||||
.mono{font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="topbar">
|
||||
<h1>GSP Stats Dashboard</h1>
|
||||
<span class="pill"><?= $AUTO_REFRESH_SECONDS>0 ? "auto-refresh {$AUTO_REFRESH_SECONDS}s" : "manual refresh" ?></span>
|
||||
<a class="btn" href="?">All machines</a>
|
||||
<?php if($machine): ?>
|
||||
<a class="btn" href="?machine=<?= urlencode($machine) ?>">Refresh</a>
|
||||
<a class="btn" target="_blank" href="stats_aggregate.php?machine=<?= urlencode($machine) ?>">View raw JSON</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
try {
|
||||
if(!$machine){
|
||||
$rows = q($mysqli, "SELECT machine_id, hostname, created_at FROM {$TABLE_PREFIX}machines ORDER BY created_at DESC");
|
||||
$payload = ['machines' => $rows];
|
||||
echo $format==='html' ? "<pre>".htmlspecialchars(json_encode($payload, JSON_PRETTY_PRINT))."</pre>" : json_encode($payload, JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
if (!$machine) {
|
||||
// list machines with last-ts
|
||||
$rows = q($mysqli, "SELECT m.machine_id, m.hostname,
|
||||
(SELECT MAX(ts) FROM {$TABLE_PREFIX}machine_samples s WHERE s.machine_id=m.machine_id) AS last_ts
|
||||
FROM {$TABLE_PREFIX}machines m ORDER BY m.created_at DESC");
|
||||
echo '<div class="grid">';
|
||||
foreach ($rows as $r) {
|
||||
echo '<div class="col-4 card">';
|
||||
echo '<div class="hdr"><h3 class="mono">'.htmlspecialchars($r['machine_id']).'</h3><span class="muted">'.htmlspecialchars($r['hostname']).'</span></div>';
|
||||
echo '<div class="small muted">Last sample: '.htmlspecialchars($r['last_ts'] ?: '—').'</div>';
|
||||
echo '<div style="margin-top:10px"><a class="btn" href="?machine='.urlencode($r['machine_id']).'">Open</a></div>';
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
echo '</div></body></html>'; exit;
|
||||
}
|
||||
|
||||
$out = ['machine' => $machine, 'windows' => []];
|
||||
// LAST sample
|
||||
$last = q($mysqli, "SELECT * FROM {$TABLE_PREFIX}machine_samples WHERE machine_id=? ORDER BY ts DESC LIMIT 1", [$machine]);
|
||||
$last = $last ? $last[0] : null;
|
||||
|
||||
$lastM = q($mysqli,
|
||||
"SELECT * FROM {$TABLE_PREFIX}machine_samples WHERE machine_id=? ORDER BY ts DESC LIMIT 1",
|
||||
[$machine]
|
||||
);
|
||||
$lastTs = $lastM ? $lastM[0]['ts'] : null;
|
||||
|
||||
$lastServers = q($mysqli, "
|
||||
SELECT server_name,
|
||||
SUM(cpu_pct) AS cpu_pct_sum,
|
||||
AVG(mem_pct) AS mem_pct_avg,
|
||||
MAX(folder_size_bytes) AS folder_size_bytes,
|
||||
GROUP_CONCAT(DISTINCT pid ORDER BY pid) AS pids
|
||||
FROM {$TABLE_PREFIX}process_samples
|
||||
WHERE machine_id=? AND ts=(SELECT MAX(ts) FROM {$TABLE_PREFIX}process_samples WHERE machine_id=?)
|
||||
GROUP BY server_name
|
||||
ORDER BY server_name ASC
|
||||
", [$machine, $machine]);
|
||||
|
||||
$out['last'] = [
|
||||
'ts' => $lastTs,
|
||||
'machine' => $lastM ? [
|
||||
'load1' => (float)$lastM[0]['load1'],
|
||||
'load5' => (float)$lastM[0]['load5'],
|
||||
'load15'=> (float)$lastM[0]['load15'],
|
||||
'cpu_pct'=> (float)$lastM[0]['cpu_pct'],
|
||||
'mem_used_pct'=> (float)$lastM[0]['mem_used_pct'],
|
||||
'disk_used_pct'=> (float)$lastM[0]['disk_used_pct'],
|
||||
'net_iface'=> $lastM[0]['net_iface'],
|
||||
'rx_bytes'=> (int)$lastM[0]['rx_bytes'],
|
||||
'tx_bytes'=> (int)$lastM[0]['tx_bytes'],
|
||||
'iface_speed_mbps'=> isset($lastM[0]['iface_speed_mbps']) ? (int)$lastM[0]['iface_speed_mbps'] : null
|
||||
] : null,
|
||||
'servers' => $lastServers
|
||||
];
|
||||
|
||||
$windows = [ '1h'=>1, '24h'=>24, '7d'=>24*7 ];
|
||||
// Windows
|
||||
$agg = [];
|
||||
foreach($windows as $label=>$hours){
|
||||
$aggM = q($mysqli, "
|
||||
SELECT COUNT(*) AS n,
|
||||
AVG(cpu_pct) AS cpu_avg,
|
||||
AVG(mem_used_pct) AS mem_avg,
|
||||
AVG(disk_used_pct)AS disk_avg
|
||||
FROM {$TABLE_PREFIX}machine_samples
|
||||
WHERE machine_id=? AND ".window_clause($hours),
|
||||
[$machine]
|
||||
);
|
||||
$aggM = q($mysqli, "SELECT
|
||||
COUNT(*) n,
|
||||
AVG(cpu_pct) cpu_avg,
|
||||
AVG(mem_used_pct) mem_avg,
|
||||
AVG(disk_used_pct) disk_avg
|
||||
FROM {$TABLE_PREFIX}machine_samples
|
||||
WHERE machine_id=? AND ts >= (NOW() - INTERVAL {$hours} HOUR)", [$machine]);
|
||||
|
||||
$netRows = q($mysqli, "
|
||||
SELECT ts, rx_bytes, tx_bytes, iface_speed_mbps
|
||||
FROM {$TABLE_PREFIX}machine_samples
|
||||
WHERE machine_id=? AND ".window_clause($hours)."
|
||||
ORDER BY ts ASC
|
||||
", [$machine]);
|
||||
|
||||
$net = null;
|
||||
if(count($netRows) >= 2){
|
||||
$first = $netRows[0]; $last = $netRows[count($netRows)-1];
|
||||
$secs = max(1, strtotime($last['ts']) - strtotime($first['ts']));
|
||||
$rx_bps = ((int)$last['rx_bytes'] - (int)$first['rx_bytes']) / $secs;
|
||||
$tx_bps = ((int)$last['tx_bytes'] - (int)$first['tx_bytes']) / $secs;
|
||||
$speed_mbps = $last['iface_speed_mbps'] ? (int)$last['iface_speed_mbps'] : null;
|
||||
$netRows = q($mysqli, "SELECT ts, rx_bytes, tx_bytes, iface_speed_mbps
|
||||
FROM {$TABLE_PREFIX}machine_samples
|
||||
WHERE machine_id=? AND ts >= (NOW() - INTERVAL {$hours} HOUR)
|
||||
ORDER BY ts ASC", [$machine]);
|
||||
$net = ['avg_rx_Bps'=>null,'avg_tx_Bps'=>null,'avg_total_Bps'=>null,'avg_util_pct'=>null];
|
||||
if (count($netRows)>=2) {
|
||||
$first = $netRows[0]; $lastn = $netRows[count($netRows)-1];
|
||||
$secs = max(1, strtotime($lastn['ts']) - strtotime($first['ts']));
|
||||
$rx_bps = ((int)$lastn['rx_bytes'] - (int)$first['rx_bytes']) / $secs;
|
||||
$tx_bps = ((int)$lastn['tx_bytes'] - (int)$first['tx_bytes']) / $secs;
|
||||
$speed_mbps = $lastn['iface_speed_mbps'] ? (int)$lastn['iface_speed_mbps'] : null;
|
||||
$util_pct = null;
|
||||
if($speed_mbps && $speed_mbps > 0){
|
||||
if ($speed_mbps && $speed_mbps>0) {
|
||||
$capacity_Bps = ($speed_mbps * 1000000) / 8.0;
|
||||
$util_pct = (($rx_bps + $tx_bps) / $capacity_Bps) * 100.0;
|
||||
}
|
||||
$net = [
|
||||
'avg_rx_Bps' => $rx_bps,
|
||||
'avg_tx_Bps' => $tx_bps,
|
||||
'avg_total_Bps' => $rx_bps + $tx_bps,
|
||||
'avg_util_pct' => $util_pct
|
||||
];
|
||||
$net = ['avg_rx_Bps'=>$rx_bps,'avg_tx_Bps'=>$tx_bps,'avg_total_Bps'=>$rx_bps+$tx_bps,'avg_util_pct'=>$util_pct];
|
||||
}
|
||||
|
||||
$aggS = q($mysqli, "
|
||||
SELECT server_name,
|
||||
AVG(cpu_pct) AS cpu_avg,
|
||||
AVG(mem_pct) AS mem_avg,
|
||||
MAX(folder_size_bytes) AS folder_size_bytes
|
||||
FROM {$TABLE_PREFIX}process_samples
|
||||
WHERE machine_id=? AND ".window_clause($hours)."
|
||||
GROUP BY server_name
|
||||
ORDER BY server_name ASC
|
||||
", [$machine]);
|
||||
$aggS = q($mysqli, "SELECT server_name,
|
||||
AVG(cpu_pct) cpu_avg,
|
||||
AVG(mem_pct) mem_avg,
|
||||
MAX(folder_size_bytes) folder_size_bytes
|
||||
FROM {$TABLE_PREFIX}process_samples
|
||||
WHERE machine_id=? AND ts >= (NOW() - INTERVAL {$hours} HOUR)
|
||||
GROUP BY server_name
|
||||
ORDER BY server_name ASC", [$machine]);
|
||||
|
||||
$out['windows'][$label] = [
|
||||
'machine' => [
|
||||
'cpu_avg' => isset($aggM[0]['cpu_avg']) ? (float)$aggM[0]['cpu_avg'] : null,
|
||||
'mem_avg' => isset($aggM[0]['mem_avg']) ? (float)$aggM[0]['mem_avg'] : null,
|
||||
'disk_avg' => isset($aggM[0]['disk_avg']) ? (float)$aggM[0]['disk_avg'] : null,
|
||||
'net' => $net
|
||||
],
|
||||
'servers' => $aggS
|
||||
];
|
||||
$agg[$label] = ['machine'=>$aggM[0], 'net'=>$net, 'servers'=>$aggS];
|
||||
}
|
||||
|
||||
$alerts = [];
|
||||
foreach (['1h','24h'] as $w) {
|
||||
$mw = $out['windows'][$w]['machine'];
|
||||
if ($mw['cpu_avg'] !== null && $mw['cpu_avg'] >= $ALERT_THRESHOLD) $alerts[] = "Machine CPU avg {$w}: ".round($mw['cpu_avg'],1)."%";
|
||||
if ($mw['mem_avg'] !== null && $mw['mem_avg'] >= $ALERT_THRESHOLD) $alerts[] = "Machine MEM avg {$w}: ".round($mw['mem_avg'],1)."%";
|
||||
if ($mw['disk_avg'] !== null && $mw['disk_avg'] >= $ALERT_THRESHOLD) $alerts[] = "Machine DISK avg {$w}: ".round($mw['disk_avg'],1)."%";
|
||||
if (isset($mw['net']['avg_util_pct']) && $mw['net']['avg_util_pct'] !== null && $mw['net']['avg_util_pct'] >= $ALERT_THRESHOLD) {
|
||||
$alerts[] = "Machine NET util avg {$w}: ".round($mw['net']['avg_util_pct'],1)."%";
|
||||
}
|
||||
foreach ($out['windows'][$w]['servers'] as $s) {
|
||||
if ($s['cpu_avg'] !== null && (float)$s['cpu_avg'] >= $ALERT_THRESHOLD)
|
||||
$alerts[] = "Server '{$s['server_name']}' CPU avg {$w}: ".round($s['cpu_avg'],1)."%";
|
||||
if ($s['mem_avg'] !== null && (float)$s['mem_avg'] >= $ALERT_THRESHOLD)
|
||||
$alerts[] = "Server '{$s['server_name']}' MEM avg {$w}: ".round($s['mem_avg'],1)."%";
|
||||
}
|
||||
}
|
||||
if (!empty($alerts) && !empty($DISCORD_WEBHOOK) && strpos($DISCORD_WEBHOOK, 'REPLACE_ME') === false) {
|
||||
$msg = ":warning: **$machine** threshold(s) >= {$ALERT_THRESHOLD}%:\n- " . implode("\n- ", $alerts);
|
||||
send_discord($DISCORD_WEBHOOK, $msg);
|
||||
$out['alerts_sent'] = $alerts;
|
||||
} else {
|
||||
$out['alerts_sent'] = [];
|
||||
}
|
||||
|
||||
$json = json_encode($out, JSON_PRETTY_PRINT);
|
||||
echo $format==='html' ? "<pre>".htmlspecialchars($json)."</pre>" : $json;
|
||||
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Exception', 'detail' => $e->getMessage()]);
|
||||
} finally {
|
||||
$mysqli->close();
|
||||
echo '<div class="card">Error: '.htmlspecialchars($e->getMessage()).'</div></div></body></html>'; exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="grid">
|
||||
<div class="col-12 card">
|
||||
<div class="hdr">
|
||||
<h2 class="mono"><?= htmlspecialchars($machine) ?></h2>
|
||||
<div class="muted small">Last sample: <?= htmlspecialchars($last['ts'] ?? '—') ?> • IF: <?= htmlspecialchars($last['net_iface'] ?? '—') ?></div>
|
||||
</div>
|
||||
<div class="split">
|
||||
<div>
|
||||
<div class="muted small">CPU (last)</div>
|
||||
<div class="kpi"><?= pct($last ? (float)$last['cpu_pct'] : null) ?></div>
|
||||
<div class="barwrap"><span class="<?= pct_class($last ? (float)$last['cpu_pct'] : null) ?>" style="width:<?= $last? min(100,max(0,(float)$last['cpu_pct'])):0 ?>%"></span></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="muted small">Memory used (last)</div>
|
||||
<div class="kpi"><?= pct($last ? (float)$last['mem_used_pct'] : null) ?></div>
|
||||
<div class="barwrap"><span class="<?= pct_class($last ? (float)$last['mem_used_pct'] : null) ?>" style="width:<?= $last? min(100,max(0,(float)$last['mem_used_pct'])):0 ?>%"></span></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="muted small">Disk used (last)</div>
|
||||
<div class="kpi"><?= pct($last ? (float)$last['disk_used_pct'] : null) ?></div>
|
||||
<div class="barwrap"><span class="<?= pct_class($last ? (float)$last['disk_used_pct'] : null) ?>" style="width:<?= $last? min(100,max(0,(float)$last['disk_used_pct'])):0 ?>%"></span></div>
|
||||
<div class="small subtle mono"><?= htmlspecialchars($last['disk_path'] ?? '') ?> • used <?= fmt_bytes($last['disk_used_bytes'] ?? null) ?> / <?= fmt_bytes($last['disk_total_bytes'] ?? null) ?></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="muted small">Net avg util (1h)</div>
|
||||
<?php $nu = $agg['1h']['net']['avg_util_pct']; ?>
|
||||
<div class="kpi"><?= $nu===null?'—':sprintf('%.1f%%',$nu) ?></div>
|
||||
<div class="barwrap"><span class="<?= pct_class($nu) ?>" style="width:<?= $nu!==null ? min(100,max(0,$nu)) : 0 ?>%"></span></div>
|
||||
<div class="small subtle mono">rx <?= fmt_bytes($agg['1h']['net']['avg_rx_Bps']) ?>/s • tx <?= fmt_bytes($agg['1h']['net']['avg_tx_Bps']) ?>/s</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php foreach ($windows as $label=>$hours): $m=$agg[$label]['machine']; ?>
|
||||
<div class="col-12 card">
|
||||
<div class="hdr">
|
||||
<h3>Window: <?= htmlspecialchars($label) ?></h3>
|
||||
<div class="row">
|
||||
<span class="small muted">AVG CPU</span>
|
||||
<div class="barwrap" style="width:160px;"><span class="<?= pct_class($m['cpu_avg']) ?>" style="width:<?= $m['cpu_avg']!==null?min(100,max(0,$m['cpu_avg'])):0 ?>%"></span></div>
|
||||
<span class="small"><?= pct($m['cpu_avg']) ?></span>
|
||||
<span class="small muted" style="margin-left:14px;">AVG MEM</span>
|
||||
<div class="barwrap" style="width:160px;"><span class="<?= pct_class($m['mem_avg']) ?>" style="width:<?= $m['mem_avg']!==null?min(100,max(0,$m['mem_avg'])):0 ?>%"></span></div>
|
||||
<span class="small"><?= pct($m['mem_avg']) ?></span>
|
||||
<span class="small muted" style="margin-left:14px;">AVG DISK</span>
|
||||
<div class="barwrap" style="width:160px;"><span class="<?= pct_class($m['disk_avg']) ?>" style="width:<?= $m['disk_avg']!==null?min(100,max(0,$m['disk_avg'])):0 ?>%"></span></div>
|
||||
<span class="small"><?= pct($m['disk_avg']) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sep"></div>
|
||||
|
||||
<div class="tablewrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Server</th>
|
||||
<th class="right">CPU avg</th>
|
||||
<th style="width:220px"></th>
|
||||
<th class="right">Mem avg</th>
|
||||
<th style="width:220px"></th>
|
||||
<th class="right">Folder size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$rows = $agg[$label]['servers'];
|
||||
if (!$rows) {
|
||||
echo '<tr><td colspan="6" class="small muted">No server samples in this window.</td></tr>';
|
||||
} else {
|
||||
foreach ($rows as $s) {
|
||||
$cpu = $s['cpu_avg']!==null ? (float)$s['cpu_avg'] : null;
|
||||
$mem = $s['mem_avg']!==null ? (float)$s['mem_avg'] : null;
|
||||
echo '<tr>';
|
||||
echo '<td class="mono">'.htmlspecialchars($s['server_name']).'</td>';
|
||||
echo '<td class="right">'.pct($cpu).'</td>';
|
||||
echo '<td><div class="barwrap"><span class="'.pct_class($cpu).'" style="width:'.($cpu!==null?min(100,max(0,$cpu)):0).'%"></span></div></td>';
|
||||
echo '<td class="right">'.pct($mem).'</td>';
|
||||
echo '<td><div class="barwrap"><span class="'.pct_class($mem).'" style="width:'.($mem!==null?min(100,max(0,$mem)):0).'%"></span></div></td>';
|
||||
echo '<td class="right">'.fmt_bytes($s['folder_size_bytes']).'</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue