The dashboard pulls device data from two on-SFP sources:
diag— Realtek's diagnostic CLI. Always available; no special config.omcicli— the OMCI client that talks to the on-deviceomci_appdaemon. Only invoked when the exporter is started with--enable-omci, which is off by default because of theomci_appwedge documented in QUIRKS.md.
Self-health metrics (gpon_exporter_*, process_*) come from the
exporter process itself, not the SFP. They are listed in README.md
and not duplicated here.
| Probe command | Metric(s) | Dashboard panel(s) |
|---|---|---|
cat /etc/version |
gpon_firmware_info{version} |
Firmware |
cat /proc/stat |
gpon_cpu_seconds_total{mode} |
SFP CPU |
cat /proc/meminfo |
gpon_memory_total_bytes, _free_bytes, _buffers_bytes, _cached_bytes |
SFP RAM |
cat /proc/uptime |
gpon_system_uptime_seconds |
(not charted yet) |
cat /sys/class/net/eth0/address |
gpon_mac_info{mac} |
SFP MAC |
cat /proc/net/dev |
gpon_network_{receive,transmit}_{bytes,packets,errors,dropped}_total{iface} |
LAN throughput, LAN errors and drops, Network traffic by packets |
cat /proc/net/snmp |
gpon_snmp_total{protocol,name}, gpon_tcp_current_established |
TCP retransmit rate, TCP connections, UDP errors |
cat /proc/net/sockstat |
gpon_sockets_used, gpon_tcp_sockets{state}, gpon_udp_sockets_inuse |
TCP connections (time-wait series) |
cat /proc/sys/net/netfilter/nf_conntrack_{count,max} |
gpon_conntrack_entries, gpon_conntrack_max |
Conntrack table fill |
cat /proc/net/igmp |
gpon_igmp_groups{iface} |
IGMP groups joined |
ps |
gpon_device_info{serial_number} |
(parsed from omci_app -s <SN> argv; replaces broken omcicli get sn) |
diag pon get transceiver tx-power |
gpon_tx_power_dbm |
Signal Tx power, Laser Tx (1310 nm) |
diag pon get transceiver rx-power |
gpon_rx_power_dbm |
Signal Rx power, Laser Rx (1490 nm) |
diag pon get transceiver temperature |
gpon_temperature_celsius |
SFP (commercial) SoC temperature, SoC temperature (gauge) |
diag pon get transceiver voltage |
gpon_voltage_volts |
SFP voltage |
diag pon get transceiver bias-current |
gpon_bias_current_amperes |
Bias current |
diag gpon get onu-state |
gpon_onu_state |
ONU state |
diag gpon get alarm-status |
gpon_alarm_los, _lof, _lom, _sf, _sd, _tx_too_long, _tx_mismatch |
Alarms |
diag gpon show counter global active |
gpon_activation_sn_requests_total, gpon_activation_ranging_requests_total |
Activation events (last 1h) |
diag gpon show counter global ds-phy |
gpon_ds_bip_error_bits_total, _blocks_total, gpon_ds_fec_correct_codewords_total, gpon_ds_fec_uncorrectable_codewords_total |
Downstream BIP errors/sec, FEC corrected codewords/sec, FEC uncorrectable codewords/sec |
diag gpon show counter global ds-plm |
gpon_ds_ploam_received_total, _crc_errors_total |
PLOAM messages/sec (DS series) |
diag gpon show counter global ds-bw |
gpon_ds_bwmap_received_total, _crc_errors_total |
Downstream BWMAP/sec |
diag gpon show counter global ds-omci |
gpon_ds_omci_received_total, _processed_total |
OMCI messages/sec |
diag gpon show counter global ds-eth |
gpon_ds_ethernet_unicast_total, _multicast_total, _fcs_errors_total |
Downstream Ethernet frames/sec |
diag gpon show counter global ds-gem |
gpon_ds_gem_non_idle_total |
Downstream GEM frames/sec |
diag gpon show counter global us-plm |
gpon_us_ploam_transmitted_total |
PLOAM messages/sec (US transmitted series) |
| Probe command | Metric(s) | Dashboard panel(s) |
|---|---|---|
| none | none | none |
The default dashboard does not chart any omcicli-sourced metrics. The
exporter still emits the following when --enable-omci is passed; you can
add panels for them if your firmware tolerates the wedge condition (see
QUIRKS.md):
| Probe command | Metric(s) emitted |
|---|---|
omcicli get authuptime |
gpon_pon_uptime_seconds |
omcicli get loidauth |
gpon_loid_auth_status, gpon_loid_auth_attempts, gpon_loid_auth_success |
omcicli get sn |
gpon_device_info{serial_number} |
A handful of diag-sourced metrics are exposed at /metrics but not on
the default dashboard, mostly because they're flat zero or otherwise
uninformative on a healthy link. Listed for completeness:
diag gpon get rogue-sd-cnt:gpon_rogue_sd_too_long_total,gpon_rogue_sd_mismatch_totaldiag gpon show counter global ds-phy(additional):gpon_ds_fec_correct_bits_total,_bytes_total,gpon_ds_superframe_los_total,gpon_ds_plen_fail_total,_correct_totaldiag gpon show counter global ds-plm(additional):gpon_ds_ploam_processed_total,_overflow_total,_unknown_totaldiag gpon show counter global ds-bw(additional):gpon_ds_bwmap_overflow_total,_invalid0_total,_invalid1_totaldiag gpon show counter global ds-omci(additional):gpon_ds_omci_bytes_total,_dropped_total,_crc_errors_totaldiag gpon show counter global ds-eth(additional):gpon_ds_ethernet_multicast_forwarded_total,_multicast_leaked_totaldiag gpon show counter global ds-gem(additional):gpon_ds_gem_idle_total(saturates at 2^32-1, see QUIRKS),_los_total,_over_interleave_total,_mis_packet_length_total,_multi_flow_match_total,gpon_ds_hec_correct_totaldiag gpon show counter global us-phy:gpon_us_boh_totaldiag gpon show counter global us-plm(additional):gpon_us_ploam_processed_total,_urgent_total,_urgent_processed_total,_normal_total,_normal_processed_total,_serial_number_total,_nomsg_totaldiag gpon show counter global us-omci:gpon_us_omci_processed_total,gpon_us_omci_transmitted_total,gpon_us_omci_bytes_totaldiag gpon show counter global us-gem:gpon_us_gem_blocks_total,_bytes_totaldiag gpon show counter global us-dbr:gpon_us_dbr_total
dashboard.json is organised into six rows. Two are collapsed by default
(Attenuation and Collector & Device Info) since they're reference / diagnostic
content; the rest are open.
The dashboard's $instance and $ip template variables filter by scrape
target and SFP IP for multi-device setups. Default time range is now-15m,
which covers about three fetch cycles at the default 5-minute interval.
Static reference content, three side-by-side text panels:
- Common attenuation sources: link-budget cheat sheet (loss per km on 1310/1490 nm, splice loss, connector loss, splitter loss formula). Plus references to UISP's GPON design guide, an ONU registration-state primer, and the EPON/GPON/XG-PON activation paper.
- Tech specs: HSGQ XPON-Stick form factor, electrical, and mode tables.
- Features: wavelengths, GPON/EPON line rates, MSA/DDM compliance, ITU-T standards, laser-class and RoHS marks.
Three stat panels at the top of the dashboard. Designed to answer "is the link healthy right now?" at a glance.
- ONU state: integer 1-7 (O1..O7). 5 (Operation) is the only good
state; anything else means we're not actually carrying traffic.
0is the parser's "I don't recognise this output" sentinel -- read it as "firmware drift", not "state O0". - Alarms: seven cells, one per alarm bit. Green
OK= clear, redRAISED= active. Severity, worst first: Loss of Signal (catastrophic), Loss of Frame / Loss of MAC / Signal Fail (severe), Signal Degraded (warning), TX Too Long / TX Mismatch (config issues). - Activation events (last 1h):
sum(increase(gpon_activation_sn_requests_total[1h])) + sum(increase(gpon_activation_ranging_requests_total[1h])). Thesum()wrapper folds any historical series with a different label set into a single cell -- needed because exporter restarts that change labels leave stale series queryable for the retention window. Healthy operation is zero events; non-zero usually means the OLT bounced you.
Six gauges. Instant readouts of the values that matter for "is this SFP
operating inside spec?". Each gauge uses the metric directly with
legendFormat={{instance}} so multi-device setups label cleanly.
- Laser Tx (1310 nm):
gpon_tx_power_dbm. Factory range 0.5–5.0 dBm. Green inside spec, orange near the edges, red out of spec. - Laser Rx (1490 nm):
gpon_rx_power_dbm. Factory range -27 to -8 dBm. Same colour banding. Below -27 dBm = link is too dark to operate. - SFP voltage:
gpon_voltage_volts. Nominal 3.3 V; thresholds at 3.135 V (-5%) and 3.465 V (+5%). - RAM:
process_resident_memory_bytes. Healthy steady-state is ~50 MiB; thresholds at 100 / 200 MiB flag a leak. - CPU:
100 * rate(process_cpu_seconds_total[5m])-- converts seconds per second to percent of one core. Threshold 5/20% flags a runaway loop. - SoC temperature:
gpon_temperature_celsius. Commercial-temp range 0–70 °C; thresholds at 60 and 80 °C.
The same four optical metrics as the gauges above, but as timeseries to show movement over the dashboard window. Filled lines, smooth interpolation, threshold red/orange zones drawn behind the line so out-of-spec excursions are obvious without checking the y-axis.
- SFP (commercial) SoC temperature: temperature trend.
- Signal Tx power: Tx power trend with threshold bands.
- Signal Rx power: Rx power trend with threshold bands.
- Bias current:
gpon_bias_current_amperes. Climbing bias current with flat or falling Tx power is the textbook laser-aging signature.
All cumulative-counter timeseries with rate(metric_total[15m]). The
counter type and rate-window choices are explained under query
conventions below.
- FEC corrected codewords/sec (
corrected): rate of FEC correction events. Non-zero is normal. - FEC uncorrectable codewords/sec (
uncorrectable): rate of codewords the FEC layer couldn't recover. Non-zero means bit errors are leaking upstream -- a real alert signal. - Downstream BIP errors/sec (
error bits+error blocks): bit interleaved parity error counters. Like FEC uncorrectable, this is the link-quality canary. - Downstream Ethernet frames/sec (
unicast,multicast,FCS errors): three series; the FCS errors series is pinned to a secondary y-axis because it's typically zero against thousands of frames per second on the primary axis. - Downstream BWMAP/sec (
received,CRC errors): bandwidth-map messages from the OLT, plus CRC errors on the same. Same secondary-y-axis treatment for the error series. - PLOAM messages/sec (
DS received,DS CRC errors,US transmitted): PLOAM (Physical Layer Operation, Administration and Maintenance) message counts per direction. CRC errors on secondary y-axis. - Downstream GEM frames/sec (
non-idle frames):gpon_ds_gem_non_idlerate. The idle counter saturates at 2^32-1 so the dashboard charts only the non-idle side as the link-utilisation indicator (see QUIRKS). - OMCI messages/sec (
DS received,DS processed): downstream OMCI management message rate. Healthy steady-state is ~10 msg/s.
Self-metrics about the exporter process, not the SFP. Useful to confirm "is the collector working?" before assuming the SFP is the problem.
- Collector status:
gpon_exporter_up. 1 = last fetch succeeded. - Firmware:
gpon_firmware_info'sversionlabel, displayed vialegendFormat={{version}}. - Last fetch:
time() - gpon_exporter_last_fetch_timestamp-- seconds since the last successful fetch. Red if it grows past one fetch interval. - Daemon uptime:
time() - process_start_time_seconds. - Fetch duration:
avg_over_time(gpon_exporter_fetch_seconds[5m]). Steady-state ~3 s on a healthy link; sustained climb suggests SFP load or link degradation. - Fetch failure rate:
rate(gpon_exporter_fetch_failures_total[15m]). Real Counter, realrate(). Persistently non-zero means the daemon is flapping, not just a one-off.
A few patterns repeat across the dashboard; they're collected here so the panels themselves stay terse.
Every cumulative device counter is exposed as a Prometheus Counter type
with a _total suffix (e.g. gpon_ds_bwmap_received_total). The
exporter's _AbsoluteCounter wrapper translates the device's absolute
running counters into proper Counter.inc(delta) calls, with explicit
counter-reset handling for SFP reboots. Because the metric type is real
Counter, rate() is the right function and there's no "metric might not
be a counter" hint in Grafana.
The 15-minute window matches the 5-minute fetch cadence: each window
covers 2-3 actual fetches, smoothing out the synchronised step pattern a
shorter window would show (every counter on the device updates at the same
moment, once per fetch -- see QUIRKS for the timing detail).
If you change --interval, scale the window proportionally.
Wrapping increase() in sum() collapses any stale time series with a
different label set into a single cell. Without it, an exporter restart
that changes labels (or adds an ip value) leaves the previous series
queryable for the retention window -- and a [1h] lookback scoops both up
and renders them as separate panels. The sum() is safe given the
single-device-per-exporter $instance filter; multi-device setups with
shared exporters would want sum by(ip) instead.
We tried a heatmap reimagining of the Tx/Rx Power panels. Looked great for Tx (some natural variance, distribution visible) and broke ugly on Rx (samples mostly at one identical value, Grafana fills the whole y-range with cells, presenting as a solid block). Threshold-banded timeseries works on any data shape and immediately telegraphs "is the link inside spec?" without reading the y-axis numbers.
Downstream Ethernet frames/sec, Downstream BWMAP/sec, and
PLOAM messages/sec chart traffic counts (thousands/sec) and error counts
(typically 0) on the same panel. Auto-scaling y-axis hides the errors at
the bottom of the chart. The error series is pinned to a secondary y-axis
so a single FCS / CRC error is visible against thousands of unicast
frames per second on the primary axis.
process_* metrics come from prometheus_client and exist for any
exporter Prometheus scrapes, so they need job="gpon_exporter" to
disambiguate. gpon_* metric names are unique to this exporter, so the
filter is redundant noise on those queries and we don't include it.
gpon_activation_sn_requests_total and _ranging_requests_total are flat
zero in healthy operation. A timeseries chart of two flat zero lines is
uninformative. A stat panel ("0 events" green, non-zero red) conveys the
same thing more compactly and gets attention only when something fires.
About 75 metrics in total. The probe → metric → panel mapping above is the source of truth; this section organises the same set by category for readers who want a quick "what kinds of things does this expose?".
All cumulative device counters carry the _total suffix and Prometheus
type counter. Gauges, Info, and the temperature/voltage/optical readouts
do not.
| Metric | What it is |
|---|---|
gpon_temperature_celsius |
SFP SoC temperature |
gpon_voltage_volts |
Supply voltage |
gpon_tx_power_dbm |
Tx optical power |
gpon_rx_power_dbm |
Rx optical power |
gpon_bias_current_amperes |
Laser bias current. Device reports mA; the parser scales to amperes for Prometheus base-unit convention. |
| Metric | What it is |
|---|---|
gpon_onu_state |
1=O1 Initial .. 5=O5 Operation .. 7=O7 Emergency Stop. 0 means the parser didn't recognise the state output (probable firmware change). |
gpon_alarm_los, _lof, _lom, _sf, _sd, _tx_too_long, _tx_mismatch |
Alarm gauges. 1 = raised, 0 = clear. |
| Metric | What it counts |
|---|---|
gpon_ds_bip_error_bits_total |
BIP-8 error bits detected downstream |
gpon_ds_bip_error_blocks_total |
BIP-8 error blocks detected |
gpon_ds_fec_correct_bits_total |
Bits corrected by FEC |
gpon_ds_fec_correct_bytes_total |
Bytes corrected by FEC |
gpon_ds_fec_correct_codewords_total |
Codewords FEC successfully corrected |
gpon_ds_fec_uncorrectable_codewords_total |
Codewords FEC could not recover |
gpon_ds_superframe_los_total |
Superframe-level loss-of-signal events |
gpon_ds_plen_fail_total |
Packet-length validation failures |
gpon_ds_plen_correct_total |
Packet-length validations passed |
| Metric | What it counts |
|---|---|
gpon_ds_ploam_received_total |
PLOAM messages received |
gpon_ds_ploam_crc_errors_total |
PLOAM messages with CRC failures |
gpon_ds_ploam_processed_total |
PLOAM messages handed off to upper layers |
gpon_ds_ploam_overflow_total |
PLOAM queue overflow events |
gpon_ds_ploam_unknown_total |
PLOAM messages of unrecognised type |
| Metric | What it counts |
|---|---|
gpon_ds_bwmap_received_total |
Bandwidth-map messages from the OLT |
gpon_ds_bwmap_crc_errors_total |
BWMAP messages with CRC failures |
gpon_ds_bwmap_overflow_total |
BWMAP queue overflow events |
gpon_ds_bwmap_invalid0_total |
Invalid-type-0 BWMAP entries |
gpon_ds_bwmap_invalid1_total |
Invalid-type-1 BWMAP entries |
| Metric | What it counts |
|---|---|
gpon_ds_omci_received_total |
OMCI messages received |
gpon_ds_omci_bytes_total |
OMCI bytes received |
gpon_ds_omci_processed_total |
OMCI messages processed |
gpon_ds_omci_dropped_total |
OMCI messages dropped |
gpon_ds_omci_crc_errors_total |
OMCI CRC failures |
| Metric | What it counts |
|---|---|
gpon_ds_ethernet_unicast_total |
Unicast frames |
gpon_ds_ethernet_multicast_total |
Multicast frames |
gpon_ds_ethernet_multicast_forwarded_total |
Multicast frames forwarded by the SFP |
gpon_ds_ethernet_multicast_leaked_total |
Multicast frames not forwarded as expected |
gpon_ds_ethernet_fcs_errors_total |
FCS errors |
| Metric | What it counts |
|---|---|
gpon_ds_gem_idle_total |
Idle GEM frames (saturates at 2^32-1, see QUIRKS) |
gpon_ds_gem_non_idle_total |
Data-carrying GEM frames; the link-utilisation indicator |
gpon_ds_gem_los_total |
GEM-level loss-of-signal events |
gpon_ds_gem_over_interleave_total |
GEM over-interleave events |
gpon_ds_gem_mis_packet_length_total |
GEM packet-length mismatches |
gpon_ds_gem_multi_flow_match_total |
GEM multi-flow match events |
gpon_ds_hec_correct_total |
HEC corrections applied |
| Metric | What it counts |
|---|---|
gpon_us_boh_total |
Burst overhead transmitted |
gpon_us_dbr_total |
Dynamic bandwidth report messages |
gpon_us_ploam_transmitted_total |
Upstream PLOAM messages transmitted |
gpon_us_ploam_processed_total |
Upstream PLOAM messages processed |
gpon_us_ploam_urgent_total |
Urgent PLOAM messages |
gpon_us_ploam_urgent_processed_total |
Urgent PLOAM messages processed |
gpon_us_ploam_normal_total |
Normal PLOAM messages |
gpon_us_ploam_normal_processed_total |
Normal PLOAM messages processed |
gpon_us_ploam_serial_number_total |
Serial-number PLOAM messages |
gpon_us_ploam_nomsg_total |
"No message" upstream PLOAM slots |
gpon_us_omci_transmitted_total |
Upstream OMCI messages transmitted |
gpon_us_omci_processed_total |
Upstream OMCI messages processed |
gpon_us_omci_bytes_total |
Upstream OMCI bytes |
gpon_us_gem_blocks_total |
Upstream GEM blocks |
gpon_us_gem_bytes_total |
Upstream GEM bytes |
| Metric | What it counts |
|---|---|
gpon_activation_sn_requests_total |
Serial-number requests from the OLT |
gpon_activation_ranging_requests_total |
Ranging requests from the OLT |
gpon_rogue_sd_too_long_total |
"SD too long" rogue detections |
gpon_rogue_sd_mismatch_total |
"SD mismatch" rogue detections |
| Metric | Type | What it is |
|---|---|---|
gpon_pon_uptime_seconds |
Gauge | Authenticated PON uptime in seconds |
gpon_loid_auth_status |
Gauge | LOID authentication state (1 = authenticated) |
gpon_loid_auth_attempts |
Gauge | LOID authentication attempts |
gpon_loid_auth_success |
Gauge | LOID authentication successes |
gpon_device_info{serial_number=...} |
Info | SFP serial number, exposed as a label |
Sourced from /proc/stat, /proc/meminfo, and /proc/uptime on the
SFP. CPU is exposed as a Counter so rate() gives per-mode utilisation
the same way node_exporter does. Memory is split into the four fields
the vendor web UI uses to compute its "Memory Usage %" number:
100 * (Total - Free - Buffers - Cached) / Total.
| Metric | Type | What it is |
|---|---|---|
gpon_cpu_seconds_total{mode=...} |
Counter | CPU jiffies/HZ on the SFP, by mode (user, nice, system, idle, iowait, irq, softirq). HZ assumed 100 (kernel default for the Realtek MIPS 2.6.30 build). |
gpon_memory_total_bytes |
Gauge | Total RAM (MemTotal from /proc/meminfo, scaled to bytes) |
gpon_memory_free_bytes |
Gauge | Free RAM (MemFree) |
gpon_memory_buffers_bytes |
Gauge | Buffer cache (Buffers) |
gpon_memory_cached_bytes |
Gauge | Page cache (Cached) |
gpon_system_uptime_seconds |
Gauge | Seconds since the SFP booted. Distinct from gpon_pon_uptime_seconds, which is PON authentication uptime. |
gpon_mac_info{mac=...} |
Info | LAN-side MAC of the SFP (eth0), from /sys/class/net/eth0/address. Matches the vendor web UI's "MAC Address" field. |
Sourced from /proc/net/dev. Same shape as node_exporter's
node_network_*_total. The iface label carries the kernel interface
name; on a typical SFU build that's lo, eth0, eth0.2, eth0.3,
nas0, pon0, br0. The dashboard charts only eth0 (= the vendor
web UI's "LAN" row). The PON-side virtuals stay flat-zero on this
firmware.
| Metric | What it counts |
|---|---|
gpon_network_receive_bytes_total{iface} |
RX bytes |
gpon_network_receive_packets_total{iface} |
RX packets |
gpon_network_receive_errors_total{iface} |
RX errors |
gpon_network_receive_dropped_total{iface} |
RX dropped |
gpon_network_transmit_bytes_total{iface} |
TX bytes |
gpon_network_transmit_packets_total{iface} |
TX packets |
gpon_network_transmit_errors_total{iface} |
TX errors |
gpon_network_transmit_dropped_total{iface} |
TX dropped |
Sourced from /proc/net/snmp, /proc/net/sockstat,
/proc/sys/net/netfilter/nf_conntrack_*, and /proc/net/igmp.
| Metric | Type | What it is |
|---|---|---|
gpon_snmp_total{protocol="Ip|Tcp|Udp",name=...} |
Counter | Selected fields from /proc/net/snmp. Tcp: ActiveOpens, PassiveOpens, AttemptFails, EstabResets, InSegs, OutSegs, RetransSegs, InErrs, OutRsts. Ip: ForwDatagrams, InDelivers, OutRequests, ReasmFails. Udp: InDatagrams, NoPorts, InErrors, OutDatagrams, RcvbufErrors, SndbufErrors. |
gpon_tcp_current_established |
Gauge | TCP connections currently in ESTABLISHED state (Tcp.CurrEstab; the only snmp field exposed as a gauge because it's a current-value, not cumulative). |
gpon_sockets_used |
Gauge | Total open sockets across all protocols (sockets: used N). |
gpon_tcp_sockets{state="inuse|orphan|tw|alloc"} |
Gauge | TCP sockets by lifecycle state. tw = TIME_WAIT, climbs on busy hosts. |
gpon_udp_sockets_inuse |
Gauge | UDP sockets currently bound. |
gpon_conntrack_entries |
Gauge | Current entries in the netfilter conntrack table. |
gpon_conntrack_max |
Gauge | Conntrack table size limit (nf_conntrack_max). Dashboard uses 100 * entries / max to chart fill %. |
gpon_igmp_groups{iface} |
Gauge | Number of multicast groups joined per interface. Useful for IPTV diagnostics. |
| Metric | Type | What it is |
|---|---|---|
gpon_exporter_up |
Gauge | 1 if last fetch succeeded, 0 if it failed. Per device. |
gpon_exporter_fetch_seconds |
Gauge | Wall-clock seconds for the last fetch attempt. |
gpon_exporter_last_fetch_timestamp |
Gauge | Unix timestamp of the last successful fetch. |
gpon_exporter_fetch_failures_total |
Counter | Fetch failures since the daemon started. |
gpon_firmware_info{version=...} |
Info | Firmware version captured from the SFP's /etc/version. |
gpon_exporter_info{version=...} |
Info | This exporter's version (pulled from __version__ in the source so it can't drift from CI/Docker pins). |