The Arista VeloCloud SD-WAN monitoring suite for LogicMonitor leverages both the VeloCloud Orchestrator API and SNMP, to deliver detailed monitoring of edge health, link performance, and path reliability. This monitoring suite enables IT operations teams to detect and resolve network issues proactively, optimize SD-WAN performance, and maintain consistent availability across distributed sites. 

Important: The Arista VeloCloud SD-WAN monitoring package replaces the VMware_SDWAN_ modules. Ensure that all previous VMware modules are removed before deployment to prevent data duplication and conflicts.

Requirements for Arista VeloCloud SD-WAN Monitoring

To deploy and operate the Arista VeloCloud SD-WAN monitoring suite, ensure the following:

  • Access to the VeloCloud Orchestrator portal (formatted as vcoX.velocloud.net)
  • API token for the VeloCloud Orchestrator (read-only scope recommended)
  • SNMP enabled on each Edge for link and path metrics (UDP 161 reachable)
  • Network access from the Collector to:
    • VeloCloud Orchestrator over HTTPS (port 443)
    • SNMP (UDP 161)

For NetScan deduplication, ensure the following:

  • LogicMonitor API access ID/Key (lmaccess.id / lmaccess.key or logicmonitor.access.id / logicmonitor.access.key)

Adding Resources into Monitoring

VeloCloud SD-WAN resources can be added into monitoring using the following methods:

Ensure that the necessary properties and credentials are configured for VeloCloud SD-WAN resources to enable proper monitoring and data collection. For more information, see Assigning Properties to Resources.

  1. In LogicMonitor, navigate to > Modules > Exchange > select the VeloCloud SD-WAN LogicModules and install them. 
  2. Navigate to the Resource Tree > Add resourceAdvanced NetScan.
  3. Complete the Add Advanced NetScan form by doing the following:
    1. In the Name field, enter a name to associate with this NetScan. For example, “VeloCloud SD-WAN.”
    2. (Optional) To add a description, use the Description field. 
    3. In the NetScan Group field, add the NetScan Group. 
    4. (Optional) To select a Collector group, use the Collector Group dropdown menu. 
    5. From the Collector dropdown menu, select the Collector to execute the NetScan.
      For more information see, Collector Assignment

      Note: By default, the NetScan assigns new resources to the Collector running the NetScan.

    6. Select “Enhanced Script NetScan” from the Discovery Method dropdownmenu. 
    7. (Optional) To send an email notification upon completion toggle the Send email notification when the scan is finished switch. 
      Add the emails in the Recipients Emails field.  
    8. Select Use custom credentials for this scan in the Resource Credentials section.
    9. Add the following properties that provide the NetScan with the required VeloCloud API credentials and control how and where the NetScan creates and organizes resources:
      PropertyValueRequired
      velocloud.sdwan.orchestrator.hostVeloCloud Orchestrator hostname (for example, vco1.velocloud.net).Yes
      velocloud.sdwan.keyVeloCloud Orchestrator API token.Yes
      velocloud.sdwan.enterprise.idEnterprise ID in the Orchestrator.Yes
      velocloud.sdwan.folder.nameName of the Resource Group the NetScan creates or uses (supports nested folder/folder/folder). For more information, see Resource Group OverviewNo
      velocloud.sdwan.collectorCollector ID for newly added resources.No
      skip.device.dedupeSkips duplicate-device checks (not recommended).No
      lmaccess.id or logicmonitor.access.idLogicMonitor API access ID to prevent duplicate resources. For more information, see LogicMonitor Portal Monitoring.Yes*
      lmaccess.key or logicmonitor.access.keyLogicMonitor API access key to prevent duplicate resources. For more information, see LogicMonitor Portal Monitoring.Yes*
      hostname.sourceSelects the hostname source used by the NetScan. See NetScan Troubleshooting.No

      * Only required if skip.device.dedupe is not set.

      Additional inclusion or exclusion filters can be set (behave like Active Discovery filters). The following device-level properties are automatically discovered and may be used in filters (for example, to exclude by “Not Equal”):
      PropertyValue
      velocloud.sdwan.edge.logical.idVeloCloud Edge Logical ID
      velocloud.sdwan.edge.nameVeloCloud Edge Name
      velocloud.sdwan.serial.numberVeloCloud Edge Serial Number
    10. Select Embed a Groovy script and embed the following VeloCloud SD-WAN Enhanced NetScan script:
/*******************************************************************************
 *  © 2007-2025 - LogicMonitor, Inc. All rights reserved.
 ******************************************************************************/

import com.santaba.agent.groovy.utils.GroovyScriptHelper as GSH
import com.logicmonitor.mod.Snippets
import groovy.json.JsonOutput

def debug = false
def 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
}

// Import any needed credentials or properties that have been set in the UI
// Required properties
def key = props.get("velocloud.sdwan.key") ?: props.get("vmware.sdwan.key")
def enterpriseId = props.get("velocloud.sdwan.enterprise.id", props.get("vmware.sdwan.enterprise.id"))
def orchestratorHost = props.get("velocloud.sdwan.orchestrator.host", props.get("vmware.sdwan.orchestrator.host"))
def logCacheContext = "VeloCloud-SDWAN-${enterpriseId}-${orchestratorHost}"

// Bail out early if we don't have the necessary credentials
if (!key) {
    throw new Exception("Must provide credentials to run this script.  Verify necessary credentials have been provided in NetScan properties.")
}

// Bail out early if we don't have the necessary properties
if (!orchestratorHost) {
    throw new Exception("Must provide velocloud.sdwan.orchestrator.host and velocloud.sdwan.enterprise.id to run this script.  Verify necessary properties have been provided in NetScan properties.")
}

def modLoader = GSH.getInstance(GroovySystem.version).getScript("Snippets", Snippets.getLoader()).withBinding(getBinding())
def debugSnippet = modLoader.load("lm.debug", "1").debugSnippetFactory(out, debug, log, logCacheContext)
def emitSnippet = modLoader.load("lm.emit", "1")
def httpSnippet = modLoader.load("proto.http", "0").httpSnippetFactory(props)
def cacheSnippet = modLoader.load("lm.cache", "0").cacheSnippetFactory(debugSnippet, logCacheContext)
def veloCloudSnippet = modLoader.load("velocloud.sdwan", "0").veloCloudSdwanSnippetFactory(props, debugSnippet, httpSnippet, cacheSnippet)

// Optional properties
def rootFolder = props.get("velocloud.sdwan.folder.name") ?: "VeloCloud SD-WAN"
def collectorId = props.get("velocloud.sdwan.collector")

Boolean skipDeviceDedupe = props.get("skip.device.dedupe", "false").toBoolean()
String hostnameSource = props.get("hostname.source", "")?.toLowerCase()?.trim()

// 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, httpSnippet, debugSnippet)
}

Map sensitiveProps = [
        "velocloud.sdwan.key": key,
]

// Get information about devices that already exist in LM portal
List fields = ["name", "currentCollectorId", "displayName", "systemProperties"]
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) {
        debugSnippet.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.")
        debugSnippet.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 {
        debugSnippet.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)
    }
}

Map lmDevicesByName
if (pathFlag == "all" && !skipDeviceDedupe) {
    // VMware does not provide individual IP addresses, check dedupe by device name
    lmDevicesByName = lmDevices?.collectEntries { device ->
        def sysName = device?.systemProperties?.find { it -> it?.name == "system.sysname" }?.value
        def displayName = device?.systemProperties?.find { it -> it?.name == "system.displayname" }?.value
        [
                ("${sysName ?: displayName}".toString().toLowerCase()): [
                        "name"              : "${device.name}",
                        "displayName"       : (device.displayName),
                        "currentCollectorId": (device.currentCollectorId),
                        "sysName"           : (sysName)
                ]
        ]
    }
}

// Get your data and build your list of resources
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": []
]

def edges = veloCloudSnippet.executeRequest("${veloCloudSnippet.serviceUrlV1}/enterprise/getEnterpriseEdges", "{}")?.response
edges = veloCloudSnippet.jsonSlurper.parseText(edges)
edges?.each { device ->
    String hostName = device.name
    String displayName = device.name
    String displayNameToCheck = "${displayName}".toString().toLowerCase()

    // 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.findPortalDeviceByProperty(hostName, args)
        } else if (pathFlag == "all") {
            deviceMatch = lmDevicesByName?.get(displayNameToCheck)
        }
    }

    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 (deviceMatch.name.toString().toLowerCase() != displayNameToCheck) {
            def collisionInfo = [
                    (hostName): [
                            "Netscan" : [
                                    "hostname": hostName
                            ],
                            "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") {
                hostName = deviceMatch.name
                collectorId = deviceMatch.currentCollectorId
                displayName = deviceMatch.displayName
                deviceMatch = false
                collisionInfo[hostName]["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[hostName]["Resolved"] = true
                displayName = "${displayName - deviceMatch.name}"
                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
        }
    }

    List groupName = ["${rootFolder}"]

    def deviceProps = [
            "velocloud.sdwan.orchestrator.host": orchestratorHost,
            "velocloud.sdwan.edge.logical.id"  : device.logicalId,
            "velocloud.sdwan.edge.name"        : device.name,
    ]

    def serialNumber = device.serialNumber
    if (serialNumber) deviceProps.put("velocloud.sdwan.serial.number", serialNumber)

    deviceProps.putAll(sensitiveProps)

    Map resource = [
            "hostname"   : hostName,    // String
            "displayname": displayName,    // String
            "hostProps"  : deviceProps, // Map<String, String>
            "groupName"  : groupName,   // List<String>
    ]

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

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

// Output validated data in JSON format
// If errors have been made in acceptable/required keys, lm.emit will throw and inform the user
emitSnippet.resource(resources, debug)

// 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) {
        debugSnippet.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 {
        debugSnippet.LMDebug("${duplicateResources["resources"].size()} devices found that were not reported in netscan output.  See LogicMonitor/Agent/logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json for details.")
    }
}

return 0

Warning: Do not edit the script. Edited Enhanced Script NetScans are not supported. If the LogicMonitor-provided script is edited, LogicMonitor Support may require you to overwrite your edits with the supported script if problems arise. The Enhanced Script NetScan may limit resource creation per hour. To create more than the hourly limit, schedule the NetScan to recur until all resources are added.

  1. In the Schedule section, select Run this NetScan on a schedule.
    For dynamic environments, schedule the NetScan to run as frequently as hourly.
  2. Select Save or Save and Run.
    After running the NetScan, review the history for the number of resources added, or for error messages if no resources were created.

Manually Adding Resources

  1. Create a VeloCloud SD-WAN resource group. For more information, see Adding Resource Groups.  
PropertyValue
velocloud.sdwan.orchestrator.hostVeloCloud Orchestrator hostname.
velocloud.sdwan.keyVeloCloud Orchestrator API token.
velocloud.sdwan.enterprise.idVeloCloud Enterprise ID.
system.sysname or system.displaynameEdge name or display name.
velocloud.sdwan.edge.logical.idVeloCloud Edge Logical ID (recommended for precise mapping).
  1. Add the resources to the VeloCloud SD-WAN resource group. 
  2. Add or verify that the following properties are set on each resource under the VeloCloud SD-WAN Resource Group:
PropertyValue
snmp.community / SNMP credentialsRead-only SNMP credentials to collect link/path metrics.

Note: If running the Netscan does not create any resource, review the history for the number of resources added, or for any error messages.

Assigning Properties to Resources

To ensure proper functionality of the VeloCloud SD-WAN LogicModules, assign the following properties to monitored resources. 

PropertyValue
velocloud.sdwan.orchestrator.hostThe VeloCloud Orchestrator portal (for example, vco1.velocloud.net).
velocloud.sdwan.keyThe VeloCloud Orchestrator API token for authentication.
velocloud.sdwan.enterprise.idThe VeloCloud Enterprise ID associated with the organization.
system.sysname or system.displaynameEdge name or display name that matches the Orchestrator configuration.

For more information, see Resource and Instance properties

Recommendation: Do not use administrative accounts for monitoring. Assign read-only API tokens scoped specifically for data collection.

Import LogicModules

Install all VeloCloud SD-WAN LogicModules from the Exchange. For more information, see LogicModules in Package. If these LogicModules are already present, ensure you have the most recent version of each module. 

Troubleshooting

This suite relies on regular polling against the VeloCloud Orchestrator API. If data gaps occur, do the following:

  • Verify that the Orchestrator API is reachable from the Collector and that the API token is valid and unexpired.
  • Confirm SNMP reachability (UDP 161) and correct read-only credentials on each Edge.
  • If duplicate devices display, review NetScan Troubleshooting and ensure hostname.source is set appropriately.
  • If API rate limits are reached, increase poll intervals or stagger polling across Collectors.
  • After onboarding, running Active Discovery on PropertySources and DataSources can expedite topology and performance coverage.

Note: NetScans cannot update system.hostname. NetScans can update display name, custom properties, and group assignments.

NetScan Troubleshooting

The NetScan for this suite can update existing devices in the portal with information retrieved from the VeloCloud API. Duplicate devices can be created when a display name matches but system.hostname differs. To prevent duplicates, the NetScan uses the LogicMonitor API to query existing devices and report conflicts (available in Collector logs).

For more information on retrieving Collector logs, see Syslogs Using the LogicMonitor Collector

Resources in the duplicate-name report are not added unless hostname.source is configured using the following:

  • “lm” or “logicmonitor”–Uses the existing system.hostname in LogicMonitor to update the device. No new device is created.
  • “netscan”–Keeps the NetScan-determined system.hostname and appends - <system.hostname> to the display name to ensure uniqueness for addition.

Note: NetScans cannot change system.hostname.

LogicModules in Package

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

Display NameTypeDescription
VeloCloud APIDataSourceMonitors VeloCloud Orchestrator API usage and responsiveness.
VeloCloud BGP SessionsDataSourceMonitors BGP session status on VeloCloud Edges.
VeloCloud HealthDataSourceMonitors Edge health, uptime, and summary status.
VeloCloud LinksDataSourceUses SNMP to monitor link quality (latency, jitter, packet loss) per ISP circuit.
VeloCloud PathsDataSourceUses SNMP to monitor tunnel paths between Edges and Gateways over given links.
VeloCloud PerformanceDataSourceMonitors throughput and performance metrics.
VeloCloud SDWAN AlertsLogSourceProvides logs and alerts from VeloCloud devices.
VeloCloud_SDWAN_TopologyTopologySourceGenerates VeloCloud SD-WAN network topology.
addCategory_VeloCloudSDWANPropertySourceAdds the VeloCloudSDWANEdge system category and auto-properties.
addERI_VeloCloud_SDWANPropertySourceDiscovers and adds ERIs for VeloCloud resources.

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

Recommendation: Adjust predefined thresholds as needed to meet the unique requirements of your environment. For guidance, see Static Thresholds for Datapoints.

14-day access to the full LogicMonitor platform