VPP client (internal/vpp/)
- New package managing connections to both VPP API and stats sockets,
treated as a unit: if either drops, both are torn down and
re-established together.
- Run() loop: connect, fetch version via vpe.ShowVersion, read
/sys/boottime from the stats segment, log vpp-connect, then monitor
with control_ping every 10s. On failure, disconnect both, retry
after 5s.
- Registers as client name "vpp-maglev" (visible in VPP's
"show api clients").
- Flags: --vpp-api-addr (default /run/vpp/api.sock) and
--vpp-stats-addr (default /run/vpp/stats.sock). Empty api addr
disables VPP integration entirely.
gRPC / proto
- Add GetVPPInfo RPC returning VPPInfo: version, build_date,
build_directory, pid, boottime_ns, connecttime_ns. Both times are
unix timestamps in nanoseconds — the client computes durations
locally for display.
- Returns codes.Unavailable if VPP is disabled or not connected.
maglevc
- Add 'show vpp info' command displaying version, build-date,
build-dir, vpp-pid, vpp-boottime (with duration), and connected
time (with duration).
Prometheus metrics (internal/metrics/, cmd/maglevd/)
- New --metrics-addr flag (default :9091, env MAGLEV_METRICS_ADDR)
serving /metrics via promhttp.
- Gauge metrics scraped on demand via a custom prometheus.Collector:
maglev_backend_state, maglev_backend_health, maglev_backend_enabled,
maglev_frontend_pool_backend_weight.
- Inline counter/histogram metrics updated per probe:
maglev_probe_total (by backend, type, result, code),
maglev_probe_duration_seconds (by backend, type),
maglev_backend_transitions_total (by backend, from, to).
- StateSource interface in metrics package breaks the import cycle
with checker; checker.Checker satisfies it via GetBackendInfo.
Integration tests
- Run maglevd inside a containerlab node (debian:trixie-slim with
build/ bind-mounted) instead of on the host. Eliminates port
collisions with any host maglevd.
- maglevc commands run via docker exec into the maglevd container.
- Add 6 Prometheus test cases: endpoint reachable, all backends
report state=up, probe counters non-zero, duration histogram
populated, pool weights correct, transition counters present.
maglevc
- Rewrite '?' handler (birdc-style): show full command paths from current
position to every leaf, right-aligned help column, dynamic slot values
displayed as an indented block when cursor is at a slot position.
- Collapse show frontends/frontend, backends/backend, healthchecks/healthcheck
into single plural-noun nodes with an optional <name> slot. Allows
'sh ba' (list all) and 'sh ba nginx0' (show one) without ambiguity.
- Add 'config reload' command.
- Fix tabwriter ANSI alignment: continuation lines in transition output
now carry the same label() byte overhead as the header line.
- Fix broken Walk for 'set frontend' command: setFrontendPoolName and
setWeightValue were fixed-word nodes that couldn't capture user input;
mark them as slot nodes with dynNone.
- Add tree_test.go covering expandPaths, cycle detection, prefix matching,
and the full weight-command walk.
gRPC / proto
- Add ReloadConfig RPC: checks config then applies it to the running
checker, returning ok/parse_error/semantic_error/reload_error.
- Add logging to CheckConfig (config-check-start/config-check-done at
INFO level).
maglevd
- SIGHUP handler now calls maglevServer.TriggerReload(), sharing the
same code path as the gRPC ReloadConfig RPC.
docs
- Collapse show command documentation to use [<name>] optional syntax.
- Remove developer-facing 'Command tree and parser' section.
- Document 'config reload'.
gRPC / proto
- Rename WatchBackendEvents → WatchEvents; return a stream of Event
oneof (LogEvent, BackendEvent, FrontendEvent) with optional filter
flags (log, log_level, backend, frontend)
- Add EnableBackend, DisableBackend, SetFrontendPoolBackendWeight RPCs
- Rename PauseResumeRequest → BackendRequest
- Add CheckConfig RPC returning ok/parse_error/semantic_error
maglevd
- Route slog through a LogBroadcaster (slog.Handler) so WatchEvents
subscribers can receive structured log records independently of the
daemon's own --log-level
- Add --reflection flag (default true) to toggle gRPC server reflection
- Add --check flag: validates config file and exits 0/1/2
- SIGHUP: use config.Check before applying reload; log parse vs semantic
error separately; refuse reload on any error
- Rename default config path /etc/maglev → /etc/vpp-maglev
maglevc
- Add 'watch events [num <n>] [log [level <level>]] [backend] [frontend]'
command; prints compact protojson, stops on any keypress or Ctrl-C;
uses cbreak mode (not raw) so output post-processing is preserved
- Add 'set backend <name> enable|disable'
- Add 'set frontend <name> pool <pool> backend <name> weight <0-100>'
- Add 'config check' command
Debian packaging
- Rename service unit to vpp-maglevd.service
- Rename conffiles to /etc/default/vpp-maglev and /etc/vpp-maglev/
- Create maglevd system user/group in postinst; add to vpp group if present
- Add postrm; add adduser to Depends
- Replaced flat `backends: [...]` list on frontends with an ordered `pools:`
list; each pool has a name and a map of backends with per-pool weights (0–100,
default 100). Pools express priority: first pool with a healthy backend wins.
- Removed global backend weight (was on the backend, now lives in the pool).
- Config validation enforces non-empty pools, non-empty pool names, weight
range, and consistent address families across all pools of a frontend.
- Added `PoolBackendInfo { name, weight }` and changed `PoolInfo.backends` from
`repeated string` to `repeated PoolBackendInfo` so weights are visible over
the API.
- Full interactive shell with readline, tab completion, and `?` inline help.
- Command tree parser (Walk) handles fixed keywords and dynamic slot nodes;
prefix matching with exact-match priority.
- Commands: `show version/frontends/frontend/backends/backend/healthchecks/
healthcheck`, `set backend <name> pause|resume`, `quit`/`exit`.
- `show frontend` output is hierarchical (pools → backends) with per-backend
weights and `[disabled]` notation; pool section uses fixed-width formatting
so ANSI color codes don't corrupt tabwriter alignment.
- `-color` flag (default true) wraps static field labels in dark-blue ANSI;
works correctly with tabwriter because all labels carry identical-length
escape sequences.
- `cmd/version.go` package holds `version`, `commit`, `date` vars set at build
time via `-ldflags -X`.
- `make build` / `make build-amd64` / `make build-arm64` all inject
`VERSION=0.1.1`, `COMMIT_HASH` (from `git rev-parse --short HEAD`), and
`DATE` (UTC ISO-8601).
- `maglevc` prints version on interactive startup and exposes `show version`.
- `maglevd` logs version/commit/date at startup; `-version` flag prints and exits.
- `doHTTPProbe` was building a `https://` target URL even though TLS was already
applied to the connection inside `inNetns`. `http.Transport` then wrapped the
connection in a second TLS layer, producing "http: server gave HTTP response
to HTTPS client". Fixed by always using `http://` in the target URL.
- Added `TestHTTPSProbe` using `httptest.NewTLSServer` to cover the full path.
- New `docs/user-guide.md`: maglevd flags/signals, maglevc commands, shell
completion, and command-tree parser walkthrough.
- New `docs/healthchecks.md`: state machine, rise/fall model, probe intervals,
all transition events with log examples.
- Updated `docs/config-guide.md`: pools design, removed global weight from
backends, updated all examples.
- Updated `README.md`: packaging table, build paths, corrected binary locations
(`/usr/sbin/maglevd`), config filename (`.yaml`).
- `debian/` directory contains `control.in`, `maglevd.service`, `default.maglev`,
`maglev.yaml` (example config), `conffiles`, `postinst`, `prerm`.
- `debian/build-deb.sh` stages a package tree and calls `dpkg-deb`; emits
`build/vpp-maglev_<version>~<commit>_<arch>.deb`.
- Cross-compiles for amd64 and arm64 in one `make pkg-deb` invocation.
- `maglevd` installed to `/usr/sbin/`, `maglevc` to `/usr/bin/`.
- Service reads `MAGLEV_CONFIG` from `/etc/default/maglev`
(default: `/etc/maglev/maglev.yaml`).
- Man pages `maglevd(8)` and `maglevc(1)` live in `docs/` and are gzip'd into
the package.
- All build output goes to `build/<arch>/`; `build/` is gitignored.