Skip to content

Commit

Permalink
column oid metrics can be created with scalar oid resource attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
kuiperda committed Aug 21, 2023
1 parent f58d098 commit 1387841
Show file tree
Hide file tree
Showing 10 changed files with 986 additions and 4 deletions.
32 changes: 32 additions & 0 deletions receiver/snmpreceiver/config_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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{},
Expand Down Expand Up @@ -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 == "" {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down
66 changes: 64 additions & 2 deletions receiver/snmpreceiver/scraper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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`
)

Expand Down Expand Up @@ -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))
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand All @@ -324,6 +332,8 @@ func getResourceAttributes(
}

resourceAttributes[attributeName] = attributeValue
case scalarOid != "":
resourceAttributes[attributeName] = columnOIDScalarResourceAttributeValues[scalarOid]
default:
return nil, errors.New(errMsgResourceAttributeEmptyValue)
}
Expand All @@ -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(
Expand Down
Loading

0 comments on commit 1387841

Please sign in to comment.