Initial commit: Cockpit GPU monitor plugin for NVIDIA

Native, self-contained Cockpit page (plain HTML/JS, no build step) that
polls nvidia-smi via cockpit.spawn to show live GPU utilization, memory,
temperature, power, fan speed, ~5min history sparklines, and the list of
processes using the GPU. No background systemd service; data collection
only happens while the page is open.
This commit is contained in:
msaldain
2026-07-15 10:40:20 -03:00
commit 42720a70bc
7 changed files with 626 additions and 0 deletions
+164
View File
@@ -0,0 +1,164 @@
/* gpu-monitor: estilos autocontenidos, sin dependencia de PatternFly ni de
* assets internos de Cockpit (para no acoplarse a rutas que cambian entre
* versiones). */
:root {
--gpu-bg: #ffffff;
--gpu-fg: #1a1a1a;
--gpu-muted: #6a6a6a;
--gpu-card-bg: #f5f5f5;
--gpu-border: #dcdcdc;
--gpu-accent: #0066cc;
--gpu-error-bg: #fdecea;
--gpu-error-fg: #7a1f1a;
--gpu-error-border: #f2b8b5;
}
@media (prefers-color-scheme: dark) {
:root {
--gpu-bg: #1e1e1e;
--gpu-fg: #e8e8e8;
--gpu-muted: #a0a0a0;
--gpu-card-bg: #2a2a2a;
--gpu-border: #3d3d3d;
--gpu-accent: #4da3ff;
--gpu-error-bg: #3a1f1e;
--gpu-error-fg: #f5b8b3;
--gpu-error-border: #5c2b28;
}
}
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 16px;
font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: var(--gpu-bg);
color: var(--gpu-fg);
}
h2 {
font-size: 1rem;
font-weight: 600;
margin: 0 0 8px;
}
.error-banner {
background: var(--gpu-error-bg);
color: var(--gpu-error-fg);
border: 1px solid var(--gpu-error-border);
border-radius: 4px;
padding: 10px 14px;
margin-bottom: 16px;
font-size: 0.9rem;
}
.gpu-container {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 24px;
}
.gpu-card {
background: var(--gpu-card-bg);
border: 1px solid var(--gpu-border);
border-radius: 6px;
padding: 16px;
}
.gpu-title {
color: var(--gpu-accent);
}
.tiles {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 12px;
margin-bottom: 16px;
}
.tile {
background: var(--gpu-bg);
border: 1px solid var(--gpu-border);
border-radius: 4px;
padding: 8px 12px;
}
.tile-label {
font-size: 0.75rem;
color: var(--gpu-muted);
text-transform: uppercase;
letter-spacing: 0.03em;
}
.tile-value {
font-size: 1.3rem;
font-weight: 600;
margin-top: 2px;
}
.charts {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
}
.chart-box {
background: var(--gpu-bg);
border: 1px solid var(--gpu-border);
border-radius: 4px;
padding: 8px 12px;
}
.chart-title {
font-size: 0.75rem;
color: var(--gpu-muted);
margin-bottom: 4px;
}
.sparkline {
width: 100%;
height: 60px;
display: block;
}
.sparkline polyline {
fill: none;
stroke: var(--gpu-accent);
stroke-width: 2;
vector-effect: non-scaling-stroke;
}
.process-section {
margin-top: 8px;
}
.process-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
.process-table th,
.process-table td {
text-align: left;
padding: 6px 10px;
border-bottom: 1px solid var(--gpu-border);
}
.process-table th {
color: var(--gpu-muted);
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.03em;
}
.empty-row td {
color: var(--gpu-muted);
font-style: italic;
}