Skip to content

Commit

Permalink
[NDM] Cache in Devicecheck (#32373)
Browse files Browse the repository at this point in the history
  • Loading branch information
dplepage-dd authored Jan 23, 2025
1 parent 870d467 commit ec49b99
Show file tree
Hide file tree
Showing 13 changed files with 689 additions and 843 deletions.
74 changes: 74 additions & 0 deletions pkg/collector/corechecks/snmp/internal/checkconfig/buildprofile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

package checkconfig

import (
"fmt"
"github.com/DataDog/datadog-agent/pkg/networkdevice/profile/profiledefinition"
"github.com/DataDog/datadog-agent/pkg/util/log"
"slices"
)

// BuildProfile builds the fetchable profile for this config.
//
// If ProfileName == ProfileNameInline, then the result just contains the inline
// metrics and tags from the initconfig. This is also true if ProfileName ==
// ProfileNameAuto and sysObjectID == "" (this is useful when you want basic
// metadata for a device that you can't yet get the sysObjectID from).
//
// Otherwise, the result will be a copy of the profile from ProfileProvider that
// matches this config, either by sysObjectID if ProfileName == ProfileNameAuto
// or by ProfileName directly otherwise.
//
// The error will be non-nil if ProfileProvider doesn't know ProfileName, or if
// ProfileName is ProfileNameAuto and ProfileProvider finds no match for
// sysObjectID. In this case the returned profile will still be non-nil, and
// will be the same as what you'd get for an inline profile.
func (c *CheckConfig) BuildProfile(sysObjectID string) (profiledefinition.ProfileDefinition, error) {
var rootProfile *profiledefinition.ProfileDefinition
var profileErr error

switch c.ProfileName {
case ProfileNameInline: // inline profile -> no parent
rootProfile = nil
case ProfileNameAuto: // determine based on sysObjectID
// empty sysObjectID happens when we need the profile but couldn't connect to the device.
if sysObjectID != "" {
if profileConfig, err := c.ProfileProvider.GetProfileForSysObjectID(sysObjectID); err != nil {
profileErr = fmt.Errorf("failed to get profile for sysObjectID %q: %v", sysObjectID, err)
} else {
rootProfile = &profileConfig.Definition
log.Debugf("detected profile %q for sysobjectid %q", rootProfile.Name, sysObjectID)
}
}
default:
if profile := c.ProfileProvider.GetProfile(c.ProfileName); profile == nil {
profileErr = fmt.Errorf("unknown profile %q", c.ProfileName)
} else {
rootProfile = &profile.Definition
}
}

profile := *profiledefinition.NewProfileDefinition()
profile.Metrics = slices.Clone(c.RequestedMetrics)
profile.MetricTags = slices.Clone(c.RequestedMetricTags)
if rootProfile != nil {
profile.Name = rootProfile.Name
profile.Version = rootProfile.Version
profile.StaticTags = append(profile.StaticTags, "snmp_profile:"+rootProfile.Name)
vendor := rootProfile.GetVendor()
if vendor != "" {
profile.StaticTags = append(profile.StaticTags, "device_vendor:"+vendor)
}
profile.StaticTags = append(profile.StaticTags, rootProfile.StaticTags...)
profile.Metadata = rootProfile.Metadata
profile.Metrics = append(profile.Metrics, rootProfile.Metrics...)
profile.MetricTags = append(profile.MetricTags, rootProfile.MetricTags...)
}
profile.Metadata = updateMetadataDefinitionWithDefaults(profile.Metadata, c.CollectTopology)

return profile, profileErr
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2024-present Datadog, Inc.

package checkconfig

import (
"github.com/DataDog/datadog-agent/pkg/collector/corechecks/snmp/internal/profile"
"github.com/DataDog/datadog-agent/pkg/networkdevice/profile/profiledefinition"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)

func TestBuildProfile(t *testing.T) {
metrics := []profiledefinition.MetricsConfig{
{Symbol: profiledefinition.SymbolConfig{OID: "1.2.3.4.5", Name: "someMetric"}},
{
Symbols: []profiledefinition.SymbolConfig{
{
OID: "1.2.3.4.6",
Name: "abc",
},
},
MetricTags: profiledefinition.MetricTagConfigList{
profiledefinition.MetricTagConfig{
Symbol: profiledefinition.SymbolConfigCompat{
OID: "1.2.3.4.7",
},
},
},
},
}
profile1 := profiledefinition.ProfileDefinition{
Name: "profile1",
Version: 12,
Metrics: metrics,
MetricTags: []profiledefinition.MetricTagConfig{
{Tag: "location", Symbol: profiledefinition.SymbolConfigCompat{OID: "1.3.6.1.2.1.1.6.0", Name: "sysLocation"}},
},
Metadata: profiledefinition.MetadataConfig{
"device": {
Fields: map[string]profiledefinition.MetadataField{
"vendor": {
Value: "a-vendor",
},
"description": {
Symbol: profiledefinition.SymbolConfig{
OID: "1.3.6.1.2.1.1.99.3.0",
Name: "sysDescr",
},
},
"name": {
Symbols: []profiledefinition.SymbolConfig{
{
OID: "1.3.6.1.2.1.1.99.1.0",
Name: "symbol1",
},
{
OID: "1.3.6.1.2.1.1.99.2.0",
Name: "symbol2",
},
},
},
},
},
"interface": {
Fields: map[string]profiledefinition.MetadataField{
"oper_status": {
Symbol: profiledefinition.SymbolConfig{
OID: "1.3.6.1.2.1.2.2.1.99",
Name: "someIfSymbol",
},
},
},
IDTags: profiledefinition.MetricTagConfigList{
{
Tag: "interface",
Symbol: profiledefinition.SymbolConfigCompat{
OID: "1.3.6.1.2.1.31.1.1.1.1",
Name: "ifName",
},
},
},
},
},
SysObjectIDs: profiledefinition.StringArray{"1.1.1.*"},
}

mergedMetadata := make(profiledefinition.MetadataConfig)
mergeMetadata(mergedMetadata, profile1.Metadata)
mergedMetadata["ip_addresses"] = LegacyMetadataConfig["ip_addresses"]

mockProfiles := profile.StaticProvider(profile.ProfileConfigMap{
"profile1": profile.ProfileConfig{
Definition: profile1,
},
})

type testCase struct {
name string
config *CheckConfig
sysObjectID string
expected profiledefinition.ProfileDefinition
expectedError string
}
for _, tc := range []testCase{
{
name: "inline",
config: &CheckConfig{
IPAddress: "1.2.3.4",
RequestedMetrics: metrics,
RequestedMetricTags: []profiledefinition.MetricTagConfig{
{Tag: "location", Symbol: profiledefinition.SymbolConfigCompat{OID: "1.3.6.1.2.1.1.6.0", Name: "sysLocation"}},
},
ProfileName: ProfileNameInline,
},
expected: profiledefinition.ProfileDefinition{
Metrics: metrics,
MetricTags: []profiledefinition.MetricTagConfig{
{Tag: "location", Symbol: profiledefinition.SymbolConfigCompat{OID: "1.3.6.1.2.1.1.6.0", Name: "sysLocation"}},
},
Metadata: LegacyMetadataConfig,
},
}, {
name: "static",
config: &CheckConfig{
IPAddress: "1.2.3.4",
ProfileProvider: mockProfiles,
ProfileName: "profile1",
},
expected: profiledefinition.ProfileDefinition{
Name: "profile1",
Version: 12,
Metrics: metrics,
MetricTags: []profiledefinition.MetricTagConfig{
{Tag: "location", Symbol: profiledefinition.SymbolConfigCompat{OID: "1.3.6.1.2.1.1.6.0", Name: "sysLocation"}},
},
StaticTags: []string{"snmp_profile:profile1", "device_vendor:a-vendor"},
Metadata: mergedMetadata,
},
}, {
name: "dynamic",
config: &CheckConfig{
IPAddress: "1.2.3.4",
ProfileProvider: mockProfiles,
ProfileName: ProfileNameAuto,
},
sysObjectID: "1.1.1.1",
expected: profiledefinition.ProfileDefinition{
Name: "profile1",
Version: 12,
Metrics: metrics,
MetricTags: []profiledefinition.MetricTagConfig{
{Tag: "location", Symbol: profiledefinition.SymbolConfigCompat{OID: "1.3.6.1.2.1.1.6.0", Name: "sysLocation"}},
},
StaticTags: []string{"snmp_profile:profile1", "device_vendor:a-vendor"},
Metadata: mergedMetadata,
},
}, {
name: "static with requested metrics",
config: &CheckConfig{
IPAddress: "1.2.3.4",
ProfileProvider: mockProfiles,
CollectDeviceMetadata: true,
CollectTopology: false,
ProfileName: "profile1",
RequestedMetrics: []profiledefinition.MetricsConfig{
{Symbol: profiledefinition.SymbolConfig{OID: "3.1", Name: "global-metric"}}},
RequestedMetricTags: []profiledefinition.MetricTagConfig{
{Tag: "global-tag", Symbol: profiledefinition.SymbolConfigCompat{OID: "3.2", Name: "globalSymbol"}},
},
},
expected: profiledefinition.ProfileDefinition{
Name: "profile1",
Version: 12,
Metrics: append([]profiledefinition.MetricsConfig{
{Symbol: profiledefinition.SymbolConfig{OID: "3.1", Name: "global-metric"}}},
metrics...),
MetricTags: []profiledefinition.MetricTagConfig{
{Tag: "global-tag", Symbol: profiledefinition.SymbolConfigCompat{OID: "3.2", Name: "globalSymbol"}},
{Tag: "location", Symbol: profiledefinition.SymbolConfigCompat{OID: "1.3.6.1.2.1.1.6.0", Name: "sysLocation"}},
},
Metadata: mergedMetadata,
StaticTags: []string{"snmp_profile:profile1", "device_vendor:a-vendor"},
},
}, {
name: "static unknown",
config: &CheckConfig{
IPAddress: "1.2.3.4",
ProfileProvider: mockProfiles,
ProfileName: "f5",
},
expectedError: "unknown profile \"f5\"",
}, {
name: "dynamic unknown",
config: &CheckConfig{
IPAddress: "1.2.3.4",
ProfileProvider: mockProfiles,
ProfileName: ProfileNameAuto,
},
sysObjectID: "3.3.3.3",
expectedError: "failed to get profile for sysObjectID \"3.3.3.3\": no profiles found for sysObjectID \"3." +
"3.3.3\"",
},
} {
t.Run(tc.name, func(t *testing.T) {
profile, err := tc.config.BuildProfile(tc.sysObjectID)
if tc.expectedError != "" {
assert.EqualError(t, err, tc.expectedError)
} else {
require.NoError(t, err)
if !assert.Equal(t, tc.expected, profile) {
for k, v := range tc.expected.Metadata["device"].Fields {
t.Log(k, v)
}
t.Log("===")
for k, v := range profile.Metadata["device"].Fields {
t.Log(k, v)
}
}
}
})
}
}
Loading

0 comments on commit ec49b99

Please sign in to comment.