Files
nasmon/internal/web/templates/dashboard.html
T
a.gritsenkoandClaude Opus 5.5 30dbb00dd1
build / build (push) Successful in 1m43s
Shares of other services and RepoISO integration
- include SMB drop-ins from /etc/samba/smb.conf.d into the generated smb.conf
- keep smbd/nfs-server running while other services (/etc/exports.d, drop-ins) share,
  instead of disabling them when nasmon has no shares of its own
- list those shares read-only on /shares
- settings: RepoISO URL; poll its /api/integration/status, dashboard card, menu link,
  alerts for portal down, stale worker, failed jobs, unmounted publications

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 15:06:17 +03:00

138 lines
8.6 KiB
HTML

{{define "content"}}
{{template "flash" .}}
{{with .Data}}
{{template "alertlist" .S.Alerts}}
<section class="grid metrics">
<div class="card metric">
<h2>CPU <span class="muted">{{.S.CPU.Cores}} cores</span></h2>
<div class="big {{levelClass .S.CPU.UsagePct 70 90}}">{{f0 .S.CPU.UsagePct}}%</div>
<div class="muted">load {{f1 (index .S.CPU.Load 0)}} / {{f1 (index .S.CPU.Load 1)}} / {{f1 (index .S.CPU.Load 2)}}</div>
{{.CPUSpark}}
<div class="muted small">last 24 h</div>
</div>
<div class="card metric">
<h2>Memory <span class="muted">{{bytes .S.Mem.Total}}</span></h2>
<div class="big {{levelClass .MemPct 80 95}}">{{f0 .MemPct}}%</div>
<div class="bar">
<span class="fill mem" style="width:{{f1 .MemPct}}%"></span>
{{if .S.Mem.ARCSize}}<span class="fill arc" style="width:{{f1 .ARCPct}}%"></span>{{end}}
</div>
<div class="muted">used {{bytes .S.Mem.Used}}{{if .S.Mem.ARCSize}} · <span class="arc-dot"></span>ARC {{bytes .S.Mem.ARCSize}} / {{bytes .S.Mem.ARCMax}}{{end}}</div>
{{if .S.Mem.SwapTotal}}<div class="muted">swap {{bytes .S.Mem.SwapUsed}} / {{bytes .S.Mem.SwapTotal}} ({{f0 .SwapPct}}%)</div>{{end}}
{{.MemSpark}}
<div class="muted small">last 24 h</div>
</div>
<div class="card metric">
<h2>Temperature</h2>
{{if .S.HasCPUTemp}}<div class="big {{tempClass .S.CPUTemp .Th.CPUTempC}}">{{f0 .S.CPUTemp}}°C</div><div class="muted">CPU</div>{{else}}<div class="big muted">—</div><div class="muted">no CPU sensor</div>{{end}}
{{.TempSpark}}
<details class="sensors">
<summary class="muted small">{{len .S.Temps}} sensors</summary>
<table class="compact">
{{range .S.Temps}}<tr><td class="muted">{{.Chip}}</td><td>{{.Label}}</td><td class="num">{{f0 .C}}°C</td></tr>{{end}}
</table>
</details>
</div>
<div class="card metric">
<h2>System</h2>
<div class="kv">
<div><span class="muted">host</span> {{.S.Host}}</div>
<div><span class="muted">root fs</span> {{bytes .S.RootFS.Free}} free of {{bytes .S.RootFS.Total}}</div>
<div><span class="muted">SMART check</span> {{ago .S.LastSmartCheck}}{{if .S.SmartRunning}} <span class="badge">running</span>{{else}} · next {{dt .S.NextSmartCheck}}{{end}}</div>
<div><span class="muted">nasmon</span> {{.S.Version}} · up {{dur .S.Uptime}}</div>
{{if .S.LastMailErr}}<div class="crit-text">mail error: {{.S.LastMailErr}}</div>{{end}}
</div>
<form method="post" action="/smart/run" class="inline"><button type="submit" {{if .S.SmartRunning}}disabled{{end}}>Check SMART now</button></form>
</div>
</section>
<section class="card">
<h2>Disks</h2>
{{if .S.DisksErr}}<div class="flash crit">{{.S.DisksErr}}</div>{{end}}
<div class="tablewrap"><table>
<thead><tr><th>Device</th><th>Model</th><th>Serial</th><th class="num">Size</th><th>Bus</th><th class="num">Temp</th><th>Health</th><th class="num">Power-on</th><th>Self-test</th><th>Warnings</th></tr></thead>
<tbody>
{{range .S.Disks}}{{$sm := .Smart}}
<tr>
<td><a href="/disks/{{.Serial}}">{{.Name}}</a></td>
<td>{{.Model}}</td>
<td class="mono small">{{.Serial}}</td>
<td class="num">{{bytes .Size}}</td>
<td class="muted">{{.Tran}}{{if not .Rota}} ssd{{end}}</td>
{{if $sm}}
<td class="num">{{if $sm.HasTemp}}<span class="{{tempClass $sm.TempC $.Data.Th.DiskTempC}}">{{$sm.TempC}}°C</span>{{if .Standby}} <span class="muted small" title="disk in standby, value from last read">zz</span>{{end}}{{else}}—{{end}}</td>
<td>{{if .Error}}<span class="badge warn" title="{{.Error}}">n/a</span>{{else if not $sm.Supported}}<span class="muted">n/a</span>{{else if $sm.Healthy}}<span class="badge ok">PASSED</span>{{else}}<span class="badge crit">FAILED</span>{{end}}</td>
<td class="num">{{if $sm.PowerOnHrs}}{{$sm.PowerOnHrs}} h{{else}}—{{end}}</td>
<td class="small">{{with $sm.LastTest}}{{if .InProgress}}<span class="badge">{{.Remaining}}% left</span>{{else}}<span class="{{if .Passed}}ok{{else}}crit{{end}}">{{.Status}}</span>{{end}}{{else}}—{{end}}</td>
<td class="small">
{{range $sm.Attrs}}{{if and (or (eq .ID 5) (eq .ID 187) (eq .ID 188) (eq .ID 197) (eq .ID 198)) (gt .Raw 0)}}<span class="badge crit" title="{{.Name}}">{{.ID}}: {{.Raw}}</span> {{end}}{{if .WhenFailed}}<span class="badge crit">{{.ID}} {{.WhenFailed}}</span> {{end}}{{end}}
{{with $sm.NVMe}}{{if .CriticalWarning}}<span class="badge crit">warn 0x{{printf "%02x" .CriticalWarning}}</span> {{end}}{{if .MediaErrors}}<span class="badge crit">media err {{.MediaErrors}}</span> {{end}}<span class="muted">{{.PercentageUsed}}% used</span>{{end}}
</td>
{{else}}
<td class="num">—</td><td>{{if .Standby}}<span class="muted">standby</span>{{else if .Error}}<span class="badge warn" title="{{.Error}}">n/a</span>{{else}}<span class="muted">pending</span>{{end}}</td><td></td><td></td><td></td>
{{end}}
</tr>
{{else}}<tr><td colspan="10" class="muted">no disks found</td></tr>{{end}}
</tbody>
</table></div>
</section>
<section class="card">
<h2>ZFS pools <a class="muted small" href="/zfs">details</a></h2>
{{if .S.PoolsErr}}<div class="flash crit">{{.S.PoolsErr}}</div>{{end}}
<div class="tablewrap"><table>
<thead><tr><th>Pool</th><th>Health</th><th class="num">Used</th><th class="num">Free</th><th class="num">Size</th><th>Capacity</th><th class="num">Frag</th><th>Last scrub</th><th>Errors</th></tr></thead>
<tbody>
{{range .S.Pools}}
<tr>
<td><a href="/zfs#{{.Name}}">{{.Name}}</a></td>
<td><span class="badge {{healthClass .Health}}">{{.Health}}</span></td>
<td class="num">{{bytes .Alloc}}</td>
<td class="num">{{bytes .Free}}</td>
<td class="num">{{bytes .Size}}</td>
<td><div class="bar small"><span class="fill {{capClass .CapPct 75 90}}" style="width:{{.CapPct}}%"></span></div> <span class="small">{{.CapPct}}%</span></td>
<td class="num">{{.FragPct}}%</td>
<td class="small">{{if .Scrubbing}}<span class="badge">scrubbing</span>{{else if .LastScrub}}{{agop .LastScrub}}{{else}}<span class="muted">never</span>{{end}}</td>
<td class="small">{{if .Errors}}{{.Errors}}{{end}}</td>
</tr>
{{else}}<tr><td colspan="9" class="muted">no pools</td></tr>{{end}}
</tbody>
</table></div>
</section>
{{with .S.RepoISO}}
<section class="card" id="repoiso">
<h2>RepoISO {{if $.RepoISO}}<a class="muted small" href="{{$.RepoISO}}" target="_blank" rel="noopener">open ↗</a>{{end}}
{{if not .Reachable}}<span class="badge crit">not responding</span>
{{else if not .WorkerAlive}}<span class="badge warn">worker stopped</span>
{{else}}<span class="badge ok">running</span>{{end}}
{{if .Version}}<span class="muted small">v{{.Version}}</span>{{end}}</h2>
{{if not .Reachable}}<div class="flash crit">{{.URL}}/api/integration/status: {{.Error}}</div>{{else}}
<div class="tablewrap"><table class="compact">
<tbody>
<tr><td class="muted">Images</td><td class="num">{{.Images}}</td><td>{{bytes .ImagesBytes}}{{if .ImagesPending}} · <span class="muted">{{.ImagesPending}} being checked</span>{{end}}{{if .ImagesError}} · <span class="crit">{{.ImagesError}} with errors</span>{{end}}</td></tr>
<tr><td class="muted">Publications</td><td class="num">{{.Publications}}</td><td>{{if .PublicationsError}}<span class="badge warn">{{.PublicationsError}} not mounted</span>{{else}}<span class="muted">HTTP repositories / file trees</span>{{end}}</td></tr>
<tr><td class="muted">Jobs</td><td class="num">{{.JobsActive}}</td><td>{{if .JobsActive}}active{{else}}<span class="muted">idle</span>{{end}}{{if .JobsFailed24h}} · <span class="badge warn">{{.JobsFailed24h}} failed in 24 h</span>{{end}}</td></tr>
<tr><td class="muted">Proxmox exports</td><td class="num">{{.Exports}}</td><td class="muted">NFS / SMB shares — listed on <a href="/shares#external">Shares</a></td></tr>
<tr><td class="muted">Worker</td><td></td><td class="small">{{if .WorkerHeartbeat}}last heartbeat {{.WorkerHeartbeat}}{{else}}no heartbeat yet{{end}}</td></tr>
</tbody>
</table></div>{{end}}
</section>
{{end}}
<section class="card">
<h2>Datasets</h2>
<div class="tablewrap"><table>
<thead><tr><th>Dataset</th><th>Mountpoint</th><th class="num">Used</th><th class="num">Referenced</th><th class="num">Available</th></tr></thead>
<tbody>
{{range .S.Datasets}}<tr><td class="mono">{{.Name}}</td><td class="muted mono small">{{.Mountpoint}}</td><td class="num">{{bytes .Used}}</td><td class="num">{{bytes .Refer}}</td><td class="num">{{bytes .Avail}}</td></tr>{{end}}
</tbody>
</table></div>
</section>
{{end}}
{{end}}