557629d2bf
gates / gates (push) Successful in 2m3s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
115 lines
3.5 KiB
Go
115 lines
3.5 KiB
Go
package web
|
|
|
|
// PBS DR datastore fill surfaces (v0.65.0, R-5): the Offsite "PBS DR" tab panel + the Dashboard PBS
|
|
// gauge. The web layer reads only the checker's cached snapshot (s.pbsdrBox) — it never polls ep0. The
|
|
// snapshot carries its own state (ok / unavailable / degraded) so the UI renders each honestly.
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-hub/internal/monitor"
|
|
)
|
|
|
|
// pbsdrBoxView is the PBS-DR panel model.
|
|
type pbsdrBoxView struct {
|
|
Configured bool // s.pbsdrBox wired (tenantsync client present)
|
|
Pending bool // configured but the first poll hasn't landed yet
|
|
State string // ok | unavailable | degraded
|
|
Unavailable bool // endpoint script predates the usage op (≤ v1.1.0) — expected pre-update
|
|
Degraded bool // last poll failed — values shown stale
|
|
HasFill bool // numbers available (ok or degraded-with-last-known)
|
|
CapacityStr string
|
|
UsedStr string
|
|
UsedExact string // R-92: exact bytes ("8 590 000 000 bytes") — UsedStr is 0.1 GB-granular
|
|
FillPercent float64
|
|
FillBand string
|
|
FetchedAt time.Time
|
|
}
|
|
|
|
// fmtBytesExact renders the exact byte count grouped by spaces ("8 590 000 000 bytes"), so a
|
|
// 50 MB delta on the PBS DR datastore is visible next to the 0.1 GB-granular UsedStr (R-92).
|
|
func fmtBytesExact(b int64) string {
|
|
neg := b < 0
|
|
if neg {
|
|
b = -b
|
|
}
|
|
digits := fmt.Sprintf("%d", b)
|
|
var out []byte
|
|
for i := range digits {
|
|
if i > 0 && (len(digits)-i)%3 == 0 {
|
|
out = append(out, ' ')
|
|
}
|
|
out = append(out, digits[i])
|
|
}
|
|
if neg {
|
|
return "-" + string(out) + " bytes"
|
|
}
|
|
return string(out) + " bytes"
|
|
}
|
|
|
|
// pbsdrTile is the compact Dashboard PBS gauge: fill %, band-colored; "n/a" when unavailable.
|
|
type pbsdrTile struct {
|
|
Unavailable bool
|
|
FillPercent float64
|
|
Band string
|
|
Degraded bool
|
|
}
|
|
|
|
// pbsdrBoxData builds the PBS-DR panel view. Nil provider → Configured:false ("not configured"). Never polls.
|
|
func (s *Server) pbsdrBoxData() pbsdrBoxView {
|
|
if s.pbsdrBox == nil {
|
|
return pbsdrBoxView{Configured: false}
|
|
}
|
|
view := pbsdrBoxView{Configured: true}
|
|
snap, ok := s.pbsdrBox()
|
|
if !ok {
|
|
view.Pending = true // configured, first poll pending
|
|
return view
|
|
}
|
|
view.State = snap.State
|
|
view.FetchedAt = snap.FetchedAt
|
|
switch snap.State {
|
|
case monitor.PBSStateUnavailable:
|
|
view.Unavailable = true
|
|
case monitor.PBSStateDegraded:
|
|
view.Degraded = true
|
|
if snap.CapacityBytes > 0 { // last-known values persist across a degraded poll
|
|
view.HasFill = true
|
|
view.CapacityStr = fmtBytesGB(snap.CapacityBytes)
|
|
view.UsedStr = fmtBytesGB(snap.UsedBytes)
|
|
view.UsedExact = fmtBytesExact(snap.UsedBytes)
|
|
view.FillPercent = snap.FillPercent
|
|
view.FillBand = snap.FillBand
|
|
}
|
|
default: // ok
|
|
view.HasFill = true
|
|
view.CapacityStr = fmtBytesGB(snap.CapacityBytes)
|
|
view.UsedStr = fmtBytesGB(snap.UsedBytes)
|
|
view.UsedExact = fmtBytesExact(snap.UsedBytes)
|
|
view.FillPercent = snap.FillPercent
|
|
view.FillBand = snap.FillBand
|
|
}
|
|
return view
|
|
}
|
|
|
|
// pbsdrBoxTile builds the Dashboard PBS gauge, or nil when there is nothing to show (no provider, or the
|
|
// first poll hasn't landed). Unavailable → a neutral "n/a" gauge (never a fake 0%).
|
|
func (s *Server) pbsdrBoxTile() *pbsdrTile {
|
|
if s.pbsdrBox == nil {
|
|
return nil
|
|
}
|
|
snap, ok := s.pbsdrBox()
|
|
if !ok {
|
|
return nil
|
|
}
|
|
if snap.State == monitor.PBSStateUnavailable {
|
|
return &pbsdrTile{Unavailable: true}
|
|
}
|
|
return &pbsdrTile{
|
|
FillPercent: snap.FillPercent,
|
|
Band: snap.FillBand,
|
|
Degraded: snap.State == monitor.PBSStateDegraded,
|
|
}
|
|
}
|