Files
hermes-config/skills/devops/cockpit-custom-packages/templates/nvidia-gpu-dashboard.html
T
2026-07-12 10:17:17 -04:00

120 lines
9.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>NVIDIA GPU</title>
<link rel="stylesheet" href="../base1/cockpit.css">
<script src="../base1/cockpit.js"></script>
<style>
body{padding:0;font-family:var(--pf-t--global--font--family--body,Inter,RedHatText,Overpass,sans-serif);background:var(--pf-t--global--background--color--primary--default,#f0f2f5)}
.ct-page-fill{padding:1.5rem;max-width:1100px;margin:0 auto}
.page-header{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem;padding:1rem 1.25rem;background:var(--pf-t--global--background--color--secondary--default,linear-gradient(135deg,#0f172a,#1e293b));border-radius:14px;border:1px solid var(--pf-t--global--border--color--default,rgba(255,255,255,0.08));box-shadow:0 2px 12px rgba(0,0,0,0.06)}
.page-header h1{font-size:1.15rem;font-weight:700;margin:0;background:linear-gradient(135deg,#38bdf8,#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.page-header .subtitle{font-size:0.7rem;color:var(--pf-t--global--text--color--subtle,#94a3b8);margin-left:auto;font-family:"SF Mono","JetBrains Mono","Fira Code",monospace;background:var(--pf-t--global--background--color--secondary--default,rgba(255,255,255,0.06));padding:0.2rem 0.6rem;border-radius:6px;border:1px solid var(--pf-t--global--border--color--default,rgba(255,255,255,0.05))}
.status-dot{width:10px;height:10px;border-radius:50%;display:inline-block;flex-shrink:0;position:relative}
.status-dot::after{content:'';position:absolute;inset:-4px;border-radius:50%;opacity:0.25}
.status-dot.ok{background:#22c55e;box-shadow:0 0 10px #22c55e;animation:pulse-dot 2s ease-in-out infinite}
.status-dot.warn{background:#f59e0b;box-shadow:0 0 10px #f59e0b;animation:pulse-dot 2s ease-in-out infinite}
.status-dot.err{background:#ef4444;box-shadow:0 0 10px #ef4444;animation:pulse-dot 2s ease-in-out infinite}
@keyframes pulse-dot{0%,100%{opacity:0.25}50%{opacity:0.6}}
.grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:1rem}
@media(max-width:900px){.grid{grid-template-columns:1fr 1fr}}
@media(max-width:600px){.grid{grid-template-columns:1fr}}
.card{background:var(--pf-t--global--background--color--primary--default,#fff);border:1px solid var(--pf-t--global--border--color--default,#e2e8f0);border-radius:12px;padding:1.25rem;transition:box-shadow 0.2s ease,transform 0.15s ease}
.card:hover{box-shadow:0 4px 20px rgba(0,0,0,0.06);transform:translateY(-2px)}
.card.full{grid-column:1/-1}
.card.accent-blue{border-top:3px solid #3b82f6}
.card.accent-green{border-top:3px solid #22c55e}
.card.accent-orange{border-top:3px solid #f59e0b}
.card.accent-purple{border-top:3px solid #a855f7}
.card.accent-red{border-top:3px solid #ef4444}
.card h2{font-size:0.65rem;text-transform:uppercase;letter-spacing:0.06em;color:var(--pf-t--global--text--color--subtle,#94a3b8);margin:0 0 0.85rem 0;font-weight:700}
.stat{display:flex;justify-content:space-between;align-items:center;padding:0.45rem 0}
.stat+.stat{border-top:1px solid var(--pf-t--global--border--color--default,rgba(0,0,0,0.04))}
.stat-label{color:var(--pf-t--global--text--color--subtle,#64748b);font-size:0.82rem;font-weight:500}
.stat-value{font-weight:600;font-family:"SF Mono","JetBrains Mono","Fira Code",monospace;font-size:0.85rem;color:var(--pf-t--global--text--color--default,#1e293b);white-space:nowrap}
table{width:100%;border-collapse:collapse;font-size:0.82rem}
th{text-align:left;padding:0.45rem 0.5rem;color:var(--pf-t--global--text--color--subtle,#94a3b8);font-weight:600;font-size:0.65rem;text-transform:uppercase;letter-spacing:0.04em;border-bottom:2px solid var(--pf-t--global--border--color--default,#e2e8f0)}
td{padding:0.45rem 0.5rem;border-bottom:1px solid var(--pf-t--global--border--color--default,rgba(0,0,0,0.03))}
td:last-child{text-align:right;font-family:"SF Mono","JetBrains Mono","Fira Code",monospace}
tr:last-child td{border-bottom:none}
tr:hover td{background:var(--pf-t--global--background--color--secondary--default,rgba(0,0,0,0.015))}
.footer-row{display:flex;gap:1rem;flex-wrap:wrap;margin-top:1rem;padding:0.5rem 1rem;background:var(--pf-t--global--background--color--secondary--default,rgba(0,0,0,0.015));border:1px solid var(--pf-t--global--border--color--default,rgba(0,0,0,0.04));border-radius:8px;font-size:0.72rem;color:var(--pf-t--global--text--color--subtle,#94a3b8)}
.alert-box{display:none;background:linear-gradient(135deg,#fef2f2,#fff5f5);border:1px solid #fecaca;color:#991b1b;padding:0.9rem 1.25rem;border-radius:10px;margin-bottom:1rem;font-size:0.85rem;align-items:center;gap:0.6rem}
@keyframes fadeUp{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
.grid>*{animation:fadeUp 0.3s ease-out both}
</style></head>
<body>
<div class="ct-page-fill">
<div id="nogpu" class="alert-box"><span style="font-size:1.2rem"></span> No NVIDIA GPU detected</div>
<div class="page-header">
<span class="status-dot" id="sdot"></span>
<h1>🎮 <span id="gpumodel">NVIDIA GPU</span></h1>
<span class="subtitle" id="pstate">P?</span>
</div>
<div id="content"><div class="grid">
<div class="card accent-blue"><h2>🌡 Thermal</h2>
<div class="stat"><span class="stat-label">Temperature</span><span class="stat-value" id="gt">--°C</span></div>
<div class="stat"><span class="stat-label">Fan Speed</span><span class="stat-value" id="gf"></span></div>
<div class="stat"><span class="stat-label">Utilization</span><span class="stat-value" id="gu">-</span></div>
</div>
<div class="card accent-green"><h2>📊 GPU Load</h2>
<div class="stat"><span class="stat-label">Utilization</span><span class="stat-value" id="gu2">-</span></div>
</div>
<div class="card accent-orange"><h2>💾 Memory</h2>
<div class="stat"><span class="stat-label">Used</span><span class="stat-value" id="mu">-</span></div>
<div class="stat"><span class="stat-label">Total</span><span class="stat-value" id="mt">-</span></div>
<div id="mpct" style="text-align:right;font-size:0.78rem;color:var(--pf-t--global--text--color--subtle,#94a3b8);font-family:monospace;margin-top:2px"></div>
</div>
<div class="card accent-purple"><h2>🔧 Details</h2>
<div class="stat"><span class="stat-label">Driver</span><span class="stat-value" id="dv">-</span></div>
<div class="stat"><span class="stat-label">CUDA Version</span><span class="stat-value" id="cv">-</span></div>
<div class="stat"><span class="stat-label">Perf State</span><span class="stat-value" id="gp">-</span></div>
<div class="stat"><span class="stat-label">Fan Speed</span><span class="stat-value" id="gf2">-</span></div>
</div>
<div class="card accent-red"><h2>⚡ Power</h2>
<div class="stat"><span class="stat-label">Draw</span><span class="stat-value" id="pw">N/A</span></div>
</div>
<div class="card model-card" style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0.4rem;text-align:center">
<div style="font-size:1.8rem">🎮</div>
<div style="font-weight:700;font-size:0.9rem" id="gpmodel2">-</div>
<div style="font-size:0.72rem;color:var(--pf-t--global--text--color--subtle,#94a3b8)" id="driverver">-</div>
</div>
<div class="card full"><h2>🔄 Active Processes</h2>
<table><thead><tr><th>PID</th><th>Process</th><th>Memory</th></tr></thead>
<tbody id="pl"><tr><td colspan="3" style="text-align:center;color:var(--pf-t--global--text--color--subtle,#94a3b8);padding:1rem 0">No GPU processes running</td></tr></tbody></table>
</div>
<div class="full footer-row"><span>🕒 <span id="lu">-</span></span><span id="modelright" style="opacity:0.55">-</span></div>
</div></div>
</div>
<script>
(function(){
function $(i){return document.getElementById(i);}
function fmt(m){var v=parseInt(m);if(isNaN(v))return m+' MiB';return v>=1024?(v/1024).toFixed(1)+' GiB':v+' MiB';}
var PKG="/usr/share/cockpit/nvidia-gpu";
function update(){
var ts=new Date().toLocaleTimeString();
cockpit.spawn(["cat",PKG+"/gpu.txt"],{err:"message"}).done(function(data){
if(!data||!data.trim()){$('nogpu').style.display='flex';$('content').style.display='none';$('sdot').className='status-dot err';return;}
$('nogpu').style.display='none';$('content').style.display='block';var p=data.trim().split(', ');if(p.length<9)return;
var model=p[0];$('gpumodel').textContent=model;$('gpmodel2').textContent=model;$('sdot').className='status-dot ok';
var drv=p[1];$('dv').textContent=drv;$('driverver').textContent='Driver '+drv;$('modelright').textContent=model+' — Driver '+drv;
var t=parseInt(p[2]);if(!isNaN(t)){$('gt').textContent=t+'°C';$('sdot').className='status-dot '+(t<50?'ok':(t<75?'warn':'err'));}
var uPct=parseInt(p[3])||0;$('gu').textContent=uPct+'%';$('gu2').textContent=uPct+'%';
var uMem=parseInt(p[4])||0;var tMem=parseInt(p[5])||1;
$('mu').textContent=fmt(uMem);$('mt').textContent=fmt(tMem);var mp=Math.round(uMem/tMem*100);$('mpct').textContent=mp+'%';
$('pw').textContent=p[6]||'N/A';$('gp').textContent=p[7]||'-';$('pstate').textContent=(p[7]||'').trim();
var fan=p[8]?p[8].trim():'0';var d=(fan==='[N/A]'||fan==='0')?'—':fan+'%';$('gf').textContent=d;$('gf2').textContent=d;
}).fail(function(){$('nogpu').style.display='flex';$('content').style.display='none';$('sdot').className='status-dot err';});
cockpit.spawn(["cat",PKG+"/cuda.txt"],{err:"message"}).done(function(d){if(d&&d.trim())$('cv').textContent=d.trim();});
cockpit.spawn(["cat",PKG+"/processes.txt"],{err:"message"}).done(function(d){
if(!d||!d.trim()){$('pl').innerHTML='<tr><td colspan="3" style="text-align:center;color:var(--pf-t--global--text--color--subtle,#94a3b8);padding:1rem 0">No GPU processes running</td></tr>';return;}
var ls=d.trim().split('\n').filter(function(l){return l.trim();});if(!ls.length){$('pl').innerHTML='<tr><td colspan="3" style="text-align:center;color:var(--pf-t--global--text--color--subtle,#94a3b8);padding:1rem 0">No GPU processes running</td></tr>';return;}
var h='';ls.forEach(function(l){var s=l.split(', ');if(s.length>=3)h+='<tr><td>'+s[0]+'</td><td>'+s[1]+'</td><td>'+fmt(s[2])+'</td></tr>';});$('pl').innerHTML=h;
});
$('lu').textContent=ts;
}
update();setInterval(update,5000);
})();
</script>
</body>
</html>