Name

global.CloudApplicationDiscovery

Description

This script is called when Cloud Application Discovery schedule is started. It launches horizontal disocovery probes that discover cloud based applications such as AWS RDS, AWS ELB, Azure Web Server etc.

Script

var CloudApplicationDiscovery = Class.create();
CloudApplicationDiscovery.prototype = {
  initialize: function() {
  	this.probes_sent = false;
  	this.patternAppService = new SNC.PatternLibraryAppService();
  	this.cloudDiscoveryUtil = new CloudResourceDiscoveryUtil();

  	// Exclude Azure OOTB Top Down patterns (with datacenter os family) from horizontal discovery
  	// Excluded patterns: Azure WebSite TD, Azure Functions TD, Azure DataBase TD
      this.azureTopDownPatternsExcludeSysIds = ['3ccb90d993183200859c5999357ffbad', '0a48c4251b69a51005e5777d8b4bcbb6', 'a62cbf4193183200859c5999357ffb2e'];
  },

  _printEventError: function (errorMsg, configObj) {
  	gs.error(errorMsg + " event configObj- "+JSON.stringify(configObj , null, 2));
  },

  /**
     run discovery of cloud applications on all logical data center hosted by the service account
  */
  discoverServiceAccount: function (serviceAccount, status) {
  	var relCiGr = new GlideRecord("cmdb_rel_ci");
  	relCiGr.addQuery("child", serviceAccount);
  	relCiGr.addQuery('type', '5f985e0ec0a8010e00a9714f2a172815'); // Hosted-on relation
  	relCiGr.query();
  	while (relCiGr.next()) {
  		gs.log("Discovering LDC " + relCiGr.parent);
  		this.discoverLDC(serviceAccount, relCiGr.parent, status, []);
  	}
  	return this.probes_sent;
  },

  deleteCiById:  function(ci_sys_id) {
  	var ciGr = new GlideRecord("cmdb_ci");
  	ciGr.addQuery("sys_id", ci_sys_id );
  	ciGr.query();

  	if (ciGr.next()) {
  		ciGr.deleteRecord();
  		return true;
  	} else {
  		this._printEventError("CloudApplicationDiscovery-> deleteCiById -> Can't find Ci's sys_id " , ci_sys_id);
  		return false;
  	}
  },

  /**
   *  Run discovery of a specific cloud resource applications via pattern invocation
   *  configObj ={
   *	   serviceAccountSysId - sys_id of the service account hosting the logicial data center
   *	   ldcSysId - sys_id of the logical data center
   *	   discoveryStatusId - sys_id of the created DiscoveryStatus for that
   *     ldcsMap - A Map between ldc-name to its sys_id of the current serviceAccount
   *	   patternId - sys_id of the pattern to be invoked
   *  }
   */
  discoverSpecificResourceByPattern:  function(configObj) {
  	// Check if the LDC exists
  	var ldcGr = new GlideRecord("cmdb_ci_logical_datacenter");
  	ldcGr.get(configObj.ldcSysId);
  	if (!ldcGr.isValid()) {
  		this._printEventError("discoverSpecificResourceByPattern-> No logical datacenter found with the given ldc sys_id", configObj);
  		return false;
  	}

  	// pick up the service account ID.. this is needed if the customer has specified a MID script override which needs the service account id
  	var serviceAccountGr = new GlideRecord("cmdb_ci_cloud_service_account");
  	serviceAccountGr.get(configObj.serviceAccountSysId);
  	var service_account_id = serviceAccountGr.getValue("account_id");

  	//Check if Specific mid is defined or not
  	var discoveryStatusGR = new GlideRecord('discovery_status');
      discoveryStatusGR.get(configObj.discoveryStatusId);
  	var midServerName = this.cloudDiscoveryUtil.getSelectedMidFromSchedule(discoveryStatusGR.dscheduler.sys_id);
  	
  	// Find MID based on LDC sys_class_name
  	var mid = midServerName ? midServerName : this.findMid(configObj.ldcSysId, service_account_id);

  	if (!mid) {
  		this._printEventError("discoverSpecificResourceByPattern-> No MID found per required capabilities", configObj );
  		return false;
  	}

  	var ldcName = ldcGr.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.log("Discovers specific resource by pattern - " + patternGr.name);

  	this.launchPatternSingleMode(mid, configObj) ;
  	return this.probes_sent;
  },

  /**
     run discovery using specific pattern on given service account
  */
  discoverServiceAccountWithPattern: function (serviceAccount, status, discovery_schedule, pattern_id, agent) {
  	
  	// Checking for Specified MID
  	var midServerName = this.cloudDiscoveryUtil.getSelectedMidFromSchedule(discovery_schedule);
  	agent = midServerName ? midServerName : agent;
  	var relCiGr = new GlideRecord("cmdb_rel_ci");
  	relCiGr.addQuery("child", serviceAccount);
  	relCiGr.addQuery('type', '5f985e0ec0a8010e00a9714f2a172815'); // Hosted-on relation
  	relCiGr.query();
  	while (relCiGr.next()) {
  		gs.log("Discovering LDC " + relCiGr.parent);
  		this.discoverLDCwithPattern(serviceAccount, relCiGr.parent, status, discovery_schedule, pattern_id, agent);
  	}

  	// If no probes were sent, close the discovery status
  	if (!this.probes_sent) {
  		var discoStatusGr = new GlideRecord('discovery_status');
  		discoStatusGr.get(status);
  		if (discoStatusGr.isValid()) {
  			discoStatusGr.setValue('state', 'Completed');
  			discoStatusGr.update();
  		}
  	}
  },

  /**
     run discovery of cloud applications on given logical data center
     serviceAccount - sys_id of the service account hosting the logicial data center
     ldc - sys_id of the logical data center
     status - DiscoveryStatus object
     ciTypes - class names to CIs to be discovered. if null or empty, we discover all
  */
  discoverLDC: function(serviceAccount, ldc, status, ciTypes) {
  	// Check if the LDC exists
  	var ldcGr = new GlideRecord("cmdb_ci_logical_datacenter");
  	ldcGr.get(ldc);
  	if (!ldcGr.isValid()) {
  		gs.log("No logical datacenter found having sys_id " + ldc);
  		return false;
  	}

  	// pick up the service account ID.. this is needed if the customer has specified a MID script override which needs the service account id
  	var serviceAccountGr = new GlideRecord("cmdb_ci_cloud_service_account");
  	serviceAccountGr.get(serviceAccount);
  	var service_account_id = serviceAccountGr.getValue("account_id");

  	// Change domain do that we pick the correct MID
      var dh = new SNC.DomainHelper();
  	try {
          dh.adjustDomainByCiSysId(ldc);
  		// Checking for the Specific MID
  		var discoveryStatusGR = new GlideRecord('discovery_status');
  		discoveryStatusGR.get(status);	
  		var midServerName = this.cloudDiscoveryUtil.getSelectedMidFromSchedule(discoveryStatusGR.dscheduler.sys_id);
  		
  		// If MID is defined select same else Find MID based on LDC sys_class_name
  		var mid = midServerName ? midServerName : this.findMid(ldc, service_account_id);
  		if (!mid) {
  			gs.log("No MID found per required capabilities" );
  			return false;
  		}

  		var ldcName = ldcGr.name;

  		// Iterate over patterns and launch HorizontalDiscoveryProbe
  		this.launchPatterns(ldcGr.sys_class_name, ldc, ldcName, serviceAccount, mid, status, ciTypes);
  	} finally {
  		dh.restoreCurrentSessionParams();
  	}

  	return this.probes_sent;
  },

  /**
     run discovery using specific pattern on a given LDC
     serviceAccount - sys_id of the service account hosting the logicial data center
     ldc - sys_id of the logical data center
     status - DiscoveryStatus object
     discovery_schedule - the discovery schedule assocateated with the status
     pattern_id - the pattern to execute
     agent - MID server
  */
  discoverLDCwithPattern: function(serviceAccount, ldc, status, discovery_schedule, pattern_id, agent) {
  	// Check if the LDC exists
  	var ldcGr = new GlideRecord("cmdb_ci_logical_datacenter");
  	ldcGr.get(ldc);
  	if (!ldcGr.isValid()) {
  		gs.log("No logical datacenter found having sys_id " + ldc);
  		return false;
  	}

  	var ldcName = ldcGr.name;

  	// Launch the pattern
  	this.launchPattern(serviceAccount, ldc, ldcName, status, discovery_schedule, pattern_id, agent);
  },


  launchPatternCore: function(ldc, ldcName, status, discovery_schedule, pattern_id, midAgent , probeParams, ppe ) {

  	if (arguments.length !== 8) {
  		gs.error("launchPatternCore -> Unexpected number of input arguments -" + JSON.stringify(arguments , null, 2));
  		return null;
  	}

  	var probeValues = {};
  	probeValues['computer'] = ldc;

  	// Add dummy entry point. MID pattern execution engine needs that
  	var entryPoint = {};
  	entryPoint['sys_class_name'] = 'cmdb_ci_endpoint_tcp';

  	probeParams['pattern_id'] = pattern_id;
  	probeParams['endpointAttributes'] = new JSON().encode(entryPoint);
  	probeParams['logical_datacenter_name'] = ldcName;
  	var sourceName = ldcName; //PRB1294636: Since we don't have source in cloud and we wantl to avoid empty cells

  	var patternLauncher = new SNC.PatternLauncherManager();
  	patternLauncher.launchPattern(status, discovery_schedule, midAgent, sourceName, pattern_id, probeParams, probeValues, ppe);

  	this.probes_sent = true;
  },

  /**
  * launch discovery pattern ONLY on a specific resource of a specific LDC (logical data center)
  */
  launchPatternSingleMode: function(midAgent, configObj) {
  	//Adding the necessary parmaeters to the patern context infavor the pattern could parse it
  	var ppe = this.createPreExecutionData( configObj.serviceAccountSysId, configObj.ldcSysId);
  	ppe.addString('account_id', configObj.accountId);
  	ppe.addString('ldc_id', configObj.ldcSysId);
  	ppe.addString('input_object_id', configObj.inputObjectId);
  	ppe.addString('cloudId', configObj.cloudId);

  	//For Azure we need to send all aviliable LDCs in order the MID could chose the right one from them later on
  	if (configObj.eventType === "AZURE"){
  		ppe.addTableEntry('ldcs_map' , configObj.ldcsMap);
  	}

  	//Add the parameters also on the prob for future manipulation on the 'HorizontalDiscoveryResultHandler -> runDeleteHandler'
  	var probeParams = {
  		"account_id": configObj.accountId,
  		"ldc_id" : configObj.ldcSysId,
  		"event_id": configObj.eventId,
  		"input_object_id": configObj.inputObjectId,
  		"class_type" : configObj.classType,
  		"send_event_on_completion" : true,
  		"correlation_id" : configObj.correlation_id
  	};

  	//No need for discovery_schedule(sending empty discoveryStatusId ) - We want to do a quick discovery and avoid multi schedules
  	this.launchPatternCore(configObj.ldcSysId, configObj.ldc , configObj.discoveryStatusId, '', configObj.patternId, midAgent, probeParams, ppe);
  },

  /**
  * launch discovery of specific pattern on specific LDC (logical data center)
  */
  launchPattern: function(serviceAccount, ldc, ldcName, status, discovery_schedule, pattern_id, agent) {
  	var ppe = this.createPreExecutionData(serviceAccount, ldc);
  	var probeParams = {};
  	this.launchPatternCore(ldc, ldcName, status, discovery_schedule, pattern_id, agent, probeParams, ppe);
  },

  /*
     Find patterns associated with the class name (having os_familty that contains this name)
  */
  launchPatterns: function (ldcClassName, ldc, ldcName, serviceAccount, mid, status, ciTypes) {
  	var patternList = this.patternAppService.getPatternsByFilter(['1'], ciTypes, [ldcClassName], true);
  	var patternGr = new GlideRecord('sa_pattern');
  	patternGr.addQuery('sys_id',patternList);
  	patternGr.addQuery('sys_id', 'NOT IN', this.azureTopDownPatternsExcludeSysIds);
  	patternGr.query();
  	while (patternGr.next()) {
  		// Omit patterns of included items
  		if (this.patternAppService.isIncludedPattern(patternGr.getUniqueValue()))
  			continue;
  		gs.log("Sending probe on pattern " + patternGr.name);
  		this.launchProbe(ldc, ldcName, serviceAccount, patternGr, mid, status);
  	}

  },

  /*
  	Launch horizontal discovery probe per given pattern and LDC
  */
  launchProbe: function(ldc, ldcName, serviceAccount, patternGr, mid, status) {
  	var probeGr = new GlideRecord('discovery_probes');
  	probeGr.addQuery('name', 'Horizontal Pattern');
  	probeGr.query();
  	if (probeGr.next()) {
  		var values = {};
  		values['computer'] = ldc;
  		var probe = new SncProbe(probeGr, values);
  		probe.addParameter('pattern', patternGr.name);
  		probe.addParameter('patternId', patternGr.sys_id);
  		probe.addParameter('pattern_type', patternGr.ci_type);
  		probe.addParameter('logical_datacenter_name', ldcName);
  		if (JSUtil.notNil(status))
  			probe.setCorrelator(status);
  		this.addParamsToContext(probe, serviceAccount, ldc);
  		probe.create(mid);
  		this.probes_sent = true;

  	}
  } ,

  /*
  * Create the pre-execution-data structure. It contains the service account, ldc, and relation type
  */
  createPreExecutionData:function(serviceAccount, ldc) {
  	var ppe = new SNC.PrePatternExecutionData();

  	// Add service account
  	var serviceAccountGr = new GlideRecord('cmdb_ci_cloud_service_account');
  	serviceAccountGr.get(serviceAccount);
  	if (serviceAccountGr.isValid())
  		ppe.addTableEntry('service_account', this.glideRecordToMap(serviceAccountGr));

  	/*
  	 * Add LDC
  	 * When LDC is null, it still manages to pass ldcGr.isValid()
  	 * This causes a 'null' key to be created in addition to a 'null' value
  	 * in 'ldc' key
  	 */
  	if (ldc != null) {
  		var ldcGr = new GlideRecord('cmdb_ci_logical_datacenter');
  		ldcGr.get(ldc);
  		if (ldcGr.isValid()){
  			var className = ldcGr.getValue('sys_class_name');
  			ppe.addTableEntry(className, this.glideRecordToMap(ldcGr));
  			ppe.addTableEntry('ldc', this.glideRecordToMap(ldcGr));

  			ppe.addString('build_relation_auto','true');
  		}
  	}

  	// Add relation type
  	ppe.addString('hosted_relation_type', 'Hosted on::Hosts');
  	ppe.addString('hosted_relation_id', '5f985e0ec0a8010e00a9714f2a172815');
  	return ppe;
  },

  /*
  	Add to probe the service account,LDC details, and hosting relation type
  */
  addParamsToContext: function(probe, serviceAccount, ldc) {
  	var ppe = this.createPreExecutionData(serviceAccount, ldc);

  	// Convert to JSON and add to probe
  	var json = ppe.getJSON();
  	probe.addParameter('prePatternExecutionData', json);

  	// Add dummy entry point. MID pattern execution engine needs that
  	var entryPoint = {};
  	entryPoint['sys_class_name'] = 'cmdb_ci_endpoint_tcp';
  	probe.addParameter('endpointAttributes', new JSON().encode(entryPoint));

  },

  /*
  * convert glide record to javascript map
  */
  glideRecordToMap: function (gr) {
  	var map = {};
  	var iterator = gr.getFields().iterator();
  	while (iterator.hasNext()) {
  		var field = iterator.next();
  		if (JSUtil.notNil(gr.getValue(field.getName()))) {
  			var fieldName = field.getName() + '';

  			if (fieldName.indexOf('sys_') != 0 || fieldName == 'sys_class_name')
  				map[field.getName()] = gr.getValue(field.getName());
  		}
  	}
  	if (map) {
  		map['sys_id'] = gr.getValue('sys_id');
  	}
  	return map;
  },


  /*
  	Find MID based on the class of the LDC
  */
  findMid: function(ldc, service_account_id) {
  	var ldcGr = new GlideRecord('cmdb_ci_logical_datacenter');
  	if (!ldcGr.get(ldc)) {
  		gs.log('Logical datacenter ' + ldc + ' not found in the CMDB');
  		return null;
  	}

  	var invocationContext = {};
  	invocationContext['service_account_id'] = service_account_id;

  	var cmdMidSelector = new global.CloudMidSelectionApi();
  	var mid_id = cmdMidSelector.selectMid(null, null, ldcGr.region, JSON.stringify(invocationContext));

  	if (!mid_id)
  		return mid_id;

  	// Find the mid name
  	var midGr = new GlideRecord('ecc_agent');
  	if (!midGr.get(mid_id)) {
  		gs.log('MID ' + mid_id + ' not found in table ecc_agent');
  		return null;
  	}
  	return midGr.name;
  },

  type: 'CloudApplicationDiscovery'

};

Sys ID

f82bdda4c35322003e76741e81d3ae91

Offical Documentation

Official Docs: