2016-01-20 20:11:28 +00:00
|
|
|
package snmp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2016-01-27 21:21:36 +00:00
|
|
|
"github.com/influxdata/telegraf"
|
2016-01-20 20:11:28 +00:00
|
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
|
|
|
|
|
|
|
"github.com/soniah/gosnmp"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Snmp is a snmp plugin
|
|
|
|
type Snmp struct {
|
|
|
|
Host []Host
|
|
|
|
Get []Data
|
|
|
|
Bulk []Data
|
2016-02-17 05:08:20 +00:00
|
|
|
Table []Table
|
|
|
|
Subtable []Subtable
|
2016-01-20 20:11:28 +00:00
|
|
|
SnmptranslateFile string
|
2016-03-09 05:55:36 +00:00
|
|
|
|
|
|
|
nameToOid map[string]string
|
|
|
|
initNode Node
|
|
|
|
subTableMap map[string]Subtable
|
2016-01-20 20:11:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Host struct {
|
|
|
|
Address string
|
|
|
|
Community string
|
|
|
|
// SNMP version. Default 2
|
|
|
|
Version int
|
|
|
|
// SNMP timeout, in seconds. 0 means no timeout
|
|
|
|
Timeout float64
|
|
|
|
// SNMP retries
|
|
|
|
Retries int
|
|
|
|
// Data to collect (list of Data names)
|
|
|
|
Collect []string
|
|
|
|
// easy get oids
|
|
|
|
GetOids []string
|
2016-02-17 05:08:20 +00:00
|
|
|
// Table
|
|
|
|
Table []HostTable
|
2016-01-20 20:11:28 +00:00
|
|
|
// Oids
|
|
|
|
getOids []Data
|
|
|
|
bulkOids []Data
|
2016-02-17 05:08:20 +00:00
|
|
|
tables []HostTable
|
2016-03-02 00:45:01 +00:00
|
|
|
// array of processed oids
|
|
|
|
// to skip oid duplication
|
|
|
|
processedOids []string
|
2016-05-09 21:11:57 +00:00
|
|
|
|
|
|
|
OidInstanceMapping map[string]map[string]string
|
2016-02-17 05:08:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Table struct {
|
|
|
|
// name = "iftable"
|
|
|
|
Name string
|
|
|
|
// oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
|
|
Oid string
|
|
|
|
//if empty get all instances
|
|
|
|
//mapping_table = ".1.3.6.1.2.1.31.1.1.1.1"
|
|
|
|
MappingTable string
|
|
|
|
// if empty get all subtables
|
|
|
|
// sub_tables could be not "real subtables"
|
|
|
|
//sub_tables=[".1.3.6.1.2.1.2.2.1.13", "bytes_recv", "bytes_send"]
|
|
|
|
SubTables []string
|
|
|
|
}
|
|
|
|
|
|
|
|
type HostTable struct {
|
|
|
|
// name = "iftable"
|
|
|
|
Name string
|
|
|
|
// Includes only these instances
|
|
|
|
// include_instances = ["eth0", "eth1"]
|
|
|
|
IncludeInstances []string
|
|
|
|
// Excludes only these instances
|
|
|
|
// exclude_instances = ["eth20", "eth21"]
|
|
|
|
ExcludeInstances []string
|
|
|
|
// From Table struct
|
|
|
|
oid string
|
|
|
|
mappingTable string
|
|
|
|
subTables []string
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO find better names
|
|
|
|
type Subtable struct {
|
|
|
|
//name = "bytes_send"
|
|
|
|
Name string
|
|
|
|
//oid = ".1.3.6.1.2.1.31.1.1.1.10"
|
|
|
|
Oid string
|
|
|
|
//unit = "octets"
|
|
|
|
Unit string
|
2016-01-20 20:11:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Data struct {
|
|
|
|
Name string
|
|
|
|
// OID (could be numbers or name)
|
|
|
|
Oid string
|
|
|
|
// Unit
|
|
|
|
Unit string
|
|
|
|
// SNMP getbulk max repetition
|
|
|
|
MaxRepetition uint8 `toml:"max_repetition"`
|
|
|
|
// SNMP Instance (default 0)
|
|
|
|
// (only used with GET request and if
|
|
|
|
// OID is a name from snmptranslate file)
|
|
|
|
Instance string
|
|
|
|
// OID (only number) (used for computation)
|
|
|
|
rawOid string
|
|
|
|
}
|
|
|
|
|
|
|
|
type Node struct {
|
|
|
|
id string
|
|
|
|
name string
|
|
|
|
subnodes map[string]Node
|
|
|
|
}
|
|
|
|
|
|
|
|
var sampleConfig = `
|
2016-02-18 21:26:51 +00:00
|
|
|
## Use 'oids.txt' file to translate oids to names
|
|
|
|
## To generate 'oids.txt' you need to run:
|
|
|
|
## snmptranslate -m all -Tz -On | sed -e 's/"//g' > /tmp/oids.txt
|
|
|
|
## Or if you have an other MIB folder with custom MIBs
|
|
|
|
## snmptranslate -M /mycustommibfolder -Tz -On -m all | sed -e 's/"//g' > oids.txt
|
2016-01-20 20:11:28 +00:00
|
|
|
snmptranslate_file = "/tmp/oids.txt"
|
|
|
|
[[inputs.snmp.host]]
|
|
|
|
address = "192.168.2.2:161"
|
|
|
|
# SNMP community
|
|
|
|
community = "public" # default public
|
|
|
|
# SNMP version (1, 2 or 3)
|
|
|
|
# Version 3 not supported yet
|
|
|
|
version = 2 # default 2
|
|
|
|
# SNMP response timeout
|
|
|
|
timeout = 2.0 # default 2.0
|
|
|
|
# SNMP request retries
|
|
|
|
retries = 2 # default 2
|
|
|
|
# Which get/bulk do you want to collect for this host
|
|
|
|
collect = ["mybulk", "sysservices", "sysdescr"]
|
|
|
|
# Simple list of OIDs to get, in addition to "collect"
|
|
|
|
get_oids = []
|
|
|
|
|
|
|
|
[[inputs.snmp.host]]
|
|
|
|
address = "192.168.2.3:161"
|
|
|
|
community = "public"
|
|
|
|
version = 2
|
|
|
|
timeout = 2.0
|
|
|
|
retries = 2
|
|
|
|
collect = ["mybulk"]
|
|
|
|
get_oids = [
|
|
|
|
"ifNumber",
|
|
|
|
".1.3.6.1.2.1.1.3.0",
|
|
|
|
]
|
|
|
|
|
|
|
|
[[inputs.snmp.get]]
|
|
|
|
name = "ifnumber"
|
|
|
|
oid = "ifNumber"
|
|
|
|
|
|
|
|
[[inputs.snmp.get]]
|
|
|
|
name = "interface_speed"
|
|
|
|
oid = "ifSpeed"
|
2016-02-17 05:08:20 +00:00
|
|
|
instance = "0"
|
2016-01-20 20:11:28 +00:00
|
|
|
|
|
|
|
[[inputs.snmp.get]]
|
|
|
|
name = "sysuptime"
|
|
|
|
oid = ".1.3.6.1.2.1.1.3.0"
|
|
|
|
unit = "second"
|
|
|
|
|
|
|
|
[[inputs.snmp.bulk]]
|
|
|
|
name = "mybulk"
|
|
|
|
max_repetition = 127
|
|
|
|
oid = ".1.3.6.1.2.1.1"
|
|
|
|
|
|
|
|
[[inputs.snmp.bulk]]
|
|
|
|
name = "ifoutoctets"
|
|
|
|
max_repetition = 127
|
|
|
|
oid = "ifOutOctets"
|
2016-02-17 05:08:20 +00:00
|
|
|
|
|
|
|
[[inputs.snmp.host]]
|
|
|
|
address = "192.168.2.13:161"
|
|
|
|
#address = "127.0.0.1:161"
|
|
|
|
community = "public"
|
|
|
|
version = 2
|
|
|
|
timeout = 2.0
|
|
|
|
retries = 2
|
|
|
|
#collect = ["mybulk", "sysservices", "sysdescr", "systype"]
|
|
|
|
collect = ["sysuptime" ]
|
|
|
|
[[inputs.snmp.host.table]]
|
|
|
|
name = "iftable3"
|
|
|
|
include_instances = ["enp5s0", "eth1"]
|
|
|
|
|
|
|
|
# SNMP TABLEs
|
|
|
|
# table without mapping neither subtables
|
|
|
|
[[inputs.snmp.table]]
|
|
|
|
name = "iftable1"
|
|
|
|
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
|
|
|
|
|
|
# table without mapping but with subtables
|
|
|
|
[[inputs.snmp.table]]
|
|
|
|
name = "iftable2"
|
|
|
|
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
|
|
sub_tables = [".1.3.6.1.2.1.2.2.1.13"]
|
|
|
|
|
|
|
|
# table with mapping but without subtables
|
|
|
|
[[inputs.snmp.table]]
|
|
|
|
name = "iftable3"
|
|
|
|
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
|
|
# if empty. get all instances
|
|
|
|
mapping_table = ".1.3.6.1.2.1.31.1.1.1.1"
|
|
|
|
# if empty, get all subtables
|
|
|
|
|
|
|
|
# table with both mapping and subtables
|
|
|
|
[[inputs.snmp.table]]
|
|
|
|
name = "iftable4"
|
|
|
|
oid = ".1.3.6.1.2.1.31.1.1.1"
|
|
|
|
# if empty get all instances
|
|
|
|
mapping_table = ".1.3.6.1.2.1.31.1.1.1.1"
|
|
|
|
# if empty get all subtables
|
2016-03-31 23:50:24 +00:00
|
|
|
# sub_tables could be not "real subtables"
|
2016-02-17 05:08:20 +00:00
|
|
|
sub_tables=[".1.3.6.1.2.1.2.2.1.13", "bytes_recv", "bytes_send"]
|
2016-01-20 20:11:28 +00:00
|
|
|
`
|
|
|
|
|
|
|
|
// SampleConfig returns sample configuration message
|
|
|
|
func (s *Snmp) SampleConfig() string {
|
|
|
|
return sampleConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
// Description returns description of Zookeeper plugin
|
|
|
|
func (s *Snmp) Description() string {
|
|
|
|
return `Reads oids value from one or many snmp agents`
|
|
|
|
}
|
|
|
|
|
|
|
|
func fillnode(parentNode Node, oid_name string, ids []string) {
|
|
|
|
// ids = ["1", "3", "6", ...]
|
|
|
|
id, ids := ids[0], ids[1:]
|
|
|
|
node, ok := parentNode.subnodes[id]
|
|
|
|
if ok == false {
|
|
|
|
node = Node{
|
|
|
|
id: id,
|
|
|
|
name: "",
|
|
|
|
subnodes: make(map[string]Node),
|
|
|
|
}
|
|
|
|
if len(ids) == 0 {
|
|
|
|
node.name = oid_name
|
|
|
|
}
|
|
|
|
parentNode.subnodes[id] = node
|
|
|
|
}
|
|
|
|
if len(ids) > 0 {
|
|
|
|
fillnode(node, oid_name, ids)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func findnodename(node Node, ids []string) (string, string) {
|
|
|
|
// ids = ["1", "3", "6", ...]
|
|
|
|
if len(ids) == 1 {
|
|
|
|
return node.name, ids[0]
|
|
|
|
}
|
|
|
|
id, ids := ids[0], ids[1:]
|
|
|
|
// Get node
|
|
|
|
subnode, ok := node.subnodes[id]
|
|
|
|
if ok {
|
|
|
|
return findnodename(subnode, ids)
|
|
|
|
}
|
|
|
|
// We got a node
|
|
|
|
// Get node name
|
|
|
|
if node.name != "" && len(ids) == 0 && id == "0" {
|
|
|
|
// node with instance 0
|
|
|
|
return node.name, "0"
|
|
|
|
} else if node.name != "" && len(ids) == 0 && id != "0" {
|
|
|
|
// node with an instance
|
|
|
|
return node.name, string(id)
|
|
|
|
} else if node.name != "" && len(ids) > 0 {
|
|
|
|
// node with subinstances
|
|
|
|
return node.name, strings.Join(ids, ".")
|
|
|
|
}
|
|
|
|
// return an empty node name
|
|
|
|
return node.name, ""
|
|
|
|
}
|
|
|
|
|
2016-01-27 21:21:36 +00:00
|
|
|
func (s *Snmp) Gather(acc telegraf.Accumulator) error {
|
2016-03-09 05:55:36 +00:00
|
|
|
// TODO put this in cache on first run
|
2016-02-17 05:08:20 +00:00
|
|
|
// Create subtables mapping
|
2016-03-09 05:55:36 +00:00
|
|
|
if len(s.subTableMap) == 0 {
|
|
|
|
s.subTableMap = make(map[string]Subtable)
|
2016-02-17 05:08:20 +00:00
|
|
|
for _, sb := range s.Subtable {
|
2016-03-09 05:55:36 +00:00
|
|
|
s.subTableMap[sb.Name] = sb
|
2016-02-17 05:08:20 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-09 05:55:36 +00:00
|
|
|
// TODO put this in cache on first run
|
2016-01-20 20:11:28 +00:00
|
|
|
// Create oid tree
|
2016-03-09 05:55:36 +00:00
|
|
|
if s.SnmptranslateFile != "" && len(s.initNode.subnodes) == 0 {
|
|
|
|
s.nameToOid = make(map[string]string)
|
|
|
|
s.initNode = Node{
|
|
|
|
id: "1",
|
|
|
|
name: "",
|
|
|
|
subnodes: make(map[string]Node),
|
|
|
|
}
|
|
|
|
|
2016-01-20 20:11:28 +00:00
|
|
|
data, err := ioutil.ReadFile(s.SnmptranslateFile)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Reading SNMPtranslate file error: %s", err)
|
|
|
|
return err
|
|
|
|
} else {
|
|
|
|
for _, line := range strings.Split(string(data), "\n") {
|
2016-03-22 00:10:17 +00:00
|
|
|
oids := strings.Fields(string(line))
|
|
|
|
if len(oids) == 2 && oids[1] != "" {
|
|
|
|
oid_name := oids[0]
|
|
|
|
oid := oids[1]
|
2016-03-09 05:55:36 +00:00
|
|
|
fillnode(s.initNode, oid_name, strings.Split(string(oid), "."))
|
|
|
|
s.nameToOid[oid_name] = oid
|
2016-01-20 20:11:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Fetching data
|
|
|
|
for _, host := range s.Host {
|
|
|
|
// Set default args
|
|
|
|
if len(host.Address) == 0 {
|
|
|
|
host.Address = "127.0.0.1:161"
|
|
|
|
}
|
|
|
|
if host.Community == "" {
|
|
|
|
host.Community = "public"
|
|
|
|
}
|
|
|
|
if host.Timeout <= 0 {
|
|
|
|
host.Timeout = 2.0
|
|
|
|
}
|
|
|
|
if host.Retries <= 0 {
|
|
|
|
host.Retries = 2
|
|
|
|
}
|
|
|
|
// Prepare host
|
|
|
|
// Get Easy GET oids
|
|
|
|
for _, oidstring := range host.GetOids {
|
|
|
|
oid := Data{}
|
2016-03-09 05:55:36 +00:00
|
|
|
if val, ok := s.nameToOid[oidstring]; ok {
|
2016-01-20 20:11:28 +00:00
|
|
|
// TODO should we add the 0 instance ?
|
|
|
|
oid.Name = oidstring
|
|
|
|
oid.Oid = val
|
|
|
|
oid.rawOid = "." + val + ".0"
|
|
|
|
} else {
|
|
|
|
oid.Name = oidstring
|
|
|
|
oid.Oid = oidstring
|
|
|
|
if string(oidstring[:1]) != "." {
|
|
|
|
oid.rawOid = "." + oidstring
|
|
|
|
} else {
|
|
|
|
oid.rawOid = oidstring
|
|
|
|
}
|
|
|
|
}
|
|
|
|
host.getOids = append(host.getOids, oid)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, oid_name := range host.Collect {
|
|
|
|
// Get GET oids
|
|
|
|
for _, oid := range s.Get {
|
|
|
|
if oid.Name == oid_name {
|
2016-03-09 05:55:36 +00:00
|
|
|
if val, ok := s.nameToOid[oid.Oid]; ok {
|
2016-01-20 20:11:28 +00:00
|
|
|
// TODO should we add the 0 instance ?
|
|
|
|
if oid.Instance != "" {
|
|
|
|
oid.rawOid = "." + val + "." + oid.Instance
|
|
|
|
} else {
|
|
|
|
oid.rawOid = "." + val + ".0"
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
oid.rawOid = oid.Oid
|
|
|
|
}
|
|
|
|
host.getOids = append(host.getOids, oid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Get GETBULK oids
|
|
|
|
for _, oid := range s.Bulk {
|
|
|
|
if oid.Name == oid_name {
|
2016-03-09 05:55:36 +00:00
|
|
|
if val, ok := s.nameToOid[oid.Oid]; ok {
|
2016-01-20 20:11:28 +00:00
|
|
|
oid.rawOid = "." + val
|
|
|
|
} else {
|
|
|
|
oid.rawOid = oid.Oid
|
|
|
|
}
|
|
|
|
host.bulkOids = append(host.bulkOids, oid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-02-17 05:08:20 +00:00
|
|
|
// Table
|
|
|
|
for _, hostTable := range host.Table {
|
|
|
|
for _, snmpTable := range s.Table {
|
|
|
|
if hostTable.Name == snmpTable.Name {
|
|
|
|
table := hostTable
|
|
|
|
table.oid = snmpTable.Oid
|
|
|
|
table.mappingTable = snmpTable.MappingTable
|
|
|
|
table.subTables = snmpTable.SubTables
|
|
|
|
host.tables = append(host.tables, table)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Launch Mapping
|
2016-03-09 05:55:36 +00:00
|
|
|
// TODO put this in cache on first run
|
2016-02-17 05:08:20 +00:00
|
|
|
// TODO save mapping and computed oids
|
|
|
|
// to do it only the first time
|
2016-03-09 05:55:36 +00:00
|
|
|
// only if len(s.OidInstanceMapping) == 0
|
2016-05-09 21:11:57 +00:00
|
|
|
if len(host.OidInstanceMapping) >= 0 {
|
2016-03-09 05:55:36 +00:00
|
|
|
if err := host.SNMPMap(acc, s.nameToOid, s.subTableMap); err != nil {
|
2016-04-18 02:08:56 +00:00
|
|
|
log.Printf("SNMP Mapping error for host '%s': %s", host.Address, err)
|
|
|
|
continue
|
2016-02-17 05:08:20 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-20 20:11:28 +00:00
|
|
|
// Launch Get requests
|
2016-03-09 05:55:36 +00:00
|
|
|
if err := host.SNMPGet(acc, s.initNode); err != nil {
|
2016-04-18 02:08:56 +00:00
|
|
|
log.Printf("SNMP Error for host '%s': %s", host.Address, err)
|
2016-01-20 20:11:28 +00:00
|
|
|
}
|
2016-03-09 05:55:36 +00:00
|
|
|
if err := host.SNMPBulk(acc, s.initNode); err != nil {
|
2016-04-18 02:08:56 +00:00
|
|
|
log.Printf("SNMP Error for host '%s': %s", host.Address, err)
|
2016-01-20 20:11:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-05-09 21:11:57 +00:00
|
|
|
func (h *Host) SNMPMap(
|
|
|
|
acc telegraf.Accumulator,
|
|
|
|
nameToOid map[string]string,
|
|
|
|
subTableMap map[string]Subtable,
|
|
|
|
) error {
|
|
|
|
if h.OidInstanceMapping == nil {
|
|
|
|
h.OidInstanceMapping = make(map[string]map[string]string)
|
|
|
|
}
|
2016-02-17 05:08:20 +00:00
|
|
|
// Get snmp client
|
|
|
|
snmpClient, err := h.GetSNMPClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Deconnection
|
|
|
|
defer snmpClient.Conn.Close()
|
|
|
|
// Prepare OIDs
|
|
|
|
for _, table := range h.tables {
|
|
|
|
// We don't have mapping
|
|
|
|
if table.mappingTable == "" {
|
|
|
|
if len(table.subTables) == 0 {
|
|
|
|
// If We don't have mapping table
|
|
|
|
// neither subtables list
|
|
|
|
// This is just a bulk request
|
|
|
|
oid := Data{}
|
|
|
|
oid.Oid = table.oid
|
2016-03-09 05:55:36 +00:00
|
|
|
if val, ok := nameToOid[oid.Oid]; ok {
|
2016-02-17 05:08:20 +00:00
|
|
|
oid.rawOid = "." + val
|
|
|
|
} else {
|
|
|
|
oid.rawOid = oid.Oid
|
|
|
|
}
|
|
|
|
h.bulkOids = append(h.bulkOids, oid)
|
|
|
|
} else {
|
|
|
|
// If We don't have mapping table
|
|
|
|
// but we have subtables
|
|
|
|
// This is a bunch of bulk requests
|
|
|
|
// For each subtable ...
|
|
|
|
for _, sb := range table.subTables {
|
|
|
|
// ... we create a new Data (oid) object
|
|
|
|
oid := Data{}
|
|
|
|
// Looking for more information about this subtable
|
2016-03-09 05:55:36 +00:00
|
|
|
ssb, exists := subTableMap[sb]
|
2016-02-17 05:08:20 +00:00
|
|
|
if exists {
|
|
|
|
// We found a subtable section in config files
|
|
|
|
oid.Oid = ssb.Oid
|
|
|
|
oid.rawOid = ssb.Oid
|
|
|
|
oid.Unit = ssb.Unit
|
|
|
|
} else {
|
|
|
|
// We did NOT find a subtable section in config files
|
|
|
|
oid.Oid = sb
|
|
|
|
oid.rawOid = sb
|
|
|
|
}
|
|
|
|
// TODO check oid validity
|
|
|
|
|
|
|
|
// Add the new oid to getOids list
|
|
|
|
h.bulkOids = append(h.bulkOids, oid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We have a mapping table
|
|
|
|
// We need to query this table
|
|
|
|
// To get mapping between instance id
|
|
|
|
// and instance name
|
|
|
|
oid_asked := table.mappingTable
|
2016-03-07 04:13:29 +00:00
|
|
|
oid_next := oid_asked
|
2016-02-17 05:08:20 +00:00
|
|
|
need_more_requests := true
|
|
|
|
// Set max repetition
|
|
|
|
maxRepetition := uint8(32)
|
|
|
|
// Launch requests
|
|
|
|
for need_more_requests {
|
|
|
|
// Launch request
|
2016-03-07 04:13:29 +00:00
|
|
|
result, err3 := snmpClient.GetBulk([]string{oid_next}, 0, maxRepetition)
|
2016-02-17 05:08:20 +00:00
|
|
|
if err3 != nil {
|
|
|
|
return err3
|
|
|
|
}
|
|
|
|
|
|
|
|
lastOid := ""
|
|
|
|
for _, variable := range result.Variables {
|
|
|
|
lastOid = variable.Name
|
|
|
|
if strings.HasPrefix(variable.Name, oid_asked) {
|
|
|
|
switch variable.Type {
|
|
|
|
// handle instance names
|
|
|
|
case gosnmp.OctetString:
|
|
|
|
// Check if instance is in includes instances
|
|
|
|
getInstances := true
|
|
|
|
if len(table.IncludeInstances) > 0 {
|
|
|
|
getInstances = false
|
|
|
|
for _, instance := range table.IncludeInstances {
|
|
|
|
if instance == string(variable.Value.([]byte)) {
|
|
|
|
getInstances = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Check if instance is in excludes instances
|
|
|
|
if len(table.ExcludeInstances) > 0 {
|
|
|
|
getInstances = true
|
|
|
|
for _, instance := range table.ExcludeInstances {
|
|
|
|
if instance == string(variable.Value.([]byte)) {
|
|
|
|
getInstances = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We don't want this instance
|
|
|
|
if !getInstances {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove oid table from the complete oid
|
|
|
|
// in order to get the current instance id
|
|
|
|
key := strings.Replace(variable.Name, oid_asked, "", 1)
|
|
|
|
|
|
|
|
if len(table.subTables) == 0 {
|
|
|
|
// We have a mapping table
|
|
|
|
// but no subtables
|
|
|
|
// This is just a bulk request
|
|
|
|
|
|
|
|
// Building mapping table
|
|
|
|
mapping := map[string]string{strings.Trim(key, "."): string(variable.Value.([]byte))}
|
2016-05-09 21:11:57 +00:00
|
|
|
_, exists := h.OidInstanceMapping[table.oid]
|
2016-02-17 05:08:20 +00:00
|
|
|
if exists {
|
2016-05-09 21:11:57 +00:00
|
|
|
h.OidInstanceMapping[table.oid][strings.Trim(key, ".")] = string(variable.Value.([]byte))
|
2016-02-17 05:08:20 +00:00
|
|
|
} else {
|
2016-05-09 21:11:57 +00:00
|
|
|
h.OidInstanceMapping[table.oid] = mapping
|
2016-02-17 05:08:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add table oid in bulk oid list
|
|
|
|
oid := Data{}
|
|
|
|
oid.Oid = table.oid
|
2016-03-09 05:55:36 +00:00
|
|
|
if val, ok := nameToOid[oid.Oid]; ok {
|
2016-02-17 05:08:20 +00:00
|
|
|
oid.rawOid = "." + val
|
|
|
|
} else {
|
|
|
|
oid.rawOid = oid.Oid
|
|
|
|
}
|
|
|
|
h.bulkOids = append(h.bulkOids, oid)
|
|
|
|
} else {
|
|
|
|
// We have a mapping table
|
|
|
|
// and some subtables
|
|
|
|
// This is a bunch of get requests
|
|
|
|
// This is the best case :)
|
|
|
|
|
|
|
|
// For each subtable ...
|
|
|
|
for _, sb := range table.subTables {
|
|
|
|
// ... we create a new Data (oid) object
|
|
|
|
oid := Data{}
|
|
|
|
// Looking for more information about this subtable
|
2016-03-09 05:55:36 +00:00
|
|
|
ssb, exists := subTableMap[sb]
|
2016-02-17 05:08:20 +00:00
|
|
|
if exists {
|
|
|
|
// We found a subtable section in config files
|
|
|
|
oid.Oid = ssb.Oid + key
|
|
|
|
oid.rawOid = ssb.Oid + key
|
|
|
|
oid.Unit = ssb.Unit
|
|
|
|
oid.Instance = string(variable.Value.([]byte))
|
|
|
|
} else {
|
|
|
|
// We did NOT find a subtable section in config files
|
|
|
|
oid.Oid = sb + key
|
|
|
|
oid.rawOid = sb + key
|
|
|
|
oid.Instance = string(variable.Value.([]byte))
|
|
|
|
}
|
|
|
|
// TODO check oid validity
|
|
|
|
|
|
|
|
// Add the new oid to getOids list
|
|
|
|
h.getOids = append(h.getOids, oid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Determine if we need more requests
|
|
|
|
if strings.HasPrefix(lastOid, oid_asked) {
|
|
|
|
need_more_requests = true
|
2016-03-07 04:13:29 +00:00
|
|
|
oid_next = lastOid
|
2016-02-17 05:08:20 +00:00
|
|
|
} else {
|
|
|
|
need_more_requests = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Mapping finished
|
|
|
|
|
|
|
|
// Create newoids based on mapping
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-09 05:55:36 +00:00
|
|
|
func (h *Host) SNMPGet(acc telegraf.Accumulator, initNode Node) error {
|
2016-01-20 20:11:28 +00:00
|
|
|
// Get snmp client
|
|
|
|
snmpClient, err := h.GetSNMPClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Deconnection
|
|
|
|
defer snmpClient.Conn.Close()
|
|
|
|
// Prepare OIDs
|
|
|
|
oidsList := make(map[string]Data)
|
|
|
|
for _, oid := range h.getOids {
|
|
|
|
oidsList[oid.rawOid] = oid
|
|
|
|
}
|
|
|
|
oidsNameList := make([]string, 0, len(oidsList))
|
|
|
|
for _, oid := range oidsList {
|
|
|
|
oidsNameList = append(oidsNameList, oid.rawOid)
|
|
|
|
}
|
|
|
|
|
|
|
|
// gosnmp.MAX_OIDS == 60
|
|
|
|
// TODO use gosnmp.MAX_OIDS instead of hard coded value
|
|
|
|
max_oids := 60
|
|
|
|
// limit 60 (MAX_OIDS) oids by requests
|
|
|
|
for i := 0; i < len(oidsList); i = i + max_oids {
|
|
|
|
// Launch request
|
|
|
|
max_index := i + max_oids
|
|
|
|
if i+max_oids > len(oidsList) {
|
|
|
|
max_index = len(oidsList)
|
|
|
|
}
|
|
|
|
result, err3 := snmpClient.Get(oidsNameList[i:max_index]) // Get() accepts up to g.MAX_OIDS
|
|
|
|
if err3 != nil {
|
|
|
|
return err3
|
|
|
|
}
|
|
|
|
// Handle response
|
2016-03-09 05:55:36 +00:00
|
|
|
_, err = h.HandleResponse(oidsList, result, acc, initNode)
|
2016-01-20 20:11:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-09 05:55:36 +00:00
|
|
|
func (h *Host) SNMPBulk(acc telegraf.Accumulator, initNode Node) error {
|
2016-01-20 20:11:28 +00:00
|
|
|
// Get snmp client
|
|
|
|
snmpClient, err := h.GetSNMPClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Deconnection
|
|
|
|
defer snmpClient.Conn.Close()
|
|
|
|
// Prepare OIDs
|
|
|
|
oidsList := make(map[string]Data)
|
|
|
|
for _, oid := range h.bulkOids {
|
|
|
|
oidsList[oid.rawOid] = oid
|
|
|
|
}
|
|
|
|
oidsNameList := make([]string, 0, len(oidsList))
|
|
|
|
for _, oid := range oidsList {
|
|
|
|
oidsNameList = append(oidsNameList, oid.rawOid)
|
|
|
|
}
|
|
|
|
// TODO Trying to make requests with more than one OID
|
|
|
|
// to reduce the number of requests
|
|
|
|
for _, oid := range oidsNameList {
|
|
|
|
oid_asked := oid
|
|
|
|
need_more_requests := true
|
|
|
|
// Set max repetition
|
|
|
|
maxRepetition := oidsList[oid].MaxRepetition
|
|
|
|
if maxRepetition <= 0 {
|
|
|
|
maxRepetition = 32
|
|
|
|
}
|
|
|
|
// Launch requests
|
|
|
|
for need_more_requests {
|
|
|
|
// Launch request
|
|
|
|
result, err3 := snmpClient.GetBulk([]string{oid}, 0, maxRepetition)
|
|
|
|
if err3 != nil {
|
|
|
|
return err3
|
|
|
|
}
|
|
|
|
// Handle response
|
2016-03-09 05:55:36 +00:00
|
|
|
last_oid, err := h.HandleResponse(oidsList, result, acc, initNode)
|
2016-01-20 20:11:28 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Determine if we need more requests
|
|
|
|
if strings.HasPrefix(last_oid, oid_asked) {
|
|
|
|
need_more_requests = true
|
|
|
|
oid = last_oid
|
|
|
|
} else {
|
|
|
|
need_more_requests = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Host) GetSNMPClient() (*gosnmp.GoSNMP, error) {
|
|
|
|
// Prepare Version
|
|
|
|
var version gosnmp.SnmpVersion
|
|
|
|
if h.Version == 1 {
|
|
|
|
version = gosnmp.Version1
|
|
|
|
} else if h.Version == 3 {
|
|
|
|
version = gosnmp.Version3
|
|
|
|
} else {
|
|
|
|
version = gosnmp.Version2c
|
|
|
|
}
|
|
|
|
// Prepare host and port
|
|
|
|
host, port_str, err := net.SplitHostPort(h.Address)
|
|
|
|
if err != nil {
|
|
|
|
port_str = string("161")
|
|
|
|
}
|
|
|
|
// convert port_str to port in uint16
|
|
|
|
port_64, err := strconv.ParseUint(port_str, 10, 16)
|
|
|
|
port := uint16(port_64)
|
|
|
|
// Get SNMP client
|
|
|
|
snmpClient := &gosnmp.GoSNMP{
|
|
|
|
Target: host,
|
|
|
|
Port: port,
|
|
|
|
Community: h.Community,
|
|
|
|
Version: version,
|
|
|
|
Timeout: time.Duration(h.Timeout) * time.Second,
|
|
|
|
Retries: h.Retries,
|
|
|
|
}
|
|
|
|
// Connection
|
|
|
|
err2 := snmpClient.Connect()
|
|
|
|
if err2 != nil {
|
|
|
|
return nil, err2
|
|
|
|
}
|
|
|
|
// Return snmpClient
|
|
|
|
return snmpClient, nil
|
|
|
|
}
|
|
|
|
|
2016-05-09 21:11:57 +00:00
|
|
|
func (h *Host) HandleResponse(
|
|
|
|
oids map[string]Data,
|
|
|
|
result *gosnmp.SnmpPacket,
|
|
|
|
acc telegraf.Accumulator,
|
|
|
|
initNode Node,
|
|
|
|
) (string, error) {
|
2016-01-20 20:11:28 +00:00
|
|
|
var lastOid string
|
|
|
|
for _, variable := range result.Variables {
|
|
|
|
lastOid = variable.Name
|
2016-03-02 00:45:01 +00:00
|
|
|
nextresult:
|
|
|
|
// Get only oid wanted
|
2016-01-20 20:11:28 +00:00
|
|
|
for oid_key, oid := range oids {
|
2016-03-02 00:45:01 +00:00
|
|
|
// Skip oids already processed
|
|
|
|
for _, processedOid := range h.processedOids {
|
|
|
|
if variable.Name == processedOid {
|
|
|
|
break nextresult
|
|
|
|
}
|
|
|
|
}
|
2016-04-16 17:00:52 +00:00
|
|
|
// If variable.Name is the same as oid_key
|
|
|
|
// OR
|
|
|
|
// the result is SNMP table which "." comes right after oid_key.
|
|
|
|
// ex: oid_key: .1.3.6.1.2.1.2.2.1.16, variable.Name: .1.3.6.1.2.1.2.2.1.16.1
|
|
|
|
if variable.Name == oid_key || strings.HasPrefix(variable.Name, oid_key+".") {
|
2016-01-20 20:11:28 +00:00
|
|
|
switch variable.Type {
|
|
|
|
// handle Metrics
|
|
|
|
case gosnmp.Boolean, gosnmp.Integer, gosnmp.Counter32, gosnmp.Gauge32,
|
2016-05-23 10:21:53 +00:00
|
|
|
gosnmp.TimeTicks, gosnmp.Counter64, gosnmp.Uinteger32, gosnmp.OctetString:
|
2016-01-20 20:11:28 +00:00
|
|
|
// Prepare tags
|
|
|
|
tags := make(map[string]string)
|
|
|
|
if oid.Unit != "" {
|
|
|
|
tags["unit"] = oid.Unit
|
|
|
|
}
|
|
|
|
// Get name and instance
|
|
|
|
var oid_name string
|
|
|
|
var instance string
|
2016-02-17 05:08:20 +00:00
|
|
|
// Get oidname and instance from translate file
|
2016-01-20 20:11:28 +00:00
|
|
|
oid_name, instance = findnodename(initNode,
|
|
|
|
strings.Split(string(variable.Name[1:]), "."))
|
2016-02-17 05:08:20 +00:00
|
|
|
// Set instance tag
|
|
|
|
// From mapping table
|
2016-05-09 21:11:57 +00:00
|
|
|
mapping, inMappingNoSubTable := h.OidInstanceMapping[oid_key]
|
2016-02-17 05:08:20 +00:00
|
|
|
if inMappingNoSubTable {
|
|
|
|
// filter if the instance in not in
|
|
|
|
// OidInstanceMapping mapping map
|
|
|
|
if instance_name, exists := mapping[instance]; exists {
|
|
|
|
tags["instance"] = instance_name
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else if oid.Instance != "" {
|
|
|
|
// From config files
|
|
|
|
tags["instance"] = oid.Instance
|
|
|
|
} else if instance != "" {
|
|
|
|
// Using last id of the current oid, ie:
|
|
|
|
// with .1.3.6.1.2.1.31.1.1.1.10.3
|
|
|
|
// instance is 3
|
2016-01-20 20:11:28 +00:00
|
|
|
tags["instance"] = instance
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set name
|
|
|
|
var field_name string
|
|
|
|
if oid_name != "" {
|
|
|
|
// Set fieldname as oid name from translate file
|
|
|
|
field_name = oid_name
|
|
|
|
} else {
|
|
|
|
// Set fieldname as oid name from inputs.snmp.get section
|
|
|
|
// Because the result oid is equal to inputs.snmp.get section
|
|
|
|
field_name = oid.Name
|
|
|
|
}
|
2016-05-10 20:27:18 +00:00
|
|
|
tags["snmp_host"], _, _ = net.SplitHostPort(h.Address)
|
2016-01-20 20:11:28 +00:00
|
|
|
fields := make(map[string]interface{})
|
|
|
|
fields[string(field_name)] = variable.Value
|
|
|
|
|
2016-03-02 00:45:01 +00:00
|
|
|
h.processedOids = append(h.processedOids, variable.Name)
|
2016-01-20 20:11:28 +00:00
|
|
|
acc.AddFields(field_name, fields, tags)
|
|
|
|
case gosnmp.NoSuchObject, gosnmp.NoSuchInstance:
|
|
|
|
// Oid not found
|
|
|
|
log.Printf("[snmp input] Oid not found: %s", oid_key)
|
|
|
|
default:
|
|
|
|
// delete other data
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return lastOid, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2016-01-27 21:21:36 +00:00
|
|
|
inputs.Add("snmp", func() telegraf.Input {
|
2016-01-20 20:11:28 +00:00
|
|
|
return &Snmp{}
|
|
|
|
})
|
|
|
|
}
|