Name

sn_itom_pattern.CloudPatternInvocation

Description

No description available

Script

var CloudPatternInvocation = Class.create();
CloudPatternInvocation.prototype = {
  isDebug: false,
  debugCounter: 1,
  eventId: '',
  deleteStrategy: {
  	KEEP: '1',
  	DELETE: '2',
  	ABSENT: '3',
  	DELETE_RELATED: '4'
  },

  _debug: function(msg, obj) {
  	if (this.isDebug) {
  		gs.info('@@@CloudPatternInvocation[{0}][{1}]->{2}   data->{3}', this.debugCounter, this.eventId, msg, JSON.stringify(obj, null, 2));
  		this.debugCounter++;
  	}
  },

  initialize: function() {},

  _printEventError: function(errorMsg, configObj, response) {
  	var msg =  '@@@CloudPatternInvocation->' +errorMsg + " event configObj- " + JSON.stringify(configObj, null, 2);
  	response.state = 'error';
  	response.error_message = msg;
  	gs.error(msg);
  },

  _validateConfiguratione: function(configObj, response) {
  	//Validate mandatory inputs on the configObj
  	var errMessage = 'Error: invokeQuickDiscovery -> ';

  	if (!configObj || typeof configObj !== 'object') {
  		errMessage += 'expect to get configuration object as an argument:';
  		this._printEventError(errMessage, configObj, response);
  		return false;
  	} else {
  	    // removed eventData from mandatory params as this params is now empty.
  		var mandatoryParams = ["changeType", "classType", "accountId", "ldc", "inputObjectId", "cloudId", "patternId", "eventId", "eventType"];
  		for (var i = 0; i < mandatoryParams.length; i++) {
  			if (!configObj[mandatoryParams[i]]) {
  				errMessage += 'expect to get mandatory parameter within the object argument: ' + mandatoryParams[i];
  				this._printEventError(errMessage, configObj, response);
  				return false;
  			}
  		}
  	}
  	return true;
  },

  /* This method expect to get an object of configuration with some mandatory data e.g.
   *	@Param configObj = {
   *  changeType: String,
   *  classType: String,
   *  accountId: String,
   *  ldc: String,
   *  inputObjectId: String,
   *  cloudId: String,
   *  patternId, String
   *  eventData: String
   * }
   * @Param response- holds the object for the response (e.g. response.state, response.error_message, response.discovery_status...)
   * return true if pattern was launched and  'Discovery Status Id' exists or false in any case of error.getContentType
   *		 Detailed of the invocation is returned through the response obj
   */
  eventBasedDiscovery: function(configObj, response) {
  	this._debug("Start eventBasedDiscovery", configObj);
  	if (!this._validateConfiguratione(configObj, response)) {
  		return false;
  	}
  	this.eventId = configObj.eventId;

  	//Retrieving the actuall sysIds from the 'strings'
  	var serviceAccountSysId = this.getServiceAccountSysId(configObj.accountId, response);
  	if (!serviceAccountSysId){
  		return false;
  	}

  	var ldcsMap = this.getServiceAccountLDCs(serviceAccountSysId,response);
  	if(!ldcsMap){
  		gs.info("CloudPatternInvocation: Failed to find LDCs for account id "+configObj.accountId);
  		return false;
  	}
  	if (Object.keys(ldcsMap).length < 1){
  		return false;
  	}

  	var currentLdcSysId = this.getLdcSysId(ldcsMap, configObj.ldc, response);
  	if (!currentLdcSysId && configObj.eventType === "AWS"){
  		return false;
  	}

  	//Enrich the config with necessary data for the discovery invocation
  	configObj.serviceAccountSysId = serviceAccountSysId;
  	configObj.ldcSysId = currentLdcSysId;
  	configObj.ldcsMap = ldcsMap;

  	if (configObj.changeType.toUpperCase().trim() === "DELETE") {
  		return this.handleDelete(configObj, response);
  	} else {
  		//Invoke the Discovery on the AWS resource we got in the event with its correlated pattern
  		var statusId = this.invokeQuickDiscovery(configObj, response);
  		response.discovery_status = statusId;
  		gs.info("'@@@CloudPatternInvocation->eventBaseDiscovery() -> pattern invocation ended with statusId - " + statusId);
  		return (statusId)? true : false;
  	}
  },

  deleteCi: function(ci_sys_id, chosenStrategy, response) {
  	var successFlag = false;
  	var ciGr = new GlideRecord("cmdb_ci");
  	ciGr.addQuery("sys_id", ci_sys_id);
  	ciGr.query();

  	if (ciGr.next()) {
  		switch (chosenStrategy) {
  			case this.deleteStrategy.DELETE:
  				this._debug("deleteCi-> delete the CI -" + ci_sys_id, chosenStrategy);
  				//The delete can work only from scope Global-> so the implementation will be in cloudApplicationDiscovery
  				var cloudApplicationDiscovery = new global.CloudApplicationDiscovery();
  				successFlag = cloudApplicationDiscovery.deleteCiById(ci_sys_id);
  				break;
  			case this.deleteStrategy.KEEP:
  				this._debug("deleteCi-> Do nothing SKIP the CI ", chosenStrategy);
  				successFlag = true;
  				break;
  			case this.deleteStrategy.ABSENT:
  				this._debug("deleteCi-> set the state to absent  ", chosenStrategy);
  				ciGr.setValue('install_status', 100);  //100 corresponds to status Absent
  				ciGr.update();
  				successFlag = true;
  				break;
  			case this.deleteStrategy.DELETE_RELATED:
  				this._printEventError("deleteCi -> delete 'related strategy' is not currently supported. Should be one of the others strategies - " + JSON.stringify(this.deleteStrategy, null, 2), chosenStrategy, response);
  				successFlag = false;
  				break;
  			default:
  				this._printEventError("deleteCi -> Unexpected delete strategy. Should be one of the strategies except 'related strategy' - " + JSON.stringify(this.deleteStrategy, null, 2), chosenStrategy, response);
  				successFlag = false;
  		}
  	} else {
  		this._printEventError(gs.getMessage('deleteCi -> did not find any CI in cmdb_ci that comply the given sys_id'), ci_sys_id, response);
  		return false;
  	}
  	return successFlag;
  },

  /**
   *	Find the unique target CI in the relation table(should be from a specific type, have a relation of host on that should be pointed to the LDC)
   *	@Param listOfSydIds - array of suspected Cis which all comply to the same name of the objectId
   *	@Param chosenStrategy - The current chosen delete strategy
   *	return boolean - True if succeeded to delete the CI
   **/
  searchAndDeleteCi: function(configObj, response, listOfSydIds, chosenStrategy) {
  	var successFlag = false;
  	this._debug("Start searchAndDeleteCi", listOfSydIds);
  	//The CI is always dependent on the LDC and hosted on it(so the LDC is the child)
  	var relCiGr = new GlideRecord("cmdb_rel_ci");
  	relCiGr.addQuery("parent", "IN", listOfSydIds.toString());
  	relCiGr.addQuery("child", configObj.ldcSysId);
  	relCiGr.addQuery('type', '5f985e0ec0a8010e00a9714f2a172815'); // Hosted-on relation
  	relCiGr.query();

  	if (relCiGr.next()) {
  		var relParent = relCiGr.getValue("parent");
  		var relChild = relCiGr.getValue("child");
  		var relSysId = relCiGr.getUniqueValue();
  		var relMsg = "parent[" + relParent + "], child-[" + relChild + "], rel_ci_sys_id[" + relSysId + "]";
  		this._debug("searchAndDeleteCi-> Preparing to delete CI according the relation in cmdb_rel_ci ", relMsg);
  		successFlag = this.deleteCi(relParent, chosenStrategy, response);
  	} else {
  		this._printEventError(gs.getMessage('searchAndDeleteCi -> did not find any objectId under a specific LDC'), configObj, response);
  		successFlag = false;
  	}
  	return successFlag;
  },

  handleDelete: function(configObj, response) {
  	var successFlag = false;
  	this._debug("Start handleDelete", configObj);
  	//Retrieve the default pattern delete strategy setting
  	var chosenStrategy = gs.getProperty("service.watch.cloud.pattern.invocation.default.delete.strategy", this.deleteStrategy.ABSENT);

  	//Try to retrieve a specific delete stratefy if exists for this CI
  	var patternCiStrategy = new GlideRecord('sa_ci_to_pattern');
  	patternCiStrategy.addQuery("pattern", configObj.patternId);
  	patternCiStrategy.addQuery('is_main_ci', true);
  	patternCiStrategy.query();

  	if (patternCiStrategy.next()) {
  		this._debug("handleDelete-> Found a configured delete strategy -", patternCiStrategy.getValue("deletion_strategy"));
  		//Direct change to the CMDB set the CI according to the pattern delete strategy (deleted=1/keep=2/absent=3)
  		var deletion_strategy = patternCiStrategy.getValue("deletion_strategy");
  		chosenStrategy = (deletion_strategy) ? deletion_strategy : chosenStrategy;
  	} else {
  		this._debug("handleDelete-> no delete strategy was found", {});
  	}
  	
  	//AWS cloud load balancer exists in two configurations
  	//for ELBv2 object_id is ARN, for classic LB object_id is name
  	//so we need to make sure that cloudId will be used only in the case of ELBv2
  	var elbv2=false;
  	if (configObj.eventType === "AWS" && configObj.classType == "cmdb_ci_cloud_load_balancer"){
  		var cloudResType = new GlideRecord('sn_capi_resource_type');
  		cloudResType.addQuery('name','AWS::ElasticLoadBalancingV2::LoadBalancer');
  		cloudResType.addQuery('pattern',configObj.patternId);
  		cloudResType.query();
  		if (cloudResType.hasNext()){
  			elbv2=true;
  		}
  	}

  	//Serch for a specific unique CI acording to objectID within a specific LDC whith a relation of host on
  	var cmdbCloudCi = new GlideRecord(configObj.classType);
  	
  	if (configObj.classType != "cmdb_ci_dynamodb_table" && configObj.classType != "cmdb_ci_cloud_object_storage" && elbv2===false) {

  		cmdbCloudCi.addQuery("object_id", configObj.inputObjectId);
  	}
  	else {
  		cmdbCloudCi.addQuery("object_id", configObj.cloudId);
  	}
  	cmdbCloudCi.query();
  	var listOfSydIds = [];
  	while (cmdbCloudCi.next()) {
  		listOfSydIds.push(cmdbCloudCi.getUniqueValue());
  	}

  	this._debug("handleDelete-> Found a list of suspected object Ids - ", listOfSydIds);
  	switch (listOfSydIds.length) {
  		case 0:
  			this._debug('handleDelete-> deleteCi expect to get at least one sysId of a CI that resides within a specific LDC', configObj);
  			response.state = 'skipped';
  			response.error_message = gs.getMessage('This event tried without a success to delete the CI with object_id[{0}] and class type[{1}]', [configObj.inputObjectId,configObj.classType]);
  			successFlag = false;
  			break;
  		case 1:
  			successFlag = this.deleteCi(listOfSydIds[0], chosenStrategy, response); //Only one sys_id, so no need to search
  			break;
  		default:
  			successFlag = this.searchAndDeleteCi(configObj, response, listOfSydIds, chosenStrategy);
  	}
  	response.state = (successFlag)? 'processed' : response.state;
  	return successFlag;
  },

  invokeQuickDiscovery: function(configObj, response) {
  	this._debug("Start invokeQuickDiscovery", configObj);
  	if (configObj.serviceAccountSysId && configObj.ldcSysId) {
  		var discoveryStatusId = this.createDiscoveryStatus(configObj);
  		configObj.discoveryStatusId = discoveryStatusId;
  		if (discoveryStatusId) {
  			this._debug("Start pattern invocation", {});
  			var cloudApplicationDiscovery = new global.CloudApplicationDiscovery();
  			var result = cloudApplicationDiscovery.discoverSpecificResourceByPattern(configObj);
  			this._debug("End pattern invocation", result);

  			if (result){
  				return discoveryStatusId;
  			}else {
  				this._printEventError(gs.getMessage('invokeQuickDiscovery did not succeed to invoke the pattern:'), configObj, response);
  				return null;
  			}
  		} else {
  			this._printEventError(gs.getMessage('invokeQuickDiscovery did not succeed to create a discovery status:'), configObj, response);
  			return null;
  		}
  	} else {
  		this._printEventError(gs.getMessage('invokeQuickDiscovery expect to get all sysIds out of the string names from the mandatory parameters: '), configObj, response);
  		return null;
  	}

  	return null; //We should not reach to this point!
  },

  getServiceAccountSysId: function(serviceAccountIdStr, response) {
  	this._debug("Start getServiceAccountSysId", serviceAccountIdStr);
  	var serviceAccountGr = new GlideRecord('cmdb_ci_cloud_service_account');
  	if (!serviceAccountGr.get('account_id', serviceAccountIdStr)) {
  		this._printEventError("invokeQuickDiscovery-> getServiceAccountSysId() - can not find account -", serviceAccountIdStr, response);
  		return null;
  	}

  	var sysId = serviceAccountGr.getUniqueValue();
  	this._debug("End getServiceAccountSysId", sysId);
  	return (sysId) ? sysId : null;
  },

  getServiceAccountLDCs: function(serviceAccountSysId, response) {
  	this._debug("Start getServiceAccountLDCs", serviceAccountSysId);
  	//The LDC is always dependent on the Account and hosted on it(so the Account is the child and all the parrent should be the LDC)
  	var relCiGr = new GlideRecord("cmdb_rel_ci");
  	relCiGr.addQuery("child", serviceAccountSysId);
  	relCiGr.addQuery('type', '5f985e0ec0a8010e00a9714f2a172815'); // Hosted-on relation
  	relCiGr.query();

  	//Garner all ldcs Ids
  	var listOfLDCSydIds = [];
  	while (relCiGr.next()) {
  		listOfLDCSydIds.push(relCiGr.getValue("parent"));
  	}
  	this._debug("listOfLDCSydIds", listOfLDCSydIds);

  	if (listOfLDCSydIds.length < 1) {
  		gs.error("AzureAlertHandlerV2-> getServiceAccountRegions() - No LDCs were found under the account ", serviceAccountSysId);
  		return null;
  	}

  	var dataCenterGr = new GlideRecord('cmdb_ci_logical_datacenter');
  	dataCenterGr.addQuery("sys_id", "IN", listOfLDCSydIds.toString());
  	dataCenterGr.query();

  	//Build a map of ldcs and their sys_ids
  	var ldcsMap = {};
  	while (dataCenterGr.next()) {
  		var region = dataCenterGr.getValue("region");
  		var ldcSysId = dataCenterGr.getUniqueValue();
  		ldcsMap[region] = ldcSysId;
  	}
  	this._debug("End getServiceAccountLDCs", ldcsMap);
  	return ldcsMap;
  },

  getLdcSysId: function(ldcsMap, ldcStr, response) {
  	this._debug("Start getLdcSysId ldc-"+ldcStr, ldcsMap);
  	if (!ldcsMap[ldcStr]) {
  		this._printEventError("invokeQuickDiscovery-> getServiceAccountSysId() - can not find LDC -", ldcStr, response);
  		return null;
  	} else {
  		this._debug("End getLdcSysId", ldcsMap[ldcStr]);
  		return ldcsMap[ldcStr];
  	}
  },

  createDiscoveryStatus: function(configObj) {
  	var newStatus = new GlideRecord("discovery_status");
  	newStatus.setValue("description", configObj.eventType+"-"+configObj.eventId);
  	newStatus.setValue("source", "cloud_quick_discovery_upon_event");
  	newStatus.setValue("discover", "Cloud");

  	var statusId = newStatus.insert();
  	return (statusId) ? statusId : null;
  },

  type: 'CloudPatternInvocation'
};

Sys ID

70af29e31b453f802e1cfccf1d4bcb1f

Offical Documentation

Official Docs: