Add GoVPP integration and GetVPPInfo gRPC call

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).
This commit is contained in:
2026-04-11 22:03:22 +02:00
parent 74448cf6d0
commit 3227263d68
13 changed files with 690 additions and 120 deletions

View File

@@ -13,6 +13,7 @@ import (
"git.ipng.ch/ipng/vpp-maglev/internal/checker"
"git.ipng.ch/ipng/vpp-maglev/internal/config"
"git.ipng.ch/ipng/vpp-maglev/internal/health"
"git.ipng.ch/ipng/vpp-maglev/internal/vpp"
)
// Server implements the MaglevServer gRPC interface.
@@ -22,16 +23,17 @@ type Server struct {
checker *checker.Checker
logs *LogBroadcaster
configPath string
vppClient *vpp.Client // nil when VPP integration is disabled
}
// NewServer creates a Server backed by the given Checker. logs may be nil, in
// which case log events are never sent to WatchEvents streams. configPath is
// used by CheckConfig to reload and validate the configuration file on demand.
// The provided context controls the lifetime of streaming RPCs: cancelling it
// closes all active WatchEvents streams so that grpc.Server.GracefulStop can
// complete.
func NewServer(ctx context.Context, c *checker.Checker, logs *LogBroadcaster, configPath string) *Server {
return &Server{ctx: ctx, checker: c, logs: logs, configPath: configPath}
// vppClient may be nil if VPP integration is disabled. The provided context
// controls the lifetime of streaming RPCs: cancelling it closes all active
// WatchEvents streams so that grpc.Server.GracefulStop can complete.
func NewServer(ctx context.Context, c *checker.Checker, logs *LogBroadcaster, configPath string, vppClient *vpp.Client) *Server {
return &Server{ctx: ctx, checker: c, logs: logs, configPath: configPath, vppClient: vppClient}
}
// ListFrontends returns the names of all configured frontends.
@@ -257,6 +259,29 @@ func (s *Server) doReloadConfig() *ReloadConfigResponse {
return &ReloadConfigResponse{Ok: true}
}
// GetVPPInfo returns VPP version and runtime information.
func (s *Server) GetVPPInfo(_ context.Context, _ *GetVPPInfoRequest) (*VPPInfo, error) {
if s.vppClient == nil {
return nil, status.Error(codes.Unavailable, "VPP integration is disabled")
}
info, err := s.vppClient.GetInfo()
if err != nil {
return nil, status.Errorf(codes.Unavailable, "%v", err)
}
var boottimeNs int64
if !info.BootTime.IsZero() {
boottimeNs = info.BootTime.UnixNano()
}
return &VPPInfo{
Version: info.Version,
BuildDate: info.BuildDate,
BuildDirectory: info.BuildDirectory,
Pid: info.PID,
BoottimeNs: boottimeNs,
ConnecttimeNs: info.ConnectedSince.UnixNano(),
}, nil
}
// ---- conversion helpers ----------------------------------------------------
func frontendToProto(name string, fe config.Frontend) *FrontendInfo {