1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,418 @@
# SNMP Input Plugin
The `snmp` input plugin uses polling to gather metrics from SNMP agents.
Support for gathering individual OIDs as well as complete SNMP tables is
included.
## Note about Paths
Path is a global variable, separate snmp instances will append the specified
path onto the global path variable
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Secret-store support
This plugin supports secrets from secret-stores for the `auth_password` and
`priv_password` option.
See the [secret-store documentation][SECRETSTORE] for more details on how
to use them.
[SECRETSTORE]: ../../../docs/CONFIGURATION.md#secret-store-secrets
## SNMP backend: gosmi and netsnmp
Telegraf has two backends to translate SNMP objects. By default, Telegraf will
use `netsnmp`, however, this option is deprecated and it is encouraged that
users migrate to `gosmi`. If users find issues with `gosmi` that do not occur
with `netsnmp` please open a project issue on GitHub.
The SNMP backend setting is a global-level setting that applies to all use of
SNMP in Telegraf. Users can set this option in the `[agent]` configuration via
the `snmp_translator` option. See the [agent configuration][AGENT] for more
details.
[AGENT]: ../../../docs/CONFIGURATION.md#agent
## Configuration
```toml @sample.conf
# Retrieves SNMP values from remote agents
[[inputs.snmp]]
## Agent addresses to retrieve values from.
## format: agents = ["<scheme://><hostname>:<port>"]
## scheme: optional, either udp, udp4, udp6, tcp, tcp4, tcp6.
## default is udp
## port: optional
## example: agents = ["udp://127.0.0.1:161"]
## agents = ["tcp://127.0.0.1:161"]
## agents = ["udp4://v4only-snmp-agent"]
agents = ["udp://127.0.0.1:161"]
## Timeout for each request.
# timeout = "5s"
## SNMP version; can be 1, 2, or 3.
# version = 2
## Unconnected UDP socket
## When true, SNMP responses are accepted from any address not just
## the requested address. This can be useful when gathering from
## redundant/failover systems.
# unconnected_udp_socket = false
## 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.
# community = "public"
## Agent host tag; should be set to "source" for consistent usage across plugins
## example: agent_host_tag = "source"
## The default value is inconsistent with other plugins. Users will get a
## warning that can be ignored if this is not changed. However, to have a
## consistent experience, set this to "source" in your config to align with
## other plugins.
# agent_host_tag = "agent_host"
## Number of retries to attempt.
# retries = 3
## The GETBULK max-repetitions parameter.
# max_repetitions = 10
## SNMPv3 authentication and encryption options.
##
## Security Name.
# sec_name = "myuser"
## Authentication protocol; one of "MD5", "SHA", "SHA224", "SHA256", "SHA384", "SHA512" or "".
# auth_protocol = "MD5"
## Authentication password.
# auth_password = "pass"
## Security Level; one of "noAuthNoPriv", "authNoPriv", or "authPriv".
# sec_level = "authNoPriv"
## Context Name.
# context_name = ""
## Privacy protocol used for encrypted messages; one of "DES", "AES", "AES192", "AES192C", "AES256", "AES256C", or "".
### Protocols "AES192", "AES192", "AES256", and "AES256C" require the underlying net-snmp tools
### to be compiled with --enable-blumenthal-aes (http://www.net-snmp.org/docs/INSTALL.html)
# priv_protocol = ""
## Privacy password used for encrypted messages.
# priv_password = ""
## Add fields and tables defining the variables you wish to collect. This
## example collects the system uptime and interface variables. Reference the
## full plugin documentation for configuration details.
[[inputs.snmp.field]]
oid = "RFC1213-MIB::sysUpTime.0"
name = "sysUptime"
conversion = "float(2)"
[[inputs.snmp.field]]
oid = "RFC1213-MIB::sysName.0"
name = "sysName"
is_tag = true
[[inputs.snmp.table]]
oid = "IF-MIB::ifTable"
name = "interface"
inherit_tags = ["sysName"]
[[inputs.snmp.table.field]]
oid = "IF-MIB::ifDescr"
name = "ifDescr"
is_tag = true
```
### Configure SNMP Requests
This plugin provides two methods for configuring the SNMP requests: `fields`
and `tables`. Use the `field` option to gather single ad-hoc variables.
To collect SNMP tables, use the `table` option.
#### Field
Use a `field` to collect a variable by OID. Requests specified with this
option operate similar to the `snmpget` utility.
```toml
[[inputs.snmp]]
# ... snip ...
[[inputs.snmp.field]]
## Object identifier of the variable as a numeric or textual OID.
oid = "RFC1213-MIB::sysName.0"
## Name of the field or tag to create. If not specified, it defaults to
## the value of 'oid'. If 'oid' is numeric, an attempt to translate the
## numeric OID into a textual OID will be made.
# name = ""
## If true the variable will be added as a tag, otherwise a field will be
## created.
# is_tag = false
## Apply one of the following conversions to the variable value:
## float(X): Convert the input value into a float and divides by the
## Xth power of 10. Effectively just moves the decimal left
## X places. For example a value of `123` with `float(2)`
## will result in `1.23`.
## float: Convert the value into a float with no adjustment. Same
## as `float(0)`.
## int: Convert the value into an integer.
## ipaddr: Convert the value to an IP address.
## hex: Convert bytes to a hex string.
## hextoint:X:Y Convert bytes to integer, where X is the endian and Y the
## bit size. For example: hextoint:LittleEndian:uint64 or
## hextoint:BigEndian:uint32. Valid options for the endian
## are: BigEndian and LittleEndian. For the bit size:
## uint16, uint32 and uint64.
## enum: Convert the value according to its syntax in the MIB.
## (Only supported with gosmi translator)
## displayhint: Format the value according to the textual convention in the MIB.
## (Only supported with gosmi translator)
##
# conversion = ""
```
#### Table
Use a `table` to configure the collection of a SNMP table. SNMP requests
formed with this option operate similarly way to the `snmptable` command.
Control the handling of specific table columns using a nested `field`. These
nested fields are specified similarly to a top-level `field`.
By default all columns of the SNMP table will be collected - it is not required
to add a nested field for each column, only those which you wish to modify. To
*only* collect certain columns, omit the `oid` from the `table` section and only
include `oid` settings in `field` sections. For more complex include/exclude
cases for columns use [metric filtering][].
One [metric][] is created for each row of the SNMP table.
```toml
[[inputs.snmp]]
# ... snip ...
[[inputs.snmp.table]]
## Object identifier of the SNMP table as a numeric or textual OID.
oid = "IF-MIB::ifTable"
## Name of the field or tag to create. If not specified, it defaults to
## the value of 'oid'. If 'oid' is numeric an attempt to translate the
## numeric OID into a textual OID will be made.
# name = ""
## Which tags to inherit from the top-level config and to use in the output
## of this table's measurement.
## example: inherit_tags = ["source"]
# inherit_tags = []
## Add an 'index' tag with the table row number. Use this if the table has
## no indexes or if you are excluding them. This option is normally not
## required as any index columns are automatically added as tags.
# index_as_tag = false
[[inputs.snmp.table.field]]
## OID to get. May be a numeric or textual module-qualified OID.
oid = "IF-MIB::ifDescr"
## Name of the field or tag to create. If not specified, it defaults to
## the value of 'oid'. If 'oid' is numeric an attempt to translate the
## numeric OID into a textual OID will be made.
# name = ""
## Output this field as a tag.
# is_tag = false
## The OID sub-identifier to strip off so that the index can be matched
## against other fields in the table.
# oid_index_suffix = ""
## Specifies the length of the index after the supplied table OID (in OID
## path segments). Truncates the index after this point to remove non-fixed
## value or length index suffixes.
# oid_index_length = 0
## Specifies if the value of given field should be snmptranslated
## by default no field values are translated
# translate = true
## Secondary index table allows to merge data from two tables with
## different index that this filed will be used to join them. There can
## be only one secondary index table.
# secondary_index_table = false
## This field is using secondary index, and will be later merged with
## primary index using SecondaryIndexTable. SecondaryIndexTable and
## SecondaryIndexUse are exclusive.
# secondary_index_use = false
## Controls if entries from secondary table should be added or not
## if joining index is present or not. I set to true, means that join
## is outer, and index is prepended with "Secondary." for missing values
## to avoid overlapping indexes from both tables. Can be set per field or
## globally with SecondaryIndexTable, global true overrides per field false.
# secondary_outer_join = false
```
#### Two Table Join
Snmp plugin can join two snmp tables that have different indexes. For this to
work one table should have translation field that return index of second table
as value. Examples of such fields are:
* Cisco portTable with translation field: `CISCO-STACK-MIB::portIfIndex`,
which value is IfIndex from ifTable
* Adva entityFacilityTable with translation field: `ADVA-FSPR7-MIB::entityFacilityOneIndex`,
which value is IfIndex from ifTable
* Cisco cpeExtPsePortTable with translation field: `CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortEntPhyIndex`,
which value is index from entPhysicalTable
Such field can be used to translate index to secondary table with
`secondary_index_table = true` and all fields from secondary table (with index
pointed from translation field), should have added option `secondary_index_use =
true`. Telegraf cannot duplicate entries during join so translation must be
1-to-1 (not 1-to-many). To add fields from secondary table with index that is
not present in translation table (outer join), there is a second option for
translation index `secondary_outer_join = true`.
##### Example configuration for table joins
CISCO-POWER-ETHERNET-EXT-MIB table before join:
```toml
[[inputs.snmp.table]]
name = "ciscoPower"
index_as_tag = true
[[inputs.snmp.table.field]]
name = "PortPwrConsumption"
oid = "CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortPwrConsumption"
[[inputs.snmp.table.field]]
name = "EntPhyIndex"
oid = "CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortEntPhyIndex"
```
Partial result (removed agent and host tags from all following outputs
in this section):
```text
> ciscoPower,index=1.2 EntPhyIndex=1002i,PortPwrConsumption=6643i 1621460628000000000
> ciscoPower,index=1.6 EntPhyIndex=1006i,PortPwrConsumption=10287i 1621460628000000000
> ciscoPower,index=1.5 EntPhyIndex=1005i,PortPwrConsumption=8358i 1621460628000000000
```
Note here that EntPhyIndex column carries index from ENTITY-MIB table, config
for it:
```toml
[[inputs.snmp.table]]
name = "entityTable"
index_as_tag = true
[[inputs.snmp.table.field]]
name = "EntPhysicalName"
oid = "ENTITY-MIB::entPhysicalName"
```
Partial result:
```text
> entityTable,index=1006 EntPhysicalName="GigabitEthernet1/6" 1621460809000000000
> entityTable,index=1002 EntPhysicalName="GigabitEthernet1/2" 1621460809000000000
> entityTable,index=1005 EntPhysicalName="GigabitEthernet1/5" 1621460809000000000
```
Now, lets attempt to join these results into one table. EntPhyIndex matches
index from second table, and lets convert EntPhysicalName into tag, so second
table will only provide tags into result. Configuration:
```toml
[[inputs.snmp.table]]
name = "ciscoPowerEntity"
index_as_tag = true
[[inputs.snmp.table.field]]
name = "PortPwrConsumption"
oid = "CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortPwrConsumption"
[[inputs.snmp.table.field]]
name = "EntPhyIndex"
oid = "CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortEntPhyIndex"
secondary_index_table = true # enables joining
[[inputs.snmp.table.field]]
name = "EntPhysicalName"
oid = "ENTITY-MIB::entPhysicalName"
secondary_index_use = true # this tag is indexed from secondary table
is_tag = true
```
Result:
```text
> ciscoPowerEntity,EntPhysicalName=GigabitEthernet1/2,index=1.2 EntPhyIndex=1002i,PortPwrConsumption=6643i 1621461148000000000
> ciscoPowerEntity,EntPhysicalName=GigabitEthernet1/6,index=1.6 EntPhyIndex=1006i,PortPwrConsumption=10287i 1621461148000000000
> ciscoPowerEntity,EntPhysicalName=GigabitEthernet1/5,index=1.5 EntPhyIndex=1005i,PortPwrConsumption=8358i 1621461148000000000
```
## Troubleshooting
Check that a numeric field can be translated to a textual field:
```sh
$ snmptranslate .1.3.6.1.2.1.1.3.0
DISMAN-EVENT-MIB::sysUpTimeInstance
```
Request a top-level field:
```sh
snmpget -v2c -c public 127.0.0.1 sysUpTime.0
```
Request a table:
```sh
snmptable -v2c -c public 127.0.0.1 ifTable
```
To collect a packet capture, run this command in the background while running
Telegraf or one of the above commands. Adjust the interface, host and port as
needed:
```sh
sudo tcpdump -s 0 -i eth0 -w telegraf-snmp.pcap host 127.0.0.1 and port 161
```
## Metrics
The field and tags will depend on the table and fields configured.
* snmp
* tags:
* agent_host (deprecated in 1.29: use `source` instead)
## Example Output
```text
snmp,agent_host=127.0.0.1,sysName=example.org uptime=113319.74 1575509815000000000
interface,agent_host=127.0.0.1,ifDescr=wlan0,ifIndex=3,sysName=example.org ifAdminStatus=1i,ifInDiscards=0i,ifInErrors=0i,ifInNUcastPkts=0i,ifInOctets=3436617431i,ifInUcastPkts=2717778i,ifInUnknownProtos=0i,ifLastChange=0i,ifMtu=1500i,ifOperStatus=1i,ifOutDiscards=0i,ifOutErrors=0i,ifOutNUcastPkts=0i,ifOutOctets=581368041i,ifOutQLen=0i,ifOutUcastPkts=1354338i,ifPhysAddress="c8:5b:76:c9:e6:8c",ifSpecific=".0.0",ifSpeed=0i,ifType=6i 1575509815000000000
interface,agent_host=127.0.0.1,ifDescr=eth0,ifIndex=2,sysName=example.org ifAdminStatus=1i,ifInDiscards=0i,ifInErrors=0i,ifInNUcastPkts=21i,ifInOctets=3852386380i,ifInUcastPkts=3634004i,ifInUnknownProtos=0i,ifLastChange=9088763i,ifMtu=1500i,ifOperStatus=1i,ifOutDiscards=0i,ifOutErrors=0i,ifOutNUcastPkts=0i,ifOutOctets=434865441i,ifOutQLen=0i,ifOutUcastPkts=2110394i,ifPhysAddress="c8:5b:76:c9:e6:8c",ifSpecific=".0.0",ifSpeed=1000000000i,ifType=6i 1575509815000000000
interface,agent_host=127.0.0.1,ifDescr=lo,ifIndex=1,sysName=example.org ifAdminStatus=1i,ifInDiscards=0i,ifInErrors=0i,ifInNUcastPkts=0i,ifInOctets=51555569i,ifInUcastPkts=339097i,ifInUnknownProtos=0i,ifLastChange=0i,ifMtu=65536i,ifOperStatus=1i,ifOutDiscards=0i,ifOutErrors=0i,ifOutNUcastPkts=0i,ifOutOctets=51555569i,ifOutQLen=0i,ifOutUcastPkts=339097i,ifSpecific=".0.0",ifSpeed=10000000i,ifType=24i 1575509815000000000
```
[metric filtering]: /docs/CONFIGURATION.md#metric-filtering
[metric]: /docs/METRICS.md

View file

@ -0,0 +1,87 @@
# Retrieves SNMP values from remote agents
[[inputs.snmp]]
## Agent addresses to retrieve values from.
## format: agents = ["<scheme://><hostname>:<port>"]
## scheme: optional, either udp, udp4, udp6, tcp, tcp4, tcp6.
## default is udp
## port: optional
## example: agents = ["udp://127.0.0.1:161"]
## agents = ["tcp://127.0.0.1:161"]
## agents = ["udp4://v4only-snmp-agent"]
agents = ["udp://127.0.0.1:161"]
## Timeout for each request.
# timeout = "5s"
## SNMP version; can be 1, 2, or 3.
# version = 2
## Unconnected UDP socket
## When true, SNMP responses are accepted from any address not just
## the requested address. This can be useful when gathering from
## redundant/failover systems.
# unconnected_udp_socket = false
## 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.
# community = "public"
## Agent host tag; should be set to "source" for consistent usage across plugins
## example: agent_host_tag = "source"
## The default value is inconsistent with other plugins. Users will get a
## warning that can be ignored if this is not changed. However, to have a
## consistent experience, set this to "source" in your config to align with
## other plugins.
# agent_host_tag = "agent_host"
## Number of retries to attempt.
# retries = 3
## The GETBULK max-repetitions parameter.
# max_repetitions = 10
## SNMPv3 authentication and encryption options.
##
## Security Name.
# sec_name = "myuser"
## Authentication protocol; one of "MD5", "SHA", "SHA224", "SHA256", "SHA384", "SHA512" or "".
# auth_protocol = "MD5"
## Authentication password.
# auth_password = "pass"
## Security Level; one of "noAuthNoPriv", "authNoPriv", or "authPriv".
# sec_level = "authNoPriv"
## Context Name.
# context_name = ""
## Privacy protocol used for encrypted messages; one of "DES", "AES", "AES192", "AES192C", "AES256", "AES256C", or "".
### Protocols "AES192", "AES192", "AES256", and "AES256C" require the underlying net-snmp tools
### to be compiled with --enable-blumenthal-aes (http://www.net-snmp.org/docs/INSTALL.html)
# priv_protocol = ""
## Privacy password used for encrypted messages.
# priv_password = ""
## Add fields and tables defining the variables you wish to collect. This
## example collects the system uptime and interface variables. Reference the
## full plugin documentation for configuration details.
[[inputs.snmp.field]]
oid = "RFC1213-MIB::sysUpTime.0"
name = "sysUptime"
conversion = "float(2)"
[[inputs.snmp.field]]
oid = "RFC1213-MIB::sysName.0"
name = "sysName"
is_tag = true
[[inputs.snmp.table]]
oid = "IF-MIB::ifTable"
name = "interface"
inherit_tags = ["sysName"]
[[inputs.snmp.table.field]]
oid = "IF-MIB::ifDescr"
name = "ifDescr"
is_tag = true

