Dataplane reconcile fixes; LB counters cleanup; SPA scope cookie

Checker / reload:
- Reload's update-in-place branch now mirrors b.Address onto the
  runtime health.Backend. Without this, GetBackend kept returning
  the pre-reload address indefinitely after a config edit that
  touched addresses but not healthcheck settings — the VPP sync
  path reads cfg.Backends directly so the dataplane moved on
  while the gRPC and SPA view stayed wedged on the old IPv4/IPv6.

Sync (internal/vpp/lbsync.go):
- reconcileVIP now detects encap mismatch in addition to
  src-ip-sticky mismatch and takes the full tear-down / re-add
  path via a new shared recreateVIP helper. Triggered when every
  backend flips address family (gre4 <-> gre6) and the existing
  VIP can no longer accept new ASes — previously the sync wedged
  with 'Invalid address family' until a full maglevd restart.
- setASWeight is issued whenever the state machine requests
  flush (a.Flush=true), not only on the weight-value transition
  edge. Fixes the case where a backend reached StateDisabled
  after its effective weight had already been drained to 0 by
  pool failover — the sticky-cache entries pointing at it were
  previously never cleared.

maglev-frontend:
- signal.Ignore(SIGHUP) so a controlling-terminal disconnect
  doesn't kill the daemon.
- debian/vpp-maglev.service grants CAP_SYS_ADMIN in addition to
  CAP_NET_RAW so setns(CLONE_NEWNET) can join the healthcheck
  netns. Comment documents the 'operation not permitted' symptom
  and notes the knob can be dropped if the deployment doesn't use
  the 'netns:' healthcheck option.

LB plugin counters (internal/vpp/lbstats.go + friends):
- Fix the VIP counter regex: the LB plugin registers
  vlib_simple_counter_main_t names without a leading '/'
  (vlib_validate_simple_counter in counter.c:50 uses cm->name
  verbatim; only entries that set cm->stat_segment_name get a
  slash). first/next/untracked/no-server now read through as
  live values instead of zero.
- Drop the per-backend FIB counter block end-to-end (proto,
  grpcapi, metrics, vpp.Client, lbstats, maglevc). Traced from
  lb/node.c:558 into ip{4,6}_forward.h:141 — the LB plugin
  forwards by writing adj_index[VLIB_TX] directly and bypassing
  ip{4,6}_lookup_inline, which is the only path that increments
  lbm_to_counters. The backend's FIB load_balance stats_index
  literally never ticks for LB-forwarded traffic, so the column
  was always zero and misleading. docs/implementation/TODO
  records the full investigation and the recommended upstream
  path (new lb_as_stats_dump API message) for when we're ready
  to carry that VPP patch.
- maglevc show vpp lb counters: plain-text tabular headers.
  label() wraps strings in ANSI escapes (~11 bytes of overhead),
  but tabwriter counts bytes, not rendered width — so a header
  row with label()'d cells and data rows with plain cells drifts
  column alignment on every row. color.go comment now spells
  out the constraint: label() only works when column N is
  wrapped identically in every row (key-value layouts are fine,
  multi-column tables with header-only labelling are not).

SPA:
- stores/scope.ts is cookie-backed (maglev_scope, 1 year,
  SameSite=Lax). App.tsx hydrates from the cookie then validates
  against the fetched snapshots: a cookie referencing a maglevd
  that no longer exists falls through to snaps[0] instead of
  leaving the user on a ghost selection.
- components/Flash.tsx wraps props.value in createMemo. Solid's
  on() fires its callback on every dep notification, not on
  value change — source is right in solid-js/dist/solid.js:460,
  no equality check. Without the memo, flipping scope between
  two 'connected' maglevds (or any other cross-store reactive
  re-eval that doesn't actually change the concrete string)
  replays the animation every time. createMemo's default ===
  dedupe fixes it in one place for every Flash consumer,
  superseding the local createMemo workaround we'd added in
  BackendRow earlier.
This commit is contained in:
2026-04-14 14:39:52 +02:00
parent 4288e22b71
commit 224167ce39
20 changed files with 435 additions and 471 deletions

View File

@@ -94,6 +94,12 @@ func run() error {
ReadHeaderTimeout: 10 * time.Second, ReadHeaderTimeout: 10 * time.Second,
} }
// Ignore SIGHUP so a controlling-terminal disconnect (or any
// stray process-group SIGHUP) doesn't kill the daemon — the
// default Go handler terminates the process with "Hangup",
// which is the wrong behaviour for a long-running network
// service. SIGTERM / SIGINT remain the clean-shutdown signals.
signal.Ignore(syscall.SIGHUP)
sigCh := make(chan os.Signal, 1) sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT) signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/x-icon" href="/view/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/view/favicon.ico" />
<title>maglev</title> <title>maglev</title>
<script type="module" crossorigin src="/view/assets/index-DCJJqBMY.js"></script> <script type="module" crossorigin src="/view/assets/index-3m4Pjc8_.js"></script>
<link rel="stylesheet" crossorigin href="/view/assets/index-3BvNJ7QB.css"> <link rel="stylesheet" crossorigin href="/view/assets/index-3BvNJ7QB.css">
</head> </head>
<body> <body>

View File

@@ -19,7 +19,14 @@ const App: Component = () => {
const [snaps, ver] = await Promise.all([fetchAllState(), fetchVersion()]); const [snaps, ver] = await Promise.all([fetchAllState(), fetchVersion()]);
replaceAll(snaps); replaceAll(snaps);
setVersion(ver); setVersion(ver);
if (!scope() && snaps.length > 0) { // Hydrate the scope: prefer a cookie-loaded value, but only if it
// still matches a maglevd we actually got back in the snapshot.
// Otherwise fall back to the first server in the list so the user
// never sees a "ghost selection" pointing at a maglevd that was
// removed or renamed since their last visit.
const current = scope();
const valid = current && snaps.some((s) => s.maglevd.name === current);
if (!valid && snaps.length > 0) {
setScope(snaps[0].maglevd.name); setScope(snaps[0].maglevd.name);
} }
openEventStream(); openEventStream();

View File

@@ -1,4 +1,4 @@
import { createEffect, on, type Component, type JSX } from "solid-js"; import { createEffect, createMemo, on, type Component, type JSX } from "solid-js";
type Props = { type Props = {
// value is used solely for change detection. When it changes the // value is used solely for change detection. When it changes the
@@ -26,9 +26,26 @@ type Props = {
const Flash: Component<Props> = (props) => { const Flash: Component<Props> = (props) => {
let el: HTMLSpanElement | undefined; let el: HTMLSpanElement | undefined;
// Solid's on() fires its callback whenever a tracked dep *notifies*,
// not whenever the tracked value actually changes — the source is
// right there in dist/solid.js:460, there's no equality check, just
// `fn(input, prevInput, prevValue)` on every notification. That's a
// problem for Flash because props.value is typically a reactive
// expression chained through the store (e.g. a per-maglevd
// vpp_state read), and changing an upstream signal like scope()
// causes every downstream reactive to re-evaluate even when the
// concrete value it produces is identical. Without this memo, the
// badge flashes every time you flip from one maglevd to another
// where both are 'connected'.
//
// createMemo gives us the equality dedupe we need: its default
// equality is ===, so an upstream notification that doesn't change
// the memoized output never propagates to the effect below.
const value = createMemo(() => props.value);
createEffect( createEffect(
on( on(
() => props.value, value,
() => { () => {
el?.animate( el?.animate(
[ [

View File

@@ -1,6 +1,48 @@
import { createSignal } from "solid-js"; import { createSignal } from "solid-js";
// The currently selected maglevd name, or undefined before first fetch. // Persisted selection of which maglevd the SPA is currently scoped to.
const [scope, setScope] = createSignal<string | undefined>(undefined); // The cookie is a best-effort hint: if it's missing, corrupt, or names a
// maglevd that no longer exists, we fall back to whatever App.tsx's
// hydration path picks (typically the first server in byName order).
// SameSite=Lax keeps it from leaking to third-party iframes; Max-Age is
// a year so the selection survives browser restarts.
const COOKIE_NAME = "maglev_scope";
const COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
function readCookie(): string | undefined {
try {
const raw = document.cookie.split("; ").find((c) => c.startsWith(COOKIE_NAME + "="));
if (!raw) return undefined;
const value = decodeURIComponent(raw.slice(COOKIE_NAME.length + 1));
return value || undefined;
} catch {
return undefined;
}
}
function writeCookie(name: string | undefined) {
try {
if (!name) {
// Clear by setting Max-Age=0. Works across every mainstream browser.
document.cookie = `${COOKIE_NAME}=; Path=/; Max-Age=0; SameSite=Lax`;
return;
}
const value = encodeURIComponent(name);
document.cookie = `${COOKIE_NAME}=${value}; Path=/; Max-Age=${COOKIE_MAX_AGE}; SameSite=Lax`;
} catch {
// quota / third-party-cookie blocks / private window — best effort
}
}
const [scope, setScopeRaw] = createSignal<string | undefined>(readCookie());
// setScope wraps the raw signal setter so every selection change writes
// back to the cookie. Callers use this exactly like the old setScope —
// the cookie plumbing is invisible at the call site.
function setScope(name: string | undefined) {
setScopeRaw(name);
writeCookie(name);
}
export { scope, setScope }; export { scope, setScope };

View File

@@ -377,12 +377,7 @@ export function applyConfiguredWeight(
// worse — typical maintenance / outage state // worse — typical maintenance / outage state
// "unknown" → fallthrough; should be unreachable, kept as // "unknown" → fallthrough; should be unreachable, kept as
// a safety net for logic bugs in this function // a safety net for logic bugs in this function
export type FrontendHealth = export type FrontendHealth = "ok" | "bug-buckets" | "primary-drained" | "degraded" | "unknown";
| "ok"
| "bug-buckets"
| "primary-drained"
| "degraded"
| "unknown";
export function frontendHealth(snap: StateSnapshot, fe: FrontendSnapshot): FrontendHealth { export function frontendHealth(snap: StateSnapshot, fe: FrontendSnapshot): FrontendHealth {
const stateOf: Record<string, string> = {}; const stateOf: Record<string, string> = {};

View File

@@ -17,9 +17,7 @@ const COOKIE_MAX_AGE = 60 * 60 * 24 * 365; // 1 year
function readCookie(): Set<string> { function readCookie(): Set<string> {
try { try {
const raw = document.cookie const raw = document.cookie.split("; ").find((c) => c.startsWith(COOKIE_NAME + "="));
.split("; ")
.find((c) => c.startsWith(COOKIE_NAME + "="));
if (!raw) return new Set(); if (!raw) return new Set();
const value = decodeURIComponent(raw.slice(COOKIE_NAME.length + 1)); const value = decodeURIComponent(raw.slice(COOKIE_NAME.length + 1));
if (!value) return new Set(); if (!value) return new Set();

View File

@@ -14,9 +14,20 @@ const (
var colorEnabled bool var colorEnabled bool
// label wraps s in dark-blue ANSI when color output is enabled. // label wraps s in dark-blue ANSI when color output is enabled.
// Because every label receives the same fixed-length prefix/suffix, tabwriter //
// alignment is preserved: the extra bytes are equal for all rows so relative // Tabwriter caveat: tabwriter.Writer counts *bytes* per cell, not
// widths remain correct. // rendered columns. ANSI escape codes (`\x1b[34m…\x1b[0m`, 11 bytes)
// inflate a cell's apparent width without affecting what the terminal
// draws. Two things follow:
//
// 1. Key-value layouts where column 1 is *always* labelled and
// column 2 is *always* plain (e.g. `show vpp info`) stay aligned,
// because every row adds the same 11 bytes to column 1.
// 2. Multi-column tables where only the *header* row is labelled
// drift: the header cells each carry 11 extra bytes that the data
// rows don't, so data cells get over-padded. In those tables,
// leave the header plain (see runShowVPPLBCounters) and only use
// label() for labels that appear uniformly column-wise.
func label(s string) string { func label(s string) string {
if !colorEnabled { if !colorEnabled {
return s return s

View File

@@ -337,9 +337,11 @@ func runShowVPPLBState(ctx context.Context, client grpcapi.MaglevClient, _ []str
return nil return nil
} }
// runShowVPPLBCounters prints the per-VIP and per-backend runtime counters // runShowVPPLBCounters prints the per-VIP runtime counters captured by
// captured by maglevd's 5s scrape loop. Values are up to 5 seconds stale; // maglevd's 5s scrape loop. Values are up to 5 seconds stale; Prometheus
// Prometheus is the right tool if you need live rates. // is the right tool if you need live rates. There is no per-backend
// block — see internal/vpp/lbstats.go::scrapeLBStats for why VPP's LB
// plugin doesn't expose per-backend packet counters today.
func runShowVPPLBCounters(ctx context.Context, client grpcapi.MaglevClient, _ []string) error { func runShowVPPLBCounters(ctx context.Context, client grpcapi.MaglevClient, _ []string) error {
ctx, cancel := context.WithTimeout(ctx, callTimeout) ctx, cancel := context.WithTimeout(ctx, callTimeout)
defer cancel() defer cancel()
@@ -348,52 +350,30 @@ func runShowVPPLBCounters(ctx context.Context, client grpcapi.MaglevClient, _ []
return err return err
} }
if len(resp.Vips) == 0 && len(resp.Backends) == 0 { if len(resp.Vips) == 0 {
fmt.Println("(no counters — VPP disconnected or scrape pending)") fmt.Println("(no counters — VPP disconnected or scrape pending)")
return nil return nil
} }
// ---- frontend-counters ---- // ---- frontend-counters ----
//
// Column headers are plain strings, not label()-wrapped. tabwriter
// counts bytes (not rendered width), so wrapping a header cell in
// ANSI escape codes inflates its apparent width by ~11 bytes and
// the data rows below — which are plain numeric strings — end up
// over-padded. The label() convention only works when every cell
// in a column shares the same wrapping, which the key-value show
// commands do but this table can't (we're not about to colourise
// every packet count).
fmt.Println(label("frontend-counters")) fmt.Println(label("frontend-counters"))
if len(resp.Vips) == 0 {
fmt.Println(" (none)")
} else {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, " %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
label("vip"), label("proto"), label("port"),
label("first"), label("next"),
label("untracked"), label("no-server"),
label("fib-packets"), label("fib-bytes"),
)
for _, v := range resp.Vips {
fmt.Fprintf(w, " %s\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
stripHostMask(v.Prefix), v.Protocol, v.Port,
v.FirstPacket, v.NextPacket,
v.UntrackedPacket, v.NoServer,
v.Packets, v.Bytes,
)
}
if err := w.Flush(); err != nil {
return err
}
}
fmt.Println()
// ---- backend-counters ----
fmt.Println(label("backend-counters"))
if len(resp.Backends) == 0 {
fmt.Println(" (none)")
return nil
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, " %s\t%s\t%s\t%s\n", fmt.Fprintf(w, " vip\tproto\tport\tfirst\tnext\tuntracked\tno-server\tfib-packets\tfib-bytes\n")
label("backend"), label("address"), for _, v := range resp.Vips {
label("fib-packets"), label("fib-bytes"), fmt.Fprintf(w, " %s\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
) stripHostMask(v.Prefix), v.Protocol, v.Port,
for _, b := range resp.Backends { v.FirstPacket, v.NextPacket,
fmt.Fprintf(w, " %s\t%s\t%d\t%d\n", v.UntrackedPacket, v.NoServer,
b.Backend, b.Address, b.Packets, b.Bytes, v.Packets, v.Bytes,
) )
} }
return w.Flush() return w.Flush()

View File

@@ -13,9 +13,20 @@ Restart=on-failure
RestartSec=5s RestartSec=5s
Type=simple Type=simple
# Grant only CAP_NET_RAW (needed for ICMP probes) and drop everything else. # Capabilities:
AmbientCapabilities=CAP_NET_RAW # CAP_NET_RAW — required to open ICMP raw sockets for L3 probes.
CapabilityBoundingSet=CAP_NET_RAW # CAP_SYS_ADMIN — required for setns(CLONE_NEWNET) when the healthcheck
# block sets `netns: <name>` in maglev.yaml. Per
# setns(2) the kernel demands CAP_SYS_ADMIN in the
# user namespace that owns the target net namespace;
# without it the probe loop logs
# `enter netns "<name>": operation not permitted`
# and every backend flips to down/L4CON on first probe.
# Drop this bound + ambient pair if the deployment
# doesn't use `netns:` — the probes still work, they
# just run in maglevd's own namespace.
AmbientCapabilities=CAP_NET_RAW CAP_SYS_ADMIN
CapabilityBoundingSet=CAP_NET_RAW CAP_SYS_ADMIN
NoNewPrivileges=yes NoNewPrivileges=yes
[Install] [Install]

View File

@@ -155,6 +155,20 @@ func (c *Checker) Reload(ctx context.Context, cfg *config.Config) error {
runtimeEnabled := w.entry.Enabled runtimeEnabled := w.entry.Enabled
w.entry = b w.entry = b
w.entry.Enabled = runtimeEnabled w.entry.Enabled = runtimeEnabled
// Mirror the new address onto the runtime
// health.Backend. health.New captured the old
// address at worker-creation time and nothing
// else tracks subsequent config edits, so without
// this line GetBackend keeps returning the pre-
// reload address via snap.Health.Address — which
// propagates into maglev-frontend's cache and
// leaves the SPA showing a stale address even
// though VPP (and the rest of maglevd) has
// already moved on. The probe loop re-reads
// w.entry.Address on its next iteration, so the
// healthcheck target follows the new value on
// its own.
w.backend.Address = b.Address
continue continue
} }
slog.Info("backend-restart", "backend", name) slog.Info("backend-restart", "backend", name)

View File

@@ -1191,87 +1191,22 @@ func (x *VPPLBVIPCounters) GetBytes() uint64 {
return 0 return 0
} }
// VPPLBBackendCounters is the FIB combined counter for a single backend's // VPPLBCounters wraps the per-VIP counter list returned by
// host prefix, summed across worker threads. // GetVPPLBCounters. There is no per-backend counter block: VPP's LB
type VPPLBBackendCounters struct { // plugin forwarding node bypasses ip{4,6}_lookup_inline and writes
state protoimpl.MessageState `protogen:"open.v1"` // adj_index[VLIB_TX] directly, so /net/route/to at a backend's FIB
Backend string `protobuf:"bytes,1,opt,name=backend,proto3" json:"backend,omitempty"` // backend name from config // entry never ticks for LB-forwarded traffic — the four per-VIP
Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` // backend IP address // counters are the only per-VIP-and-coarser signal VPP exposes today.
Packets uint64 `protobuf:"varint,3,opt,name=packets,proto3" json:"packets,omitempty"`
Bytes uint64 `protobuf:"varint,4,opt,name=bytes,proto3" json:"bytes,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *VPPLBBackendCounters) Reset() {
*x = VPPLBBackendCounters{}
mi := &file_proto_maglev_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *VPPLBBackendCounters) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VPPLBBackendCounters) ProtoMessage() {}
func (x *VPPLBBackendCounters) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[22]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use VPPLBBackendCounters.ProtoReflect.Descriptor instead.
func (*VPPLBBackendCounters) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{22}
}
func (x *VPPLBBackendCounters) GetBackend() string {
if x != nil {
return x.Backend
}
return ""
}
func (x *VPPLBBackendCounters) GetAddress() string {
if x != nil {
return x.Address
}
return ""
}
func (x *VPPLBBackendCounters) GetPackets() uint64 {
if x != nil {
return x.Packets
}
return 0
}
func (x *VPPLBBackendCounters) GetBytes() uint64 {
if x != nil {
return x.Bytes
}
return 0
}
type VPPLBCounters struct { type VPPLBCounters struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Vips []*VPPLBVIPCounters `protobuf:"bytes,1,rep,name=vips,proto3" json:"vips,omitempty"` Vips []*VPPLBVIPCounters `protobuf:"bytes,1,rep,name=vips,proto3" json:"vips,omitempty"`
Backends []*VPPLBBackendCounters `protobuf:"bytes,2,rep,name=backends,proto3" json:"backends,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
func (x *VPPLBCounters) Reset() { func (x *VPPLBCounters) Reset() {
*x = VPPLBCounters{} *x = VPPLBCounters{}
mi := &file_proto_maglev_proto_msgTypes[23] mi := &file_proto_maglev_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1283,7 +1218,7 @@ func (x *VPPLBCounters) String() string {
func (*VPPLBCounters) ProtoMessage() {} func (*VPPLBCounters) ProtoMessage() {}
func (x *VPPLBCounters) ProtoReflect() protoreflect.Message { func (x *VPPLBCounters) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[23] mi := &file_proto_maglev_proto_msgTypes[22]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1296,7 +1231,7 @@ func (x *VPPLBCounters) ProtoReflect() protoreflect.Message {
// Deprecated: Use VPPLBCounters.ProtoReflect.Descriptor instead. // Deprecated: Use VPPLBCounters.ProtoReflect.Descriptor instead.
func (*VPPLBCounters) Descriptor() ([]byte, []int) { func (*VPPLBCounters) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{23} return file_proto_maglev_proto_rawDescGZIP(), []int{22}
} }
func (x *VPPLBCounters) GetVips() []*VPPLBVIPCounters { func (x *VPPLBCounters) GetVips() []*VPPLBVIPCounters {
@@ -1306,13 +1241,6 @@ func (x *VPPLBCounters) GetVips() []*VPPLBVIPCounters {
return nil return nil
} }
func (x *VPPLBCounters) GetBackends() []*VPPLBBackendCounters {
if x != nil {
return x.Backends
}
return nil
}
type SetWeightRequest struct { type SetWeightRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Frontend string `protobuf:"bytes,1,opt,name=frontend,proto3" json:"frontend,omitempty"` Frontend string `protobuf:"bytes,1,opt,name=frontend,proto3" json:"frontend,omitempty"`
@@ -1330,7 +1258,7 @@ type SetWeightRequest struct {
func (x *SetWeightRequest) Reset() { func (x *SetWeightRequest) Reset() {
*x = SetWeightRequest{} *x = SetWeightRequest{}
mi := &file_proto_maglev_proto_msgTypes[24] mi := &file_proto_maglev_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1342,7 +1270,7 @@ func (x *SetWeightRequest) String() string {
func (*SetWeightRequest) ProtoMessage() {} func (*SetWeightRequest) ProtoMessage() {}
func (x *SetWeightRequest) ProtoReflect() protoreflect.Message { func (x *SetWeightRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[24] mi := &file_proto_maglev_proto_msgTypes[23]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1355,7 +1283,7 @@ func (x *SetWeightRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use SetWeightRequest.ProtoReflect.Descriptor instead. // Deprecated: Use SetWeightRequest.ProtoReflect.Descriptor instead.
func (*SetWeightRequest) Descriptor() ([]byte, []int) { func (*SetWeightRequest) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{24} return file_proto_maglev_proto_rawDescGZIP(), []int{23}
} }
func (x *SetWeightRequest) GetFrontend() string { func (x *SetWeightRequest) GetFrontend() string {
@@ -1407,7 +1335,7 @@ type WatchRequest struct {
func (x *WatchRequest) Reset() { func (x *WatchRequest) Reset() {
*x = WatchRequest{} *x = WatchRequest{}
mi := &file_proto_maglev_proto_msgTypes[25] mi := &file_proto_maglev_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1419,7 +1347,7 @@ func (x *WatchRequest) String() string {
func (*WatchRequest) ProtoMessage() {} func (*WatchRequest) ProtoMessage() {}
func (x *WatchRequest) ProtoReflect() protoreflect.Message { func (x *WatchRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[25] mi := &file_proto_maglev_proto_msgTypes[24]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1432,7 +1360,7 @@ func (x *WatchRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead. // Deprecated: Use WatchRequest.ProtoReflect.Descriptor instead.
func (*WatchRequest) Descriptor() ([]byte, []int) { func (*WatchRequest) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{25} return file_proto_maglev_proto_rawDescGZIP(), []int{24}
} }
func (x *WatchRequest) GetLog() bool { func (x *WatchRequest) GetLog() bool {
@@ -1472,7 +1400,7 @@ type ListFrontendsResponse struct {
func (x *ListFrontendsResponse) Reset() { func (x *ListFrontendsResponse) Reset() {
*x = ListFrontendsResponse{} *x = ListFrontendsResponse{}
mi := &file_proto_maglev_proto_msgTypes[26] mi := &file_proto_maglev_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1484,7 +1412,7 @@ func (x *ListFrontendsResponse) String() string {
func (*ListFrontendsResponse) ProtoMessage() {} func (*ListFrontendsResponse) ProtoMessage() {}
func (x *ListFrontendsResponse) ProtoReflect() protoreflect.Message { func (x *ListFrontendsResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[26] mi := &file_proto_maglev_proto_msgTypes[25]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1497,7 +1425,7 @@ func (x *ListFrontendsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListFrontendsResponse.ProtoReflect.Descriptor instead. // Deprecated: Use ListFrontendsResponse.ProtoReflect.Descriptor instead.
func (*ListFrontendsResponse) Descriptor() ([]byte, []int) { func (*ListFrontendsResponse) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{26} return file_proto_maglev_proto_rawDescGZIP(), []int{25}
} }
func (x *ListFrontendsResponse) GetFrontendNames() []string { func (x *ListFrontendsResponse) GetFrontendNames() []string {
@@ -1518,7 +1446,7 @@ type PoolBackendInfo struct {
func (x *PoolBackendInfo) Reset() { func (x *PoolBackendInfo) Reset() {
*x = PoolBackendInfo{} *x = PoolBackendInfo{}
mi := &file_proto_maglev_proto_msgTypes[27] mi := &file_proto_maglev_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1530,7 +1458,7 @@ func (x *PoolBackendInfo) String() string {
func (*PoolBackendInfo) ProtoMessage() {} func (*PoolBackendInfo) ProtoMessage() {}
func (x *PoolBackendInfo) ProtoReflect() protoreflect.Message { func (x *PoolBackendInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[27] mi := &file_proto_maglev_proto_msgTypes[26]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1543,7 +1471,7 @@ func (x *PoolBackendInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use PoolBackendInfo.ProtoReflect.Descriptor instead. // Deprecated: Use PoolBackendInfo.ProtoReflect.Descriptor instead.
func (*PoolBackendInfo) Descriptor() ([]byte, []int) { func (*PoolBackendInfo) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{27} return file_proto_maglev_proto_rawDescGZIP(), []int{26}
} }
func (x *PoolBackendInfo) GetName() string { func (x *PoolBackendInfo) GetName() string {
@@ -1577,7 +1505,7 @@ type PoolInfo struct {
func (x *PoolInfo) Reset() { func (x *PoolInfo) Reset() {
*x = PoolInfo{} *x = PoolInfo{}
mi := &file_proto_maglev_proto_msgTypes[28] mi := &file_proto_maglev_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1589,7 +1517,7 @@ func (x *PoolInfo) String() string {
func (*PoolInfo) ProtoMessage() {} func (*PoolInfo) ProtoMessage() {}
func (x *PoolInfo) ProtoReflect() protoreflect.Message { func (x *PoolInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[28] mi := &file_proto_maglev_proto_msgTypes[27]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1602,7 +1530,7 @@ func (x *PoolInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use PoolInfo.ProtoReflect.Descriptor instead. // Deprecated: Use PoolInfo.ProtoReflect.Descriptor instead.
func (*PoolInfo) Descriptor() ([]byte, []int) { func (*PoolInfo) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{28} return file_proto_maglev_proto_rawDescGZIP(), []int{27}
} }
func (x *PoolInfo) GetName() string { func (x *PoolInfo) GetName() string {
@@ -1634,7 +1562,7 @@ type FrontendInfo struct {
func (x *FrontendInfo) Reset() { func (x *FrontendInfo) Reset() {
*x = FrontendInfo{} *x = FrontendInfo{}
mi := &file_proto_maglev_proto_msgTypes[29] mi := &file_proto_maglev_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1646,7 +1574,7 @@ func (x *FrontendInfo) String() string {
func (*FrontendInfo) ProtoMessage() {} func (*FrontendInfo) ProtoMessage() {}
func (x *FrontendInfo) ProtoReflect() protoreflect.Message { func (x *FrontendInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[29] mi := &file_proto_maglev_proto_msgTypes[28]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1659,7 +1587,7 @@ func (x *FrontendInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use FrontendInfo.ProtoReflect.Descriptor instead. // Deprecated: Use FrontendInfo.ProtoReflect.Descriptor instead.
func (*FrontendInfo) Descriptor() ([]byte, []int) { func (*FrontendInfo) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{29} return file_proto_maglev_proto_rawDescGZIP(), []int{28}
} }
func (x *FrontendInfo) GetName() string { func (x *FrontendInfo) GetName() string {
@@ -1720,7 +1648,7 @@ type ListBackendsResponse struct {
func (x *ListBackendsResponse) Reset() { func (x *ListBackendsResponse) Reset() {
*x = ListBackendsResponse{} *x = ListBackendsResponse{}
mi := &file_proto_maglev_proto_msgTypes[30] mi := &file_proto_maglev_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1732,7 +1660,7 @@ func (x *ListBackendsResponse) String() string {
func (*ListBackendsResponse) ProtoMessage() {} func (*ListBackendsResponse) ProtoMessage() {}
func (x *ListBackendsResponse) ProtoReflect() protoreflect.Message { func (x *ListBackendsResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[30] mi := &file_proto_maglev_proto_msgTypes[29]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1745,7 +1673,7 @@ func (x *ListBackendsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListBackendsResponse.ProtoReflect.Descriptor instead. // Deprecated: Use ListBackendsResponse.ProtoReflect.Descriptor instead.
func (*ListBackendsResponse) Descriptor() ([]byte, []int) { func (*ListBackendsResponse) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{30} return file_proto_maglev_proto_rawDescGZIP(), []int{29}
} }
func (x *ListBackendsResponse) GetBackendNames() []string { func (x *ListBackendsResponse) GetBackendNames() []string {
@@ -1764,7 +1692,7 @@ type ListHealthChecksResponse struct {
func (x *ListHealthChecksResponse) Reset() { func (x *ListHealthChecksResponse) Reset() {
*x = ListHealthChecksResponse{} *x = ListHealthChecksResponse{}
mi := &file_proto_maglev_proto_msgTypes[31] mi := &file_proto_maglev_proto_msgTypes[30]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1776,7 +1704,7 @@ func (x *ListHealthChecksResponse) String() string {
func (*ListHealthChecksResponse) ProtoMessage() {} func (*ListHealthChecksResponse) ProtoMessage() {}
func (x *ListHealthChecksResponse) ProtoReflect() protoreflect.Message { func (x *ListHealthChecksResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[31] mi := &file_proto_maglev_proto_msgTypes[30]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1789,7 +1717,7 @@ func (x *ListHealthChecksResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListHealthChecksResponse.ProtoReflect.Descriptor instead. // Deprecated: Use ListHealthChecksResponse.ProtoReflect.Descriptor instead.
func (*ListHealthChecksResponse) Descriptor() ([]byte, []int) { func (*ListHealthChecksResponse) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{31} return file_proto_maglev_proto_rawDescGZIP(), []int{30}
} }
func (x *ListHealthChecksResponse) GetNames() []string { func (x *ListHealthChecksResponse) GetNames() []string {
@@ -1814,7 +1742,7 @@ type HTTPCheckParams struct {
func (x *HTTPCheckParams) Reset() { func (x *HTTPCheckParams) Reset() {
*x = HTTPCheckParams{} *x = HTTPCheckParams{}
mi := &file_proto_maglev_proto_msgTypes[32] mi := &file_proto_maglev_proto_msgTypes[31]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1826,7 +1754,7 @@ func (x *HTTPCheckParams) String() string {
func (*HTTPCheckParams) ProtoMessage() {} func (*HTTPCheckParams) ProtoMessage() {}
func (x *HTTPCheckParams) ProtoReflect() protoreflect.Message { func (x *HTTPCheckParams) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[32] mi := &file_proto_maglev_proto_msgTypes[31]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1839,7 +1767,7 @@ func (x *HTTPCheckParams) ProtoReflect() protoreflect.Message {
// Deprecated: Use HTTPCheckParams.ProtoReflect.Descriptor instead. // Deprecated: Use HTTPCheckParams.ProtoReflect.Descriptor instead.
func (*HTTPCheckParams) Descriptor() ([]byte, []int) { func (*HTTPCheckParams) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{32} return file_proto_maglev_proto_rawDescGZIP(), []int{31}
} }
func (x *HTTPCheckParams) GetPath() string { func (x *HTTPCheckParams) GetPath() string {
@@ -1902,7 +1830,7 @@ type TCPCheckParams struct {
func (x *TCPCheckParams) Reset() { func (x *TCPCheckParams) Reset() {
*x = TCPCheckParams{} *x = TCPCheckParams{}
mi := &file_proto_maglev_proto_msgTypes[33] mi := &file_proto_maglev_proto_msgTypes[32]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1914,7 +1842,7 @@ func (x *TCPCheckParams) String() string {
func (*TCPCheckParams) ProtoMessage() {} func (*TCPCheckParams) ProtoMessage() {}
func (x *TCPCheckParams) ProtoReflect() protoreflect.Message { func (x *TCPCheckParams) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[33] mi := &file_proto_maglev_proto_msgTypes[32]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1927,7 +1855,7 @@ func (x *TCPCheckParams) ProtoReflect() protoreflect.Message {
// Deprecated: Use TCPCheckParams.ProtoReflect.Descriptor instead. // Deprecated: Use TCPCheckParams.ProtoReflect.Descriptor instead.
func (*TCPCheckParams) Descriptor() ([]byte, []int) { func (*TCPCheckParams) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{33} return file_proto_maglev_proto_rawDescGZIP(), []int{32}
} }
func (x *TCPCheckParams) GetSsl() bool { func (x *TCPCheckParams) GetSsl() bool {
@@ -1972,7 +1900,7 @@ type HealthCheckInfo struct {
func (x *HealthCheckInfo) Reset() { func (x *HealthCheckInfo) Reset() {
*x = HealthCheckInfo{} *x = HealthCheckInfo{}
mi := &file_proto_maglev_proto_msgTypes[34] mi := &file_proto_maglev_proto_msgTypes[33]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -1984,7 +1912,7 @@ func (x *HealthCheckInfo) String() string {
func (*HealthCheckInfo) ProtoMessage() {} func (*HealthCheckInfo) ProtoMessage() {}
func (x *HealthCheckInfo) ProtoReflect() protoreflect.Message { func (x *HealthCheckInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[34] mi := &file_proto_maglev_proto_msgTypes[33]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -1997,7 +1925,7 @@ func (x *HealthCheckInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use HealthCheckInfo.ProtoReflect.Descriptor instead. // Deprecated: Use HealthCheckInfo.ProtoReflect.Descriptor instead.
func (*HealthCheckInfo) Descriptor() ([]byte, []int) { func (*HealthCheckInfo) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{34} return file_proto_maglev_proto_rawDescGZIP(), []int{33}
} }
func (x *HealthCheckInfo) GetName() string { func (x *HealthCheckInfo) GetName() string {
@@ -2105,7 +2033,7 @@ type BackendInfo struct {
func (x *BackendInfo) Reset() { func (x *BackendInfo) Reset() {
*x = BackendInfo{} *x = BackendInfo{}
mi := &file_proto_maglev_proto_msgTypes[35] mi := &file_proto_maglev_proto_msgTypes[34]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -2117,7 +2045,7 @@ func (x *BackendInfo) String() string {
func (*BackendInfo) ProtoMessage() {} func (*BackendInfo) ProtoMessage() {}
func (x *BackendInfo) ProtoReflect() protoreflect.Message { func (x *BackendInfo) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[35] mi := &file_proto_maglev_proto_msgTypes[34]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -2130,7 +2058,7 @@ func (x *BackendInfo) ProtoReflect() protoreflect.Message {
// Deprecated: Use BackendInfo.ProtoReflect.Descriptor instead. // Deprecated: Use BackendInfo.ProtoReflect.Descriptor instead.
func (*BackendInfo) Descriptor() ([]byte, []int) { func (*BackendInfo) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{35} return file_proto_maglev_proto_rawDescGZIP(), []int{34}
} }
func (x *BackendInfo) GetName() string { func (x *BackendInfo) GetName() string {
@@ -2186,7 +2114,7 @@ type TransitionRecord struct {
func (x *TransitionRecord) Reset() { func (x *TransitionRecord) Reset() {
*x = TransitionRecord{} *x = TransitionRecord{}
mi := &file_proto_maglev_proto_msgTypes[36] mi := &file_proto_maglev_proto_msgTypes[35]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -2198,7 +2126,7 @@ func (x *TransitionRecord) String() string {
func (*TransitionRecord) ProtoMessage() {} func (*TransitionRecord) ProtoMessage() {}
func (x *TransitionRecord) ProtoReflect() protoreflect.Message { func (x *TransitionRecord) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[36] mi := &file_proto_maglev_proto_msgTypes[35]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -2211,7 +2139,7 @@ func (x *TransitionRecord) ProtoReflect() protoreflect.Message {
// Deprecated: Use TransitionRecord.ProtoReflect.Descriptor instead. // Deprecated: Use TransitionRecord.ProtoReflect.Descriptor instead.
func (*TransitionRecord) Descriptor() ([]byte, []int) { func (*TransitionRecord) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{36} return file_proto_maglev_proto_rawDescGZIP(), []int{35}
} }
func (x *TransitionRecord) GetFrom() string { func (x *TransitionRecord) GetFrom() string {
@@ -2246,7 +2174,7 @@ type LogAttr struct {
func (x *LogAttr) Reset() { func (x *LogAttr) Reset() {
*x = LogAttr{} *x = LogAttr{}
mi := &file_proto_maglev_proto_msgTypes[37] mi := &file_proto_maglev_proto_msgTypes[36]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -2258,7 +2186,7 @@ func (x *LogAttr) String() string {
func (*LogAttr) ProtoMessage() {} func (*LogAttr) ProtoMessage() {}
func (x *LogAttr) ProtoReflect() protoreflect.Message { func (x *LogAttr) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[37] mi := &file_proto_maglev_proto_msgTypes[36]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -2271,7 +2199,7 @@ func (x *LogAttr) ProtoReflect() protoreflect.Message {
// Deprecated: Use LogAttr.ProtoReflect.Descriptor instead. // Deprecated: Use LogAttr.ProtoReflect.Descriptor instead.
func (*LogAttr) Descriptor() ([]byte, []int) { func (*LogAttr) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{37} return file_proto_maglev_proto_rawDescGZIP(), []int{36}
} }
func (x *LogAttr) GetKey() string { func (x *LogAttr) GetKey() string {
@@ -2301,7 +2229,7 @@ type LogEvent struct {
func (x *LogEvent) Reset() { func (x *LogEvent) Reset() {
*x = LogEvent{} *x = LogEvent{}
mi := &file_proto_maglev_proto_msgTypes[38] mi := &file_proto_maglev_proto_msgTypes[37]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -2313,7 +2241,7 @@ func (x *LogEvent) String() string {
func (*LogEvent) ProtoMessage() {} func (*LogEvent) ProtoMessage() {}
func (x *LogEvent) ProtoReflect() protoreflect.Message { func (x *LogEvent) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[38] mi := &file_proto_maglev_proto_msgTypes[37]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -2326,7 +2254,7 @@ func (x *LogEvent) ProtoReflect() protoreflect.Message {
// Deprecated: Use LogEvent.ProtoReflect.Descriptor instead. // Deprecated: Use LogEvent.ProtoReflect.Descriptor instead.
func (*LogEvent) Descriptor() ([]byte, []int) { func (*LogEvent) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{38} return file_proto_maglev_proto_rawDescGZIP(), []int{37}
} }
func (x *LogEvent) GetAtUnixNs() int64 { func (x *LogEvent) GetAtUnixNs() int64 {
@@ -2368,7 +2296,7 @@ type BackendEvent struct {
func (x *BackendEvent) Reset() { func (x *BackendEvent) Reset() {
*x = BackendEvent{} *x = BackendEvent{}
mi := &file_proto_maglev_proto_msgTypes[39] mi := &file_proto_maglev_proto_msgTypes[38]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -2380,7 +2308,7 @@ func (x *BackendEvent) String() string {
func (*BackendEvent) ProtoMessage() {} func (*BackendEvent) ProtoMessage() {}
func (x *BackendEvent) ProtoReflect() protoreflect.Message { func (x *BackendEvent) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[39] mi := &file_proto_maglev_proto_msgTypes[38]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -2393,7 +2321,7 @@ func (x *BackendEvent) ProtoReflect() protoreflect.Message {
// Deprecated: Use BackendEvent.ProtoReflect.Descriptor instead. // Deprecated: Use BackendEvent.ProtoReflect.Descriptor instead.
func (*BackendEvent) Descriptor() ([]byte, []int) { func (*BackendEvent) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{39} return file_proto_maglev_proto_rawDescGZIP(), []int{38}
} }
func (x *BackendEvent) GetBackendName() string { func (x *BackendEvent) GetBackendName() string {
@@ -2422,7 +2350,7 @@ type FrontendEvent struct {
func (x *FrontendEvent) Reset() { func (x *FrontendEvent) Reset() {
*x = FrontendEvent{} *x = FrontendEvent{}
mi := &file_proto_maglev_proto_msgTypes[40] mi := &file_proto_maglev_proto_msgTypes[39]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -2434,7 +2362,7 @@ func (x *FrontendEvent) String() string {
func (*FrontendEvent) ProtoMessage() {} func (*FrontendEvent) ProtoMessage() {}
func (x *FrontendEvent) ProtoReflect() protoreflect.Message { func (x *FrontendEvent) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[40] mi := &file_proto_maglev_proto_msgTypes[39]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -2447,7 +2375,7 @@ func (x *FrontendEvent) ProtoReflect() protoreflect.Message {
// Deprecated: Use FrontendEvent.ProtoReflect.Descriptor instead. // Deprecated: Use FrontendEvent.ProtoReflect.Descriptor instead.
func (*FrontendEvent) Descriptor() ([]byte, []int) { func (*FrontendEvent) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{40} return file_proto_maglev_proto_rawDescGZIP(), []int{39}
} }
func (x *FrontendEvent) GetFrontendName() string { func (x *FrontendEvent) GetFrontendName() string {
@@ -2479,7 +2407,7 @@ type Event struct {
func (x *Event) Reset() { func (x *Event) Reset() {
*x = Event{} *x = Event{}
mi := &file_proto_maglev_proto_msgTypes[41] mi := &file_proto_maglev_proto_msgTypes[40]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -2491,7 +2419,7 @@ func (x *Event) String() string {
func (*Event) ProtoMessage() {} func (*Event) ProtoMessage() {}
func (x *Event) ProtoReflect() protoreflect.Message { func (x *Event) ProtoReflect() protoreflect.Message {
mi := &file_proto_maglev_proto_msgTypes[41] mi := &file_proto_maglev_proto_msgTypes[40]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -2504,7 +2432,7 @@ func (x *Event) ProtoReflect() protoreflect.Message {
// Deprecated: Use Event.ProtoReflect.Descriptor instead. // Deprecated: Use Event.ProtoReflect.Descriptor instead.
func (*Event) Descriptor() ([]byte, []int) { func (*Event) Descriptor() ([]byte, []int) {
return file_proto_maglev_proto_rawDescGZIP(), []int{41} return file_proto_maglev_proto_rawDescGZIP(), []int{40}
} }
func (x *Event) GetEvent() isEvent_Event { func (x *Event) GetEvent() isEvent_Event {
@@ -2642,15 +2570,9 @@ const file_proto_maglev_proto_rawDesc = "" +
"\x10untracked_packet\x18\x06 \x01(\x04R\x0funtrackedPacket\x12\x1b\n" + "\x10untracked_packet\x18\x06 \x01(\x04R\x0funtrackedPacket\x12\x1b\n" +
"\tno_server\x18\a \x01(\x04R\bnoServer\x12\x18\n" + "\tno_server\x18\a \x01(\x04R\bnoServer\x12\x18\n" +
"\apackets\x18\b \x01(\x04R\apackets\x12\x14\n" + "\apackets\x18\b \x01(\x04R\apackets\x12\x14\n" +
"\x05bytes\x18\t \x01(\x04R\x05bytes\"z\n" + "\x05bytes\x18\t \x01(\x04R\x05bytes\"C\n" +
"\x14VPPLBBackendCounters\x12\x18\n" +
"\abackend\x18\x01 \x01(\tR\abackend\x12\x18\n" +
"\aaddress\x18\x02 \x01(\tR\aaddress\x12\x18\n" +
"\apackets\x18\x03 \x01(\x04R\apackets\x12\x14\n" +
"\x05bytes\x18\x04 \x01(\x04R\x05bytes\"w\n" +
"\rVPPLBCounters\x12,\n" + "\rVPPLBCounters\x12,\n" +
"\x04vips\x18\x01 \x03(\v2\x18.maglev.VPPLBVIPCountersR\x04vips\x128\n" + "\x04vips\x18\x01 \x03(\v2\x18.maglev.VPPLBVIPCountersR\x04vipsJ\x04\b\x02\x10\x03\"\x8a\x01\n" +
"\bbackends\x18\x02 \x03(\v2\x1c.maglev.VPPLBBackendCountersR\bbackends\"\x8a\x01\n" +
"\x10SetWeightRequest\x12\x1a\n" + "\x10SetWeightRequest\x12\x1a\n" +
"\bfrontend\x18\x01 \x01(\tR\bfrontend\x12\x12\n" + "\bfrontend\x18\x01 \x01(\tR\bfrontend\x12\x12\n" +
"\x04pool\x18\x02 \x01(\tR\x04pool\x12\x18\n" + "\x04pool\x18\x02 \x01(\tR\x04pool\x12\x18\n" +
@@ -2788,7 +2710,7 @@ func file_proto_maglev_proto_rawDescGZIP() []byte {
return file_proto_maglev_proto_rawDescData return file_proto_maglev_proto_rawDescData
} }
var file_proto_maglev_proto_msgTypes = make([]protoimpl.MessageInfo, 42) var file_proto_maglev_proto_msgTypes = make([]protoimpl.MessageInfo, 41)
var file_proto_maglev_proto_goTypes = []any{ var file_proto_maglev_proto_goTypes = []any{
(*ListFrontendsRequest)(nil), // 0: maglev.ListFrontendsRequest (*ListFrontendsRequest)(nil), // 0: maglev.ListFrontendsRequest
(*GetFrontendRequest)(nil), // 1: maglev.GetFrontendRequest (*GetFrontendRequest)(nil), // 1: maglev.GetFrontendRequest
@@ -2812,85 +2734,83 @@ var file_proto_maglev_proto_goTypes = []any{
(*SyncVPPLBStateResponse)(nil), // 19: maglev.SyncVPPLBStateResponse (*SyncVPPLBStateResponse)(nil), // 19: maglev.SyncVPPLBStateResponse
(*GetVPPLBCountersRequest)(nil), // 20: maglev.GetVPPLBCountersRequest (*GetVPPLBCountersRequest)(nil), // 20: maglev.GetVPPLBCountersRequest
(*VPPLBVIPCounters)(nil), // 21: maglev.VPPLBVIPCounters (*VPPLBVIPCounters)(nil), // 21: maglev.VPPLBVIPCounters
(*VPPLBBackendCounters)(nil), // 22: maglev.VPPLBBackendCounters (*VPPLBCounters)(nil), // 22: maglev.VPPLBCounters
(*VPPLBCounters)(nil), // 23: maglev.VPPLBCounters (*SetWeightRequest)(nil), // 23: maglev.SetWeightRequest
(*SetWeightRequest)(nil), // 24: maglev.SetWeightRequest (*WatchRequest)(nil), // 24: maglev.WatchRequest
(*WatchRequest)(nil), // 25: maglev.WatchRequest (*ListFrontendsResponse)(nil), // 25: maglev.ListFrontendsResponse
(*ListFrontendsResponse)(nil), // 26: maglev.ListFrontendsResponse (*PoolBackendInfo)(nil), // 26: maglev.PoolBackendInfo
(*PoolBackendInfo)(nil), // 27: maglev.PoolBackendInfo (*PoolInfo)(nil), // 27: maglev.PoolInfo
(*PoolInfo)(nil), // 28: maglev.PoolInfo (*FrontendInfo)(nil), // 28: maglev.FrontendInfo
(*FrontendInfo)(nil), // 29: maglev.FrontendInfo (*ListBackendsResponse)(nil), // 29: maglev.ListBackendsResponse
(*ListBackendsResponse)(nil), // 30: maglev.ListBackendsResponse (*ListHealthChecksResponse)(nil), // 30: maglev.ListHealthChecksResponse
(*ListHealthChecksResponse)(nil), // 31: maglev.ListHealthChecksResponse (*HTTPCheckParams)(nil), // 31: maglev.HTTPCheckParams
(*HTTPCheckParams)(nil), // 32: maglev.HTTPCheckParams (*TCPCheckParams)(nil), // 32: maglev.TCPCheckParams
(*TCPCheckParams)(nil), // 33: maglev.TCPCheckParams (*HealthCheckInfo)(nil), // 33: maglev.HealthCheckInfo
(*HealthCheckInfo)(nil), // 34: maglev.HealthCheckInfo (*BackendInfo)(nil), // 34: maglev.BackendInfo
(*BackendInfo)(nil), // 35: maglev.BackendInfo (*TransitionRecord)(nil), // 35: maglev.TransitionRecord
(*TransitionRecord)(nil), // 36: maglev.TransitionRecord (*LogAttr)(nil), // 36: maglev.LogAttr
(*LogAttr)(nil), // 37: maglev.LogAttr (*LogEvent)(nil), // 37: maglev.LogEvent
(*LogEvent)(nil), // 38: maglev.LogEvent (*BackendEvent)(nil), // 38: maglev.BackendEvent
(*BackendEvent)(nil), // 39: maglev.BackendEvent (*FrontendEvent)(nil), // 39: maglev.FrontendEvent
(*FrontendEvent)(nil), // 40: maglev.FrontendEvent (*Event)(nil), // 40: maglev.Event
(*Event)(nil), // 41: maglev.Event
} }
var file_proto_maglev_proto_depIdxs = []int32{ var file_proto_maglev_proto_depIdxs = []int32{
15, // 0: maglev.VPPLBVIP.application_servers:type_name -> maglev.VPPLBAS 15, // 0: maglev.VPPLBVIP.application_servers:type_name -> maglev.VPPLBAS
14, // 1: maglev.VPPLBState.conf:type_name -> maglev.VPPLBConf 14, // 1: maglev.VPPLBState.conf:type_name -> maglev.VPPLBConf
16, // 2: maglev.VPPLBState.vips:type_name -> maglev.VPPLBVIP 16, // 2: maglev.VPPLBState.vips:type_name -> maglev.VPPLBVIP
21, // 3: maglev.VPPLBCounters.vips:type_name -> maglev.VPPLBVIPCounters 21, // 3: maglev.VPPLBCounters.vips:type_name -> maglev.VPPLBVIPCounters
22, // 4: maglev.VPPLBCounters.backends:type_name -> maglev.VPPLBBackendCounters 26, // 4: maglev.PoolInfo.backends:type_name -> maglev.PoolBackendInfo
27, // 5: maglev.PoolInfo.backends:type_name -> maglev.PoolBackendInfo 27, // 5: maglev.FrontendInfo.pools:type_name -> maglev.PoolInfo
28, // 6: maglev.FrontendInfo.pools:type_name -> maglev.PoolInfo 31, // 6: maglev.HealthCheckInfo.http:type_name -> maglev.HTTPCheckParams
32, // 7: maglev.HealthCheckInfo.http:type_name -> maglev.HTTPCheckParams 32, // 7: maglev.HealthCheckInfo.tcp:type_name -> maglev.TCPCheckParams
33, // 8: maglev.HealthCheckInfo.tcp:type_name -> maglev.TCPCheckParams 35, // 8: maglev.BackendInfo.transitions:type_name -> maglev.TransitionRecord
36, // 9: maglev.BackendInfo.transitions:type_name -> maglev.TransitionRecord 36, // 9: maglev.LogEvent.attrs:type_name -> maglev.LogAttr
37, // 10: maglev.LogEvent.attrs:type_name -> maglev.LogAttr 35, // 10: maglev.BackendEvent.transition:type_name -> maglev.TransitionRecord
36, // 11: maglev.BackendEvent.transition:type_name -> maglev.TransitionRecord 35, // 11: maglev.FrontendEvent.transition:type_name -> maglev.TransitionRecord
36, // 12: maglev.FrontendEvent.transition:type_name -> maglev.TransitionRecord 37, // 12: maglev.Event.log:type_name -> maglev.LogEvent
38, // 13: maglev.Event.log:type_name -> maglev.LogEvent 38, // 13: maglev.Event.backend:type_name -> maglev.BackendEvent
39, // 14: maglev.Event.backend:type_name -> maglev.BackendEvent 39, // 14: maglev.Event.frontend:type_name -> maglev.FrontendEvent
40, // 15: maglev.Event.frontend:type_name -> maglev.FrontendEvent 0, // 15: maglev.Maglev.ListFrontends:input_type -> maglev.ListFrontendsRequest
0, // 16: maglev.Maglev.ListFrontends:input_type -> maglev.ListFrontendsRequest 1, // 16: maglev.Maglev.GetFrontend:input_type -> maglev.GetFrontendRequest
1, // 17: maglev.Maglev.GetFrontend:input_type -> maglev.GetFrontendRequest 2, // 17: maglev.Maglev.ListBackends:input_type -> maglev.ListBackendsRequest
2, // 18: maglev.Maglev.ListBackends:input_type -> maglev.ListBackendsRequest 3, // 18: maglev.Maglev.GetBackend:input_type -> maglev.GetBackendRequest
3, // 19: maglev.Maglev.GetBackend:input_type -> maglev.GetBackendRequest 4, // 19: maglev.Maglev.PauseBackend:input_type -> maglev.BackendRequest
4, // 20: maglev.Maglev.PauseBackend:input_type -> maglev.BackendRequest 4, // 20: maglev.Maglev.ResumeBackend:input_type -> maglev.BackendRequest
4, // 21: maglev.Maglev.ResumeBackend:input_type -> maglev.BackendRequest 4, // 21: maglev.Maglev.EnableBackend:input_type -> maglev.BackendRequest
4, // 22: maglev.Maglev.EnableBackend:input_type -> maglev.BackendRequest 4, // 22: maglev.Maglev.DisableBackend:input_type -> maglev.BackendRequest
4, // 23: maglev.Maglev.DisableBackend:input_type -> maglev.BackendRequest 5, // 23: maglev.Maglev.ListHealthChecks:input_type -> maglev.ListHealthChecksRequest
5, // 24: maglev.Maglev.ListHealthChecks:input_type -> maglev.ListHealthChecksRequest 6, // 24: maglev.Maglev.GetHealthCheck:input_type -> maglev.GetHealthCheckRequest
6, // 25: maglev.Maglev.GetHealthCheck:input_type -> maglev.GetHealthCheckRequest 23, // 25: maglev.Maglev.SetFrontendPoolBackendWeight:input_type -> maglev.SetWeightRequest
24, // 26: maglev.Maglev.SetFrontendPoolBackendWeight:input_type -> maglev.SetWeightRequest 24, // 26: maglev.Maglev.WatchEvents:input_type -> maglev.WatchRequest
25, // 27: maglev.Maglev.WatchEvents:input_type -> maglev.WatchRequest 7, // 27: maglev.Maglev.CheckConfig:input_type -> maglev.CheckConfigRequest
7, // 28: maglev.Maglev.CheckConfig:input_type -> maglev.CheckConfigRequest 9, // 28: maglev.Maglev.ReloadConfig:input_type -> maglev.ReloadConfigRequest
9, // 29: maglev.Maglev.ReloadConfig:input_type -> maglev.ReloadConfigRequest 11, // 29: maglev.Maglev.GetVPPInfo:input_type -> maglev.GetVPPInfoRequest
11, // 30: maglev.Maglev.GetVPPInfo:input_type -> maglev.GetVPPInfoRequest 13, // 30: maglev.Maglev.GetVPPLBState:input_type -> maglev.GetVPPLBStateRequest
13, // 31: maglev.Maglev.GetVPPLBState:input_type -> maglev.GetVPPLBStateRequest 18, // 31: maglev.Maglev.SyncVPPLBState:input_type -> maglev.SyncVPPLBStateRequest
18, // 32: maglev.Maglev.SyncVPPLBState:input_type -> maglev.SyncVPPLBStateRequest 20, // 32: maglev.Maglev.GetVPPLBCounters:input_type -> maglev.GetVPPLBCountersRequest
20, // 33: maglev.Maglev.GetVPPLBCounters:input_type -> maglev.GetVPPLBCountersRequest 25, // 33: maglev.Maglev.ListFrontends:output_type -> maglev.ListFrontendsResponse
26, // 34: maglev.Maglev.ListFrontends:output_type -> maglev.ListFrontendsResponse 28, // 34: maglev.Maglev.GetFrontend:output_type -> maglev.FrontendInfo
29, // 35: maglev.Maglev.GetFrontend:output_type -> maglev.FrontendInfo 29, // 35: maglev.Maglev.ListBackends:output_type -> maglev.ListBackendsResponse
30, // 36: maglev.Maglev.ListBackends:output_type -> maglev.ListBackendsResponse 34, // 36: maglev.Maglev.GetBackend:output_type -> maglev.BackendInfo
35, // 37: maglev.Maglev.GetBackend:output_type -> maglev.BackendInfo 34, // 37: maglev.Maglev.PauseBackend:output_type -> maglev.BackendInfo
35, // 38: maglev.Maglev.PauseBackend:output_type -> maglev.BackendInfo 34, // 38: maglev.Maglev.ResumeBackend:output_type -> maglev.BackendInfo
35, // 39: maglev.Maglev.ResumeBackend:output_type -> maglev.BackendInfo 34, // 39: maglev.Maglev.EnableBackend:output_type -> maglev.BackendInfo
35, // 40: maglev.Maglev.EnableBackend:output_type -> maglev.BackendInfo 34, // 40: maglev.Maglev.DisableBackend:output_type -> maglev.BackendInfo
35, // 41: maglev.Maglev.DisableBackend:output_type -> maglev.BackendInfo 30, // 41: maglev.Maglev.ListHealthChecks:output_type -> maglev.ListHealthChecksResponse
31, // 42: maglev.Maglev.ListHealthChecks:output_type -> maglev.ListHealthChecksResponse 33, // 42: maglev.Maglev.GetHealthCheck:output_type -> maglev.HealthCheckInfo
34, // 43: maglev.Maglev.GetHealthCheck:output_type -> maglev.HealthCheckInfo 28, // 43: maglev.Maglev.SetFrontendPoolBackendWeight:output_type -> maglev.FrontendInfo
29, // 44: maglev.Maglev.SetFrontendPoolBackendWeight:output_type -> maglev.FrontendInfo 40, // 44: maglev.Maglev.WatchEvents:output_type -> maglev.Event
41, // 45: maglev.Maglev.WatchEvents:output_type -> maglev.Event 8, // 45: maglev.Maglev.CheckConfig:output_type -> maglev.CheckConfigResponse
8, // 46: maglev.Maglev.CheckConfig:output_type -> maglev.CheckConfigResponse 10, // 46: maglev.Maglev.ReloadConfig:output_type -> maglev.ReloadConfigResponse
10, // 47: maglev.Maglev.ReloadConfig:output_type -> maglev.ReloadConfigResponse 12, // 47: maglev.Maglev.GetVPPInfo:output_type -> maglev.VPPInfo
12, // 48: maglev.Maglev.GetVPPInfo:output_type -> maglev.VPPInfo 17, // 48: maglev.Maglev.GetVPPLBState:output_type -> maglev.VPPLBState
17, // 49: maglev.Maglev.GetVPPLBState:output_type -> maglev.VPPLBState 19, // 49: maglev.Maglev.SyncVPPLBState:output_type -> maglev.SyncVPPLBStateResponse
19, // 50: maglev.Maglev.SyncVPPLBState:output_type -> maglev.SyncVPPLBStateResponse 22, // 50: maglev.Maglev.GetVPPLBCounters:output_type -> maglev.VPPLBCounters
23, // 51: maglev.Maglev.GetVPPLBCounters:output_type -> maglev.VPPLBCounters 33, // [33:51] is the sub-list for method output_type
34, // [34:52] is the sub-list for method output_type 15, // [15:33] is the sub-list for method input_type
16, // [16:34] is the sub-list for method input_type 15, // [15:15] is the sub-list for extension type_name
16, // [16:16] is the sub-list for extension type_name 15, // [15:15] is the sub-list for extension extendee
16, // [16:16] is the sub-list for extension extendee 0, // [0:15] is the sub-list for field type_name
0, // [0:16] is the sub-list for field type_name
} }
func init() { file_proto_maglev_proto_init() } func init() { file_proto_maglev_proto_init() }
@@ -2899,8 +2819,8 @@ func file_proto_maglev_proto_init() {
return return
} }
file_proto_maglev_proto_msgTypes[18].OneofWrappers = []any{} file_proto_maglev_proto_msgTypes[18].OneofWrappers = []any{}
file_proto_maglev_proto_msgTypes[25].OneofWrappers = []any{} file_proto_maglev_proto_msgTypes[24].OneofWrappers = []any{}
file_proto_maglev_proto_msgTypes[41].OneofWrappers = []any{ file_proto_maglev_proto_msgTypes[40].OneofWrappers = []any{
(*Event_Log)(nil), (*Event_Log)(nil),
(*Event_Backend)(nil), (*Event_Backend)(nil),
(*Event_Frontend)(nil), (*Event_Frontend)(nil),
@@ -2911,7 +2831,7 @@ func file_proto_maglev_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_maglev_proto_rawDesc), len(file_proto_maglev_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_maglev_proto_rawDesc), len(file_proto_maglev_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 42, NumMessages: 41,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@@ -375,10 +375,17 @@ func (s *Server) GetVPPLBState(_ context.Context, _ *GetVPPLBStateRequest) (*VPP
return lbStateToProto(state), nil return lbStateToProto(state), nil
} }
// GetVPPLBCounters returns the most recent per-VIP and per-backend counter // GetVPPLBCounters returns the most recent per-VIP counter snapshot
// snapshot captured by the client's 5s scrape loop. The call is served // captured by the client's 5s scrape loop. The call is served from an
// from an in-process cache and does not hit VPP. An empty response is // in-process cache and does not hit VPP. An empty response is returned
// returned when VPP is disconnected or no scrape has completed yet. // when VPP is disconnected or no scrape has completed yet.
//
// Per-backend counters are deliberately not included: the LB plugin's
// forwarding node bypasses the ip{4,6}-lookup path, so the FIB's
// /net/route/to counter at a backend's stats_index never ticks for
// LB-forwarded traffic (see internal/vpp/lbstats.go::scrapeLBStats
// for the full chain). Exposing a zero-valued "packets" column would
// just mislead operators.
func (s *Server) GetVPPLBCounters(_ context.Context, _ *GetVPPLBCountersRequest) (*VPPLBCounters, error) { func (s *Server) GetVPPLBCounters(_ context.Context, _ *GetVPPLBCountersRequest) (*VPPLBCounters, error) {
if s.vppClient == nil { if s.vppClient == nil {
return nil, status.Error(codes.Unavailable, "VPP integration is disabled") return nil, status.Error(codes.Unavailable, "VPP integration is disabled")
@@ -406,17 +413,6 @@ func (s *Server) GetVPPLBCounters(_ context.Context, _ *GetVPPLBCountersRequest)
} }
return out.Vips[i].Port < out.Vips[j].Port return out.Vips[i].Port < out.Vips[j].Port
}) })
for _, b := range s.vppClient.BackendRouteStats() {
out.Backends = append(out.Backends, &VPPLBBackendCounters{
Backend: b.Backend,
Address: b.Address,
Packets: b.Packets,
Bytes: b.Bytes,
})
}
sort.Slice(out.Backends, func(i, j int) bool {
return out.Backends[i].Backend < out.Backends[j].Backend
})
return out, nil return out, nil
} }

View File

@@ -66,17 +66,6 @@ type VIPStatEntry struct {
Bytes uint64 Bytes uint64
} }
// BackendRouteStat is a point-in-time snapshot of the FIB combined counter
// (/net/route/to) for a single backend's host prefix. Values are summed
// across worker threads. Labels match the backend's identity so a time
// series corresponds 1:1 to a maglev backend entry.
type BackendRouteStat struct {
Backend string // backend name from the config
Address string // backend IP address as a string (e.g. "192.0.2.10")
Packets uint64
Bytes uint64
}
// VPPSource provides read-only access to the VPP client's state. vpp.Client // VPPSource provides read-only access to the VPP client's state. vpp.Client
// is adapted to this interface via a small shim in the collector so the // is adapted to this interface via a small shim in the collector so the
// metrics package stays decoupled from the vpp package's concrete types. // metrics package stays decoupled from the vpp package's concrete types.
@@ -87,11 +76,6 @@ type VPPSource interface {
// counters, as captured by the LB stats loop. Returns nil when VPP is // counters, as captured by the LB stats loop. Returns nil when VPP is
// disconnected or no scrape has happened yet. // disconnected or no scrape has happened yet.
VIPStats() []VIPStatEntry VIPStats() []VIPStatEntry
// BackendRouteStats returns the most recent snapshot of per-backend
// FIB combined counters (/net/route/to), as captured by the LB stats
// loop. Returns nil when VPP is disconnected, no scrape has happened
// yet, or the route lookup for every backend failed.
BackendRouteStats() []BackendRouteStat
} }
// ---- inline metrics (updated per probe) ------------------------------------ // ---- inline metrics (updated per probe) ------------------------------------
@@ -160,11 +144,9 @@ type Collector struct {
vppConnectedFor *prometheus.Desc vppConnectedFor *prometheus.Desc
vppInfo *prometheus.Desc vppInfo *prometheus.Desc
vipPackets *prometheus.Desc // per-VIP LB counters from stats segment vipPackets *prometheus.Desc // per-VIP LB counters from stats segment
vipRoutePkts *prometheus.Desc // per-VIP FIB combined counter: packets vipRoutePkts *prometheus.Desc // per-VIP FIB combined counter: packets
vipRouteByts *prometheus.Desc // per-VIP FIB combined counter: bytes vipRouteByts *prometheus.Desc // per-VIP FIB combined counter: bytes
backendRoutePkts *prometheus.Desc // per-backend FIB combined counter: packets
backendRouteByts *prometheus.Desc // per-backend FIB combined counter: bytes
} }
// NewCollector creates a Collector backed by the given StateSource. vpp may // NewCollector creates a Collector backed by the given StateSource. vpp may
@@ -229,16 +211,6 @@ func NewCollector(src StateSource, vpp VPPSource) *Collector {
"Bytes forwarded by VPP's FIB toward each VIP's host prefix (from /net/route/to), summed across workers.", "Bytes forwarded by VPP's FIB toward each VIP's host prefix (from /net/route/to), summed across workers.",
[]string{"prefix", "protocol", "port"}, nil, []string{"prefix", "protocol", "port"}, nil,
), ),
backendRoutePkts: prometheus.NewDesc(
"maglev_vpp_backend_route_packets_total",
"Packets forwarded by VPP's FIB toward each backend's host prefix (from /net/route/to), summed across workers.",
[]string{"backend", "address"}, nil,
),
backendRouteByts: prometheus.NewDesc(
"maglev_vpp_backend_route_bytes_total",
"Bytes forwarded by VPP's FIB toward each backend's host prefix (from /net/route/to), summed across workers.",
[]string{"backend", "address"}, nil,
),
} }
} }
@@ -255,8 +227,6 @@ func (c *Collector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.vipPackets ch <- c.vipPackets
ch <- c.vipRoutePkts ch <- c.vipRoutePkts
ch <- c.vipRouteByts ch <- c.vipRouteByts
ch <- c.backendRoutePkts
ch <- c.backendRouteByts
} }
// Collect implements prometheus.Collector. // Collect implements prometheus.Collector.
@@ -353,6 +323,13 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) {
// stats loop in internal/vpp. CounterValue so rate()/increase() work // stats loop in internal/vpp. CounterValue so rate()/increase() work
// as expected; VPP counter resets (e.g. VIP recreate) are handled by // as expected; VPP counter resets (e.g. VIP recreate) are handled by
// Prometheus's built-in counter-reset detection. // Prometheus's built-in counter-reset detection.
//
// No per-backend counters are exposed here: the LB plugin's
// forwarding node sets adj_index[VLIB_TX] directly and bypasses
// ip{4,6}_lookup_inline, which is the only path that increments
// lbm_to_counters — so /net/route/to at the backend's stats_index
// never ticks for LB-forwarded traffic. See the comment block in
// internal/vpp/lbstats.go::scrapeLBStats for the full chain.
for _, v := range c.vpp.VIPStats() { for _, v := range c.vpp.VIPStats() {
port := fmt.Sprintf("%d", v.Port) port := fmt.Sprintf("%d", v.Port)
ch <- prometheus.MustNewConstMetric(c.vipPackets, prometheus.CounterValue, float64(v.NextPkt), v.Prefix, v.Protocol, port, "next") ch <- prometheus.MustNewConstMetric(c.vipPackets, prometheus.CounterValue, float64(v.NextPkt), v.Prefix, v.Protocol, port, "next")
@@ -362,13 +339,6 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(c.vipRoutePkts, prometheus.CounterValue, float64(v.Packets), v.Prefix, v.Protocol, port) ch <- prometheus.MustNewConstMetric(c.vipRoutePkts, prometheus.CounterValue, float64(v.Packets), v.Prefix, v.Protocol, port)
ch <- prometheus.MustNewConstMetric(c.vipRouteByts, prometheus.CounterValue, float64(v.Bytes), v.Prefix, v.Protocol, port) ch <- prometheus.MustNewConstMetric(c.vipRouteByts, prometheus.CounterValue, float64(v.Bytes), v.Prefix, v.Protocol, port)
} }
// Per-backend FIB counters from /net/route/to. Same CounterValue
// semantics as above.
for _, b := range c.vpp.BackendRouteStats() {
ch <- prometheus.MustNewConstMetric(c.backendRoutePkts, prometheus.CounterValue, float64(b.Packets), b.Backend, b.Address)
ch <- prometheus.MustNewConstMetric(c.backendRouteByts, prometheus.CounterValue, float64(b.Bytes), b.Backend, b.Address)
}
} }
// Register registers all metrics with the given registry. vpp may be nil // Register registers all metrics with the given registry. vpp may be nil

View File

@@ -66,11 +66,6 @@ type Client struct {
// lbStatsLoop. Published as an immutable slice via atomic.Pointer so // lbStatsLoop. Published as an immutable slice via atomic.Pointer so
// Prometheus scrapes (metrics.Collector.Collect) don't take any lock. // Prometheus scrapes (metrics.Collector.Collect) don't take any lock.
lbStatsSnap atomic.Pointer[[]metrics.VIPStatEntry] lbStatsSnap atomic.Pointer[[]metrics.VIPStatEntry]
// backendRouteSnap is the most recent per-backend FIB stats snapshot
// captured by lbStatsLoop. Same atomic-pointer publish pattern as
// lbStatsSnap; see logBackendRouteStats in fibstats.go.
backendRouteSnap atomic.Pointer[[]metrics.BackendRouteStat]
} }
// SetStateSource attaches a live config + health state source. When set, the // SetStateSource attaches a live config + health state source. When set, the
@@ -241,18 +236,6 @@ func (c *Client) VIPStats() []metrics.VIPStatEntry {
return *p return *p
} }
// BackendRouteStats satisfies metrics.VPPSource. It returns the latest
// snapshot of per-backend FIB combined counters (/net/route/to) captured
// by lbStatsLoop. Returns nil until the first scrape completes, or after
// a disconnect.
func (c *Client) BackendRouteStats() []metrics.BackendRouteStat {
p := c.backendRouteSnap.Load()
if p == nil {
return nil
}
return *p
}
// VPPInfo satisfies metrics.VPPSource. It returns a copy of the cached // VPPInfo satisfies metrics.VPPSource. It returns a copy of the cached
// connection info as a metrics-local struct so the metrics package doesn't // connection info as a metrics-local struct so the metrics package doesn't
// need to import internal/vpp. Second return is false when VPP is not // need to import internal/vpp. Second return is false when VPP is not
@@ -309,7 +292,6 @@ func (c *Client) disconnect() {
c.lastLBConf = nil // force re-push of lb_conf on reconnect c.lastLBConf = nil // force re-push of lb_conf on reconnect
c.mu.Unlock() c.mu.Unlock()
c.lbStatsSnap.Store(nil) c.lbStatsSnap.Store(nil)
c.backendRouteSnap.Store(nil)
safeDisconnectAPI(apiConn) safeDisconnectAPI(apiConn)
safeDisconnectStats(statsConn) safeDisconnectStats(statsConn)

View File

@@ -6,7 +6,6 @@ import (
"context" "context"
"fmt" "fmt"
"log/slog" "log/slog"
"sort"
"time" "time"
"go.fd.io/govpp/adapter" "go.fd.io/govpp/adapter"
@@ -20,25 +19,30 @@ import (
const lbStatsInterval = 5 * time.Second const lbStatsInterval = 5 * time.Second
// LB VIP counter names as they appear in the VPP stats segment. These // LB VIP counter names as they appear in the VPP stats segment. These
// come from lb_foreach_vip_counter in src/plugins/lb/lb.h — each entry is // come from lb_foreach_vip_counter in src/plugins/lb/lb.h — the plugin
// registered with only .name set, so the stats segment exposes them at // passes them through vlib_simple_counter_main_t with only .name set
// the top level (spaces and all). Replace if the VPP plugin renames them. // (.stat_segment_name unset), so vlib_validate_simple_counter /
// vlib_stats_add_counter_vector register them under the name verbatim
// with no leading slash. Contrast with /net/route/to below, which IS
// registered with stat_segment_name="/net/route/to" in
// src/vnet/dpo/load_balance.c. Replace if the VPP plugin renames them
// or starts setting stat_segment_name.
const ( const (
lbStatNextPacket = "/packet from existing sessions" lbStatNextPacket = "packet from existing sessions"
lbStatFirstPacket = "/first session packet" lbStatFirstPacket = "first session packet"
lbStatUntrackedPkt = "/untracked packet" lbStatUntrackedPkt = "untracked packet"
lbStatNoServer = "/no server configured" lbStatNoServer = "no server configured"
) )
// lbStatPatterns is the full list of anchored regexes passed to DumpStats // lbStatPatterns is the full list of anchored regexes passed to DumpStats
// for one scrape cycle: the four LB-plugin SimpleCounters plus the FIB // for one scrape cycle: the four LB-plugin SimpleCounters plus the FIB
// CombinedCounter for per-route packet+byte totals. Doing it in a single // CombinedCounter for per-VIP packet+byte totals. Doing it in a single
// DumpStats avoids walking the stats segment twice. // DumpStats avoids walking the stats segment twice.
var lbStatPatterns = []string{ var lbStatPatterns = []string{
`^/packet from existing sessions$`, `^packet from existing sessions$`,
`^/first session packet$`, `^first session packet$`,
`^/untracked packet$`, `^untracked packet$`,
`^/no server configured$`, `^no server configured$`,
`^/net/route/to$`, `^/net/route/to$`,
} }
@@ -63,9 +67,24 @@ func (c *Client) lbStatsLoop(ctx context.Context) {
} }
// scrapeLBStats runs one full scrape cycle: discover VIPs via cli_inband, // scrapeLBStats runs one full scrape cycle: discover VIPs via cli_inband,
// look up FIB stats_indices for every VIP and every backend via // look up the FIB stats_index for every VIP via ip_route_lookup, dump
// ip_route_lookup, dump all five stats-segment paths in one DumpStats // all five stats-segment paths in one DumpStats call, and reduce the
// call, and reduce the counters into the two published snapshots. // counters into the per-VIP snapshot.
//
// NOTE: there is no per-backend counter here. The LB plugin's forwarding
// node (src/plugins/lb/node.c) sets adj_index[VLIB_TX] to the AS's DPO
// directly and enqueues into the lb*-gre* encap node, bypassing
// ip6_lookup_inline / ip4_lookup_inline entirely. Since
// lbm_to_counters is only incremented in those lookup paths (see
// ip6_forward.h / ip4_forward.h), the backend's FIB-entry
// load_balance stats_index never ticks for LB-forwarded traffic —
// /net/route/to at that index would always report zero. Per-backend
// packet/byte counters would need a new counter inside the LB plugin
// itself (e.g. a vlib_combined_counter_main_t keyed by AS index
// incremented in lb4-gre4 / lb6-gre6 / etc.). Until that lands
// upstream we simply don't expose per-backend rates; the VIP-level
// counters tell the dataplane story on the granularity VPP actually
// provides today.
func (c *Client) scrapeLBStats() error { func (c *Client) scrapeLBStats() error {
if !c.IsConnected() { if !c.IsConnected() {
return nil return nil
@@ -100,37 +119,6 @@ func (c *Client) scrapeLBStats() error {
vipStatsIdx[k] = idx vipStatsIdx[k] = idx
} }
// Resolve FIB stats_indices for every backend in the running config.
type backendLookup struct {
name, addr string
index uint32
}
var backends []backendLookup
if src := c.getStateSource(); src != nil {
if cfg := src.Config(); cfg != nil {
names := make([]string, 0, len(cfg.Backends))
for name := range cfg.Backends {
names = append(names, name)
}
sort.Strings(names) // stable snapshot order
for _, name := range names {
b := cfg.Backends[name]
if b.Address == nil {
continue
}
idx, err := fibStatsIndex(ch, b.Address)
if err != nil {
slog.Debug("vpp-backend-route-lookup-failed",
"backend", name, "address", b.Address.String(), "err", err)
continue
}
backends = append(backends, backendLookup{
name: name, addr: b.Address.String(), index: idx,
})
}
}
}
c.mu.Lock() c.mu.Lock()
sc := c.statsClient sc := c.statsClient
c.mu.Unlock() c.mu.Unlock()
@@ -178,26 +166,6 @@ func (c *Client) scrapeLBStats() error {
) )
} }
c.lbStatsSnap.Store(&vipOut) c.lbStatsSnap.Store(&vipOut)
// ---- backend snapshot ----
backendOut := make([]metrics.BackendRouteStat, 0, len(backends))
for _, l := range backends {
pkts, byts := reduceCombinedCounter(routeTo, int(l.index))
entry := metrics.BackendRouteStat{
Backend: l.name,
Address: l.addr,
Packets: pkts,
Bytes: byts,
}
backendOut = append(backendOut, entry)
slog.Debug("vpp-backend-route-stats",
"backend", entry.Backend,
"address", entry.Address,
"packets", entry.Packets,
"bytes", entry.Bytes,
)
}
c.backendRouteSnap.Store(&backendOut)
return nil return nil
} }

View File

@@ -251,27 +251,25 @@ func reconcileVIP(ch *loggedChannel, d desiredVIP, cur *LBVIP, curSticky bool, f
} }
if curSticky != d.SrcIPSticky { if curSticky != d.SrcIPSticky {
slog.Info("vpp-lb-sync-vip-recreate", return recreateVIP(ch, d, *cur, st, "src-ip-sticky-changed",
"vip", d.Prefix.IP.String(), "from", curSticky, "to", d.SrcIPSticky)
"protocol", protocolName(d.Protocol), }
"port", d.Port,
"reason", "src-ip-sticky-changed", // Encap mismatch: every backend flipped address family (e.g. all
"from", curSticky, // IPv6 → all IPv4 after a config edit). VPP's encap is a VIP-level
"to", d.SrcIPSticky) // attribute set at lb_add_del_vip time with no mutation API, so
if err := removeVIP(ch, *cur, st); err != nil { // adding new-family ASes under the old encap wedges the
return err // reconciler: packets would be wrapped for the wrong family and
} // the new backends never see traffic. The only recovery is a VIP
if err := addVIP(ch, d); err != nil { // recreate. The old-family ASes end up orphaned in VPP's pool and
return err // are GC'd on the plugin's own ~40s "USED-flag=false" schedule;
} // the next regular sync tick confirms steady state. A recreate
st.vipAdd++ // does tear down existing flows to the VIP, which is why we gate
for _, as := range d.ASes { // on an explicit encap difference rather than reconciling every
if err := addAS(ch, d.Prefix, d.Protocol, d.Port, as); err != nil { // sync cycle.
return err if desiredEncap := encapString(d.Encap); desiredEncap != cur.Encap {
} return recreateVIP(ch, d, *cur, st, "encap-changed",
st.asAdd++ "from", cur.Encap, "to", desiredEncap)
}
return nil
} }
// VIP exists in both — reconcile ASes. // VIP exists in both — reconcile ASes.
@@ -301,20 +299,38 @@ func reconcileVIP(ch *loggedChannel, d desiredVIP, cur *LBVIP, curSticky bool, f
st.asAdd++ st.asAdd++
continue continue
} }
if c.Weight != a.Weight { // setASWeight is issued whenever the weight changes OR whenever
// Flush only on the transition from serving traffic (cur > 0) to // the state machine asks for a flush (a.Flush=true, currently
// zero, and only when the desired state explicitly asks for it // emitted only for StateDisabled). The a.Flush path has to
// (i.e. the backend was disabled, not merely drained). Steady- // fire even on a no-op weight change, because a backend can
// state syncs where weight doesn't change never re-flush. // reach StateDisabled via a pool-failover that already drained
flush := a.Flush && c.Weight > 0 && a.Weight == 0 // its VPP weight to 0 on an earlier tick — at that moment
// Caller-forced flush: used by SetFrontendPoolBackendWeight // c.Weight == a.Weight == 0, and a gate keyed solely on the
// with flush=true to explicitly drop live sessions for a // weight diff would silently drop the flush intent and leave
// single backend. The address match is exact — no other // stale sticky-cache entries pointing at the now-disabled AS
// AS's weight change is affected, even if several happen // (see the "disable nlams0 after fallback deactivation" trace
// in the same reconcile pass. // in the bug investigation).
if flushAddress != "" && addr == flushAddress { //
flush = true // Firing unconditionally on a.Flush is idempotent at VPP's
} // side: lb_as_set_weight with an unchanged weight is a no-op
// on the Maglev table (lb_vip_update_new_flow_table rebuilds
// the same table), and a redundant lb_flush_vip_as is bounded
// — it walks each per-worker sticky_ht once. The trade-off is
// that disabled backends re-issue the flush on every periodic
// SyncLBStateAll tick, and any sticky entries that happened to
// land in the meantime get cleared; both are acceptable for
// the "correctness over churn" semantics we want here.
weightChanged := c.Weight != a.Weight
flush := a.Flush
// Caller-forced flush: used by SetFrontendPoolBackendWeight
// with flush=true to explicitly drop live sessions for a
// single backend. The address match is exact — no other
// AS's weight change is affected, even if several happen
// in the same reconcile pass.
if flushAddress != "" && addr == flushAddress {
flush = true
}
if weightChanged || flush {
if err := setASWeight(ch, d.Prefix, d.Protocol, d.Port, a, c.Weight, flush); err != nil { if err := setASWeight(ch, d.Prefix, d.Protocol, d.Port, a, c.Weight, flush); err != nil {
return err return err
} }
@@ -324,6 +340,37 @@ func reconcileVIP(ch *loggedChannel, d desiredVIP, cur *LBVIP, curSticky bool, f
return nil return nil
} }
// recreateVIP tears down an existing VIP and rebuilds it with the
// desired configuration and ASes. Used when a VIP attribute that VPP
// can't mutate in place has changed — today src_ip_sticky and the
// encap family. reason is logged as an operator-facing explanation;
// extra is appended to the slog call as additional fields (typically
// "from", <oldvalue>, "to", <newvalue>).
func recreateVIP(ch *loggedChannel, d desiredVIP, cur LBVIP, st *syncStats, reason string, extra ...any) error {
logAttrs := []any{
"vip", d.Prefix.IP.String(),
"protocol", protocolName(d.Protocol),
"port", d.Port,
"reason", reason,
}
logAttrs = append(logAttrs, extra...)
slog.Info("vpp-lb-sync-vip-recreate", logAttrs...)
if err := removeVIP(ch, cur, st); err != nil {
return err
}
if err := addVIP(ch, d); err != nil {
return err
}
st.vipAdd++
for _, as := range d.ASes {
if err := addAS(ch, d.Prefix, d.Protocol, d.Port, as); err != nil {
return err
}
st.asAdd++
}
return nil
}
// removeVIP flushes all ASes from a VIP and then deletes the VIP itself. // removeVIP flushes all ASes from a VIP and then deletes the VIP itself.
func removeVIP(ch *loggedChannel, v LBVIP, st *syncStats) error { func removeVIP(ch *loggedChannel, v LBVIP, st *syncStats) error {
for _, as := range v.ASes { for _, as := range v.ASes {

View File

@@ -152,18 +152,18 @@ message VPPLBVIPCounters {
uint64 bytes = 9; // /net/route/to (FIB, summed across workers) uint64 bytes = 9; // /net/route/to (FIB, summed across workers)
} }
// VPPLBBackendCounters is the FIB combined counter for a single backend's // VPPLBCounters wraps the per-VIP counter list returned by
// host prefix, summed across worker threads. // GetVPPLBCounters. There is no per-backend counter block: VPP's LB
message VPPLBBackendCounters { // plugin forwarding node bypasses ip{4,6}_lookup_inline and writes
string backend = 1; // backend name from config // adj_index[VLIB_TX] directly, so /net/route/to at a backend's FIB
string address = 2; // backend IP address // entry never ticks for LB-forwarded traffic — the four per-VIP
uint64 packets = 3; // counters are the only per-VIP-and-coarser signal VPP exposes today.
uint64 bytes = 4;
}
message VPPLBCounters { message VPPLBCounters {
repeated VPPLBVIPCounters vips = 1; repeated VPPLBVIPCounters vips = 1;
repeated VPPLBBackendCounters backends = 2; // Field 2 (repeated VPPLBBackendCounters backends) was removed; the
// index is reserved so a future replacement doesn't accidentally
// reuse it.
reserved 2;
} }
message SetWeightRequest { message SetWeightRequest {