Add initial wifi collector, bump netlink to fix 32-bit builds
This commit is contained in:
parent
8ee7f5bad2
commit
efa25665ec
9 changed files with 337 additions and 7 deletions
281
collector/wifi_linux.go
Normal file
281
collector/wifi_linux.go
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
// Copyright 2017 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package collector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mdlayher/wifi"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type wifiCollector struct {
|
||||
InterfaceFrequencyHertz *prometheus.Desc
|
||||
|
||||
StationConnectedSecondsTotal *prometheus.Desc
|
||||
StationInactiveSeconds *prometheus.Desc
|
||||
StationReceiveBitsPerSecond *prometheus.Desc
|
||||
StationTransmitBitsPerSecond *prometheus.Desc
|
||||
StationSignalDBM *prometheus.Desc
|
||||
StationTransmitRetriesTotal *prometheus.Desc
|
||||
StationTransmitFailedTotal *prometheus.Desc
|
||||
StationBeaconLossTotal *prometheus.Desc
|
||||
|
||||
stat wifiStater
|
||||
}
|
||||
|
||||
var (
|
||||
collectorWifi = flag.String("collector.wifi", "", "test fixtures to use for wifi collector metrics")
|
||||
)
|
||||
|
||||
func init() {
|
||||
Factories["wifi"] = NewWifiCollector
|
||||
}
|
||||
|
||||
var _ wifiStater = &wifi.Client{}
|
||||
|
||||
// wifiStater is an interface used to swap out a *wifi.Client for end to end tests.
|
||||
type wifiStater interface {
|
||||
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"
|
||||
)
|
||||
|
||||
var (
|
||||
labels = []string{"device"}
|
||||
)
|
||||
|
||||
return &wifiCollector{
|
||||
InterfaceFrequencyHertz: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "interface_frequency_hertz"),
|
||||
"The current frequency a WiFi interface is operating at, in hertz.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
StationConnectedSecondsTotal: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "station_connected_seconds_total"),
|
||||
"The total number of seconds a station has been connected to an access point.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
StationInactiveSeconds: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "station_inactive_seconds"),
|
||||
"The number of seconds since any wireless activity has occurred on a station.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
StationReceiveBitsPerSecond: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "station_receive_bits_per_second"),
|
||||
"The current WiFi receive bitrate of a station, in bits per second.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
StationTransmitBitsPerSecond: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "station_transmit_bits_per_second"),
|
||||
"The current WiFi transmit bitrate of a station, in bits per second.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
StationSignalDBM: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "station_signal_dbm"),
|
||||
"The current WiFi signal strength, in decibel-milliwatts (dBm).",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
StationTransmitRetriesTotal: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "station_transmit_retries_total"),
|
||||
"The total number of times a station has had to retry while sending a packet.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
StationTransmitFailedTotal: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "station_transmit_failed_total"),
|
||||
"The total number of times a station has failed to send a packet.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
StationBeaconLossTotal: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(Namespace, subsystem, "station_beacon_loss_total"),
|
||||
"The total number of times a station has detected a beacon loss.",
|
||||
labels,
|
||||
nil,
|
||||
),
|
||||
|
||||
stat: stat,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *wifiCollector) Update(ch chan<- prometheus.Metric) error {
|
||||
ifis, err := c.stat.Interfaces()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve wifi interfaces: %v", err)
|
||||
}
|
||||
|
||||
for _, ifi := range ifis {
|
||||
// Only collect metrics on stations for now
|
||||
if ifi.Type != wifi.InterfaceTypeStation {
|
||||
continue
|
||||
}
|
||||
|
||||
info, err := c.stat.StationInfo(ifi)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve station info for device %s: %v",
|
||||
ifi.Name, err)
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.InterfaceFrequencyHertz,
|
||||
prometheus.GaugeValue,
|
||||
mHzToHz(ifi.Frequency),
|
||||
ifi.Name,
|
||||
)
|
||||
|
||||
c.updateStationStats(ch, ifi.Name, info)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wifiCollector) updateStationStats(ch chan<- prometheus.Metric, device string, info *wifi.StationInfo) {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StationConnectedSecondsTotal,
|
||||
prometheus.CounterValue,
|
||||
info.Connected.Seconds(),
|
||||
device,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StationInactiveSeconds,
|
||||
prometheus.GaugeValue,
|
||||
info.Inactive.Seconds(),
|
||||
device,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StationReceiveBitsPerSecond,
|
||||
prometheus.GaugeValue,
|
||||
float64(info.ReceiveBitrate),
|
||||
device,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StationTransmitBitsPerSecond,
|
||||
prometheus.GaugeValue,
|
||||
float64(info.TransmitBitrate),
|
||||
device,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StationSignalDBM,
|
||||
prometheus.GaugeValue,
|
||||
float64(info.Signal),
|
||||
device,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StationTransmitRetriesTotal,
|
||||
prometheus.CounterValue,
|
||||
float64(info.TransmitRetries),
|
||||
device,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StationTransmitFailedTotal,
|
||||
prometheus.CounterValue,
|
||||
float64(info.TransmitFailed),
|
||||
device,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StationBeaconLossTotal,
|
||||
prometheus.CounterValue,
|
||||
float64(info.BeaconLoss),
|
||||
device,
|
||||
)
|
||||
}
|
||||
|
||||
func mHzToHz(mHz int) float64 {
|
||||
return float64(mHz) * 1000 * 1000
|
||||
}
|
||||
|
||||
// All code below this point is used to assist with end-to-end tests for
|
||||
// the wifi collector, since wifi devices are not available in CI.
|
||||
|
||||
// newWifiStater determines if mocked test fixtures from files should be used for
|
||||
// collecting wifi metrics, or if package wifi should be used.
|
||||
func newWifiStater(fixtures string) (wifiStater, error) {
|
||||
if fixtures != "" {
|
||||
return &mockWifiStater{
|
||||
fixtures: fixtures,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return wifi.New()
|
||||
}
|
||||
|
||||
var _ wifiStater = &mockWifiStater{}
|
||||
|
||||
type mockWifiStater struct {
|
||||
fixtures string
|
||||
}
|
||||
|
||||
func (s *mockWifiStater) unmarshalJSONFile(filename string, v interface{}) error {
|
||||
b, err := ioutil.ReadFile(filepath.Join(s.fixtures, filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(b, v)
|
||||
}
|
||||
|
||||
func (s *mockWifiStater) Interfaces() ([]*wifi.Interface, error) {
|
||||
var ifis []*wifi.Interface
|
||||
if err := s.unmarshalJSONFile("interfaces.json", &ifis); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ifis, nil
|
||||
}
|
||||
|
||||
func (s *mockWifiStater) StationInfo(ifi *wifi.Interface) (*wifi.StationInfo, error) {
|
||||
p := filepath.Join(ifi.Name, "stationinfo.json")
|
||||
|
||||
var info wifi.StationInfo
|
||||
if err := s.unmarshalJSONFile(p, &info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &info, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue