Name

global.ChangeRequestDiscoverySNC

Description

Base script for use of integration of Change Requests and other applications such as Discovery and Service Mapping

Script

var ChangeRequestDiscoverySNC = Class.create();
ChangeRequestDiscoverySNC.prototype = {
  //Discovery State Values
  DISCO_IN_PROGRESS: "in_progress",
  DISCO_SUCCESSFUL: "successful",
  DISCO_SUCCESSFUL_WARN: "warning",
  DISCO_ERROR: "error",
  DISCO_CANCEL: "canceled",
  DISCO_NA: "not_applicable",
  PLUGIN_SVC_MAPPING: "com.snc.service-mapping",

  //Discovery State Icons
  DISCO_ICONS: {
  	"in_progress": {"color": "#278EFC", "icon": "icon-activity-circle"},
  	"successful": {"color": "#71E279", "icon": "icon-success-circle"},
  	"warning": {"color": "#F95050", "icon": "icon-success-circle"},
  	"error": {"color": "#F95050", "icon": "icon-error-circle"},
  	"canceled": {"color": "#000000", "icon": "icon-error-circle"},
  	"not_applicable": {"color": "#000000", "icon": "icon-delete"}
  },

  initialize: function() {
  },

  canManuallyTriggerDiscovery: function(taskSysId) {
  	var propertyValue = gs.getProperty('com.snc.change_request.auto.discovery', 'off');
  	var propertyStates = gs.getProperty('com.snc.change_request.disco.manual.state', '');

  	//Check if the property is valid for a manual trigger
  	var validProperty = propertyValue === 'manual' || propertyValue === 'both';

  	return validProperty && this._canTriggerDiscovery(taskSysId, propertyStates, false);
  },

  canAutoTriggerDiscovery: function(taskSysId) {
  	var propertyValue = gs.getProperty('com.snc.change_request.auto.discovery', 'off');
  	var propertyStates = gs.getProperty('com.snc.change_request.disco.auto.state', '');

  	var validProperty = propertyValue === 'automatic' || propertyValue === 'both';

  	return validProperty && this._canTriggerDiscovery(taskSysId, propertyStates, false);
  },
  
  canTriggerTopDownDiscovery: function(taskSysId) {
  	if (!GlidePluginManager.isActive(this.PLUGIN_SVC_MAPPING))
  		return false;

  	var propertyStates = gs.getProperty('com.snc.change_request.disco.topdown.state', '');
  	return this._canTriggerDiscovery(taskSysId, propertyStates, true);
  },

  _updateTaskCI: function(taskCI, isManual) {
  	var ipAddress = this._getCIAddress(taskCI.ci_item);
  	if (ipAddress !== '') {
  		try {
  			var statusSysId = sn_discovery.DiscoveryAPI.discoverIpAddress(ipAddress);
  			if (statusSysId) {
  				taskCI.setValue('discovery', statusSysId);
  				taskCI.setValue('discovery_result', this.DISCO_IN_PROGRESS);
  				taskCI.setValue('discovery_log_url', this.generateDiscoveryStatusLink(statusSysId));
  				taskCI.setValue('discovery_last_updated', gs.nowGlideDateTime());
  				taskCI.setValue('discovery_manually_triggered', isManual);
  			}
  		} catch (err) {
  			gs.error('Discovery Error: ' + err.message + ' for CI: ' + taskCI.ci_item + ' (' + ipAddress + ')');
  			taskCI.setValue('discovery_result', this.DISCO_ERROR);
  			taskCI.setValue('discovery_last_updated', gs.nowGlideDateTime());
  		}
  	} else {
  		taskCI.setValue('discovery_result', this.DISCO_NA);
  		taskCI.setValue('discovery_last_updated', gs.nowGlideDateTime());
  	}
  	taskCI.update();
  },

  triggerDiscovery: function(taskSysId, isManual) {
  	var changeRequestGR = new GlideRecord('change_request');
  	if ((!taskSysId) || !changeRequestGR.get(taskSysId)) {
  		gs.warn('[ChangeRequestDiscovery - triggerDiscovery] Unable to retrieve change record for sys id: ' + taskSysId);
  		return false;
  	}

  	gs.eventQueue("change_request.trigger.discovery", changeRequestGR, isManual);
  	if (isManual)
  		gs.addInfoMessage(gs.getMessage("Discovery has been initiated on the Affeced CIs"));
  },

  triggerDiscoveryFromEvent: function(taskSysId, isManual) {
  	if (!taskSysId)
  		return;

  	var taskCI = new GlideRecord('task_ci');
  	taskCI.addQuery('task', taskSysId);
  	if (!isManual)
  		taskCI.addQuery('discovery_manually_triggered', false);
  	taskCI.query();

  	while (taskCI.next())
  		this._updateTaskCI(taskCI, isManual);

  },

  _setValuesTaskCI: function(taskCI, returnedStatus, discoverySysId) {
  	var status = returnedStatus.getCiOperationStatus() + '';
  	var issueCount = returnedStatus.getIssues();
  	if (status !== 'Successful') {
  		taskCI.setValue('discovery_result', this.DISCO_ERROR);
  		taskCI.setValue('discovery_log_url', this.generateDiscoveryIssuesLink(returnedStatus.getIssuesLink()));
  	} else {
  		if (issueCount > 0) {
  			taskCI.setValue('discovery_result', this.DISCO_SUCCESSFUL_WARN);
  			taskCI.setValue('discovery_log_url', this.generateDiscoveryIssuesLink(returnedStatus.getIssuesLink()));
  		} else {
  			taskCI.setValue('discovery_result', this.DISCO_SUCCESSFUL);
  			taskCI.setValue('discovery_log_url', this.generateDiscoveryStatusLink(discoverySysId));
  		}
  	}
  	taskCI.setValue('discovery_last_updated', gs.nowGlideDateTime());
  	return taskCI;
  },

  updateDiscoveryState: function(taskCISysId, discoverySysId, isTopDown) {
  	if (isTopDown) {
  		var serviceCI = new GlideRecord('task_cmdb_ci_service');
  		if (!serviceCI.get(taskCISysId))
  			return;

  		serviceCI.setValue('discovery_result', this.DISCO_SUCCESSFUL);
  		serviceCI.setValue('discovery_log_url', this.generateDiscoveryStatusLink(discoverySysId));
  		serviceCI.update();
  	} else {
  		var taskCI = new GlideRecord('task_ci');
  		if (!taskCI.get(taskCISysId))
  			return;

  		try {
  			var statusArr = sn_discovery.DiscoveryAPI.reportCiStatus(discoverySysId);
  			for (var i = 0; i < statusArr.length; i++) {
  				taskCI = this._setValuesTaskCI(taskCI, statusArr[i], discoverySysId);
  				taskCI.update();
  			}
  		} catch (err) {
  			taskCI.setValue('discovery_result', this.DISCO_ERROR);
  			taskCI.setValue('discovery_log_url', this.generateDiscoveryLogLink(discoverySysId));
  			taskCI.setValue('discovery_last_updated', gs.nowGlideDateTime());
  			taskCI.update();
  		}
  	}
  },

  generateDiscoveryIssuesLink: function(link) {
  	if (!link || link.indexOf("href") < 0)
  		return "";

  	return link.match(/href="([^"]*)/)[1];
  },

  generateDiscoveryStatusLink: function(targetSysId) {
  	if (!targetSysId)
  		return '';
  	
  	return "discovery_status.do?sys_id=" + targetSysId;
  },

  generateDiscoveryLogLink: function(targetSysId) {
  	return "discovery_log_list.do?sysparm_query=status=" + targetSysId + "^level!=0";
  },

  generateDiscoveryStateLink: function(taskCiGr) {
  	var stateLink = "";

  	if (!taskCiGr)
  		return stateLink;

  	var discoResult = taskCiGr.getDisplayValue("discovery_result");
  	if (!discoResult)
  		return stateLink;

  	var discoLogURL = taskCiGr.getValue("discovery_log_url");

  	stateLink = "[code]";
  	var title = gs.getMessage("See log for details...");

  	// Add the icon first if we've got one defined for this state
  	var discoIcon = this.DISCO_ICONS[taskCiGr.getValue("discovery_result")];
  	
  	if (discoIcon) {
  		if (discoLogURL)
  			stateLink += '<a title="' + title + '" style="padding-right:7px;" class="linked" href="' + discoLogURL + '"><span class="' + discoIcon.icon + '" style="color:' + discoIcon.color + ';"></span></a>';
  		else
  			stateLink += '<span class="' + discoIcon.icon + '" style="margin-right:10px; color:' + discoIcon.color + ';"></span>';
  	}

  	// Now we can add the discovery result text e.g. Completed Successfully
  	if (discoLogURL)
  		stateLink += '<a title="' + title + '" class="linked" href="' + discoLogURL + '">' + discoResult + '</a>';
  	else
  		stateLink += '<span>' + discoResult + '</span>';

  	return stateLink;
  },

  _getCIAddress: function(sysId) {
  	var disco = new Discovery();
  	var ipAddress = disco.getIPsInCI(sysId);
  	//Check that we do have IP Addresses
  	if (!Array.isArray(ipAddress) || ipAddress.length === 0)
  		return '';

  	if (ipAddress.length === 1) //If we only have 1 IP Address then return
  		return ipAddress[0];
  	else { //If there are multiple then find one that is in a schedule
  		var params = disco.getScheduleContainingAnyIP(ipAddress);
  		if (params)
  			return params.ip;
  		else { //If ip is not in a schedule, default to what the ip is on the ci
  			var ci = new GlideRecord('cmdb_ci');
  			if (ci.get(sysId) && !ci.ip_address.nil())
  				return ci.getValue('ip_address');
  		}
  	}
  	return '';
  },

  _isStateMatches: function(propertyStates, changeRequestGR) {
  	if (propertyStates !== '') {
  		var triggerStates = propertyStates.split(',');
  		for (var i = 0; i < triggerStates.length; i++) {
  			var state = triggerStates[i].trim();
  			if ((this._isNumeric(state) && changeRequestGR.state + 0 === parseInt(state)) || (changeRequestGR.getDisplayValue('state') === state))
  				return true;
  		}
  		return false;
  	}
  	return true;  //Default to true if the property does not have any states defined.
  },


  _canTriggerDiscovery: function(taskSysId, propertyStates, isTopDown) {
  	var changeRequestGR = new GlideRecord('change_request');
  	if ((!taskSysId) || !changeRequestGR.get(taskSysId))
  		return false;

  	var taskCI;

  	if (isTopDown)
  		taskCI = new GlideRecord('task_cmdb_ci_service');
  	else
  		taskCI = new GlideRecord('task_ci');

  	taskCI.initialize();
  	taskCI.task = taskSysId;
  	taskCI.task.setRefRecord(changeRequestGR);
  	if (!taskCI.canWrite())
  		return false;

  	var stateMatches = this._isStateMatches(propertyStates, changeRequestGR);
  	if (!stateMatches)
  		return false;


  	//Exit for top down discovery as we do not need to validate address
  	if (isTopDown) 
  		return true;

  	taskCI.initialize();
  	taskCI.addQuery('task', taskSysId);
  	taskCI.query();
  	while (taskCI.next()) {
  		if (this._getCIAddress(taskCI.ci_item) !== '')
  			return true;
  	}
  	return false;
  },
  
  triggerTopDownDiscovery: function(taskSysId) {
  	if (GlidePluginManager.isActive(this.PLUGIN_SVC_MAPPING)) {
  		if (!taskSysId)
  			return false;

  		var taskCI = new GlideRecord('task_cmdb_ci_service');
  		taskCI.addQuery('task', taskSysId);
  		taskCI.query();

  		var ciSysIds = [];
  		while(taskCI.next())
  			ciSysIds.push(taskCI.getValue('cmdb_ci_service'));

  		if (ciSysIds.length > 0) {
  			try {
  				var additionalData = new SNC.TopDownAPIAdditionalDataJS();
  				additionalData.setCallerId(taskSysId);
  				additionalData.setCallerClass('change_request');
  				var context = new SNC.TopDownDiscoveryAPI().discoverApplications(ciSysIds, additionalData);
  				this._updateTopDownCIs(context.getDiscoveryStatus(), context.getValidCisToDiscover(), taskSysId);
  				this._updateTopDownCIs('', context.getInvalidCisToDiscover(), taskSysId);
  				return true;
  			} catch (err) {
  				gs.addErrorMessage(gs.getMessage('Discovery Error: {0}', err.message));
  			}
  		}
  	}
  	return false;
  },
  
  _updateTopDownCIs: function(statusSysId, ciSysIds, taskSysId) {
  	if (!ciSysIds || !taskSysId)
  		return;

  	var taskCI = new GlideRecord('task_cmdb_ci_service');
  	taskCI.addQuery('cmdb_ci_service', 'IN', ciSysIds);
  	taskCI.addQuery('task', taskSysId);
  	taskCI.query();

  	if (statusSysId) {
  		taskCI.setValue('discovery', statusSysId);
  		taskCI.setValue('discovery_result', this.DISCO_IN_PROGRESS);
  	} else
  		taskCI.setValue('discovery_result', this.DISCO_NA);

  	taskCI.setValue('discovery_log_url', this.generateDiscoveryStatusLink(statusSysId));
  	taskCI.setValue('discovery_last_updated', gs.nowGlideDateTime());
  	taskCI.updateMultiple();
  },
  
  getApplicationsByChangeId: function(chgId) {
  	var applications = [];
  	
  	if (!chgId)
  		return applications;
  	
  	var aUtils = new ArrayUtil();
  	
  	var taskCiGr = new GlideRecord('task_ci');
  	taskCiGr.addQuery('task', chgId);
  	taskCiGr.addQuery('ci_item.sys_class_name', GlideDBObjectManager.get().getAllExtensions("cmdb_ci_appl"));
  	taskCiGr.query();
  	while (taskCiGr.next())
  		applications.push(taskCiGr.getValue("ci_item"));

  	var cmdbRelCiGr = new GlideRecord("cmdb_rel_ci");
  	cmdbRelCiGr.addQuery("type", "60bc4e22c0a8010e01f074cbe6bd73c3"); //Runs on
  	cmdbRelCiGr.addQuery('JOINcmdb_rel_ci.child=task_ci.ci_item!task=' + chgId);
  	cmdbRelCiGr.query();
  	while (cmdbRelCiGr.next())
  		applications.push(cmdbRelCiGr.getValue("parent"));
  	
  	return applications;
  },

  _isNumeric: function (val) {
  	return !isNaN(parseFloat(val)) && isFinite(val);
  },

  type: 'ChangeRequestDiscoverySNC'
};

Sys ID

78865dfc23c81300178ddc1756bf65f0

Offical Documentation

Official Docs: