Name

sn_itom_pattern.NutanixEventHandler

Description

Handling the events from Nutanix and invoking discovery pattern

Script

var NutanixEventHandler = Class.create();

var cloudPatternInvocation = new sn_itom_pattern.CloudPatternInvocation();
var nutanixProbeInvoker = new sn_itom_pattern.NutanixProbeInvoker();
var cloudApplicationDiscovery = new global.CloudApplicationDiscovery();

NutanixEventHandler.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("NutanixEventHandler: Not supported type "+this.getResourceType(jsonBody)+" with object ID "+objectID);
  	}else if(this.shouldInvokeDiscovery(jsonBody)){
  		gs.info("NutanixEventHandler: invoking discovery on event ID "+eventId+" with object ID "+objectID);
  		response = this.invokeDiscovery(jsonBody,eventId,objectID);
  	}else{
  		gs.info("NutanixEventHandler: handing in server event ID "+eventId+" with object ID "+objectID);
  		response.ci = this.handleAlertInServer(jsonBody,objectID,ciType);
  	}
  	response.state = "processed";
  	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
  	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)){
  		if (cmdbCIType == "cmdb_ci_nutanix_vm_instance"){
  			objectId = this.getClusterUUID(jsonBody) + "::" + objectId;
  		}
  		resCI = this.markCIAbsent(objectId,cmdbCIType);
  		gs.info("NutanixEventHandler: CI was marked as absent with object ID "+objectId);
  	}else{
  		gs.warn("NutanixEventHandler: 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("NutanixEventHandler: marking object "+objectID+" for absent");
  			new sn_cmp.CMPReconciler().markCIAbsent(ciGR);
  			resultVal = ciGR.getUniqueValue();
  		}
  	}else{
  		gs.error("NutanixEventHandler: 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("NutanixEventHandler: Invoking discovery from event ID "+eventID+" on object id "+ objectID + " with pattern "+configObj.patternId);
  	var result = this.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("NutanixEventHandler: Finished Event based discovery on object ID "+objectID);
  	}else {
  		gs.error("NutanixEventHandler: 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;
  	return response;
  },

  /*
   * prepare the config type for invoking the discovery
   * @param jsonBody
   * @param eventID
   * @param objectID
   * @returns {{changeType: *|string, classType: *, accountId: *|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 clusterUUID = this.getClusterUUID(jsonBody);
  	var context = this.extractContext(jsonBody);

  	//Prepare the mandatory parameters to pass to the pattern context
  	var configObj = {
  		changeType : actionType,
  		classType: ciClassType,
  		clusterUuid: clusterUUID,
  		inputObjectId: objectID,
  		objectID: objectID,
  		patternId: invocationPatternId,
  		eventId: eventID,
  		eventType: "Nutanix",
  		eventData: JSON.stringify(context)
  	};

  	return configObj;
  },
  
  getPatternType : function(patternID) {
  	var ciGr = new GlideRecord('sa_pattern');
  	ciGr.addQuery('sys_id',patternID);
  	ciGr.query();
  	if(ciGr.next()){
  		return ciGr.ci_type;
  	}
  },
  
  getPatternCIs : function(patternType, patternID) {
  	var ciGr = new GlideRecord('sa_ci_to_pattern');
  	ciGr.addQuery('pattern',patternID);
  	ciGr.query();
  	var rtrn = "\"" + patternType + "\"";
  	while(ciGr.next()){
  		rtrn += ", \"" + ciGr.ci_type + "\"";
  	}
  	return rtrn;
  },

  getResourceType : function(jsonBody) {
  	return "Nutanix " + jsonBody.entity_list[0].entity_type;
  },

  getActionType : function(jsonBody) {
  	return jsonBody.operation_type;
  },

  getClusterUUID : function(jsonBody) {
  	return jsonBody.cluster_uuid;
  },

  getObjectID : function(jsonBody) {
  	return jsonBody.entity_list[0].entity_id;
  },
  
  extractContext : function(jsonBody) {
  	return jsonBody.jsonPayload;
  },
  
  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.toLowerCase().includes("delete")){
  		res = true;
  	}
  	return res;

  },

  extractCMDBCIType : function(jsonBody) {
  	var resouceType = this.getResourceType(jsonBody);
  	gs.info("NutanixEventHandler: 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;
  },
  
  eventBasedDiscovery: function(configObj, response) {
  	if (configObj.changeType.toUpperCase().trim() === "DELETE") {
  		return cloudPatternInvocation.handleDelete(configObj, response);
  	} else {
  		var statusId = this.invokeQuickDiscovery(configObj, response);
  		response.discovery_status = statusId;
  		return (statusId)? true : false;
  	}
  },
  
  invokeQuickDiscovery: function(configObj, response) {
  	cloudPatternInvocation._debug("Start invokeQuickDiscovery", configObj);
  	var discoveryStatusId = cloudPatternInvocation.createDiscoveryStatus(configObj);
  	configObj.discoveryStatusId = discoveryStatusId;
  	if (discoveryStatusId) {
  		cloudPatternInvocation._debug("Start pattern invocation", {});
  		var result = this.discoverSpecificResourceByPattern(configObj);
  		cloudPatternInvocation._debug("End pattern invocation", result);

  		if (result){
  			return discoveryStatusId;
  		}else {
  			cloudPatternInvocation._printEventError(gs.getMessage('invokeQuickDiscovery did not succeed to invoke the pattern:'), configObj, response);
  			return null;
  		}
  	} else {
  		cloudPatternInvocation._printEventError(gs.getMessage('invokeQuickDiscovery did not succeed to create a discovery status:'), configObj, response);
  		return null;
  	}

  	return null; //We should not reach to this point!
  },
  
  discoverSpecificResourceByPattern:  function(configObj) {
  	// Find MID based on Nutanix capability
  	var midSelector = new global.CloudMidSelectionApi();
  	var midID = midSelector.selectMid(null, "Nutanix", null, "{}");
  	var midGr = new GlideRecord('ecc_agent');
  	midGr.get(midID);
  	gs.debug("Selected MID: " + midGr.name);
  	var mid = midGr.name;
  	
  	// Iterate over patterns and launch HorizontalDiscoveryProbe
      var patternGr = new GlideRecord('sa_pattern');
  	patternGr.get(configObj.patternId);
  	if (!patternGr.isValid()) {
  		this._printEventError("discoverSpecificResourceByPattern-> No pattern found having sys_id " , configObj);
  		return false;
  	}
  	
  	gs.info("Discovers specific resource by pattern - " + patternGr.name);
  	this.sendProbe(mid, configObj, patternGr.name) ;
  	return true;
  },
  
  getClusterIP: function(clusterUUID){
  	var ciGr = new GlideRecord('cmdb_ci_nutanix_cluster');
  	ciGr.addQuery('cluster_id',clusterUUID);
  	ciGr.query();
  	if(ciGr.next()){
  		return ciGr.ip_address;
  	}
  },
  
  sendProbe: function(midId, configObj, PatternName) {
  	gs.info("NutanixEventHandler: Invoking probe on Mid "+midId);
  	
  	var jobName = "Pattern Launcher: " + PatternName;
  	var payload = this._createProbePayload(configObj, PatternName);
  	var clusterIp = this.getClusterIP(configObj.clusterUuid);
  	this._sendProbe(midId,payload,jobName,clusterIp,configObj);
  },
  
  _createProbePayload: function(configObj, PatternName) {
  	var payload = new XMLDocument2();
  	payload.createElement("parameters");
  	var patternType = this.getPatternType(configObj.patternId);
  	
  	// prePatternExecutionData
  	nutanixProbeInvoker._addParameter(payload,'prePatternExecutionData', "{ \"fMapOfStrings\" : { \"input_object_id\" : \"" + configObj.inputObjectId + "\", \"cluster_uuid\" : \"" + configObj.clusterUuid + "\", \"cluster_ip\" : \"" + this.getClusterIP(configObj.clusterUuid) + "\"}, \"executePattern\" : true, \"empty\" : false, \"className\" : \"PrePatternExecutionDTO\" }");
  	nutanixProbeInvoker._addParameter(payload,'pattern_type', patternType);
  	nutanixProbeInvoker._addParameter(payload,'patternMetadata', "{ \"relatedTables\" : [ \"java.util.ArrayList\", [ \"cmdb_key_value\" ] ], \"ciTypes\" : [ \"java.util.ArrayList\", [ " + this.getPatternCIs(patternType, configObj.patternId) + " ] ] }");
  	
  	// set pattern information
  	var computerSystem = "{ \"solaris\" : false, \"managementIP\" : \"No Source\", \"primaryManagementIP\" : \"No Source\", \"addressWidth\" : 0, \"hpux\" : false, \"aix\" : false, \"computerIP\" : \"No Source\", \"ciSnapshotId\" : 0 }";
  	nutanixProbeInvoker._addParameter(payload,'computerSystem', computerSystem);
  	nutanixProbeInvoker._addParameter(payload,'patternId', configObj.patternId);
  	nutanixProbeInvoker._addParameter(payload,'pattern', PatternName);
  	
  	// set infra context params
  	nutanixProbeInvoker._addParameter(payload,'used_by_runbook', "true");
  	nutanixProbeInvoker._addParameter(payload,'glide.xmlhelper.trim.enable', "true");
  	nutanixProbeInvoker._addParameter(payload,'probe', "4f64c6389f230200fe2ab0aec32e7068");
  	nutanixProbeInvoker._addParameter(payload,'used_by_discovery', "true");
  	nutanixProbeInvoker._addParameter(payload,'probe_name', "Horizontal Pattern");
  	nutanixProbeInvoker._addParameter(payload,'excludeSshInteractivePatterns', "DB2 On Linux,WMB Flow On Unix,WMB On Unix");
  	nutanixProbeInvoker._addParameter(payload,'fileTracking', "{ \"maxFileSize\" : 500000, \"trackedFilesRelType\" : \"Contains::Contained by\", \"maxFilesPerCi\" : 50, \"changeCountLimitDto\" : { \"count\" : 4, \"days\" : 7 }, \"simplifiedComparisonMinSize\" : 50000, \"fileTrackingEnabled\" : true }");

  	return payload;
  },
  
  _sendProbe: function(midId, payload,jobName,clusterIp,configObj) {
  	var gr = new GlideRecord('ecc_queue');
  	gr.initialize();
  	gr.setValue('agent_correlator', configObj.discoveryStatusId);
  	gr.setValue('agent', 'mid.server.'+midId);
  	gr.setValue('source', clusterIp);
  	gr.setValue('name', jobName);
  	if (payload) {
  		gr.setValue('payload', payload.toString());
  	}
  	gr.setValue('topic', 'HorizontalDiscoveryProbe');
  	gr.setValue('queue', 'output');
  	gr.setValue('state', 'ready');
  	gr.setValue('priority', '0');
  	gr.insert();
  },

  type: 'NutanixEventHandler'
});

Sys ID

12dbb608db0e3700e7dc7c4daf9619b7

Offical Documentation

Official Docs: