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
181
plugins/inputs/ethtool/namespace_linux.go
Normal file
181
plugins/inputs/ethtool/namespace_linux.go
Normal file
|
@ -0,0 +1,181 @@
|
|||
//go:build linux
|
||||
|
||||
package ethtool
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net"
|
||||
"runtime"
|
||||
|
||||
"github.com/safchain/ethtool"
|
||||
"github.com/vishvananda/netns"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
)
|
||||
|
||||
type namespace interface {
|
||||
name() string
|
||||
interfaces() ([]namespacedInterface, error)
|
||||
driverName(intf namespacedInterface) (string, error)
|
||||
stats(intf namespacedInterface) (map[string]uint64, error)
|
||||
get(intf namespacedInterface) (map[string]uint64, error)
|
||||
}
|
||||
|
||||
type namespacedInterface struct {
|
||||
net.Interface
|
||||
namespace namespace
|
||||
}
|
||||
|
||||
type namespacedAction struct {
|
||||
result chan<- namespacedResult
|
||||
f func(*namespaceGoroutine) (interface{}, error)
|
||||
}
|
||||
|
||||
type namespacedResult struct {
|
||||
result interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
type namespaceGoroutine struct {
|
||||
namespaceName string
|
||||
handle netns.NsHandle
|
||||
ethtoolClient *ethtool.Ethtool
|
||||
c chan namespacedAction
|
||||
log telegraf.Logger
|
||||
}
|
||||
|
||||
func (n *namespaceGoroutine) name() string {
|
||||
return n.namespaceName
|
||||
}
|
||||
|
||||
func (n *namespaceGoroutine) interfaces() ([]namespacedInterface, error) {
|
||||
interfaces, err := n.do(func(n *namespaceGoroutine) (interface{}, error) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
namespacedInterfaces := make([]namespacedInterface, 0, len(interfaces))
|
||||
for _, iface := range interfaces {
|
||||
namespacedInterfaces = append(
|
||||
namespacedInterfaces,
|
||||
namespacedInterface{
|
||||
Interface: iface,
|
||||
namespace: n,
|
||||
},
|
||||
)
|
||||
}
|
||||
return namespacedInterfaces, nil
|
||||
})
|
||||
|
||||
return interfaces.([]namespacedInterface), err
|
||||
}
|
||||
|
||||
func (n *namespaceGoroutine) driverName(intf namespacedInterface) (string, error) {
|
||||
driver, err := n.do(func(n *namespaceGoroutine) (interface{}, error) {
|
||||
return n.ethtoolClient.DriverName(intf.Name)
|
||||
})
|
||||
return driver.(string), err
|
||||
}
|
||||
|
||||
func (n *namespaceGoroutine) stats(intf namespacedInterface) (map[string]uint64, error) {
|
||||
driver, err := n.do(func(n *namespaceGoroutine) (interface{}, error) {
|
||||
return n.ethtoolClient.Stats(intf.Name)
|
||||
})
|
||||
return driver.(map[string]uint64), err
|
||||
}
|
||||
|
||||
func (n *namespaceGoroutine) get(intf namespacedInterface) (map[string]uint64, error) {
|
||||
result, err := n.do(func(n *namespaceGoroutine) (interface{}, error) {
|
||||
ecmd := ethtool.EthtoolCmd{}
|
||||
speed32, err := n.ethtoolClient.CmdGet(&ecmd, intf.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var speed = uint64(speed32)
|
||||
if speed == math.MaxUint32 {
|
||||
speed = math.MaxUint64
|
||||
}
|
||||
|
||||
var link32 uint32
|
||||
link32, err = n.ethtoolClient.LinkState(intf.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]uint64{
|
||||
"speed": speed,
|
||||
"duplex": uint64(ecmd.Duplex),
|
||||
"autoneg": uint64(ecmd.Autoneg),
|
||||
"link": uint64(link32),
|
||||
}, nil
|
||||
})
|
||||
|
||||
if result != nil {
|
||||
return result.(map[string]uint64), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// start locks a goroutine to an OS thread and ties it to the namespace, then
|
||||
// loops for actions to run in the namespace.
|
||||
func (n *namespaceGoroutine) start() error {
|
||||
n.c = make(chan namespacedAction)
|
||||
started := make(chan error)
|
||||
go func() {
|
||||
// We're going to hold this thread locked permanently. We're going to
|
||||
// do this for every namespace. This makes it very likely that the Go
|
||||
// runtime will spin up new threads to replace it. To avoid thread
|
||||
// leaks, we don't unlock when we're done and instead let this thread
|
||||
// die.
|
||||
runtime.LockOSThread()
|
||||
|
||||
// If this goroutine is for the initial namespace, we are already in
|
||||
// the correct namespace. Switching would require CAP_SYS_ADMIN, which
|
||||
// we may not have. Don't switch if the desired namespace matches the
|
||||
// current one.
|
||||
initialNamespace, err := netns.Get()
|
||||
if err != nil {
|
||||
n.log.Errorf("Could not get initial namespace: %s", err)
|
||||
started <- err
|
||||
return
|
||||
}
|
||||
if !initialNamespace.Equal(n.handle) {
|
||||
if err := netns.Set(n.handle); err != nil {
|
||||
n.log.Errorf("Could not switch to namespace %q: %s", n.namespaceName, err.Error())
|
||||
started <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Every namespace needs its own connection to ethtool
|
||||
e, err := ethtool.NewEthtool()
|
||||
if err != nil {
|
||||
n.log.Errorf("Could not create ethtool client for namespace %q: %s", n.namespaceName, err.Error())
|
||||
started <- err
|
||||
return
|
||||
}
|
||||
n.ethtoolClient = e
|
||||
started <- nil
|
||||
for command := range n.c {
|
||||
result, err := command.f(n)
|
||||
command.result <- namespacedResult{
|
||||
result: result,
|
||||
err: err,
|
||||
}
|
||||
close(command.result)
|
||||
}
|
||||
}()
|
||||
return <-started
|
||||
}
|
||||
|
||||
// do runs a function inside the OS thread tied to the namespace.
|
||||
func (n *namespaceGoroutine) do(f func(*namespaceGoroutine) (interface{}, error)) (interface{}, error) {
|
||||
result := make(chan namespacedResult)
|
||||
n.c <- namespacedAction{
|
||||
result: result,
|
||||
f: f,
|
||||
}
|
||||
r := <-result
|
||||
return r.result, r.err
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue