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

@@ -33,6 +33,7 @@ const (
Maglev_WatchEvents_FullMethodName = "/maglev.Maglev/WatchEvents"
Maglev_CheckConfig_FullMethodName = "/maglev.Maglev/CheckConfig"
Maglev_ReloadConfig_FullMethodName = "/maglev.Maglev/ReloadConfig"
Maglev_GetVPPInfo_FullMethodName = "/maglev.Maglev/GetVPPInfo"
)
// MaglevClient is the client API for Maglev service.
@@ -55,6 +56,7 @@ type MaglevClient interface {
WatchEvents(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error)
CheckConfig(ctx context.Context, in *CheckConfigRequest, opts ...grpc.CallOption) (*CheckConfigResponse, error)
ReloadConfig(ctx context.Context, in *ReloadConfigRequest, opts ...grpc.CallOption) (*ReloadConfigResponse, error)
GetVPPInfo(ctx context.Context, in *GetVPPInfoRequest, opts ...grpc.CallOption) (*VPPInfo, error)
}
type maglevClient struct {
@@ -214,6 +216,16 @@ func (c *maglevClient) ReloadConfig(ctx context.Context, in *ReloadConfigRequest
return out, nil
}
func (c *maglevClient) GetVPPInfo(ctx context.Context, in *GetVPPInfoRequest, opts ...grpc.CallOption) (*VPPInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(VPPInfo)
err := c.cc.Invoke(ctx, Maglev_GetVPPInfo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// MaglevServer is the server API for Maglev service.
// All implementations must embed UnimplementedMaglevServer
// for forward compatibility.
@@ -234,6 +246,7 @@ type MaglevServer interface {
WatchEvents(*WatchRequest, grpc.ServerStreamingServer[Event]) error
CheckConfig(context.Context, *CheckConfigRequest) (*CheckConfigResponse, error)
ReloadConfig(context.Context, *ReloadConfigRequest) (*ReloadConfigResponse, error)
GetVPPInfo(context.Context, *GetVPPInfoRequest) (*VPPInfo, error)
mustEmbedUnimplementedMaglevServer()
}
@@ -286,6 +299,9 @@ func (UnimplementedMaglevServer) CheckConfig(context.Context, *CheckConfigReques
func (UnimplementedMaglevServer) ReloadConfig(context.Context, *ReloadConfigRequest) (*ReloadConfigResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ReloadConfig not implemented")
}
func (UnimplementedMaglevServer) GetVPPInfo(context.Context, *GetVPPInfoRequest) (*VPPInfo, error) {
return nil, status.Error(codes.Unimplemented, "method GetVPPInfo not implemented")
}
func (UnimplementedMaglevServer) mustEmbedUnimplementedMaglevServer() {}
func (UnimplementedMaglevServer) testEmbeddedByValue() {}
@@ -552,6 +568,24 @@ func _Maglev_ReloadConfig_Handler(srv interface{}, ctx context.Context, dec func
return interceptor(ctx, in, info, handler)
}
func _Maglev_GetVPPInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetVPPInfoRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaglevServer).GetVPPInfo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maglev_GetVPPInfo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaglevServer).GetVPPInfo(ctx, req.(*GetVPPInfoRequest))
}
return interceptor(ctx, in, info, handler)
}
// Maglev_ServiceDesc is the grpc.ServiceDesc for Maglev service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -611,6 +645,10 @@ var Maglev_ServiceDesc = grpc.ServiceDesc{
MethodName: "ReloadConfig",
Handler: _Maglev_ReloadConfig_Handler,
},
{
MethodName: "GetVPPInfo",
Handler: _Maglev_GetVPPInfo_Handler,
},
},
Streams: []grpc.StreamDesc{
{