Skip to content

Instantly share code, notes, and snippets.

@MaxAnderson95
Last active June 22, 2023 18:25
Show Gist options
  • Save MaxAnderson95/9e8eb770e60cd779dfe1a427e04f9953 to your computer and use it in GitHub Desktop.
Save MaxAnderson95/9e8eb770e60cd779dfe1a427e04f9953 to your computer and use it in GitHub Desktop.
A LogicMonitor EventSource script to alert on badge swipes at a QTS Colo facility
import com.santaba.agent.groovyapi.http.*;
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.Duration
import java.time.format.DateTimeFormatter
import groovy.json.JsonBuilder
DEBUG = true
API_URL = "https://api.qtsdatacenters.com"
USERNAME = hostProps.get("qts.username")
PASSWORD = hostProps.get("qts.password")
API_ACCESS_KEY = hostProps.get("qts.api_access_key")
API_SECRET_KEY = hostProps.get("qts.api_secret_key")
TOKEN_CACHE_KEY = "qtsToken"
LAST_EVENT_PROCESSED_CACHE_KEY = "qtsLastEventProcessed"
scriptCache = this.class.classLoader.loadClass("com.santaba.agent.util.script.ScriptCache").getCache()
main()
def main() {
// Get the badge activity in LogicMonitor format
activity = getBadgeActivityInLMFormat()
// Print the badge activity in LogicMonitor format
prettyPrintJson(activity)
log(activity)
// Set the lastEventProcessed cache key to the timestamp of the most recent event
if (activity?.events?.size() > 0) {
setLastProcessedEventToFile(activity?.events[0]?.qtsTimeStamp)
}
}
def getBadgeActivityInLMFormat() {
// Get the sorted and filtered badge activity
def badgeEvents = collectSortAndFilterBadgeActivity()
// Construct a new object
def outputObject = [:]
// Loop through each event and add it to a list within the property "events"
def events = []
badgeEvents?.each { event ->
object = [
"severity": event?.scanStatus == "Granted" ? "warn" : "error",
"happenedOn": convertLMTimestampToISO8601(event?.eventDateTime),
"message": "${event?.firstName} ${event?.lastName} was ${event?.scanStatus} access to ${event?.cardReaderAlias != null ? event?.cardReaderAlias : event?.cardReader} in building ${event?.building} at ${event?.eventDateTime}",
"user": event?.firstName + " " + event?.lastName,
"cardReaderAlias": event?.cardReaderAlias,
"cardReader": event?.cardReader,
"cardNumber": event?.cardNumber,
"cardType": event?.cardType,
"scanStatus": event?.scanStatus,
"building": event?.building,
"customerAssigned": event?.customerAssigned,
"qtsTimeStamp": event?.eventDateTime
]
events.add(object)
}
// Add the events list to the outputObject object under the key "events"
outputObject.put("events", events)
return outputObject
}
def collectSortAndFilterBadgeActivity() {
// Get raw badge activity
def rawBadgeActivity = getRawBadgeActivity()
// Collect the main and child events into a single list
def badgeEvents = []
rawBadgeActivity?.data?.each { person ->
badgeEvents.add(person?.main)
person?.children?.each { childEvent ->
badgeEvents.add(childEvent)
}
}
// Sort the events by eventDateTime (descending)
badgeEvents.sort { a, b ->
parseToLocalDateTime(b?.eventDateTime, "yyyy-MM-dd HH:mm:ss").compareTo(parseToLocalDateTime(a?.eventDateTime, "yyyy-MM-dd HH:mm:ss"))
}
// If there is a lastEventProcessed key in the script cache, filter out all events that are older than the lastEventProcessed
def lastEventProcessed = getLastProcessedEventFromFile()
if (lastEventProcessed != null) {
log("${LAST_EVENT_PROCESSED_CACHE_KEY} key found in cache. Filtering out events older than ${lastEventProcessed}")
badgeEvents = badgeEvents.findAll { event ->
parseToLocalDateTime(event?.eventDateTime, "yyyy-MM-dd HH:mm:ss").isAfter(parseToLocalDateTime(lastEventProcessed, "yyyy-MM-dd HH:mm:ss"))
}
} else {
log("No ${LAST_EVENT_PROCESSED_CACHE_KEY} key found in cache. Not filtering out any events")
}
// Return the badge activity
log("Number of events collected: ${badgeEvents.size()}")
return badgeEvents
}
def getRawBadgeActivity() {
// Set Headers
def token = getAuthToken()
def headers = [
"User-Agent" : "LogicMonitor Polling Agent",
"Content-Type" : "application/json",
"Authorization" : "Bearer ${token}"
]
// Set parameters
def params = [
"badgeReader": "CUSTOMER_ASSIGNED",
"cardTypes": ["Client", "Client Contractor", "QTS Employee", "QTS Contractor", "QTS Security", "QTS Guard Tour"],
"startDate": getTodaysDate(),
"startTime": "00:00",
"endDate": getTodaysDate(),
"endTime": "23:59",
"timezone": "America/New_York"
]
// Make the request
def httpClient = HTTP.open(API_URL, 443, true)
def url = API_URL + "/sdp/api/roster/reports/v1/event_log/grouped" + mapToUrlParameters(params)
log("Making request to ${url}")
httpClient.get(url, headers)
// Collect and parse the response
def response = httpClient.getResponseBody()
def jsonResponse = new groovy.json.JsonSlurper().parseText(response)
return jsonResponse
}
def getAuthToken() {
def token = scriptCache.get(TOKEN_CACHE_KEY)
if (token != null) {
log("Using cached token")
return token
} else {
log("Token cache empty. Fetching new token")
}
// Set headers
def headers = [
"User-Agent" : "LogicMonitor Polling Agent",
"Content-Type" : "application/json",
]
// Set body
def body = [
"username" : USERNAME,
"password" : PASSWORD,
"api_access_key": API_ACCESS_KEY,
"api_secret_key": API_SECRET_KEY,
]
def jsonBody = new groovy.json.JsonBuilder(body).toString()
// Make the request
def httpClient = HTTP.open(API_URL, 443, true)
httpClient.post(API_URL + "/sdp/api/token/access", jsonBody, headers)
// Collect and parse the response
def response = httpClient.getResponseBody()
def jsonResponse = new groovy.json.JsonSlurper().parseText(response)
// Cache the token
scriptCache.set(TOKEN_CACHE_KEY, jsonResponse.access_token, jsonResponse.expires_in * 1000)
// Return the token
return jsonResponse.access_token
}
def getLastProcessedEventFromFile() {
def filePath = "tmp/${LAST_EVENT_PROCESSED_CACHE_KEY}.txt"
def file = new File(filePath)
if (file.exists()) {
def content = file.text
return content
} else {
return null
}
}
def setLastProcessedEventToFile(String eventDateTime) {
def filePath = "tmp/${LAST_EVENT_PROCESSED_CACHE_KEY}.txt"
def file = new File(filePath)
file.write(eventDateTime)
}
// Converts a map of parameters to a URL encoded string
String mapToUrlParameters(Map<String, Object> params) {
def stringBuilder = new StringBuilder()
params.each { key, value ->
if (stringBuilder.length() > 0) {
stringBuilder.append('&')
} else {
stringBuilder.append('?')
}
if (value instanceof List) {
value.each { listItem ->
stringBuilder.append(URLEncoder.encode(key, 'UTF-8'))
stringBuilder.append('=')
stringBuilder.append(URLEncoder.encode(listItem.toString(), 'UTF-8'))
stringBuilder.append('&')
}
} else {
stringBuilder.append(URLEncoder.encode(key, 'UTF-8'))
stringBuilder.append('=')
stringBuilder.append(URLEncoder.encode(value.toString(), 'UTF-8'))
}
}
return stringBuilder.toString()
}
def getTodaysDate() {
def today = LocalDate.now()
def formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
return today.format(formatter)
}
def getTime(negativeOffset = 0) {
def today = LocalDateTime.now().minusMinutes(negativeOffset)
def formatter = DateTimeFormatter.ofPattern("HH:mm")
return today.format(formatter)
}
LocalDateTime parseToLocalDateTime(String dateTimeString, String format) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format)
return LocalDateTime.parse(dateTimeString, formatter)
}
String convertLMTimestampToISO8601(String dateTimeString) {
// Parse the original string into a LocalDateTime object
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
LocalDateTime localDateTime = LocalDateTime.parse(dateTimeString, inputFormatter)
// Format the LocalDateTime object into an ISO-8601 string
DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
return localDateTime.format(isoFormatter)
}
def prettyPrintJson(object) {
def json = new JsonBuilder(object).toPrettyString()
println json
}
def log(message) {
if (DEBUG) {
def msg = "[DEBUG ${getTodaysDate()} ${getTime()}] ${message}"
def filePath = "tmp/qts_log.txt"
def file = new File(filePath)
file.append(msg + "\n")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment