This exporter allows a Prometheus instance to scrape metrics from Cisco Catalyst 9800 Wireless Controllers.
Please enable RESTCONF and HTTPS on the C9800 before using this exporter. Please see:
Encode your controller credentials as Base64.
# username:password → Base64 echo -n "admin:your-password" | base64 # Output: YWRtaW46eW91ci1wYXNzd29yZA==
export WNC_CONTROLLER="wnc1.example.internal" export WNC_ACCESS_TOKEN="YWRtaW46eW91ci1wYXNzd29yZA=="
docker run -p 10039:10039 -e WNC_CONTROLLER -e WNC_ACCESS_TOKEN \ ghcr.io/umatare5/cisco-wnc-exporter:latest
Tip
If you prefer using binaries, download them from the Release.
Supported Platform: linux_amd64, linux_arm64, darwin_amd64, darwin_arm64 and windows_amd64
cisco-wnc-exporter --help prints every flag, and docs/configuration.md carries the same list.
Each collector is enabled per module:
| Module | Flags |
|---|---|
ap |
--collector.ap.general, .radio, .traffic, .errors, .join, .geolocation, .spectrum, .info |
client |
--collector.client.general, .radio, .traffic, .errors, .info |
wlan |
--collector.wlan.general, .traffic, .config, .info |
controller |
--collector.controller.general |
Caution
--wnc.tls-skip-verify disables TLS certificate verification. Never use it in production.
This exporter reads two environment variables:
| Environment Variable | Description |
|---|---|
WNC_CONTROLLER |
WNC controller hostname or IP address (required) |
WNC_ACCESS_TOKEN |
WNC API access token (required) |
The exporter serves three endpoints:
/ — landing page, which confirms the exporter is running when reached at http://localhost:10039//metrics — metrics endpoint, configurable via --web.telemetry-path/healthz — liveness probe, which returns a static 200 and deliberately ignores WNC reachabilityNote
Reflecting the WNC state in /healthz would let an orchestrator kill the exporter during a controller outage, taking the stale snapshot and the Exporter Health Metrics series down with it.
This exporter collects wireless network metrics from Cisco C9800 WNC using four collectors:
| Collector | Focus |
|---|---|
| AP | RF foundation and radio performance |
| Client | User experience quality and connection performance |
| WLAN | Logical SSID performance and parameter checks |
| Controller | The controller itself metrics such as boot time |
The series a dashboard usually starts from:
| Collector | Metric | Type | Description |
|---|---|---|---|
| AP | wnc_ap_oper_state |
Gauge | Operational state (4=registered) |
| AP | wnc_ap_channel_number |
Gauge | Operating channel number |
| AP | wnc_ap_tx_power_dbm |
Gauge | Current transmit power (dBm) |
| AP | wnc_ap_noise_floor_dbm |
Gauge | Noise on the operating channel (dBm) |
| AP | wnc_ap_channel_utilization_ratio |
Gauge | Channel utilization ratio (CCA), 0-1 |
| AP | wnc_ap_clients |
Gauge | Run-state clients count (calculated) |
| Client | wnc_client_state |
Gauge | Connection state (11=run state) |
| Client | wnc_client_protocol |
Gauge | 802.11 protocol (0=unknown, 1..7) |
| Client | wnc_client_speed_mbps |
Gauge | Negotiated PHY rate (Mbps) |
| Client | wnc_client_rssi_dbm |
Gauge | Signal strength (dBm) |
| Client | wnc_client_snr_decibels |
Gauge | Signal-to-noise ratio (dB) |
| WLAN | wnc_wlan_enabled |
Gauge | WLAN status |
| WLAN | wnc_wlan_clients |
Gauge | Run-state clients count (calculated) |
| Controller | wnc_controller_boot_time_seconds |
Gauge | Unix time of the last boot |
See docs/README.md for the refresh, caching, counter-reset and state semantics every collector shares.
Important
All collectors are disabled by default to reduce load on both Prometheus and the controller, and an exporter with no collector enabled never contacts the controller at all.
--wnc.cache-ttl, so the controller sees one pass of requests per interval.data types the enabled modules need, so a client-only deployment never fetches ap_capwap_data.These series describe the exporter itself rather than the wireless network. They have no module and no collector flag. Without the refresh series a failed refresh produces a successful scrape carrying no series, which no alert can detect.
| Metric | Type | Description |
|---|---|---|
wnc_build_info |
Gauge | Exporter version in the version label, always 1 |
wnc_up |
Gauge | Whether last completed refresh reached WNC |
wnc_refresh_duration_seconds |
Gauge | Duration of the last refresh attempt |
wnc_refresh_success_timestamp_seconds |
Gauge | Start time of the refresh behind the served snapshot |
wnc_refresh_errors_total |
Counter | Fetch failures per data type since start-up |
wnc_refresh_items |
Gauge | Items the last refresh returned per data type |
wnc_refresh_defaults_fallback_total |
Counter | WLAN config fetches that fell back to a plain read |
Important
wnc_up == 1 is not a claim that the data series are present, and up == 1 is not a claim that the controller is reachable. A scrape always returns 200 because it is served from the cached snapshot.
Note
wnc_build_info is registered before any collector, so it is the only series a scrape carries when every collector is disabled.
$ WNC_CONTROLLER="wnc1.example.internal" $ WNC_ACCESS_TOKEN="$(echo -n 'admin:your-password' | base64)" $ ./cisco-wnc-exporter time="2025-04-13T18:50:54Z" level=info msg="Starting the cisco-wnc-exporter on port 10039."
$ WNC_CONTROLLER="wnc1.example.internal" $ WNC_ACCESS_TOKEN="$(echo -n 'admin:your-password' | base64)" $ ./cisco-wnc-exporter \ --collector.ap.general --collector.client.general --collector.wlan.general
For complete monitoring, see .air.toml which enables every collector module with maximum info-labels.
Add the job config to your Prometheus YAML file using examples/prometheus.yml as a reference.
Note
A refresh starts on the first scrape that arrives after --wnc.cache-ttl has elapsed since the previous refresh finished, so the effective refresh period is:
P = scrape_interval * ceil((cache-ttl + R) / scrape_interval)
R — the refresh duration, which wnc_refresh_duration_seconds reports.P — 120s for R over 5s up to 65s, with default --wnc.cache-ttl 55s and scrape_interval 60s.Add the alerting rules to your Prometheus YAML file using examples/prometheus_alert_rules.yml as a reference.
Import examples/grafana_cisco-wnc-exporter-admin-dashboard.json to add admin dashboard.
Tip
See docs/assets/cisco-wnc-exporter-admin-dashboard_full.png for the full capture image of the example.
Import examples/grafana_cisco-wnc-exporter-user-dashboard.json to add user dashboard.
Tip
See docs/assets/cisco-wnc-exporter-user-dashboard_full.png for the full capture image of the example.
See CONTRIBUTING.md for the make targets, the Docker build, the release process and how to open a pull request.
I launched this project with the help of GitHub Copilot Coding Agent, and I am grateful to the global developer community for their contributions to open source projects and public repositories.
MIT. The binary statically links Apache-2.0, MIT and BSD 3-Clause dependencies, whose notices are reproduced in NOTICE and shipped alongside LICENSE in every release archive and container image.
Owner
Contributors
Categories
NetworkingObservabilityProducts
Catalyst Access PointsProgramming Languages
GoLicense
Code Exchange Community
Get help, share code, and collaborate with other developers in the Code Exchange community.View Community