Cisco Catalyst Wireless Access Point Monitoring

Last updated on 22 February, 2024

LogicMonitor’s Cisco Catalyst Center Wireless monitoring package leverages the Cisco Catalyst Center Intent API to monitor discrete Cisco Wireless Access Points and Wireless LAN Controllers (“WLC”). This monitoring package is designed to monitor and alert on vital wireless health and performance metrics.

For more information about the Cisco Catalyst Intent API, see Introduction to Cisco DNA Center REST APIs from Cisco.

Requirements

  • Collector version 32.400 or higher
  • Cisco Catalyst Center username and password with read-only access set on the resource.

Add Resources into Monitoring

Two methods are available for on-boarding Cisco Catalyst Center-managed Wireless Access Points and Wireless Controller resources into monitoring. The Advanced NetScan is the preferred method for on-boarding but resources can be manually added into monitoring as well. For more information on NetScans, see What is a NetScan.

  • (Recommended) Advanced NetScan
    For more information on NetScans, see What is a NetScan.
  • Manually Add Resources

Using Advanced NetScan to add Resources

Resources can be automatically added using an Enhanced NetScan Script. For more information, see Enhanced Script Netscan.

  1. In your LogicMonitor Portal > Modules > Exchange, install the Cisco Catalyst Center modules. For a list of modules, see LogicModules in Package.
  2. Navigate to Resources > Add > Several Devices > Advanced NetScan. The Add Advanced NetScan page is displayed.
  3. Enter a name that you want to associate with this NetScan. For example, “Cisco Catalyst Center Devices”.
  4. Select the Collector to execute the NetScan. For more information, see Collector Assignment in Enhanced Script Netscan.
  5. Select Enhanced Script NetScan from the Method drop-down list.
  6. From the Enhanced Script section, select Device Credentials > Use custom credentials for this scan.
  7. Add the following properties that provide the NetScan with the required Cisco Catalyst Center DNA API credentials and change how and where the NetScan creates and organizes resources:
PropertyValueRequired
cisco.catalyst.center.userREST API username.Yes
cisco.catalyst.center.passREST API password.Yes
cisco.catalyst.center.hostCisco Catalyst Center device IP or FQDN.Yes
lmaccess.id  
or
logicmonitor.access.id
A LogicMonitor API access id to search for duplicate resources in the NetScan prior to adding them to the portal. For more information on portal monitoring, see LogicMonitor Portal Monitoring.Yes

Not required if skip.device.dedupe is set.
lmaccess.key 
or
logicmonitor.access.key
A LogicMonitor API key to search for duplicate resources in the NetScan prior to adding them to the portal. For more information on portal monitoring, see LogicMonitor Portal Monitoring.Yes

Not required if skip.device.dedupe is set.
cisco.catalyst.center.folderThe name of the LogicMonitor Resource Group that this NetScan creates or uses if already existing. The value can be a nested child folder (for example, folder/folder/folder). This defaults to “Cisco Catalyst Center”. This should not be assigned to a dynamic device group as those are not added by NetScans.
For more information, see Device Groups Overview.
No
cisco.catalyst.center.nameThe name of the LogicMonitor Resource Group that this NetScan creates or uses if already existing. This defaults to “Cisco Catalyst Center”.No
hostname.sourceAllows for selection of the source of the hostname that is used by the NetScan. This is useful in preventing duplicate device creation when conflicts are found for existing resources.No
skip.device.dedupeAllows you to skip device deduplication checks, making LogicMonitor API credentials not required.No
cisco.catalyst.center.collector.sites.csvPath of a CSV file that contains: Site Name, Collector ID, and Site Folder Name. CSV file needs to be located under the following path: /usr/local/logicmonitor/agent/bin directory (Linux) or C:\Program Files\LogicMonitor\Agent\bin directory (Windows)No
cisco.catalyst.service.urlURL used to make API calls. Default to “/dna/intent/api/v1”No
cisco.catalyst.center.sitesComma-seperated list of Cisco Catalyst Center site IDs to consider.No
cisco.catalyst.center.devicefamiliesFamily of Cisco Catalyst Center Wireless devices to consider. Default to UNIFIED_AP and WIRELESS_CONTROLLER.No
  1. Select Embed a Groovy script and embed the following script:

Warning: Do not edit the script. Edited Enhanced Script NetScans are not supported. If the LogicMonitor-provided script is edited, LogicMonitor Support can require you to overwrite your edits with the supported script if problems arise.

/*******************************************************************************
 * © 2007-2024 - LogicMonitor, Inc. All rights reserved.
 ******************************************************************************/

import com.logicmonitor.common.sse.utils.GroovyScriptHelper as GSH
import com.logicmonitor.mod.Snippets
import com.santaba.agent.AgentVersion
import java.text.DecimalFormat
import groovy.json.JsonOutput
import groovy.json.JsonSlurper

// To run in debug mode, set to true
Boolean debug = false

// To enable logging, set to true
Boolean log = false

// Set props object based on whether or not we are running inside a netscan or debug console
def props
try {
    hostProps.get("system.hostname")
    props = hostProps
    debug = true  // set debug to true so that we can ensure we do not print sensitive properties
}
catch (MissingPropertyException) {
    props = netscanProps
}

String user = props.get("cisco.catalyst.center.user", props.get("cisco.dna.center.user"))
String pass = props.get("cisco.catalyst.center.pass", props.get("cisco.dna.center.pass"))
String catalystCenterHost = props.get("cisco.catalyst.center.host", props.get("cisco.dna.center.host"))
if (!user) {
    throw new Exception("Must provide cisco.catalyst.center.user to run this script.  Verify necessary credentials have been provided in Netscan properties.")
}
if (!pass) {
    throw new Exception("Must provide cisco.catalyst.center.pass credentials to run this script.  Verify necessary credentials have been provided in Netscan properties.")
}
if (!catalystCenterHost) {
    throw new Exception("Must provide cisco.catalyst.center.host to run this script.  Verify necessary properties have been provided in Netscan properties.")
}

def logCacheContext = "${catalystCenterHost}::cisco-catalyst-center"
Boolean skipDeviceDedupe = props.get("skip.device.dedupe", "false").toBoolean()
String hostnameSource    = props.get("hostname.source", "")?.toLowerCase()?.trim()

Integer collectorVersion = AgentVersion.AGENT_VERSION.toInteger()
 
// Bail out early if we don't have the correct minimum collector version to ensure netscan runs properly
if (collectorVersion < 32400) {
    def formattedVer = new DecimalFormat("00.000").format(collectorVersion / 1000)
    throw new Exception("Upgrade collector running netscan to 32.400 or higher to run full featured enhanced netscan. Currently running version ${formattedVer}.")
}
 
// Bail out early if we don't have the correct minimum collector version to ensure netscan runs properly
if (collectorVersion < 32400) {
    def formattedVer = new DecimalFormat("00.000").format(collectorVersion / 1000)
    throw new Exception("Upgrade collector running netscan to 32.400 or higher to run full featured enhanced netscan. Currently running version ${formattedVer}.")
}

def modLoader = GSH.getInstance()._getScript("Snippets", Snippets.getLoader()).withBinding(getBinding())
def emit        = modLoader.load("lm.emit", "1")
def lmDebugSnip = modLoader.load("lm.debug", "1")
def lmDebug     = lmDebugSnip.debugSnippetFactory(out, debug, log, logCacheContext)
def httpSnip    = modLoader.load("proto.http", "0")
def http        = httpSnip.httpSnippetFactory(props)
def cacheSnip   = modLoader.load("lm.cache", "0")
def cache       = cacheSnip.cacheSnippetFactory(lmDebug, logCacheContext)
// Only initialize lmApi snippet class if customer has not opted out
def lmApi
if (!skipDeviceDedupe) {
    def lmApiSnippet = modLoader.load("lm.api", "0")
    lmApi = lmApiSnippet.lmApiSnippetFactory(props, http, lmDebug)
}
def ciscoCatalystCenterSnip = modLoader.load("cisco.catalyst.center", "0")
def ciscoCatalystCenter     = ciscoCatalystCenterSnip.ciscoCatalystCenterSnippetFactory(props, lmDebug, cache, http)

String orgDisplayname    = props.get("cisco.catalyst.center.name") ?: "Cisco Catalyst Center"
String orgFolder         = props.get("cisco.catalyst.center.folder") ? props.get("cisco.catalyst.center.folder") + "/" : ""
String serviceUrl        = props.get("cisco.catalyst.service.url") ?: "https://${ciscoCatalystCenter.host}/dna/intent/api/v1"
def sitesWhitelist       = props.get("cisco.catalyst.center.sites")?.tokenize(",")?.collect{ it.trim() }
def deviceFamilies       = props.get("cisco.catalyst.center.devicefamilies")?.tokenize(",")?.collect{ it.trim() }
def collectorSitesCSV    = props.get("cisco.catalyst.center.collector.sites.csv")
def collectorSiteInfo
if (collectorSitesCSV) collectorSiteInfo = processCollectorSiteInfoCSV(collectorSitesCSV)

// Get information about devices that already exist in LM portal
List fields = ["name", "currentCollectorId", "displayName"]
Map args = ["size": 1000, "fields": fields.join(",")]
def lmDevices
// But first determine if the portal size is within a range that allows us to get all devices at once
def pathFlag, portalInfo, timeLimitSec, timeLimitMs
if (!skipDeviceDedupe) {
    portalInfo = lmApi.apiCallInfo("Devices", args)
    timeLimitSec = props.get("lmapi.timelimit.sec", "60").toInteger()
    timeLimitMs = (timeLimitSec) ? Math.min(Math.max(timeLimitSec, 30), 120) * 1000 : 60000 // Allow range 30-120 sec if configured; default to 60 sec

    if (portalInfo.timeEstimateMs > timeLimitMs) {
        lmDebug.LMDebugPrint("Estimate indicates LM API calls would take longer than time limit configured.  Proceeding with individual queries by display name for each device to add.")
        lmDebug.LMDebugPrint("\t${portalInfo}\n\tNOTE:  Time limit is set to ${timeLimitSec} seconds.  Adjust this limit by setting the property lmapi.timelimit.sec.  Max 120 seconds, min 30 seconds.")
        pathFlag = "ind"
    }
    else {
        lmDebug.LMDebugPrint("Response time indicates LM API calls will complete in a reasonable time range.  Proceeding to collect info on all devices to cross reference and prevent duplicate device creation.\n\t${portalInfo}")
        pathFlag = "all"
        lmDevices = lmApi.getPortalDevices(args)
    }
}

List<Map> resources = []

def now = new Date()
def dateFormat = "yyyy-MM-dd'T'HH:mm:ss.s z"
TimeZone tz = TimeZone.getDefault()
Map duplicateResources = [
    "date" : now.format(dateFormat, tz),
    "message" : "Duplicate device names and display names, keyed by display name that would be assigned by the netscan, found within LogicMonitor portal.  Refer to documentation for how to resolve name collisions using 'hostname.source' netscan property.",    "total" : 0,
    "total" : 0,
    "resources" : []
]

// Gather data from cache if running in debug otherwise make API requests
def deviceHealth
if (debug) {
    deviceHealth = cache.cacheGet("${ciscoCatalystCenter.host}:deviceHealth")
    if (deviceHealth) {
        deviceHealth = ciscoCatalystCenter.slurper.parseText(deviceHealth).values()
    } else {
        deviceHealth = ciscoCatalystCenter.httpGet("device-health")?.response
        if (deviceHealth) deviceHealth = ciscoCatalystCenter.slurper.parseText(JsonOutput.toJson(deviceHealth))        
    }
} else {
    deviceHealth = ciscoCatalystCenter.httpGet("device-health")?.response
    if (deviceHealth) deviceHealth = ciscoCatalystCenter.slurper.parseText(JsonOutput.toJson(deviceHealth))
}

// Device health data is required; we cannot proceed without it
if (!deviceHealth) {
    throw new Exception("Error occurred during /device-health HTTP GET: ${deviceHealth}.")
}

// Gather data from cache if running in debug otherwise make API requests
def networkDevice
if (debug) {
    networkDevice = cache.cacheGet("${ciscoCatalystCenter.host}:networkDevice")
    if (networkDevice) {
        networkDevice = ciscoCatalystCenter.slurper.parseText(networkDevice).values()
    } else {
        networkDevice = ciscoCatalystCenter.httpGet("network-device")?.response
        if (networkDevice) networkDevice = ciscoCatalystCenter.slurper.parseText(JsonOutput.toJson(networkDevice))        
    }
} else {
    networkDevice = ciscoCatalystCenter.httpGet("network-device")?.response
    if (networkDevice) networkDevice = ciscoCatalystCenter.slurper.parseText(JsonOutput.toJson(networkDevice))
}

// Network device data is required; we cannot proceed without it
if (!networkDevice) {
    throw new Exception("Error occurred during /network-device HTTP GET: ${networkDevice}.")
}

// Gather data from cache if running in debug otherwise make API requests
def sites
if (debug) {
    sites = cache.cacheGet("${ciscoCatalystCenter.host}:site")
    if (sites) {
        sites = ciscoCatalystCenter.slurper.parseText(sites).values()
    } else {
        sites = ciscoCatalystCenter.httpGet("site")?.response
        if (sites) sites = ciscoCatalystCenter.slurper.parseText(JsonOutput.toJson(sites))        
    }
} else {
    sites = ciscoCatalystCenter.httpGet("site")?.response
    if (sites) sites = ciscoCatalystCenter.slurper.parseText(JsonOutput.toJson(sites))
}

