Forum Discussion
12 Replies
- Barb
Advisor
Since we cant publish to Exchange for the time being here are the 2 Property Sources. Ill publish as soon as we have the option again.
we confirmed that the development team has disabled Exchange features in the backend as part of preparations for the upcoming DiagnosticSources (DS) beta release. This explains why you are currently unable to publish modules.
We will keep you updated as soon as we have more details on when the Exchange features will be re-enabled. - Barb
Advisor
import groovy.json.JsonSlurper
import java.time.LocalDateTime
import com.logicmonitor.mod.Snippets// switch on to enable debug output
def debug = false// get the info we need to call the API
def client_key = hostProps.get("ciscoSN2INFOapi.key")
def client_secret = hostProps.get("ciscoSN2INFOapi.pass")
def serial_number = hostProps.get("auto.endpoint.serial_number")// Get an API token. This calls a function that will get it from collector cache if available, otherwise will call the API to generate a new token
api_token = getToken(client_key, client_secret, false, debug)
// Create GET request
def coverageApiURL = new URL("https://apix.cisco.com/sn2info/v2/coverage/summary/serial_numbers/${serial_number}?page_index=1");
connection = coverageApiURL.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("Authorization", "Bearer " + api_token);if (connection.responseCode == 200) {
apiResponse = connection.inputStream.withReader { Reader reader -> reader.text }} else {
println("Failed: ${connection.responseCode}")
}apiResponseJson = new JsonSlurper().parseText(apiResponse);
// println "auto.support.service_contract_cover=${apiResponseJson.serial_numbers[0].is_covered}"
// println "auto.support.warranty_end_date=${apiResponseJson.serial_numbers[0].warranty_end_date}"
// STANDARD OUTPUTS AVAILABLE FOR ALL DEVICES
// ==========================================// Cisco: Indicates whether the specified serial number is covered by a service contract; one of the following values: YES or NO. If the serial number is covered by a service contract, the value is Yes.
def is_covered = apiResponseJson.serial_numbers[0].is_covered
if (is_covered) { println "auto.ciscosupport.has_service_contract=${is_covered}" }// Cisco: End date of the warranty for the specified serial number in the following format: YYYY-MM-DD; for example, 2010-01-01.
def warranty_end_date = apiResponseJson.serial_numbers[0].warranty_end_date
if (warranty_end_date) { println "auto.ciscosupport.warranty_end_date=${warranty_end_date}" }// Function for retrieving token from the collector cache or getting a new one from the Cisco API if we don't
// have one cached (either it's expired or we've never run this script before on this collector)
def getToken (client_key, client_secret, force = false, debug = false) {
// Do we already have a token in the cache on this collector?
def scriptCache = this.class.classLoader.loadClass("com.santaba.agent.util.script.ScriptCache").getCache();
access_token = scriptCache.get("cisco-support-api-token") ?: nullif (access_token == null || force == true) {
// we must create a new token as there wasn't a valid one in the cache, or the
// calling script has forced us to get a new one
LMDebugPrint("Getting a new token from Cisco API", debug)// Create POST URL.
def loginUrl = new URL("https://id.cisco.com/oauth2/default/v1/token");
connection = loginUrl.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setDoOutput(true);// Build credential check
def authRequest = 'grant_type=client_credentials&client_id=' + client_key + '&client_secret=' + client_secret// Write credential request to output stream.
def writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(authRequest);
writer.flush();response = connection.inputStream.withReader { Reader reader -> reader.text }
jsonResponse = new JsonSlurper().parseText(response);// calculate the datetime that the token will expire and add it to the json response
LocalDateTime now = LocalDateTime.now();
LocalDateTime expiryDateTime = now.plusSeconds(jsonResponse.expires_in)
jsonResponse.expiryDateTime = expiryDateTime// store the token in the cache with a lifetime 2 mins less than what it really has to make sure
// we don't try and use it when it is about to expire
cacheLifetime = (jsonResponse.expires_in - 120) * 1000
scriptCache.set("cisco-support-api-token", jsonResponse.access_token, cacheLifetime)
access_token = jsonResponse.access_token} else {
LMDebugPrint("Reusing token from collector cache", debug)
}return access_token
}
/**
* Helper function to print out debug messages for troubleshooting purposes.
*/
def LMDebugPrint(message, boolean debug) {
if (debug) {
println(message.toString())
}
} - Barb
Advisor
Tech notes of SN2INFO one
For this property source to work you will need to follow the Cisco onboarding process; https://developer.cisco.com/docs/support-apis/user-onboarding-process/#user-onboarding-process
If your company already has API access you then need to send an email For access to the SN2INFO Support APIs, you need to send an email request to: supportapis-help@cisco.com;mailto:apix-support@cisco.com;mailto:services-api-help@cisco.com
This property source uses the SN2INFO API
When this has been granted you then need to setup an APP https://apiconsole.cisco.com/apps/myapps in the apiconsole using the 'Serial Number to Information API Version 2 : Serial Number to Information API Version 2' api. Grant Type is Client Credentials and Application Type is Service.
Add the API Key and Secret as properties in Logicmonitor API Key = ciscoSN2INFOapi.key API Secret = ciscoSN2INFOapi.pass
These API's only allow 10 calls per second so can take sometime depending on the number of devices you are trying to populate with the info.
- Barb
Advisor
import groovy.json.JsonSlurper
import java.time.LocalDateTime
import com.logicmonitor.mod.Snippets// switch on to enable debug output
def debug = false// get the info we need to call the API
def client_key = hostProps.get("ciscoEOXapi.key")
def client_secret = hostProps.get("ciscoEOXapi.pass")
def serial_number = hostProps.get("auto.endpoint.serial_number")// Get an API token. This calls a function that will get it from collector cache if available, otherwise will call the API to generate a new token
api_token = getToken(client_key, client_secret, false, debug)
// Call the EOX API to get end-of-life information about the device (if it is available)
def eoxApiURL = new URL("https://apix.cisco.com/supporttools/eox/rest/5/EOXBySerialNumber/1/${serial_number}");
connection = eoxApiURL.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("Authorization", "Bearer " + api_token);if (connection.responseCode == 200) {
apiResponse = connection.inputStream.withReader { Reader reader -> reader.text }} else {
println("Failed: ${connection.responseCode}")
}apiResponseJson = new JsonSlurper().parseText(apiResponse);
// Cisco: Last date to receive service and support for the product. After this date, all support services for the product are unavailable, and the product becomes obsolete.
if (apiResponseJson.EOXRecord[0].LastDateOfSupport != "null") {
println "auto.ciscosupport.endofsupport=${apiResponseJson.EOXRecord[0].LastDateOfSupport.value}"}
// Cisco: The date on which the end of sale and the end-of-life milestones for a Product is communicated to the public.
if (apiResponseJson.EOXRecord[0].EOXExternalAnnouncementDate ) {
println "auto.ciscosupport.endoflifenotification=${apiResponseJson.EOXRecord[0].EOXExternalAnnouncementDate.value}"
}// Cisco: The Product is no longer offered for sale after this date. This is also the last date to order the Product through Cisco point-of-sale mechanisms. The EOS date is documented in the EOL notification.
if (apiResponseJson.EOXRecord[0].EndOfSaleDate) {
println "auto.ciscosupport.endofsale=${apiResponseJson.EOXRecord[0].EndOfSaleDate.value}"
}
// Function for retrieving token from the collector cache or getting a new one from the Cisco API if we don't
// have one cached (either it's expired or we've never run this script before on this collector)
def getToken (client_key, client_secret, force = false, debug = false) {// Do we already have a token in the cache on this collector?
def scriptCache = this.class.classLoader.loadClass("com.santaba.agent.util.script.ScriptCache").getCache();
access_token = scriptCache.get("cisco-support-api-token") ?: nullif (access_token == null || force == true) {
// we must create a new token as there wasn't a valid one in the cache, or the
// calling script has forced us to get a new one
LMDebugPrint("Getting a new token from Cisco API", debug)// Create POST URL.
def loginUrl = new URL("https://id.cisco.com/oauth2/default/v1/token");
connection = loginUrl.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setDoOutput(true);// Build credential check
def authRequest = 'grant_type=client_credentials&client_id=' + client_key + '&client_secret=' + client_secret// Write credential request to output stream.
def writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(authRequest);
writer.flush();response = connection.inputStream.withReader { Reader reader -> reader.text }
jsonResponse = new JsonSlurper().parseText(response);// calculate the datetime that the token will expire and add it to the json response
LocalDateTime now = LocalDateTime.now();
LocalDateTime expiryDateTime = now.plusSeconds(jsonResponse.expires_in)
jsonResponse.expiryDateTime = expiryDateTime// store the token in the cache with a lifetime 2 mins less than what it really has to make sure
// we don't try and use it when it is about to expire
cacheLifetime = (jsonResponse.expires_in - 120) * 1000
scriptCache.set("cisco-support-api-token", jsonResponse.access_token, cacheLifetime)
access_token = jsonResponse.access_token} else {
LMDebugPrint("Reusing token from collector cache", debug)
}return access_token
}
/**
* Helper function to print out debug messages for troubleshooting purposes.
*/
def LMDebugPrint(message, boolean debug) {
if (debug) {
println(message.toString())
}
} - Barb
Advisor
Some tech notes
For this property source to work you will need to follow the Cisco onboarding process; https://developer.cisco.com/docs/support-apis/user-onboarding-process/#user-onboarding-process
If your company already has API access you then need to send an email For access to the EOX Support APIs, you need to send an email request to: supportapis-help@cisco.com;mailto:apix-support@cisco.com;mailto:services-api-help@cisco.com
This property source uses the EOX API
When this has been granted you then need to setup an APP https://apiconsole.cisco.com/apps/myapps in the apiconsole using the 'EOX V5 API : EOX V5 API' api. Grant Type is Client Credentials and Application Type is Service.
Add the API Key and Secret as properties in Logicmonitor API Key = ciscoEOXapi.key API Secret = ciscoEOXapi.pass
These API's only allow 10 calls per second so can take sometime depending on the number of devices you are trying to populate with the info.
You can add further properties from https://developer.cisco.com/docs/support-apis/eox/#get-eox-by-serial-numbers
Using the below, replacing the hashes with the parameter you want to pull and replace the question marks with the property name you want to use.
if (apiResponseJson.EOXRecord[0].###########) { println "auto.ciscosupport.??????=${apiResponseJson.EOXRecord[0].############.value}" }
- Barb
Advisor
Hi Dave I have have now written a couple of property sources to get the different data using EOX and SN2INFO
I used your script there was just one fix i needed for the SN2INFO one to work
def coverageApiURL = new URL("https://apix.cisco.com/sn2info/v2/coverage/summary/serial_numbers/${serial_number}?page_index=1");
Just needed to add the page index filter for it to work :)
Im going to tidy up the sources and put some tech notes on following the cisco onboard process then ill make them public
- Barb
Advisor
And just to explain why i have 2 property sources as there is 2 sets of API creds for each SN2INFO and EOX
- Dave_Lee
Advisor
I've had a couple of stabs at doing exactly this recently.
I ended up actually doing it outside of LogicMonitor, with a script that runs each night to query the LM API for Cisco devices, then push that info back into properties on each device in the LM API.
I did also do something similar as a Property Source, but felt it was quite inefficient as it would have to query the API individually for each device. Although maybe this is a good use case for using collector cache.
I might be able to share something. What are you after? Properties with the info on a device? Or a datasource to alert on something?
- Cole_McDonald
Professor
I usually have a single collector that I have access to on which I increase the timeout for scripts... and use it for this type of effort, mostly RestAPI calls to be able to perform more complex jobs. PropSources are also my preferred weapon for these tasks.
- Barb
Advisor
Thanks Dave :) I am going to write a property source to get the info on each device. Just getting the right access to the cisco apix was quite the task :)
But finally i have developer enabled.
I can get the info in postman and python now just need the time to translate that to Groovy.
- Dave_Lee
Advisor
I found the groovy code I used for this. I've not used it for a while as I went another route, but I just tested it and it will work. I've published it as a gist in case it's of any use to you.
https://gist.github.com/davelee212/a3db0add871d9c0fe06fca7bc914ca0e