Cisco Catalyst SD-WAN Monitoring

Last updated on 26 March, 2024

Disclaimer: These modules monitor Cisco Catalyst SD-WAN Edge Routers (vEdge and cEdge), Controllers (vSmart), and Validators (vBond) devices as discrete LogicMonitor Resources and are not in-place upgrades to Cisco vManage (Legacy) modules. For more information, see Cisco SD-WAN Monitoring (Legacy).

LogicMonitor does not support using these Catalyst SD-WAN modules in parallel with the Cisco vManage (Legacy) modules (or other 3rd party monitoring that leverage the Cisco SD-WAN real-time monitoring API). Doing so can negatively affect the performance and reliability of Cisco vManage and cause data gaps across both modules and third-party monitoring solutions. To mitigate risk, follow your organization’s change control processes.

LogicMonitor’s Cisco Catalyst SD-WAN monitoring package leverages the Cisco Catalyst SD-WAN Bulk API for a wide variety of health and performance metrics of Catalyst SD-WAN.

Technology Type

Networking & Firewalls

Technology Notes

These modules use the Cisco Catalyst SD-WAN Bulk API. For more information, see the Bulk API documentation from Cisco.

Recommendation: Use the versions of vManage or IOS that Cisco recommends for your controllers, edges, or IOS XE resources. For more information, see the Cisco Recommended SD-WAN Software Versions for Controllers and WAN Edge Routers documentation from Cisco.

Compatibility

Cisco Catalyst SD-WAN (vManage) version 20.6.3 or later is required for full compatibility with these modules.

A connection to a vManage host’s Bulk API port and credentials with Bulk API access from the collector is required on all monitored resources.

Note: As of 2023 September, LogicMonitor’s Cisco Catalyst SD-WAN package is known to be compatible with version 20.6.3 though 20.10.1.

Setup Requirements

Import the LogicMonitor_Collector_Snippets DataSource to ensure that your collector supports the code in this monitoring suite, or update to EA Collector 32.100 or later.

Satisfy Dependencies

Obtain Credentials

LogicMonitor must provide the appropriate credentials that belong to a Bulk API user account to successfully collect state and statistics data. The cisco.sdwan.user account set by the NetScan on each resource must have the following role permissions or belong to a user group that does:

RoleDescription
Device MonitoringCisco defines this role task feature as “Device Monitoring”, both read and enabled must be true for the group the user is in. For more information, see the User and Group documentation from Cisco.

Ports

8443 is set as the default port for Bulk API access.

Add Resources into Monitoring

You can add Cisco Catalyst SD-WAN resources using Advanced NetScan that uses the Cisco API to discover and create Resources for each monitored Cisco Catalyst SD-WAN resource. For more information, see What is NetScan.

Warning: To avoid overages, verify that you have sufficient LogicMonitor licenses before running the NetScan. In addition, test your NetScan filters to ensure you do not inadvertently add Catalyst SD-WAN devices you do not want to monitor.

Using Advanced NetScan to add Cisco Catalyst SD-WAN Resources

For more information about using Advanced NetScan, see Enhanced Script Netscan.

  1. In your LogicMonitor Portal > Modules > Exchange, install the Cisco Catalyst SD-WAN LogicModules.
  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 SD-WAN”. 
  4. Select the Collector to execute the NetScan.
    By default, the NetScan assigns new resources to the collector running the 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 API credentials and change how and where the NetScan creates and organizes resources:
PropertyValueRequired?
cisco.sdwan.userThe Cisco Catalyst SD-WAN username used to access the Bulk API. If not found, LogicMonitor attempts to use the existing value from vmanage.user if found.The cisco.sdwan.user property is required for the NetScan output and is the preferred user property for the Cisco Catalyst SD-WAN modules.Yes
cisco.sdwan.passThe Cisco Catalyst SDWAN password used to access the Bulk API. The NetScan masks the value of this property. If not found, LogicMonitor tries to use the existing value for vmanage.pass if found.The cisco.sdwan.pass property is required for the NetScan output and is the preferred password property for the Cisco Catalyst SDWAN modules.Yes
cisco.sdwan.vmanagehostThe hostname of the vmanage host to gather Bulk API metrics from (for example, yourvmanagehostname.cisco.com). This must be a hostname that is accessible by the collector that is used to cache Bulk API data.Yes
lmaccess.id OR logicmonitor.access.idA 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
lmaccess.key OR logicmonitor.access.keyA 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
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. For more information, see NetScan Troubleshooting.No
cisco.sdwan.portThe TCP port on which the Cisco Catalyst SDWAN API exposes metrics using the REST Bulk API. This port can be customized, but, by default, the Bulk API uses port 8443. If not found, LogicMonitor attempts to use the existing value for vmanage.port if found.No
cisco.sdwan.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 SDWAN”.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.sdwan.allowedsitesA list of site IDs to be added by the NetScan. This list format must be 100,102,103 to specify what devices contained in allowed site IDs should be added by the NetScan as resources. If this property is not set all site IDs are allowed to be added by the NetScan as resources.No
cisco.sdwan.manager.nameA name label for the vManage being used to access the vManage API. This defaults to “Cisco vManage”.No
cisco.sdwan.statisticslookbackA range of time from 20-60 minutes used for statistics based API calls. The default is 60 minutes.No
cisco.sdwan.sites.csvManual file based settings: By default, new resources created by the NetScan are assigned to the collector that runs the NetScan. As an option to override this you can use this property to tell the NetScan to use a CSV file that you create to override the default behavior and assign devices to your desired collector(s) based on site id and LM Collector names. The CSV file must be stored on the collector that executes the NetScan:Linux: /usr/local/logicmonitor/agent/binWindows: C:\Program Files\LogicMonitor\Agent\bin Header should include (collector ID, region folder, site ID, site name) and the file data should be in a format that follows the header structure.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 may (at their discretion) require you to overwrite your edits with the supported script if problems arise. The Enhanced Script NetScan limits LM Envision Resource creation to <= 600 per hour. To create more than 600 Resources, schedule the NetScan to recur each hour until all resources are added.

/*******************************************************************************
 * © 2007-2023 - 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
def debug = false
// To save collector logs, set to true
Boolean log = false

Integer collectorVersion = AgentVersion.AGENT_VERSION.toInteger()
String vManageHost, vManageName, rootFolder, sites, host, user, pass, csvFile
Integer port


// 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
}

vManageHost = props.get("cisco.sdwan.vmanagehost") // required
def cleanedvManageHost = vManageHost?.replaceAll(":","_") // replace : if found since it's a delimiter
def logCacheContext = "cisco-sdwan" + "_" + cleanedvManageHost// used in keys also

user = props.get("cisco.sdwan.user") // required
pass = props.get("cisco.sdwan.pass") // required

// Check that required properties are present
if (!user || !pass || !vManageHost) {
    throw new Exception("Not all required properties have been configured.  Please update netscan properties to include cisco.sdwan.user, cisco.sdwan.pass, and cisco.sdwan.vmanagehost.")
}

vManageName = props.get("cisco.sdwan.manager.name", "Cisco vManage")
rootFolder = props.get("cisco.sdwan.folder", "Cisco Catalyst SDWAN")
port = (props.get("cisco.sdwan.port", props.get("vmanage.port", "8443"))).toInteger()

// CSV file containing headers "Collector ID", "Region Folder", "Site ID" and "Site Name" to customize collector assignment and group names created
// Save in /usr/local/logicmonitor/agent/bin directory (Linux)
// or C:\Program Files\LogicMonitor\Agent\bin directory (Windows)
csvFile = props.get("cisco.sdwan.sites.csv", null)
String cacheFilename = "Cisco_Catalyst_SDWAN_${vManageHost}_devices"
statisticsLookBack = (props.get("cisco.sdwan.statisticslookback", "60")).toInteger()
String hostnameSource = props.get("hostname.source")?.toLowerCase()?.trim() ?: ""

Map sensitiveProps = [
        "cisco.sdwan.user" : user,
        "cisco.sdwan.pass" : pass,
]

// 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}.")
}

// Convert to map with site ID as key if CSV file was provided
Map siteInfo = (csvFile) ? processCollectorSiteInfoCSV(csvFile) : null

// Configure all required snippets
def modLoader = GSH.getInstance()._getScript("Snippets", Snippets.getLoader()).withBinding(getBinding())
def emit = modLoader.load("lm.emit", "1.1")
def lmDebugSnip = modLoader.load("lm.debug", "1")
def lmDebug = lmDebugSnip.debugSnippetFactory(out, debug, log, logCacheContext)
def cacheSnip = modLoader.load("lm.cache", "0")
def cache = cacheSnip.cacheSnippetFactory(lmDebug, logCacheContext)
def httpSnippet = modLoader.load("proto.http", "0")
def http = httpSnippet.httpSnippetFactory(props)
def lmApiSnippet = modLoader.load("lm.api", "0")
def lmApi = lmApiSnippet.lmApiSnippetFactory(props, http, lmDebug)
def ciscoSDWANSnippet = modLoader.load("cisco.sdwan", "0")
def ciscoSDWAN = ciscoSDWANSnippet.ciscoSDWANSnippetFactory(props, lmDebug, cache, http)

def sessionIdKey = "${vManageHost}:sessionId"
def csrfTokenKey = "${vManageHost}:csrfToken"
def response
def testSessionId = ciscoSDWAN.cache.cacheGet(sessionIdKey)
def siteAllowList = props.get("cisco.sdwan.allowedsites", null)?.tokenize(",")?.collect{ it.trim() }

// Get information about devices that already exist in LM portal
List fields = ["name", "currentCollectorId", "displayName"]
Map args = ["size": 1000, "fields": fields.join(",")]
def lmDevices = lmApi.getPortalDevices(args)

if (testSessionId == null) {
    // Since login and logouts can lock a portal we will only do so if we cannot find data for sessionId in cache.
    ciscoSDWAN.sessionId = ciscoSDWAN.login()
    ciscoSDWAN.csrfToken = ciscoSDWAN.getCSRFtoken()
    response = ciscoSDWAN.getDevice()
}
else {
    // Try to do a getDevice with cached sessionId so we don't lock existing resources
    ciscoSDWAN.sessionId = ciscoSDWAN.cache.cacheGet(sessionIdKey)
    ciscoSDWAN.csrfToken = ciscoSDWAN.cache.cacheGet(csrfTokenKey)
    response = ciscoSDWAN.getDevice()
}

if (response) {
    def devices = ciscoSDWAN.slurper.parseText(response)
    // Set variable to store all resources that will be added by netscan
    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 display names found within LogicMonitor portal wherein hostname in LM does not match hostname in Netscan output.  Refer to documentation for how to resolve name collisions using 'hostname.source' netscan property.",
        "total" : 0,
        "resources" : []
    ]

    // Keep track of region devices that have been added including their associated sites
    List seenSites = []

    // Add each device into monitoring 
    devices?.'data'?.each{ it ->
        if ((siteAllowList?.contains(it.'site-id')) || siteAllowList == null) {
            String ip = it.'system-ip'.toString()
            String displayName = it.'host-name'.toString()

            // Check for existing device in LM portal with this displayName
            def deviceMatch = lmApi.checkExistingDevices(displayName, 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
                }
            }

            String siteId = it.'site-id'.toString()
            String setCategory = null
            String deviceType = it.'device-type'

            if (deviceType.contains("edge")) {
                setCategory = "CiscoSDWANEdge"
            }
            if (deviceType.contains("smart")) {
                setCategory = "CiscoSDWANSmart"
            }
            if (deviceType.contains("bond")) {
                setCategory = "CiscoSDWANBond"
            }

            Map deviceProps = [
                    "cisco.sdwan.user"               : emit.sanitizePropertyValue(ciscoSDWAN.user),
                    "cisco.sdwan.pass"               : pass,
                    "cisco.sdwan.vmanagehost"        : emit.sanitizePropertyValue(ciscoSDWAN.vManageHost),
                    "cisco.sdwan.port"               : emit.sanitizePropertyValue(ciscoSDWAN.port),
                    "cisco.sdwan.device.type"        : emit.sanitizePropertyValue(it.'device-type'),
                    "cisco.sdwan.device.id"          : emit.sanitizePropertyValue(it.'system-ip'),
                    "cisco.sdwan.site.id"            : emit.sanitizePropertyValue(siteId),
                    "cisco.sdwan.statisticslookback" : emit.sanitizePropertyValue(statisticsLookBack),
                    "system.categories"              : setCategory,
            ]

            // Initialize groupName and collectorId, then assign values based on whether info has been provided via CSV
            String groupName
            Integer collectorId
            if (seenSites.contains(siteId)) {
                // If we have site info from the CSV, we can use that to build the user defined group name
                if (siteInfo) {
                    // When data is provided, we build a nested group with the site folder under the region folder
                    groupName = "${siteInfo[siteId].regionFolder}/${siteInfo[siteId].siteName}"
                    collectorId =  siteInfo[siteId].collectorId.toInteger()
                }
                // If not, we'll simply name it with the site ID and not assign collector ID
                else {
                    groupName = "Site ID: ${siteId}"
                }
            }
            // If we haven't seen any info about this site, we'll also simply name it with the site ID and not assign collector ID
            else {
                groupName = "Site ID: ${siteId}"
            }
            if (!(deviceType.contains("manage"))) {
                Map resource = [
                        "hostname"   : ip,
                        "displayname": displayName,
                        "hostProps"  : deviceProps,
                        "groupName"  : ["${rootFolder}/${groupName}"],
                ]
                // Only add the collectorId field to resource map if we found a collector ID above
                if (collectorId) {
                    resource["collectorId"] = collectorId
                    duplicateResources["resources"][displayName]["Netscan"]["collectorId"] = collectorId
                }
                // Only add devices that have not been flagged as a display name device match
                // Name collisions that have been resolved are updated to remove the device match flag
                if (!deviceMatch) {
                    resources.add(resource)
                }
                else {
                    lmDebug.LMDebug("${displayName} skipped due to unresolved name collison.")
                }
            }
        }
    }

    // After building full map of resources, emit complete JSON of discovered devices
    // Enhanced netscan format (requires collector version 32.400+)
    if (resources) {
        // emit.resource(resources, debug) // when we don't need to limit output this can be used.
        emitWriteJsonResources(emit, "catalyst_devices", resources, lmDebug) // limit output to 600
    }

    // Report devices that already exist in LM via log file named after root folder
    if (duplicateResources["resources"].size() > 0) {
        def netscanDupLog = new File("../logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json")
        new File(netscanDupLog.getParent()).mkdirs()
        duplicateResources["total"] = duplicateResources["resources"].size()
        def json = JsonOutput.prettyPrint(JsonOutput.toJson(duplicateResources))
        netscanDupLog.write(json)
        if (hostnameSource) {
            lmDebug.LMDebug("${duplicateResources["resources"].size()} devices found that were resolved with hostname.source=${hostnameSource} in netscan output.  See LogicMonitor/Agent/logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json for details.")
        }
        else {
            lmDebug.LMDebug("${duplicateResources["resources"].size()} devices found that were not reported in netscan output.  See LogicMonitor/Agent/logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json for details.")
        }
    }
}
else {
    lmDebug.LMDebugLog("NETSCAN: No response from device API call. Credential check likely failed or some other communication error occurred.")
    throw new Exception(" No response data from device. Verify credentials and that we get data back from the device endpoint using them.")
}

return 0


/**
* Helper function to process CSV with collector id, site device name, folder, site id and site name info
* @param String filename Filename of the CSV containing user-defined site info
* @return Map siteInfo with site ID as the key with a Map containing the rest of the info as key value pairs
*/
def processCollectorSiteInfoCSV(String filename) {
    // Read file into memory and split into list of lists
    def csv = newFile(filename, "csv")
    def rows = csv.readLines()*.split(",")
    def siteInfo = [:]

    // Verify whether headers are present and contain expected values
    // Sanitize for casing and extra whitespaces while gathering headers
    def maybeHeaders = rows[0]*.toLowerCase()*.trim()
    if (maybeHeaders.contains("collector id") &&
        maybeHeaders.contains("region folder") &&
        maybeHeaders.contains("site id") &&
        maybeHeaders.contains("site name")) {

        Map headerIndices = [:]
        maybeHeaders.eachWithIndex{ val, i ->
            headerIndices[val] = i
        }
        // Index values for headers to ensure we key the correct index regardless of order
        def ci = headerIndices["collector id"]
        def rf = headerIndices["region folder"]
        def si = headerIndices["site id"]
        def sn = headerIndices["site name"]

        // Remove headers from dataset
        def data = rows[1..-1]

        // Build a map of common site names with site ID as key
        data.each{ entry ->
            siteId = entry[si].trim()
            siteInfo[siteId] = [
                    "collectorId"      : entry[ci].trim(),
                    "regionFolder"     : entry[rf].trim(),
                    "siteName"         : entry[sn].trim(),
            ]
        }
    }
    // 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\", \"Region Folder\", \"Site ID\", and \"Site Name\" (case insensitive).  Headers provided: \"${rows[0]}\"")
    }

    return siteInfo
}


/**
 * 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)
}


/**
 * Output resources to stdout and cache any remainders to JSON file on collector disk
 * @param emit Snippet object for lm.emit (requires version 1.0)
 * @param filename String
 * @param resources List<Map> resources to be added from netscan
 * @param lmDebug Snippet object class instantiation of lm.debug (requires version 1.0)
 */
def emitWriteJsonResources(emit, String filename, List<Map> resources, lmDebug) {
    def chunkSize = 600
    def chunk = Math.min(resources.size(), chunkSize)
    lmDebug.LMDebugPrint("Adding ${chunk} devices.")
    // Output resources in chunk size deemed safe by platform team
    emit.resource(resources[0..chunk-1], lmDebug.debug)

    File cacheFile = newFile(filename, "json")
    // If the number of resources is less than or equal to our chunk size, our batching is complete and we can delete the file and exit
    if (resources.size() <= chunk) {
        cacheFile.delete()
        lmDebug.LMDebugPrint("All known devices have been reported.")
        return
    }
    // Remove sensitive properties prior to storing data in cache file; hardcode to true to ensure props are masked regardless of debug mode
    def remainingResources = emit.sanitizeResourceSensitiveProperties(resources, true)
    remainingResources = remainingResources[chunk..-1]
    def jsonRR = JsonOutput.toJson(remainingResources)
    // println JsonOutput.prettyPrint(jsonRR) // Uncomment for debugging purposes if needed

    lmDebug.LMDebug("Caching ${remainingResources.size()} devices to disk to add to portal in upcoming netscan executions.")
    cacheFile.write(jsonRR)
    return
}
  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.

Note: Subsequent NetScan runs will add or move resources or resource groups based on changes in Cisco Catalyst SD-WAN. However, the NetScan does not have the ability to delete resources.

  1. 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 SDWAN device group.
  2. Add the devices to the Cisco Catalyst SDWAN device group.
  3. Add or verify that the following properties and cisco.sdwan.device.id are set on the resource under the Cisco Catalyst SDWAN Resource Group:
PropertyValueRequired
cisco.sdwan.userThe Cisco Catalyst SD-WAN username used to access the Bulk API. If not found, LogicMonitor attempts to use the existing value from vmanage.user if found. The cisco.sdwan.user property is required for the NetScan output and is the preferred user property for the Cisco Catalyst SD-WAN modules. Yes
cisco.sdwan.passThe Cisco Catalyst SDWAN password used to access the Bulk API. The NetScan masks the value of this property. If not found, LogicMonitor tries to use the existing value for vmanage.pass if found. The cisco.sdwan.pass property is required for the NetScan output and is the preferred password property for the Cisco Catalyst SDWAN modules.Yes
cicso.sdwan.vmanagehostThe hostname of the vmanagage host to gather Bulk API metrics from (for example, yourvmanagehostname.cisco.com). This must be a hostname that is accessible by the collector that is used to cache Bulk API data. Yes
  1. Manually adding a cisco.sdwan.device.id property will be needed at each resource.

Note: cisco.sdwan.device.id needs to be the “System IP” that is shown on the devices tab in the vmanage web interface from the vmanagehost and cannot be any other IP address the resource has assigned. For more information, see Cisco’s Systems and Interfaces Configuration Guide.

  1. If the system.categories are edge, smart, or smart devices, use on of the following depending on device type:
  • CiscoSDWANEdge
  • CiscoSDWANSmart
  • CiscoSDWANBond

Import LogicModules

From the LogicMonitor public repository, import all Cisco Catalyst SDWAN LogicModules. For more information, see LogicModules in Package.

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

After the LogicModules are imported, data collection automatically begins.​

Considerations and Warnings

  • Login sessions should persist for 24 hours. Logout requests should be limited so sessions can remain active.
  • The Cisco_Catalyst_SDWAN_API is required to monitor Cisco Catalyst SDWAN resources. This module caches data Bulk API data. LogicMonitor uses a caching design in these modules to limit API calls.
  • 2800 API calls per hour should not be exceeded 100 Concurrent API calls should not be exceeded.
    For more information, see the How to authenticate documentation from Cisco.

Troubleshooting

The following debug and log modes for troubleshooting may stop data collection on some endpoints when in use to prevent hitting API limits:

  • The modules in this package are designed with a debug mode embedded in the data collection scripts.
    If issues occur, enable debug mode by setting the variable debug to “true” to get more information in the output when testing the script in the Collector Debug Facility.
    For more information on testing scripts in the Collector Debug Facility, see Script Troubleshooting.
  • The modules in this package are designed with a log mode embedded in the Active Discovery and data collection scripts. Log mode allows for troubleshooting issues seen during normal active discovery or data collection runs, which can be used to investigate data gaps.
    If issues occur, enable log mode by setting the variable log to “true” to get more information in the collector logs.
    For more information on retrieving collector logs, see Sending logs to LogicMonitor in Collector logging.
  • DataSources print error messages to the console when present. These errors display in the output when manually polling data (from the Raw Data tab that displays from the Resources page for each DataSource). DataSources are designed to handle errors gracefully and attempt to grab any available data even when errors occur.
  • Ensure that the user used for monitoring is in a group that has the device monitoring enabled and has read permissions also enabled for Bulk API data for the user set in cisco.sdwan.user. For more information, see the User Group Operation documentation from Cisco.

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.

Migration from Legacy LogicModules

As of October 2023, LogicMonitor released a new suite of Cisco Catalyst SD-WAN modules. The new DataSources query the Cisco Catalyst SD-WAN Bulk API for data, whereas the LogicMonitor modules for Cisco vManage relied on realtime and per device API calls, and did not support vManage versions 20.4.x or later. Gathering metrics using the Bulk API offers the following advantages:

  • Access to more data
  • Uses Cisco’s recommended method for continuous monitoring using the Bulk API rather than Realtime API and per device calls
  • Less CPU usage on resources that were queried against directly using the Realtime API
  • Less network and interconnect resource usage, which allows DataSources to collect data in less API calls reducing the risk of rate limits
  • Setup and configuration using NetScan
  • Centralized caching of Bulk API data

Disclaimer: These modules monitor Cisco Catalyst SDWAN devices as discrete LogicMonitor Resources and are not in-place upgrades to Cisco vManage modules. For more information, see Cisco SD-WAN Monitoring.

Recommendation: Do not use these modules in parallel with the LogicMonitor Cisco vManage modules (or other third-party monitoring solutions). This can cause Cisco vManage API connection errors and data gaps across both modules and third-party monitoring solutions. To mitigate risk, follow your organization’s change control processes.

If you are currently monitoring Cisco vManage devices using any of these legacy DataSources, you will not experience data loss when importing the new DataSources. DataSource names are changed to eliminate module overwriting. You will collect duplicate data and receive duplicate alerts for as long as both sets of DataSources are active. 

Recommendation: Disable the legacy DataSources after importing the new set of DataSources and confirm that they are working as intended in your environment.

When a DataSource is disabled, it stops querying the host and generating alerts, but maintains all historical data. You can delete the legacy DataSources altogether, but consider this move carefully as all historical data will be lost. For more information on disabling DataSources, see Disabling Monitoring for a DataSource or Instance.

LogicModules in Package

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

Display NameTypeDescription
Catalyst SDWAN APIDataSourceControls data collection for Cisco Catalyst SDWAN package and monitors API usage.
Catalyst SDWAN Control ConnectionsDataSourceMonitors the state of individual control connections.
Catalyst SDWAN Controller InterfacesDataSourceMonitors status of controller interfaces.
Catalyst SDWAN Device HealthDataSourceCollects device health and system status state metrics.
Catalyst SDWAN Network InterfacesDataSourceMonitors Cisco Catalyst SDWAN network interface statistics.
Catalyst SDWAN PerformanceDataSourceMonitors the storage, memory, and CPU performance of Cisco Catalyst SDWAN resources.
Catalyst SDWAN TunnelsDataSourcePer tunnel summary data for throughput data as well as jitter, loss, latency, vQoE, and state.
Cisco_Catalyst_SDWAN_TopologyTopologySourceGenerate Cisco Catalyst SD-WAN topologies based on data from the vManage API.
addCategory_Cisco_Catalyst_SDWANPropertySourceIdentifies Cisco Catalyst SDWAN devices by attempting login with provided credentials. Sets a system category, and auto properties including version number (if version number available) and flag indicating whether CSRF tokens are required.
addERI_Cisco_Catalyst_SDWANPropertySourceDiscover and add ERI’s for Cisco Catalyst SDWAN resources.

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. 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