// Sites data is required; we cannot proceed without it
if (!sites) {
    throw new Exception("Error occurred during /site HTTP GET: ${sites}.")
}

// Physical topology data is required; we cannot proceed without it
def physicalTopology = ciscoCatalystCenter.httpGet("topology/physical-topology")
if (!physicalTopology) {
    throw new Exception("Error occurred during /topology/physical-topology HTTP GET: ${physicalTopology}.")
}

physicalTopology = ciscoCatalystCenter.slurper.parseText(JsonOutput.toJson(physicalTopology))
def ipToSiteId = [:]
physicalTopology.response.nodes.each { node ->
    def ip = node.ip
    def siteId = node.additionalInfo?.siteid
    if (ip && siteId) ipToSiteId.put(ip, siteId)
}


if (deviceFamilies) {
    deviceHealth = deviceHealth.findAll { deviceFamilies.contains(it.deviceFamily) }
} else {
    deviceHealth = deviceHealth.findAll { it.deviceFamily == "UNIFIED_AP" || it.deviceFamily == "WIRELESS_CONTROLLER" }
}

deviceHealth.each { device ->
    def ip = device.ipAddress
    def displayName = device.name
    def siteInfo = sites.find { it.id == ipToSiteId[device.ipAddress]}
    def siteId = siteInfo?.id
    def siteName = siteInfo?.name
    
    def associatedWlcIp = networkDevice.find { it.managementIpAddress == ip }?.associatedWlcIp

    // Verify this site should be included based on customer whitelist configuration
    if (sitesWhitelist != null && !sitesWhitelist.contains(siteId)) return

    // Check for existing device in LM portal with this displayName; set to false initially and update to true when dupe found
    def deviceMatch = false
    // If customer has opted out of device deduplication checks, we skip the lookups where we determine if a match exists and proceed as false
    if (!skipDeviceDedupe) {
        if (pathFlag == "ind") {
            deviceMatch = lmApi.findPortalDevice(displayName, args)
            if (!deviceMatch) deviceMatch = lmApi.findPortalDeviceByName(ip, args)
        }
        else if (pathFlag == "all") {
            deviceMatch = lmApi.checkExistingDevices(displayName, lmDevices)
            if (!deviceMatch) deviceMatch = lmApi.checkExistingDevicesByName(ip, lmDevices)
        }
    }
    if (deviceMatch) {
        // Log duplicates that would cause additional devices to be created; unless these entries are resolved, they will not be added to resources for netscan output
        if (ip != deviceMatch.name) {
            def collisionInfo = [
                (displayName) : [
                    "Netscan" : [
                        "hostname"    : ip
                    ],
                    "LM" : [
                        "hostname"    : deviceMatch.name,
                        "collectorId" : deviceMatch.currentCollectorId
                    ],
                    "Resolved" : false
                ]
            ]
    
            // If user specified to use LM hostname on display name match, update hostname variable accordingly
            // and flag it as no longer a match since we have resolved the collision with user's input
            if (hostnameSource == "lm" || hostnameSource == "logicmonitor") {
                ip = deviceMatch.name
                deviceMatch = false
                collisionInfo[displayName]["Resolved"] = true
            }
            // If user specified to use netscan data for hostname, update the display name to make it unique
            // and flag it as no longer a match since we have resolved the collision with user's input
            else if (hostnameSource == "netscan") {
                // Update the resolved status before we change the displayName
                collisionInfo[displayName]["Resolved"] = true
                displayName = "${displayName} - ${ip}"
                deviceMatch = false
            }
    
            duplicateResources["resources"].add(collisionInfo)
        }
        // Don't worry about matches where the hostname values are the same
        // These will update via normal netscan processing and should be ignored
        else {
            deviceMatch = false
        }
    }

    // Verify we have minimum requirements for device creation
    if (ip && siteId && siteName) {
        def deviceProps = [
            "cisco.catalyst.center.host"            : catalystCenterHost,
            "cisco.catalyst.center.user"            : user,
            "cisco.catalyst.center.pass"            : pass,
            "cisco.catalyst.center.site"            : emit.sanitizePropertyValue(siteName),
            "cisco.catalyst.center.site.id"         : emit.sanitizePropertyValue(siteId)        ]

        if (device.deviceFamily == "UNIFIED_AP") {
            deviceProps.put("system.categories", "CiscoCatalystAccessPoint")
            deviceProps.put("cisco.catalyst.center.associatedWlcIp", emit.sanitizePropertyValue(associatedWlcIp))
        } else if (device.deviceFamily == "WIRELESS_CONTROLLER") {
            deviceProps.put("system.categories", "CiscoCatalystWLC")
        }

        if (device.location) {
            try {
                def address = device.location.tokenize("/")[-2..-1]?.join(" ")?.tokenize("-")[1..-1]?.join(" ")
                if (address) deviceProps.put("location", address)    
            } catch (Exception e) {
                lmDebug.LMDebugPrint("Exception parsing address: ${e}")
            }
        }

        if (sitesWhitelist != null) deviceProps.put("cisco.catalyst.center.sites", emit.sanitizePropertyValue(sitesWhitelist))

        // Set group and collector ID based on user CSV inputs if provided
        def collectorId
        Map resource
        if (collectorSiteInfo) {
            collectorId = collectorSiteInfo[siteId]["collectorId"]
            def folder      = collectorSiteInfo[siteId]["folder"]
            resource = [
                "hostname"    : ip,
                "displayname" : displayName,
                "hostProps"   : deviceProps,
                "groupName"   : ["${orgFolder}${folder}/${siteName}"],
                "collectorId" : collectorId
            ]
            resources.add(resource)
        } else {
            resource = [
                "hostname"    : ip,
                "displayname" : displayName,
                "hostProps"   : deviceProps,
                "groupName"   : ["${orgFolder}${orgDisplayname}/${siteName}"]
            ]
            resources.add(resource)
        }

        // Only add the collectorId field to resource map if we found a collector ID above
        if (collectorId) {
            resource["collectorId"] = collectorId
            duplicateResources["resources"][displayName]["Netscan"][0]["collectorId"] = collectorId
        }

        if (!deviceMatch) {
            resources.add(resource)
        }
    }
}

