Adding Arista AP's to LogicMonitor
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 0
Arista 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 0
Collection 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