206
plugins/inputs/snmp/snmp.go Normal file
View file

@ -0,0 +1,206 @@
//go:generate ../../../tools/readme_config_includer/generator
package snmp
import (
_ "embed"
"errors"
"fmt"
"sync"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal/snmp"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
type Snmp struct {
// The SNMP agent to query. Format is [SCHEME://]ADDR[:PORT] (e.g.
// udp://1.2.3.4:161). If the scheme is not specified then "udp" is used.
Agents []string `toml:"agents"`
// The tag used to name the agent host
AgentHostTag string `toml:"agent_host_tag"`
snmp.ClientConfig
Tables []snmp.Table `toml:"table"`
// Name & Fields are the elements of a Table.
// Telegraf chokes if we try to embed a Table. So instead we have to embed the
// fields of a Table, and construct a Table during runtime.
Name string `toml:"name"`
Fields []snmp.Field `toml:"field"`
Log telegraf.Logger `toml:"-"`
connectionCache []snmp.Connection
translator snmp.Translator
}
func (*Snmp) SampleConfig() string {
return sampleConfig
}
func (s *Snmp) SetTranslator(name string) {
s.Translator = name
}
func (s *Snmp) Init() error {
var err error
switch s.Translator {
case "gosmi":
s.translator, err = snmp.NewGosmiTranslator(s.Path, s.Log)
if err != nil {
return err
}
case "netsnmp":
s.translator = snmp.NewNetsnmpTranslator(s.Log)
default:
return errors.New("invalid translator value")
}
s.connectionCache = make([]snmp.Connection, len(s.Agents))
for i := range s.Tables {
if err := s.Tables[i].Init(s.translator); err != nil {
return fmt.Errorf("initializing table %s: %w", s.Tables[i].Name, err)
}
}
for i := range s.Fields {
if err := s.Fields[i].Init(s.translator); err != nil {
return fmt.Errorf("initializing field %s: %w", s.Fields[i].Name, err)
}
}
if len(s.AgentHostTag) == 0 {
s.AgentHostTag = "agent_host"
}
if s.AgentHostTag != "source" {
config.PrintOptionValueDeprecationNotice("inputs.snmp", "agent_host_tag", s.AgentHostTag, telegraf.DeprecationInfo{
Since: "1.29.0",
Notice: `set to "source" for consistent usage across plugins or safely ignore this message and continue to use the current value`,
})
}
return nil
}
func (s *Snmp) Gather(acc telegraf.Accumulator) error {
var wg sync.WaitGroup
for i, agent := range s.Agents {
wg.Add(1)
go func(i int, agent string) {
defer wg.Done()
gs, err := s.getConnection(i)
if err != nil {
acc.AddError(fmt.Errorf("agent %s: %w", agent, err))
return
}
// First is the top-level fields. We treat the fields as table prefixes with an empty index.
t := snmp.Table{
Name: s.Name,
Fields: s.Fields,
}
topTags := make(map[string]string)
if err := s.gatherTable(acc, gs, t, topTags, false); err != nil {
acc.AddError(fmt.Errorf("agent %s: %w", agent, err))
}
// Now is the real tables.
for _, t := range s.Tables {
if err := s.gatherTable(acc, gs, t, topTags, true); err != nil {
acc.AddError(fmt.Errorf("agent %s: gathering table %s: %w", agent, t.Name, err))
}
}
}(i, agent)
}
wg.Wait()
return nil
}
func (s *Snmp) gatherTable(acc telegraf.Accumulator, gs snmp.Connection, t snmp.Table, topTags map[string]string, walk bool) error {
rt, err := t.Build(gs, walk)
if err != nil {
return err
}
for _, tr := range rt.Rows {
if !walk {
// top-level table. Add tags to topTags.
for k, v := range tr.Tags {
topTags[k] = v
}
} else {
// real table. Inherit any specified tags.
for _, k := range t.InheritTags {
if v, ok := topTags[k]; ok {
tr.Tags[k] = v
}
}
}
if _, ok := tr.Tags[s.AgentHostTag]; !ok {
tr.Tags[s.AgentHostTag] = gs.Host()
}
acc.AddFields(rt.Name, tr.Fields, tr.Tags, rt.Time)
}
return nil
}
// getConnection creates a snmpConnection (*gosnmp.GoSNMP) object and caches the
// result using `agentIndex` as the cache key. This is done to allow multiple
// connections to a single address. It is an error to use a connection in
// more than one goroutine.
func (s *Snmp) getConnection(idx int) (snmp.Connection, error) {
if gs := s.connectionCache[idx]; gs != nil {
if err := gs.Reconnect(); err != nil {
return gs, fmt.Errorf("reconnecting: %w", err)
}
return gs, nil
}
agent := s.Agents[idx]
gs, err := snmp.NewWrapper(s.ClientConfig)
if err != nil {
return nil, err
}
err = gs.SetAgent(agent)
if err != nil {
return nil, err
}
s.connectionCache[idx] = gs
if err := gs.Connect(); err != nil {
return nil, fmt.Errorf("setting up connection: %w", err)
}
return gs, nil
}
func init() {
inputs.Add("snmp", func() telegraf.Input {
return &Snmp{
Name: "snmp",
ClientConfig: snmp.ClientConfig{
Retries: 3,
MaxRepetitions: 10,
Timeout: config.Duration(5 * time.Second),
Version: 2,
Path: []string{"/usr/share/snmp/mibs"},
Community: "public",
},
}
})
}

View file

@ -0,0 +1,801 @@
package snmp
import (
"fmt"
"net"
"path/filepath"
"sync"
"testing"
"time"
"github.com/gosnmp/gosnmp"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal/snmp"
"github.com/influxdata/telegraf/testutil"
)
type testSNMPConnection struct {
host string
values map[string]interface{}
}
func (tsc *testSNMPConnection) Host() string {
return tsc.host
}
func (tsc *testSNMPConnection) Get(oids []string) (*gosnmp.SnmpPacket, error) {
sp := &gosnmp.SnmpPacket{}
for _, oid := range oids {
v, ok := tsc.values[oid]
if !ok {
sp.Variables = append(sp.Variables, gosnmp.SnmpPDU{
Name: oid,
Type: gosnmp.NoSuchObject,
})
continue
}
sp.Variables = append(sp.Variables, gosnmp.SnmpPDU{
Name: oid,
Value: v,
})
}
return sp, nil
}
func (tsc *testSNMPConnection) Walk(oid string, wf gosnmp.WalkFunc) error {
for void, v := range tsc.values {
if void == oid || (len(void) > len(oid) && void[:len(oid)+1] == oid+".") {
if err := wf(gosnmp.SnmpPDU{
Name: void,
Value: v,
}); err != nil {
return err
}
}
}
return nil
}
func (*testSNMPConnection) Reconnect() error {
return nil
}
var tsc = &testSNMPConnection{
host: "tsc",
values: map[string]interface{}{
".1.0.0.0.1.1.0": "foo",
".1.0.0.0.1.1.1": []byte("bar"),
".1.0.0.0.1.1.2": []byte(""),
".1.0.0.0.1.102": "bad",
".1.0.0.0.1.2.0": 1,
".1.0.0.0.1.2.1": 2,
".1.0.0.0.1.2.2": 0,
".1.0.0.0.1.3.0": "0.123",
".1.0.0.0.1.3.1": "0.456",
".1.0.0.0.1.3.2": "0.000",
".1.0.0.0.1.3.3": "9.999",
".1.0.0.0.1.5.0": 123456,
".1.0.0.0.1.6.0": ".1.0.0.0.1.7",
".1.0.0.1.1": "baz",
".1.0.0.1.2": 234,
".1.0.0.1.3": []byte("byte slice"),
".1.0.0.2.1.5.0.9.9": 11,
".1.0.0.2.1.5.1.9.9": 22,
".1.0.0.3.1.1.10": "instance",
".1.0.0.3.1.1.11": "instance2",
".1.0.0.3.1.1.12": "instance3",
".1.0.0.3.1.2.10": 10,
".1.0.0.3.1.2.11": 20,
".1.0.0.3.1.2.12": 20,
".1.0.0.3.1.3.10": 1,
".1.0.0.3.1.3.11": 2,
".1.0.0.3.1.3.12": 3,
".1.3.6.1.2.1.3.1.1.1.0": "foo",
".1.3.6.1.2.1.3.1.1.1.1": []byte("bar"),
".1.3.6.1.2.1.3.1.1.1.2": []byte(""),
".1.3.6.1.2.1.3.1.1.102": "bad",
".1.3.6.1.2.1.3.1.1.2.0": 1,
".1.3.6.1.2.1.3.1.1.2.1": 2,
".1.3.6.1.2.1.3.1.1.2.2": 0,
".1.3.6.1.2.1.3.1.1.3.0": "1.3.6.1.2.1.3.1.1.3",
".1.3.6.1.2.1.3.1.1.5.0": 123456,
},
}
func TestSnmpInit(t *testing.T) {
s := &Snmp{
ClientConfig: snmp.ClientConfig{
Translator: "netsnmp",
},
}
require.NoError(t, s.Init())
}
func TestSnmpInit_noTranslate(t *testing.T) {
s := &Snmp{
Fields: []snmp.Field{
{Oid: ".1.1.1.1", Name: "one", IsTag: true},
{Oid: ".1.1.1.2", Name: "two"},
{Oid: ".1.1.1.3"},
},
Tables: []snmp.Table{
{Name: "testing",
Fields: []snmp.Field{
{Oid: ".1.1.1.4", Name: "four", IsTag: true},
{Oid: ".1.1.1.5", Name: "five"},
{Oid: ".1.1.1.6"},
}},
},
ClientConfig: snmp.ClientConfig{
Translator: "netsnmp",
},
Log: testutil.Logger{Name: "inputs.snmp"},
}
err := s.Init()
require.NoError(t, err)
require.Equal(t, ".1.1.1.1", s.Fields[0].Oid)
require.Equal(t, "one", s.Fields[0].Name)
require.True(t, s.Fields[0].IsTag)
require.Equal(t, ".1.1.1.2", s.Fields[1].Oid)
require.Equal(t, "two", s.Fields[1].Name)
require.False(t, s.Fields[1].IsTag)
require.Equal(t, ".1.1.1.3", s.Fields[2].Oid)
require.Equal(t, ".1.1.1.3", s.Fields[2].Name)
require.False(t, s.Fields[2].IsTag)
require.Equal(t, ".1.1.1.4", s.Tables[0].Fields[0].Oid)
require.Equal(t, "four", s.Tables[0].Fields[0].Name)
require.True(t, s.Tables[0].Fields[0].IsTag)
require.Equal(t, ".1.1.1.5", s.Tables[0].Fields[1].Oid)
require.Equal(t, "five", s.Tables[0].Fields[1].Name)
require.False(t, s.Tables[0].Fields[1].IsTag)
require.Equal(t, ".1.1.1.6", s.Tables[0].Fields[2].Oid)
require.Equal(t, ".1.1.1.6", s.Tables[0].Fields[2].Name)
require.False(t, s.Tables[0].Fields[2].IsTag)
}
func TestSnmpInit_noName_noOid(t *testing.T) {
s := &Snmp{
Tables: []snmp.Table{
{Fields: []snmp.Field{
{Oid: ".1.1.1.4", Name: "four", IsTag: true},
{Oid: ".1.1.1.5", Name: "five"},
{Oid: ".1.1.1.6"},
}},
},
}
require.Error(t, s.Init())
}
func TestGetSNMPConnection_v2(t *testing.T) {
s := &Snmp{
Agents: []string{"1.2.3.4:567", "1.2.3.4", "udp://127.0.0.1"},
ClientConfig: snmp.ClientConfig{
Timeout: config.Duration(3 * time.Second),
Retries: 4,
Version: 2,
Community: "foo",
Translator: "netsnmp",
},
}
require.NoError(t, s.Init())
gsc, err := s.getConnection(0)
require.NoError(t, err)
gs := gsc.(snmp.GosnmpWrapper)
require.Equal(t, "1.2.3.4", gs.Target)
require.EqualValues(t, 567, gs.Port)
require.Equal(t, gosnmp.Version2c, gs.Version)
require.Equal(t, "foo", gs.Community)
require.Equal(t, "udp", gs.Transport)
gsc, err = s.getConnection(1)
require.NoError(t, err)
gs = gsc.(snmp.GosnmpWrapper)
require.Equal(t, "1.2.3.4", gs.Target)
require.EqualValues(t, 161, gs.Port)
require.Equal(t, "udp", gs.Transport)
gsc, err = s.getConnection(2)
require.NoError(t, err)
gs = gsc.(snmp.GosnmpWrapper)
require.Equal(t, "127.0.0.1", gs.Target)
require.EqualValues(t, 161, gs.Port)
require.Equal(t, "udp", gs.Transport)
}
func TestGetSNMPConnectionTCP(t *testing.T) {
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
require.NoError(t, err)
tcpServer, err := net.ListenTCP("tcp", tcpAddr)
require.NoError(t, err)
defer tcpServer.Close()
s := &Snmp{
Agents: []string{fmt.Sprintf("tcp://%s", tcpServer.Addr())},
ClientConfig: snmp.ClientConfig{
Translator: "netsnmp",
},
}
require.NoError(t, s.Init())
gsc, err := s.getConnection(0)
require.NoError(t, err)
gs := gsc.(snmp.GosnmpWrapper)
require.Equal(t, "127.0.0.1", gs.Target)
require.Equal(t, "tcp", gs.Transport)
}
func TestGetSNMPConnection_v3(t *testing.T) {
s := &Snmp{
Agents: []string{"1.2.3.4"},
ClientConfig: snmp.ClientConfig{
Version: 3,
MaxRepetitions: 20,
ContextName: "mycontext",
SecLevel: "authPriv",
SecName: "myuser",
AuthProtocol: "md5",
AuthPassword: config.NewSecret([]byte("password123")),
PrivProtocol: "des",
PrivPassword: config.NewSecret([]byte("321drowssap")),
EngineID: "myengineid",
EngineBoots: 1,
EngineTime: 2,
Translator: "netsnmp",
},
}
err := s.Init()
require.NoError(t, err)
gsc, err := s.getConnection(0)
require.NoError(t, err)
gs := gsc.(snmp.GosnmpWrapper)
require.Equal(t, gosnmp.Version3, gs.Version)
sp := gs.SecurityParameters.(*gosnmp.UsmSecurityParameters)
require.Equal(t, "1.2.3.4", gsc.Host())
require.EqualValues(t, 20, gs.MaxRepetitions)
require.Equal(t, "mycontext", gs.ContextName)
require.Equal(t, gosnmp.AuthPriv, gs.MsgFlags&gosnmp.AuthPriv)
require.Equal(t, "myuser", sp.UserName)
require.Equal(t, gosnmp.MD5, sp.AuthenticationProtocol)
require.Equal(t, "password123", sp.AuthenticationPassphrase)
require.Equal(t, gosnmp.DES, sp.PrivacyProtocol)
require.Equal(t, "321drowssap", sp.PrivacyPassphrase)
require.Equal(t, "myengineid", sp.AuthoritativeEngineID)
require.EqualValues(t, 1, sp.AuthoritativeEngineBoots)
require.EqualValues(t, 2, sp.AuthoritativeEngineTime)
}
func TestGetSNMPConnection_v3_blumenthal(t *testing.T) {
testCases := []struct {
Name string
Algorithm gosnmp.SnmpV3PrivProtocol
Config *Snmp
}{
{
Name: "AES192",
Algorithm: gosnmp.AES192,
Config: &Snmp{
Agents: []string{"1.2.3.4"},
ClientConfig: snmp.ClientConfig{
Version: 3,
MaxRepetitions: 20,
ContextName: "mycontext",
SecLevel: "authPriv",
SecName: "myuser",
AuthProtocol: "md5",
AuthPassword: config.NewSecret([]byte("password123")),
PrivProtocol: "AES192",
PrivPassword: config.NewSecret([]byte("password123")),
EngineID: "myengineid",
EngineBoots: 1,
EngineTime: 2,
Translator: "netsnmp",
},
},
},
{
Name: "AES192C",
Algorithm: gosnmp.AES192C,
Config: &Snmp{
Agents: []string{"1.2.3.4"},
ClientConfig: snmp.ClientConfig{
Version: 3,
MaxRepetitions: 20,
ContextName: "mycontext",
SecLevel: "authPriv",
SecName: "myuser",
AuthProtocol: "md5",
AuthPassword: config.NewSecret([]byte("password123")),
PrivProtocol: "AES192C",
PrivPassword: config.NewSecret([]byte("password123")),
EngineID: "myengineid",
EngineBoots: 1,
EngineTime: 2,
Translator: "netsnmp",
},
},
},
{
Name: "AES256",
Algorithm: gosnmp.AES256,
Config: &Snmp{
Agents: []string{"1.2.3.4"},
ClientConfig: snmp.ClientConfig{
Version: 3,
MaxRepetitions: 20,
ContextName: "mycontext",
SecLevel: "authPriv",
SecName: "myuser",
AuthProtocol: "md5",
AuthPassword: config.NewSecret([]byte("password123")),
PrivProtocol: "AES256",
PrivPassword: config.NewSecret([]byte("password123")),
EngineID: "myengineid",
EngineBoots: 1,
EngineTime: 2,
Translator: "netsnmp",
},
},
},
{
Name: "AES256C",
Algorithm: gosnmp.AES256C,
Config: &Snmp{
Agents: []string{"1.2.3.4"},
ClientConfig: snmp.ClientConfig{
Version: 3,
MaxRepetitions: 20,
ContextName: "mycontext",
SecLevel: "authPriv",
SecName: "myuser",
AuthProtocol: "md5",
AuthPassword: config.NewSecret([]byte("password123")),
PrivProtocol: "AES256C",
PrivPassword: config.NewSecret([]byte("password123")),
EngineID: "myengineid",
EngineBoots: 1,
EngineTime: 2,
Translator: "netsnmp",
},
},
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
s := tc.Config
err := s.Init()
require.NoError(t, err)
gsc, err := s.getConnection(0)
require.NoError(t, err)
gs := gsc.(snmp.GosnmpWrapper)
require.Equal(t, gosnmp.Version3, gs.Version)
sp := gs.SecurityParameters.(*gosnmp.UsmSecurityParameters)
require.Equal(t, "1.2.3.4", gsc.Host())
require.EqualValues(t, 20, gs.MaxRepetitions)
require.Equal(t, "mycontext", gs.ContextName)
require.Equal(t, gosnmp.AuthPriv, gs.MsgFlags&gosnmp.AuthPriv)
require.Equal(t, "myuser", sp.UserName)
require.Equal(t, gosnmp.MD5, sp.AuthenticationProtocol)
require.Equal(t, "password123", sp.AuthenticationPassphrase)
require.Equal(t, tc.Algorithm, sp.PrivacyProtocol)
require.Equal(t, "password123", sp.PrivacyPassphrase)
require.Equal(t, "myengineid", sp.AuthoritativeEngineID)
require.EqualValues(t, 1, sp.AuthoritativeEngineBoots)
require.EqualValues(t, 2, sp.AuthoritativeEngineTime)
})
}
}
func TestGetSNMPConnection_caching(t *testing.T) {
s := &Snmp{
Agents: []string{"1.2.3.4", "1.2.3.5", "1.2.3.5"},
ClientConfig: snmp.ClientConfig{
Translator: "netsnmp",
},
}
err := s.Init()
require.NoError(t, err)
gs1, err := s.getConnection(0)
require.NoError(t, err)
gs2, err := s.getConnection(0)
require.NoError(t, err)
gs3, err := s.getConnection(1)
require.NoError(t, err)
gs4, err := s.getConnection(2)
require.NoError(t, err)
require.Equal(t, gs1, gs2)
require.NotEqual(t, gs2, gs3)
require.NotEqual(t, gs3, gs4)
}
func TestGosnmpWrapper_walk_retry(t *testing.T) {
t.Skip("Skipping test due to random failures.")
srvr, err := net.ListenUDP("udp4", &net.UDPAddr{})
require.NoError(t, err)
defer srvr.Close()
reqCount := 0
// Set up a WaitGroup to wait for the server goroutine to exit and protect
// reqCount.
// Even though simultaneous access is impossible because the server will be
// blocked on ReadFrom, without this the race detector gets unhappy.
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
buf := make([]byte, 256)
for {
_, addr, err := srvr.ReadFrom(buf)
if err != nil {
return
}
reqCount++
// will cause decoding error
if _, err := srvr.WriteTo([]byte{'X'}, addr); err != nil {
return
}
}
}()
gs := &gosnmp.GoSNMP{
Target: srvr.LocalAddr().(*net.UDPAddr).IP.String(),
Port: uint16(srvr.LocalAddr().(*net.UDPAddr).Port),
Version: gosnmp.Version2c,
Community: "public",
Timeout: time.Millisecond * 10,
Retries: 1,
}
err = gs.Connect()
require.NoError(t, err)
conn := gs.Conn
gsw := snmp.GosnmpWrapper{
GoSNMP: gs,
}
err = gsw.Walk(".1.0.0", func(gosnmp.SnmpPDU) error { return nil })
require.NoError(t, srvr.Close())
wg.Wait()
require.Error(t, err)
require.NotEqual(t, gs.Conn, conn)
require.Equal(t, (gs.Retries+1)*2, reqCount)
}
func TestGosnmpWrapper_get_retry(t *testing.T) {
// TODO: Fix this test
t.Skip("Test failing too often, skip for now and revisit later.")
srvr, err := net.ListenUDP("udp4", &net.UDPAddr{})
require.NoError(t, err)
defer srvr.Close()
reqCount := 0
// Set up a WaitGroup to wait for the server goroutine to exit and protect
// reqCount.
// Even though simultaneous access is impossible because the server will be
// blocked on ReadFrom, without this the race detector gets unhappy.
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
buf := make([]byte, 256)
for {
_, addr, err := srvr.ReadFrom(buf)
if err != nil {
return
}
reqCount++
// will cause decoding error
if _, err := srvr.WriteTo([]byte{'X'}, addr); err != nil {
return
}
}
}()
gs := &gosnmp.GoSNMP{
Target: srvr.LocalAddr().(*net.UDPAddr).IP.String(),
Port: uint16(srvr.LocalAddr().(*net.UDPAddr).Port),
Version: gosnmp.Version2c,
Community: "public",
Timeout: time.Millisecond * 10,
Retries: 1,
}
err = gs.Connect()
require.NoError(t, err)
conn := gs.Conn
gsw := snmp.GosnmpWrapper{
GoSNMP: gs,
}
_, err = gsw.Get([]string{".1.0.0"})
require.NoError(t, srvr.Close())
wg.Wait()
require.Error(t, err)
require.NotEqual(t, gs.Conn, conn)
require.Equal(t, (gs.Retries+1)*2, reqCount)
}
func TestGather(t *testing.T) {
s := &Snmp{
Agents: []string{"TestGather"},
Name: "mytable",
Fields: []snmp.Field{
{
Name: "myfield1",
Oid: ".1.0.0.1.1",
IsTag: true,
},
{
Name: "myfield2",
Oid: ".1.0.0.1.2",
},
{
Name: "myfield3",
Oid: "1.0.0.1.1",
},
},
Tables: []snmp.Table{
{
Name: "myOtherTable",
InheritTags: []string{"myfield1"},
Fields: []snmp.Field{
{
Name: "myOtherField",
Oid: ".1.0.0.0.1.5",
},
},
},
},
connectionCache: []snmp.Connection{
tsc,
},
}
acc := &testutil.Accumulator{}
tstart := time.Now()
require.NoError(t, s.Gather(acc))
tstop := time.Now()
require.Len(t, acc.Metrics, 2)
m := acc.Metrics[0]
require.Equal(t, "mytable", m.Measurement)
require.Equal(t, "tsc", m.Tags[s.AgentHostTag])
require.Equal(t, "baz", m.Tags["myfield1"])
require.Len(t, m.Fields, 2)
require.Equal(t, 234, m.Fields["myfield2"])
require.Equal(t, "baz", m.Fields["myfield3"])
require.WithinRange(t, m.Time, tstart, tstop)
m2 := acc.Metrics[1]
require.Equal(t, "myOtherTable", m2.Measurement)
require.Equal(t, "tsc", m2.Tags[s.AgentHostTag])
require.Equal(t, "baz", m2.Tags["myfield1"])
require.Len(t, m2.Fields, 1)
require.Equal(t, 123456, m2.Fields["myOtherField"])
}
func TestGather_host(t *testing.T) {
s := &Snmp{
Agents: []string{"TestGather"},
Name: "mytable",
Fields: []snmp.Field{
{
Name: "host",
Oid: ".1.0.0.1.1",
IsTag: true,
},
{
Name: "myfield2",
Oid: ".1.0.0.1.2",
},
},
connectionCache: []snmp.Connection{
tsc,
},
}
acc := &testutil.Accumulator{}
require.NoError(t, s.Gather(acc))
require.Len(t, acc.Metrics, 1)
m := acc.Metrics[0]
require.Equal(t, "baz", m.Tags["host"])
}
func TestSnmpInitGosmi(t *testing.T) {
testDataPath, err := filepath.Abs("../../../internal/snmp/testdata/gosmi")
require.NoError(t, err)
s := &Snmp{
Tables: []snmp.Table{
{Oid: "RFC1213-MIB::atTable"},
},
Fields: []snmp.Field{
{Oid: "RFC1213-MIB::atPhysAddress"},
},
ClientConfig: snmp.ClientConfig{
Path: []string{testDataPath},
Translator: "gosmi",
},
}
require.NoError(t, s.Init())
require.Len(t, s.Tables[0].Fields, 3)
require.Equal(t, ".1.3.6.1.2.1.3.1.1.1", s.Tables[0].Fields[0].Oid)
require.Equal(t, "atIfIndex", s.Tables[0].Fields[0].Name)
require.True(t, s.Tables[0].Fields[0].IsTag)
require.Empty(t, s.Tables[0].Fields[0].Conversion)
require.Equal(t, ".1.3.6.1.2.1.3.1.1.2", s.Tables[0].Fields[1].Oid)
require.Equal(t, "atPhysAddress", s.Tables[0].Fields[1].Name)
require.False(t, s.Tables[0].Fields[1].IsTag)
require.Equal(t, "displayhint", s.Tables[0].Fields[1].Conversion)
require.Equal(t, ".1.3.6.1.2.1.3.1.1.3", s.Tables[0].Fields[2].Oid)
require.Equal(t, "atNetAddress", s.Tables[0].Fields[2].Name)
require.True(t, s.Tables[0].Fields[2].IsTag)
require.Empty(t, s.Tables[0].Fields[2].Conversion)
require.Equal(t, ".1.3.6.1.2.1.3.1.1.2", s.Fields[0].Oid)
require.Equal(t, "atPhysAddress", s.Fields[0].Name)
require.False(t, s.Fields[0].IsTag)
require.Equal(t, "displayhint", s.Fields[0].Conversion)
}
func TestSnmpInit_noTranslateGosmi(t *testing.T) {
s := &Snmp{
Fields: []snmp.Field{
{Oid: ".9.1.1.1.1", Name: "one", IsTag: true},
{Oid: ".9.1.1.1.2", Name: "two"},
{Oid: ".9.1.1.1.3"},
},
Tables: []snmp.Table{
{Name: "testing",
Fields: []snmp.Field{
{Oid: ".9.1.1.1.4", Name: "four", IsTag: true},
{Oid: ".9.1.1.1.5", Name: "five"},
{Oid: ".9.1.1.1.6"},
}},
},
ClientConfig: snmp.ClientConfig{
Translator: "gosmi",
},
}
require.NoError(t, s.Init())
require.Equal(t, ".9.1.1.1.1", s.Fields[0].Oid)
require.Equal(t, "one", s.Fields[0].Name)
require.True(t, s.Fields[0].IsTag)
require.Equal(t, ".9.1.1.1.2", s.Fields[1].Oid)
require.Equal(t, "two", s.Fields[1].Name)
require.False(t, s.Fields[1].IsTag)
require.Equal(t, ".9.1.1.1.3", s.Fields[2].Oid)
require.Equal(t, ".9.1.1.1.3", s.Fields[2].Name)
require.False(t, s.Fields[2].IsTag)
require.Equal(t, ".9.1.1.1.4", s.Tables[0].Fields[0].Oid)
require.Equal(t, "four", s.Tables[0].Fields[0].Name)
require.True(t, s.Tables[0].Fields[0].IsTag)
require.Equal(t, ".9.1.1.1.5", s.Tables[0].Fields[1].Oid)
require.Equal(t, "five", s.Tables[0].Fields[1].Name)
require.False(t, s.Tables[0].Fields[1].IsTag)
require.Equal(t, ".9.1.1.1.6", s.Tables[0].Fields[2].Oid)
require.Equal(t, ".9.1.1.1.6", s.Tables[0].Fields[2].Name)
require.False(t, s.Tables[0].Fields[2].IsTag)
}
func TestGatherGosmi(t *testing.T) {
s := &Snmp{
Agents: []string{"TestGather"},
Name: "mytable",
Fields: []snmp.Field{
{
Name: "myfield1",
Oid: ".1.0.0.1.1",
IsTag: true,
},
{
Name: "myfield2",
Oid: ".1.0.0.1.2",
},
{
Name: "myfield3",
Oid: "1.0.0.1.1",
},
},
Tables: []snmp.Table{
{
Name: "myOtherTable",
InheritTags: []string{"myfield1"},
Fields: []snmp.Field{
{
Name: "myOtherField",
Oid: ".1.0.0.0.1.5",
},
},
},
},
connectionCache: []snmp.Connection{tsc},
ClientConfig: snmp.ClientConfig{
Translator: "gosmi",
},
}
acc := &testutil.Accumulator{}
tstart := time.Now()
require.NoError(t, s.Gather(acc))
tstop := time.Now()
require.Len(t, acc.Metrics, 2)
m := acc.Metrics[0]
require.Equal(t, "mytable", m.Measurement)
require.Equal(t, "tsc", m.Tags[s.AgentHostTag])
require.Equal(t, "baz", m.Tags["myfield1"])
require.Len(t, m.Fields, 2)
require.Equal(t, 234, m.Fields["myfield2"])
require.Equal(t, "baz", m.Fields["myfield3"])
require.WithinRange(t, m.Time, tstart, tstop)
m2 := acc.Metrics[1]
require.Equal(t, "myOtherTable", m2.Measurement)
require.Equal(t, "tsc", m2.Tags[s.AgentHostTag])
require.Equal(t, "baz", m2.Tags["myfield1"])
require.Len(t, m2.Fields, 1)
require.Equal(t, 123456, m2.Fields["myOtherField"])
}
func TestGather_hostGosmi(t *testing.T) {
s := &Snmp{
Agents: []string{"TestGather"},
Name: "mytable",
Fields: []snmp.Field{
{
Name: "host",
Oid: ".1.0.0.1.1",
IsTag: true,
},
{
Name: "myfield2",
Oid: ".1.0.0.1.2",
},
},
connectionCache: []snmp.Connection{tsc},
}
acc := &testutil.Accumulator{}
require.NoError(t, s.Gather(acc))
require.Len(t, acc.Metrics, 1)
m := acc.Metrics[0]
require.Equal(t, "baz", m.Tags["host"])
}