lmDebug.LMDebugPrint("Duplicate Resources:")
duplicateResources.resources.each {
    lmDebug.LMDebugPrint("\t${it}")
}

emit.resource(resources, debug)

return 0

/**
 * Processes a CSV with headers collector id, folder, and site
 * @param filename String
 * @return collectorInfo Map with site id as key and Map of additional attributes as value
*/
Map processCollectorSiteInfoCSV(String filename) {
    // Read file into memory and split into list of lists
    def csv = newFile(filename, "csv")
    def rows = csv.readLines()*.split(",")
    def collectorInfo = [:]

    // Verify whether headers are present and expected values
    // Sanitize for casing and extra whitespaces while gathering headers
    def maybeHeaders = rows[0]*.toLowerCase()*.trim()
    if (maybeHeaders.contains("collector id") && maybeHeaders.contains("folder") && maybeHeaders.contains("site")) {
        Map headerIndices = [:]
        maybeHeaders.eachWithIndex{ val, i ->
            headerIndices[val] = i
        }
        // Index values for headers to ensure we key the correct index regardless of order
        def ni = headerIndices["site"]
        def ci = headerIndices["collector id"]
        def fi = headerIndices["folder"]

        // Remove headers from dataset
        def data = rows[1..-1]
        // Build a map indexed by site for easy lookups later
        data.each{ entry ->
            collectorInfo[entry[ni]] = [
                    "collectorId" : entry[ci],
                    "folder"      : entry[fi]
                ]
        }
    }
    // Bail out early if we don't have the expected headers in the provided CSV
    else {
        throw new Exception(" Required headers not provided in CSV.  Please provide \"Collector ID\", \"Network Organization Device Name\", \"Folder Name, \"and Network (case insensitive).  Headers provided: \"${rows[0]}\"")
    }

    return collectorInfo
}

/**
 * Sanitizes filepath and instantiates File object
 * @param filename String
 * @param fileExtension String
 * @return File object using sanitized relative filepath
*/
File newFile(String filename, String fileExtension) {
    // Ensure relative filepath is complete with extension type
    def filepath
    if (!filename.startsWith("./")) {
        filepath = "./${filename}"
    }
    if (!filepath.endsWith(".${fileExtension}")) {
        filepath = "${filepath}.${fileExtension}"
    }

    return new File(filepath)
}
  1. In the Schedule section, select Run this NetScan on a schedule. For dynamic environments, you can schedule the NetScan to run as frequently as hourly.
  2. Select Save or Save & Run.

