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