From 1bffce21b620f3f655d063c9a0e551f9bc293f01 Mon Sep 17 00:00:00 2001 From: Yusuf Shalaby <31964058+yusufshalaby@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:41:55 -0400 Subject: [PATCH] fix(inputs.cisco_telemetry_mdt): Handle NXOS DME subtree telemetry format (#15923) (cherry picked from commit 22681758f1efa4cd840945d054f0141cad64d3e9) --- .../cisco_telemetry_mdt.go | 10 +- .../cisco_telemetry_mdt_test.go | 140 ++++++++++++++++++ 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/plugins/inputs/cisco_telemetry_mdt/cisco_telemetry_mdt.go b/plugins/inputs/cisco_telemetry_mdt/cisco_telemetry_mdt.go index 80ff47832602d..b37e3287b5c4f 100644 --- a/plugins/inputs/cisco_telemetry_mdt/cisco_telemetry_mdt.go +++ b/plugins/inputs/cisco_telemetry_mdt/cisco_telemetry_mdt.go @@ -624,13 +624,19 @@ func (c *CiscoTelemetryMDT) parseClassAttributeField(grouper *metric.SeriesGroup } nxAttributes = field.Fields[0].Fields[0].Fields[0].Fields[0] + // Find dn tag among list of attributes for _, subfield := range nxAttributes.Fields { if subfield.Name == "dn" { tags["dn"] = decodeTag(subfield) - } else { - c.parseContentField(grouper, subfield, "", encodingPath, tags, timestamp) + break } } + // Add attributes to grouper with consistent dn tag + for _, subfield := range nxAttributes.Fields { + c.parseContentField(grouper, subfield, "", encodingPath, tags, timestamp) + } + // Delete dn tag to prevent it from being added to the next node's attributes + delete(tags, "dn") } func (c *CiscoTelemetryMDT) getMeasurementName(encodingPath string) string { diff --git a/plugins/inputs/cisco_telemetry_mdt/cisco_telemetry_mdt_test.go b/plugins/inputs/cisco_telemetry_mdt/cisco_telemetry_mdt_test.go index 98d95f05fb547..7348585f8e0c0 100644 --- a/plugins/inputs/cisco_telemetry_mdt/cisco_telemetry_mdt_test.go +++ b/plugins/inputs/cisco_telemetry_mdt/cisco_telemetry_mdt_test.go @@ -820,6 +820,146 @@ func TestHandleNXDME(t *testing.T) { fields1 := map[string]interface{}{"value": "foo"} acc.AssertContainsTaggedFields(t, "dme", fields1, tags1) } +func TestHandleNXDMESubtree(t *testing.T) { + c := &CiscoTelemetryMDT{ + Transport: "dummy", + Aliases: map[string]string{"dme": "sys/dme"}, + } + acc := &testutil.Accumulator{} + err := c.Start(acc) + // error is expected since we are passing in dummy transport + require.Error(t, err) + + telemetry := &telemetryBis.Telemetry{ + MsgTimestamp: 1543236572000, + EncodingPath: "sys/dme", + NodeId: &telemetryBis.Telemetry_NodeIdStr{NodeIdStr: "hostname"}, + Subscription: &telemetryBis.Telemetry_SubscriptionIdStr{SubscriptionIdStr: "subscription"}, + DataGpbkv: []*telemetryBis.TelemetryField{ + { + Fields: []*telemetryBis.TelemetryField{ + { + Name: "keys", + Fields: []*telemetryBis.TelemetryField{ + { + Name: "sys/dme", + ValueByType: &telemetryBis.TelemetryField_StringValue{StringValue: "sys/dme"}, + }, + }, + }, + { + Name: "content", + Fields: []*telemetryBis.TelemetryField{ + { + Fields: []*telemetryBis.TelemetryField{ + { + Name: "children", + Fields: []*telemetryBis.TelemetryField{ + { + Fields: []*telemetryBis.TelemetryField{ + { + Name: "fooEntity", + Fields: []*telemetryBis.TelemetryField{ + { + Fields: []*telemetryBis.TelemetryField{ + { + Name: "attributes", + Fields: []*telemetryBis.TelemetryField{ + { + Fields: []*telemetryBis.TelemetryField{ + { + Name: "foo", + ValueByType: &telemetryBis.TelemetryField_StringValue{ + StringValue: "bar1", + }, + }, + { + Name: "dn", + ValueByType: &telemetryBis.TelemetryField_StringValue{ + StringValue: "eth1/1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + Fields: []*telemetryBis.TelemetryField{ + { + Name: "fooEntity", + Fields: []*telemetryBis.TelemetryField{ + { + Fields: []*telemetryBis.TelemetryField{ + { + Name: "attributes", + Fields: []*telemetryBis.TelemetryField{ + { + Fields: []*telemetryBis.TelemetryField{ + { + Name: "foo", + ValueByType: &telemetryBis.TelemetryField_StringValue{ + StringValue: "bar2", + }, + }, + { + Name: "dn", + ValueByType: &telemetryBis.TelemetryField_StringValue{ + StringValue: "eth1/2", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }} + data, err := proto.Marshal(telemetry) + require.NoError(t, err) + + c.handleTelemetry(data) + require.Empty(t, acc.Errors) + + require.Len(t, acc.Metrics, 2) + + tags1 := map[string]string{ + "dn": "eth1/1", + "path": "sys/dme", + "source": "hostname", + "subscription": "subscription", + "sys/dme": "sys/dme", + } + fields1 := map[string]interface{}{"foo": "bar1", "dn": "eth1/1"} + acc.AssertContainsTaggedFields(t, "dme", fields1, tags1) + + tags2 := map[string]string{ + "dn": "eth1/2", + "path": "sys/dme", + "source": "hostname", + "subscription": "subscription", + "sys/dme": "sys/dme", + } + fields2 := map[string]interface{}{"foo": "bar2", "dn": "eth1/2"} + acc.AssertContainsTaggedFields(t, "dme", fields2, tags2) +} func TestTCPDialoutOverflow(t *testing.T) { c := &CiscoTelemetryMDT{