After running the NetScan, review the history for the number of resources added, or for error messages if the NetScan does not create any resources.

Manually Adding Resources

  1. Create a Cisco Catalyst Center device group. For more information, see Adding Device Groups.
  2. Add the devices to the Cisco Catalyst Center Organization group.
  3. Add or verify that the following properties are set on the resources or under the Cisco Catalyst Center Resource Group. For more information on setting properties, see Resource and Instance Properties.
ProeprtyValue
system.categoryCiscoCatalystAccessPoint or CiscoCatalystWLC.
cisco.catalyst.center.userREST API username.
cisco.catalyst.center.passREST API password.
cisco.catalyst.center.hostThe Cisco Catalyst Center IP
or 
FQDN

Importing LogicModules

From the LogicMonitor public repository, import all Cisco Catalyst Center LogicModules.

If these LogicModules are already present, ensure you have the most recent versions.

After the LogicModules are imported, data collection automatically commences. For more information on importing modules, see LM Exchange.

Troubleshooting

This package relies on collector script cache to continuously retrieve and store data from the Cisco Catalyst Center API module to minimize rate limiting constraints. For more information, refer to Collector Script Caching. If data gaps occur, verify Cisco_Catalyst_Center_API is running successfully and check script cache health in the LogicMonitor_Collector_ScriptCache DataSource.

NetScan Troubleshooting

The NetScan for this suite can update existing devices in the portal to add relevant information retrieved from the Cisco API. It is also possible for the NetScan to create duplicate devices in the portal when a conflict exists where the display name is the same, but the value for system.hostname is different. To ensure devices are updated properly and duplicate devices are not created, this NetScan uses LogicMonitor’s API to query existing devices and report name conflicts discovered. This file can be accessed in the collector logs. For more information on retrieving collector logs, see Collector logging – Sending logs to LogicMonitor.

The devices in this report are not reported to LogicMonitor as part of the NetScan output unless the NetScan has been configured with the property hostname.source. This property allows a user to resolve the name conflicts discovered by selecting the source of the hostname that is used in the NetScan output. Two possible hostname sources are determined by the following values:

  • “lm” or “logicmonitor” Devices with name conflicts use the existing system.hostname in LogicMonitor to ensure a device in the portal is updated using the NetScan. This setting does not create a new device.
  • “netscan” Devices with name conflicts maintain system.hostname determined in the NetScan script and update the display name reported to append - <system.hostname> to ensure it is unique and can be added to the portal. This option is beneficial if you do not have strict naming conventions and have multiple devices with the same display name.

Note: NetScans are not able to update the value of system.hostname. NetScans can update display name, custom properties, and group assignments.

LogicModules in Package

LogicMonitor’s package for Cisco Catalyst Center consists of the following LogicModules. For full coverage, import all of the following LogicModules into your LogicMonitor platform:

LogicMonitor’s package for Cisco Catalyst Center consists of the following LogicModules. For full coverage, import all of the following LogicModules into your LogicMonitor platform:

Display NameTypeDescription
addCategory_Cisco_Catalyst_Center_DevicePropertySourceAdds system category CiscoCatalystAccessPoint or CiscoCatalystWLC and auto properties with relevant device information.
addERI_Cisco_Catalyst_Center_DevicePropertySourceDiscover and add ERI’s for Cisco Catalyst Center resources.
Cisco_Catalyst_Center_TopologyTopologySourceMaps Cisco Catalyst Center topologies.
Cisco_Catalyst_Center_APIDataSourceMonitors Catalyst Center API usage.
Cisco_Catalyst_AccessPointHealthDataSourceHealth of Catalyst Center-managed Access Points.
Cisco_Catalyst_AccessPointPerformanceDataSourcePerformance of Catalyst Center-managed Access Points.
Cisco_Catalyst_AccessPointRadiosDataSourceRadio band monitoring for Catalyst Center-managed Access Points.
Cisco_Catalyst_WLCHealthDataSourceHealth of Catalyst Center-managed Wireless LAN Controllers.
Cisco_Catalyst_WLCPerformanceDataSourcePerformance of Catalyst Center-managed Wireless LAN Controllers.

When setting static datapoint thresholds on the various metrics tracked by this package’s DataSources, LogicMonitor follows the technology owner’s best practice KPI recommendations.

Recommendation: If necessary, adjust these predefined thresholds to meet the unique needs of your environment. For more information on tuning datapoint thresholds, see Tuning Static Thresholds for Datapoints.

In This Article