DataSource/Property Source that monitors Cisco End of Life (EOL) or End of Support (EOS) dates.
DataSource/Property Source that monitors Cisco End of Life (EOL) or End of Support (EOS) dates. Using API from - https://developer.cisco.com/docs/support-apis/eox/#get-eox-by-serial-numbers with auto.entphysical.serialnum or auto.endpoint.serial_number properties
Barb Ramirez
Posted 1 year ago·Last reply 10 months ago
12 comments
Barb Ramirez
OP10 months agoSince 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 Ramirez
OP10 months agoimport 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") ?: null
if (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 Ramirez
OP10 months agoTech 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 Ramirez
OP10 months agoimport 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") ?: null
if (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 Ramirez
OP10 months agoSome 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 Ramirez
OP1 year agoHi 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 Ramirez
OP1 year agoAnd just to explain why i have 2 property sources as there is 2 sets of API creds for each SN2INFO and EOX