Name

global.ChangeCollisionHelper

Description

Helper methods for Change Collision detection (com.snc.change.collision)

Script

var ChangeCollisionHelper = Class.create();

ChangeCollisionHelper.getCiById = function(cIId) {

  var glideRecordUtil = new GlideRecordUtil();
  return glideRecordUtil.getGR("cmdb_ci", cIId);

};

ChangeCollisionHelper.getConditionalMaintenanceSchedules = function(changeGr) {

  var maintenanceSchedules = [];

  var scheduleGR = new GlideRecord('cmn_schedule_maintenance');
  scheduleGR.addNotNullQuery('applies_to');
  if (changeGr)
  	scheduleGR.addDomainQuery(changeGr);
  scheduleGR.query();
  while(scheduleGR.next())
  	maintenanceSchedules.push({sys_id : scheduleGR.sys_id.toString(),
  		condition : scheduleGR.condition.toString(),
  		name : scheduleGR.name.toString(),
  		applies_to : scheduleGR.applies_to.toString()}
  	);

  return maintenanceSchedules;
};

/**
* Checks if the time span defined by startDate and endDate falls wholly/partially in the
* CI's maintenance window
*
* @param configuration item's sys_id
* @param startDate
* @param endDate
* @param bPartial (optional) default is false. When true checks partial overlap instead of whole.
* @return boolean
*/
ChangeCollisionHelper.isDateInCiMaintenanceWindows = function(startDate, endDate, maintenanceWindow, bPartial) {
  // Create new GlideDateTime equal to endDate so if manipulated the change will not persist
  var workingEndDate = new GlideDateTime(endDate);

  if (startDate.equals(workingEndDate))
  	workingEndDate.add(1000);

  var sched = new GlideSchedule(maintenanceWindow);

  // check maintenance window is valid
  if (!sched.isValid())
  	return true;

  // check maintenance window
  var duration;
  var rollingStartDate = new GlideDateTime();
  var rollingEndDate = new GlideDateTime(startDate);
  do {
  	rollingStartDate.setGlideDateTime(rollingEndDate);
  	rollingEndDate.addYearsLocalTime(1);
  	if (rollingEndDate.after(workingEndDate))
  		rollingEndDate.setValue(workingEndDate);
  	// does schedule overlap itself within (startDate, workingEndDate) range?
  	duration = sched.duration(rollingStartDate, rollingEndDate);
  } while(duration.getNumericValue() === 0 && rollingEndDate.before(workingEndDate));

  var schedule_time = parseInt(duration.getNumericValue() / 1000);
  if (bPartial) {
  	if (schedule_time === 0)
  		return false;
  	return true;
  }

  var wall_clock_time = parseInt(gs.dateDiff(startDate.getDisplayValue(), workingEndDate.getDisplayValue(), true));
  if (wall_clock_time !== schedule_time)
  	return false;

  // Default Case: Assume date is within maintenance window for all other cases
  return true;
};

ChangeCollisionHelper.getCiMaintenanceSchedule = function(ci, changeGr) {
  var maintenanceSchedule = null;
  var g = new GlideRecord('cmn_schedule');
  g.addQuery("JOINcmn_schedule.sys_id=cmdb_ci.maintenance_schedule!sys_id=" + ci);
  if (changeGr)
  	g.addDomainQuery(changeGr);
  g.query();
  if (g.next())
  	maintenanceSchedule = g.sys_id.toString();
  return maintenanceSchedule;
};

ChangeCollisionHelper.getCiMaintenanceScheduleByGR = function(ciGR) {
  return ciGR.maintenance_schedule + "";
};

/**
* Gets any blackout that overlap the period defined by startDate and endDate
*
* @param startDate
* @param endDate
* @return Array(blackoutId:stringSpan)
*/
ChangeCollisionHelper.getBlackoutsByDate = function(startDate, endDate, changeGr) {
  // Create new GlideDateTime equal to endDate so if manipulated the change will not persist
  var workingEndDate = new GlideDateTime(endDate);

  if (startDate.equals(workingEndDate))
  	workingEndDate.add(1000);

  var blackoutList = [];
  var scheduleGR = new GlideRecord('cmn_schedule_blackout');
  scheduleGR.addQuery('type', 'blackout');
  if (changeGr)
  	scheduleGR.addDomainQuery(changeGr);
  scheduleGR.query();
  while (scheduleGR.next()) {
  	var scheduleId = scheduleGR.sys_id.toString();
  	var sched = new GlideSchedule(scheduleId);

  	var duration;
  	var rollingStartDate = new GlideDateTime();
  	var rollingEndDate = new GlideDateTime(startDate);
  	do {
  		rollingStartDate.setGlideDateTime(rollingEndDate);
  		rollingEndDate.addYearsLocalTime(1);
  		if (rollingEndDate.after(workingEndDate))
  			rollingEndDate.setValue(workingEndDate);
  		// does schedule overlap itself within (startDate, workingEndDate) range?
  		duration = sched.duration(rollingStartDate, rollingEndDate);
  	} while(duration.getNumericValue() === 0 && rollingEndDate.before(workingEndDate));

  	if (duration.getNumericValue() > 0){
  		// caller expects Schedule IDs but does not use value
  		blackoutList.push({sys_id:scheduleGR.sys_id.toString(),
  		condition:scheduleGR.condition.toString(),
  		name: scheduleGR.name.toString(),
  		applies_to: scheduleGR.applies_to.toString()}
  		);
  	}
  }
  return blackoutList;
};

/**
* Get changes scheduled in the timespan (defined by startDate and endDate) that
* have the given CI in their affected CIs List
*
* @param CI's sys_id
* @param startDate
* @param endDate
*
* @return Array changeIds
*/
ChangeCollisionHelper.getChangesWithAffectedCi = function(ci, startDate, endDate, changeGr) {
  var changeIds = [];

  var changeRequestGR = new GlideRecord('change_request');
  changeRequestGR.addActiveQuery();
  changeRequestGR.addQuery("JOINchange_request.sys_id=task_ci.task!ci_item=" + ci);
  ChangeCollisionHelper.addQueryDateRange(changeRequestGR, startDate, endDate);
  if (changeGr)
  	changeRequestGR.addDomainQuery(changeGr);
  changeRequestGR.query();

  while (changeRequestGR.next())
  	changeIds.push(changeRequestGR.sys_id.toString());
  return changeIds;
};

/**
* Get the changes that are in the timespan (startDate, endDate) and that are
* link to the given ci ci Ci's sys_id startDate endDate excludeCR (optional) -
* change_request record to exclude from search
*
* @return Array changeIds
*/
ChangeCollisionHelper.getChangesWithCi = function(ci, startDate, endDate, excludeCR) {
  var changeIds = [];

  var changeRequestGR = new GlideRecord('change_request');
  changeRequestGR.addActiveQuery();
  changeRequestGR.addQuery('cmdb_ci', ci);
  if (excludeCR) {
  	changeRequestGR.addQuery('sys_id', '!=', excludeCR.sys_id);
  	changeRequestGR.addDomainQuery(excludeCR);
  }
  ChangeCollisionHelper.addQueryDateRange(changeRequestGR, startDate, endDate);
  changeRequestGR.query();

  while (changeRequestGR.next())
  	changeIds.push(changeRequestGR.sys_id.toString());
  return changeIds;
};

/**
* Gets the affected CI Ids for the given change
*
* @param changeId
* @return array
*/
ChangeCollisionHelper.getAffectedCisByChangeId = function(changeId) {
  var affectedCiIds = [];
  var affectedCiGR = new GlideRecord('task_ci');
  affectedCiGR.addQuery('task', changeId);
  affectedCiGR.query();

  while (affectedCiGR.next())
  	affectedCiIds.push(affectedCiGR.ci_item.toString());
  return affectedCiIds;
};

/**
* Get impacted services due to the conflicts identified in the change.
* It gets (an array of sys_ids containing) all the services which depends on any of the conflicting CIs
*/
ChangeCollisionHelper.getImpactedServicesByChangeId = function(changeId) {
  var services = [];
  var arrayUtil = new ArrayUtil();

  // gets dependency services
  var gr = new GlideAggregate('svc_ci_assoc');
  var grSQ = gr.addJoinQuery('conflict', 'ci_id', 'configuration_item');
  grSQ.addCondition('change', changeId);
  gr.groupBy('service_id');
  gr.query();
  while(gr.next())
  	services.push(gr.service_id + "");

  // gets the Business Services directly impacted
  gr = new GlideRecord('cmdb_ci_service');
  grSQ = gr.addJoinQuery('conflict', 'sys_id', 'configuration_item');
  grSQ.addCondition('change', changeId);
  gr.query();

  while(gr.next())
  	services.push(gr.sys_id + "");

  return arrayUtil.unique(services);
};

/**
* Add CI to the change's affected CI list
*/
ChangeCollisionHelper.addCiToChangeAffectedCis = function(ci, changeId) {
  if (JSUtil.nil(ci) || JSUtil.nil(changeId) || ChangeCollisionHelper.isCiInAffectedCis(ci, changeId))
  	return;

  var affectedCiGR = new GlideRecord('task_ci');
  affectedCiGR.task = changeId;
  affectedCiGR.ci_item = ci;
  affectedCiGR.insert();
};

/**
* check if an ci is already in the change's affected CIs list
*/
ChangeCollisionHelper.isCiInAffectedCis = function(ci, changeId) {
  var affectedCiGR = new GlideRecord('task_ci');
  affectedCiGR.addQuery('ci_item', ci);
  affectedCiGR.addQuery('task', changeId);
  affectedCiGR.query();
  affectedCiGR.setLimit(1);
  return (affectedCiGR.hasNext());
};

/**
* Get all the CIs that depend on the given CI
*
* return an Array of CI sys_ids (as strings)
*/
ChangeCollisionHelper.getDependants = function(ci, returnGlideRecords) {
  var dependents = [];
  var cc = new GlideRecord('cmdb_rel_ci');
  cc.addQuery('child', ci);
  cc.query();

  if (returnGlideRecords)
  	return cc.hasNext() ? cc : false;
  else {
  	while (cc.next())
  		dependents.push(cc.parent.toString());

  	return dependents;
  }
};

/**
* Get all the CIs that the given CI depends on
*
* return an Array of CI sys_ids (as strings)
*/
ChangeCollisionHelper.getDependencies = function(ci, returnGlideRecords) {
  var dependencies = [];
  var cc = new GlideRecord('cmdb_rel_ci');
  cc.addQuery('parent', ci);
  cc.query();

  if (returnGlideRecords)
  	return cc.hasNext() ? cc : false;
  else {
  	while (cc.next())
  		dependencies.push(cc.child.toString());

  	return dependencies;
  }
};

/**
* allowContiguousChanges boolean
*
* Add query conditions for
* start_date >= startDate and start_date <= endDate
* end_date >= startDate and end_date <= endDate
* end_date >= endDate and start_date <= startDate
*
* If allowContiguousChanges is true it will not check for
* start_date = endDate or end_date = startDate.
*/
ChangeCollisionHelper.addQueryDateRange = function(gr, startDate, endDate, tablePrefix, allowContiguousChanges) {
  if (!tablePrefix)
  	tablePrefix = '';
  var queryCondition = gr.addQuery(tablePrefix+'start_date', '9999-12-31 23:59:59');

  var startDateQueryCondition = queryCondition.addOrCondition(tablePrefix+'start_date', '>=', startDate);
  startDateQueryCondition.addCondition(tablePrefix+'start_date', allowContiguousChanges ? '<' : '<=', endDate);

  var endDateQueryCondition = queryCondition.addOrCondition(tablePrefix+'end_date', allowContiguousChanges ? '>' : '>=', startDate);
  endDateQueryCondition.addCondition(tablePrefix+'end_date', '<=', endDate);

  var overallQueryCondition = queryCondition.addOrCondition(tablePrefix+'end_date', '>=', endDate);
  overallQueryCondition.addCondition(tablePrefix+'start_date', '<=', startDate);
};

/**
* @deprecated -- use ChangeCollisionHelper.getCIDependencies instead
*
* Get all the CI GlideRecords that the given CI depends on
*
* return an Array of CI GlideRecords
*/
ChangeCollisionHelper.getDependenciesGR = function(ciSysId, glideRecordUtil) {
  var dependencies = [];
  var cc = new GlideRecord('cmdb_rel_ci');
  cc.addQuery('parent', ciSysId);
  cc.query();

  var dependency;
  while (cc.next()) {
  	dependency = glideRecordUtil.getGR("cmdb_ci", cc.child);
  	if (dependency)
  		dependencies.push(dependency);
  }

  return dependencies;
};

/**
* Get all the CI GlideRecords that the given CI depends on
*
* return GlideRecord
*/
ChangeCollisionHelper.getCIDependencies = function(ciSysId, changeGr) {
  var dependenciesGR = new GlideRecord("cmdb_ci");
  dependenciesGR.addQuery("JOINcmdb_ci.sys_id=cmdb_rel_ci.child!parent=" + ciSysId);
  if (changeGr)
  	dependenciesGR.addDomainQuery(changeGr);
  dependenciesGR.query();
  return dependenciesGR;
};

/**
* @deprecated -- use ChangeCollisionHelper.getCIDependants instead
*
* Get all the CI GlideRecords that depend on the given CI
*
* return an Array of CI GlideRecords
*/
ChangeCollisionHelper.getDependantsGR = function(ciSysId, glideRecordUtil) {
  var dependents = [];
  var cc = new GlideRecord('cmdb_rel_ci');
  cc.addQuery('child', ciSysId);
  cc.query();

  var dependent;
  while (cc.next()) {
  	dependent = glideRecordUtil.getGR("cmdb_ci", cc.parent);
  	if (dependent)
  		dependents.push(dependent);
  }
  return dependents;

};

/**
* Get all the CI GlideRecords that depend on the given CI
*
* return GlideRecord
*/
ChangeCollisionHelper.getCIDependants = function(ciSysId, changeGr) {
  var dependentsGR = new GlideRecord("cmdb_ci");
  dependentsGR.addQuery("JOINcmdb_ci.sys_id=cmdb_rel_ci.parent!child=" + ciSysId);
  if (changeGr)
  	dependentsGR.addDomainQuery(changeGr);
  dependentsGR.query();
  return dependentsGR;
};

Sys ID

63853c880a0a2c3e15136f8080b90e5e

Offical Documentation

Official Docs: