From b558a172bf32470fba21fa3fa340c6037f8f3553 Mon Sep 17 00:00:00 2001 From: Shangqi Liu Date: Mon, 8 Jun 2026 22:25:50 +0800 Subject: [PATCH 1/2] testing/fake/gnmi: use Path.Elem instead of deprecated Path.Element MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit valToResp() was building gNMI SubscribeResponse messages with &gpb.Path{Element: val.Path}, where Element is the []string field that was deprecated in gNMI v0.4 (2018) in favour of Path.Elem ([]*PathElem). Modern consumers — gnmic v0.44+, openconfig/gnmi libraries, and VictoriaMetrics exporters — call p.GetElem() only and silently ignore Element. The result is that every subscription response from the fake server carries an empty path, causing all metric values to land under "/" rather than their real OpenConfig paths. Add stringsToPathElems() which converts the []string path representation from the fake proto config into []*PathElem, correctly extracting key-value pairs from bracketed decorators such as "interface[name=swp1]" → PathElem{Name:"interface", Key:{"name":"swp1"}}. Both the Delete and Update branches of valToResp() are updated to use Path.Elem via this helper. Also add: - TestStringsToPathElems: unit tests for the new helper covering simple elements, single-key elements, multi-key elements, and empty-string filtering. - TestValToRespPathEncoding: integration-style test asserting that responses from valToResp() populate Path.Elem and leave the deprecated Path.Element empty. - cumulus-linux.pb.txt: a Cumulus Linux 5.x simulation config for the fake_server command (interfaces swp1-swp4, temperature sensors, fans, PSU, QoS buffers) that serves as a realistic test fixture and demonstrates the fix with real-world OpenConfig paths. --- testing/fake/gnmi/client.go | 32 +- .../gnmi/cmd/fake_server/cumulus-linux.pb.txt | 681 ++++++++++++++++++ testing/fake/gnmi/gnmi_test.go | 86 +++ 3 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 testing/fake/gnmi/cmd/fake_server/cumulus-linux.pb.txt diff --git a/testing/fake/gnmi/client.go b/testing/fake/gnmi/client.go index da33502b..470b696d 100644 --- a/testing/fake/gnmi/client.go +++ b/testing/fake/gnmi/client.go @@ -19,6 +19,8 @@ package gnmi import ( "fmt" "io" + "regexp" + "strings" "sync" log "github.com/golang/glog" @@ -32,6 +34,32 @@ import ( fpb "github.com/openconfig/gnmi/testing/fake/proto" ) +// elemKeyRe matches a path element with optional key(s), e.g. "interface[name=eth0][vlan=100]". +var elemKeyRe = regexp.MustCompile(`\[([^=\]]+)=([^\]]+)\]`) + +// stringsToPathElems converts the old-style []string path (Element field) to +// the current []*gpb.PathElem (Elem field) so that gnmic and other modern +// gNMI consumers can resolve the full path from subscription responses. +func stringsToPathElems(elems []string) []*gpb.PathElem { + out := make([]*gpb.PathElem, 0, len(elems)) + for _, e := range elems { + name := elemKeyRe.ReplaceAllString(e, "") + pe := &gpb.PathElem{Name: name} + for _, m := range elemKeyRe.FindAllStringSubmatch(e, -1) { + if pe.Key == nil { + pe.Key = make(map[string]string) + } + pe.Key[m[1]] = m[2] + } + // guard: skip empty elements that can arise from leading slashes + if strings.TrimSpace(pe.Name) == "" && len(pe.Key) == 0 { + continue + } + out = append(out, pe) + } + return out +} + // Client contains information about a client that has connected to the fake. type Client struct { errors int64 @@ -336,7 +364,7 @@ func valToResp(val *fpb.Value) (*gpb.SubscribeResponse, error) { Response: &gpb.SubscribeResponse_Update{ Update: &gpb.Notification{ Timestamp: val.Timestamp.Timestamp, - Delete: []*gpb.Path{{Element: val.Path}}, + Delete: []*gpb.Path{{Elem: stringsToPathElems(val.Path)}}, }, }, }, nil @@ -361,7 +389,7 @@ func valToResp(val *fpb.Value) (*gpb.SubscribeResponse, error) { Timestamp: val.Timestamp.Timestamp, Update: []*gpb.Update{ { - Path: &gpb.Path{Element: val.Path}, + Path: &gpb.Path{Elem: stringsToPathElems(val.Path)}, Val: tv, }, }, diff --git a/testing/fake/gnmi/cmd/fake_server/cumulus-linux.pb.txt b/testing/fake/gnmi/cmd/fake_server/cumulus-linux.pb.txt new file mode 100644 index 00000000..4262e724 --- /dev/null +++ b/testing/fake/gnmi/cmd/fake_server/cumulus-linux.pb.txt @@ -0,0 +1,681 @@ +# Cumulus Linux 5.x gNMI streaming metrics simulation +# Models a 4-port switch (swp1-swp4) with platform sensors and QoS buffers. +# Paths match OpenConfig models used by Cumulus Linux 5.16 gNMI agent. +# See: https://docs.nvidia.com/networking-ethernet-software/cumulus-linux-516/Monitoring-and-Troubleshooting/gNMI-Streaming/#metrics + +target: "cumulus-switch-01" +client_type: GRPC_GNMI +disable_eof: true + +# ─── Interface swp1 ──────────────────────────────────────────────────────────── + +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "oper-status" + timestamp: < timestamp: 1000000000 delta_min: 30000000000 delta_max: 30000000000 > + string_value: < value: "UP" > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "admin-status" + timestamp: < timestamp: 1010000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "UP" > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "ethernet" + path: "state" + path: "port-speed" + timestamp: < timestamp: 1020000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "SPEED_10GB" > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "ethernet" + path: "state" + path: "negotiated-duplex-mode" + timestamp: < timestamp: 1030000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "FULL" > +> +# swp1 counters — monotonically increasing (delta_min/delta_max drive cumulative increments) +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "in-pkts" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 5000 delta_max: 50000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "out-pkts" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 4000 delta_max: 45000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "in-octets" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000000000 delta_min: 600000 delta_max: 6000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "out-octets" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000000000 delta_min: 500000 delta_max: 5000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "in-unicast-pkts" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 4800 delta_max: 48000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "out-unicast-pkts" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 3900 delta_max: 43000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "in-multicast-pkts" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 1000000000000 delta_min: 10 delta_max: 500 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "in-broadcast-pkts" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000000 delta_min: 1 delta_max: 50 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "in-errors" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 1000000 delta_min: 0 delta_max: 2 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "out-errors" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 1000000 delta_min: 0 delta_max: 1 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "in-discards" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000 delta_min: 0 delta_max: 5 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "state" + path: "counters" + path: "out-discards" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000 delta_min: 0 delta_max: 3 > > +> +# swp1 Ethernet error counters +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "ethernet" + path: "state" + path: "counters" + path: "in-fcs-errors" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000 delta_min: 0 delta_max: 1 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "ethernet" + path: "state" + path: "counters" + path: "in-mac-pause-frames" + timestamp: < timestamp: 1000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000 delta_min: 0 delta_max: 100 > > +> +# swp1 rates (fluctuating, not cumulative — no delta_min/delta_max on range) +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "rates" + path: "state" + path: "in-bits-rate" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 1500000000 range: < minimum: 100000000 maximum: 9000000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "rates" + path: "state" + path: "out-bits-rate" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 1200000000 range: < minimum: 100000000 maximum: 9000000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "rates" + path: "state" + path: "in-pkts-rate" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 125000 range: < minimum: 8000 maximum: 750000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp1]" + path: "rates" + path: "state" + path: "out-pkts-rate" + timestamp: < timestamp: 1000000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 100000 range: < minimum: 8000 maximum: 750000 > > +> + +# ─── Interface swp2 ──────────────────────────────────────────────────────────── + +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "state" + path: "oper-status" + timestamp: < timestamp: 1100000000 delta_min: 30000000000 delta_max: 30000000000 > + string_value: < value: "UP" > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "state" + path: "admin-status" + timestamp: < timestamp: 1110000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "UP" > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "ethernet" + path: "state" + path: "port-speed" + timestamp: < timestamp: 1120000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "SPEED_10GB" > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "state" + path: "counters" + path: "in-pkts" + timestamp: < timestamp: 1100000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 3000 delta_max: 30000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "state" + path: "counters" + path: "out-pkts" + timestamp: < timestamp: 1100000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 2500 delta_max: 25000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "state" + path: "counters" + path: "in-octets" + timestamp: < timestamp: 1100000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000000000 delta_min: 400000 delta_max: 4000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "state" + path: "counters" + path: "out-octets" + timestamp: < timestamp: 1100000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000000000 delta_min: 350000 delta_max: 3500000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "state" + path: "counters" + path: "in-errors" + timestamp: < timestamp: 1100000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 1000000 delta_min: 0 delta_max: 1 > > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "state" + path: "counters" + path: "out-errors" + timestamp: < timestamp: 1100000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 1000000 delta_min: 0 delta_max: 1 > > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "rates" + path: "state" + path: "in-bits-rate" + timestamp: < timestamp: 1100000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 800000000 range: < minimum: 50000000 maximum: 9000000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp2]" + path: "rates" + path: "state" + path: "out-bits-rate" + timestamp: < timestamp: 1100000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 750000000 range: < minimum: 50000000 maximum: 9000000000 > > +> + +# ─── Interface swp3 ──────────────────────────────────────────────────────────── + +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "state" + path: "oper-status" + timestamp: < timestamp: 1200000000 delta_min: 30000000000 delta_max: 30000000000 > + string_value: < value: "UP" > +> +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "state" + path: "admin-status" + timestamp: < timestamp: 1210000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "UP" > +> +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "ethernet" + path: "state" + path: "port-speed" + timestamp: < timestamp: 1220000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "SPEED_100GB" > +> +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "state" + path: "counters" + path: "in-pkts" + timestamp: < timestamp: 1200000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 50000 delta_max: 500000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "state" + path: "counters" + path: "out-pkts" + timestamp: < timestamp: 1200000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 45000 delta_max: 480000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "state" + path: "counters" + path: "in-octets" + timestamp: < timestamp: 1200000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000000000 delta_min: 5000000 delta_max: 60000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "state" + path: "counters" + path: "out-octets" + timestamp: < timestamp: 1200000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000000000 delta_min: 4500000 delta_max: 55000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "rates" + path: "state" + path: "in-bits-rate" + timestamp: < timestamp: 1200000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 40000000000 range: < minimum: 1000000000 maximum: 95000000000 > > +> +values: < + path: "interfaces" + path: "interface[name=swp3]" + path: "rates" + path: "state" + path: "out-bits-rate" + timestamp: < timestamp: 1200000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 38000000000 range: < minimum: 1000000000 maximum: 95000000000 > > +> + +# ─── Interface swp4 (DOWN — link state simulation) ──────────────────────────── + +values: < + path: "interfaces" + path: "interface[name=swp4]" + path: "state" + path: "oper-status" + timestamp: < timestamp: 1300000000 delta_min: 30000000000 delta_max: 30000000000 > + string_value: < value: "DOWN" > +> +values: < + path: "interfaces" + path: "interface[name=swp4]" + path: "state" + path: "admin-status" + timestamp: < timestamp: 1310000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "UP" > +> +values: < + path: "interfaces" + path: "interface[name=swp4]" + path: "state" + path: "counters" + path: "in-pkts" + timestamp: < timestamp: 1300000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 142876 range: < minimum: 142876 maximum: 10000000000000 delta_min: 0 delta_max: 0 > > +> +values: < + path: "interfaces" + path: "interface[name=swp4]" + path: "state" + path: "counters" + path: "out-pkts" + timestamp: < timestamp: 1300000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 98312 range: < minimum: 98312 maximum: 10000000000000 delta_min: 0 delta_max: 0 > > +> +values: < + path: "interfaces" + path: "interface[name=swp4]" + path: "state" + path: "counters" + path: "in-errors" + timestamp: < timestamp: 1300000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 4 range: < minimum: 4 maximum: 10000 delta_min: 0 delta_max: 1 > > +> +values: < + path: "interfaces" + path: "interface[name=swp4]" + path: "rates" + path: "state" + path: "in-bits-rate" + timestamp: < timestamp: 1300000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 0 > > +> +values: < + path: "interfaces" + path: "interface[name=swp4]" + path: "rates" + path: "state" + path: "out-bits-rate" + timestamp: < timestamp: 1300000000 delta_min: 5000000000 delta_max: 5000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 0 > > +> + +# ─── Platform: Temperature sensors ──────────────────────────────────────────── + +# ASIC temperature (Spectrum ASIC typically runs 60-80°C under load) +values: < + path: "components" + path: "component[name=ASIC]" + path: "state" + path: "temperature" + path: "instant" + timestamp: < timestamp: 2000000000 delta_min: 10000000000 delta_max: 10000000000 > + double_value: < value: 68.5 range: < minimum: 55.0 maximum: 85.0 > > +> +values: < + path: "components" + path: "component[name=ASIC]" + path: "state" + path: "temperature" + path: "avg" + timestamp: < timestamp: 2000000000 delta_min: 60000000000 delta_max: 60000000000 > + double_value: < value: 67.0 range: < minimum: 55.0 maximum: 82.0 > > +> +values: < + path: "components" + path: "component[name=ASIC]" + path: "state" + path: "temperature" + path: "alarm-status" + timestamp: < timestamp: 2000000000 delta_min: 60000000000 delta_max: 60000000000 > + bool_value: < value: false > +> +# CPU temperature +values: < + path: "components" + path: "component[name=CPU]" + path: "state" + path: "temperature" + path: "instant" + timestamp: < timestamp: 2100000000 delta_min: 10000000000 delta_max: 10000000000 > + double_value: < value: 52.0 range: < minimum: 40.0 maximum: 70.0 > > +> +values: < + path: "components" + path: "component[name=CPU]" + path: "state" + path: "temperature" + path: "alarm-status" + timestamp: < timestamp: 2100000000 delta_min: 60000000000 delta_max: 60000000000 > + bool_value: < value: false > +> + +# ─── Platform: Fans ──────────────────────────────────────────────────────────── + +values: < + path: "components" + path: "component[name=FAN1]" + path: "fan" + path: "state" + path: "speed" + timestamp: < timestamp: 2200000000 delta_min: 10000000000 delta_max: 10000000000 > + uint_value: < value: 6200 range: < minimum: 4500 maximum: 9000 > > +> +values: < + path: "components" + path: "component[name=FAN2]" + path: "fan" + path: "state" + path: "speed" + timestamp: < timestamp: 2210000000 delta_min: 10000000000 delta_max: 10000000000 > + uint_value: < value: 6350 range: < minimum: 4500 maximum: 9000 > > +> +values: < + path: "components" + path: "component[name=FAN3]" + path: "fan" + path: "state" + path: "speed" + timestamp: < timestamp: 2220000000 delta_min: 10000000000 delta_max: 10000000000 > + uint_value: < value: 6100 range: < minimum: 4500 maximum: 9000 > > +> + +# ─── Platform: Power Supplies ────────────────────────────────────────────────── + +values: < + path: "components" + path: "component[name=PSU1]" + path: "power-supply" + path: "state" + path: "output-power" + timestamp: < timestamp: 2300000000 delta_min: 10000000000 delta_max: 10000000000 > + double_value: < value: 95.5 range: < minimum: 60.0 maximum: 150.0 > > +> +values: < + path: "components" + path: "component[name=PSU1]" + path: "power-supply" + path: "state" + path: "output-voltage" + timestamp: < timestamp: 2300000000 delta_min: 10000000000 delta_max: 10000000000 > + double_value: < value: 12.05 range: < minimum: 11.8 maximum: 12.2 > > +> +values: < + path: "components" + path: "component[name=PSU1]" + path: "power-supply" + path: "state" + path: "output-current" + timestamp: < timestamp: 2300000000 delta_min: 10000000000 delta_max: 10000000000 > + double_value: < value: 7.93 range: < minimum: 5.0 maximum: 12.5 > > +> +values: < + path: "components" + path: "component[name=PSU2]" + path: "power-supply" + path: "state" + path: "output-power" + timestamp: < timestamp: 2310000000 delta_min: 10000000000 delta_max: 10000000000 > + double_value: < value: 0.0 range: < minimum: 0.0 maximum: 0.0 > > +> +values: < + path: "components" + path: "component[name=PSU2]" + path: "state" + path: "oper-status" + timestamp: < timestamp: 2310000000 delta_min: 60000000000 delta_max: 60000000000 > + string_value: < value: "INACTIVE" > +> + +# ─── QoS: swp1 ingress priority-group buffers ───────────────────────────────── + +values: < + path: "qos" + path: "interfaces" + path: "interface[interface-id=swp1]" + path: "input" + path: "priority-group[pg-id=0]" + path: "state" + path: "shared-buffer" + path: "data" + path: "instant-occupancy" + timestamp: < timestamp: 3000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 50000 > > +> +values: < + path: "qos" + path: "interfaces" + path: "interface[interface-id=swp1]" + path: "input" + path: "priority-group[pg-id=0]" + path: "state" + path: "shared-buffer" + path: "data" + path: "max-occupancy" + timestamp: < timestamp: 3000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 1200 range: < minimum: 0 maximum: 50000 > > +> +values: < + path: "qos" + path: "interfaces" + path: "interface[interface-id=swp1]" + path: "input" + path: "priority-group[pg-id=0]" + path: "state" + path: "counters" + path: "in-pkts" + timestamp: < timestamp: 3000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 10000000000000 delta_min: 4800 delta_max: 48000 > > +> + +# ─── QoS: swp3 egress queue buffers (100G uplink) ───────────────────────────── + +values: < + path: "qos" + path: "interfaces" + path: "interface[interface-id=swp3]" + path: "output" + path: "queues" + path: "queue[name=0]" + path: "state" + path: "shared-buffer" + path: "data" + path: "instant-occupancy" + timestamp: < timestamp: 3100000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 200000 > > +> +values: < + path: "qos" + path: "interfaces" + path: "interface[interface-id=swp3]" + path: "output" + path: "queues" + path: "queue[name=0]" + path: "state" + path: "shared-buffer" + path: "data" + path: "max-occupancy" + timestamp: < timestamp: 3100000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 8000 range: < minimum: 0 maximum: 200000 > > +> +values: < + path: "qos" + path: "interfaces" + path: "interface[interface-id=swp3]" + path: "output" + path: "queues" + path: "queue[name=0]" + path: "state" + path: "transmit-octets" + timestamp: < timestamp: 3100000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000000000 delta_min: 4000000 delta_max: 50000000 > > +> + +# ─── System: Adaptive routing ───────────────────────────────────────────────── + +values: < + path: "system" + path: "adaptive-routing" + path: "state" + path: "counters" + path: "congestion-changes" + timestamp: < timestamp: 4000000000 delta_min: 1000000000 delta_max: 1000000000 > + uint_value: < value: 0 range: < minimum: 0 maximum: 100000000 delta_min: 0 delta_max: 50 > > +> diff --git a/testing/fake/gnmi/gnmi_test.go b/testing/fake/gnmi/gnmi_test.go index 7032ae6a..7c091af1 100644 --- a/testing/fake/gnmi/gnmi_test.go +++ b/testing/fake/gnmi/gnmi_test.go @@ -483,6 +483,92 @@ func TestAddRequest(t *testing.T) { }) } +func TestStringsToPathElems(t *testing.T) { + tests := []struct { + desc string + in []string + want []*gnmipb.PathElem + }{{ + desc: "simple elements without keys", + in: []string{"interfaces", "state", "counters"}, + want: []*gnmipb.PathElem{ + {Name: "interfaces"}, + {Name: "state"}, + {Name: "counters"}, + }, + }, { + desc: "element with one key", + in: []string{"interfaces", "interface[name=swp1]", "state"}, + want: []*gnmipb.PathElem{ + {Name: "interfaces"}, + {Name: "interface", Key: map[string]string{"name": "swp1"}}, + {Name: "state"}, + }, + }, { + desc: "element with multiple keys", + in: []string{"network-instances", "network-instance[name=default][type=L3VRF]"}, + want: []*gnmipb.PathElem{ + {Name: "network-instances"}, + {Name: "network-instance", Key: map[string]string{"name": "default", "type": "L3VRF"}}, + }, + }, { + desc: "empty string elements are skipped", + in: []string{"", "interfaces", ""}, + want: []*gnmipb.PathElem{ + {Name: "interfaces"}, + }, + }, { + desc: "nil / empty slice", + in: nil, + want: []*gnmipb.PathElem{}, + }} + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + got := stringsToPathElems(tt.in) + if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" { + t.Errorf("stringsToPathElems(%v) mismatch (-want +got):\n%s", tt.in, diff) + } + }) + } +} + +func TestValToRespPathEncoding(t *testing.T) { + path := []string{"interfaces", "interface[name=eth0]", "state", "counters", "in-octets"} + val := &fpb.Value{ + Path: path, + Timestamp: &fpb.Timestamp{Timestamp: 1000000000}, + Value: &fpb.Value_IntValue{IntValue: &fpb.IntValue{Value: 42}}, + } + resp, err := valToResp(val) + if err != nil { + t.Fatalf("valToResp() error: %v", err) + } + upd := resp.GetUpdate() + if upd == nil { + t.Fatal("valToResp() returned no Update") + } + if len(upd.GetUpdate()) != 1 { + t.Fatalf("expected 1 update, got %d", len(upd.GetUpdate())) + } + p := upd.GetUpdate()[0].GetPath() + if p == nil { + t.Fatal("update path is nil") + } + // Elem must be populated with the converted path. + if len(p.GetElem()) == 0 { + t.Error("Path.Elem is empty; want non-empty (deprecated Element was used instead)") + } + // Element must be empty — we no longer populate the deprecated field. + if len(p.GetElement()) != 0 { + t.Errorf("Path.Element is non-empty (%v); must be empty after migration to Elem", p.GetElement()) + } + // Verify key extraction: interface[name=eth0] → Elem{Name:"interface", Key:{"name":"eth0"}} + wantElems := stringsToPathElems(path) + if diff := cmp.Diff(wantElems, p.GetElem(), protocmp.Transform()); diff != "" { + t.Errorf("Path.Elem mismatch (-want +got):\n%s", diff) + } +} + type fakeQueue struct { val any err error From ed2095569b00d30e0ba4f2b8123401c65f368b51 Mon Sep 17 00:00:00 2001 From: Shangqi Liu Date: Mon, 8 Jun 2026 22:50:20 +0800 Subject: [PATCH 2/2] cla: re-trigger CLA check after registration