Add snmp table feature #540

This commit is contained in:
Thibault Cohen 2016-02-17 00:08:20 -05:00 committed by Thibault Cohen
parent 74aaf4f75b
commit ca989b4229
1 changed files with 318 additions and 4 deletions

View File

@ -20,6 +20,8 @@ type Snmp struct {
Host []Host Host []Host
Get []Data Get []Data
Bulk []Data Bulk []Data
Table []Table
Subtable []Subtable
SnmptranslateFile string SnmptranslateFile string
} }
@ -36,9 +38,51 @@ type Host struct {
Collect []string Collect []string
// easy get oids // easy get oids
GetOids []string GetOids []string
// Table
Table []HostTable
// Oids // Oids
getOids []Data getOids []Data
bulkOids []Data bulkOids []Data
tables []HostTable
}
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
} }
type Data struct { type Data struct {
@ -69,8 +113,12 @@ var initNode = Node{
subnodes: make(map[string]Node), subnodes: make(map[string]Node),
} }
var SubTableMap = make(map[string]Subtable)
var NameToOid = make(map[string]string) var NameToOid = make(map[string]string)
var OidInstanceMapping = make(map[string]map[string]string)
var sampleConfig = ` var sampleConfig = `
## Use 'oids.txt' file to translate oids to names ## Use 'oids.txt' file to translate oids to names
## To generate 'oids.txt' you need to run: ## To generate 'oids.txt' you need to run:
@ -113,7 +161,7 @@ var sampleConfig = `
[[inputs.snmp.get]] [[inputs.snmp.get]]
name = "interface_speed" name = "interface_speed"
oid = "ifSpeed" oid = "ifSpeed"
instance = 0 instance = "0"
[[inputs.snmp.get]] [[inputs.snmp.get]]
name = "sysuptime" name = "sysuptime"
@ -129,6 +177,52 @@ var sampleConfig = `
name = "ifoutoctets" name = "ifoutoctets"
max_repetition = 127 max_repetition = 127
oid = "ifOutOctets" oid = "ifOutOctets"
[[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
# sub_tables could be not "real subtables"
sub_tables=[".1.3.6.1.2.1.2.2.1.13", "bytes_recv", "bytes_send"]
` `
// SampleConfig returns sample configuration message // SampleConfig returns sample configuration message
@ -189,6 +283,12 @@ func findnodename(node Node, ids []string) (string, string) {
} }
func (s *Snmp) Gather(acc telegraf.Accumulator) error { func (s *Snmp) Gather(acc telegraf.Accumulator) error {
// Create subtables mapping
if len(SubTableMap) == 0 {
for _, sb := range s.Subtable {
SubTableMap[sb.Name] = sb
}
}
// Create oid tree // Create oid tree
if s.SnmptranslateFile != "" && len(initNode.subnodes) == 0 { if s.SnmptranslateFile != "" && len(initNode.subnodes) == 0 {
data, err := ioutil.ReadFile(s.SnmptranslateFile) data, err := ioutil.ReadFile(s.SnmptranslateFile)
@ -273,6 +373,27 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error {
} }
} }
} }
// 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
// TODO save mapping and computed oids
// to do it only the first time
// only if len(OidInstanceMapping) == 0
if len(OidInstanceMapping) >= 0 {
if err := host.SNMPMap(acc); err != nil {
return err
}
}
// Launch Get requests // Launch Get requests
if err := host.SNMPGet(acc); err != nil { if err := host.SNMPGet(acc); err != nil {
return err return err
@ -284,6 +405,183 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error {
return nil return nil
} }
func (h *Host) SNMPMap(acc telegraf.Accumulator) error {
// 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
if val, ok := NameToOid[oid.Oid]; ok {
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
ssb, exists := SubTableMap[sb]
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
need_more_requests := true
// Set max repetition
maxRepetition := uint8(32)
// Launch requests
for need_more_requests {
// Launch request
result, err3 := snmpClient.GetBulk([]string{oid_asked}, 0, maxRepetition)
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))}
_, exists := OidInstanceMapping[table.oid]
if exists {
OidInstanceMapping[table.oid][strings.Trim(key, ".")] = string(variable.Value.([]byte))
} else {
OidInstanceMapping[table.oid] = mapping
}
// Add table oid in bulk oid list
oid := Data{}
oid.Oid = table.oid
if val, ok := NameToOid[oid.Oid]; ok {
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
ssb, exists := SubTableMap[sb]
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
} else {
need_more_requests = false
}
}
}
}
// Mapping finished
// Create newoids based on mapping
return nil
}
func (h *Host) SNMPGet(acc telegraf.Accumulator) error { func (h *Host) SNMPGet(acc telegraf.Accumulator) error {
// Get snmp client // Get snmp client
snmpClient, err := h.GetSNMPClient() snmpClient, err := h.GetSNMPClient()
@ -431,11 +729,27 @@ func (h *Host) HandleResponse(oids map[string]Data, result *gosnmp.SnmpPacket, a
// Get name and instance // Get name and instance
var oid_name string var oid_name string
var instance string var instance string
// Get oidname and instannce from translate file // Get oidname and instance from translate file
oid_name, instance = findnodename(initNode, oid_name, instance = findnodename(initNode,
strings.Split(string(variable.Name[1:]), ".")) strings.Split(string(variable.Name[1:]), "."))
// Set instance tag
if instance != "" { // From mapping table
mapping, inMappingNoSubTable := OidInstanceMapping[oid_key]
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
tags["instance"] = instance tags["instance"] = instance
} }