Name

global.DiscoveryPatternOrchestratorEventHandler

Description

This file is responsible for handling Orchestrator s events The 3 main events needed to be handled are 1. An Orchestrator page was received 2. A pattern was completed its execution 3. Partial payload was received

Script

var DiscoveryPatternOrchestratorEventHandler;

(function() {
  /**********************************************
  				  Constants
  ***********************************************/
  var FILE_NAME = 'DiscoveryPatternOrchestratorEventHandler',
  	FILE_NAME_LOG_PREFIX = '[' + FILE_NAME + ']',
  	LOG_PREFIX = DiscoveryPatternOrchestratorUtil.LOG_PREFIX + FILE_NAME_LOG_PREFIX;

  /**********************************************
  			    Local Variables
  ***********************************************/


  var nodeLogger = DiscoveryPatternOrchestratorUtil.getNodeLogger();
  var patternResultStatus = {
  		succeed: 'SUCCEED',
  		failed: 'FAILED',
  		gracefulTermination: 'GRACEFUL_TERMINATION'
  	},
  	partialItemStatus = {
  		pending: 'Pending',
  		resolved: 'Resolved',
  		failed: 'Failed'
  	};

  /**********************************************
  				  Public API
  ***********************************************/

  DiscoveryPatternOrchestratorEventHandler = {
  	patternResultStatus: patternResultStatus,
  	onPatternExecutionCompleted: _onPatternExecutionCompleted,
  	onPageProcessed: _onPageProcessed,
  	onRecievedPartialPayloads: _onRecievedPartialPayloads
  };

  /*
   * This function is called when the pattern was done!
   * That includes all Orchestrator pages and multipages as well
   *
   * @param statusId - Status sys_id created for the triggered discovery
   * @param outputEccId - The output ECC queue sys_id representing the pattern's execution
   */
  function _onPatternExecutionCompleted(statusId, outputEccId) {
  	var executionInfo = {
  		status: statusId,
  		executionId: outputEccId
  	};
  	// Grab the execution record representing this pattern's execution from 'sn_discovery_orchestrator_pattern_execution'
  	var patternExecutionGlideRecord = DiscoveryPatternOrchestratorUtil.getExecutionRecordByStatusOrExecutionId(executionInfo.status, executionInfo.executionId);

  	// If no executions found - quit processing
  	if (!patternExecutionGlideRecord.next()) {
  		nodeLogger.debug(LOG_PREFIX + ' No execution record found for: ' + JSON.stringify(executionInfo) + '. Abort processing');
  		return;
  	}

  	// Based on the execution record, enhance executionInfo with all details about this execution
  	DiscoveryPatternOrchestratorUtil.enhanceExecutionInfoFromGlideRecord(patternExecutionGlideRecord, executionInfo);
  	var executionSysIdLogFormat = '[' + executionInfo.sysId + ']';
  	nodeLogger.info(LOG_PREFIX + executionSysIdLogFormat + ' Pattern execution completed...\nGrabbing execution record from CMDB...');
  	nodeLogger.debug(LOG_PREFIX + executionSysIdLogFormat + ' Pattern execution details:\n'
  					+ 'Execution Info: ' + JSON.stringify(executionInfo));

  	// Get execution state & pagination information
  	var finalExecutionState = patternExecutionGlideRecord.state + '';
  	var paginationInformationGlideRecord = DiscoveryPageManager.getPageCountRecordByOutputEccQueue(patternExecutionGlideRecord.launcher + '');

  	// The sensor is calling this function when ALL pages were received
  	// We need to mark the execution as 'false'
  	nodeLogger.debug(LOG_PREFIX + executionSysIdLogFormat + ' Marking execution record as inactive');
  	patternExecutionGlideRecord.active = false;

  	/*
  	 * In case we have pagination information for this execution, make sure the
  	 * execution is pointing to it
  	 * Pagination information will only exist for executions with more than 1 ORCHESTRATOR page
  	 */
  	if (paginationInformationGlideRecord != null)
  		patternExecutionGlideRecord.pagination_information = paginationInformationGlideRecord.sys_id + '';

  	/*
  	 * In case we had issues updating - quit processing
  	 * We are updating 'active' state of the execution in this function and if this not updated,
  	 * the Orchestrator won't be able to detect whether the flow was done
  	 */
  	if (!patternExecutionGlideRecord.update()) {
  		nodeLogger.error(LOG_PREFIX + executionSysIdLogFormat + ' Error updating execution record with state: ' + finalExecutionState + ' and pagination information');
  		return;
  	}

  	executionInfo.finalState = finalExecutionState;
  	nodeLogger.info(LOG_PREFIX + executionSysIdLogFormat + ' Finished running pattern "' + executionInfo.patternName + '"');

  	/* 
  	 * Find and prepare dependent patterns for execution
  	 * We are running this logic regardless of current execution's state, because child
  	 * patterns that are configured with 'Use Parent Context' don't need their direct parent's results,
  	 * hence they should be able to run
  	 */
  	if (executionInfo.globalData.runDependentPatterns)
  		DiscoveryPatternOrchestratorFlowManager.prepareAndTriggerDependentPatterns(executionInfo);
  }

  /*
   * This function is called when an Orchestrator's page was received
   * An Orchestrator page is an execution of a single pattern on MID side out of unknown
   * number of execution of the SAME pattern
   * This is NOT the same as an ENTIRE pattern execution!
   * A full pattern execution can have 1 or more executions of the same pattern till all data fetched!
   * 
   *
   * @param executionInfo - A JS object contains basic information on the execution
   * This object has the following data:
   * { 
   *		executionId: Output ECC queue sys_id,
   *		status: Status sys_id,
   *		pattern: Pattern sys_id,
   *		schedule: Schedule sys_id,
   *		outputEccGlideRecord: Output ECC queue glide record - MAY NEED TO REMOVE
   * }
   * @param page - A JS object contains information on the page received
   * This object has the following data:
   * {
   *		status: Result status based on 'DiscoveryPatternOrchestratorEventHandler.patternResultStatus' enum,
   *		payload: The payload received from the pattern [The 'items' array specifically],
   *		number: Page number [E.g. 1.3],
   *		isLastPage: Indication if this is the last Orchestrator page,
   *		errorMessage: An error message in case of an error [E.g. while IRE processing]
   * }
   */
  function _onPageProcessed(executionInfo, page) {
  	nodeLogger.info(LOG_PREFIX + ' Page received. Finding execution information...');

  	// Grab the execution record representing this pattern's execution from 'sn_discovery_orchestrator_pattern_execution'
  	var patternExecutionGlideRecord = DiscoveryPatternOrchestratorUtil.getExecutionRecordByStatusOrExecutionId(executionInfo.status, executionInfo.executionId);

  	// If no executions found - quit processing page
  	if (!patternExecutionGlideRecord.next()) {
  		nodeLogger.debug(LOG_PREFIX + ' No execution record found for: ' + JSON.stringify(executionInfo) + '. Abort processing page');
  		return;
  	}

  	var executionSysIdLogFormat = '[' + patternExecutionGlideRecord.sys_id + ']';
  	var executionState = patternExecutionGlideRecord.state + '';

  	// In case the current pattern execution failed - quit processing page
  	if (executionState == DiscoveryPatternOrchestratorFlowManager.executionState.failed) {
  		nodeLogger.debug(LOG_PREFIX + executionSysIdLogFormat + ' Execution was marked as failed. Abort processing page');
  		return;
  	}
  	// Based on the execution record, enhance executionInfo with all details about this execution
  	DiscoveryPatternOrchestratorUtil.enhanceExecutionInfoFromGlideRecord(patternExecutionGlideRecord, executionInfo);
  	nodeLogger.debug(LOG_PREFIX + executionSysIdLogFormat + ' Page details: ' + JSON.stringify(page));

  	// In case nothing failed but we have an empty payload - quit processing page 
  	var isEmptyPayload = JSUtil.isEmpty(page.payload) || JSUtil.nil(page.payload);

  	if (isEmptyPayload) {
  		nodeLogger.warn(LOG_PREFIX + executionSysIdLogFormat + ' Recieved a page with an empty payload. Abort page processing');
  		return;
  	}

  	nodeLogger.debug(LOG_PREFIX + executionSysIdLogFormat + ' Grabbing output configuration for pattern "' + executionInfo.patternName + '"');
  	// Based on 'sn_pattern_outputs' table, grab all attributes to extract from the payload
  	var outputAttributeMap = DiscoveryPatternOrchestratorUtil.generateClassToAttributesMapFromOutputConfig(executionInfo.patternId);

  	nodeLogger.debug(LOG_PREFIX + executionSysIdLogFormat + ' Attributes to extract: ' + JSON.stringify(outputAttributeMap) + ' from payload:\n' + JSON.stringify(page.payload));
  	nodeLogger.debug(LOG_PREFIX + executionSysIdLogFormat + ' About to extract information from payload...\nPattern: ' + executionInfo.patternName);

  	// Extract requested attributes from the payload into a different cleaner payload
  	var payloadExtracted = PatternPayloadUtil.extractAttributesFromPayload(page.payload, outputAttributeMap);

  	// If no data extracted - quit processing page
  	if (!payloadExtracted) {
  		var message = 'No attributes were extracted from payload. Please verify the attributes configured for pattern\'s output are in the payload. Abort page processing';
  		nodeLogger.error(LOG_PREFIX + executionSysIdLogFormat + ' ' + message);
  		DiscoveryLogger.error(message, FILE_NAME);
  		return;
  	}

  	/* 
  	 * Save the extracted data to the output table
  	 * Pay attention that each object is saved in a separate record!
  	 * This is done in order to prevent duplicates to be saved aside
  	 * Multiple Orchestrator pages can have the same item multiple times - This can cause the Orchestrator
  	 * to run a child pattern on the same object multiple times!
  	 */
  	var filteredPayload = DiscoveryPatternOrchestratorUtil.filterPayload(executionInfo, payloadExtracted);

  	if (JSUtil.nil(filteredPayload) || JSUtil.isEmpty(filteredPayload))
  		return;
  	DiscoveryPatternOrchestratorUtil.saveExtractedPayload(executionInfo, filteredPayload, page.number);
  }

  /*
   * This function is called when the horizontal sensor inserted partial data
   *
   * @param partialPayloadIdList - List of sys_ids representind records inserted to CMDB's partial payload table
   * @param patternId - Pattern sys_id
   * @param eccInputId - The input ECC queue sys_id of this payload
   * @param statusId - Status sys_id created for the triggered discovery
   */
  function _onRecievedPartialPayloads(partialPayloadIdList, patternId, eccInputId, statusId) {
  	if (statusId == null) {
  		nodeLogger.error(LOG_PREFIX + ' Failed to find status during partial payload tracking associated with the eccId: ' + eccInputId);
  		return;
  	}

  	if (partialPayloadIdList == null) {
  		nodeLogger.error(LOG_PREFIX + ' Expected partial payload IDs associated with the eccId: ' + eccInputId);
  		return;
  	}

  	/*
  	 * Whenever CMDB gets partial data, it inserts it to partial payload table and provides us with sys_id
  	 * In case another payload came and matched it, the relevant records get depracated and new partial
  	 * records created with the data that still doesn't have any match
  	 *
  	 * We are tracking these changes in order to provide informative logs to the user!
  	 * At the end of the discovery the user will get a feedback based on the table below, saying if
  	 * there is partial data left in CMDB's table
  	 */
  	partialPayloadIdList.forEach(function(payloadId) {
  		var partialPayloadTrackRecord = new GlideRecord('sn_discovery_pattern_orchestrator_partial_inserts');
  		partialPayloadTrackRecord.initialize();
  		partialPayloadTrackRecord.setValue('partial_payload_id', payloadId);
  		partialPayloadTrackRecord.setValue('pattern_id', patternId);
  		partialPayloadTrackRecord.setValue('ecc_input_id', eccInputId);
  		partialPayloadTrackRecord.setValue('status_id', statusId);
  		partialPayloadTrackRecord.setValue('state', partialItemStatus.pending);
  		partialPayloadTrackRecord.insert();
  	});
  }

  /**********************************************
  			Private Helper Functions
  ***********************************************/

})();

Sys ID

4eea4e860f3580106dc4fe39b4767ef3

Offical Documentation

Official Docs: