Last active
June 22, 2023 18:25
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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