mirror of
				https://github.com/prometheus/node_exporter.git
				synced 2025-08-20 18:33:52 -07:00 
			
		
		
		
	Make wifi collector fail gracefully if metrics not available
This commit is contained in:
		
							parent
							
								
									2884181cce
								
							
						
					
					
						commit
						d3089f2ce8
					
				|  | @ -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