Skip to content

Commit

Permalink
fix client signal level bug, add client signal-to-noise ratio (#88)
Browse files Browse the repository at this point in the history
* go fmt (whitespace changes only)

* (bug) signal level is a percent, not dBm

* Add client signal to noise ratio

* fix missing client values

The HostName client field seems somewhat unreliable in practice. Fall back on the Name field if it is absent.

Maybe a fix for #22

* add an mdocs command to emit metrics docs

This should guarantee docs matching the code, and replace the defunct metric-markdown-table.sh script.

* refresh metrics descriptions

* replace metric-markdown-table.sh with mdocs command

Remaining buglet: the version and mdocs commands shouldn’t require host, username and password arguments.

* redefine client label, add host_name label

Formerly the client metrics `client` label was populated with the hostname provided by the controller. The hostname parameter is not reliably present.

This change populates the `client` label with the name value from the controller, and moves the hostname to a separate `hostname` label.

It is not 100% clear how Omada sets the name. A best guess is:

- If the name explicitly set a name in the Omada UI, that.
- If no name is explicitly set, and a hostname has been determined, use the hostname.
- If no name is explicitly set, and no hostname has been determined, us the mac address.
  • Loading branch information
jfieber authored Dec 18, 2023
1 parent 2fa9077 commit 681af44
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 69 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ lint:
golangci-lint run

generate-metrics-table:
sh ./scripts/metric-markdown-table.sh
go run main.go --host dummy --username dummy --password dummy mdocs > gen-metrics-table.md
47 changes: 28 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,23 +110,32 @@ omada:
```

## 📊 Metrics
Name|Description|Labels
| Name | Description | Labels |
|--|--|--|
omada_device_uptime_seconds | Uptime of the device. | device, model, version, ip, mac, site, site_id, device_type
omada_device_cpu_percentage | Percentage of device CPU used. | device, model, version, ip, mac, site, site_id, device_type
omada_device_mem_percentage | Percentage of device Memory used. | device, model, version, ip, mac, site, site_id, device_type
omada_device_need_upgrade | A boolean on whether the device needs an upgrade. | device, model, version, ip, mac, site, site_id, device_type
omada_device_tx_rate | The tx rate of the device. | device, model, version, ip, mac, site, site_id, device_type
omada_device_rx_rate | The rx rate of the device. | device, model, version, ip, mac, site, site_id, device_type
omada_device_poe_remain_watts | The remaining amount of PoE power for the device in watts. | device, model, version, ip, mac, site, site_id, device_type
omada_client_download_activity_bytes | The current download activity for the client in bytes. | client, vendor, switch_port, vlan_id, ip, mac, site, site_id, ap_name, ssid, wifi_mode
omada_client_signal_dbm | The signal level for the wireless client in dBm. | client, vendor, ip, mac, ap_name, site, site_id, ssid, wifi_mode
omada_port_power_watts | The current PoE usage of the port in watts. | device, device_mac, client, vendor, switch_port, name, switch_mac, switch_id, vlan_id, profile, site, site_id
omada_port_link_status | A boolean representing the link status of the port. | device, device_mac, client, vendor, switch_port, name, switch_mac, switch_id, vlan_id, profile, site, site_id
omada_port_link_speed_mbps | Port link speed in mbps. This is the capability of the connection, not the active throughput. | device, device_mac, client, vendor, switch_port, name, switch_mac, switch_id, vlan_id, profile, site, site_id
omada_port_link_rx | Bytes recieved on a port. | device, device_mac, client, vendor, switch_port, name, switch_mac, switch_id, vlan_id, profile, site, site_id
omada_port_link_tx | Bytes transmitted on a port. | device, device_mac, client, vendor, switch_port, name, switch_mac, switch_id, vlan_id, profile, site, site_id
omada_controller_uptime_seconds | Uptime of the controller. | controller_name, model, controller_version, firmware_version, mac
omada_controller_storage_used_bytes | Storage used on the controller. | storage_name, controller_name, model, controller_version, firmware_version, mac
omada_controller_storage_available_bytes | Total storage available for the controller. | storage_name, controller_name, model, controller_version, firmware_version, mac
omada_client_connected_total | Total number of connected clients. | site, site_id
| omada_client_download_activity_bytes | The current download activity for the client in bytes. | client vendor ip mac host_name site site_id connection_mode wifi_mode ap_name ssid switch_port vlan_id |
| omada_client_signal_pct | The signal quality for the wireless client in percent. | client vendor ip mac host_name site site_id connection_mode wifi_mode ap_name ssid |
| omada_client_snr_dbm | The signal to noise ration for the wireless client in dBm. | client vendor ip mac host_name site site_id connection_mode wifi_mode ap_name ssid |
| omada_client_rssi_dbm | The RSSI for the wireless client in dBm. | client vendor ip mac host_name site site_id connection_mode wifi_mode ap_name ssid |
| omada_client_traffic_down_bytes | Total bytes received by wireless client. | client vendor ip mac host_name site site_id connection_mode wifi_mode ap_name ssid |
| omada_client_traffic_up_bytes | Total bytes sent by wireless client. | client vendor ip mac host_name site site_id connection_mode wifi_mode ap_name ssid |
| omada_client_tx_rate | TX rate of wireless client. | client vendor ip mac host_name site site_id connection_mode wifi_mode ap_name ssid |
| omada_client_rx_rate | RX rate of wireless client. | client vendor ip mac host_name site site_id connection_mode wifi_mode ap_name ssid |
| omada_client_connected_total | Total number of connected clients. | site site_id connection_mode wifi_mode |
| omada_controller_uptime_seconds | Uptime of the controller. | controller_name model controller_version firmware_version mac site site_id |
| omada_controller_storage_used_bytes | Storage used on the controller. | storage_name controller_name model controller_version firmware_version mac site site_id |
| omada_controller_storage_available_bytes | Total storage available for the controller. | storage_name controller_name model controller_version firmware_version mac site site_id |
| omada_device_uptime_seconds | Uptime of the device. | device model version ip mac site site_id device_type |
| omada_device_uptime_seconds | Uptime of the device. | device model version ip mac site site_id device_type |
| omada_device_cpu_percentage | Percentage of device CPU used. | device model version ip mac site site_id device_type |
| omada_device_mem_percentage | Percentage of device Memory used. | device model version ip mac site site_id device_type |
| omada_device_need_upgrade | A boolean on whether the device needs an upgrade. | device model version ip mac site site_id device_type |
| omada_device_tx_rate | The tx rate of the device. | device model version ip mac site site_id device_type |
| omada_device_rx_rate | The rx rate of the device. | device model version ip mac site site_id device_type |
| omada_device_poe_remain_watts | The remaining amount of PoE power for the device in watts. | device model version ip mac site site_id device_type |
| omada_device_download | Device download traffic. | device model version ip mac site site_id device_type |
| omada_device_upload | Device upload traffic. | device model version ip mac site site_id device_type |
| omada_port_power_watts | The current PoE usage of the port in watts. | device device_mac client vendor switch_port name switch_mac switch_id vlan_id profile site site_id |
| omada_port_link_status | A boolean representing the link status of the port. | device device_mac client vendor switch_port name switch_mac switch_id vlan_id profile site site_id |
| omada_port_link_speed_mbps | Port link speed in mbps. This is the capability of the connection, not the active throughput. | device device_mac client vendor switch_port name switch_mac switch_id vlan_id profile site site_id |
| omada_port_link_rx | Bytes recieved on a port. | device device_mac client vendor switch_port name switch_mac switch_id vlan_id profile site site_id |
| omada_port_link_tx | Bytes transmitted on a port. | device device_mac client vendor switch_port name switch_mac switch_id vlan_id profile site site_id |
54 changes: 50 additions & 4 deletions cmd/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ func Run() {
os.Exit(0)
return nil
}},
{Name: "mdocs", Aliases: []string{"md"}, Usage: "prints the metric docs.",
Action: func(c *cli.Context) error {
mdocs()
os.Exit(0)
return nil
}},
}
app.Action = run

Expand Down Expand Up @@ -88,10 +94,9 @@ func run(c *cli.Context) error {
}

// register omada collectors
prometheus.MustRegister(collector.NewClientCollector(client))
prometheus.MustRegister(collector.NewControllerCollector(client))
prometheus.MustRegister(collector.NewDeviceCollector(client))
prometheus.MustRegister(collector.NewPortCollector(client))
for _, c := range collectors(client) {
prometheus.MustRegister(c)
}

log.Info().Msg(fmt.Sprintf("listening on :%s", conf.Port))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
Expand All @@ -116,3 +121,44 @@ func run(c *cli.Context) error {

return nil
}

// mdocs just spits out the metrics descriptions and exits
func mdocs() {

// Describe wants to return descriptions via a channel, so make and fill a channel.
dc := make(chan *prometheus.Desc)
go func() {
// collectors can't Collect without a client, but Describe doesn't need one.
for _, c := range collectors(nil) {
c.Describe(dc)
}
close(dc)
}()

fmt.Fprintln(os.Stdout, "| Name | Description | Labels |\n|--|--|--|")

// drain the channel
for {
if description := <-dc; description != nil {
// Sure would be nice if the prometheus.Desc wasn't so opaque. This is gross and fragile.
d := description.String()
d = strings.Replace(d, `Desc{fqName: "`, "| ", 1)
d = strings.Replace(d, `", help: "`, " | ", 1)
d = strings.Replace(d, `", constLabels: {}, variableLabels: [`, " | ", 1)
d = strings.Replace(d, `]}`, " | ", 1)
fmt.Fprintln(os.Stdout, d)
} else {
break
}
}
}

// collectors returns the full complement of configured collectors.
func collectors(client *api.Client) []prometheus.Collector {
return []prometheus.Collector{
collector.NewClientCollector(client),
collector.NewControllerCollector(client),
collector.NewDeviceCollector(client),
collector.NewPortCollector(client),
}
}
1 change: 1 addition & 0 deletions pkg/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ type NetworkClient struct {
Vendor string `json:"vendor"`
Activity float64 `json:"activity"`
SignalLevel float64 `json:"signalLevel"`
SignalNoise float64 `json:"snr"`
WifiMode float64 `json:"wifiMode"`
Ssid string `json:"ssid"`
Rssi float64 `json:"rssi"`
Expand Down
50 changes: 30 additions & 20 deletions pkg/collector/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,31 @@ import (

type clientCollector struct {
omadaClientDownloadActivityBytes *prometheus.Desc
omadaClientSignalDbm *prometheus.Desc
omadaClientRssiDbm *prometheus.Desc
omadaClientTrafficDown *prometheus.Desc
omadaClientTrafficUp *prometheus.Desc
omadaClientTxRate *prometheus.Desc
omadaClientRxRate *prometheus.Desc
omadaClientSignalPct *prometheus.Desc
omadaClientSignalNoiseDbm *prometheus.Desc
omadaClientRssiDbm *prometheus.Desc
omadaClientTrafficDown *prometheus.Desc
omadaClientTrafficUp *prometheus.Desc
omadaClientTxRate *prometheus.Desc
omadaClientRxRate *prometheus.Desc
omadaClientConnectedTotal *prometheus.Desc
client *api.Client
}

func (c *clientCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- c.omadaClientDownloadActivityBytes
ch <- c.omadaClientSignalDbm
ch <- c.omadaClientRssiDbm
ch <- c.omadaClientTrafficDown
ch <- c.omadaClientTrafficUp
ch <- c.omadaClientTxRate
ch <- c.omadaClientRxRate
ch <- c.omadaClientSignalPct
ch <- c.omadaClientSignalNoiseDbm
ch <- c.omadaClientRssiDbm
ch <- c.omadaClientTrafficDown
ch <- c.omadaClientTrafficUp
ch <- c.omadaClientTxRate
ch <- c.omadaClientRxRate
ch <- c.omadaClientConnectedTotal
}

func FormatWifiMode(wifiMode int) string {
mapping := map[int]string {
mapping := map[int]string{
0: "802.11a",
1: "802.11b",
2: "802.11g",
Expand Down Expand Up @@ -65,14 +67,16 @@ func (c *clientCollector) Collect(ch chan<- prometheus.Metric) {
for _, item := range clients {
vlanId := fmt.Sprintf("%.0f", item.VlanId)
port := fmt.Sprintf("%.0f", item.Port)

if item.Wireless {
wifiMode := FormatWifiMode(int(item.WifiMode))

CollectWirelessMetrics := func(desc *prometheus.Desc, valueType prometheus.ValueType, value float64) {
ch <- prometheus.MustNewConstMetric(desc, valueType, value,
item.HostName, item.Vendor, item.Ip, item.Mac, site, client.SiteId, "wireless", wifiMode, item.ApName, item.Ssid)
item.Name, item.Vendor, item.Ip, item.Mac, item.HostName, site, client.SiteId, "wireless", wifiMode, item.ApName, item.Ssid)
}
CollectWirelessMetrics(c.omadaClientSignalDbm, prometheus.GaugeValue, -item.SignalLevel)
CollectWirelessMetrics(c.omadaClientSignalPct, prometheus.GaugeValue, item.SignalLevel)
CollectWirelessMetrics(c.omadaClientSignalNoiseDbm, prometheus.GaugeValue, item.SignalNoise)
CollectWirelessMetrics(c.omadaClientRssiDbm, prometheus.GaugeValue, item.Rssi)
CollectWirelessMetrics(c.omadaClientTrafficDown, prometheus.CounterValue, item.TrafficDown)
CollectWirelessMetrics(c.omadaClientTrafficUp, prometheus.CounterValue, item.TrafficUp)
Expand All @@ -81,12 +85,12 @@ func (c *clientCollector) Collect(ch chan<- prometheus.Metric) {

totals[wifiMode] += 1
ch <- prometheus.MustNewConstMetric(c.omadaClientDownloadActivityBytes, prometheus.GaugeValue, item.Activity,
item.HostName, item.Vendor, item.Ip, item.Mac, site, client.SiteId, "wireless", wifiMode, item.ApName, item.Ssid, "", "")
item.Name, item.Vendor, item.Ip, item.Mac, item.HostName, site, client.SiteId, "wireless", wifiMode, item.ApName, item.Ssid, "", "")
}
if !item.Wireless {
totals["wired"] += 1
ch <- prometheus.MustNewConstMetric(c.omadaClientDownloadActivityBytes, prometheus.GaugeValue, item.Activity,
item.HostName, item.Vendor, item.Ip, item.Mac, site, client.SiteId, "wired", "", "", "", port, vlanId)
item.Name, item.Vendor, item.Ip, item.Mac, item.HostName, site, client.SiteId, "wired", "", "", "", port, vlanId)
}
}

Expand All @@ -102,7 +106,7 @@ func (c *clientCollector) Collect(ch chan<- prometheus.Metric) {
}

func NewClientCollector(c *api.Client) *clientCollector {
client_labels := []string{"client", "vendor", "ip", "mac", "site", "site_id", "connection_mode", "wifi_mode", "ap_name", "ssid"}
client_labels := []string{"client", "vendor", "ip", "mac", "host_name", "site", "site_id", "connection_mode", "wifi_mode", "ap_name", "ssid"}
wired_client_labels := append(client_labels, "switch_port", "vlan_id")

return &clientCollector{
Expand All @@ -112,8 +116,14 @@ func NewClientCollector(c *api.Client) *clientCollector {
nil,
),

omadaClientSignalDbm: prometheus.NewDesc("omada_client_signal_dbm",
"The noise level for the wireless client in dBm.",
omadaClientSignalPct: prometheus.NewDesc("omada_client_signal_pct",
"The signal quality for the wireless client in percent.",
client_labels,
nil,
),

omadaClientSignalNoiseDbm: prometheus.NewDesc("omada_client_snr_dbm",
"The signal to noise ration for the wireless client in dBm.",
client_labels,
nil,
),
Expand Down
25 changes: 0 additions & 25 deletions scripts/metric-markdown-table.sh

This file was deleted.

0 comments on commit 681af44

Please sign in to comment.