Name
global.AvailabilityCalculatorV2
Description
No description available
Script
var AvailabilityCalculatorV2 = Class.create();
AvailabilityCalculatorV2.prototype = {
initialize: function() {
this.CONSTANTS = (new global.AvailabilityConstants());
this.UTILS = (new global.AvailabilityUtils());
this.SCHEDULES = new GlideLRUCache(50);
this.cmdbCi = null;
this.offeringCommitments = null;
this.outages = []; // [{begin, end, type}]
},
/**
* Set configuration item on AvailabilityCalculator
* @param {GlideRecord} ci
*/
setCi: function(ci) {
this.cmdbCi = ci;
},
/**
* Set availability commitments for availability generation on AvailabilityCalculator
* @param {GlideRecord} commitments
*/
setOfferingCommitments: function(commitments) {
this.offeringCommitments = commitments;
},
/**
* Encapsulate retrival and generation of availability records in one external method
* @param {GlideDateTime} originalBegin - the original, unadjusted begin date of the availability segment
* @param {GlideDateTime} originalEnd - the original, unadjusted end date of the availability segment
*/
calculate: function(originalBegin, originalEnd) {
try {
if (!this.cmdbCi)
throw "cmdb_ci is not defined";
// system timezone changed from current user timezone to system timezone for date calculation purposes
var oldTZ = gs.getSession().getTimeZoneName();
var defaultTZ = GlideUser.getSysTimeZone();
try {
gs.getSession().setTimeZoneName(defaultTZ);
var interval = (new global.AvailabilityIntervalProcessor()).determineInterval(originalBegin, originalEnd);
if (!interval.adjustedBegin && !interval.adjustedEnd)
throw "failed to calculate availability outage interval";
// convert adjustedBegin to GDT for cases where we need a GDT
var adjustedBegin = new GlideDateTime();
adjustedBegin.setNumericValue(interval.adjustedBegin);
var adjustedEnd = new GlideDateTime();
adjustedEnd.setNumericValue(interval.adjustedEnd);
var outages = this._getOutages(adjustedBegin, adjustedEnd);
var processedOutages = (new global.AvailabilityOutageProcessor()).processOutages(outages, adjustedBegin.getNumericValue(), adjustedEnd.getNumericValue());
// originalBegin and originalEnd needed here to determine calculated segments
this._calculateAvailabilityRecords(processedOutages, originalBegin, originalEnd);
} finally {
// return timezone to original state
gs.getSession().setTimeZoneName(oldTZ);
}
}
catch(e) {
gs.info("ACV2 - calculate: " + e);
}
},
/**
* Format outage record to new object for future processing
* @param {GlideRecord} grOutage
* @returns {Object} - object containing begin date, end date, outage type
*/
_formatOutage: function(grOutage) {
var beginObj = grOutage[this.CONSTANTS.BEGIN].hasValue() && grOutage[this.CONSTANTS.BEGIN].getGlideObject();
var endObj = grOutage[this.CONSTANTS.END].hasValue() && grOutage[this.CONSTANTS.END].getGlideObject();
return {
"begin": (beginObj && beginObj.isValid() ? beginObj.getNumericValue() : null),
"end": (endObj && endObj.isValid() ? endObj.getNumericValue() : null),
// "beginGDT": beginObj.getValue(),
// "endGDT": endObj.getValue(),
"type": grOutage.getValue(this.CONSTANTS.TYPE)
};
},
/**
* Return all outages between a begin date and an end date
* @param {GlideDateTime} begin
* @param {GlideDateTime} end
* @returns {Array} - array of formatted outages
*/
_getOutages: function(begin, end) {
var outages = [];
// Get outages with cmdbCi as the configuration item
var gr = new GlideRecord(this.CONSTANTS.CMDB_CI_OUTAGE);
gr.addQuery(this.CONSTANTS.CMDB_CI, this.cmdbCi.sys_id);
gr.addQuery(this.CONSTANTS.TYPE, this.CONSTANTS.OUTAGE)
.addOrCondition(this.CONSTANTS.TYPE, this.CONSTANTS.PLANNED);
gr.addQuery(this.CONSTANTS.BEGIN, '!=', null);
gr.addQuery(this.CONSTANTS.END, '!=', null);
gr.addQuery(this.CONSTANTS.BEGIN, '<', end);
gr.addQuery(this.CONSTANTS.END, '>', begin);
// Get outates that have an assocaited affected ci of cmdbCi
var relatedOutages = new GlideRecord(this.CONSTANTS.CMDB_CI_OUTAGE);
relatedOutages.addQuery(this.CONSTANTS.TYPE, 'outage')
.addOrCondition(this.CONSTANTS.TYPE, this.CONSTANTS.PLANNED);
relatedOutages.addQuery(this.CONSTANTS.BEGIN, '!=', null);
relatedOutages.addQuery(this.CONSTANTS.END, '!=', null);
relatedOutages.addQuery(this.CONSTANTS.BEGIN, '<', end);
relatedOutages.addQuery(this.CONSTANTS.END, '>', begin);
var grJoin = relatedOutages.addJoinQuery(this.CONSTANTS.CMDB_OUTAGE_CI_MTOM, 'sys_id', 'outage');
grJoin.addCondition('ci_item', this.cmdbCi.sys_id);
// Combine both queries
relatedOutages.addEncodedQuery('^NQ' + gr.getEncodedQuery());
relatedOutages.orderBy(this.CONSTANTS.BEGIN);
relatedOutages.query();
while (relatedOutages.next()) {
outages.push(this._formatOutage(relatedOutages));
}
return outages;
},
/**
* Iterate though all availability types and create/update the availabilty records for each segment
* @param {Array<Object>} processedOutages
* @param {Number} startIndex - beginning index (inclusive) to start iterating though processedOutages
* @param {Number} endIndex - end index (inclusive) to stop iterating though processedOutages
* @param {Number} segmentBegin - date stored as UNIX timestamp
* @param {Number} endBegin - date stored as UNIX timestamp
*/
_calculateAvailabilityRecords: function(processedOutages, begin, end) {
// get all segments to calculate grouped by availability type
var segmentProcessor = new global.AvailabilitySegmentProcessor();
segmentProcessor.determineWorkingSegments(processedOutages, begin, end);
var bucketMap = segmentProcessor.getBucketMap();
// process segments for each commitment
var offeringCommitments = this.offeringCommitments;
while (offeringCommitments.next()) {
// iterate through all availability types in the map
for (var type in bucketMap) {
if (bucketMap.hasOwnProperty(type)) {
var bucket = bucketMap[type];
// iterate through all the segments of an availability type
for (var segmentKey in bucket) {
if (bucket.hasOwnProperty(segmentKey)) {
var segment = bucket[segmentKey];
this._generateAvailabilityData(offeringCommitments, processedOutages, type, segment.startIndex, segment.endIndex, segment.begin, segment.end);
}
}
}
}
}
},
/**
* Create/update an availability record for a single segment
* @param {GlideRecord} offeringCommitment
* @param {Array<Object>} processedOutages
* @param {String} type - availability type
* @param {Number} startIndex - beginning index (inclusive) to start iterating though processedOutages
* @param {Number} endIndex - end index (inclusive) to stop iterating though processedOutages
* @param {Number} segmentBegin - date stored as UNIX timestamp
* @param {Number} endBegin - date stored as UNIX timestamp
*/
_generateAvailabilityData: function(offeringCommitment, processedOutages, type, startIndex, endIndex, begin, end) {
// get calculated values for availability
var availValues = this._generateAvailabilityValuesFor(offeringCommitment, processedOutages, startIndex, endIndex, begin, end);
// pass field values to record update/create
var availRecord = new global.AvailabilityRecord(this.cmdbCi.sys_id, begin, end);
availRecord.setCiClass(this.UTILS.getCiClassFromCommitment(offeringCommitment));
availRecord.setType(type);
availRecord.updateOrCreateRecord(availValues.commitment, availValues.absoluteDuration, availValues.scheduledDuration,
availValues.absoluteAvailability, availValues.scheduledAvailability, availValues.absoluteCount, availValues.scheduledCount,
availValues.scheduledTotal, availValues.mtbf, availValues.mtrs, availValues.allowedDowntime, availValues.commitmentMet);
},
/**
* Generates and calculates availability data given a commitment and outages to process
* @param {GlideRecord} commitment
* @param {Array<Object>} outages
* @returns {Object} containing all data necessary for a service_availability record
*/
_generateAvailabilityValuesFor: function(offeringCommitment, outages, startIndex, endIndex, begin, end) {
var commitmentSchedule = offeringCommitment.service_commitment && offeringCommitment.service_commitment.schedule + '';
var commitmentTz = offeringCommitment.service_commitment && offeringCommitment.service_commitment.timezone + '';
var cmdbCi = this.UTILS.getCiFromCommitment(offeringCommitment);
var commitmentGlideSchedule = this._getCommitmentGlideSchedule(commitmentSchedule, commitmentTz, cmdbCi);
var absoluteOutageData = this._addOutages(outages, null, begin, end, startIndex, endIndex);
// (V1) absolute
var absoluteOutageDataDuration = absoluteOutageData.totalDuration.getNumericValue();
// (V1) absolute_count
var absoluteOutageDataOutageCount = absoluteOutageData.totalOutages;
var scheduledOutageData = this._addOutages(outages, commitmentGlideSchedule, begin, end, startIndex, endIndex);
// (V1) scheduled
var scheduledOutageDataDuration = scheduledOutageData.totalDuration.getNumericValue();
// (V1) scheduled_count
var scheduledOutageDataOutageCount = scheduledOutageData.totalOutages;
var changeInAvailability = [];
var calculationSpan = {};
// create an outage spanning the whole period between begin and end
// use _addOutage() on this created outage
// to get total duration of the period
calculationSpan.begin = begin;
calculationSpan.end = end;
calculationSpan.type = this.CONSTANTS.OUTAGE_TYPES.OUTAGE;
changeInAvailability.push(calculationSpan);
var changedAbsoluteOutageData = this._addOutages(changeInAvailability);
// (V1) absolute_total
var changedAbsoluteOutageDataDuration = changedAbsoluteOutageData.totalDuration.getNumericValue();
var changedScheduledOutageData = this._addOutages(changeInAvailability, commitmentGlideSchedule);
// (V1) scheduled_total
var changedScheduledOutageDataDuration = changedScheduledOutageData.totalDuration.getNumericValue();
var absoluteAvailabilityTotal = 100 * ((changedAbsoluteOutageDataDuration - absoluteOutageDataDuration) / changedAbsoluteOutageDataDuration);
var scheduledAvailabilityTotal = 100;
if (changedScheduledOutageDataDuration != 0)
scheduledAvailabilityTotal = 100 * ((changedScheduledOutageDataDuration - scheduledOutageDataDuration) / changedScheduledOutageDataDuration);
// Mean Time Between Failures - average time between Commitment outages for this Configuration Item
var mtbf = 0;
// Mean Time to Restore Service - average duration of Commitment outages for this Configuration Item
var mtrs = 0;
if (scheduledOutageDataOutageCount != 0) {
mtbf = (changedScheduledOutageDataDuration - scheduledOutageDataDuration) / scheduledOutageDataOutageCount;
mtrs = scheduledOutageDataDuration / scheduledOutageDataOutageCount;
}
// Allowed downtime is downtime that we're allowed under this schedule (might be zero)
var allowedDowntime = changedScheduledOutageDataDuration * ((100 - offeringCommitment.service_commitment.availability) / 100);
// Update vars with GlideDuration
mtbf = new GlideDuration(mtbf);
mtrs = new GlideDuration(mtrs);
allowedDowntime = new GlideDuration(allowedDowntime);
var commitmentMet = scheduledOutageDataDuration <= allowedDowntime.getNumericValue();
var finalCommitmentData = {
commitment: offeringCommitment.getValue('service_commitment'),
absoluteDuration: absoluteOutageData.totalDuration,
scheduledDuration: scheduledOutageData.totalDuration,
absoluteAvailability: absoluteAvailabilityTotal,
scheduledAvailability: scheduledAvailabilityTotal,
absoluteCount: absoluteOutageDataOutageCount,
scheduledCount: scheduledOutageDataOutageCount,
scheduledTotal: changedScheduledOutageData.totalDuration,
mtbf: mtbf,
mtrs: mtrs,
allowedDowntime: allowedDowntime,
commitmentMet: commitmentMet
};
return finalCommitmentData;
},
/**
* Function that adds up all outages duration
* @param {Array<Object>} outages
* @param {GlideSchedule|null} schedule
* @param {GlideDateTime|null} begin beginning cutoff to trim outages to
* @param {GlideDateTime|null} end end cutoff to trim outages to
* @param {Number|null} startIndex index to start iteration of outages array [inclusive]
* @param {Number|null} startIndex index to end iteration of outages array (exclusive)
* @returns {Object} containing totalOutages processed and totalDuration
*/
_addOutages: function(outages, schedule, begin, end, startIndex, endIndex) {
var innerStartIndex = startIndex;
var innerEndIndex = endIndex;
if (!outages) {
var zeroDuration = new GlideDuration();
zeroDuration.setNumericValue(0);
return {
totalOutages: 0,
totalDuration: zeroDuration
};
}
if (!startIndex)
innerStartIndex = 0;
if (!endIndex || endIndex > outages.length)
innerEndIndex = outages.length;
var totalOutages = 0;
var scheduleBegin, scheduleEnd;
if (schedule) {
scheduleBegin = new GlideDateTime();
scheduleEnd = new GlideDateTime();
}
/**
* Trims outages to passed in begin and end dates
* @param {Object} outage
* @param {Number} begin begin cutoff to trim outage to
* @param {Number} end end cutoff to trim outage to
* @returns {Object|null}
*/
function _adjustBoundaries(outage, begin, end) {
if (!outage)
return null;
else if (begin == null || end == null)
return outage;
else if (outage.begin >= begin && outage.end <= end)
return outage;
var adjustedOutage = {
begin: outage.begin,
end: outage.end,
type: outage.type
};
if (outage.begin < begin)
adjustedOutage.begin = begin;
if (outage.end > end)
adjustedOutage.end = end;
return adjustedOutage;
};
var totalDurationSum = 0;
for (var outageIndex = innerStartIndex; outageIndex < innerEndIndex; outageIndex++) {
// trim currentOutage to within segment boundaries
var currentOutage = _adjustBoundaries(outages[outageIndex], begin, end);
var duration = 0;
if (currentOutage.type && currentOutage.type === this.CONSTANTS.OUTAGE_TYPES.OUTAGE) {
if (schedule) {
scheduleBegin.setNumericValue(currentOutage.begin);
scheduleEnd.setNumericValue(currentOutage.end);
duration = schedule.duration(scheduleBegin, scheduleEnd).getNumericValue();
} else
duration = (currentOutage.end - currentOutage.begin);
}
if (duration > 0)
totalOutages++;
totalDurationSum += duration;
}
var totalDuration = new GlideDuration();
totalDuration.setNumericValue(totalDurationSum);
return {
totalOutages: totalOutages,
totalDuration: totalDuration
};
},
/**
* Gets commitment's GlideSchedule
* @param {String} commitmentSchedule
* @param {String} commitmentTz
* @param {String} cmdbCi
* @returns {GlideSchedule}
*/
_getCommitmentGlideSchedule: function(commitmentSchedule, commitmentTz, cmdbCi) {
var currentSchedule = this._getCachedSchedule(commitmentSchedule, commitmentTz);
var maintenanceWindows = this._getMaintenanceWindow(cmdbCi);
if (!maintenanceWindows)
return currentSchedule;
if (!currentSchedule)
currentSchedule = this._getFullDay(commitmentTz);
for (var maintWindowsIndex = 0; maintWindowsIndex < maintenanceWindows.length; maintWindowsIndex++){
currentSchedule.addOtherSchedule0(maintenanceWindows[maintWindowsIndex], false);
}
return currentSchedule;
},
/**
* Gets GlideSchedule from cache. If not present, caches schedule and returns it
* @param {String} commitmentSchedule
* @param {String} commitmentTz
* @returns {GlideSchedule}
*/
_getCachedSchedule: function(commitmentSchedule, commitmentTz) {
if (!commitmentSchedule)
return null;
var cachedSchedule = this.SCHEDULES.get(commitmentSchedule);
if (!cachedSchedule){
cachedSchedule = new GlideSchedule(commitmentSchedule);
this.SCHEDULES.put(commitmentSchedule, cachedSchedule);
}
// Copy this in case we add a maintenance schedule later (munges the schedule itself)
cachedSchedule = new GlideSchedule(cachedSchedule);
if (commitmentTz)
cachedSchedule.setTimeZone(commitmentTz);
return cachedSchedule;
},
/**
* Gets maintanance windows for a given CI
* @param {String} cmdbCi sys_id of cmdbCi
* @returns {Array<GlideRecord>} maintenance windows array
*/
_getMaintenanceWindow: function(cmdbCi) {
var maintenanceCommitment = new GlideRecord(this.CONSTANTS.SERVICE_OFFERING_COMMITMENT);
maintenanceCommitment.addQuery('service_commitment.type', 'maintenance_window');
maintenanceCommitment.addQuery('service_offering', cmdbCi).addOrCondition('cmdb_ci', cmdbCi);
maintenanceCommitment.addNotNullQuery('service_commitment.schedule');
maintenanceCommitment.query();
if (!maintenanceCommitment.hasNext())
return null;
var maintenanceWindows = new Array();
while (maintenanceCommitment.next()) {
var maintenanceSchedule = maintenanceCommitment.service_commitment.schedule;
var maintenanceCommitmentTz = maintenanceCommitment.service_commitment.timezone;
maintenanceWindows.push(this._getCachedSchedule(maintenanceSchedule, maintenanceCommitmentTz));
}
return maintenanceWindows;
},
/**
* Gets a full day as a GlideSchedule
* @param {String} commitmentTz
* @returns {GlideSchedule}
*/
_getFullDay: function(commitmentTz) {
var schedule = new GlideSchedule();
var cmnGr = new GlideRecord(this.CONSTANTS.CMN_SCHEDULE_SPAN);
cmnGr.initialize(); // necessary to force in a guid
cmnGr.setValue('start_date_time', '20100101T000000');
cmnGr.setValue('end_date_time', '20100102T000000');
cmnGr.setValue('repeat_type', 'daily');
schedule.addTimeSpan(cmnGr); // cmnGr must have a guid at this point
if (commitmentTz)
schedule.setTimeZone(commitmentTz);
return schedule;
},
type: 'AvailabilityCalculatorV2'
};
Sys ID
ac07dd4b43ad1110a6dfaff3fab8f24a