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:
1 parent
6ad1bb9a0d
commit
5e6c61cc8e
31 files changed
+358
-39
No files matched your search
@@ -2,13 +2,14 @@ package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"steward/internal/model"
|
||||
)
|
||||
|
||||
const leaseCols = `ip, subnet_id, mac, client_id, hostname, state, start, end, interface, updated`
|
||||
const leaseCols = `ip, subnet_id, mac, client_id, hostname, state, start, end, interface, updated, last_seen`
|
||||
|
||||
// LoadLeases reads every lease.
|
||||
func (s *Store) LoadLeases(ctx context.Context) ([]*model.Lease, error) {
|
||||
@@ -21,11 +22,13 @@ func (s *Store) LoadLeases(ctx context.Context) ([]*model.Lease, error) {
|
||||
for rows.Next() {
|
||||
l := &model.Lease{}
|
||||
var ip, start, end, updated string
|
||||
if err := rows.Scan(&ip, &l.SubnetID, &l.MAC, &l.ClientID, &l.Hostname, &l.State, &start, &end, &l.Interface, &updated); err != nil {
|
||||
var seen sql.NullString
|
||||
if err := rows.Scan(&ip, &l.SubnetID, &l.MAC, &l.ClientID, &l.Hostname, &l.State, &start, &end, &l.Interface, &updated, &seen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.IP, _ = netip.ParseAddr(ip)
|
||||
l.Start, l.End, l.Updated = parseTS(start), parseTS(end), parseTS(updated)
|
||||
l.LastSeen = parseTSPtr(seen)
|
||||
out = append(out, l)
|
||||
}
|
||||
return out, rows.Err()
|
||||
@@ -46,10 +49,26 @@ func (s *Store) PutLeases(ctx context.Context, ls []*model.Lease) error {
|
||||
}
|
||||
defer tx.Rollback()
|
||||
for _, l := range ls {
|
||||
if _, err := tx.ExecContext(ctx, `INSERT INTO leases (`+leaseCols+`) VALUES (?,?,?,?,?,?,?,?,?,?)
|
||||
if _, err := tx.ExecContext(ctx, `INSERT INTO leases (`+leaseCols+`) VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
||||
ON CONFLICT(ip) DO UPDATE SET subnet_id = excluded.subnet_id, mac = excluded.mac, client_id = excluded.client_id, hostname = excluded.hostname,
|
||||
state = excluded.state, start = excluded.start, end = excluded.end, interface = excluded.interface, updated = excluded.updated`,
|
||||
l.IP.String(), l.SubnetID, l.MAC, l.ClientID, l.Hostname, l.State, ts(l.Start), ts(l.End), l.Interface, ts(l.Updated)); err != nil {
|
||||
state = excluded.state, start = excluded.start, end = excluded.end, interface = excluded.interface, updated = excluded.updated, last_seen = excluded.last_seen`,
|
||||
l.IP.String(), l.SubnetID, l.MAC, l.ClientID, l.Hostname, l.State, ts(l.Start), ts(l.End), l.Interface, ts(l.Updated), tsPtr(l.LastSeen)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// SetLastSeen records when leases' clients answered a ping, in one
|
||||
// transaction.
|
||||
func (s *Store) SetLastSeen(ctx context.Context, seen map[netip.Addr]time.Time) error {
|
||||
tx, err := s.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
for ip, t := range seen {
|
||||
if _, err := tx.ExecContext(ctx, `UPDATE leases SET last_seen = ? WHERE ip = ?`, ts(t), ip.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +197,10 @@ ALTER TABLE tokens ADD COLUMN prev_expires_at TEXT;
|
||||
// 3: disabled reservations.
|
||||
`
|
||||
ALTER TABLE reservations ADD COLUMN disabled INTEGER NOT NULL DEFAULT 0;
|
||||
`,
|
||||
// 4: when a lease's client last answered a ping.
|
||||
`
|
||||
ALTER TABLE leases ADD COLUMN last_seen TEXT;
|
||||
`,
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user