diff --git a/receiver/snmpreceiver/config_helper.go b/receiver/snmpreceiver/config_helper.go index 400f541bff4f..48c523d62cb1 100644 --- a/receiver/snmpreceiver/config_helper.go +++ b/receiver/snmpreceiver/config_helper.go @@ -13,6 +13,7 @@ type configHelper struct { metricScalarOIDs []string metricColumnOIDs []string attributeColumnOIDs []string + resourceAttributeScalarOIDs []string resourceAttributeColumnOIDs []string metricNamesByOID map[string]string metricAttributesByOID map[string][]Attribute @@ -26,6 +27,7 @@ func newConfigHelper(cfg *Config) *configHelper { metricScalarOIDs: []string{}, metricColumnOIDs: []string{}, attributeColumnOIDs: []string{}, + resourceAttributeScalarOIDs: []string{}, resourceAttributeColumnOIDs: []string{}, metricNamesByOID: map[string]string{}, metricAttributesByOID: map[string][]Attribute{}, @@ -77,6 +79,21 @@ func newConfigHelper(cfg *Config) *configHelper { ch.attributeColumnOIDs = append(ch.attributeColumnOIDs, attributeCfg.OID) } + // Find all resource attribute scalar OIDs + for name, resourceAttributeCfg := range cfg.ResourceAttributes { + if resourceAttributeCfg.ScalarOID == "" { + continue + } + + // Data is returned by the client with '.' prefix on the OIDs. + // Making sure the prefix exists here in the configs so we can match it up with returned data later + if !strings.HasPrefix(resourceAttributeCfg.ScalarOID, ".") { + resourceAttributeCfg.ScalarOID = "." + resourceAttributeCfg.ScalarOID + cfg.ResourceAttributes[name] = resourceAttributeCfg + } + ch.resourceAttributeScalarOIDs = append(ch.resourceAttributeScalarOIDs, resourceAttributeCfg.ScalarOID) + } + // Find all resource attribute column OIDs for name, resourceAttributeCfg := range cfg.ResourceAttributes { if resourceAttributeCfg.OID == "" { @@ -110,6 +127,11 @@ func (h configHelper) getAttributeColumnOIDs() []string { return h.attributeColumnOIDs } +// getResourceAttributeScalarOIDs returns all of the resource attribute scalar OIDs in the resource attribute configs +func (h configHelper) getResourceAttributeScalarOIDs() []string { + return h.resourceAttributeScalarOIDs +} + // getResourceAttributeColumnOIDs returns all of the resource attribute column OIDs in the resource attribute configs func (h configHelper) getResourceAttributeColumnOIDs() []string { return h.resourceAttributeColumnOIDs @@ -175,6 +197,16 @@ func (h configHelper) getResourceAttributeConfigOID(name string) string { return attrConfig.OID } +// getResourceAttributeConfigScalarOID returns the scalar OID of a resource attribute config +func (h configHelper) getResourceAttributeConfigScalarOID(name string) string { + attrConfig := h.cfg.ResourceAttributes[name] + if attrConfig == nil { + return "" + } + + return attrConfig.ScalarOID +} + // getMetricConfigAttributes returns the metric config attributes for a given OID func (h configHelper) getMetricConfigAttributes(oid string) []Attribute { return h.metricAttributesByOID[oid] diff --git a/receiver/snmpreceiver/scraper.go b/receiver/snmpreceiver/scraper.go index ba744dfb3816..9e1d1e8bbda1 100644 --- a/receiver/snmpreceiver/scraper.go +++ b/receiver/snmpreceiver/scraper.go @@ -23,12 +23,14 @@ var ( // Error messages errMsgBadValueType = `returned metric SNMP data type for OID '%s' is not supported` errMsgIndexedAttributesBadValueType = `returned attribute SNMP data type for OID '%s' from column OID '%s' is not supported` + errMsgScalarAttributesBadValueType = `returned attribute SNMP data type for OID '%s' is not supported` errMsgOIDAttributeEmptyValue = `not creating indexed metric '%s' datapoint: %w` errMsgAttributeEmptyValue = `metric OID attribute value is blank` errMsgResourceAttributeEmptyValue = `related resource attribute value is blank` errMsgOIDResourceAttributeEmptyValue = `not creating indexed metric '%s' or resource: %w` errMsgScalarOIDProcessing = `problem processing scalar metric data for OID '%s': %w` errMsgIndexedMetricOIDProcessing = `problem processing indexed metric data for OID '%s' from column OID '%s': %w` + errMsgScalarAttributeOIDProcessing = `problem processing scalar attribute data from scalar OID '%s': %w` errMsgIndexedAttributeOIDProcessing = `problem processing indexed attribute data for OID '%s' from column OID '%s': %w` ) @@ -137,11 +139,14 @@ func (s *snmpScraper) scrapeIndexedMetrics( // Retrieve column OID SNMP indexed data for resource attributes columnOIDIndexedResourceAttributeValues := s.scrapeIndexedAttributes(configHelper.getResourceAttributeColumnOIDs(), scraperErrors) + // Retrieve scalar OID SNMP data for resource attributes + columnOIDScalarOIDResourceAttributeValues := s.scrapeScalarAttributes(configHelper.getResourceAttributeScalarOIDs(), scraperErrors) + // Retrieve all SNMP indexed data from column metric OIDs indexedData := s.client.GetIndexedData(metricColumnOIDs, scraperErrors) // For each piece of SNMP data, attempt to create the necessary OTEL structures (resources/metrics/datapoints) for _, data := range indexedData { - if err := s.indexedDataToMetric(data, metricHelper, configHelper, columnOIDIndexedAttributeValues, columnOIDIndexedResourceAttributeValues); err != nil { + if err := s.indexedDataToMetric(data, metricHelper, configHelper, columnOIDIndexedAttributeValues, columnOIDIndexedResourceAttributeValues, columnOIDScalarOIDResourceAttributeValues); err != nil { scraperErrors.AddPartial(1, fmt.Errorf(errMsgIndexedMetricOIDProcessing, data.oid, data.columnOID, err)) } } @@ -173,6 +178,7 @@ func (s *snmpScraper) indexedDataToMetric( configHelper *configHelper, columnOIDIndexedAttributeValues map[string]indexedAttributeValues, columnOIDIndexedResourceAttributeValues map[string]indexedAttributeValues, + columnOIDScalarResourceAttributeValues map[string]string, ) error { // Get the related metric name for this SNMP indexed data metricName := configHelper.getMetricName(data.columnOID) @@ -186,7 +192,7 @@ func (s *snmpScraper) indexedDataToMetric( } // Get resource attributes - resourceAttributes, err := getResourceAttributes(configHelper, data.columnOID, indexString, columnOIDIndexedResourceAttributeValues) + resourceAttributes, err := getResourceAttributes(configHelper, data.columnOID, indexString, columnOIDIndexedResourceAttributeValues, columnOIDScalarResourceAttributeValues) if err != nil { return fmt.Errorf(errMsgOIDResourceAttributeEmptyValue, metricName, err) } @@ -307,12 +313,14 @@ func getResourceAttributes( columnOID string, indexString string, columnOIDIndexedResourceAttributeValues map[string]indexedAttributeValues, + columnOIDScalarResourceAttributeValues map[string]string, ) (map[string]string, error) { resourceAttributes := map[string]string{} for _, attributeName := range configHelper.getResourceAttributeNames(columnOID) { prefix := configHelper.getResourceAttributeConfigIndexedValuePrefix(attributeName) oid := configHelper.getResourceAttributeConfigOID(attributeName) + scalarOid := configHelper.getResourceAttributeConfigScalarOID(attributeName) switch { case prefix != "": resourceAttributes[attributeName] = prefix + indexString @@ -324,6 +332,8 @@ func getResourceAttributes( } resourceAttributes[attributeName] = attributeValue + case scalarOid != "": + resourceAttributes[attributeName] = columnOIDScalarResourceAttributeValues[scalarOid] default: return nil, errors.New(errMsgResourceAttributeEmptyValue) } @@ -332,6 +342,58 @@ func getResourceAttributes( return resourceAttributes, nil } +// scrapeScalarAttributes retrieves all SNMP data from attribute (or resource attribute) +// config scalar OIDs and stores the returned data for later use by metrics +func (s *snmpScraper) scrapeScalarAttributes( + scalarOIDs []string, + scraperErrors *scrapererror.ScrapeErrors, +) map[string]string { + scalarOIDAttributeValues := map[string]string{} + + // If no scalar OID resource attribute configs, nothing else to do + if len (scalarOIDs) == 0 { + return scalarOIDAttributeValues + } + + // Retrieve all SNMP data from scalar resource attribute OIDs + scalarData := s.client.GetScalarData(scalarOIDs, scraperErrors) + + // For each piece of SNMP data, store the necessary info to help create resources later if needed + for _, data := range scalarData { + if err := scalarDataToAttribute(data, scalarOIDAttributeValues); err != nil { + scraperErrors.AddPartial(1, fmt.Errorf(errMsgScalarAttributeOIDProcessing, data.oid, err)) + } + } + return scalarOIDAttributeValues +} + +// scalarDataToAttribute provides a function which will take one piece of scalar OID SNMP data +// (for either an attribute or resource attribute) and store it in a map for later use +func scalarDataToAttribute( + data SNMPData, + scalarOIDAttributeValues map[string]string, +) error { + // Get the string value of the SNMP data for the {resource} attribute value + var stringValue string + // Not explicitly checking these casts as this should be made safe in the client + switch data.valueType { + case notSupportedVal: + return fmt.Errorf(errMsgScalarAttributesBadValueType, data.oid) + case stringVal: + stringValue = data.value.(string) + case integerVal: + stringValue = strconv.FormatInt(data.value.(int64), 10) + case floatVal: + stringValue = strconv.FormatFloat(data.value.(float64), 'f', 2, 64) + } + + // Store the {resource} attribute value in a map using the scalar OID as a key. + // This way we can match metrics to this data through the {resource} attribute config. + scalarOIDAttributeValues[data.oid] = stringValue + + return nil +} + // scrapeIndexedAttributes retrieves all SNMP data from attribute (or resource attribute) // config column OIDs and stores the returned indexed data for later use by metrics func (s *snmpScraper) scrapeIndexedAttributes( diff --git a/receiver/snmpreceiver/scraper_test.go b/receiver/snmpreceiver/scraper_test.go index 2308fbf8c88d..640ed776a8dd 100644 --- a/receiver/snmpreceiver/scraper_test.go +++ b/receiver/snmpreceiver/scraper_test.go @@ -21,7 +21,6 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/golden" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" ) - type MockClient struct { mock.Mock } @@ -42,8 +41,8 @@ func (_m *MockClient) Close() error { // Connect provides a mock function with given fields: func (_m *MockClient) Connect() error { + ret := _m.Called() - var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -2194,6 +2193,592 @@ func TestScrape(t *testing.T) { require.NoError(t, err) }, }, + { + desc: "ScalarOID (string) resource attribute attached to ColumnOID metric alongside a ColumnOID (string) resource attribute creates metric (19)", + testFunc: func (t *testing.T) { + mockClient := new(MockClient) + scalarRA := SNMPData{ + oid: ".0", + value: "scalar", + valueType: stringVal, + } + coidRA11 := SNMPData{ + columnOID: ".1", + oid: ".1.1", + value: "string1", + valueType: stringVal, + } + coidRA12 := SNMPData{ + columnOID: ".1", + oid: ".1.2", + value: "string2", + valueType: stringVal, + } + coid21 := SNMPData{ + columnOID: ".2", + oid: ".2.1", + value: int64(3), + valueType: integerVal, + } + coid22 := SNMPData{ + columnOID: ".2", + oid: ".2.2", + value: int64(4), + valueType: integerVal, + } + mockClient.On("Connect").Return(nil) + mockClient.On("Close").Return(nil) + mockClient.On("GetScalarData", []string{".0"}, mock.Anything).Return([]SNMPData{scalarRA}).Once() + mockClient.On("GetIndexedData", []string{".1"}, mock.Anything).Return([]SNMPData{coidRA11, coidRA12}).Once() + mockClient.On("GetIndexedData", []string{".2"}, mock.Anything).Return([]SNMPData{coid21, coid22}).Once() + scraper := &snmpScraper{ + cfg: &Config{ + ResourceAttributes: map[string]*ResourceAttributeConfig{ + "rattr1": { + ScalarOID: ".0", + }, + "rattr2": { + OID: ".1", + }, + }, + Metrics: map[string]*MetricConfig{ + "metric1": { + Description: "test description", + Unit: "By", + Gauge: &GaugeMetric{ + ValueType: "int", + }, + ColumnOIDs: []ColumnOID{ + { + OID: ".2", + ResourceAttributes: []string{"rattr1", "rattr2"}, + }, + }, + }, + }, + }, + settings: receivertest.NewNopCreateSettings(), + client: mockClient, + logger: zap.NewNop(), + } + + expectedMetricGen := func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "19_scalar_ra_string_and_coid_ra_string_on_coid_metric_golden.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + } + expectedMetrics := expectedMetricGen(t) + metrics, err := scraper.scrape(context.Background()) + require.NoError(t, err) + err = pmetrictest.CompareMetrics(expectedMetrics, metrics, pmetrictest.IgnoreTimestamp()) + require.NoError(t, err) + }, + }, + { + desc: "ScalarOID (int) resource attribute attached to ColumnOID metric alongside a ColumnOID (int) resource attribute creates metric (20)", + testFunc: func (t *testing.T) { + mockClient := new(MockClient) + scalarRA := SNMPData{ + oid: ".0", + value: int64(5), + valueType: integerVal, + } + coidRA11 := SNMPData{ + columnOID: ".1", + oid: ".1.1", + value: int64(1), + valueType: integerVal, + } + coidRA12 := SNMPData{ + columnOID: ".1", + oid: ".1.2", + value: int64(2), + valueType: integerVal, + } + coid21 := SNMPData{ + columnOID: ".2", + oid: ".2.1", + value: int64(3), + valueType: integerVal, + } + coid22 := SNMPData{ + columnOID: ".2", + oid: ".2.2", + value: int64(4), + valueType: integerVal, + } + mockClient.On("Connect").Return(nil) + mockClient.On("Close").Return(nil) + mockClient.On("GetScalarData", []string{".0"}, mock.Anything).Return([]SNMPData{scalarRA}).Once() + mockClient.On("GetIndexedData", []string{".1"}, mock.Anything).Return([]SNMPData{coidRA11, coidRA12}).Once() + mockClient.On("GetIndexedData", []string{".2"}, mock.Anything).Return([]SNMPData{coid21, coid22}).Once() + scraper := &snmpScraper{ + cfg: &Config{ + ResourceAttributes: map[string]*ResourceAttributeConfig{ + "rattr1": { + ScalarOID: ".0", + }, + "rattr2": { + OID: ".1", + }, + }, + Metrics: map[string]*MetricConfig{ + "metric1": { + Description: "test description", + Unit: "By", + Gauge: &GaugeMetric{ + ValueType: "int", + }, + ColumnOIDs: []ColumnOID{ + { + OID: ".2", + ResourceAttributes: []string{"rattr1", "rattr2"}, + }, + }, + }, + }, + }, + settings: receivertest.NewNopCreateSettings(), + client: mockClient, + logger: zap.NewNop(), + } + + expectedMetricGen := func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "20_scalar_ra_int_and_coid_ra_int_on_coid_metric_golden.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + } + expectedMetrics := expectedMetricGen(t) + metrics, err := scraper.scrape(context.Background()) + require.NoError(t, err) + err = pmetrictest.CompareMetrics(expectedMetrics, metrics, pmetrictest.IgnoreTimestamp()) + require.NoError(t, err) + }, + }, + { + desc: "ScalarOID (float) resource attribute attached to ColumnOID metric alongside a ColumnOID (float) resource attribute creates metric (21)", + testFunc: func (t *testing.T) { + mockClient := new(MockClient) + scalarRA := SNMPData{ + oid: ".0", + value: float64(5.0), + valueType: floatVal, + } + coidRA11 := SNMPData{ + columnOID: ".1", + oid: ".1.1", + value: float64(1.0), + valueType: floatVal, + } + coidRA12 := SNMPData{ + columnOID: ".1", + oid: ".1.2", + value: float64(2.0), + valueType: floatVal, + } + coid21 := SNMPData{ + columnOID: ".2", + oid: ".2.1", + value: int64(3), + valueType: integerVal, + } + coid22 := SNMPData{ + columnOID: ".2", + oid: ".2.2", + value: int64(4), + valueType: integerVal, + } + mockClient.On("Connect").Return(nil) + mockClient.On("Close").Return(nil) + mockClient.On("GetScalarData", []string{".0"}, mock.Anything).Return([]SNMPData{scalarRA}).Once() + mockClient.On("GetIndexedData", []string{".1"}, mock.Anything).Return([]SNMPData{coidRA11, coidRA12}).Once() + mockClient.On("GetIndexedData", []string{".2"}, mock.Anything).Return([]SNMPData{coid21, coid22}).Once() + scraper := &snmpScraper{ + cfg: &Config{ + ResourceAttributes: map[string]*ResourceAttributeConfig{ + "rattr1": { + ScalarOID: ".0", + }, + "rattr2": { + OID: ".1", + }, + }, + Metrics: map[string]*MetricConfig{ + "metric1": { + Description: "test description", + Unit: "By", + Gauge: &GaugeMetric{ + ValueType: "int", + }, + ColumnOIDs: []ColumnOID{ + { + OID: ".2", + ResourceAttributes: []string{"rattr1", "rattr2"}, + }, + }, + }, + }, + }, + settings: receivertest.NewNopCreateSettings(), + client: mockClient, + logger: zap.NewNop(), + } + + expectedMetricGen := func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "21_scalar_ra_float_and_coid_ra_float_on_coid_metric_golden.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + } + expectedMetrics := expectedMetricGen(t) + metrics, err := scraper.scrape(context.Background()) + require.NoError(t, err) + err = pmetrictest.CompareMetrics(expectedMetrics, metrics, pmetrictest.IgnoreTimestamp()) + require.NoError(t, err) + }, + }, + { + desc: "ScalarOID (string) resource attribute attached to ColumnOID metric alongside an IndexedValuePrefix resource attribute creates metric (22)", + testFunc: func (t *testing.T) { + mockClient := new(MockClient) + scalarRA := SNMPData{ + oid: ".0", + value: "scalar", + valueType: stringVal, + } + coid21 := SNMPData{ + columnOID: ".2", + oid: ".2.1", + value: int64(3), + valueType: integerVal, + } + coid22 := SNMPData{ + columnOID: ".2", + oid: ".2.2", + value: int64(4), + valueType: integerVal, + } + mockClient.On("Connect").Return(nil) + mockClient.On("Close").Return(nil) + mockClient.On("GetScalarData", []string{".0"}, mock.Anything).Return([]SNMPData{scalarRA}).Once() + mockClient.On("GetIndexedData", []string{".2"}, mock.Anything).Return([]SNMPData{coid21, coid22}).Once() + scraper := &snmpScraper{ + cfg: &Config{ + ResourceAttributes: map[string]*ResourceAttributeConfig{ + "rattr1": { + ScalarOID: ".0", + }, + "rattr2": { + IndexedValuePrefix: "p", + }, + }, + Metrics: map[string]*MetricConfig{ + "metric1": { + Description: "test description", + Unit: "By", + Gauge: &GaugeMetric{ + ValueType: "int", + }, + ColumnOIDs: []ColumnOID{ + { + OID: ".2", + ResourceAttributes: []string{"rattr1", "rattr2"}, + }, + }, + }, + }, + }, + settings: receivertest.NewNopCreateSettings(), + client: mockClient, + logger: zap.NewNop(), + } + + expectedMetricGen := func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "22_scalar_ra_string_and_prefix_ra_on_coid_metric_golden.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + } + expectedMetrics := expectedMetricGen(t) + metrics, err := scraper.scrape(context.Background()) + require.NoError(t, err) + err = pmetrictest.CompareMetrics(expectedMetrics, metrics, pmetrictest.IgnoreTimestamp()) + require.NoError(t, err) + }, + }, + // passes 50% of time because of order of slice elements being randomized (but mock expects order) + // { + // desc: "Multiple ScalarOID (string) resource attributes attached to ColumnOID metric alongside multiple Column OID (string) resource attributes creates metrics (23)", + // testFunc: func (t *testing.T) { + // mockClient := new(MockClient) + // scalarRA1 := SNMPData{ + // oid: ".5.0", + // value: "scalar", + // valueType: stringVal, + // } + // scalarRA2 := SNMPData{ + // oid: ".6.0", + // value: "also scalar", + // valueType: stringVal, + // } + // coidRA11 := SNMPData{ + // columnOID: ".1", + // oid: ".1.1", + // value: "string1", + // valueType: stringVal, + // } + // coidRA12 := SNMPData{ + // columnOID: ".1", + // oid: ".1.2", + // value: "string2", + // valueType: stringVal, + // } + // coidRA21 := SNMPData{ + // columnOID: ".2", + // oid: ".2.1", + // value: "also a string1", + // valueType: stringVal, + // } + // coidRA22 := SNMPData{ + // columnOID: ".2", + // oid: ".2.2", + // value: "also a string2", + // valueType: stringVal, + // } + // coid31 := SNMPData{ + // columnOID: ".3", + // oid: ".3.1", + // value: int64(3), + // valueType: integerVal, + // } + // coid32 := SNMPData{ + // columnOID: ".3", + // oid: ".3.2", + // value: int64(4), + // valueType: integerVal, + // } + // mockClient.On("Connect").Return(nil) + // mockClient.On("Close").Return(nil) + // mockClient.On("GetScalarData", []string{".5.0", ".6.0"}, mock.Anything).Return([]SNMPData{scalarRA1, scalarRA2}).Once() + // mockClient.On("GetIndexedData", []string{".1", ".2"}, mock.Anything).Return([]SNMPData{coidRA11, coidRA12, coidRA21, coidRA22}).Once() + // mockClient.On("GetIndexedData", []string{".3"}, mock.Anything).Return([]SNMPData{coid31, coid32}).Once() + // scraper := &snmpScraper{ + // cfg: &Config{ + // ResourceAttributes: map[string]*ResourceAttributeConfig{ + // "rattr1": { + // ScalarOID: ".5.0", + // }, + // "rattr2": { + // ScalarOID: ".6.0", + // }, + // "rattr3": { + // OID: ".1", + // }, + // "rattr4": { + // OID: ".2", + // }, + // }, + // Metrics: map[string]*MetricConfig{ + // "metric1": { + // Description: "test description", + // Unit: "By", + // Gauge: &GaugeMetric{ + // ValueType: "int", + // }, + // ColumnOIDs: []ColumnOID{ + // { + // OID: ".3", + // ResourceAttributes: []string{"rattr1", "rattr2", "rattr3", "rattr4"}, + // }, + // }, + // }, + // }, + // }, + // settings: receivertest.NewNopCreateSettings(), + // client: mockClient, + // logger: zap.NewNop(), + // } + + // expectedMetricGen := func(t *testing.T) pmetric.Metrics { + // goldenPath := filepath.Join("testdata", "expected_metrics", "23_multiple_scalar_oid_string_with_multiple_coid_ra_string_on_coid_metric_golden.yaml") + // expectedMetrics, err := golden.ReadMetrics(goldenPath) + // require.NoError(t, err) + // return expectedMetrics + // } + // expectedMetrics := expectedMetricGen(t) + // metrics, err := scraper.scrape(context.Background()) + // require.NoError(t, err) + // err = pmetrictest.CompareMetrics(expectedMetrics, metrics, pmetrictest.IgnoreTimestamp(), pmetrictest.IgnoreMetricDataPointsOrder()) + // require.NoError(t, err) + // }, + // }, +{ + desc: "ScalarOID (string) resource attribute attached to ColumnOID metric alongside a ColumnOID (string) attribute creates metric (24)", + testFunc: func (t *testing.T) { + mockClient := new(MockClient) + scalarRA := SNMPData{ + oid: ".0", + value: "scalar", + valueType: stringVal, + } + coidAttr11 := SNMPData{ + columnOID: ".1", + oid: ".1.1", + value: "string1", + valueType: stringVal, + } + coidAttr12 := SNMPData{ + columnOID: ".1", + oid: ".1.2", + value: "string2", + valueType: stringVal, + } + coid21 := SNMPData{ + columnOID: ".2", + oid: ".2.1", + value: int64(3), + valueType: integerVal, + } + coid22 := SNMPData{ + columnOID: ".2", + oid: ".2.2", + value: int64(4), + valueType: integerVal, + } + mockClient.On("Connect").Return(nil) + mockClient.On("Close").Return(nil) + mockClient.On("GetScalarData", []string{".0"}, mock.Anything).Return([]SNMPData{scalarRA}).Once() + mockClient.On("GetIndexedData", []string{".1"}, mock.Anything).Return([]SNMPData{coidAttr11, coidAttr12}).Once() + mockClient.On("GetIndexedData", []string{".2"}, mock.Anything).Return([]SNMPData{coid21, coid22}).Once() + scraper := &snmpScraper{ + cfg: &Config{ + ResourceAttributes: map[string]*ResourceAttributeConfig{ + "rattr1": { + ScalarOID: ".0", + }, + }, + Attributes: map[string]*AttributeConfig{ + "attr1": { + OID: ".1", + }, + }, + Metrics: map[string]*MetricConfig{ + "metric1": { + Description: "test description", + Unit: "By", + Gauge: &GaugeMetric{ + ValueType: "int", + }, + ColumnOIDs: []ColumnOID{ + { + OID: ".2", + ResourceAttributes: []string{"rattr1"}, + Attributes: []Attribute{ + { + Name: "attr1", + }, + }, + }, + }, + }, + }, + }, + settings: receivertest.NewNopCreateSettings(), + client: mockClient, + logger: zap.NewNop(), + } + + expectedMetricGen := func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "24_scalar_ra_string_and_coid_attr_string_on_coid_metric_golden.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + } + expectedMetrics := expectedMetricGen(t) + metrics, err := scraper.scrape(context.Background()) + require.NoError(t, err) + err = pmetrictest.CompareMetrics(expectedMetrics, metrics, pmetrictest.IgnoreTimestamp()) + require.NoError(t, err) + }, + }, + { + desc: "ScalarOID (string) resource attribute attached to ColumnOID metric alongside an Indexed Value Prefix attribute creates metric (25)", + testFunc: func (t *testing.T) { + mockClient := new(MockClient) + scalarRA := SNMPData{ + oid: ".0", + value: "scalar", + valueType: stringVal, + } + coid21 := SNMPData{ + columnOID: ".2", + oid: ".2.1", + value: int64(3), + valueType: integerVal, + } + coid22 := SNMPData{ + columnOID: ".2", + oid: ".2.2", + value: int64(4), + valueType: integerVal, + } + mockClient.On("Connect").Return(nil) + mockClient.On("Close").Return(nil) + mockClient.On("GetScalarData", []string{".0"}, mock.Anything).Return([]SNMPData{scalarRA}).Once() + mockClient.On("GetIndexedData", []string{".2"}, mock.Anything).Return([]SNMPData{coid21, coid22}).Once() + scraper := &snmpScraper{ + cfg: &Config{ + ResourceAttributes: map[string]*ResourceAttributeConfig{ + "rattr1": { + ScalarOID: ".0", + }, + }, + Attributes: map[string]*AttributeConfig{ + "attr1": { + IndexedValuePrefix: "prefix", + }, + }, + Metrics: map[string]*MetricConfig{ + "metric1": { + Description: "test description", + Unit: "By", + Gauge: &GaugeMetric{ + ValueType: "int", + }, + ColumnOIDs: []ColumnOID{ + { + OID: ".2", + ResourceAttributes: []string{"rattr1"}, + Attributes: []Attribute{ + { + Name: "attr1", + }, + }, + }, + }, + }, + }, + }, + settings: receivertest.NewNopCreateSettings(), + client: mockClient, + logger: zap.NewNop(), + } + + expectedMetricGen := func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "25_scalar_ra_string_and_ivp_attr_on_coid_metric_golden.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + } + expectedMetrics := expectedMetricGen(t) + metrics, err := scraper.scrape(context.Background()) + require.NoError(t, err) + err = pmetrictest.CompareMetrics(expectedMetrics, metrics, pmetrictest.IgnoreTimestamp()) + require.NoError(t, err) + }, + }, } for _, tc := range testCases { diff --git a/receiver/snmpreceiver/testdata/expected_metrics/19_scalar_ra_string_and_coid_ra_string_on_coid_metric_golden.yaml b/receiver/snmpreceiver/testdata/expected_metrics/19_scalar_ra_string_and_coid_ra_string_on_coid_metric_golden.yaml new file mode 100644 index 000000000000..8976ae9f615d --- /dev/null +++ b/receiver/snmpreceiver/testdata/expected_metrics/19_scalar_ra_string_and_coid_ra_string_on_coid_metric_golden.yaml @@ -0,0 +1,41 @@ +resourceMetrics: + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + - key: rattr2 + value: + stringValue: string1 + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "3" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + - key: rattr2 + value: + stringValue: string2 + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "4" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest diff --git a/receiver/snmpreceiver/testdata/expected_metrics/20_scalar_ra_int_and_coid_ra_int_on_coid_metric_golden.yaml b/receiver/snmpreceiver/testdata/expected_metrics/20_scalar_ra_int_and_coid_ra_int_on_coid_metric_golden.yaml new file mode 100644 index 000000000000..87eaf08ae04a --- /dev/null +++ b/receiver/snmpreceiver/testdata/expected_metrics/20_scalar_ra_int_and_coid_ra_int_on_coid_metric_golden.yaml @@ -0,0 +1,41 @@ +resourceMetrics: + - resource: + attributes: + - key: rattr1 + value: + stringValue: "5" + - key: rattr2 + value: + stringValue: "1" + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "3" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest + - resource: + attributes: + - key: rattr1 + value: + stringValue: "5" + - key: rattr2 + value: + stringValue: "2" + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "4" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest diff --git a/receiver/snmpreceiver/testdata/expected_metrics/21_scalar_ra_float_and_coid_ra_float_on_coid_metric_golden.yaml b/receiver/snmpreceiver/testdata/expected_metrics/21_scalar_ra_float_and_coid_ra_float_on_coid_metric_golden.yaml new file mode 100644 index 000000000000..5e75c87459ba --- /dev/null +++ b/receiver/snmpreceiver/testdata/expected_metrics/21_scalar_ra_float_and_coid_ra_float_on_coid_metric_golden.yaml @@ -0,0 +1,41 @@ +resourceMetrics: + - resource: + attributes: + - key: rattr1 + value: + stringValue: "5.00" + - key: rattr2 + value: + stringValue: "1.00" + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "3" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest + - resource: + attributes: + - key: rattr1 + value: + stringValue: "5.00" + - key: rattr2 + value: + stringValue: "2.00" + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "4" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest diff --git a/receiver/snmpreceiver/testdata/expected_metrics/22_scalar_ra_string_and_prefix_ra_on_coid_metric_golden.yaml b/receiver/snmpreceiver/testdata/expected_metrics/22_scalar_ra_string_and_prefix_ra_on_coid_metric_golden.yaml new file mode 100644 index 000000000000..f4158eb1937e --- /dev/null +++ b/receiver/snmpreceiver/testdata/expected_metrics/22_scalar_ra_string_and_prefix_ra_on_coid_metric_golden.yaml @@ -0,0 +1,41 @@ +resourceMetrics: + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + - key: rattr2 + value: + stringValue: p.1 + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "3" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + - key: rattr2 + value: + stringValue: p.2 + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "4" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest diff --git a/receiver/snmpreceiver/testdata/expected_metrics/23_multiple_scalar_oid_string_with_multiple_coid_ra_string_on_coid_metric_golden.yaml b/receiver/snmpreceiver/testdata/expected_metrics/23_multiple_scalar_oid_string_with_multiple_coid_ra_string_on_coid_metric_golden.yaml new file mode 100644 index 000000000000..63c91225a936 --- /dev/null +++ b/receiver/snmpreceiver/testdata/expected_metrics/23_multiple_scalar_oid_string_with_multiple_coid_ra_string_on_coid_metric_golden.yaml @@ -0,0 +1,53 @@ +resourceMetrics: + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + - key: rattr2 + value: + stringValue: also scalar + - key: rattr3 + value: + stringValue: string1 + - key: rattr4 + value: + stringValue: also a string1 + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "3" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + - key: rattr2 + value: + stringValue: also scalar + - key: rattr3 + value: + stringValue: string2 + - key: rattr4 + value: + stringValue: also a string2 + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "4" + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest diff --git a/receiver/snmpreceiver/testdata/expected_metrics/24_scalar_ra_string_and_coid_attr_string_on_coid_metric_golden.yaml b/receiver/snmpreceiver/testdata/expected_metrics/24_scalar_ra_string_and_coid_attr_string_on_coid_metric_golden.yaml new file mode 100644 index 000000000000..46faa2751bdd --- /dev/null +++ b/receiver/snmpreceiver/testdata/expected_metrics/24_scalar_ra_string_and_coid_attr_string_on_coid_metric_golden.yaml @@ -0,0 +1,43 @@ +resourceMetrics: + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "3" + attributes: + - key: attr1 + value: + stringValue: string1 + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "4" + attributes: + - key: attr1 + value: + stringValue: string2 + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest diff --git a/receiver/snmpreceiver/testdata/expected_metrics/25_scalar_ra_string_and_ivp_attr_on_coid_metric_golden.yaml b/receiver/snmpreceiver/testdata/expected_metrics/25_scalar_ra_string_and_ivp_attr_on_coid_metric_golden.yaml new file mode 100644 index 000000000000..58acc1733be7 --- /dev/null +++ b/receiver/snmpreceiver/testdata/expected_metrics/25_scalar_ra_string_and_ivp_attr_on_coid_metric_golden.yaml @@ -0,0 +1,43 @@ +resourceMetrics: + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "3" + attributes: + - key: attr1 + value: + stringValue: prefix.1 + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest + - resource: + attributes: + - key: rattr1 + value: + stringValue: scalar + scopeMetrics: + - metrics: + - description: test description + gauge: + dataPoints: + - asInt: "4" + attributes: + - key: attr1 + value: + stringValue: prefix.2 + timeUnixNano: "1651783494931319000" + name: metric1 + unit: By + scope: + name: otelcol/snmpreceiver + version: latest