mirror of
https://github.com/unpoller/unpoller.git
synced 2026-10-18 04:35:53 +02:00
feat(umbb): review fixes for cellular modem metrics
- Skip signal readings and max bitrates the modem does not report, so a missing reading is not exported as 0 dBm. - Export the 5G NR leg (rsrp_nr, rsrq_nr, snr_nr) separately; the plain values are the LTE anchor in 5G NSA mode. - Export max negotiated bitrate, internet connectivity, and per-SIM card present, PIN blocked, data warning and data limited. - Add primary slot, MCC/MNC, cell ID, PCI and channel to mbb_info, and has_carrier/metered to mbb_sim_info. - Rename the mbb_mode label/field to failover_mode, matching lte_info. - Drop network_reject_age: its unit is undocumented and it does not behave like a running age. - Clarify descriptions for registration_state, signal and SIM byte counters, and count UMBB devices in the Prometheus export log line. Signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
This commit is contained in:
@@ -567,19 +567,25 @@ gauges:
|
||||
- unifi.udb.user_num_sta
|
||||
- unifi.udb.user_wlan_num_sta
|
||||
- unifi.umbb_sim.active
|
||||
- unifi.umbb_sim.card_present
|
||||
- unifi.umbb_sim.data_limited
|
||||
- unifi.umbb_sim.data_warning
|
||||
- unifi.umbb_sim.display_state_elapsed
|
||||
- unifi.umbb_sim.network_reject_age
|
||||
- unifi.umbb_sim.pin_blocked
|
||||
- unifi.umbb_sim.rx_bytes
|
||||
- unifi.umbb_sim.tx_bytes
|
||||
- unifi.umbb.cpu
|
||||
- unifi.umbb.current_slot
|
||||
- unifi.umbb.dl_bandwidth_mhz
|
||||
- unifi.umbb.has_coverage
|
||||
- unifi.umbb.internet
|
||||
- unifi.umbb.last_seen
|
||||
- unifi.umbb.loadavg_1
|
||||
- unifi.umbb.loadavg_15
|
||||
- unifi.umbb.loadavg_5
|
||||
- unifi.umbb.lte_carriers
|
||||
- unifi.umbb.lte_max_bitrate_dl
|
||||
- unifi.umbb.lte_max_bitrate_ul
|
||||
- unifi.umbb.mem
|
||||
- unifi.umbb.mem_buffer
|
||||
- unifi.umbb.mem_total
|
||||
@@ -587,6 +593,12 @@ gauges:
|
||||
- unifi.umbb.memory
|
||||
- unifi.umbb.network
|
||||
- unifi.umbb.nr_carriers
|
||||
- unifi.umbb.nr_max_bitrate_dl
|
||||
- unifi.umbb.nr_max_bitrate_ul
|
||||
- unifi.umbb.nr_rsrp
|
||||
- unifi.umbb.nr_rsrq
|
||||
- unifi.umbb.nr_snr
|
||||
- unifi.umbb.primary_slot
|
||||
- unifi.umbb.probe
|
||||
- unifi.umbb.registration_state
|
||||
- unifi.umbb.roaming
|
||||
|
||||
+51
-21
@@ -21,20 +21,20 @@ func (u *DatadogUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
dl, ul := mbbBandwidth(rad)
|
||||
|
||||
tags := cleanTags(map[string]string{
|
||||
"mac": s.Mac,
|
||||
"site_name": s.SiteName,
|
||||
"source": s.SourceName,
|
||||
"name": s.Name,
|
||||
"version": s.Version,
|
||||
"model": s.Model,
|
||||
"serial": s.Serial,
|
||||
"type": s.Type,
|
||||
"ip": s.IP,
|
||||
"mbb_state": s.Mbb.State,
|
||||
"mbb_mode": s.Mbb.Mode,
|
||||
"rat": rad.Rat,
|
||||
"band": rad.Band,
|
||||
"operator": rad.NetworkOperator,
|
||||
"mac": s.Mac,
|
||||
"site_name": s.SiteName,
|
||||
"source": s.SourceName,
|
||||
"name": s.Name,
|
||||
"version": s.Version,
|
||||
"model": s.Model,
|
||||
"serial": s.Serial,
|
||||
"type": s.Type,
|
||||
"ip": s.IP,
|
||||
"mbb_state": s.Mbb.State,
|
||||
"failover_mode": s.Mbb.Mode,
|
||||
"rat": rad.Rat,
|
||||
"band": rad.Band,
|
||||
"operator": rad.NetworkOperator,
|
||||
})
|
||||
|
||||
data := CombineFloat64(
|
||||
@@ -50,11 +50,8 @@ func (u *DatadogUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
"uplink_uptime": s.Uplink.Uptime.Val,
|
||||
"sa_mode": rad.SA5GMode.Float64(),
|
||||
"current_slot": rad.CurrentSlot.Val,
|
||||
"rsrp": rad.Rsrp.Val,
|
||||
"rsrq": rad.Rsrq.Val,
|
||||
"snr": rad.Snr.Val,
|
||||
"signal": rad.Signal.Val,
|
||||
"signal_percent": rad.SignalPercent.Val,
|
||||
"primary_slot": s.MbbOverrides.PrimarySlot.Val,
|
||||
"internet": s.Internet.Float64(),
|
||||
"has_coverage": rad.HasCoverage.Float64(),
|
||||
"roaming": rad.Roaming.Float64(),
|
||||
"registration_state": rad.RegistrationState.Val,
|
||||
@@ -62,7 +59,8 @@ func (u *DatadogUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
"lte_carriers": float64(len(rad.CaLte)),
|
||||
"dl_bandwidth_mhz": dl,
|
||||
"ul_bandwidth_mhz": ul,
|
||||
})
|
||||
},
|
||||
mbbOptional(rad))
|
||||
|
||||
r.addCount(umbbT)
|
||||
reportGaugeForFloat64Map(r, metricNamespace("umbb"), data, tags)
|
||||
@@ -79,13 +77,18 @@ func (u *DatadogUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
"esim": strconv.FormatBool(sim.Esim.Val),
|
||||
"spn": sim.Spn,
|
||||
"display_state": sim.DisplayState,
|
||||
"has_carrier": strconv.FormatBool(sim.HasCarrier.Val),
|
||||
"metered": strconv.FormatBool(sim.Metered.Val),
|
||||
"network_reject_type": sim.NetworkRejectType,
|
||||
"network_reject_text": sim.NetworkRejectText,
|
||||
})
|
||||
simData := map[string]float64{
|
||||
"active": sim.Active.Float64(),
|
||||
"card_present": sim.CardPresent.Float64(),
|
||||
"pin_blocked": sim.PinBlocked.Float64(),
|
||||
"data_warning": sim.DataWarning.Float64(),
|
||||
"data_limited": sim.DataLimited.Float64(),
|
||||
"display_state_elapsed": sim.DisplayStateElapsed.Val,
|
||||
"network_reject_age": sim.NetworkRejectAge.Val,
|
||||
"rx_bytes": sim.RxBytes.Val,
|
||||
"tx_bytes": sim.TxBytes.Val,
|
||||
}
|
||||
@@ -94,6 +97,33 @@ func (u *DatadogUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
}
|
||||
}
|
||||
|
||||
// mbbOptional returns the signal readings and bitrates the modem reported, keyed by field name.
|
||||
// Missing readings are left out, so they are not mistaken for 0 dBm.
|
||||
func mbbOptional(rad unifi.MBBRadio) map[string]float64 {
|
||||
out := map[string]float64{}
|
||||
|
||||
for k, v := range map[string]unifi.FlexInt{
|
||||
"rsrp": rad.Rsrp,
|
||||
"rsrq": rad.Rsrq,
|
||||
"snr": rad.Snr,
|
||||
"nr_rsrp": rad.RsrpNr,
|
||||
"nr_rsrq": rad.RsrqNr,
|
||||
"nr_snr": rad.SnrNr,
|
||||
"signal": rad.Signal,
|
||||
"signal_percent": rad.SignalPercent,
|
||||
"lte_max_bitrate_dl": rad.MaxBitrateDl,
|
||||
"lte_max_bitrate_ul": rad.MaxBitrateUl,
|
||||
"nr_max_bitrate_dl": rad.MaxBitrateDlNr,
|
||||
"nr_max_bitrate_ul": rad.MaxBitrateUlNr,
|
||||
} {
|
||||
if v.Txt != "" {
|
||||
out[k] = v.Val
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// mbbBandwidth sums the downlink and uplink bandwidth of every aggregated carrier, NR and LTE.
|
||||
// A carrier with no uplink reports a null bandwidth, which decodes as zero.
|
||||
// A sum that overflows is reported as zero: InfluxDB rejects a whole point with an infinite field.
|
||||
|
||||
@@ -683,24 +683,38 @@ points:
|
||||
- version
|
||||
fields:
|
||||
band: string
|
||||
cell_id: float
|
||||
channel: float
|
||||
cpu: float
|
||||
current_slot: float
|
||||
dl_bandwidth_mhz: float
|
||||
failover_mode: string
|
||||
has_coverage: bool
|
||||
internet: bool
|
||||
ip: string
|
||||
last_seen: float
|
||||
loadavg_1: float
|
||||
loadavg_5: float
|
||||
loadavg_15: float
|
||||
lte_carriers: float
|
||||
mbb_mode: string
|
||||
lte_max_bitrate_dl: float
|
||||
lte_max_bitrate_ul: float
|
||||
mbb_state: string
|
||||
mcc: float
|
||||
mem: float
|
||||
mem_buffer: float
|
||||
mem_total: float
|
||||
mem_used: float
|
||||
mnc: float
|
||||
nr_carriers: float
|
||||
nr_max_bitrate_dl: float
|
||||
nr_max_bitrate_ul: float
|
||||
nr_rsrp: float
|
||||
nr_rsrq: float
|
||||
nr_snr: float
|
||||
operator: string
|
||||
pci: float
|
||||
primary_slot: float
|
||||
rat: string
|
||||
registration_state: float
|
||||
roaming: bool
|
||||
@@ -737,11 +751,16 @@ points:
|
||||
- type
|
||||
fields:
|
||||
active: bool
|
||||
card_present: bool
|
||||
data_limited: bool
|
||||
data_warning: bool
|
||||
display_state: string
|
||||
display_state_elapsed: float
|
||||
network_reject_age: float
|
||||
has_carrier: bool
|
||||
metered: bool
|
||||
network_reject_text: string
|
||||
network_reject_type: string
|
||||
pin_blocked: bool
|
||||
rx_bytes: float
|
||||
tx_bytes: float
|
||||
unifi_alarm:
|
||||
|
||||
+45
-7
@@ -43,18 +43,20 @@ func (u *InfluxUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
"uplink_max_speed": s.Uplink.MaxSpeed.Val,
|
||||
"uplink_latency": s.Uplink.Latency.Val,
|
||||
"uplink_uptime": s.Uplink.Uptime.Val,
|
||||
"internet": s.Internet.Val,
|
||||
"mbb_state": s.Mbb.State,
|
||||
"mbb_mode": s.Mbb.Mode,
|
||||
"failover_mode": s.Mbb.Mode,
|
||||
"rat": rad.Rat,
|
||||
"band": rad.Band,
|
||||
"operator": rad.NetworkOperator,
|
||||
"sa_mode": rad.SA5GMode.Val,
|
||||
"current_slot": rad.CurrentSlot.Val,
|
||||
"rsrp": rad.Rsrp.Val,
|
||||
"rsrq": rad.Rsrq.Val,
|
||||
"snr": rad.Snr.Val,
|
||||
"signal": rad.Signal.Val,
|
||||
"signal_percent": rad.SignalPercent.Val,
|
||||
"primary_slot": s.MbbOverrides.PrimarySlot.Val,
|
||||
"mcc": rad.Mcc.Val,
|
||||
"mnc": rad.Mnc.Val,
|
||||
"cell_id": rad.CellID.Val,
|
||||
"pci": rad.Pci.Val,
|
||||
"channel": rad.Channel.Val,
|
||||
"has_coverage": rad.HasCoverage.Val,
|
||||
"roaming": rad.Roaming.Val,
|
||||
"registration_state": rad.RegistrationState.Val,
|
||||
@@ -64,6 +66,10 @@ func (u *InfluxUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
"ul_bandwidth_mhz": ul,
|
||||
})
|
||||
|
||||
for k, v := range mbbOptional(rad) {
|
||||
fields[k] = v
|
||||
}
|
||||
|
||||
r.addCount(umbbT)
|
||||
r.send(&metric{Table: "umbb", Tags: tags, Fields: fields})
|
||||
|
||||
@@ -83,11 +89,16 @@ func (u *InfluxUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
},
|
||||
Fields: map[string]any{
|
||||
"active": sim.Active.Val,
|
||||
"card_present": sim.CardPresent.Val,
|
||||
"pin_blocked": sim.PinBlocked.Val,
|
||||
"has_carrier": sim.HasCarrier.Val,
|
||||
"metered": sim.Metered.Val,
|
||||
"data_warning": sim.DataWarning.Val,
|
||||
"data_limited": sim.DataLimited.Val,
|
||||
"display_state": sim.DisplayState,
|
||||
"display_state_elapsed": sim.DisplayStateElapsed.Val,
|
||||
"network_reject_type": sim.NetworkRejectType,
|
||||
"network_reject_text": sim.NetworkRejectText,
|
||||
"network_reject_age": sim.NetworkRejectAge.Val,
|
||||
"rx_bytes": sim.RxBytes.Val,
|
||||
"tx_bytes": sim.TxBytes.Val,
|
||||
},
|
||||
@@ -95,6 +106,33 @@ func (u *InfluxUnifi) batchUMBB(r report, s *unifi.UMBB) {
|
||||
}
|
||||
}
|
||||
|
||||
// mbbOptional returns the signal readings and bitrates the modem reported, keyed by field name.
|
||||
// Missing readings are left out, so they are not mistaken for 0 dBm.
|
||||
func mbbOptional(rad unifi.MBBRadio) map[string]float64 {
|
||||
out := map[string]float64{}
|
||||
|
||||
for k, v := range map[string]unifi.FlexInt{
|
||||
"rsrp": rad.Rsrp,
|
||||
"rsrq": rad.Rsrq,
|
||||
"snr": rad.Snr,
|
||||
"nr_rsrp": rad.RsrpNr,
|
||||
"nr_rsrq": rad.RsrqNr,
|
||||
"nr_snr": rad.SnrNr,
|
||||
"signal": rad.Signal,
|
||||
"signal_percent": rad.SignalPercent,
|
||||
"lte_max_bitrate_dl": rad.MaxBitrateDl,
|
||||
"lte_max_bitrate_ul": rad.MaxBitrateUl,
|
||||
"nr_max_bitrate_dl": rad.MaxBitrateDlNr,
|
||||
"nr_max_bitrate_ul": rad.MaxBitrateUlNr,
|
||||
} {
|
||||
if v.Txt != "" {
|
||||
out[k] = v.Val
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// mbbBandwidth sums the downlink and uplink bandwidth of every aggregated carrier, NR and LTE.
|
||||
// A carrier with no uplink reports a null bandwidth, which decodes as zero.
|
||||
// A sum that overflows is reported as zero: InfluxDB rejects a whole point with an infinite field.
|
||||
|
||||
+42
-16
@@ -28,7 +28,7 @@ func (u *OtelOutput) exportUMBB(ctx context.Context, meter metric.Meter, r *Repo
|
||||
attribute.String("type", s.Type),
|
||||
attribute.String("ip", s.IP),
|
||||
attribute.String("mbb_state", s.Mbb.State),
|
||||
attribute.String("mbb_mode", s.Mbb.Mode),
|
||||
attribute.String("failover_mode", s.Mbb.Mode),
|
||||
attribute.String("rat", rad.Rat),
|
||||
attribute.String("band", rad.Band),
|
||||
attribute.String("operator", rad.NetworkOperator),
|
||||
@@ -47,22 +47,40 @@ func (u *OtelOutput) exportUMBB(ctx context.Context, meter metric.Meter, r *Repo
|
||||
"UMBB CPU utilization percentage", s.SystemStats.CPU.Val, attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_mem_utilization",
|
||||
"UMBB memory utilization percentage", s.SystemStats.Mem.Val, attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_rsrp_dbm",
|
||||
"Cellular RSRP in dBm", rad.Rsrp.Val, attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_rsrq_db",
|
||||
"Cellular RSRQ in dB", rad.Rsrq.Val, attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_snr_db",
|
||||
"Cellular signal-to-noise ratio in dB", rad.Snr.Val, attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_signal_bars",
|
||||
"Cellular signal strength in bars (0-5)", rad.Signal.Val, attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_signal_percent",
|
||||
"Cellular signal strength percentage", rad.SignalPercent.Val, attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_internet",
|
||||
"Modem reports internet connectivity (1) or not (0)", s.Internet.Float64(), attrs)
|
||||
|
||||
// Signal readings and bitrates are left out when the modem does not report them,
|
||||
// so a missing reading is not mistaken for 0 dBm.
|
||||
for _, g := range []struct {
|
||||
name, desc string
|
||||
val unifi.FlexInt
|
||||
}{
|
||||
{"unifi_device_umbb_rsrp_dbm", "Cellular RSRP of the serving cell in dBm. LTE anchor in 5G NSA mode", rad.Rsrp},
|
||||
{"unifi_device_umbb_rsrq_db", "Cellular RSRQ of the serving cell in dB. LTE anchor in 5G NSA mode", rad.Rsrq},
|
||||
{"unifi_device_umbb_snr_db", "Cellular signal-to-noise ratio of the serving cell in dB. LTE anchor in 5G NSA mode", rad.Snr},
|
||||
{"unifi_device_umbb_nr_rsrp_dbm", "5G NR RSRP in dBm", rad.RsrpNr},
|
||||
{"unifi_device_umbb_nr_rsrq_db", "5G NR RSRQ in dB", rad.RsrqNr},
|
||||
{"unifi_device_umbb_nr_snr_db", "5G NR signal-to-noise ratio in dB", rad.SnrNr},
|
||||
{"unifi_device_umbb_signal_bars", "Cellular signal strength in bars, as shown by the controller", rad.Signal},
|
||||
{"unifi_device_umbb_signal_percent", "Cellular signal strength percentage (0-100)", rad.SignalPercent},
|
||||
{"unifi_device_umbb_lte_max_bitrate_dl_bps", "Maximum LTE downlink bitrate in bits per second", rad.MaxBitrateDl},
|
||||
{"unifi_device_umbb_lte_max_bitrate_ul_bps", "Maximum LTE uplink bitrate in bits per second", rad.MaxBitrateUl},
|
||||
{"unifi_device_umbb_nr_max_bitrate_dl_bps", "Maximum 5G NR downlink bitrate in bits per second", rad.MaxBitrateDlNr},
|
||||
{"unifi_device_umbb_nr_max_bitrate_ul_bps", "Maximum 5G NR uplink bitrate in bits per second", rad.MaxBitrateUlNr},
|
||||
} {
|
||||
if g.val.Txt != "" {
|
||||
u.recordGauge(ctx, meter, r, g.name, g.desc, g.val.Val, attrs)
|
||||
}
|
||||
}
|
||||
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_coverage",
|
||||
"Modem has cellular coverage (1) or not (0)", rad.HasCoverage.Float64(), attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_roaming",
|
||||
"Modem is roaming (1) or on its home network (0)", rad.Roaming.Float64(), attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_registration_state",
|
||||
"Cellular network registration state code", rad.RegistrationState.Val, attrs)
|
||||
"Cellular network registration state code (undocumented; likely 0 not registered, 1 home, "+
|
||||
"2 searching, 3 denied, 4 unknown, 5 roaming)", rad.RegistrationState.Val, attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_nr_carriers",
|
||||
"Aggregated 5G NR component carriers in use", float64(len(rad.CaNr)), attrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_lte_carriers",
|
||||
@@ -85,6 +103,8 @@ func (u *OtelOutput) exportUMBB(ctx context.Context, meter metric.Meter, r *Repo
|
||||
attribute.String("esim", strconv.FormatBool(sim.Esim.Val)),
|
||||
attribute.String("spn", sim.Spn),
|
||||
attribute.String("display_state", sim.DisplayState),
|
||||
attribute.Bool("has_carrier", sim.HasCarrier.Val),
|
||||
attribute.Bool("metered", sim.Metered.Val),
|
||||
attribute.String("network_reject_type", sim.NetworkRejectType),
|
||||
attribute.String("network_reject_text", sim.NetworkRejectText),
|
||||
)
|
||||
@@ -93,12 +113,18 @@ func (u *OtelOutput) exportUMBB(ctx context.Context, meter metric.Meter, r *Repo
|
||||
"SIM slot is the active data SIM (1) or not (0)", sim.Active.Float64(), simAttrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_sim_state_seconds",
|
||||
"Seconds the SIM has been in its current display state", sim.DisplayStateElapsed.Val, simAttrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_sim_network_reject_age",
|
||||
"Age of the last carrier network reject, as reported by the controller", sim.NetworkRejectAge.Val, simAttrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_sim_card_present",
|
||||
"SIM card or eSIM profile is present (1) or not (0)", sim.CardPresent.Float64(), simAttrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_sim_pin_blocked",
|
||||
"SIM is PIN blocked (1) or not (0)", sim.PinBlocked.Float64(), simAttrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_sim_data_warning",
|
||||
"SIM has passed its data usage warning threshold (1) or not (0)", sim.DataWarning.Float64(), simAttrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_sim_data_limited",
|
||||
"SIM has reached its data limit (1) or not (0)", sim.DataLimited.Float64(), simAttrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_sim_receive_bytes_total",
|
||||
"Bytes received over this SIM", sim.RxBytes.Val, simAttrs)
|
||||
"Bytes received over this SIM. Cumulative; not reset when the modem reboots", sim.RxBytes.Val, simAttrs)
|
||||
u.recordGauge(ctx, meter, r, "unifi_device_umbb_sim_transmit_bytes_total",
|
||||
"Bytes transmitted over this SIM", sim.TxBytes.Val, simAttrs)
|
||||
"Bytes transmitted over this SIM. Cumulative; not reset when the modem reboots", sim.TxBytes.Val, simAttrs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,9 @@ func (r *Report) report(c poller.Logger, descs map[*prometheus.Desc]bool) {
|
||||
m := r.Metrics
|
||||
|
||||
c.Logf("UniFi Measurements Exported. Site: %d, Client: %d, "+
|
||||
"UAP: %d, USG/UDM: %d, USW: %d, DPI Site/Client: %d/%d, Countries: %d, Desc: %d, "+
|
||||
"UAP: %d, USG/UDM: %d, USW: %d, UMBB: %d, DPI Site/Client: %d/%d, Countries: %d, Desc: %d, "+
|
||||
"Metric: %d, Bytes: %d, Err: %d, 0s: %d, Req/Total: %v / %v",
|
||||
len(m.Sites), len(m.Clients), r.UAP, r.UDM+r.USG+r.UXG, r.USW, len(m.SitesDPI),
|
||||
len(m.Sites), len(m.Clients), r.UAP, r.UDM+r.USG+r.UXG, r.USW, r.UMBB, len(m.SitesDPI),
|
||||
len(m.ClientsDPI), len(m.CountryTraffic), len(descs), r.Total, r.Bytes, r.Errors, r.Zeros,
|
||||
r.Fetch.Round(time.Millisecond/oneDecimalPoint),
|
||||
r.Elapsed.Round(time.Millisecond/oneDecimalPoint))
|
||||
|
||||
+82
-29
@@ -10,9 +10,13 @@ import (
|
||||
|
||||
// umbb holds the cellular modem descriptors for UMBB (mobile broadband) devices, such as the U5G Max.
|
||||
type umbb struct {
|
||||
Internet *prometheus.Desc
|
||||
Rsrp *prometheus.Desc
|
||||
Rsrq *prometheus.Desc
|
||||
Snr *prometheus.Desc
|
||||
RsrpNr *prometheus.Desc
|
||||
RsrqNr *prometheus.Desc
|
||||
SnrNr *prometheus.Desc
|
||||
SignalBars *prometheus.Desc
|
||||
SignalPercent *prometheus.Desc
|
||||
Coverage *prometheus.Desc
|
||||
@@ -20,12 +24,16 @@ type umbb struct {
|
||||
RegistrationState *prometheus.Desc
|
||||
Carriers *prometheus.Desc
|
||||
BandwidthMhz *prometheus.Desc
|
||||
MaxBitrate *prometheus.Desc
|
||||
Info *prometheus.Desc
|
||||
SimActive *prometheus.Desc
|
||||
SimCardPresent *prometheus.Desc
|
||||
SimPinBlocked *prometheus.Desc
|
||||
SimDataWarning *prometheus.Desc
|
||||
SimDataLimited *prometheus.Desc
|
||||
SimRxBytes *prometheus.Desc
|
||||
SimTxBytes *prometheus.Desc
|
||||
SimStateSeconds *prometheus.Desc
|
||||
SimRejectAge *prometheus.Desc
|
||||
SimInfo *prometheus.Desc
|
||||
}
|
||||
|
||||
@@ -35,26 +43,44 @@ func descUMBB(ns string) *umbb {
|
||||
nd := prometheus.NewDesc
|
||||
|
||||
return &umbb{
|
||||
Rsrp: nd(ns+"mbb_rsrp_dbm", "Cellular RSRP in dBm.", labels, nil),
|
||||
Rsrq: nd(ns+"mbb_rsrq_db", "Cellular RSRQ in dB.", labels, nil),
|
||||
Snr: nd(ns+"mbb_snr_db", "Cellular signal-to-noise ratio in dB.", labels, nil),
|
||||
SignalBars: nd(ns+"mbb_signal_bars", "Cellular signal strength in bars (0-5).", labels, nil),
|
||||
SignalPercent: nd(ns+"mbb_signal_percent", "Cellular signal strength percentage.", labels, nil),
|
||||
Coverage: nd(ns+"mbb_coverage", "Modem has cellular coverage (1) or not (0).", labels, nil),
|
||||
Roaming: nd(ns+"mbb_roaming", "Modem is roaming (1) or on its home network (0).", labels, nil),
|
||||
RegistrationState: nd(ns+"mbb_registration_state", "Cellular network registration state code.", labels, nil),
|
||||
Carriers: nd(ns+"mbb_carriers", "Aggregated component carriers in use.", append(labels, "carrier_type"), nil),
|
||||
BandwidthMhz: nd(ns+"mbb_bandwidth_mhz", "Total aggregated carrier bandwidth in MHz.", append(labels, "direction"), nil),
|
||||
Internet: nd(ns+"mbb_internet", "Modem reports internet connectivity (1) or not (0).", labels, nil),
|
||||
Rsrp: nd(ns+"mbb_rsrp_dbm", "Cellular RSRP of the serving cell in dBm. LTE anchor in 5G NSA mode.", labels, nil),
|
||||
Rsrq: nd(ns+"mbb_rsrq_db", "Cellular RSRQ of the serving cell in dB. LTE anchor in 5G NSA mode.", labels, nil),
|
||||
Snr: nd(ns+"mbb_snr_db", "Cellular signal-to-noise ratio of the serving cell in dB. LTE anchor in 5G NSA mode.", labels, nil),
|
||||
RsrpNr: nd(ns+"mbb_nr_rsrp_dbm", "5G NR RSRP in dBm. Same as mbb_rsrp_dbm in 5G SA mode.", labels, nil),
|
||||
RsrqNr: nd(ns+"mbb_nr_rsrq_db", "5G NR RSRQ in dB. Same as mbb_rsrq_db in 5G SA mode.", labels, nil),
|
||||
SnrNr: nd(ns+"mbb_nr_snr_db", "5G NR signal-to-noise ratio in dB. Same as mbb_snr_db in 5G SA mode.", labels, nil),
|
||||
SignalBars: nd(ns+"mbb_signal_bars",
|
||||
"Cellular signal strength in bars, as shown by the controller.", labels, nil),
|
||||
SignalPercent: nd(ns+"mbb_signal_percent",
|
||||
"Cellular signal strength percentage (0-100), as computed by the controller.", labels, nil),
|
||||
Coverage: nd(ns+"mbb_coverage", "Modem has cellular coverage (1) or not (0).", labels, nil),
|
||||
Roaming: nd(ns+"mbb_roaming", "Modem is roaming (1) or on its home network (0).", labels, nil),
|
||||
RegistrationState: nd(ns+"mbb_registration_state",
|
||||
"Cellular network registration state code. Undocumented; likely 0 not registered, 1 registered home, "+
|
||||
"2 searching, 3 denied, 4 unknown, 5 registered roaming.", labels, nil),
|
||||
Carriers: nd(ns+"mbb_carriers", "Aggregated component carriers in use.", append(labels, "carrier_type"), nil),
|
||||
BandwidthMhz: nd(ns+"mbb_bandwidth_mhz", "Total aggregated carrier bandwidth in MHz.",
|
||||
append(labels, "direction"), nil),
|
||||
MaxBitrate: nd(ns+"mbb_max_bitrate_bps", "Maximum bitrate negotiated with the network, in bits per second.",
|
||||
append(labels, "carrier_type", "direction"), nil),
|
||||
Info: nd(ns+"mbb_info", "Cellular modem state. Always 1; state and network details are labels.",
|
||||
append(labels, "mbb_state", "mbb_mode", "rat", "band", "operator", "sa_mode", "current_slot"), nil),
|
||||
SimActive: nd(ns+"mbb_sim_active", "SIM slot is the active data SIM (1) or not (0).", labelS, nil),
|
||||
SimRxBytes: nd(ns+"mbb_sim_receive_bytes_total", "Bytes received over this SIM.", labelS, nil),
|
||||
SimTxBytes: nd(ns+"mbb_sim_transmit_bytes_total", "Bytes transmitted over this SIM.", labelS, nil),
|
||||
SimStateSeconds: nd(ns+"mbb_sim_state_seconds", "Seconds the SIM has been in its current display state.", labelS, nil),
|
||||
SimRejectAge: nd(ns+"mbb_sim_network_reject_age",
|
||||
"Age of the last carrier network reject, as reported by the controller.", labelS, nil),
|
||||
append(labels, "mbb_state", "failover_mode", "rat", "band", "operator", "sa_mode", "current_slot",
|
||||
"primary_slot", "mcc", "mnc", "cell_id", "pci", "channel"), nil),
|
||||
SimActive: nd(ns+"mbb_sim_active", "SIM slot is the active data SIM (1) or not (0).", labelS, nil),
|
||||
SimCardPresent: nd(ns+"mbb_sim_card_present", "SIM card or eSIM profile is present (1) or not (0).", labelS, nil),
|
||||
SimPinBlocked: nd(ns+"mbb_sim_pin_blocked", "SIM is PIN blocked (1) or not (0).", labelS, nil),
|
||||
SimDataWarning: nd(ns+"mbb_sim_data_warning", "SIM has passed its data usage warning threshold (1) or not (0).",
|
||||
labelS, nil),
|
||||
SimDataLimited: nd(ns+"mbb_sim_data_limited", "SIM has reached its data limit (1) or not (0).", labelS, nil),
|
||||
SimRxBytes: nd(ns+"mbb_sim_receive_bytes_total",
|
||||
"Bytes received over this SIM. Cumulative; not reset when the modem reboots.", labelS, nil),
|
||||
SimTxBytes: nd(ns+"mbb_sim_transmit_bytes_total",
|
||||
"Bytes transmitted over this SIM. Cumulative; not reset when the modem reboots.", labelS, nil),
|
||||
SimStateSeconds: nd(ns+"mbb_sim_state_seconds", "Seconds the SIM has been in its current display state.",
|
||||
labelS, nil),
|
||||
SimInfo: nd(ns+"mbb_sim_info", "SIM state. Always 1; display state and last network reject are labels.",
|
||||
append(labelS, "display_state", "network_reject_type", "network_reject_text"), nil),
|
||||
append(labelS, "display_state", "has_carrier", "metered", "network_reject_type", "network_reject_text"), nil),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +100,7 @@ func (u *promUnifi) exportUMBB(r report, d *unifi.UMBB) {
|
||||
|
||||
u.exportSYSstats(r, labels, d.SysStats, d.SystemStats)
|
||||
u.exportDeviceUplink(r, labels, d.Uplink)
|
||||
u.exportMBB(r, labels, d.Mbb)
|
||||
u.exportMBB(r, labels, d)
|
||||
|
||||
r.send([]*metric{
|
||||
{u.Device.Info, gauge, 1.0, append(append([]string{}, baseLabels...), infoLabels...)},
|
||||
@@ -84,19 +110,17 @@ func (u *promUnifi) exportUMBB(r report, d *unifi.UMBB) {
|
||||
})
|
||||
}
|
||||
|
||||
func (u *promUnifi) exportMBB(r report, labels []string, m unifi.MBB) {
|
||||
func (u *promUnifi) exportMBB(r report, labels []string, d *unifi.UMBB) {
|
||||
m := d.Mbb
|
||||
rad := m.Radio
|
||||
dl, ul := mbbBandwidth(rad)
|
||||
info := append(append([]string{}, labels...),
|
||||
m.State, m.Mode, rad.Rat, rad.Band, rad.NetworkOperator,
|
||||
strconv.FormatBool(rad.SA5GMode.Val), rad.CurrentSlot.Txt)
|
||||
strconv.FormatBool(rad.SA5GMode.Val), rad.CurrentSlot.Txt, d.MbbOverrides.PrimarySlot.Txt,
|
||||
rad.Mcc.Txt, rad.Mnc.Txt, rad.CellID.Txt, rad.Pci.Txt, rad.Channel.Txt)
|
||||
|
||||
r.send([]*metric{
|
||||
{u.UMBB.Rsrp, gauge, rad.Rsrp, labels},
|
||||
{u.UMBB.Rsrq, gauge, rad.Rsrq, labels},
|
||||
{u.UMBB.Snr, gauge, rad.Snr, labels},
|
||||
{u.UMBB.SignalBars, gauge, rad.Signal, labels},
|
||||
{u.UMBB.SignalPercent, gauge, rad.SignalPercent, labels},
|
||||
{u.UMBB.Internet, gauge, d.Internet.Val, labels},
|
||||
{u.UMBB.Coverage, gauge, rad.HasCoverage.Val, labels},
|
||||
{u.UMBB.Roaming, gauge, rad.Roaming.Val, labels},
|
||||
{u.UMBB.RegistrationState, gauge, rad.RegistrationState, labels},
|
||||
@@ -107,18 +131,47 @@ func (u *promUnifi) exportMBB(r report, labels []string, m unifi.MBB) {
|
||||
{u.UMBB.Info, gauge, 1.0, info},
|
||||
})
|
||||
|
||||
// Signal readings and bitrates are left out when the modem does not report them,
|
||||
// so a missing reading is not mistaken for 0 dBm.
|
||||
for _, s := range []struct {
|
||||
desc *prometheus.Desc
|
||||
val unifi.FlexInt
|
||||
dims []string
|
||||
}{
|
||||
{u.UMBB.Rsrp, rad.Rsrp, nil},
|
||||
{u.UMBB.Rsrq, rad.Rsrq, nil},
|
||||
{u.UMBB.Snr, rad.Snr, nil},
|
||||
{u.UMBB.RsrpNr, rad.RsrpNr, nil},
|
||||
{u.UMBB.RsrqNr, rad.RsrqNr, nil},
|
||||
{u.UMBB.SnrNr, rad.SnrNr, nil},
|
||||
{u.UMBB.SignalBars, rad.Signal, nil},
|
||||
{u.UMBB.SignalPercent, rad.SignalPercent, nil},
|
||||
{u.UMBB.MaxBitrate, rad.MaxBitrateDl, []string{"lte", "dl"}},
|
||||
{u.UMBB.MaxBitrate, rad.MaxBitrateUl, []string{"lte", "ul"}},
|
||||
{u.UMBB.MaxBitrate, rad.MaxBitrateDlNr, []string{"nr", "dl"}},
|
||||
{u.UMBB.MaxBitrate, rad.MaxBitrateUlNr, []string{"nr", "ul"}},
|
||||
} {
|
||||
if s.val.Txt != "" {
|
||||
r.send([]*metric{{s.desc, gauge, s.val.Val, append(append([]string{}, labels...), s.dims...)}})
|
||||
}
|
||||
}
|
||||
|
||||
for _, sim := range m.Sim {
|
||||
simLabels := append(append([]string{}, labels...),
|
||||
sim.Slot.Txt, strconv.FormatBool(sim.Esim.Val), sim.Spn)
|
||||
simInfo := append(append([]string{}, simLabels...),
|
||||
sim.DisplayState, sim.NetworkRejectType, sim.NetworkRejectText)
|
||||
sim.DisplayState, strconv.FormatBool(sim.HasCarrier.Val), strconv.FormatBool(sim.Metered.Val),
|
||||
sim.NetworkRejectType, sim.NetworkRejectText)
|
||||
|
||||
r.send([]*metric{
|
||||
{u.UMBB.SimActive, gauge, sim.Active.Val, simLabels},
|
||||
{u.UMBB.SimCardPresent, gauge, sim.CardPresent.Val, simLabels},
|
||||
{u.UMBB.SimPinBlocked, gauge, sim.PinBlocked.Val, simLabels},
|
||||
{u.UMBB.SimDataWarning, gauge, sim.DataWarning.Val, simLabels},
|
||||
{u.UMBB.SimDataLimited, gauge, sim.DataLimited.Val, simLabels},
|
||||
{u.UMBB.SimRxBytes, counter, sim.RxBytes, simLabels},
|
||||
{u.UMBB.SimTxBytes, counter, sim.TxBytes, simLabels},
|
||||
{u.UMBB.SimStateSeconds, gauge, sim.DisplayStateElapsed, simLabels},
|
||||
{u.UMBB.SimRejectAge, gauge, sim.NetworkRejectAge, simLabels},
|
||||
{u.UMBB.SimInfo, gauge, 1.0, simInfo},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,13 +20,16 @@ const u5gMaxJSON = `{
|
||||
"mac": "00:00:5e:00:53:02",
|
||||
"version": "7.5.3.19533",
|
||||
"uptime": 5264387,
|
||||
"internet": true,
|
||||
"mbb_overrides": {"primary_slot": 1},
|
||||
"mbb": {
|
||||
"mode": "failover",
|
||||
"state": "ready",
|
||||
"sim": [
|
||||
{"active": false, "slot": 1, "esim": false, "display_state": "no-sim", "display_state_elapsed": 5264323},
|
||||
{"active": false, "slot": 1, "esim": false, "card_present": false, "display_state": "no-sim", "display_state_elapsed": 5264323},
|
||||
{
|
||||
"active": true, "slot": 2, "esim": true, "spn": "EE",
|
||||
"active": true, "slot": 2, "esim": true, "spn": "EE", "card_present": true, "has_carrier": true,
|
||||
"metered": false, "pin_blocked": false, "data_warning": true, "data_limited": false,
|
||||
"display_state": "operational", "display_state_elapsed": 5230,
|
||||
"network_reject_type": "wds", "network_reject_text": "Carrier rejected data", "network_reject_age": 45,
|
||||
"rxbytes": "3013150278176", "txbytes": "725650860811"
|
||||
@@ -35,6 +38,9 @@ const u5gMaxJSON = `{
|
||||
"radio": {
|
||||
"5g_sa_mode": true, "band": "n78", "rat": "5G", "networkoperator": "EE", "current_slot": 2,
|
||||
"has_coverage": true, "roaming": false, "registration_state": 1,
|
||||
"mcc": 234, "mnc": 30, "cell_id": 2719245, "pci": 216, "channel": 646080,
|
||||
"max_bitrate_dl": 0, "max_bitrate_ul": 0, "max_bitrate_dl_nr": 640000000, "max_bitrate_ul_nr": 224000000,
|
||||
"rsrp_nr": -96, "rsrq_nr": -13, "snr_nr": 17.600000381469727,
|
||||
"rsrp": -96, "rsrq": -13, "snr": 17.600000381469727, "signal": 3, "signal_percent": 95,
|
||||
"ca_lte": [],
|
||||
"ca_nr": [
|
||||
@@ -74,21 +80,60 @@ func TestExportUMBB(t *testing.T) {
|
||||
a.InDelta(80, findMetric(t, r.sent, "unifi_mbb_bandwidth_mhz", append(labels, "dl")).value, 0.001)
|
||||
a.InDelta(40, findMetric(t, r.sent, "unifi_mbb_bandwidth_mhz", append(labels, "ul")).value, 0.001,
|
||||
"a null uplink bandwidth adds nothing")
|
||||
a.InDelta(-96, findMetric(t, r.sent, "unifi_mbb_nr_rsrp_dbm", labels).value, 0.001)
|
||||
a.InDelta(-13, findMetric(t, r.sent, "unifi_mbb_nr_rsrq_db", labels).value, 0.001)
|
||||
a.InDelta(17.6, findMetric(t, r.sent, "unifi_mbb_nr_snr_db", labels).value, 0.001)
|
||||
a.InDelta(1, findMetric(t, r.sent, "unifi_mbb_internet", labels).value, 0.001)
|
||||
a.InDelta(640000000, findMetric(t, r.sent, "unifi_mbb_max_bitrate_bps", append(labels, "nr", "dl")).value, 1)
|
||||
a.InDelta(224000000, findMetric(t, r.sent, "unifi_mbb_max_bitrate_bps", append(labels, "nr", "ul")).value, 1)
|
||||
a.InDelta(0, findMetric(t, r.sent, "unifi_mbb_max_bitrate_bps", append(labels, "lte", "dl")).value, 1)
|
||||
a.InDelta(1, findMetric(t, r.sent, "unifi_mbb_info",
|
||||
append(labels, "ready", "failover", "5G", "n78", "EE", "true", "2")).value, 0.001)
|
||||
append(labels, "ready", "failover", "5G", "n78", "EE", "true", "2", "1",
|
||||
"234", "30", "2719245", "216", "646080")).value, 0.001)
|
||||
|
||||
noSim := append(labels, "1", "false", "")
|
||||
a.InDelta(0, findMetric(t, r.sent, "unifi_mbb_sim_active", noSim).value, 0.001)
|
||||
a.InDelta(1, findMetric(t, r.sent, "unifi_mbb_sim_info", append(noSim, "no-sim", "", "")).value, 0.001)
|
||||
a.InDelta(0, findMetric(t, r.sent, "unifi_mbb_sim_card_present", noSim).value, 0.001)
|
||||
a.InDelta(1, findMetric(t, r.sent, "unifi_mbb_sim_info",
|
||||
append(noSim, "no-sim", "false", "false", "", "")).value, 0.001)
|
||||
|
||||
esim := append(labels, "2", "true", "EE")
|
||||
a.InDelta(1, findMetric(t, r.sent, "unifi_mbb_sim_active", esim).value, 0.001)
|
||||
a.InDelta(3013150278176, findMetric(t, r.sent, "unifi_mbb_sim_receive_bytes_total", esim).value, 1)
|
||||
a.InDelta(725650860811, findMetric(t, r.sent, "unifi_mbb_sim_transmit_bytes_total", esim).value, 1)
|
||||
a.InDelta(5230, findMetric(t, r.sent, "unifi_mbb_sim_state_seconds", esim).value, 0.001)
|
||||
a.InDelta(45, findMetric(t, r.sent, "unifi_mbb_sim_network_reject_age", esim).value, 0.001)
|
||||
a.InDelta(1, findMetric(t, r.sent, "unifi_mbb_sim_card_present", esim).value, 0.001)
|
||||
a.InDelta(0, findMetric(t, r.sent, "unifi_mbb_sim_pin_blocked", esim).value, 0.001)
|
||||
a.InDelta(1, findMetric(t, r.sent, "unifi_mbb_sim_data_warning", esim).value, 0.001)
|
||||
a.InDelta(0, findMetric(t, r.sent, "unifi_mbb_sim_data_limited", esim).value, 0.001)
|
||||
a.InDelta(1, findMetric(t, r.sent, "unifi_mbb_sim_info",
|
||||
append(esim, "operational", "wds", "Carrier rejected data")).value, 0.001)
|
||||
append(esim, "operational", "true", "false", "wds", "Carrier rejected data")).value, 0.001)
|
||||
}
|
||||
|
||||
func TestExportUMBBMissingSignalIsSkipped(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var d unifi.UMBB
|
||||
|
||||
require.NoError(t, json.Unmarshal([]byte(`{"adopted": true, "type": "umbb", "name": "U5G Max",
|
||||
"mbb": {"state": "searching", "radio": {"has_coverage": false}}}`), &d))
|
||||
|
||||
r := &fakeReport{}
|
||||
u := testProm()
|
||||
u.UMBB = descUMBB("unifi_")
|
||||
u.exportUMBB(r, &d)
|
||||
|
||||
for _, m := range r.sent {
|
||||
name := prometheusDescName(t, m.Desc)
|
||||
assert.NotContains(t, []string{
|
||||
"unifi_mbb_rsrp_dbm", "unifi_mbb_rsrq_db", "unifi_mbb_snr_db",
|
||||
"unifi_mbb_nr_rsrp_dbm", "unifi_mbb_nr_rsrq_db", "unifi_mbb_nr_snr_db",
|
||||
"unifi_mbb_signal_bars", "unifi_mbb_signal_percent", "unifi_mbb_max_bitrate_bps",
|
||||
}, name, "a reading the modem did not report must not be exported as 0")
|
||||
}
|
||||
|
||||
labels := []string{"umbb", "", "U5G Max", "", ""}
|
||||
assert.InDelta(t, 0, findMetric(t, r.sent, "unifi_mbb_coverage", labels).value, 0.001)
|
||||
}
|
||||
|
||||
func TestExportUMBBUnadoptedIsSkipped(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user