Skip to content

Commit

Permalink
fix: Add option to select translator (#10802)
Browse files Browse the repository at this point in the history
  • Loading branch information
reimda authored Mar 18, 2022
1 parent 7e652fd commit 77040ef
Show file tree
Hide file tree
Showing 19 changed files with 2,026 additions and 500 deletions.
5 changes: 5 additions & 0 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/internal/snmp"
"github.com/influxdata/telegraf/models"
"github.com/influxdata/telegraf/plugins/serializers/influx"
)
Expand Down Expand Up @@ -186,6 +187,10 @@ func (a *Agent) Run(ctx context.Context) error {
// initPlugins runs the Init function on plugins.
func (a *Agent) initPlugins() error {
for _, input := range a.Config.Inputs {
// Share the snmp translator setting with plugins that need it.
if tp, ok := input.Input.(snmp.TranslatorPlugin); ok {
tp.SetTranslator(a.Config.Agent.Translator)
}
err := input.Init()
if err != nil {
return fmt.Errorf("could not initialize input %s: %v",
Expand Down
12 changes: 12 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ type AgentConfig struct {

Hostname string
OmitHostname bool

Translator string `toml:"translator"`
}

// InputNames returns a list of strings of the configured inputs.
Expand Down Expand Up @@ -418,6 +420,11 @@ var agentConfig = `
hostname = ""
## If set to true, do no set the "host" tag in the telegraf agent.
omit_hostname = false
## Translator for SNMP OIDs
## Valid values are "netsnmp" which runs snmptranslate and snmptable,
## and "gosmi" which uses the gosmi library.
# translator = "netsnmp"
`

var outputHeader = `
Expand Down Expand Up @@ -855,6 +862,11 @@ func (c *Config) LoadConfigData(data []byte) error {
c.Tags["host"] = c.Agent.Hostname
}

// Set snmp agent translator default
if c.Agent.Translator == "" {
c.Agent.Translator = "netsnmp"
}

if len(c.UnusedFields) > 0 {
return fmt.Errorf("line %d: configuration specified the fields %q, but they weren't used", tbl.Line, keys(c.UnusedFields))
}
Expand Down
2 changes: 2 additions & 0 deletions internal/snmp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type ClientConfig struct {
Version uint8 `toml:"version"`
// Path to mib files
Path []string `toml:"path"`
// Translator implementation
Translator string `toml:"-"`

// Parameters for Version 1 & 2
Community string `toml:"community"`
Expand Down
5 changes: 5 additions & 0 deletions internal/snmp/translator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package snmp

type TranslatorPlugin interface {
SetTranslator(name string) // Agent calls this on inputs before Init
}
2 changes: 2 additions & 0 deletions plugins/inputs/snmp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ path onto the global path variable
# version = 2

## Path to mib files
## Used by the gosmi translator.
## To add paths when translating with netsnmp, use the MIBDIRS environment variable
# path = ["/usr/share/snmp/mibs"]

## SNMP community string.
Expand Down
123 changes: 123 additions & 0 deletions plugins/inputs/snmp/gosmi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package snmp

import (
"fmt"
"sync"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal/snmp"
"github.com/sleepinggenius2/gosmi"
)

type gosmiTranslator struct {
}

func NewGosmiTranslator(paths []string, log telegraf.Logger) (*gosmiTranslator, error) {
err := snmp.LoadMibsFromPath(paths, log, &snmp.GosmiMibLoader{})
if err == nil {
return &gosmiTranslator{}, nil
}
return nil, err
}

type gosmiSnmpTranslateCache struct {
mibName string
oidNum string
oidText string
conversion string
node gosmi.SmiNode
err error
}

var gosmiSnmpTranslateCachesLock sync.Mutex
var gosmiSnmpTranslateCaches map[string]gosmiSnmpTranslateCache

//nolint:revive
func (g *gosmiTranslator) SnmpTranslate(oid string) (string, string, string, string, error) {
a, b, c, d, _, e := g.SnmpTranslateFull(oid)
return a, b, c, d, e
}

//nolint:revive
func (g *gosmiTranslator) SnmpTranslateFull(oid string) (
mibName string, oidNum string, oidText string,
conversion string,
node gosmi.SmiNode,
err error) {
gosmiSnmpTranslateCachesLock.Lock()
if gosmiSnmpTranslateCaches == nil {
gosmiSnmpTranslateCaches = map[string]gosmiSnmpTranslateCache{}
}

var stc gosmiSnmpTranslateCache
var ok bool
if stc, ok = gosmiSnmpTranslateCaches[oid]; !ok {
// This will result in only one call to snmptranslate running at a time.
// We could speed it up by putting a lock in snmpTranslateCache and then
// returning it immediately, and multiple callers would then release the
// snmpTranslateCachesLock and instead wait on the individual
// snmpTranslation.Lock to release. But I don't know that the extra complexity
// is worth it. Especially when it would slam the system pretty hard if lots
// of lookups are being performed.

stc.mibName, stc.oidNum, stc.oidText, stc.conversion, stc.node, stc.err = snmp.SnmpTranslateCall(oid)
gosmiSnmpTranslateCaches[oid] = stc
}

gosmiSnmpTranslateCachesLock.Unlock()

return stc.mibName, stc.oidNum, stc.oidText, stc.conversion, stc.node, stc.err
}

type gosmiSnmpTableCache struct {
mibName string
oidNum string
oidText string
fields []Field
err error
}

var gosmiSnmpTableCaches map[string]gosmiSnmpTableCache
var gosmiSnmpTableCachesLock sync.Mutex

// snmpTable resolves the given OID as a table, providing information about the
// table and fields within.
//nolint:revive //Too many return variable but necessary
func (g *gosmiTranslator) SnmpTable(oid string) (
mibName string, oidNum string, oidText string,
fields []Field,
err error) {
gosmiSnmpTableCachesLock.Lock()
if gosmiSnmpTableCaches == nil {
gosmiSnmpTableCaches = map[string]gosmiSnmpTableCache{}
}

var stc gosmiSnmpTableCache
var ok bool
if stc, ok = gosmiSnmpTableCaches[oid]; !ok {
stc.mibName, stc.oidNum, stc.oidText, stc.fields, stc.err = g.SnmpTableCall(oid)
gosmiSnmpTableCaches[oid] = stc
}

gosmiSnmpTableCachesLock.Unlock()
return stc.mibName, stc.oidNum, stc.oidText, stc.fields, stc.err
}

//nolint:revive //Too many return variable but necessary
func (g *gosmiTranslator) SnmpTableCall(oid string) (mibName string, oidNum string, oidText string, fields []Field, err error) {
mibName, oidNum, oidText, _, node, err := g.SnmpTranslateFull(oid)
if err != nil {
return "", "", "", nil, fmt.Errorf("translating: %w", err)
}

mibPrefix := mibName + "::"

col, tagOids, err := snmp.GetIndex(oidNum, mibPrefix, node)

for _, c := range col {
_, isTag := tagOids[mibPrefix+c]
fields = append(fields, Field{Name: c, Oid: mibPrefix + c, IsTag: isTag})
}

return mibName, oidNum, oidText, fields, err
}
Loading

0 comments on commit 77040ef

Please sign in to comment.