Add WatchEvents, enable/disable/weight RPCs, and config check

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
This commit is contained in:
2026-04-11 16:42:11 +02:00
parent d612086a5f
commit 58391f5463
26 changed files with 1969 additions and 400 deletions

View File

@@ -19,15 +19,19 @@ import (
const _ = grpc.SupportPackageIsVersion9
const (
Maglev_ListFrontends_FullMethodName = "/maglev.Maglev/ListFrontends"
Maglev_GetFrontend_FullMethodName = "/maglev.Maglev/GetFrontend"
Maglev_ListBackends_FullMethodName = "/maglev.Maglev/ListBackends"
Maglev_GetBackend_FullMethodName = "/maglev.Maglev/GetBackend"
Maglev_PauseBackend_FullMethodName = "/maglev.Maglev/PauseBackend"
Maglev_ResumeBackend_FullMethodName = "/maglev.Maglev/ResumeBackend"
Maglev_ListHealthChecks_FullMethodName = "/maglev.Maglev/ListHealthChecks"
Maglev_GetHealthCheck_FullMethodName = "/maglev.Maglev/GetHealthCheck"
Maglev_WatchBackendEvents_FullMethodName = "/maglev.Maglev/WatchBackendEvents"
Maglev_ListFrontends_FullMethodName = "/maglev.Maglev/ListFrontends"
Maglev_GetFrontend_FullMethodName = "/maglev.Maglev/GetFrontend"
Maglev_ListBackends_FullMethodName = "/maglev.Maglev/ListBackends"
Maglev_GetBackend_FullMethodName = "/maglev.Maglev/GetBackend"
Maglev_PauseBackend_FullMethodName = "/maglev.Maglev/PauseBackend"
Maglev_ResumeBackend_FullMethodName = "/maglev.Maglev/ResumeBackend"
Maglev_EnableBackend_FullMethodName = "/maglev.Maglev/EnableBackend"
Maglev_DisableBackend_FullMethodName = "/maglev.Maglev/DisableBackend"
Maglev_ListHealthChecks_FullMethodName = "/maglev.Maglev/ListHealthChecks"
Maglev_GetHealthCheck_FullMethodName = "/maglev.Maglev/GetHealthCheck"
Maglev_SetFrontendPoolBackendWeight_FullMethodName = "/maglev.Maglev/SetFrontendPoolBackendWeight"
Maglev_WatchEvents_FullMethodName = "/maglev.Maglev/WatchEvents"
Maglev_CheckConfig_FullMethodName = "/maglev.Maglev/CheckConfig"
)
// MaglevClient is the client API for Maglev service.
@@ -40,11 +44,15 @@ type MaglevClient interface {
GetFrontend(ctx context.Context, in *GetFrontendRequest, opts ...grpc.CallOption) (*FrontendInfo, error)
ListBackends(ctx context.Context, in *ListBackendsRequest, opts ...grpc.CallOption) (*ListBackendsResponse, error)
GetBackend(ctx context.Context, in *GetBackendRequest, opts ...grpc.CallOption) (*BackendInfo, error)
PauseBackend(ctx context.Context, in *PauseResumeRequest, opts ...grpc.CallOption) (*BackendInfo, error)
ResumeBackend(ctx context.Context, in *PauseResumeRequest, opts ...grpc.CallOption) (*BackendInfo, error)
PauseBackend(ctx context.Context, in *BackendRequest, opts ...grpc.CallOption) (*BackendInfo, error)
ResumeBackend(ctx context.Context, in *BackendRequest, opts ...grpc.CallOption) (*BackendInfo, error)
EnableBackend(ctx context.Context, in *BackendRequest, opts ...grpc.CallOption) (*BackendInfo, error)
DisableBackend(ctx context.Context, in *BackendRequest, opts ...grpc.CallOption) (*BackendInfo, error)
ListHealthChecks(ctx context.Context, in *ListHealthChecksRequest, opts ...grpc.CallOption) (*ListHealthChecksResponse, error)
GetHealthCheck(ctx context.Context, in *GetHealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckInfo, error)
WatchBackendEvents(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BackendEvent], error)
SetFrontendPoolBackendWeight(ctx context.Context, in *SetWeightRequest, opts ...grpc.CallOption) (*FrontendInfo, error)
WatchEvents(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error)
CheckConfig(ctx context.Context, in *CheckConfigRequest, opts ...grpc.CallOption) (*CheckConfigResponse, error)
}
type maglevClient struct {
@@ -95,7 +103,7 @@ func (c *maglevClient) GetBackend(ctx context.Context, in *GetBackendRequest, op
return out, nil
}
func (c *maglevClient) PauseBackend(ctx context.Context, in *PauseResumeRequest, opts ...grpc.CallOption) (*BackendInfo, error) {
func (c *maglevClient) PauseBackend(ctx context.Context, in *BackendRequest, opts ...grpc.CallOption) (*BackendInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(BackendInfo)
err := c.cc.Invoke(ctx, Maglev_PauseBackend_FullMethodName, in, out, cOpts...)
@@ -105,7 +113,7 @@ func (c *maglevClient) PauseBackend(ctx context.Context, in *PauseResumeRequest,
return out, nil
}
func (c *maglevClient) ResumeBackend(ctx context.Context, in *PauseResumeRequest, opts ...grpc.CallOption) (*BackendInfo, error) {
func (c *maglevClient) ResumeBackend(ctx context.Context, in *BackendRequest, opts ...grpc.CallOption) (*BackendInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(BackendInfo)
err := c.cc.Invoke(ctx, Maglev_ResumeBackend_FullMethodName, in, out, cOpts...)
@@ -115,6 +123,26 @@ func (c *maglevClient) ResumeBackend(ctx context.Context, in *PauseResumeRequest
return out, nil
}
func (c *maglevClient) EnableBackend(ctx context.Context, in *BackendRequest, opts ...grpc.CallOption) (*BackendInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(BackendInfo)
err := c.cc.Invoke(ctx, Maglev_EnableBackend_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *maglevClient) DisableBackend(ctx context.Context, in *BackendRequest, opts ...grpc.CallOption) (*BackendInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(BackendInfo)
err := c.cc.Invoke(ctx, Maglev_DisableBackend_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *maglevClient) ListHealthChecks(ctx context.Context, in *ListHealthChecksRequest, opts ...grpc.CallOption) (*ListHealthChecksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListHealthChecksResponse)
@@ -135,13 +163,23 @@ func (c *maglevClient) GetHealthCheck(ctx context.Context, in *GetHealthCheckReq
return out, nil
}
func (c *maglevClient) WatchBackendEvents(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[BackendEvent], error) {
func (c *maglevClient) SetFrontendPoolBackendWeight(ctx context.Context, in *SetWeightRequest, opts ...grpc.CallOption) (*FrontendInfo, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Maglev_ServiceDesc.Streams[0], Maglev_WatchBackendEvents_FullMethodName, cOpts...)
out := new(FrontendInfo)
err := c.cc.Invoke(ctx, Maglev_SetFrontendPoolBackendWeight_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[WatchRequest, BackendEvent]{ClientStream: stream}
return out, nil
}
func (c *maglevClient) WatchEvents(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Maglev_ServiceDesc.Streams[0], Maglev_WatchEvents_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[WatchRequest, Event]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
@@ -152,7 +190,17 @@ func (c *maglevClient) WatchBackendEvents(ctx context.Context, in *WatchRequest,
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Maglev_WatchBackendEventsClient = grpc.ServerStreamingClient[BackendEvent]
type Maglev_WatchEventsClient = grpc.ServerStreamingClient[Event]
func (c *maglevClient) CheckConfig(ctx context.Context, in *CheckConfigRequest, opts ...grpc.CallOption) (*CheckConfigResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CheckConfigResponse)
err := c.cc.Invoke(ctx, Maglev_CheckConfig_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
@@ -164,11 +212,15 @@ type MaglevServer interface {
GetFrontend(context.Context, *GetFrontendRequest) (*FrontendInfo, error)
ListBackends(context.Context, *ListBackendsRequest) (*ListBackendsResponse, error)
GetBackend(context.Context, *GetBackendRequest) (*BackendInfo, error)
PauseBackend(context.Context, *PauseResumeRequest) (*BackendInfo, error)
ResumeBackend(context.Context, *PauseResumeRequest) (*BackendInfo, error)
PauseBackend(context.Context, *BackendRequest) (*BackendInfo, error)
ResumeBackend(context.Context, *BackendRequest) (*BackendInfo, error)
EnableBackend(context.Context, *BackendRequest) (*BackendInfo, error)
DisableBackend(context.Context, *BackendRequest) (*BackendInfo, error)
ListHealthChecks(context.Context, *ListHealthChecksRequest) (*ListHealthChecksResponse, error)
GetHealthCheck(context.Context, *GetHealthCheckRequest) (*HealthCheckInfo, error)
WatchBackendEvents(*WatchRequest, grpc.ServerStreamingServer[BackendEvent]) error
SetFrontendPoolBackendWeight(context.Context, *SetWeightRequest) (*FrontendInfo, error)
WatchEvents(*WatchRequest, grpc.ServerStreamingServer[Event]) error
CheckConfig(context.Context, *CheckConfigRequest) (*CheckConfigResponse, error)
mustEmbedUnimplementedMaglevServer()
}
@@ -191,20 +243,32 @@ func (UnimplementedMaglevServer) ListBackends(context.Context, *ListBackendsRequ
func (UnimplementedMaglevServer) GetBackend(context.Context, *GetBackendRequest) (*BackendInfo, error) {
return nil, status.Error(codes.Unimplemented, "method GetBackend not implemented")
}
func (UnimplementedMaglevServer) PauseBackend(context.Context, *PauseResumeRequest) (*BackendInfo, error) {
func (UnimplementedMaglevServer) PauseBackend(context.Context, *BackendRequest) (*BackendInfo, error) {
return nil, status.Error(codes.Unimplemented, "method PauseBackend not implemented")
}
func (UnimplementedMaglevServer) ResumeBackend(context.Context, *PauseResumeRequest) (*BackendInfo, error) {
func (UnimplementedMaglevServer) ResumeBackend(context.Context, *BackendRequest) (*BackendInfo, error) {
return nil, status.Error(codes.Unimplemented, "method ResumeBackend not implemented")
}
func (UnimplementedMaglevServer) EnableBackend(context.Context, *BackendRequest) (*BackendInfo, error) {
return nil, status.Error(codes.Unimplemented, "method EnableBackend not implemented")
}
func (UnimplementedMaglevServer) DisableBackend(context.Context, *BackendRequest) (*BackendInfo, error) {
return nil, status.Error(codes.Unimplemented, "method DisableBackend not implemented")
}
func (UnimplementedMaglevServer) ListHealthChecks(context.Context, *ListHealthChecksRequest) (*ListHealthChecksResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListHealthChecks not implemented")
}
func (UnimplementedMaglevServer) GetHealthCheck(context.Context, *GetHealthCheckRequest) (*HealthCheckInfo, error) {
return nil, status.Error(codes.Unimplemented, "method GetHealthCheck not implemented")
}
func (UnimplementedMaglevServer) WatchBackendEvents(*WatchRequest, grpc.ServerStreamingServer[BackendEvent]) error {
return status.Error(codes.Unimplemented, "method WatchBackendEvents not implemented")
func (UnimplementedMaglevServer) SetFrontendPoolBackendWeight(context.Context, *SetWeightRequest) (*FrontendInfo, error) {
return nil, status.Error(codes.Unimplemented, "method SetFrontendPoolBackendWeight not implemented")
}
func (UnimplementedMaglevServer) WatchEvents(*WatchRequest, grpc.ServerStreamingServer[Event]) error {
return status.Error(codes.Unimplemented, "method WatchEvents not implemented")
}
func (UnimplementedMaglevServer) CheckConfig(context.Context, *CheckConfigRequest) (*CheckConfigResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CheckConfig not implemented")
}
func (UnimplementedMaglevServer) mustEmbedUnimplementedMaglevServer() {}
func (UnimplementedMaglevServer) testEmbeddedByValue() {}
@@ -300,7 +364,7 @@ func _Maglev_GetBackend_Handler(srv interface{}, ctx context.Context, dec func(i
}
func _Maglev_PauseBackend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PauseResumeRequest)
in := new(BackendRequest)
if err := dec(in); err != nil {
return nil, err
}
@@ -312,13 +376,13 @@ func _Maglev_PauseBackend_Handler(srv interface{}, ctx context.Context, dec func
FullMethod: Maglev_PauseBackend_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaglevServer).PauseBackend(ctx, req.(*PauseResumeRequest))
return srv.(MaglevServer).PauseBackend(ctx, req.(*BackendRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maglev_ResumeBackend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PauseResumeRequest)
in := new(BackendRequest)
if err := dec(in); err != nil {
return nil, err
}
@@ -330,7 +394,43 @@ func _Maglev_ResumeBackend_Handler(srv interface{}, ctx context.Context, dec fun
FullMethod: Maglev_ResumeBackend_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaglevServer).ResumeBackend(ctx, req.(*PauseResumeRequest))
return srv.(MaglevServer).ResumeBackend(ctx, req.(*BackendRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maglev_EnableBackend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BackendRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaglevServer).EnableBackend(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maglev_EnableBackend_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaglevServer).EnableBackend(ctx, req.(*BackendRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maglev_DisableBackend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BackendRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaglevServer).DisableBackend(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maglev_DisableBackend_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaglevServer).DisableBackend(ctx, req.(*BackendRequest))
}
return interceptor(ctx, in, info, handler)
}
@@ -371,16 +471,52 @@ func _Maglev_GetHealthCheck_Handler(srv interface{}, ctx context.Context, dec fu
return interceptor(ctx, in, info, handler)
}
func _Maglev_WatchBackendEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
func _Maglev_SetFrontendPoolBackendWeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetWeightRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaglevServer).SetFrontendPoolBackendWeight(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maglev_SetFrontendPoolBackendWeight_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaglevServer).SetFrontendPoolBackendWeight(ctx, req.(*SetWeightRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Maglev_WatchEvents_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(WatchRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(MaglevServer).WatchBackendEvents(m, &grpc.GenericServerStream[WatchRequest, BackendEvent]{ServerStream: stream})
return srv.(MaglevServer).WatchEvents(m, &grpc.GenericServerStream[WatchRequest, Event]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Maglev_WatchBackendEventsServer = grpc.ServerStreamingServer[BackendEvent]
type Maglev_WatchEventsServer = grpc.ServerStreamingServer[Event]
func _Maglev_CheckConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CheckConfigRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MaglevServer).CheckConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Maglev_CheckConfig_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MaglevServer).CheckConfig(ctx, req.(*CheckConfigRequest))
}
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,
@@ -413,6 +549,14 @@ var Maglev_ServiceDesc = grpc.ServiceDesc{
MethodName: "ResumeBackend",
Handler: _Maglev_ResumeBackend_Handler,
},
{
MethodName: "EnableBackend",
Handler: _Maglev_EnableBackend_Handler,
},
{
MethodName: "DisableBackend",
Handler: _Maglev_DisableBackend_Handler,
},
{
MethodName: "ListHealthChecks",
Handler: _Maglev_ListHealthChecks_Handler,
@@ -421,11 +565,19 @@ var Maglev_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetHealthCheck",
Handler: _Maglev_GetHealthCheck_Handler,
},
{
MethodName: "SetFrontendPoolBackendWeight",
Handler: _Maglev_SetFrontendPoolBackendWeight_Handler,
},
{
MethodName: "CheckConfig",
Handler: _Maglev_CheckConfig_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "WatchBackendEvents",
Handler: _Maglev_WatchBackendEvents_Handler,
StreamName: "WatchEvents",
Handler: _Maglev_WatchEvents_Handler,
ServerStreams: true,
},
},