Name

global.DiscoveryStatus

Description

Encapsulates the notion of a Discovery status record.

Script

// Discovery class

/**
* Encapsulates the notion of a Discovery status record.  Instances where isValid() returns true have the following
* properties initialized:
*
* sysID:           sys_id of the Discovery Status record
* number:          number of this record (e.g., "DIS10186")
* scheduleID:      sys_id of the associated Discovery Schedule record
* jobID:           sys_id of the associated job (sys_trigger) record
* discover:        what to discover: 'CIs', 'IPs', or 'Nets'
* includeAlive:    boolean, true if include alive was checked
* logStateChanges: boolean, true to log state changes
* source:          How discovery is triggered 
*
* Tom Dilatush tom.dilatush@service-now.com
*/
var DiscoveryStatus = Class.create();

DiscoveryStatus.updateStatusStartedCount = function(statusId, num) {
  this.updateStatusCount(statusId, num, 'started');
};

DiscoveryStatus.updateStatusCompletedCount = function(statusId, num) {
  this.updateStatusCount(statusId, num, 'completed');
};

DiscoveryStatus.updateStatusCount = function(statusId, num, type) {
  var statusGR = new GlideRecord('discovery_status');
  if (!statusGR.get('sys_id', statusId))
  	return;

  // if status is cancelled, don't need to update counts any longer 
  if (statusGR.state == 'Canceled' || statusGR.scratchpad.cancel)
  	return;
  
  if (JSUtil.nil(num))
  	num = 1;
  
  if (gs.getProperty('glide.discovery.count.use_mutex', 'false') == 'false') {
  	var mu = GlideMultipleUpdate("discovery_status");
  	mu.setIncrement(type, num);
  	mu.addQuery("sys_id", statusId);
  	mu.execute();

  	// workaround to force trigger of business rules until we have a real fix.
  	var gr = new GlideRecord("discovery_status");
  	gr.get('sys_id', statusId);
  	// Generate a guid to ensure there will be an update. Previously used the gs.now(), but there could be collision of the same value with multiple threads
  	gr.scratchpad.unique = gs.generateGUID();
  	gr.update();
  } else {
  	var rl = new GlideRecordLock('discovery_status', statusId);
  	try {
  		var gotLock = rl.get();
  		statusGR.get('sys_id', statusId);
  		var newCount = statusGR[type] + num;
  		statusGR[type] = newCount;
  		statusGR.update();
  	} finally {
  		if (gotLock)
  			rl.release();
  	}
  }
  
};

DiscoveryStatus.prototype = Object.extend(new AbstractDBObject(), {
  /*
  * Retrieves or creates the status record, using the given source, and initializes this instance with the
  * information in that record. If the qualifier is a DiscoveryJob instance, then this method either finds the
  * existing status record (for a "Discover Now") or creates a new one (for a scheduled Discovery). If the qualifier
  * is a schedule record, then a new status record is created. Otherwise the qualifier is treated as a discovery
  * status GlideRecord or string instance containing a sysID for the desired status record.
  */
  initialize: function(type, description, source) {
  	this.valid = false;
  	var gr = null;
  	if (type instanceof DiscoveryJob)
  		gr = this._findOrCreate(type, description, source);
  	else if (type instanceof DiscoverySchedule) {
  		this.schedule = type;
  		gr = this._create(type, null, description, source);
  	}
  	else if (type instanceof GlideRecord) {
  		if (type.getTableName() == 'discovery_status') {
  			gr = type;
  			this.valid = true;
  		}
  	} else {
  		gr = new GlideRecord('discovery_status');
  		if (gr.get('sys_id', type))
  			this.valid = true;
  	}
  	
  	if (!this.valid)
  		return;
  	
  	// everything's cool, so save our values...
  	this.sysID           = gr.getValue('sys_id'          );
  	this.number          = gr.getValue('number'          );
  	this.scheduleID      = gr.getValue('dscheduler'      );
  	this.jobID           = gr.getValue('scheduler_job'   );
  	this.discover        = gr.getValue('discover'        );
  	this.include         = gr.getValue('include'         );
  	this.description     = gr.getValue('description'     );
  	this.createdOn       = gr.getValue('sys_created_on'  );
  	this.updatedOn       = gr.getValue('sys_updated_on'  );
  	this.useSnmpVersion  = gr.getValue('use_snmp_version');
  	this.source          = gr.getValue('source');
  	this.priority        = gr.getValue('priority');
  	this.includeAlive    = JSUtil.getBooleanValue(gr, 'include_alive'    );
  	this.logStateChanges = JSUtil.getBooleanValue(gr, 'log_state_changes');
  	this.scratchpad      = gr.scratchpad;
  },

  /*
  * Returns a GlideRecord instance initialized to this status record, or null if none.
  */
  getGlideRecord: function() {
  	if (!this.valid)
  		return null;

  	var gr = new GlideRecord('discovery_status');
  	gr.get('sys_id', this.sysID);
  	return gr;
  },
  
  /*
  * Returns the current phase for the given behavior, or null if none has been recorded.  Note that the behavior
  * phase is stored in the scratchpad as variable named "behavior:<sys_id>", where <sys_id> is the sys_id of the
  * behavior.
  *
  * behavior: the DiscoveryBehaviorRecord instance for the behavior whose phase is being queried.
  * returns:  the integer phase number for this behavior, or null if none has been recorded.
  */
  getPhase: function(behavior) {
  	var phase = this.scratchpad[this.getBehaviorPhaseKey(behavior)];
  	return (phase == null) ? null : (phase - 0);
  },
  
  /*
  * Sets the current phase for the given behavior.  Note that the behavior phase is stored in the scratchpad as
  * variable named "behavior:<sys_id>", where <sys_id> is the sys_id of the behavior.
  */
  setPhase: function(behavior, phase) {
  	var gr = this.getGlideRecord();
  	if (!gr)
  		return;
  	
  	gr.scratchpad[this.getBehaviorPhaseKey(behavior)] = (phase - 0);
  	gr.setWorkflow(false);  // stomp on a totally useless recursive business rule...
  	gr.update();
  	this.scratchpad = gr.getValue( 'scratchpad' );
  },
  
  /*
  * Returns the behavior phase key for the given behavior.
  *
  * behavior: the DiscoveryBehaviorRecord instance a key is needed for.
  */
  getBehaviorPhaseKey: function(behavior) {
  	return 'behavior:' + behavior.sysID;
  },
  
  /*
  * Find or create this status record in the database.
  *
  * job: the DiscoveryJob instance for the job associated with this status record.
  */
  _findOrCreate: function(job, description, source) {
  	if (!job.isValid())
  		return null;
  	
  	// if we've got a "run once" job, then it was triggered by "Discover Now" and we may already have a status
  	// record...
  	var gr = new GlideRecord('discovery_status');
  	if (job.isRunOnce()) {
  		gr.addQuery('scheduler_job', job.sysID);
  		gr.orderByDesc('sys_created_on');
  		gr.query();
  		if (gr.next()) {
  			this.valid = true;
  			return gr;
  		}
  	}
  	
  	// otherwise this is a scheduled job, and we need to create a new status record...
  	var sched = new DiscoverySchedule(job.scheduleID);
  	return this._create(sched, job.sysID, description, source);
  },
  
  calculateAvgProbeCount: function(schedule) {
  	var rowLimit = 2;
  	var numberOfRecords = 0;
  	var sum = 0;
  	var result = 0;
  	var gr = new GlideRecord('discovery_status');
  	gr.addQuery('dscheduler', schedule);
  	gr.addQuery('state', 'Completed');
  	gr.addQuery('estimated_probe_count', '>=', 0);
  	gr.orderByDesc('sys_created_on');
  	gr.setLimit(rowLimit);
  	gr.query();
  	while (gr.next()) {
  		sum = sum + parseInt(gr.getValue('completed'));
  		numberOfRecords++;
  	}
  	if (numberOfRecords > 0) {
  		result = sum/numberOfRecords;
  	}
  	return result;
  },
  
  _create: function(schedule, jobID, description, source) {
  	var gr = new GlideRecord('discovery_status');
  	gr.initialize();
  	gr.setValue('dscheduler',        schedule ? schedule.sysID : 'NULL'         );
  	gr.setValue('scheduler_job',     jobID                                      );
  	gr.setValue('description',       !!description ? description : 'Unknown'    );
  	gr.setValue('discover',          schedule ? schedule.discover : 'CIs'       );
  	gr.setValue('log_state_changes', schedule ? schedule.logStateChanges : false);
  	gr.setValue('include_alive',     schedule ? schedule.includeAlive : false   );
  	gr.setValue('max_run',           schedule ? schedule.maxRun : null          );
  	gr.setValue('use_snmp_version',  schedule ? schedule.useSnmpVersion : 'all' );
  	gr.setValue('estimated_probe_count', description != 'Discover CI' ? this.calculateAvgProbeCount(schedule.sysID) : 0);
  	gr.setValue('source',            source);
  	gr.setValue('priority',          this._getPriorityFromSource(source));
  	gr.insert();
  	this.valid = true;
  	return gr;
  },
  
  _getPriorityFromSource: function(source) {
  	var priority = '2';       // Standard
  	
  	switch(source) {
  		case 'PatternDesigner':
  			priority = '0';  // Interactive
  			break;
  		case 'Discover_now_ci':
  		case 'Quick_Discovery':
  		case 'ServiceWatch':
  			priority = '1';  // Expedited
  			break;
  	}
  	
  	return priority;
  },
  
  discoverCIs: function() {
  	return this.discover == 'CIs';
  },
  
  discoverIPs: function() {
  	return this.discover == 'IPs';
  },
  
  discoverNets: function() {
  	return this.discover == 'Nets';
  },
  
  discoverWebService: function() {
  	return this.discover == 'Web Service';
  },
  
  discoverService: function() {
  	return this.discover == 'Service';
  },
  
  isFromErrorRetry: function() {
  	return (JSUtil.notNil(this.description) && this.description.startsWith('Retry Discovery'));
  },
  
  getIPFromRetryDiscovery: function() {
  	var ipAddress = null;
  	
  	// Re-discovery spawned from an active error has a status with the description 'Retry Discovery <ip_address>'
  	var tokens = this.description.split(' ');
  	
  	if (tokens.length == 3)
  		ipAddress = tokens[2];
  	
  	return ipAddress;
  },
  	
  /*
  * Sets the IP Address of the CI being discovered in the scratchpad as this information may not be available in the 
  * status if discovery is triggered from the CI form and the associated IP is part of a discovery schedule that uses 
  * a multi-phase behavior.
  */
  setIPFromCI: function(ip) {
      var gr = this.getGlideRecord();
      if (!gr)
          return;

      gr.scratchpad.discoverNowIp = ip;
      gr.setWorkflow(false);
      gr.update();
  },

  /*
  * Returns IP address of the CI being discovered if it was stored in the scratchpad
  */
  getIPFromCI: function() {
      var gr = this.getGlideRecord();
      if (!gr)
          return;

      var ip = gr.scratchpad.discoverNowIp;
      if (!ip)
          return;

      return '' + ip;
  },

  getSchedule: function() {
      return this.schedule;
  },

  setSchedule: function(schedule) {
      this.schedule = schedule;
  },

  type: 'DiscoveryStatus'
});

Sys ID

0923b5ee0ab30150007c408f74342949

Offical Documentation

Official Docs: