Forum Discussion

llama's avatar
llama
Icon for Neophyte rankNeophyte
2 months ago

Datasource - Extract dates from a json response.

Hi,

I'm writing a datasource logic module to pull the system certificates from Cisco ISE to track the expiry date, similar to SSL certificates.

I can get the response back but when i create the datapoints, they don't populate, could someone advise?

// import the logicmonitor http and jsonslurper classes
import com.santaba.agent.groovyapi.http.*
import groovy.json.JsonSlurper

// setup
ip = hostProps.get('system.hostname')
user = hostProps.get('ise.monitoring.user')
pass = hostProps.get('ise.monitoring.pass')
host = hostProps.get('auto.system_name')

// base64 encoding un+pw for basic auth header
auth_string = user + ':' + pass
encodedString = auth_string.bytes.encodeBase64()
auth = 'Basic ' + encodedString

// open a connection
httpClient = HTTP.open(ip, 443)

// define api endpoint
url = "https://${ip}/api/v1/certs/system-certificate/${host}"

// create headers
headers = [:]
headers['Authorization'] = auth
headers['Content-Type'] = 'application/json'

// get request to api endpoint
getResponse = httpClient.get(url, headers)

// get status code
statusCode = httpClient.getStatusCode()

// close connection
httpClient.close()

// parse response and error handling

if (statusCode == 200) {
    response = new JsonSlurper().parseText(httpClient.getResponseBody())
    println response['response']
    } else {
    return 1
    }

return 0

my datapoint is setup as below:

DP Source: Content the script writes to standard output

Interpret with: regex

Regex: 

expirationDate:\S*\s\S*\s\S*\s\S*\s\S*\s\d*

Metric type: derive

you can see from the attachment that i get no data, I've also attached the output from the collector debug

  • ok so i've got this now

    // import the logicmonitor http and jsonslurper classes
    import com.santaba.agent.groovyapi.http.*
    import groovy.json.JsonSlurper
    import java.text.SimpleDateFormat
    import java.util.TimeZone
    
    // setup
    ip = hostProps.get('system.hostname')
    user = hostProps.get('ise.monitoring.user')
    pass = hostProps.get('ise.monitoring.pass')
    host = hostProps.get('auto.system_name')
    
    // base64 encoding un+pw for basic auth header
    auth_string = user + ':' + pass
    encodedString = auth_string.bytes.encodeBase64()
    auth = 'Basic ' + encodedString
    
    // open a connection
    httpClient = HTTP.open(ip, 443)
    
    // define api endpoint
    url = "https://${ip}/api/v1/certs/system-certificate/${host}"
    
    // create headers
    headers = [:]
    headers['Authorization'] = auth
    headers['Content-Type'] = 'application/json'
    
    // get request to api endpoint
    getResponse = httpClient.get(url, headers)
    
    // get status code
    statusCode = httpClient.getStatusCode()
    
    // close connection
    httpClient.close()
    
    // Check for HTTP response status
    if (statusCode != 200) {
        println "Error: Received HTTP status code ${statusCode}"
        return 1
    }
    
    // parse response and error handling
    response = httpClient.getResponseBody()
    
    // current time in epoch
    now_epoch = new Date().getTime()
    
    data = new JsonSlurper().parseText(response)
    
    // Collect friendlyNames and expirationDates into a list of maps
    data_list = data.response.collect { [friendlyName: it.friendlyName, expirationDate: it.expirationDate] }
    
    // define simple date format
    dateFormat = new SimpleDateFormat('EEE MMM dd HH:mm:ss z yyyy')
    dateFormat.setTimeZone(TimeZone.getTimeZone('UTC')) // set Tz to UTC
    
    // Loop over the data_list to process each item
    data_list.each { item ->
        // Print friendly name and expiration date
        println "friendlyName: ${item.friendlyName}, expirationDate: ${item.expirationDate}"
    
        // Parse the expiration date
        try {
            date = dateFormat.parse(item.expirationDate)
            epoch_millis = date.getTime()
            println "expiry_epoch: ${epoch_millis}"
    
            // Calculate days to expiry
            millis_to_expiry = epoch_millis - now_epoch
            days_to_expiry = millis_to_expiry / (1000 * 60 * 60 * 24)
            println "days_to_expiry: ${days_to_expiry}"
        } catch (Exception e) {
            println "Error parsing expiration date for ${item.friendlyName}: ${e.message}"
            return 1
        }
    }
    
    // If everything was successful, exit with code 0
    return 0

    and it returns as shown in the screenshot, however my data points don't work because it says it's NaN:

    Attribute not valid or not found in output - (method=regex, param=days_to_expiry:\s\d+)

    in your example Mike_Moniz you have TextDescriptor=Number

    how would you set the datapoint up? would that be multiline key/value pair?

    Apologies for the potentially dumb question, i'm new to logic monitor.

  • right i've got my head round that bit now.

    What i don't know / understand is my script pulls back multiple certificates, it's not multi instance because it's only running against the one node, however each ISE node has multiple certificates, so how do i distinguish between each one?

    this needs to scale over multiple different environments for different customers, so there will not be any commonality in number of certificates etc so i can't rely on saying index 0 is certificate A for all environments.

    • Mike_Moniz's avatar
      Mike_Moniz
      Icon for Professor rankProfessor

      Assuming each node is a device/resource in LM, you actually do want to use mutli-instance. Instances don't normally go across multiple devices and meant for this kinda situation. LM will run your datasource script for each device that the DataSource is assigned to, and update the ##system.hostname## value depending on which one it wants to look at.

      The classic example is Windows drives. One server may have Drive C, D and E. Your script would query querying the system at ##system.hostname## and determine what drives it has in AutoDiscover. The Batch collection script would provide the data for C, D, and E all at once. For another server your AutoDiscover script might just see a C drive and the batch collection would just return it for C. LM will then take care of making sure the instances are show for the correct device.

  • Take a look at the LM provided Cisco_ISE_ServerSessions DataSource. That uses a multi-instance batchscript that might be a good example to look at.

  • I think (did not test) that the regex needs to have ( ) around the part you want to extract.

    But more importantly, datapoints must be numbers. So "Mon Jun 11" can't be put in a datapoint. Since you are already writing a script I would do all the extracting and math in the script itself and just output the final numbers. For dates I suggest converting them to epoch which is a single number LM can read. I would also convert it to DaysRemaining (or hours or minutes if preferred) and have a datapoint for that which you can alert against. It may still be useful to also output the raw date in text; although LM will ignore it, it does show up in Poll Now which is useful when reviewing an alert.

    So I would output something like this in the script:

    ExpirationDateRAW=Mon Jun 11 14:22:33 UTC 2029
    ExpirationDate=1875896553
    DaysRemaining=1649.161828703704