cisco-wnc-exporter

A third-party Prometheus Exporter for Cisco C9800 Wireless Network Controller.

GitHub Tag Test and Build Test Coverage Go Report Card
OpenSSF Best Practices License: MIT

Overview

This exporter allows a prometheus instance to scrape metrics from Cisco Catalyst 9800 Wireless Controllers.

  • šŸ›”ļø Critical State Monitoring: Detects changes such as AP mis-configurations or WLAN enable/disable
  • 🌐 Client Connectivity Tracking: Monitors client signal strength, speed, protocols, traffic and latency
  • šŸ“Š Long-Term Observability: Extends metric retention for historical analysis and wireless trend tracking

It provides pull-based model telemetry as an alternative to standard Streaming Telemetry, leveraging standard RESTCONF APIs to deliver real-time wireless network visibility.

Important

Please enable RESTCONF and HTTPS on the C9800 before using this exporter. Please see:

Quick Start

1. Generate a Basic Auth token

Encode your controller credentials as Base64.

# username:password → Base64
echo -n "admin:your-password" | base64
# Output: YWRtaW46eW91ci1wYXNzd29yZA==

2. Set required environment variables

export WNC_CONTROLLER="wnc1.example.internal"
export WNC_ACCESS_TOKEN="YWRtaW46eW91ci1wYXNzd29yZA=="

3. Run the exporter with Docker

docker run -p 10039:10039 -e WNC_CONTROLLER -e WNC_ACCESS_TOKEN \
  ghcr.io/umatare5/cisco-wnc-exporter
  • -p: Maps container port 10039/tcp to host port 10039/tcp.
  • -e: Passes the environment variables into the container.

Tip

If you prefer using binaries, download them from the release page.

  • Supported Platforms: linux_amd64, linux_arm64, darwin_amd64, darwin_arm64 and windows_amd64

Syntax

NAME:
   cisco-wnc-exporter - Prometheus exporter for Cisco WNC

USAGE:
   cisco-wnc-exporter [global options]

VERSION:
   0.1.0

OPTIONS:
   --dry-run                    Validate configuration without starting the server
   --help, -h                   show help
   --log.format string          Log format (json, text) (default: "json")
   --log.level string           Log level (debug, info, warn, error) (default: "info")
   --version, -v                print the version
   --web.listen-address string  Address to bind the HTTP server to (default: "0.0.0.0")
   --web.listen-port int        Port number to bind the HTTP server to (default: 10039)
   --web.telemetry-path string  Path for the metrics endpoint (default: "/metrics")
   --wnc.access-token string    WNC API access token [$WNC_ACCESS_TOKEN]
   --wnc.cache-ttl duration     WNC API response cache TTL in seconds (default: 55s)
   --wnc.controller string      WNC controller hostname or IP address [$WNC_CONTROLLER]
   --wnc.timeout duration       WNC API request timeout (default: 55s)
   --wnc.tls-skip-verify        Skip TLS certificate verification (default: false)

   # AP Collector Options

   --collector.ap.errors              Enable AP error metrics
   --collector.ap.general             Enable AP general metrics
   --collector.ap.info                Enable AP info metrics
   --collector.ap.info-labels string  Comma-separated list of AP info labels (default: "name,ip")
   --collector.ap.radio               Enable AP radio metrics
   --collector.ap.traffic             Enable AP traffic metrics

   # Client Collector Options

   --collector.client.errors              Enable Client error metrics
   --collector.client.general             Enable Client general metrics
   --collector.client.info                Enable Client info metrics
   --collector.client.info-labels string  Comma-separated list of Client info labels (default: "name,ipv4")
   --collector.client.radio               Enable Client radio metrics
   --collector.client.traffic             Enable Client traffic metrics

   # WLAN Collector Options

   --collector.wlan.config              Enable WLAN config metrics
   --collector.wlan.general             Enable WLAN general metrics
   --collector.wlan.info                Enable WLAN info metrics
   --collector.wlan.info-labels string  Comma-separated list of WLAN info labels (default: "name")
   --collector.wlan.traffic             Enable WLAN traffic metrics

   * Collector Wide Options

   --collector.info-cache-ttl duration  Cache TTL for collector info metrics (default: 30m0s)

   * Internal Collector Options

   --collector.internal.go-runtime  Enable Go runtime metrics collector
   --collector.internal.process     Enable process metrics collector

Configuration

This exporter supports following environment variables:

Environment Variable Description
WNC_CONTROLLER WNC controller hostname or IP address (required)
WNC_ACCESS_TOKEN WNC API access token (required)

Metrics

This exporter collects wireless network metrics from Cisco C9800 WNC using following collectors:

  • AP Collector - For RF foundation and radio performance
  • Client Collector - For user experience quality and connection performance
  • WLAN Collector - For logical SSID performance and parameter checks

All collectors have multiple modules to allow fine-grained control over which metrics to collect.

Important

All collectors are disabled by default to reduce load on both Prometheus and the Cisco C9800 WNC. Because a Cisco C9800 WNC typically manages hundreds or even thousands of APs and clients, selective monitoring is essential to maintain performance and stability.

Cisco C9800 WNC caching (55 seconds)

  • API responses are cached for 55 seconds
  • This TTL is tuned for both Prometheus scrape intervals and AP metric reporting intervals
  • You can adjust it with the --wnc.cache-ttl flag

Prometheus info metrics caching (1800 seconds)

  • Info metrics are cached for 1800 seconds (30 minutes)
  • Wireless clients often roam between APs, causing the ap label to change frequently
  • You can adjust it with the --collector.info-cache-ttl flag

AP Collector

AP collector focus on RF foundation and radio performance.

Module Metric Type Description
general wnc_ap_admin_state Gauge Admin state (0=disabled, 1=enabled)
general wnc_ap_oper_state Gauge Operational state (0=down, 1=up)
general wnc_ap_radio_state Gauge Radio state (0=down, 1=up)
general wnc_ap_config_state Gauge Tag config state (0=valid, 1=invalid)
general wnc_ap_uptime_seconds Gauge AP uptime in seconds
general wnc_ap_cpu_utilization_percent Gauge CPU utilization percentage
general wnc_ap_memory_utilization_percent Gauge Memory utilization percentage
radio wnc_ap_channel_number Gauge Operating channel number
radio wnc_ap_channel_width_mhz Gauge Channel bandwidth (MHz)
radio wnc_ap_tx_power_dbm Gauge Current transmit power (dBm)
radio wnc_ap_tx_power_max_dbm Gauge Maximum TX power capability (dBm)
radio wnc_ap_noise_floor_dbm Gauge Channel noise floor (dBm)
radio wnc_ap_channel_utilization_percent Gauge Channel utilization percentage (CCA)
radio wnc_ap_rx_utilization_percent Gauge RX utilization percentage
radio wnc_ap_tx_utilization_percent Gauge TX utilization percentage
radio wnc_ap_noise_utilization_percent Gauge Noise channel utilization percentage
traffic wnc_ap_clients_total Gauge Associated clients count (calclulated)
traffic wnc_ap_rx_bytes_total Counter Total received bytes (calculated)
traffic wnc_ap_tx_bytes_total Counter Total transmitted bytes (calculated)
traffic wnc_ap_rx_packets_total Counter Total received packets
traffic wnc_ap_tx_packets_total Counter Total transmitted packets
traffic wnc_ap_total_tx_frames_total Counter Total TX frames
traffic wnc_ap_data_rx_frames_total Counter Data RX frames
traffic wnc_ap_data_tx_frames_total Counter Data TX frames
traffic wnc_ap_management_rx_frames_total Counter Management RX frames
traffic wnc_ap_management_tx_frames_total Counter Management TX frames
traffic wnc_ap_control_rx_frames_total Counter Control RX frames (*1)
traffic wnc_ap_control_tx_frames_total Counter Control TX frames (*1)
traffic wnc_ap_multicast_rx_frames_total Counter Multicast RX frames (*1)
traffic wnc_ap_multicast_tx_frames_total Counter Multicast TX frames (*1)
traffic wnc_ap_rts_success_total Counter Successful RTS transmissions (*1)
errors wnc_ap_tx_errors_total Counter Total TX errors (*1)
errors wnc_ap_rx_errors_total Counter Total RX errors (*1)
errors wnc_ap_tx_drops_total Counter Total TX drops (calculated)
errors wnc_ap_tx_retries_total Counter Total TX retries (calculated)
errors wnc_ap_transmission_failures_total Counter Failed transmission attempts (*1) (*2)
errors wnc_ap_duplicate_frames_total Counter Duplicate frames received
errors wnc_ap_fcs_errors_total Counter Frame Check Sequence errors
errors wnc_ap_fragmentation_rx_total Counter RX fragmented packets (*1)
errors wnc_ap_fragmentation_tx_total Counter TX fragmented packets (*1)
errors wnc_ap_rts_failures_total Counter RTS failures (*1)
errors wnc_ap_decryption_errors_total Counter Decryption errors (*1)
errors wnc_ap_mic_errors_total Counter MIC errors (*1)
errors wnc_ap_wep_undecryptable_total Counter WEP undecryptable frames (*1)
errors wnc_ap_coverage_hole_events_total Counter Coverage hole events
errors wnc_ap_last_radar_on_radio_at Gauge Last radar detection unix timestamp
errors wnc_ap_radio_reset_total Counter Radio reset count
*1 Metrics consistently returning zero values on Cisco IOS-XE 17.12.6a with FlexConnect AP

The following metrics consistently return zero values due to implementation limitations:

  • wnc_ap_(tx|rx)_errors_total
  • wnc_ap_control_(rx|tx)_frames_total
  • wnc_ap_decryption_errors_total
  • wnc_ap_fragmentation_(rx|tx)_total
  • wnc_ap_mic_errors_total
  • wnc_ap_multicast_(rx|tx)_frames_total
  • wnc_ap_rts_(success|failures)_total
  • wnc_ap_transmission_failures_total
  • wnc_ap_wep_undecryptable_total

This was verified through direct RESTCONF API access to the live WNC environment:

āÆ curl -sS -k -H "Authorization: Basic $WNC_ACCESS_TOKEN" \
           -H "Accept: application/yang-data+json" \
           "https://$WNC_CONTROLLER/restconf/data/Cisco-IOS-XE-wireless-access-point-oper:access-point-oper-data/radio-oper-stats" \
           | jq '.["Cisco-IOS-XE-wireless-access-point-oper:radio-oper-stats"]'
[
  {
    "ap-mac": "aa:bb:cc:11:22:30",
    "slot-id": 0,
    "aid-user-list": 2,
    "tx-fragment-count": 0,
    "multicast-tx-frame-cnt": 0,
    "failed-count": 0,
    "retry-count": 79738,
    "multiple-retry-count": 0,
    "frame-duplicate-count": 0,
    "rts-success-count": 0,
    "rts-failure-count": 0,
    "ack-failure-count": 0,
    "rx-fragment-count": 0,
    "multicast-rx-frame-cnt": 0,
    "fcs-error-count": 324978,
    "tx-frame-count": 2457172,
    "wep-undecryptable-count": 0,
    "rx-error-frame-count": 0,
    "mac-mic-err-frame-count": 0,
    "rx-mgmt-frame-count": 2084196,
    "rx-ctrl-frame-count": 0,
    "rx-data-frame-count": 3121831,
    "tx-mgmt-frame-count": 2345422,
    "tx-ctrl-frame-count": 0,
    "tx-data-frame-count": 2457172,
    "rx-data-pkt-count": 0,
    "tx-data-pkt-count": 0,
    "noise-floor": 0,
    "ap-radio-stats": {
      "stuck-ts": "1970-01-01T00:00:00+00:00",
      "last-ts": "2025-09-23T10:25:19+00:00",
      "num-radio-stuck-reset": 0
    },
    "mac-decry-err-frame-count": 0
  },
  {
    "ap-mac": "aa:bb:cc:11:22:30",
    "slot-id": 1,
    "aid-user-list": 3,
    "tx-fragment-count": 0,
    "multicast-tx-frame-cnt": 0,
    "failed-count": 0,
    "retry-count": 216205,
    "multiple-retry-count": 0,
    "frame-duplicate-count": 0,
    "rts-success-count": 0,
    "rts-failure-count": 0,
    "ack-failure-count": 0,
    "rx-fragment-count": 0,
    "multicast-rx-frame-cnt": 0,
    "fcs-error-count": 15398676,
    "tx-frame-count": 17767278,
    "wep-undecryptable-count": 0,
    "rx-error-frame-count": 0,
    "mac-mic-err-frame-count": 0,
    "rx-mgmt-frame-count": 19155,
    "rx-ctrl-frame-count": 0,
    "rx-data-frame-count": 6552508,
    "tx-mgmt-frame-count": 4628753,
    "tx-ctrl-frame-count": 0,
    "tx-data-frame-count": 17767278,
    "rx-data-pkt-count": 0,
    "tx-data-pkt-count": 0,
    "noise-floor": 0,
    "ap-radio-stats": {
      "stuck-ts": "1970-01-01T00:00:00+00:00",
      "last-ts": "2025-09-23T10:25:19+00:00",
      "num-radio-stuck-reset": 0
    },
    "mac-decry-err-frame-count": 0
  },
  <snip>
]
*2 Cisco Bug CSCwn96363 - AckFailureCount vs FailedCount

According to Cisco Bug CSCwn96363, there are redundant counters in the wireless statistics:

  • Issue: AckFailureCount always returns 0 and does not increment
  • Root Cause: AckFailureCount and FailedCount represent the same counter
  • Solution: Use FailedCount instead of AckFailureCount for accurate transmission failure statistics

This exporter implements the recommended workaround by using failed-count from the RESTCONF API for the wnc_ap_transmission_failures_total metric.

Tip

info module provides wnc_ap_info contains following labels to join with other metrics:

Labels Description Example Value Default Required
mac AP wireless MAC address aa:bb:cc:dd:ee:f0 Yes Yes
name AP hostname TEST-AP01 Yes No
ip AP IP address 192.168.1.10 Yes No
radio Radio identifier 0, 1, 2 Yes Yes
band Radio band 2.4, 5, 6 No No
model AP model AIR-AP1815I-Q-K9 No No
serial AP serial number FGL1234ABCD No No
sw_version Software version 17.12.5.41 No No
eth_mac Ethernet MAC address aa:bb:cc:00:11:22 No No

Use this info metric to add contextual labels to other metrics in PromQL queries:

wnc_ap_radio_admin_state * on(mac,radio) group_left(name,ip) wnc_ap_info

Client Collector

Client collector focus on user experience quality and connection performance.

Module Metric Type Description
general wnc_client_state Gauge Client state (0-2)
general wnc_client_state_transition_seconds Gauge State transition latency
general wnc_client_power_save_state Gauge Power save state (0=active, 1=save)
radio wnc_client_protocol Gauge 802.11 protocol (5=ac, 6=ax)
general wnc_client_uptime_seconds Gauge Connection duration
radio wnc_client_mcs_index Gauge MCS index (-1=legacy, 0-11)
radio wnc_client_spatial_streams Gauge Spatial streams count
radio wnc_client_speed_mbps Gauge Connection throughput
radio wnc_client_rssi_dbm Gauge Signal strength (dBm)
radio wnc_client_snr_decibels Gauge Signal-to-noise ratio (dB)
traffic wnc_client_rx_bytes_total Counter Received bytes
traffic wnc_client_tx_bytes_total Counter Transmitted bytes
traffic wnc_client_rx_packets_total Counter Received packets
traffic wnc_client_tx_packets_total Counter Transmitted packets
errors wnc_client_retry_ratio_percent Gauge Retry rate percentage
errors wnc_client_tx_retries_total Counter TX retries count (*3)
errors wnc_client_data_retries_total Counter Data retries by mobile station
errors wnc_client_excessive_retries_total Counter Excessive retries count (*3)
errors wnc_client_rts_retries_total Counter RTS retries count (*3)
errors wnc_client_duplicate_received_total Counter Duplicate packets received (*3)
errors wnc_client_tx_drops_total Counter TX drops count
errors wnc_client_decryption_failed_total Counter Decryption failures
errors wnc_client_mic_mismatch_total Counter MIC mismatch errors (*3)
errors wnc_client_mic_missing_total Counter MIC missing errors (*3)
errors wnc_client_policy_errors_total Counter Policy errors (*3)
errors wnc_client_rx_group_counter_total Counter RX group counter (*3)
*3 Client error metrics consistently returning zero values on Cisco IOS-XE 17.12.6a with FlexConnect AP

The following client error metrics consistently return zero values due to implementation limitations:

  • wnc_client_duplicate_received_total
  • wnc_client_excessive_retries_total
  • wnc_client_mic_mismatch_total
  • wnc_client_mic_missing_total
  • wnc_client_policy_errors_total
  • wnc_client_rts_retries_total
  • wnc_client_rx_group_counter_total
  • wnc_client_tx_retries_total

This was verified through direct RESTCONF API access to the live WNC environment:

āÆ curl -sS -k -H "Authorization: Basic $WNC_ACCESS_TOKEN" \
           -H "Accept: application/yang-data+json" \
           "https://$WNC_CONTROLLER/restconf/data/Cisco-IOS-XE-wireless-client-oper:client-oper-data/traffic-stats" \
           | jq '.["Cisco-IOS-XE-wireless-client-oper:traffic-stats"][0]' | \
           jq '{duplicate_rcv, tx_excessive_retries, mic_mismatch, mic_missing, policy_errs, rts_retries, rx_group_counter, tx_retries}'
{
  "duplicate_rcv": "0",
  "tx_excessive_retries": "0",
  "mic_mismatch": "0",
  "mic_missing": "0",
  "policy_errs": "0",
  "rts_retries": "0",
  "rx_group_counter": "0",
  "tx_retries": "0"
}

Tip

info module provides wnc_client_info contains following labels to join with other metrics:

Labels Description Example Value Default Required
mac MAC address aa:bb:cc:12:34:56 Yes Yes
ap Access point identifier TEST-AP01 No No
band Radio band 2.4, 5, 6 No No
wlan WLAN ESSID name labo-wifi No No
name Device Classification Name MacBook Pro (14-inch, 2021) Yes No
username EAP authentication identity john.doe@example.com No No
ipv4 Client IPv4 address 192.168.1.100 Yes No
ipv6 Client IPv6 address 2001:db8::1 No No

Use this info metric to add contextual labels to other metrics in PromQL queries:

wnc_client_state * on(mac) group_left(ap,wlan,name) wnc_client_info

WLAN Collector

WLAN collector focus on logical SSID performance and parameter checks.

Module Metric Type Description
general wnc_wlan_enabled Gauge WLAN status
traffic wnc_wlan_clients_total Gauge Connected clients count (calclulated)
traffic wnc_wlan_rx_bytes_total Counter WLAN received bytes (calclulated)
traffic wnc_wlan_tx_bytes_total Counter WLAN transmitted bytes (calclulated)
config wnc_wlan_auth_psk_enabled Gauge PSK authentication enabled
config wnc_wlan_auth_dot1x_enabled Gauge 802.1x authentication enabled
config wnc_wlan_auth_dot1x_sha256_enabled Gauge 802.1x SHA256 auth enabled
config wnc_wlan_wpa2_enabled Gauge WPA2 support enabled
config wnc_wlan_wpa3_enabled Gauge WPA3 support enabled
config wnc_wlan_session_timeout_seconds Gauge Session timeout duration
config wnc_wlan_load_balance_enabled Gauge Load balancing enabled
config wnc_wlan_11k_neighbor_list_enabled Gauge 802.11k neighbor list enabled
config wnc_wlan_client_steering_enabled Gauge 6GHz client steering enabled
config wnc_wlan_central_switching_enabled Gauge Central switching enabled
config wnc_wlan_central_authentication_enabled Gauge Central authentication enabled
config wnc_wlan_central_dhcp_enabled Gauge Central DHCP enabled
config wnc_wlan_central_association_enabled Gauge Central association enabled

Tip

info module provides wnc_wlan_info contains following labels to join with other metrics:

Labels Description Example Value Default Required
id WLAN identifier 1, 5, 10 Yes Yes
name WLAN ESSID name labo-wifi Yes No

Use this info metric to add contextual labels to other metrics in PromQL queries:

wnc_wlan_enabled * on(id) group_left(name) wnc_wlan_info

Usecase

There are multiple ways to run the exporter, including direct binary execution and Docker containerization.

Exporter Configuration

Visit http://localhost:10039/ to verify the exporter is running.

Basic Usage - No Collectors

The exporter starts without any collectors enabled by default:

$ WNC_CONTROLLER="wnc1.example.internal"
$ WNC_ACCESS_TOKEN="foobarbaz"
$ ./cisco-wnc-exporter
time="2025-04-13T18:50:54Z" level=info msg="Starting the cisco-wnc-exporter on port 10039."

Essential Usage

Enable essential collectors for basic monitoring:

$ WNC_CONTROLLER="wnc1.example.internal"
$ WNC_ACCESS_TOKEN="foobarbaz"
$ ./cisco-wnc-exporter \
    --collector.ap.general --collector.client.general --collector.wlan.general

Complete Usage

For complete monitoring, see .air.toml which enables all collectors with maximum info-labels.

Prometheus Configuration

This section describes how to configure Prometheus to scrape metrics from the controld-exporter.

  1. Add the job config to your Prometheus YAML file using examples/prometheus.yml as a reference.

Example Grafana Dashboard

Development

Commands

The following make commands are available for development and testing:

Command Description
make help Display available targets and requirements
make build Build the binary to ./tmp/cisco-wnc-exporter
make lint Run golangci-lint and tidy go.mod
make test-unit Run unit tests with coverage using gotestsum
make test-unit-coverage Generate HTML coverage report
make clean Remove build artifacts and backup files
make image Build Docker image

Build

The repository includes a ready to use Dockerfile. To build a new Docker image:

make image

This creates an image named ghcr.io/$USER/cisco-wnc-exporter and exposes 10039/tcp.

Release

To release a new version, follow these steps:

  1. Update the version in the VERSION file.
  2. Submit a pull request with the updated VERSION file.

Once the pull request is merged, the GitHub Workflow automatically creates and pushes a new tag, after which I manually publish a release using the GitHub Actions release workflow.

Contribution

  1. Fork (https://github.com/umatare5/cisco-wnc-exporter/fork)
  2. Create a feature branch
  3. Commit your changes
  4. Rebase your local changes against the master branch
  5. Create a new Pull Request

Acknowledgement

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.

Licence

MIT

View code on GitHub

Code Exchange Community

Get help, share code, and collaborate with other developers in the Code Exchange community.View Community
Disclaimer:
Cisco provides Code Exchange for convenience and informational purposes only, with no support of any kind. This page contains information and links from third-party websites that are governed by their own separate terms. Reference to a project or contributor on this page does not imply any affiliation with or endorsement by Cisco.