diff --git a/plugins/inputs/gnmi/handler.go b/plugins/inputs/gnmi/handler.go index c89f49547..be2140f3c 100644 --- a/plugins/inputs/gnmi/handler.go +++ b/plugins/inputs/gnmi/handler.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "net" - "path" "sort" "strconv" "strings" @@ -231,30 +230,34 @@ func (h *handler) handleSubscribeResponseUpdate(acc telegraf.Accumulator, respon h.emptyNameWarnShown = true } } + aliasInfo := newInfoFromString(aliasPath) // Group metrics - fieldPath := field.path.String() - key := strings.ReplaceAll(fieldPath, "-", "_") + var key string if h.canonicalFieldNames { // Strip the origin is any for the field names - if parts := strings.SplitN(key, ":", 2); len(parts) == 2 { + if parts := strings.SplitN(strings.ReplaceAll(field.path.String(), "-", "_"), ":", 2); len(parts) == 2 { key = parts[1] } } else { - if len(aliasPath) < len(key) && len(aliasPath) != 0 { - // This may not be an exact prefix, due to naming style - // conversion on the key. - key = key[len(aliasPath)+1:] - } else if len(aliasPath) >= len(key) { + // If the alias is a subpath of the field path and the alias is + // shorter than the full path to avoid an empty key, then strip the + // common part of the field is prefixed with the alias path. Note + // the origins can match or be empty and be considered equal. + if aliasInfo.isSubPathOf(field.path) && len(aliasInfo.segments) < len(field.path.segments) { + relative := field.path.segments[len(aliasInfo.segments):len(field.path.segments)] + key = strings.Join(relative, "/") + } else { // Otherwise use the last path element as the field key. - key = path.Base(key) + key = field.path.segments[len(field.path.segments)-1] } + key = strings.ReplaceAll(key, "-", "_") } if h.trimSlash { key = strings.TrimLeft(key, "/.") } if key == "" { - h.log.Errorf("Invalid empty path %q with alias %q", fieldPath, aliasPath) + h.log.Errorf("Invalid empty path %q with alias %q", field.path.String(), aliasPath) continue } grouper.Add(name, tags, timestamp, key, field.value) diff --git a/plugins/inputs/gnmi/path.go b/plugins/inputs/gnmi/path.go index e6ac208f8..f48ff5a3d 100644 --- a/plugins/inputs/gnmi/path.go +++ b/plugins/inputs/gnmi/path.go @@ -187,6 +187,11 @@ func (pi *pathInfo) normalize() { if len(groups) == 2 { pi.origin = groups[1] pi.segments[0] = pi.segments[0][len(groups[1])+1:] + + // if we get empty string back, remove the segment + if pi.segments[0] == "" { + pi.segments = pi.segments[1:] + } } } diff --git a/plugins/inputs/gnmi/testcases/issue_14530/expected.out b/plugins/inputs/gnmi/testcases/issue_14530/expected.out new file mode 100644 index 000000000..5bc93e343 --- /dev/null +++ b/plugins/inputs/gnmi/testcases/issue_14530/expected.out @@ -0,0 +1 @@ +ifcounters,name=Ethernet35,path=/interfaces/interface/state/counters,source=127.0.0.1 in_broadcast_pkts=0u,in_discards=0u,in_errors=0u,in_fcs_errors=0u,in_multicast_pkts=0u,in_octets=0u,in_pkts=0u,in_unicast_pkts=0u,out_broadcast_pkts=0u,out_discards=0u,out_errors=0u,out_multicast_pkts=0u,out_octets=0u,out_pkts=0u,out_unicast_pkts=0u 1704442117721474264 diff --git a/plugins/inputs/gnmi/testcases/issue_14530/responses.json b/plugins/inputs/gnmi/testcases/issue_14530/responses.json new file mode 100644 index 000000000..02a4694df --- /dev/null +++ b/plugins/inputs/gnmi/testcases/issue_14530/responses.json @@ -0,0 +1,208 @@ +[ + { + "update": { + "timestamp": "1704442117721474264", + "prefix": { + "elem": [ + { + "name": "interfaces" + }, + { + "name": "interface", + "key": { + "name": "Ethernet35" + } + }, + { + "name": "state" + }, + { + "name": "counters" + } + ] + }, + "update": [ + { + "path": { + "elem": [ + { + "name": "in-broadcast-pkts" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "in-discards" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "in-errors" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "in-fcs-errors" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "in-multicast-pkts" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "in-octets" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "in-pkts" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "in-unicast-pkts" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "out-broadcast-pkts" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "out-discards" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "out-errors" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "out-multicast-pkts" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "out-octets" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "out-pkts" + } + ] + }, + "val": { + "uintVal": "0" + } + }, + { + "path": { + "elem": [ + { + "name": "out-unicast-pkts" + } + ] + }, + "val": { + "uintVal": "0" + } + } + ] + } + } +] diff --git a/plugins/inputs/gnmi/testcases/issue_14530/telegraf.conf b/plugins/inputs/gnmi/testcases/issue_14530/telegraf.conf new file mode 100644 index 000000000..a4dd71e4c --- /dev/null +++ b/plugins/inputs/gnmi/testcases/issue_14530/telegraf.conf @@ -0,0 +1,14 @@ +[[inputs.gnmi]] + addresses = ["dummy"] + encoding = "json_ietf" + tagexclude = ["path"] + + [inputs.gnmi.tags] + test_tag = "test" + + [[inputs.gnmi.subscription]] + name = "ifcounters" + origin = "openconfig" + path = "/interfaces/interface/state/counters" + subscription_mode = "sample" + sample_interval = "10s"