Name
sn_itom_pattern.GoogleEventHandler
Description
Handling the events from GCP and invoking discovery pattern
Script
var GoogleEventHandler = Class.create();
GoogleEventHandler.prototype = Object.extendsObject(sn_cmp.CloudEventHandler, {
initialize: function() {
},
processEvent : function(headersStr, bodyStr, queryParamsStr, eventId) {
var response = {};
var jsonBody = JSON.parse(bodyStr);
var ciType;
var objectID = this.getObjectID(jsonBody);
try {
ciType = this.extractCMDBCIType(jsonBody);
}
catch(e){
gs.error("Failed to get cmdb type for object ID "+objectID);
}
if(!ciType){
gs.warn("GoogleEventHandler: Not supported type "+this.getResourceType(jsonBody)+" with object ID "+objectID);
}else if(this.shouldInvokeDiscovery(jsonBody)){
gs.info("GoogleEventHandler: invoking discovery on event ID "+eventId+" with object ID "+objectID);
response = this.invokeDiscovery(jsonBody,eventId,objectID);
}else{
gs.info("GoogleEventHandler: handing in server event ID "+eventId+" with object ID "+objectID);
response.ci = this.handleAlertInServer(jsonBody,objectID,ciType);
}
response.state = "processed";
// getting the account Domain
var serviceAccountId = this.getProjectID(jsonBody);
var serviceAccountGR = this._verifyServiceAccount(serviceAccountId);
response.sys_domain = serviceAccountGR.sys_domain;
if (this.getActionType(jsonBody))
response.subject = this.getActionType(jsonBody);
if (jsonBody.timestamp)
response.event_time = this.getEventTime(jsonBody.timestamp);
return response;
},
/***
* returns true if the alert should trigger a discovery
* @param jsonBody
*/
shouldInvokeDiscovery : function(jsonBody) {
// for now the only logic is that we need to check if its a delete event
// the shouldInvokeDiscovery is still here to support future cases of handling events in the server
// TODO: the problem in deleting CI from server is that the 'absent' and 'terminated' is not aggregating to the
// children
return !this.isDeleteEvent(jsonBody);
},
/***
* handle the alert without running a discovery
* Alerts that will be handled here are
* - delete
* @param jsonBody - the event json body
* @param objectId - the CI object ID
*/
handleAlertInServer : function(jsonBody,objectId,cmdbCIType) {
var resCI;
if(this.isDeleteEvent(jsonBody)){
gs.info("GoogleEventHandler: CI was marked as absent with object ID "+objectId);
resCI = this.markCIAbsent(this.getObjectID(jsonBody),cmdbCIType);
}else{
gs.warn("GoogleEventHandler: No Action was set for CI with object ID "+objectId);
}
return resCI;
},
/***
* Mark the CI from type ciClass with object ID objectID as absent
* @param objectID - the object ID attribute
* @param ciClass - the cmdb class
* return: glide record of the CI that is marked as absent
*
*/
markCIAbsent : function(objectID, ciClass) {
var resultVal;
if (ciClass) {
var ciGR = new GlideRecord(ciClass);
if (ciGR.get('object_id', objectID)) {
gs.info("GoogleEventHandler: marking object "+objectID+" for absent");
new sn_cmp.CMPReconciler().markCIAbsent(ciGR);
resultVal = ciGR.getUniqueValue();
}
}else{
gs.error("GoogleEventHandler: failed to terminate CI "+objectID+", no cmdb_ci table was found");
}
return resultVal;
},
/***
* invoke the discovery based on alert
* @param jsonBody
* @param eventID - event ID
* @param objectID - ci object ID
*/
invokeDiscovery : function(jsonBody,eventID,objectID) {
var response = {};
var configObj = this.prepareConfigObj(jsonBody,eventID,objectID);
response.state = 'processing';
response.discovery_status = null;
response.error_message = "";
gs.info("GoogleEventHandler: Invoking discovery from event ID "+eventID+" on object id "+ objectID + " with pattern "+configObj.patternId);
var cloudPatternInvocation = new sn_itom_pattern.CloudPatternInvocation();
var result = cloudPatternInvocation.eventBasedDiscovery(configObj, response);
if (result){
//For sucess invocation the state | error_mesaage will be update in HorizontalDiscoveryResultHandler.finalizeCloudChangeEvent()
//Will be determine if the pattern is success or not
gs.info("GoogleEventHandler: Finished Event based discovery on object ID "+objectID);
}else {
gs.error("GoogleEventHandler: Failed Event based discovery on object ID "+objectID);
response.state = (response.state) ? response.state : 'error';
response.error_message = (response.error_message) ? response.error_message : gs.getMessage('Cannot invoke pattern discovery for: {0}', [JSON.stringify(configObj , null, 2)]);
}
response.resource_id = objectID;
response.resource_type = configObj.resource_type;
// response.resource_block = this._getResourceTypeGr(this.resourceType);
return response;
},
/***
* prepare the config type for invoking the discovery
* @param jsonBody
* @param eventID
* @param objectID
* @returns {{changeType: *|string, classType: *, accountId: *|string, ldc: *|string, inputObjectId: *, cloudId: *, patternId: *, eventId: *, eventType: string, eventData: string}}
*/
prepareConfigObj : function(jsonBody,eventID,objectID){
var resourceType = this.getResourceType(jsonBody);
var invocationPatternId = this._getPatternId(resourceType);
var ciClassType = this._getClassTypeId(resourceType);
var actionType = this.getActionType(jsonBody);
var projectID = this.getProjectID(jsonBody);
var context = this.extractContext(jsonBody);
var ldc = this.extractLDC(jsonBody);
//Prepare the mandatory parameters to pass to the pattern context
var configObj = {
changeType : actionType,
classType: ciClassType,
accountId: projectID,
ldc: ldc,
inputObjectId: objectID,
cloudId: objectID,
patternId: invocationPatternId,
eventId: eventID,
eventType: "GCP"
};
return configObj;
},
getResourceType : function(jsonBody) {
return jsonBody.resource.type;
},
getActionType : function(jsonBody) {
return jsonBody.protoPayload.response.operationType;
},
getProjectID : function(jsonBody){
return jsonBody.resource.labels.project_id;
},
/***
* it will parse event timestamp like 2020-01-28T18:27:46.171277Z to the readable format[yyyy-mm-dd hr:min:sec]
* @param eventTime
* @returns {eventTime in format yyyy-mm-dd hr:min:sec}
*/
getEventTime: function(eventTime) {
var re = /^(\d\d\d\d-\d\d-\d\d)T(\d\d:\d\d:\d\d)\.\d\d\d\d\d\dZ$/g;
var arr = re.exec(eventTime);
if (arr && arr.length == 3)
return new GlideDateTime(arr[1] + ' ' + arr[2]);
return '';
},
getObjectID : function(jsonBody){
return jsonBody.protoPayload.response.targetId;
},
extractContext : function(jsonBody){
return jsonBody.protoPayload;
},
extractLDC : function(jsonBody) {
var ldcRes = "us-east1" ; //TODO: today we dont have support for global resources. it needs to be added in future version. currently the pattern is ignoring this attribute for global CIs
var region = jsonBody.resource.labels.region;
var location = jsonBody.resource.labels.location;
var findLdc = false;
var zone = '';
zone = jsonBody.resource.labels.zone;
if (!zone) { // For subnetworks
zone = jsonBody.resource.labels.location;
}
// the assumption here is that i can ignore the service account context since all Availability zones with the name XXX will always be connected to the LDC with name YYY.
if (region) {
ldcRes = region;
findLdc = true;
return ldcRes;
}
// List of location taken from https://cloud.google.com/spanner/docs/instance-configurations#available-configurations-multi-region
if(location) {
switch (location)
{ case "asia1" : ldcRes = "asia-northeast1"; break;
case "eur3" : ldcRes = "europe-west1"; break;
case "eur5" : ldcRes = "europe-west2"; break;
case "eur6" : ldcRes = "europe-west4"; break;
case "nam3" : ldcRes = "us-east4"; break;
case "nam6" : ldcRes = "us-central1"; break;
case "nam7" : ldcRes = "us-central1"; break;
case "nam8" : ldcRes = "us-west2"; break;
case "nam9" : ldcRes = "us-east4"; break;
case "nam10" : ldcRes = "us-central1"; break;
case "nam11" : ldcRes = "us-central1"; break;
case "nam12" : ldcRes = "us-central1"; break;
case "nam-eur-asia1" : ldcRes = "us-central1"; break;
default: ldcRes = location;
}
findLdc = true;
return ldcRes;
}
if(zone) {
var azGR = new GlideRecord('cmdb_ci_availability_zone');
azGR.addQuery('name', zone);
azGR.query();
if (azGR.next()) { // For Zones
var relGR = new GlideRecord('cmdb_rel_ci');
relGR.addQuery("child", azGR.sys_id);
relGR.addQuery("parent.sys_class_name",'cmdb_ci_google_datacenter');
relGR.query();
if (relGR.next()) {
if(relGR.parent.name) {
ldcRes = relGR.parent.name;
findLdc = true;
}
gs.info("GoogleEventHandler: Found LDC with name " + relGR.parent.name + " for availability zone " + zone);
}else{
gs.error("GoogleEventHandler: Failed to find datacenter to availability zone " + azGR.name);
}
} else { // For TCP LB,Region name comming as zone.
var ldcGr = new GlideRecord('cmdb_ci_google_datacenter');
ldcGr.addQuery('name', zone);
ldcGr.query();
if (ldcGr.next()) {
ldcRes = ldcGr.name;
findLdc = true;
gs.info("GoogleEventHandler: Found LDC with name " + ldcRes + " for Region " + zone);
} else {
gs.error("GoogleEventHandler: Failed to find datacenter to Region " + zone);
}
}
}
if (!findLdc) {
gs.info("GoogleEventHandler: failed to find Availability zone/ Region for name " + zone + ", assuming global");
}
return ldcRes;
},
isDeleteEvent : function(jsonBody) {
// check if the type of the discovery is delete, otherwise run discovery
var res = false;
var action = this.getActionType(jsonBody);
if(action.includes("delete")){
res = true;
}
return res;
},
extractCMDBCIType : function(jsonBody) {
var resouceType = this.getResourceType(jsonBody);
gs.info("GoogleEventHandler: extracting class type for "+resouceType);
var ciType;
if(resouceType){
try {
ciType = this._getClassTypeId(resouceType);
}catch (e){
gs.warn("Failed to get cmdb type for object ID "+this.getObjectID(jsonBody)+" and type "+this.getResourceType(jsonBody));
}
}
return ciType;
},
type: 'GoogleEventHandler'
});
Sys ID
472489db730133005ad2db37aef6a763