Hidden interfaces, last-seen pings, activity by subnet

- interfaces.exclude in steward.yaml (names or shell patterns) leaves
  interfaces out of GET /interfaces, the web UI's picker and
  'steward interface list'. Subnets on them are still served.
- With the ping check on, active leases are pinged every minute; a
  client that answers gets a last-seen time (leases.last_seen,
  migration 4). Not answering never takes a client offline.
- The activity log filters by subnet (--subnet, the Activity page,
  GET /events?subnet=).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-18 17:50:21 +01:00
1 parent 6ad1bb9a0d
commit 5e6c61cc8e
31 files changed
+358 -39

No files matched your search

+38
View File
@@ -461,3 +461,41 @@ func TestDisabledReservation(t *testing.T) {
t.Errorf("the printer got %s from a full pool", out.Packet.YIAddr)
}
}
func TestLastSeen(t *testing.T) {
h := newHarness(t, nil)
up := h.lease("eth0", mac(1)).YIAddr
quiet := h.lease("eth0", mac(2)).YIAddr
h.ping.answers[up] = true
h.s.CheckSeen(context.Background())
if l := h.s.Lease(up); l.LastSeen == nil || !l.LastSeen.Equal(h.clock) {
t.Errorf("answering client: %+v", l)
}
// One that never answers has no last-seen time, and stays active.
if l := h.s.Lease(quiet); l.LastSeen != nil || !l.ActiveAt(h.clock) {
t.Errorf("quiet client: %+v", l)
}
// Later, the time stays when it stops answering, and survives a
// renewal and a restart.
seen := h.clock
h.clock = h.clock.Add(10 * time.Minute)
h.ping.answers[up] = false
h.s.CheckSeen(context.Background())
h.lease("eth0", mac(1))
if l := h.s.Lease(up); l.LastSeen == nil || !l.LastSeen.Equal(seen) {
t.Errorf("after a renewal: %+v", l)
}
ls, _ := h.st.LoadLeases(context.Background())
for _, l := range ls {
if l.IP == up && (l.LastSeen == nil || !l.LastSeen.Equal(seen)) {
t.Errorf("stored: %+v", l)
}
}
// Expired leases aren't pinged.
h.clock = h.clock.Add(48 * time.Hour)
h.ping.pinged = nil
h.s.CheckSeen(context.Background())
if len(h.ping.pinged) != 0 {
t.Errorf("pinged %v", h.ping.pinged)
}
}