Add IPVS input plugin (#4890)
This commit is contained in:
committed by
Daniel Nelson
parent
600b468db2
commit
b88436c9d7
26
plugins/inputs/ipvs/README.md
Normal file
26
plugins/inputs/ipvs/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# IPVS Input Plugin (Linux)
|
||||
|
||||
The IPVS input plugin uses the linux kernel netlink socket interface to gather
|
||||
metrics about ipvs virtual and real servers.
|
||||
|
||||
## Configuration
|
||||
|
||||
[[inputs.ipvs]]
|
||||
# no configuration
|
||||
|
||||
## Permissions
|
||||
|
||||
Assuming you installed the telegraf package via one of the published packages,
|
||||
the process will be running as the `telegraf` user. However, in order for this
|
||||
plugin to communicate over netlink sockets it needs the telegraf process to be
|
||||
running as `root` (or some user with `CAP_NET_ADMIN` and `CAP_NET_RAW`). Be sure
|
||||
to ensure these permissions before running telegraf with this plugin included.
|
||||
|
||||
## Sample Output
|
||||
|
||||
This is what you can expect the emitted metrics to look like
|
||||
|
||||
```
|
||||
ipvs_virtual_server,address=172.18.64.234,address_family=inet,netmask=32,port=9000,protocol=tcp,sched=mh_418 bytes_out=0i,pps_in=0i,pps_out=0i,cps=0i,pkts_in=0i,pkts_out=0i,connections=0i,bytes_in=0i 1540407540000000000
|
||||
ipvs_virtual_server,address_family=inet,fwmark=47,netmask=32,sched=mh_418 connections=0i,pkts_in=0i,bytes_out=0i,pps_in=0i,pps_out=0i,pkts_out=0i,bytes_in=0i,cps=0i 1540407540000000000
|
||||
```
|
||||
112
plugins/inputs/ipvs/ipvs.go
Normal file
112
plugins/inputs/ipvs/ipvs.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// +build linux
|
||||
|
||||
package ipvs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/libnetwork/ipvs"
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
// IPVS holds the state for this input plugin
|
||||
type IPVS struct {
|
||||
handle *ipvs.Handle
|
||||
}
|
||||
|
||||
// Description returns a description string
|
||||
func (i *IPVS) Description() string {
|
||||
return "Collect virtual and real server stats from Linux IPVS"
|
||||
}
|
||||
|
||||
// SampleConfig returns a sample configuration for this input plugin
|
||||
func (i *IPVS) SampleConfig() string {
|
||||
return ``
|
||||
}
|
||||
|
||||
// Gather gathers the stats
|
||||
func (i *IPVS) Gather(acc telegraf.Accumulator) error {
|
||||
if i.handle == nil {
|
||||
h, err := ipvs.New("") // TODO: make the namespace configurable
|
||||
if err != nil {
|
||||
return errors.New("Unable to open IPVS handle")
|
||||
}
|
||||
i.handle = h
|
||||
}
|
||||
|
||||
services, err := i.handle.GetServices()
|
||||
if err != nil {
|
||||
i.handle.Close()
|
||||
i.handle = nil // trigger a reopen on next call to gather
|
||||
return errors.New("Failed to list IPVS services")
|
||||
}
|
||||
for _, s := range services {
|
||||
fields := map[string]interface{}{
|
||||
"connections": s.Stats.Connections,
|
||||
"pkts_in": s.Stats.PacketsIn,
|
||||
"pkts_out": s.Stats.PacketsOut,
|
||||
"bytes_in": s.Stats.BytesIn,
|
||||
"bytes_out": s.Stats.BytesOut,
|
||||
"pps_in": s.Stats.PPSIn,
|
||||
"pps_out": s.Stats.PPSOut,
|
||||
"cps": s.Stats.CPS,
|
||||
}
|
||||
acc.AddGauge("ipvs_virtual_server", fields, serviceTags(s))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// helper: given a Service, return tags that identify it
|
||||
func serviceTags(s *ipvs.Service) map[string]string {
|
||||
ret := map[string]string{
|
||||
"sched": s.SchedName,
|
||||
"netmask": fmt.Sprintf("%d", bits.OnesCount32(s.Netmask)),
|
||||
"address_family": addressFamilyToString(s.AddressFamily),
|
||||
}
|
||||
// Per the ipvsadm man page, a virtual service is defined "based on
|
||||
// protocol/addr/port or firewall mark"
|
||||
if s.FWMark > 0 {
|
||||
ret["fwmark"] = strconv.Itoa(int(s.FWMark))
|
||||
} else {
|
||||
ret["protocol"] = protocolToString(s.Protocol)
|
||||
ret["address"] = s.Address.String()
|
||||
ret["port"] = strconv.Itoa(int(s.Port))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// helper: convert protocol uint16 to human readable string (if possible)
|
||||
func protocolToString(p uint16) string {
|
||||
switch p {
|
||||
case syscall.IPPROTO_TCP:
|
||||
return "tcp"
|
||||
case syscall.IPPROTO_UDP:
|
||||
return "udp"
|
||||
case syscall.IPPROTO_SCTP:
|
||||
return "sctp"
|
||||
default:
|
||||
return fmt.Sprintf("%d", p)
|
||||
}
|
||||
}
|
||||
|
||||
// helper: convert addressFamily to a human readable string
|
||||
func addressFamilyToString(af uint16) string {
|
||||
switch af {
|
||||
case syscall.AF_INET:
|
||||
return "inet"
|
||||
case syscall.AF_INET6:
|
||||
return "inet6"
|
||||
default:
|
||||
return fmt.Sprintf("%d", af)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("ipvs", func() telegraf.Input { return &IPVS{} })
|
||||
}
|
||||
3
plugins/inputs/ipvs/ipvs_notlinux.go
Normal file
3
plugins/inputs/ipvs/ipvs_notlinux.go
Normal file
@@ -0,0 +1,3 @@
|
||||
// +build !linux
|
||||
|
||||
package ipvs
|
||||
Reference in New Issue
Block a user