Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
418
plugins/inputs/snmp/README.md
Normal file
418
plugins/inputs/snmp/README.md
Normal 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
|
87
plugins/inputs/snmp/sample.conf
Normal file
87
plugins/inputs/snmp/sample.conf
Normal 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
206
plugins/inputs/snmp/snmp.go
Normal 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",
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
801
plugins/inputs/snmp/snmp_test.go
Normal file
801
plugins/inputs/snmp/snmp_test.go
Normal 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"])
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue