Adding Arista AP's to LogicMonitor
Summary: Steve Bamford shared a detailed script for integrating Arista WiFi Access Points into LogicMonitor. The script includes requirements for API integration with Arista CVCue and offers optional settings for configuration. It aims to assist others in creating an Advanced Netscan script by providing clear instructions on necessary properties and the script's functionality.
There was a previous request about this, and I have been working on this my self, so I thought I would share what I have delivered.
Required Custom Parameters.
The Discovery and collection scripts are expecting the following custom properties to be present for each device I have added these at the folder (in our case "Networks") so they cascade down.
AristaWIFI.api.endpoint - This is the endpoint within CV-Cue under advanced setting for configuration and management API integrations.
AristaWIFI.api.key - The API Key for CV-CUE
AristaWIFI.api.token - The API Token for CV-CUE
If these are not provided then the collector and discovery scripts will fail.
NetScan:
I had issues getting the netscan to work in Groovy so wrote a pyhton script to do this which links to Arista CloudVision and LogicMonitor, I have something in my backlog now to migrate this to groovy, the biggest challenge I have is that the HTTP DELETE call in Groovy is expecting data which is not needed for the delete call to close the session. This also uses the Python Core Library we use for all of our LogicMonitor integrations, so sharing is a tad difficult as I would have share the script and our Core Library. If people think this is worth While I would add it in the future.
I set a system category of "AristaWifi" to easily identify Arista AP's for the collection and discovery scripts.
Finally I also include the AP's ID on discovery as this is used to pull the specific AP from CV-CUE as the custom Property "CVcueId".
What Metrics can be collected.
Currently I am collecting CPU and Memory which I have created a Arista WIFI General Metrics Data source, and Arista WIFI SSID Metrics which is collecting the Associated Clients and the SSID State which is whether it is Enabled (1) or Disabled (0), I also have unknown (3) if the information can not be retrieved from the JSON.
Arista Wifi General Metrics:
This data source is configured to use Script as the collection source, below is the script.
Note: The logout is commented out until I can result the http delete issue mentioned above.
/*******************************************************************************
* Arista WIFI Integration with CUE for discovery of the AP's
******************************************************************************/
import com.santaba.agent.groovyapi.http.*
import com.santaba.agent.groovy.utils.GroovyScriptHelper as GSH
import com.logicmonitor.mod.Snippets
import com.santaba.agent.AgentVersion
import java.text.DecimalFormat
import com.santaba.agent.util.Settings
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
// 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 key = props.get("AristaWIFI.api.key")
String token = props.get("AristaWIFI.api.token")
String AristaEndPoint = props.get("AristaWIFI.api.endpoint")
//Get the Arista CV WIFI Node Node Id as discovered in the scanning script and submitted to the device..
String nodeId = props.get("CVcueId")
String clientId = "logicmonitor"
String wifiUrl = "https://"+AristaEndPoint+"/wifi/api/"
if (!key) {
throw new Exception("Must provide AristaWIFI.api.key to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
if (!token) {
throw new Exception("Must provide AristaWIFI.api.token credentials to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
if (!clientId) {
throw new Exception("Must provide AristaWIFI.api.clientId credentials to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
//def logCacheContext = "${org}::arista-wifi-cloud"
//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}.")
}
httpClient = HTTP.open(AristaEndPoint,443);
httpClient.setHTTPProxy('[YOUR Proxy]',8080);
// Log in to arista
loginurl = "https://"+AristaEndPoint+"/wifi/api/session"
payloadstring = '{"type":"apiKeycredentials","keyId":"'+key+'","keyValue":"'+token+'","timeout":129,"clientIdentifier": "'+clientId+'"}';
def payload = payloadstring;
//println payload;
def loginResponse = httpClient.post(loginurl,payload,["Content-Type":"application/json"]);
if ( !(httpClient.getStatusCode() =~ /20/) )
{
// Error has occured
println "Authentication Failure";
println httpClient.getStatusCode();
println loginResponse;
return(1);
}
String RawCookie = httpClient.getHeader("Set-Cookie")
//Have Kept but as yet not needed will remove if not needed..
String httpResponseBody = httpClient.getResponseBody()
//Retrieve the Cookie to use from the header.
AristaCookie = RawCookie.split(';')[0]
//Retrieve AP data.
deviceURL = 'manageddevices/aps?startindex=0&pagesize=1000&locationid=0&nodeid=0&boxid='+nodeId
SendUrl = wifiUrl+deviceURL
def header = ["Content-Type":"application/json","Cookie":AristaCookie]
String deviceResponse = httpClient.get(SendUrl,header)
if ( !(httpClient.getStatusCode() =~ /200/))
{
println "Failed to retrieve data "+httpClient.getStatusCode
return 1
}
String deviceBody = httpClient.getResponseBody()
def deviceJson = new JsonSlurper().parseText(deviceBody)
def healthStats = deviceJson.managedDevices.healthStats
def wildvalue = ""
Integer rawminCPU = healthStats[0].minCpuUtilization
Integer minCpu = rawminCPU/100
Integer rawavgCPU = healthStats[0].avgCpuUtilization
Integer avgCpu = rawavgCPU/100
Integer rawmaxCPU = healthStats[0].maxCpuUtilization
Integer maxCpu = rawmaxCPU/100
Integer rawminMem = healthStats[0].minMemoryUtilization
Integer minMem = rawminCPU/100
Integer rawAvgMem = healthStats[0].avgMemoryUtilization
Integer avgMem = rawAvgMem/100
Integer rawmaxMem = healthStats[0].maxMemoryUtilization
Integer maxMem = rawmaxCPU/100
println "${wildvalue}.mincpu=${minCpu}"
println "${wildvalue}.avgcpu=${avgCpu}"
println "${wildvalue}.maxcpu=${maxCpu}"
println "${wildvalue}.minMem=${minMem}"
println "${wildvalue}.avgMem=${avgMem}"
println "${wildvalue}.maxMem=${maxMem}"
// Commented out log out process for now as will need to revisit currently reducing Timeout to 5 minutes for the session.
/* String cookieString = '"'+AristaCookie+'"'
def logoutheaders = ["Content-Type":"application/json","Cookie":AristaCookie]
println logoutheaders
def logoutResponse = httpClient.delete(loginurl,"{}",logoutheaders)
logoutResult = httpClient.getStatusCode()
if ( !(httpClient.getStatusCode() =~ /20/) )
{
//Error has occured
println "Logout Failure";
println httpClient.getStatusCode();
println loginResponse;
return(1);
}
println logoutResponse;*/
def LMDebugPrint(message) {
if (debug) {
println(message.toString())
}
}
return 0Arista WIFI SSID Metrics:
This will collect the number of associated clients by Band (IE 2.4ghz) and SSID I have the created graphs to aggregate the data together for total number of associated clients.
Discovery Script:
/*******************************************************************************
* Arista WIFI Integration with CUE for discovery of the AP's
******************************************************************************/
import com.santaba.agent.groovyapi.http.*
import com.santaba.agent.groovy.utils.GroovyScriptHelper as GSH
import com.logicmonitor.mod.Snippets
import com.santaba.agent.AgentVersion
import java.text.DecimalFormat
import com.santaba.agent.util.Settings
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
// 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 key = props.get("AristaWIFI.api.key")
String token = props.get("AristaWIFI.api.token")
String AristaEndPoint = props.get("AristaWIFI.api.endpoint")
//Get the Arista CV WIFI Node Node Id as discovered in the scanning script and submitted to the device..
String nodeId = props.get("CVcueId")
String clientId = "logicmonitor"
String wifiUrl = "https://"+AristaEndPoint+"/wifi/api/"
if (!key) {
throw new Exception("Must provide AristaWIFI.api.key to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
if (!token) {
throw new Exception("Must provide AristaWIFI.api.token credentials to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
if (!clientId) {
throw new Exception("Must provide AristaWIFI.api.clientId credentials to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
//def logCacheContext = "${org}::arista-wifi-cloud"
//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}.")
}
httpClient = HTTP.open(AristaEndPoint,443);
httpClient.setHTTPProxy('[Your Proxy]',8080);
// Log in to arista
loginurl = "https://"+AristaEndPoint+"/wifi/api/session"
payloadstring = '{"type":"apiKeycredentials","keyId":"'+key+'","keyValue":"'+token+'","timeout":129,"clientIdentifier": "'+clientId+'"}';
def payload = payloadstring;
//println payload;
def loginResponse = httpClient.post(loginurl,payload,["Content-Type":"application/json"]);
if ( !(httpClient.getStatusCode() =~ /20/) )
{
// Error has occured
println "Authentication Failure";
println httpClient.getStatusCode();
println loginResponse;
return(1);
}
String RawCookie = httpClient.getHeader("Set-Cookie")
//Have Kept but as yet not needed will remove if not needed..
String httpResponseBody = httpClient.getResponseBody()
//Retrieve the Cookie to use from the header.
AristaCookie = RawCookie.split(';')[0]
//Retrieve AP data.
deviceURL = 'manageddevices/aps?startindex=0&pagesize=1000&locationid=0&nodeid=0&boxid='+nodeId
SendUrl = wifiUrl+deviceURL
def header = ["Content-Type":"application/json","Cookie":AristaCookie]
String deviceResponse = httpClient.get(SendUrl,header)
if ( !(httpClient.getStatusCode() =~ /200/))
{
println "Failed to retrieve data "+httpClient.getStatusCode
return 1
}
String deviceBody = httpClient.getResponseBody()
def encoded_instance_props_array = []
def deviceJson = new JsonSlurper().parseText(deviceBody)
def wildvalue = ""
def wildalias = ""
def description = ""
def getOperatingBand = ""
def getSSID = ""
def radios = deviceJson.managedDevices.radios
radios[0].each { RadioEntry ->
// Build out wireless entries.
getOperatingBand = RadioEntry.operatingBand
RadioEntry.wirelessInterfaces.each { ssidEntry ->
getssid = ssidEntry.ssid
description = ssidEntry.bssid
wildvalue = getOperatingBand+"_"+getssid
wildalias = wildvalue
def instance_props = [
"auto.opertating.band": RadioEntry.operatingBand,
"auto.ssid": ssidEntry.ssid,
"auto.bssid":ssidEntry.bssid,
"auto.ssid.profileId":ssidEntry.ssidProfileId
]
encoded_instance_props_array = instance_props.collect() { property, value ->
URLEncoder.encode(property.toString()) + "=" +
URLEncoder.encode(value.toString())
}
println "${wildvalue}##${wildalias}##${description}####${encoded_instance_props_array.join("&")}"
}
}
// Commented out log out process for now as will need to revisit currently reducing Timeout to 5 minutes for the session.
/* String cookieString = '"'+AristaCookie+'"'
def logoutheaders = ["Content-Type":"application/json","Cookie":AristaCookie]
println logoutheaders
def logoutResponse = httpClient.delete(loginurl,"{}",logoutheaders)
logoutResult = httpClient.getStatusCode()
if ( !(httpClient.getStatusCode() =~ /20/) )
{
//Error has occured
println "Logout Failure";
println httpClient.getStatusCode();
println loginResponse;
return(1);
}
println logoutResponse;*/
def LMDebugPrint(message) {
if (debug) {
println(message.toString())
}
}
return 0Collection Script:
/*******************************************************************************
* Arista WIFI Integration with CUE for discovery of the AP's
******************************************************************************/
import com.santaba.agent.groovyapi.http.*
import com.santaba.agent.groovy.utils.GroovyScriptHelper as GSH
import com.logicmonitor.mod.Snippets
import com.santaba.agent.AgentVersion
import java.text.DecimalFormat
import com.santaba.agent.util.Settings
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
// 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 key = props.get("AristaWIFI.api.key")
String token = props.get("AristaWIFI.api.token")
String AristaEndPont = props.get("AristaWIFI.api.endpoint")
//Get the Arista CV WIFI Node Node Id as discovered in the scanning script and submitted to the device..
String nodeId = props.get("CVcueId")
String clientId = "logicmonitor"
String wifiUrl = "https://"+AristaEndPont+"/wifi/api/"
if (!key) {
throw new Exception("Must provide AristaWIFI.api.key to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
if (!token) {
throw new Exception("Must provide AristaWIFI.api.token credentials to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
if (!clientId) {
throw new Exception("Must provide AristaWIFI.api.clientId credentials to run this script. Verify necessary credentials have been provided in Netscan properties.")
}
if (!AristaEndPont) {
throw new Exception("Must provide AristaWIFI.api.endpoint to run this script. Verify necessary variable is within your LogicMonitor instance before continuing.")
}
//def logCacheContext = "${org}::arista-wifi-cloud"
//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}.")
}
httpClient = HTTP.open(AristaEndPont,443);
httpClient.setHTTPProxy('[Your Proxy]',8080);
// Log in to arista
loginurl = "https://"+AristaEndPont+"/wifi/api/session"
payloadstring = '{"type":"apiKeycredentials","keyId":"'+key+'","keyValue":"'+token+'","timeout":129,"clientIdentifier": "'+clientId+'"}';
def payload = payloadstring;
//println payload;
def loginResponse = httpClient.post(loginurl,payload,["Content-Type":"application/json"]);
if ( !(httpClient.getStatusCode() =~ /20/) )
{
// Error has occured
println "Authentication Failure";
println httpClient.getStatusCode();
println loginResponse;
return(1);
}
String RawCookie = httpClient.getHeader("Set-Cookie")
//Have Kept but as yet not needed will remove if not needed..
String httpResponseBody = httpClient.getResponseBody()
//Retrieve the Cookie to use from the header.
AristaCookie = RawCookie.split(';')[0]
//Retrieve AP data.
deviceURL = 'manageddevices/aps?startindex=0&pagesize=1000&locationid=0&nodeid=0&boxid='+nodeId
SendUrl = wifiUrl+deviceURL
def header = ["Content-Type":"application/json","Cookie":AristaCookie]
String deviceResponse = httpClient.get(SendUrl,header)
if ( !(httpClient.getStatusCode() =~ /200/))
{
println "Failed to retrieve data "+httpClient.getStatusCode
return 1
}
String deviceBody = httpClient.getResponseBody()
def encoded_instance_props_array = []
def deviceJson = new JsonSlurper().parseText(deviceBody)
def wildvalue = ""
def wildalias = ""
def description = ""
def getOperatingBand = ""
def getSSID = ""
def radios = deviceJson.managedDevices.radios
def ssidActive = 3
radios[0].each { RadioEntry ->
// Build out wireless entries.
getOperatingBand = RadioEntry.operatingBand
RadioEntry.wirelessInterfaces.each { ssidEntry ->
getssid = ssidEntry.ssid
description = ssidEntry.bssid
wildvalue = getOperatingBand+"_"+getssid
wildalias = wildvalue
if (ssidEntry.active == true) {
ssidActive = 1
} else {
ssidActive = 0
}
println "${wildvalue}.numAssocClients=${ssidEntry.numAssocClients}"
println "${wildvalue}.active=${ssidActive}"
}
}
// Commented out log out process for now as will need to revisit currently reducing Timeout to 5 minutes for the session.
/* String cookieString = '"'+AristaCookie+'"'
def logoutheaders = ["Content-Type":"application/json","Cookie":AristaCookie]
println logoutheaders
def logoutResponse = httpClient.delete(loginurl,"{}",logoutheaders)
logoutResult = httpClient.getStatusCode()
if ( !(httpClient.getStatusCode() =~ /20/) )
{
//Error has occured
println "Logout Failure";
println httpClient.getStatusCode();
println loginResponse;
return(1);
}
println logoutResponse;*/
def LMDebugPrint(message) {
if (debug) {
println(message.toString())
}
}
return 0
Steve Bamford
·1 week agoI have finally got around to creating the NetScan script which can be found below, I had to create a new community account for some reason, but I hope this helps.
You should create it as an Advanced Netscan script and included the following:
Required:
arista.wifi.keyid - This is the ID for your API integration with Arista CVCue.
aristal.wifi.key.pass - this is the key or password for your API integration with Arista CVCue
Optional:
The script allows for you to include default options for these
arista.wifi.baseurl - This is will be the following https://[Your Domain].srv.wifi.arista.com/wifi/api/ where [your domain] is the
domain provided in CVCue in accordance with the API documentation for Arista WIFI.
lm.ap.group.root - This is the root folder where the script should locate the folder for the location or site that the devices reside in
within CVCue.
proxy.host - The fqdn of the proxy you are using.
proxy.port - The tcp port your proxy uses for communication with client hosts.
You should also ensure the folder where the New AP's are going to be has been added to LogicMonitor in the correct root folder as stipulated in lm.ap.group.root this must be the same as the name of the folder in CVCue.
/** * Arista WiFi AP discovery for LogicMonitor Enhanced Script NetScan. * * Required NetScan properties: * arista.wifi.keyid * arista.wifi.key.pass * * Optional NetScan properties: * arista.wifi.baseurl * lm.ap.group.root * proxy.host * proxy.port * discovery.debug */ import com.santaba.agent.groovy.utils.GroovyScriptHelper as GSH import com.logicmonitor.mod.Snippets import groovy.json.JsonOutput import groovy.json.JsonSlurper import java.net.HttpURLConnection import java.nio.charset.StandardCharsets /* * Retrieve properties configured under: * Resource Credentials Options > Use custom credentials for this scan */ String keyId = netscanProps.get('arista.wifi.keyid') String keyValue = netscanProps.get('arista.wifi.key.pass') String baseUrl = netscanProps.get('arista.wifi.baseurl') ?: 'https://[YOUR DOMAIN].wifi.arista.com/wifi/api/' String groupRoot = netscanProps.get('lm.ap.group.root') ?: '[ROOT FOLDER DEFAULT]' String proxyHost = netscanProps.get('proxy.host') ?: '[Your Default Proxy Option]' String proxyPort = netscanProps.get('proxy.port') ?: '[The port your proxy works on]' boolean debug = ( netscanProps.get('discovery.debug') ?: 'false' ).toBoolean() if (!keyId) { throw new Exception( 'Missing required Enhanced NetScan property: arista.wifi.keyid' ) } if (!keyValue) { throw new Exception( 'Missing required Enhanced NetScan property: arista.wifi.key.pass' ) } if (!baseUrl.endsWith('/')) { baseUrl += '/' } /* * Configure HTTPS proxy. */ if (proxyHost && proxyPort) { System.setProperty('https.proxyHost', proxyHost) System.setProperty('https.proxyPort', proxyPort) } /* * Load LogicMonitor Enhanced Script output handler. */ def modLoader = GSH.getInstance(GroovySystem.version) .getScript( 'Snippets', Snippets.getLoader() ) .withBinding(getBinding()) def lmEmit = modLoader.load('lm.emit', '1.0') /* * HTTP request helper. */ Map requestJson( String method, String path, String baseUrl, boolean debug, String cookie = null, Object body = null ) { URL url = new URL(baseUrl + path) if (debug) { System.err.println( "Arista request: ${method} ${url}" ) } HttpURLConnection connection = (HttpURLConnection) url.openConnection() connection.requestMethod = method connection.connectTimeout = 20000 connection.readTimeout = 120000 connection.setRequestProperty( 'Accept', 'application/json' ) connection.setRequestProperty( 'Content-Type', 'application/json' ) if (cookie) { connection.setRequestProperty( 'Cookie', cookie ) } if (body != null) { connection.doOutput = true byte[] payload = JsonOutput .toJson(body) .getBytes(StandardCharsets.UTF_8) connection.outputStream.withCloseable { stream -> stream.write(payload) } } int status = connection.responseCode InputStream responseStream = ( status >= 200 && status < 300 ) ? connection.inputStream : connection.errorStream String responseText = responseStream ? responseStream.getText('UTF-8') : '' if (debug) { System.err.println( "Arista response status: ${status}" ) } if (status < 200 || status >= 300) { throw new RuntimeException( "Arista API ${method} ${path} returned " + "HTTP ${status}: ${responseText}" ) } Object responseData = responseText?.trim() ? new JsonSlurper().parseText(responseText) : [:] String setCookie = connection.getHeaderField('Set-Cookie') String responseCookie = setCookie ? setCookie.split(';', 2)[0] : null return [ status: status, data: responseData, cookie: responseCookie ] } /* * Recursively index the Arista location hierarchy. * * Arista returns IDs as nested maps: * * id: [ * type: locallocationid, * id: 31 * ] */ void walkLocations( Object nodes, String parentPath, Map<Long, Map> index ) { if (!(nodes instanceof Collection)) { return } nodes.each { node -> if (!(node instanceof Map)) { return } String locationName = node.name?.toString() ?: 'Unknown Location' String locationPath = parentPath ? "${parentPath}/${locationName}" : locationName Long locationId = null if ( node.id instanceof Map && node.id.id != null ) { locationId = node.id.id as Long } else if (node.id != null) { locationId = node.id as Long } if (locationId != null) { index[locationId] = [ name: locationName, path: locationPath, type: node.type?.toString() ] } walkLocations( node.children, locationPath, index ) } } /* * Normalise a value for use as an LM property. */ String safeProperty(Object value) { return value == null ? '' : value.toString() .replace('\r', ' ') .replace('\n', ' ') } /* * Discovered resources returned to LogicMonitor. */ List<Map> resources = [] Map<Long, Map> locationsById = [:] String cookie = null try { /* * Authenticate to Arista WiFi. */ Map login = requestJson( 'POST', 'session', baseUrl, debug, null, [ type: 'apiKeycredentials', keyId: keyId, keyValue: keyValue, timeout: 3600, clientIdentifier: 'LogicMonitor-NetScan' ] ) cookie = login.cookie if (!cookie) { throw new RuntimeException( 'Arista login succeeded but no session cookie was returned' ) } /* * Retrieve and index locations. */ Map locationResponse = requestJson( 'GET', 'locations', baseUrl, debug, cookie ) walkLocations( locationResponse.data?.children, '', locationsById ) /* * Retrieve managed access points. */ Map deviceResponse = requestJson( 'GET', 'manageddevices/aps?' + 'startindex=0&pagesize=1000&locationid=0&nodeid=0', baseUrl, debug, cookie ) Collection devices = ( deviceResponse.data?.managedDevices instanceof Collection ) ? deviceResponse.data.managedDevices : [] /* * Build Enhanced Script NetScan resource objects. */ devices.each { ap -> String deviceName = ap.name?.toString()?.trim() String ipAddress = ap.ipAddress?.toString()?.trim() if (!deviceName || !ipAddress) { if (debug) { System.err.println( 'Skipping AP without name or IP: ' + JsonOutput.toJson(ap) ) } return } Long locationId = null if ( ap.locationId instanceof Map && ap.locationId.id != null ) { locationId = ap.locationId.id as Long } else if (ap.locationId != null) { locationId = ap.locationId as Long } Map location = locationId == null ? null : locationsById[locationId] String locationName = location?.name ?: 'Unassigned' String locationPath = location?.path ?: locationName /* * groupName must be a list, even when assigning one group. */ List<String> groups = [ "${groupRoot}/${locationName}" ] /* * Enhanced Script NetScan ignores auto.*, predef.*, and most * system.* properties. Use custom Arista properties instead. * * system.categories is explicitly supported. */ Map<String, String> hostProperties = [ 'CVcueId': safeProperty(ap.boxId), 'arista.ap.id': safeProperty(ap.boxId), 'arista.endpoint.manufacturer': 'Arista Networks', 'arista.endpoint.model': safeProperty(ap.model), 'arista.software.version': safeProperty(ap.softwareVersion), 'arista.network.address': safeProperty(ipAddress), 'arista.network.mac_address': safeProperty(ap.macaddress), 'arista.location.id': safeProperty(locationId), 'arista.location.name': safeProperty(locationName), 'arista.location.path': safeProperty(locationPath), 'system.categories': 'AristaWifi,NoPing' ] Map resource = [ hostname: ipAddress, displayname: deviceName, hostProps: hostProperties, groupName: groups ] resources.add(resource) } if (debug) { System.err.println( "Prepared ${resources.size()} Arista AP resources" ) } /* * Validate and emit Enhanced Script NetScan JSON. * * Do not print CSV or other text to stdout. */ lmEmit.resource(resources, debug) return 0 } catch (Throwable error) { System.err.println( "Arista WiFi Enhanced NetScan failed: " + "${error.getClass().getName()}: ${error.message}" ) if (debug) { error.printStackTrace(System.err) } return 1 } finally { if (cookie) { try { requestJson( 'DELETE', 'session', baseUrl, debug, cookie ) } catch (Throwable logoutError) { System.err.println( 'Warning: Arista logout failed: ' + "${logoutError.message}" ) } } }Jason Hupka
·1 year agoThis is a very thorough and enlightening post - GREAT JOB, SteveBamford !!!
We don't use Arista, but what I love best about this post is it is a great example on how to do discovery and data collection in LogicMonitor.
I know there's lots of code examples over on github and within the LM Docs, and it is great seeing posts like this in the LM Community.
SteveBamford
OP1 year agoThanks for the great comment.
SteveBamford
OP1 year agoFAQs
Where are the interface stats? They are not available from the API at this time, the only ones that is possibly interface stats.
Are there more metrics that can be retrieved? Yes specific Band (IE 2.4GHZ) metrics which if we add I will include here.
Have you submitted to LM Exchange? No Not yet waiting for feedback internally and feel free to drop a comment in below as I only work in Groovy when doing stuff in LogicMonitor, so any feedback is greatly appreciated.