Server statistics, multi-view record commands, and rc.3 fixes

Added:
- Server statistics: named's memory and CPU (from /proc), cache size,
  sockets and client counts against their limits (statistics channel,
  rndc status), host memory and load, and free disk space. Memory and
  CPU are kept over time as gauges (sum and sample count, averaged in
  timelines). Statistics page Server section, jarl stats server,
  GET /stats/server.
- jarl record add|set|delete|disable|enable --view a,b | all: the same
  change in each view, applied as one changeset.
- Blocking can be paused until resumed (jarl block pause indefinitely,
  "Until I resume", {"indefinitely": true}).

Changed:
- List options (allow-transfer, forwarders, ...) get their braces added
  when given without them.
- The dashboard lists named-checkconf's warnings about the running
  configuration; the CLI prints them after an apply.
- Stats page: tile and card rows fill their width, and the queries and
  failed answers charts share margins so their time axes line up.
- Dashboard: options have their own tile.
- 24-hour times in the web UI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-11 18:03:05 +01:00
1 parent b097e10bef
commit 7be0762f48
48 files changed
+1346 -128

No files matched your search

+36 -10
View File
@@ -15,19 +15,31 @@ import (
type Snapshot struct {
BootTime string
Values map[string]int64
// Gauges are named's current values (memory, cache size, sockets),
// not counters.
Gauges map[string]int64
Version string
ConfigTime string
}
// serverJSON is the part of /json/v1/server Jarl uses.
// serverJSON is the part of /json/v1 Jarl uses.
type serverJSON struct {
BootTime string `json:"boot-time"`
Opcodes map[string]int64 `json:"opcodes"`
RCodes map[string]int64 `json:"rcodes"`
QTypes map[string]int64 `json:"qtypes"`
NSStats map[string]int64 `json:"nsstats"`
Views map[string]struct {
BootTime string `json:"boot-time"`
ConfigTime string `json:"config-time"`
Version string `json:"version"`
SockStats map[string]int64 `json:"sockstats"`
Memory struct {
InUse int64 `json:"InUse"`
} `json:"memory"`
Opcodes map[string]int64 `json:"opcodes"`
RCodes map[string]int64 `json:"rcodes"`
QTypes map[string]int64 `json:"qtypes"`
NSStats map[string]int64 `json:"nsstats"`
Views map[string]struct {
Resolver struct {
Stats map[string]int64 `json:"stats"`
CacheStats map[string]int64 `json:"cachestats"`
Cache map[string]int64 `json:"cache"` // RRsets in the cache by type
} `json:"resolver"`
} `json:"views"`
}
@@ -77,7 +89,21 @@ func Flatten(body []byte) (*Snapshot, error) {
v[name] = n
}
}
g := map[string]int64{"named:mem": j.Memory.InUse,
"named:recursion-highwater": j.NSStats["RecursHighwater"], "named:tcp-highwater": j.NSStats["TCPConnHighWater"]}
for k, n := range j.SockStats {
switch {
case strings.HasPrefix(k, "UDP") && strings.HasSuffix(k, "Active"):
g["sockets:udp"] += n
case strings.HasPrefix(k, "TCP") && strings.HasSuffix(k, "Active"):
g["sockets:tcp"] += n
}
}
for _, view := range j.Views {
g["cache:mem"] += view.Resolver.CacheStats["TreeMemInUse"] + view.Resolver.CacheStats["HeapMemInUse"]
for _, n := range view.Resolver.Cache {
g["cache:rrsets"] += n
}
for k, name := range rttKeys {
v[name] += view.Resolver.Stats[k]
}
@@ -92,7 +118,7 @@ func Flatten(body []byte) (*Snapshot, error) {
delete(v, k)
}
}
return &Snapshot{BootTime: j.BootTime, Values: v}, nil
return &Snapshot{BootTime: j.BootTime, Values: v, Gauges: g, Version: j.Version, ConfigTime: j.ConfigTime}, nil
}
// Delta returns the change from prev to cur. When named restarted (its
@@ -120,13 +146,13 @@ func Delta(prev, cur *Snapshot) map[string]int64 {
// Poller reads named's statistics channel.
type Poller struct {
URL string // e.g. http://127.0.0.1:8058/json/v1/server
URL string // e.g. http://127.0.0.1:8058/json/v1 (server, memory and sockets)
Client *http.Client
}
// NewPoller returns a poller for a statistics channel address.
func NewPoller(addr string) *Poller {
return &Poller{URL: "http://" + addr + "/json/v1/server", Client: &http.Client{Timeout: 10 * time.Second}}
return &Poller{URL: "http://" + addr + "/json/v1", Client: &http.Client{Timeout: 10 * time.Second}}
}
// Poll fetches a snapshot.