cisco-ios-xe-wireless-go

A Go SDK for interacting with Cisco Catalyst 9800 Wireless Network Controller.

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

✨️ Key Features

  • 🔧 Developer-Friendly: Seamless YANG model handling with responses consistently in JSON
  • 🚀 Fast Integration: Start in minutes with straightforward setup and clear examples
  • 📊 Broad Coverage: Access most configurations and statistics provided by the WNC
  • 🎯 Type-Safe Operations: Strongly typed Go structs for reliable API calls and responses
  • 📖 Detailed Documentation: Detailed API references, testing guides, and best practices via godoc

📡 Supported Environment

Cisco Catalyst 9800 Wireless Network Controller running on:

  • Cisco IOS-XE 17.12.x - Verified on 17.12.8
  • Cisco IOS-XE 17.15.x - Verified on 17.15.6 (Experimental: Spaces)
  • Cisco IOS-XE 17.18.x - Verified on 17.18.4a (Experimental: URWB, WAT)

📦 Installation

This SDK requires Go 1.27 or newer.

go get github.com/umatare5/cisco-ios-xe-wireless-go

🚀 Quick Start

You have to enable RESTCONF and HTTPS on the C9800 before using this SDK. Please see:

1. Generate a Basic Auth token

Encode your controller credentials as Base64.

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

2. Create a sample application

Use your controller host and token to fetch AP operational data.

package main

import (
    "context"
    "fmt"
    "os"
    "time"

    wnc "github.com/umatare5/cisco-ios-xe-wireless-go"
)

func main() {
    // Load environment variables
    controller := os.Getenv("WNC_CONTROLLER")
    token := os.Getenv("WNC_ACCESS_TOKEN")

    // Create client
    client, err := wnc.NewClient(controller, token,
        wnc.WithTimeout(30*time.Second),
        wnc.WithInsecureSkipVerify(true), // remove for production
    )
    if err != nil {
        fmt.Fprintf(os.Stderr, "Failed to create client: %v\n", err)
        os.Exit(1)
    }

    // Create simple context with timeout
    ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
    defer cancel()

    // Request AP operational data
    apData, err := client.AP().GetOperational(ctx)
    if err != nil {
        fmt.Fprintf(os.Stderr, "AP oper request failed: %v\n", err)
        os.Exit(1)
    }

    // Print AP operational data
    fmt.Printf("Successfully connected! Found %d APs\n",
        len(apData.CiscoIOSXEWirelessAPOperData.CAPWAPData))
}

Caution

The wnc.WithInsecureSkipVerify(true) option disables TLS certificate verification. This should only be used in development environments or when connecting to controllers with self-signed certificates. Never use this option in production environments as it compromises security. Where the controller presents a certificate from a private CA, pass wnc.WithRootCAs(pool) instead: the certificate is then verified rather than unverified.

3. Run the application with environment variables

# Set environment variables
export WNC_CONTROLLER="wnc1.example.internal"
export WNC_ACCESS_TOKEN="YWRtaW46eW91ci1wYXNzd29yZA=="

# Run the application
go run main.go

# result: Successfully connected! Found 2 APs

🌐 API Reference

This SDK provides a client to interact with the Cisco Catalyst 9800 Wireless Network Controller's RESTCONF.

Client Initialization

To create a new client, use the wnc.NewClient function with the controller address and access token.

Parameter Type Description
controller string The hostname or IP address of the WNC.
accessToken string The Base64-encoded Basic Auth token.
options... ...Option Optional client configuration options.

Client Options

There are several options to customize the client behavior. Each argument type is in the package documentation.

Option Default Description
WithTimeout(d) 60s Whole-request timeout
WithResponseHeaderTimeout(d) 5s Header wait timeout
WithTLSHandshakeTimeout(d) 5s TLS handshake wait
WithRootCAs(pool) host roots Trust a private CA
WithClientCertificate(cert) none Present a client cert
WithInsecureSkipVerify(skip) false Skip TLS verify
WithProxy(fn) nil Proxy resolver
WithLogger(l) slog.Default() Structured logger
WithUserAgent(ua) cisco-ios-xe-wireless-go/<version> Custom User-Agent

Request Options

Every read method takes optional GetOption values after ctx, which apply to that single request.

Option Value on the wire Description
WithDefaults(wnc.ReportAll) with-defaults=report-all Adds the leaves in force at their default.
WithDefaults(wnc.Explicit) with-defaults=explicit Adds the leaves a client set to the default.
WithFields(expr) fields=<expr> Returns only the nodes named.
WithDepth(n) depth=<n> Returns the top n levels only.
entries, err := client.WLAN().ListWlanCfgEntries(ctx, wnc.WithDefaults(wnc.ReportAll))

Note

RFC 6243 3.3 is why wnc.Explicit differs from a plain GET, which omits any leaf equal to its default. Scope wnc.ReportAll to the container you need, because on a whole-container read the added leaves accumulate across every nested one. A pruned leaf decodes to zero, so WithFields and WithDepth must name every node the caller reads.

Untyped Requests

Every node this SDK types has an accessor.

For one it does not — a container a later IOS-XE release adds, or an RPC with no typed wrapper — the root client carries untyped methods that share the client's credentials, TLS settings, timeouts and *APIError typing.

Method RESTCONF resource Notes
GetData(ctx, path, opts...) /restconf/data Read with same GetOption
GetDataInto[T](ctx, client, path, opts...) /restconf/data Read into a typed envelope
PostData / PutData / PatchData / DeleteData /restconf/data Edit via fixed call verb
PostRPC(ctx, path, payload) /restconf/operations Invoke RPC
Request(ctx, method, path, payload) either Fallback; carries the status

GetDataInto is the one entry above that validates the envelope, so it takes a T whose outermost tag is the module-qualified node the path reads. It is a function rather than a method because a generic method may not be declared in an interface and is invisible to reflect.

body, err := client.PatchData(ctx, "Cisco-IOS-XE-wireless-wlan-cfg:wlan-cfg-data/wlan-cfg-entries/wlan-cfg-entry=1,demo", payload)

Warning

A []byte or json.RawMessage payload is sent as written once checked for well-formed JSON, and anything else is marshaled. Edit a body read with GetData as bytes, because decoding it into a Go value first rounds a 64-bit number.

Supported Services

Please refer to the Go Reference for the complete reference.

Go Reference

The following table summarizes the supported service APIs and their capabilities.

Legend:

  • ✅️ Supported
  • 🟩 Partial Supported
  • 🟨 Experimental Supported
  • ⬜️ Not Supported
API GetOperational() GetConfig() Other Functions Notes
AFC() ✅️ ⬜️ ⬜️
AP() ✅️ ✅️ 🟩 Issue #47 on 17.15+
APF() ⬜️ ✅️ ⬜️
AWIPS() ✅️ ⬜️ ⬜️ Issue #48 on 17.15+
BLE() ✅️ ⬜️ ⬜️
Client() ✅️ ⬜️ ⬜️
Controller() ⬜️ ⬜️ 🟩
CTS() ⬜️ ✅️ ⬜️
Dot11() ⬜️ ✅️ ⬜️
Dot15() ⬜️ ✅️ ⬜️
Fabric() ⬜️ ✅️ ⬜️
Flex() ⬜️ ✅️ ⬜️
General() ✅️ ✅️ ⬜️
Geolocation() ✅️ ⬜️ ⬜️
Hyperlocation() ✅️ ⬜️ ⬜️
LISP() ✅️ ⬜️ ⬜️
Location() ✅️ ✅️ ⬜️
Mcast() ✅️ ⬜️ ⬜️
MDNS() ✅️ ⬜️ ⬜️
Mesh() ✅️ ✅️ ⬜️
Mobility() ✅️ ⬜️ ⬜️
NMSP() ✅️ ⬜️ ⬜️
Radio() ⬜️ ✅️ ⬜️
RF() ⬜️ ✅️ ⬜️
RFTag() ⬜️ ⬜️ 🟩
RFID() ✅️ ✅️ ⬜️
Rogue() ✅️ ⬜️ ⬜️
RRM() ✅️ ✅️ ⬜️
Site() ✅️ ✅️ ⬜️
SiteTag() ⬜️ ⬜️ 🟩
Spaces() 🟨 ⬜️ ⬜️ Requires 17.15+
URWB() 🟨 🟨 ⬜️ Requires 17.18+
WAT() ⬜️ 🟨 ⬜️ Requires 17.18+
WLAN() ✅️ ✅️ ⬜️
PolicyTag() ⬜️ ⬜️ 🟩

Tip

wtpMac is the same as radioMac. WTP (Wireless Termination Point), defined in RFC 5415 denotes an AP.

🔖 Usecases

Runnable examples are available:

List Operation

Usecase 1: List Associating APs

example/list_aps/main.go lists APs managed by the controller.

Click to show example

❯ go run example/list_aps/main.go

Successfully connected! Found 2 APs

AP Name           | MAC Address         | IP Address       | Status
------------------|---------------------|------------------|-----------------
TEST-AP01         | aa:bb:cc:dd:ee:01   | 192.168.1.11   | registered
TEST-AP02         | aa:bb:cc:dd:ee:02   | 192.168.1.12   | registered

Usecase 2: List Associating Clients

example/list_clients/main.go lists clients associating to wireless networks.

Click to show example

❯ go run example/list_clients/main.go

Successfully connected! Found 17 clients

MAC Address           | IP Address
----------------------|----------------
aa:bb:cc:dd:ee:a1     | 192.168.1.101
aa:bb:cc:dd:ee:a2     | 192.168.1.102
aa:bb:cc:dd:ee:a3     | 192.168.1.103
aa:bb:cc:dd:ee:a4     | 192.168.1.104
<snip>

Usecase 3: List WLANs and BSSIDs

example/list_wlans/main.go lists WLANs and their BSSIDs.

Click to show example

❯ go run example/list_wlans/main.go

Successfully connected! Found 7 WLANs across all APs

AP Name           | AP MAC Address    | Slot | WLAN | BSSID             | SSID
------------------|-------------------|------|------|-------------------|-------------------------
TEST-AP01         | aa:bb:cc:dd:ee:01 |    0 |    1 | aa:bb:cc:dd:ee:b1 | test-wlan
TEST-AP01         | aa:bb:cc:dd:ee:01 |    1 |    2 | aa:bb:cc:dd:ee:b2 | test-psk
TEST-AP01         | aa:bb:cc:dd:ee:01 |    1 |    4 | aa:bb:cc:dd:ee:b3 | test-tls
<snip>

Usecase 4: List AP Neighbors

example/list_neighbors/main.go lists neighboring APs detected by the APs.

Click to show example

❯ go run example/list_neighbors/main.go

Successfully connected! Found 11 AP neighbors

AP Name           | Slot | Neighbor BSSID    | Neighbor SSID          | RSSI  | Channel | Last Heard At
------------------|------|-------------------|------------------------|-------|---------|--------------------------
TEST-AP01         |    0 | aa:bb:cc:dd:ee:f1 | test-rogue-01         |   -20 |      11 | 2024-01-15 10:40:00
TEST-AP01         |    0 | aa:bb:cc:dd:ee:f2 | test-rogue-02         |   -62 |       4 | 2024-01-15 10:41:00
TEST-AP01         |    1 | aa:bb:cc:dd:ee:f3 | test-rogue-03         |   -64 |      36 | 2024-01-15 10:42:00
<snip>

Destructive Operation

Usecase 1: Reset an AP

example/reset_ap/main.go resets a specified AP by its MAC address.

Click to show example

❯ go run example/reset_ap/main.go

=== Access Point Reset Tool ===
WARNING: This tool will restart access points causing service interruption!
Use only in controlled environments with proper authorization.

Target Controller: wnc1.example.internal
Enter AP MAC address (format: xx:xx:xx:xx:xx:xx or xx-xx-xx-xx-xx-xx): aa:bb:cc:dd:ee:01
Target AP MAC: aa:bb:cc:dd:ee:01
This will restart the specified Access Point(s). Type 'YES' to confirm: YES

✓ WNC client created successfully
Executing AP reset for MAC aa:bb:cc:dd:ee:01
WARNING: AP will become unavailable and disconnect all clients during restart...

✓ AP reset command sent successfully for MAC: aa:bb:cc:dd:ee:01
Note: AP is now restarting and will be temporarily unavailable
Clients will need to reconnect after AP restart completes

Usecase 2: Reload a Controller

example/reload_controller/main.go reloads the entire wireless controller.

Click to show example

❯ go run ./example/reload_controller/main.go

=== WNC Controller Reload Tool ===
WARNING: This tool will restart the wireless controller!
Use only in controlled environments with proper authorization.

Target Controller: wnc1.example.internal

This will restart the WNC controller. Type 'YES' to confirm: YES

✓ WNC client created successfully
Executing controller reload with reason: Manual reload via CLI tool at 2024-01-15T10:30:00+09:00
WARNING: Controller will become unavailable during restart...

✓ Controller reload command sent successfully
Note: Controller is now restarting and will be temporarily unavailable
Wait for controller to complete restart before attempting reconnection

Usecase 3: Save the Configuration

example/save_config/main.go copies the running configuration to the startup configuration.

Click to show example

❯ go run example/save_config/main.go

=== WNC Configuration Save Tool ===
WARNING: This tool overwrites the startup configuration and cannot be undone!
Use only in controlled environments with proper authorization.

Target Controller: wnc1.example.internal

This will overwrite the startup configuration. Type 'YES' to confirm: YES

✓ WNC client created successfully
Executing configuration save...

✓ Save running-config successful

📦 Used By

🤝 Contributing

Please read the Contribution Guide before submitting PRs and issues and also see the following documents:

🙏 Acknowledgments

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.

📄 License

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.