Skip to content

Commit bcce1fd

Browse files
committed
Support wireless metrics for wifiwave2 devices (nshttpd#155)
from https://forum.mikrotik.com/viewtopic.php?t=195124#p999722: wifiwave2 is an implementation of drivers from the manufacturer of the chipset, rather than an in-house written driver (which wireless is). So there are many small details that are missing or incomplete... wifiwave2 has a slightly different API and has fewer properties to query. This means that not all metrics are available for wifiwave2 devices. Also, the implementation aims at keeping the name metric names for wifiwave2 devices to ensure that existing dashboard continue to work without any modification.
1 parent e1b06c6 commit bcce1fd

File tree

4 files changed

+92
-19
lines changed

4 files changed

+92
-19
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ devices:
6565
port: 8999
6666
user: prometheus2
6767
password: password_to_second_router
68+
wifiwave2: true
6869
- name: routers_srv_dns
6970
srv:
7071
record: _mikrotik._udp.example.com
@@ -87,12 +88,18 @@ features:
8788
routes: true
8889
pools: true
8990
optics: true
91+
wlanif: true
92+
wlansta: true
9093
```
9194
9295
If you add a devices with the `srv` parameter instead of `address` the exporter will perform a DNS query
9396
to obtain the SRV record and discover the devices dynamically. Also, you can specify a DNS server to use
9497
on the query.
9598

99+
Use the option `wifiwave2: true` for devices that have the `wifiwave2` package,
100+
which replaces the `wireless` implementation, installed. This is necessary as `wifiwave2` has a slightly
101+
different API and exposes a slightly smaller set of attributes (for example, no signal-to-noise, etc.)
102+
96103

97104
###### example output
98105

collector/wlanif_collector.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ func (c *wlanIFCollector) collect(ctx *collectorContext) error {
5353
}
5454

5555
func (c *wlanIFCollector) fetchInterfaceNames(ctx *collectorContext) ([]string, error) {
56-
reply, err := ctx.client.Run("/interface/wireless/print", "?disabled=false", "=.proplist=name")
56+
cmd := ""
57+
if ctx.device.Wifiwave2 {
58+
cmd = "/interface/wifiwave/print"
59+
} else {
60+
cmd = "/interface/wireless/print"
61+
}
62+
reply, err := ctx.client.Run(cmd, "?disabled=false", "=.proplist=name")
5763
if err != nil {
5864
log.WithFields(log.Fields{
5965
"device": ctx.device.Name,
@@ -71,7 +77,13 @@ func (c *wlanIFCollector) fetchInterfaceNames(ctx *collectorContext) ([]string,
7177
}
7278

7379
func (c *wlanIFCollector) collectForInterface(iface string, ctx *collectorContext) error {
74-
reply, err := ctx.client.Run("/interface/wireless/monitor", fmt.Sprintf("=numbers=%s", iface), "=once=", "=.proplist="+strings.Join(c.props, ","))
80+
cmd := ""
81+
if ctx.device.Wifiwave2 {
82+
cmd = "/interface/wifiwave/monitor"
83+
} else {
84+
cmd = "/interface/wireless/monitor"
85+
}
86+
reply, err := ctx.client.Run(cmd, fmt.Sprintf("=numbers=%s", iface), "=once=", "=.proplist="+strings.Join(c.props, ","))
7587
if err != nil {
7688
log.WithFields(log.Fields{
7789
"interface": iface,

collector/wlansta_collector.go

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,20 @@ import (
99
"gopkg.in/routeros.v2/proto"
1010
)
1111

12+
// from https://forum.mikrotik.com/viewtopic.php?t=195124#p999722:
13+
// wifiwave2 is an implementation of drivers from the manufacturer of the
14+
// chipset, rather than an in-house written driver (which wireless is). So
15+
// there are many small details that are missing or incomplete...
16+
1217
type wlanSTACollector struct {
13-
props []string
14-
descriptions map[string]*prometheus.Desc
18+
// Both wifiwave2 and wireless have a similar, yet different API. They also
19+
// expose a slightly different set of properties.
20+
props []string
21+
propsWirelessExtra []string
22+
propsWirelessRXTX []string
23+
propsWifiwave2Extra []string
24+
propsWifiwave2RXTX []string
25+
descriptions map[string]*prometheus.Desc
1526
}
1627

1728
func newWlanSTACollector() routerOSCollector {
@@ -21,13 +32,28 @@ func newWlanSTACollector() routerOSCollector {
2132
}
2233

2334
func (c *wlanSTACollector) init() {
24-
c.props = []string{"interface", "mac-address", "signal-to-noise", "signal-strength", "packets", "bytes", "frames"}
35+
// common properties
36+
c.props = []string{"interface", "mac-address"}
37+
// wifiwave2 doesn't expose SNR, and uses different name for signal-strength
38+
c.propsWirelessExtra = []string{"signal-to-noise", "signal-strength"}
39+
// wireless exposes extra field "frames", not available in wifiwave2
40+
c.propsWirelessRXTX = []string{"packets", "bytes", "frames"}
41+
c.propsWifiwave2Extra = []string{"signal"}
42+
c.propsWifiwave2RXTX = []string{"packets", "bytes"}
43+
// all metrics have the same label names
2544
labelNames := []string{"name", "address", "interface", "mac_address"}
2645
c.descriptions = make(map[string]*prometheus.Desc)
27-
for _, p := range c.props[:len(c.props)-3] {
46+
for _, p := range c.propsWirelessExtra {
2847
c.descriptions[p] = descriptionForPropertyName("wlan_station", p, labelNames)
2948
}
30-
for _, p := range c.props[len(c.props)-3:] {
49+
// normalize the metric name 'signal-strength' for the property "signal", so that dashboards
50+
// that capture both wireless and wifiwave2 devices don't need to normalize
51+
c.descriptions["signal"] = descriptionForPropertyName("wlan_station", "signal-strength", labelNames)
52+
for _, p := range c.propsWirelessRXTX {
53+
c.descriptions["tx_"+p] = descriptionForPropertyName("wlan_station", "tx_"+p, labelNames)
54+
c.descriptions["rx_"+p] = descriptionForPropertyName("wlan_station", "rx_"+p, labelNames)
55+
}
56+
for _, p := range c.propsWifiwave2RXTX {
3157
c.descriptions["tx_"+p] = descriptionForPropertyName("wlan_station", "tx_"+p, labelNames)
3258
c.descriptions["rx_"+p] = descriptionForPropertyName("wlan_station", "rx_"+p, labelNames)
3359
}
@@ -53,7 +79,25 @@ func (c *wlanSTACollector) collect(ctx *collectorContext) error {
5379
}
5480

5581
func (c *wlanSTACollector) fetch(ctx *collectorContext) ([]*proto.Sentence, error) {
56-
reply, err := ctx.client.Run("/interface/wireless/registration-table/print", "=.proplist="+strings.Join(c.props, ","))
82+
var cmd []string
83+
var props []string = c.props
84+
if ctx.device.Wifiwave2 {
85+
props = append(props, c.propsWifiwave2Extra...)
86+
props = append(props, c.propsWifiwave2RXTX...)
87+
cmd = []string{
88+
"/interface/wifiwave2/registration-table/print",
89+
"=.proplist=" + strings.Join(props, ","),
90+
}
91+
} else {
92+
props = append(props, c.propsWirelessExtra...)
93+
props = append(props, c.propsWirelessRXTX...)
94+
cmd = []string{
95+
"/interface/wireless/registration-table/print",
96+
"=.proplist=" + strings.Join(props, ","),
97+
}
98+
}
99+
log.Debugf("Running collector command: %s", cmd)
100+
reply, err := ctx.client.Run(cmd...)
57101
if err != nil {
58102
log.WithFields(log.Fields{
59103
"device": ctx.device.Name,
@@ -69,11 +113,20 @@ func (c *wlanSTACollector) collectForStat(re *proto.Sentence, ctx *collectorCont
69113
iface := re.Map["interface"]
70114
mac := re.Map["mac-address"]
71115

72-
for _, p := range c.props[2 : len(c.props)-3] {
73-
c.collectMetricForProperty(p, iface, mac, re, ctx)
74-
}
75-
for _, p := range c.props[len(c.props)-3:] {
76-
c.collectMetricForTXRXCounters(p, iface, mac, re, ctx)
116+
if ctx.device.Wifiwave2 {
117+
for _, p := range c.propsWifiwave2Extra {
118+
c.collectMetricForProperty(p, iface, mac, re, ctx)
119+
}
120+
for _, p := range c.propsWifiwave2RXTX {
121+
c.collectMetricForTXRXCounters(p, iface, mac, re, ctx)
122+
}
123+
} else {
124+
for _, p := range c.propsWirelessExtra {
125+
c.collectMetricForProperty(p, iface, mac, re, ctx)
126+
}
127+
for _, p := range c.propsWirelessRXTX {
128+
c.collectMetricForTXRXCounters(p, iface, mac, re, ctx)
129+
}
77130
}
78131
}
79132

config/config.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ type Config struct {
3535

3636
// Device represents a target device
3737
type Device struct {
38-
Name string `yaml:"name"`
39-
Address string `yaml:"address,omitempty"`
40-
Srv SrvRecord `yaml:"srv,omitempty"`
41-
User string `yaml:"user"`
42-
Password string `yaml:"password"`
43-
Port string `yaml:"port"`
38+
Name string `yaml:"name"`
39+
Address string `yaml:"address,omitempty"`
40+
Srv SrvRecord `yaml:"srv,omitempty"`
41+
User string `yaml:"user"`
42+
Password string `yaml:"password"`
43+
Port string `yaml:"port"`
44+
Wifiwave2 bool `yaml:"wifiwave2"`
4445
}
4546

4647
type SrvRecord struct {

0 commit comments

Comments
 (0)