Name

global.StartDiscovery

Description

Starts Discovery jobs.

Script

// Discovery class

/*
* Starts Discovery jobs.
*/
var StartDiscovery = Class.create();

StartDiscovery.prototype = {
  
  initialize: function(source) {
  	this.schedule = null;
  	this.job = null;
  	this.status = null;
  	if (JSUtil.nil(source))
  		this.source = 'Schedule_Discovery';
  	else
  		this.source = source;
  },

  _specificMid: function() {
  	return 'specific_mid' === this.schedule.midSelMethod;
  },
  /*
  * A private method that cancels the discovery status and logs an error
  * @private
  */
  _cancelDiscoveryAndLogError: function() {
  	// cancel the discovery and its running statuses
  	(new SncDiscoveryCancel()).cancelAll(this.status.sysID);
  	var msg = gs.getMessage('Discovery canceled on previously started schedule [{0}] due to mid not available\n', this.schedule.name);
  	var dlog = new DiscoveryLogger(this.status.sysID);
  	dlog.error(msg, 'StartDiscovery');
  },

  /*
  * Invoked by the "Discovery" script include to kick off a scheduled (or "Discover Now") discovery.
  * schedule: a GlideRecord with the instance of discovery_schedule that this discovery is based on
  * job:      a GlideRecord with the instance of sys_trigger that triggered this discovery
  */
  startFromSchedule: function(schedule, job) {
  	this.job = new DiscoveryJob(job);
  	this.schedule = new DiscoverySchedule(this.job.scheduleID);
  	this.status = new DiscoveryStatus(this.job, 'Scheduled', this.source);
  	if (this.status.description == 'Scheduled') {
  		if (this.atMaxConcurrentRunsForSchedule(schedule))
  			return;
  		else if (this.schedule.valid) {
  			// resolve any open issues
  			var errorManager = new SNC.DiscoveryErrorManager();
  			errorManager.finishKeyInInstance(this.schedule.name, this.status.sysID);
  		}
  			
  	}
  	
  	this._fireStartDiscoEvent();
  	
  	this.discoverFromSchedule();
  },
  
  /*
  * Starts a discovery for a single IP, using given schedule to get MID servers, behaviors etc.
  * schedule: a DiscoverySchedule instance to use
  * ip:       the ip address to discover, as a dotted-format string
  * readCache: true => read DiscoveryRangesDB cache during Shazzam Launch
  * 
  * returns:  sys_id of status record created
  */
  startFromIPInCache: function(schedule, ip) {
  	schedule.readCache = true;
  	
  	return this.startFromIP(schedule, ip);
  },
  
  /*
  * Starts a discovery for a single IP, using the given schedule to get MID servers, behaviors, etc.
  * schedule: a DiscoverySchedule instance for the schedule to use
  * ip:       the ip address to discover, as a dotted-format string
  * returns:  sys_id of status record created
  */
  startFromIP: function(schedule, ip) {
  	this.schedule = schedule;
  	this.status = new DiscoveryStatus(schedule, 'Discover CI', this.source);
  	
  	this._fireStartDiscoEvent();
  	
  	this.discoverFromSchedule(ip);
  	return this.status.sysID;
  },
  
  startAdmFromIP: function(schedule, ciId, osType, ip) {
  	this.schedule = schedule;
  	this.admCiId = ciId;
  	this.admOsType = osType;
  	this.status = new DiscoveryStatus(schedule, 'Refresh processes and connections', this.source);
  	
  	this._fireStartDiscoEvent();
  	
  	this.discoverFromSchedule(ip);
  	return this.status.sysID;
  },

  startCloudDiscoveryFromDatacenter: function(datacenterSysId, serviceAccountSysId /* optional */) {
  	this.status = DiscoveryPatternOrchestratorFlowLauncher.startQuickDatacenterDiscovery(datacenterSysId, serviceAccountSysId);
  	this.schedule = this.status.getSchedule();
  	this._fireStartDiscoEvent();
  	
  	return this.status.sysID;
  },

  startCloudDiscoveryFromDatacenterWithStatus: function(statusId, datacenterSysId, serviceAccountSysId /* optional */) {
  	this.status = DiscoveryPatternOrchestratorFlowLauncher.startQuickDatacenterDiscoveryExistingStatus(statusId, datacenterSysId, serviceAccountSysId);
  	this.schedule = this.status.getSchedule();
  	this._fireStartDiscoEvent();
  	
  	return this.status.sysID;
  },

  /*
  * Adds an IP (or comma-separated list of IPs) to an existing discovery (used by networks discovery).
  */
  addAdditionalIP: function(status, ip, sensor) {
  	this.status = new DiscoveryStatus(status);
  	this.schedule = new DiscoverySchedule(this.status.scheduleID);
  	this.discoverFromSchedule(ip, true, sensor);
  },
  
  _fireStartDiscoEvent: function() {
  	var sgr = this.status.getGlideRecord();
  	if (sgr) {
  		gs.eventQueue("discovery.started", sgr, this.status.name, this.schedule.name);
  		gs.log('Fired started event for: ' + this.status.name + ' ' + this.schedule.name);
  	}

  	if (this.schedule.discover === 'CIs') {
  		var dr = new DiscoveryResult(this.status.sysID);
  		dr.createResult(this.schedule.sysID);
  	}
  },
  
  /*
  * Handles discovery of devices (whether Basic or Advanced) from a schedule.
  */
  discoverFromSchedule: function(ip, samePhase, sensor) {
  	this.scheduleCancelJob();

  	// Handle case of service discovery
  	if (this.schedule.discoverService()) {
  		var rdm = new SNC.RediscoveryManager();
  		rdm.runScheduledDiscovery(this.job.scheduleID, this.status.sysID);
  		return;
  	}

  	// Handle case of hostless discovery
  	if (this.schedule.discoverHostless() ||
  		this.schedule.discoverCaTrustCertificates() ||
  		this.schedule.importCertificates()) {
  			DiscoveryPatternOrchestratorFlowLauncher.startDiscovery(this.status.sysID, this.schedule.sysID, DiscoveryPatternOrchestratorFlowLauncher.discoveryType.serverless);
  		return;
  	}
  	
  	// Handle case of cloud discovery
  	if (this.schedule.discoverCloud()) {
  		var ptrnLchr = new SNC.PatternLauncherManager();
  		ptrnLchr.launch(this.status.sysID, this.schedule.sysID,'cloud');
  		return;
  	}
  	
  	//Handle case of cloud resource discovery
  	if(this.schedule.discoverCloudResources()){
  		DiscoveryPatternOrchestratorFlowLauncher.startDiscovery(this.status.sysID, this.schedule.sysID, DiscoveryPatternOrchestratorFlowLauncher.discoveryType.cloudResources);
  		new sn_cmp.CloudResourceDiscoveryLaunch(this.status, this.schedule).launch();
  		return;
  	}
  	
  	// Handle launch of ADM probe for a refresh of host's processes and connections.
  	if (this.schedule.refreshAdm()) {
  		gs.log('status = ' + this.status.sysID);
  		var utc = new UpdateTCPConnections();
  		utc.run(this.admCiId, this.admOsType, ip, this.status);
  		return;
  	}

  	if (this.schedule.discoverUrlCertificates()) {
  		new URLCertificateDiscoveryLaunch(this.status, this.schedule).launch();
  		return;
  	}

  	if (!this.schedule.discoverWebService()) {
  		// cancel discovery if midServer is not valid
  		if (this._specificMid() && !this.schedule.midServer.valid) {
  			this._cancelDiscoveryAndLogError();
  			return;
  		}
  		if (ip)
  			this.status.setIPFromCI(ip);
  		var dl = new ShazzamLaunch(this.status, this.schedule);
  		dl.launch(ip, samePhase, sensor);
  	} else {
  			var dlArr = WebServiceDiscoveryLaunch.getDiscoveries(this.status, this.schedule);
  			for (var i = 0; i < dlArr.length; i++)
  				dlArr[i].launch();
  	}
  },
  
  /*
  * If the schedule specifies a cancel time, schedule a job to cancel if necessary...
  */
  scheduleCancelJob: function() {
  	var chk = /^(?:(\d+) )?(\d{2}):(\d{2}):(\d{2})$/;
  	// if a time is specified, launch a cancel-if-necessary job...
  	var max_run = this.schedule.maxRun;
  	if (!max_run)
  		return;
  	
  	var dura = max_run.getDurationValue();
  	var mat = chk.exec(dura);
  	if (!mat)
  		return;
  	
  	// calculate the cancellation time...
  	var days = 0;
  	if (mat[1])
  		days += (mat[1] - 0);
  	var secs = days * 3600 * 24;
  	secs += ((mat[2] - 0) * 3600);
  	secs += ((mat[3] - 0) * 60);
  	secs += ((mat[4] - 0) * 1);
  	if (secs <= 0)
  		return;
  	
  	var ct = new GlideDateTime();
  	ct.addSeconds(secs);
  	
  	gs.log('Scheduling Discovery ' + this.status.number + ' for cancellation at ' + ct.toString());
  	
  	// insert our scheduled job into sys_trigger
  	var gr = new GlideRecord('sys_trigger');
  	gr.initialize();
  	gr.name = 'Cancel Discovery';
  	gr.next_action = ct; // cancellation GlideDateTime we computed above
  	gr.trigger_type = 0; // 0 = "once only"
  	gr.state = 0; // 0 = "ready"
  	gr.script = this.getCancelScript();
  	
  	gr.insert();
  },
  	
  getCancelScript: function() {
  	var script =
  		"cancelThisDiscovery();\n" +
  		"function cancelThisDiscovery() {\n" +
  		"var gr = new GlideRecord('discovery_status');\n" +
  		"if (!gr.get('sys_id', '" + this.status.sysID + "'))\n" +
  		"return;\n" +
  		"var state = '' + gr.state;\n" +
  		"if (!((state == 'Active') || (state == 'Starting')))\n" +
  		"return;\n" +
  		"gs.log('Cancelling Discovery " + this.status.number + "');\n" +
  		"var dl = new SncDiscoveryLog('" + this.status.sysID + "');\n" +
  		"dl.info('Canceling discovery because max run time window has been exceeded');\n" +
  		"var dc = new SncDiscoveryCancel();\n" +
  		"dc.cancelAll('" + this.status.sysID + "');\n" +
  		"}";
  	return script;
  },
  		
  atMaxConcurrentRunsForSchedule: function(schedule) {
  	var maxDefault = 3;
  	var maxActive = parseInt(gs.getProperty('glide.discovery.max_concurrent_invocations_per_schedule', maxDefault), 10);
  	if (isNaN(maxActive))
  		maxActive = maxDefault;
  	
  	if (maxActive < 1)
  		return false;

  	var gr = new GlideRecord('discovery_status');
  	gr.addQuery('dscheduler', schedule.getValue('sys_id'));
  	gr.addQuery('description', 'Scheduled');
  	gr.addQuery('state', 'Active');
  	gr.query();
  	var activeCount = gr.getRowCount();

  	if (activeCount < maxActive)
  		return false;
  	
  	// At this point, we know the activeCount is >= maxActive, so we are over the limit.
  	// Per PRB1342888 we will initiate an additional gating condition before we prevent 
  	// creation of a new discovery for this schedule, namely whether the discoveries have ECC Queue records
  	//
  	// First, fetch the oldest discovery status for this schedule that has these active discoveries
  	// Ideally, we should check all of them, but this is a compromise optimization to improve performance
  	// The reasoning is that if the newer Discovery Status have ECC Queue records cleaned up, then certainly
  	// the older ones will as well.  We only need one Discovery Status to clean up, so let's check the
  	// most likely candidate.
  	var oldestDiscoGr = new GlideRecord('discovery_status');
  	oldestDiscoGr.addQuery('dscheduler', schedule.getValue('sys_id'));
  	oldestDiscoGr.addQuery('description', 'Scheduled');
  	oldestDiscoGr.addQuery('state', 'Active');
  	oldestDiscoGr.orderBy('sys_created_on');
  	oldestDiscoGr.setLimit(1);
  	oldestDiscoGr.query();

  	var preventFurtherDiscoveries = true;
  	if (oldestDiscoGr.hasNext()) {
  		oldestDiscoGr.next();
  		var agent_correlator = oldestDiscoGr.getValue('sys_id');
  		// Check whether this discovery status has no ECC Queue records
  		var eccQueueGr = new GlideRecord('ecc_queue');
  		eccQueueGr.addQuery('agent_correlator', agent_correlator);
  		eccQueueGr.setLimit(1);
  		eccQueueGr.query();
  		if (eccQueueGr.hasNext()) {
  			// At least one ECC Queue record exists for this oldest discovery, so the prevention prevails
  			preventFurtherDiscoveries = true;
  		} else {
  			// No ECC Queue record exists, so Cancel this oldest discovery and lift the prevention
  			var canceller =  new SncDiscoveryCancel();
  			canceller.cancelAll(agent_correlator);
  			preventFurtherDiscoveries = false;
  			return false;
  		}
  	}
  	
  	if (preventFurtherDiscoveries) {
  		var link = new RecordToHTML(schedule.getTableName(), schedule.getValue('sys_id'), '${name}',  true);
  		var msg = '[code]'
  			+ 'Canceled discovery of '
  			+ link.toString()
  			+ '. Already at maximum number of active \'Scheduled\' invocations ('
  			+ maxDefault
  			+ ') for a given schedule'
  			+ '[/code]';
  		var dl = new DiscoveryLogger(this.status.sysID);
  		dl.error(msg, 'Discovery');
  		gr = this.status.getGlideRecord();
  		gr.setValue('state', 'Canceled');
  		gr.update();
  		
  		//TODO: Increase the scope when we need to log other errors in this Script Include
  		var errorManager = new SNC.DiscoveryErrorManager();
  		var scheduleName = schedule.getValue('name');
  		errorManager.addError(new SNC.DiscoveryErrorMsg('SN-5402', scheduleName, this.status.sysID, null,
  			'Canceled discovery of ' + scheduleName + 
  			'. Already at maximum number of active \'Scheduled\' invocations (' + maxActive + ')', null));
  		return true;
  	}
  },

  _logInfo: function(msg) {
  	this.dLogger.setLevel(DiscoveryLogger.INFO);   // enforce the level
  	this.dLogger.info(msg, 'StartDiscovery');
  },
  
  _logError: function(msg) {
  	this.dLogger.setLevel(DiscoveryLogger.ERROR);
  	this.dLogger.error(msg, 'StartDiscovery');
  },
  
  _logWarning: function(msg) {
  	this.dLogger.setLevel(DiscoveryLogger.WARNING);
  	this.dLogger.warn(msg, 'StartDiscovery');
  },
  
  _logErrorAndCancelStatus: function(msg) {
  	this.dLogger = new DiscoveryLogger(this.status);
  	this._logError(msg);
  	var dac =  new SncDiscoveryCancel();
  	dac.cancelAll(this.status.sysID);
  },

  type: "StartDiscovery"
};

Sys ID

092266f60ab30150007b0466d082578c

Offical Documentation

Official Docs: