mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-26 22:19:44 -08:00
Merge pull request #418 from mdlayher/wifi-graceful-fail
Make wifi collector fail gracefully if metrics not available
This commit is contained in:
commit
276112c7ef
|
@ -18,10 +18,12 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mdlayher/wifi"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/log"
|
||||
)
|
||||
|
||||
type wifiCollector struct {
|
||||
|
@ -35,8 +37,6 @@ type wifiCollector struct {
|
|||
StationTransmitRetriesTotal *prometheus.Desc
|
||||
StationTransmitFailedTotal *prometheus.Desc
|
||||
StationBeaconLossTotal *prometheus.Desc
|
||||
|
||||
stat wifiStater
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -51,16 +51,12 @@ var _ wifiStater = &wifi.Client{}
|
|||
|
||||
// wifiStater is an interface used to swap out a *wifi.Client for end to end tests.
|
||||
type wifiStater interface {
|
||||
Close() error
|
||||
Interfaces() ([]*wifi.Interface, error)
|
||||
StationInfo(ifi *wifi.Interface) (*wifi.StationInfo, error)
|
||||
}
|
||||
|
||||
func NewWifiCollector() (Collector, error) {
|
||||
stat, err := newWifiStater(*collectorWifi)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to access wifi data: %v", err)
|
||||
}
|
||||
|
||||
const (
|
||||
subsystem = "wifi"
|
||||
)
|
||||
|
@ -132,13 +128,23 @@ func NewWifiCollector() (Collector, error) {
|
|||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
stat: stat,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error {
|
||||
ifis, err := c.stat.Interfaces()
|
||||
stat, err := newWifiStater(*collectorWifi)
|
||||
if err != nil {
|
||||
// Cannot access wifi metrics, report no error
|
||||
if os.IsNotExist(err) {
|
||||
log.Debug("wifi collector metrics are not available for this system")
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to access wifi data: %v", err)
|
||||
}
|
||||
defer stat.Close()
|
||||
|
||||
ifis, err := stat.Interfaces()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve wifi interfaces: %v", err)
|
||||
}
|
||||
|
@ -149,7 +155,7 @@ func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error {
|
|||
continue
|
||||
}
|
||||
|
||||
info, err := c.stat.StationInfo(ifi)
|
||||
info, err := stat.StationInfo(ifi)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve station info for device %s: %v",
|
||||
ifi.Name, err)
|
||||
|
@ -260,6 +266,8 @@ func (s *mockWifiStater) unmarshalJSONFile(filename string, v interface{}) error
|
|||
return json.Unmarshal(b, v)
|
||||
}
|
||||
|
||||
func (s *mockWifiStater) Close() error { return nil }
|
||||
|
||||
func (s *mockWifiStater) Interfaces() ([]*wifi.Interface, error) {
|
||||
var ifis []*wifi.Interface
|
||||
if err := s.unmarshalJSONFile("interfaces.json", &ifis); err != nil {
|
||||
|
|
47
vendor/github.com/mdlayher/netlink/conn.go
generated
vendored
47
vendor/github.com/mdlayher/netlink/conn.go
generated
vendored
|
@ -2,7 +2,7 @@ package netlink
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
|
@ -23,6 +23,12 @@ type Conn struct {
|
|||
// seq is an atomically incremented integer used to provide sequence
|
||||
// numbers when Conn.Send is called.
|
||||
seq *uint32
|
||||
|
||||
// pid is an atomically set/loaded integer which is set to the PID assigned
|
||||
// by netlink, when netlink sends its first response message. pidOnce performs
|
||||
// the assignment exactl once.
|
||||
pid *uint32
|
||||
pidOnce sync.Once
|
||||
}
|
||||
|
||||
// An osConn is an operating-system specific implementation of netlink
|
||||
|
@ -53,6 +59,7 @@ func newConn(c osConn) *Conn {
|
|||
return &Conn{
|
||||
c: c,
|
||||
seq: new(uint32),
|
||||
pid: new(uint32),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,8 +103,8 @@ func (c *Conn) Execute(m Message) ([]Message, error) {
|
|||
// If m.Header.Sequence is 0, it will be automatically populated using the
|
||||
// next sequence number for this connection.
|
||||
//
|
||||
// If m.Header.PID is 0, it will be automatically populated using the
|
||||
// process ID (PID) of this process.
|
||||
// If m.Header.PID is 0, it will be automatically populated using a PID
|
||||
// assigned by netlink.
|
||||
func (c *Conn) Send(m Message) (Message, error) {
|
||||
ml := nlmsgLength(len(m.Data))
|
||||
|
||||
|
@ -115,7 +122,7 @@ func (c *Conn) Send(m Message) (Message, error) {
|
|||
}
|
||||
|
||||
if m.Header.PID == 0 {
|
||||
m.Header.PID = uint32(os.Getpid())
|
||||
m.Header.PID = atomic.LoadUint32(c.pid)
|
||||
}
|
||||
|
||||
if err := c.c.Send(m); err != nil {
|
||||
|
@ -127,14 +134,29 @@ func (c *Conn) Send(m Message) (Message, error) {
|
|||
|
||||
// Receive receives one or more messages from netlink. Multi-part messages are
|
||||
// handled transparently and returned as a single slice of Messages, with the
|
||||
// final empty "multi-part done" message removed. If any of the messages
|
||||
// indicate a netlink error, that error will be returned.
|
||||
// final empty "multi-part done" message removed.
|
||||
//
|
||||
// If a PID has not yet been assigned to this Conn by netlink, the PID will
|
||||
// be set from the first received message. This PID will be used in all
|
||||
// subsequent communications with netlink.
|
||||
//
|
||||
// If any of the messages indicate a netlink error, that error will be returned.
|
||||
func (c *Conn) Receive() ([]Message, error) {
|
||||
msgs, err := c.receive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(msgs) > 0 {
|
||||
// netlink multicast messages from kernel have PID of 0, so don't
|
||||
// assign 0 as the expected PID for next messages
|
||||
if pid := msgs[0].Header.PID; pid != 0 {
|
||||
c.pidOnce.Do(func() {
|
||||
atomic.StoreUint32(c.pid, pid)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Trim the final message with multi-part done indicator if
|
||||
// present
|
||||
if m := msgs[len(msgs)-1]; m.Header.Flags&HeaderFlagsMulti != 0 && m.Header.Type == HeaderTypeDone {
|
||||
|
@ -200,10 +222,19 @@ func (c *Conn) nextSequence() uint32 {
|
|||
// ensuring that they contain matching sequence numbers and PIDs.
|
||||
func Validate(request Message, replies []Message) error {
|
||||
for _, m := range replies {
|
||||
if m.Header.Sequence != request.Header.Sequence {
|
||||
// Check for mismatched sequence, unless:
|
||||
// - request had no sequence, meaning we are probably validating
|
||||
// a multicast reply
|
||||
if m.Header.Sequence != request.Header.Sequence && request.Header.Sequence != 0 {
|
||||
return errMismatchedSequence
|
||||
}
|
||||
if m.Header.PID != request.Header.PID {
|
||||
|
||||
// Check for mismatched PID, unless:
|
||||
// - request had no PID, meaning we are either:
|
||||
// - validating a multicast reply
|
||||
// - netlink has not yet assigned us a PID
|
||||
// - response had no PID, meaning it's from the kernel as a multicast reply
|
||||
if m.Header.PID != request.Header.PID && request.Header.PID != 0 && m.Header.PID != 0 {
|
||||
return errMismatchedPID
|
||||
}
|
||||
}
|
||||
|
|
20
vendor/github.com/mdlayher/wifi/client.go
generated
vendored
20
vendor/github.com/mdlayher/wifi/client.go
generated
vendored
|
@ -1,5 +1,15 @@
|
|||
package wifi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// errNotStation is returned when attempting to query station info for
|
||||
// an interface which is not a station.
|
||||
errNotStation = errors.New("interface is not a station")
|
||||
)
|
||||
|
||||
// A Client is a type which can access WiFi device actions and statistics
|
||||
// using operating system-specific operations.
|
||||
type Client struct {
|
||||
|
@ -18,6 +28,11 @@ func New() (*Client, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Close releases resources used by a Client.
|
||||
func (c *Client) Close() error {
|
||||
return c.c.Close()
|
||||
}
|
||||
|
||||
// Interfaces returns a list of the system's WiFi network interfaces.
|
||||
func (c *Client) Interfaces() ([]*Interface, error) {
|
||||
return c.c.Interfaces()
|
||||
|
@ -26,11 +41,16 @@ func (c *Client) Interfaces() ([]*Interface, error) {
|
|||
// StationInfo retrieves statistics about a WiFi interface operating in
|
||||
// station mode.
|
||||
func (c *Client) StationInfo(ifi *Interface) (*StationInfo, error) {
|
||||
if ifi.Type != InterfaceTypeStation {
|
||||
return nil, errNotStation
|
||||
}
|
||||
|
||||
return c.c.StationInfo(ifi)
|
||||
}
|
||||
|
||||
// An osClient is the operating system-specific implementation of Client.
|
||||
type osClient interface {
|
||||
Close() error
|
||||
Interfaces() ([]*Interface, error)
|
||||
StationInfo(ifi *Interface) (*StationInfo, error)
|
||||
}
|
||||
|
|
13
vendor/github.com/mdlayher/wifi/client_linux.go
generated
vendored
13
vendor/github.com/mdlayher/wifi/client_linux.go
generated
vendored
|
@ -36,6 +36,7 @@ type client struct {
|
|||
// genl is an interface over generic netlink, so netlink interactions can
|
||||
// be stubbed in tests.
|
||||
type genl interface {
|
||||
Close() error
|
||||
GetFamily(name string) (genetlink.Family, error)
|
||||
Execute(m genetlink.Message, family uint16, flags netlink.HeaderFlags) ([]genetlink.Message, error)
|
||||
}
|
||||
|
@ -66,6 +67,11 @@ func initClient(c genl) (*client, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the client's generic netlink connection.
|
||||
func (c *client) Close() error {
|
||||
return c.c.Close()
|
||||
}
|
||||
|
||||
// Interfaces requests that nl80211 return a list of all WiFi interfaces present
|
||||
// on this system.
|
||||
func (c *client) Interfaces() ([]*Interface, error) {
|
||||
|
@ -117,7 +123,12 @@ func (c *client) StationInfo(ifi *Interface) (*StationInfo, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if len(msgs) > 1 {
|
||||
switch len(msgs) {
|
||||
case 0:
|
||||
return nil, os.ErrNotExist
|
||||
case 1:
|
||||
break
|
||||
default:
|
||||
return nil, errMultipleMessages
|
||||
}
|
||||
|
||||
|
|
5
vendor/github.com/mdlayher/wifi/client_others.go
generated
vendored
5
vendor/github.com/mdlayher/wifi/client_others.go
generated
vendored
|
@ -24,6 +24,11 @@ func newClient() (*client, error) {
|
|||
return nil, errUnimplemented
|
||||
}
|
||||
|
||||
// Close always returns an error.
|
||||
func (c *client) Close() error {
|
||||
return errUnimplemented
|
||||
}
|
||||
|
||||
// Interfaces always returns an error.
|
||||
func (c *client) Interfaces() ([]*Interface, error) {
|
||||
return nil, errUnimplemented
|
||||
|
|
12
vendor/vendor.json
vendored
12
vendor/vendor.json
vendored
|
@ -51,10 +51,10 @@
|
|||
"revisionTime": "2016-04-24T11:30:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "87nUxyFGVJFXB6MQpGCGUHi5NY0=",
|
||||
"checksumSHA1": "n31d2o+dY0HXZTDWE5Rc0+7NEjc=",
|
||||
"path": "github.com/mdlayher/netlink",
|
||||
"revision": "a65cbc3bb3f7a793b7d79ad7d19b16d471ddbd78",
|
||||
"revisionTime": "2017-01-10T22:29:47Z"
|
||||
"revision": "e5da4eb480835e5bce0dc5e526fe5f9a8002b54e",
|
||||
"revisionTime": "2017-01-13T17:56:52Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "+2roeIWCAjCC58tZcs12Vqgf1Io=",
|
||||
|
@ -69,10 +69,10 @@
|
|||
"revisionTime": "2017-01-04T04:59:06Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "dSy4F6HRYyadhiQbv3Bof/Tdxec=",
|
||||
"checksumSHA1": "l8M/rZH5s/ZVtCCyeiUQXZ5FosA=",
|
||||
"path": "github.com/mdlayher/wifi",
|
||||
"revision": "88fd1c0ec178645c1b7d300090b5a9d4b226b8e1",
|
||||
"revisionTime": "2017-01-07T15:17:58Z"
|
||||
"revision": "eb8b29b956ba5ff2fdd2d2f1f0b988b57fd3d8a3",
|
||||
"revisionTime": "2017-01-12T20:47:29Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "VzutdH69PUqRqhrDVv6F91ebQd4=",
|
||||
|
|
Loading…
Reference in a new issue