Name
global.AbstractDeleteStrategy
Description
No description available
Script
var AbstractDeleteStrategy = Class.create();
AbstractDeleteStrategy.LOGGER_NAME = 'DeleteStrategy';
AbstractDeleteStrategy.LOGGER_NAME_LOG_PREFIX = '[' + AbstractDeleteStrategy.LOGGER_NAME + ']';
AbstractDeleteStrategy.prototype = {
discoverySource: 'ServiceNow',
deleteStrategies: {
// 1 is "keep" but we don't use it in delete strategy
2: {
description: 'delete CI'
},
3: {
description: 'mark as absent'
},
4: {
description: 'delete relations'
},
5: {
description: 'mark as retired'
}
},
initialize: function() {
// Can't initialize at the top. Functions are undefined on that phase
this._intializeDeleteStategyToDeleteFunctionMapping();
},
_intializeDeleteStategyToDeleteFunctionMapping: function() {
this.deleteStrategies[2].deletionFunction = this._deleteCI.bind(this);
this.deleteStrategies[3].deletionFunction = this._markAbsent.bind(this);
this.deleteStrategies[4].deletionFunction = this._deleteRelations.bind(this);
this.deleteStrategies[5].deletionFunction = this._markRetired.bind(this);
},
/*
*_getRelationshipPaths: function() {
* // a sublass can provide additional relationship paths to walk down ex:
*
* var additionalRelationPaths = {};
*
* additionalRelationPaths['cmdb_ci_endpoint_block'] = [
* 'cmdb_ci_endpoint_block', 'cmdb_ci_storage_volume', 'cmdb_ci_aws_datacenter'
* ];
*
* return additionalRelationPaths;
* },
*/
/*
scheduleStartTime - GlideDate
ciClass - class
patternInputs - specific discovery content
patternContext - specific discovery content
ciTypeStrategyMapping - map between class and delete strategy for children CIs
agentCorrelator - Discovery Status - optional for logging
*/
reconcile: function(scheduleStartTime, ciClass, patternInputs, patternContext, ciTypeStrategyMapping, agentCorrelator) {
/*
* The reason creating this variable is to reduce the number of arguments on future function calls
* We can't have it as "this.varibaleName" since the value is shared among multiple instances
* of this object
* At the moment, I [Oron] have decided to create a single object from input parameters
* to pass through, rather having functions with ~10+ arguments
* The algorithm did NOT change
*/
var deleteStrategyData = {
inputParams: {
scheduleStartTime: scheduleStartTime,
targetCiClass: ciClass,
patternInputs: patternInputs,
patternContext: patternContext,
ciTypeToStrategy: ciTypeStrategyMapping,
statusSysId: agentCorrelator
},
maxCIsToDelete: GlideProperties.get('glide.discovery.delete_strategy.max_deletion_records', 2000),
maxCIsInSearchGroup: GlideProperties.get('glide.discovery.delete_strategy.max_ci_search_group', 100),
maxRelatedEntriesToDeleteInIteration: GlideProperties.get('glide.discovery.delete_strategy.max_related_entried_delete_in_iteration', 500),
cisDeleted: 0,
relatedEntries: this._findRelatedEntries(ciClass),
nodeLogger: new sn_automation.AutomationAPI().getNodeLogger(AbstractDeleteStrategy.LOGGER_NAME)
};
deleteStrategyData.nodeLogger.info(this._buildLogMessage('Starting delete strategy'));
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Input parameters:\n' + JSON.stringify(deleteStrategyData.inputParams)));
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Configuration:\nMax CIs to handle:' +
deleteStrategyData.maxCIsToDelete + '\nCIs group size for related entries deletion: ' +
deleteStrategyData.maxCIsInSearchGroup + '\nRelated entries config: ' + JSON.stringify(deleteStrategyData.relatedEntries)));
if (this._getCIs) {
deleteStrategyData.nodeLogger.info(this._buildLogMessage('"_getCIs" function was implemented in provided script. Getting CIs to delete'));
var reconcilables = this._getCIs(scheduleStartTime, ciClass, patternInputs, patternContext, deleteStrategyData.nodeLogger);
if (reconcilables) {
var message = 'Reconcilable CIs were specified by the subclass. Processing delete strategy for ' + ciClass;
deleteStrategyData.nodeLogger.info(this._buildLogMessage(message));
deleteStrategyData.nodeLogger.debug(this._buildLogMessage(' CIs to delete: ' + JSON.stringify(reconcilables)));
for (var item in reconcilables) {
deleteStrategyData.nodeLogger.debug(this._buildLogMessage(' Deleting ' + reconcilables[item]));
this._reconcileOnStrategy(deleteStrategyData, reconcilables[item]);
}
deleteStrategyData.nodeLogger.debug(this._buildLogMessage(' Deleted total of ' + reconcilables.length + ' CIs'));
}
return;
}
// a subclass MUST provide an implementation of _getBoundaryConditions to find the CI's to reconcile
// if no boundaries are specified, stop processing.. without a boundary condition, we _MIGHT_ end up
// marking all the CI's in the CMDB as stale which is not a desirable behavior
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Getting boundaries for context:\n' + JSON.stringify(patternContext)));
var boundaries = this._getBoundaryConditions(patternContext, deleteStrategyData.nodeLogger);
if (boundaries && boundaries.length == 0) {
var message = 'No boundary conditions were provided. Cannot process delete strategy without a valid boundary condition';
deleteStrategyData.nodeLogger.warn(this._buildLogMessage(message));
gs.warn(message, 'DeleteStrategy');
return;
}
var existingRelationPaths = this._getRelationshipPaths();
deleteStrategyData.nodeLogger.info(this._buildLogMessage('Processing boundary conditions : ' + boundaries.join()));
for (var boundary in boundaries) {
var boundaryItem = boundaries[boundary];
if (typeof boundaryItem == 'string')
boundaryItem = this._getRecordByID(boundaryItem);
var boundaryClass = boundaryItem.sys_class_name;
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Processing boundary "' + boundaryItem.getValue('sys_id') + '" of class "' + boundaryClass + '"'));
// Get path between our class and ldc
var path = [];
if (existingRelationPaths && existingRelationPaths[ciClass])
path = existingRelationPaths[ciClass];
else
path = this._getPathBetweenClasses(boundaryClass, ciClass);
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Classpath found between "' + ciClass + '" to "' + boundaryClass + '": ' + path.join('->')));
if (path == null) {
var message = 'No path could be created between ' + boundaryClass + ' and ' + ciClass;
deleteStrategyData.nodeLogger.warn(this._buildLogMessage(message));
gs.warn(message, 'DeleteStrategy');
continue;
}
deleteStrategyData.pathToBoundaryClass = path;
var levelInPath = path.length - 2;
var layerToExplore = {
level: levelInPath,
className: path[levelInPath],
parentCi: boundaryItem.sys_id + ''
};
deleteStrategyData.nodeLogger.info(this._buildLogMessage('Starting CMDB search for stale CIs'));
this._recursiveReconcileOnStrategy(deleteStrategyData, layerToExplore);
}
},
_recursiveReconcileOnStrategy: function(deleteStrategyData, layerToExplore) {
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Layer to explore: ' + JSON.stringify(layerToExplore)));
if (this._internalWalkStep(deleteStrategyData, 'parent', 'child', layerToExplore))
return;
this._internalWalkStep(deleteStrategyData, 'child', 'parent', layerToExplore);
},
_internalWalkStep: function(deleteStrategyData, relationDirectionFrom, relationDirectionTo, layerToExplore) {
var isDeletionLayer = layerToExplore.className == deleteStrategyData.inputParams.targetCiClass;
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Preparing a query on relations table for ' + relationDirectionTo + ' CIs of type ' + layerToExplore.className + ' that have a relation to ' + layerToExplore.parentCi));
var children = this._prepareChildrenQuery(relationDirectionFrom, layerToExplore.parentCi, relationDirectionTo, layerToExplore.className);
if (isDeletionLayer) {
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Deletion layer have been reached. Adding additional conditions to the search'));
this._logCommonQueryConditions(deleteStrategyData.nodeLogger, relationDirectionTo);
this._addDeleteLayerCommonConditions(children, relationDirectionTo);
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Adding condition: ' + relationDirectionTo + '\'s last_discovered is at or before ' + scheduleStartTime));
this._addStaleConditions(children, relationDirectionTo, deleteStrategyData.inputParams.scheduleStartTime);
this._enhanceDeleteLayerQuery(children, relationDirectionTo, deleteStrategyData.inputParams.scheduleStartTime);
message = 'About to find and delete ' + layerToExplore.className + ' records. Deletion strategy is "' + this._getStrategyNameByCode(this._getDeleteStrategyCodeFromData(deleteStrategyData)) + '"';
deleteStrategyData.nodeLogger.info(this._buildLogMessage(message));
}
children.query();
var hasChildren = children.hasNext();
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Found ' + children.getRowCount() + ' ' + layerToExplore.className + ' records'));
while (children.next()) {
if (deleteStrategyData.cisDeleted > deleteStrategyData.maxCIsToDelete)
return;
if (isDeletionLayer) {
this._reconcileOnStrategy(deleteStrategyData, children.getValue(relationDirectionTo));
deleteStrategyData.cisDeleted = deleteStrategyData.cisDeleted + 1;
if (deleteStrategyData.cisDeleted > deleteStrategyData.maxCIsToDelete) {
var warnMsg = 'Exceeded the maximum number of deletions (' + deleteStrategyData.maxCIsToDelete + ') in one pattern. Execute the pattern again to continue deleteing stale records. Increase the number property: "glide.discovery.delete_strategy.max_deletion_records" to be able to trigger more deletions in one pattern.';
deleteStrategyData.nodeLogger.warn(this._buildLogMessage(warnMsg));
if (!deleteStrategyData.inputParams.statusSysId){
gs.warn(warnMsg);
return;
}
var logger = new DiscoveryLogger(deleteStrategyData.inputParams.statusSysId);
logger.warn(warnMsg);
return;
}
continue;
}
var nextLevelInPath = layerToExplore.level - 1;
var nextLayerToExplore = {
level: nextLevelInPath,
className: deleteStrategyData.pathToBoundaryClass[nextLevelInPath],
parentCi: children.getValue(relationDirectionTo) + ''
};
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Further search required. Drilling down to the next layer'));
this._recursiveReconcileOnStrategy(deleteStrategyData, nextLayerToExplore);
}
// For related entries
if (isDeletionLayer) {
message = 'About to handle related entries for non-stale CIs';
deleteStrategyData.nodeLogger.info(this._buildLogMessage(message));
var nonStaleChildrenRelations = this._prepareChildrenQuery(relationDirectionFrom, layerToExplore.parentCi, relationDirectionTo, layerToExplore.className);
this._logCommonQueryConditions(deleteStrategyData.nodeLogger, relationDirectionTo);
this._addDeleteLayerCommonConditions(nonStaleChildrenRelations, relationDirectionTo);
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Adding condition: ' + relationDirectionTo + '\'s last_discovered is after ' + scheduleStartTime));
this._addNonStaleConditions(nonStaleChildrenRelations, relationDirectionTo, deleteStrategyData.inputParams.scheduleStartTime);
nonStaleChildrenRelations.query();
this._reconcileRelatedEntriesUsingReltions(deleteStrategyData, nonStaleChildrenRelations, relationDirectionTo);
message = 'Finished handling stale related entries';
deleteStrategyData.nodeLogger.info(this._buildLogMessage(message));
}
return hasChildren;
},
_prepareChildrenQuery: function(source, refCi, target, childClass) {
var relationsGlideRecord = new GlideRecord('cmdb_rel_ci');
relationsGlideRecord.addQuery(source, refCi);
relationsGlideRecord.addQuery(target + '.sys_class_name', childClass);
return relationsGlideRecord;
},
_logCommonQueryConditions: function(nodeLogger, positionInRelation) {
nodeLogger.debug(this._buildLogMessage('Adding condition: ' + positionInRelation + '\'s operational status does not contain ' + JSON.stringify(this._deleteLayerExcludes())));
nodeLogger.debug(this._buildLogMessage('Adding condition: ' + positionInRelation + '\'s discovery_source is ' + JSON.stringify(this._deleteLayerSources())));
},
_reconcileRelatedEntriesUsingReltions: function(deleteStrategyData, relationsGlideRecord, relationPosition) {
if (!deleteStrategyData.relatedEntries || JSUtil.isEmpty(deleteStrategyData.relatedEntries )) {
deleteStrategyData.nodeLogger.warn(this._buildLogMessage('No related entries configuration was detected'));
return;
}
var cisToWorkOn = [],
totalStaleRelatedEntriesDeleted = 0;
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Working on groups of ' + deleteStrategyData.maxCIsInSearchGroup + ' ' + deleteStrategyData.inputParams.targetCiClass));
while (relationsGlideRecord.next()) {
var sysId = relationsGlideRecord.getValue(relationPosition);
cisToWorkOn.push(sysId);
if (cisToWorkOn.length == deleteStrategyData.maxCIsInSearchGroup)
totalStaleRelatedEntriesDeleted += this._findAndDeleteStaleRelatedEntriesOfCIs(deleteStrategyData, cisToWorkOn);
}
if (this._haveLeftOvers(cisToWorkOn.length))
totalStaleRelatedEntriesDeleted += this._findAndDeleteStaleRelatedEntriesOfCIs(deleteStrategyData, cisToWorkOn);
deleteStrategyData.nodeLogger.info(this._buildLogMessage('Deleted total of ' + totalStaleRelatedEntriesDeleted + ' related entries'));
},
_findAndDeleteStaleRelatedEntriesOfCIs: function(deleteStrategyData, cisToWorkOn) {
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('CIs to work on: ' + JSON.stringify(cisToWorkOn)));
var relatedEntriesConfigData = deleteStrategyData.relatedEntries,
totalStaleRelatedEntriesDeleted = 0;
for (var index in relatedEntriesConfigData) {
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('About to find stale ' + relatedEntriesConfigData[index].table + ' records'));
var staleRelatedEntriesRecordsSysIds = this._findStaleRelatedEntriesForCIs(relatedEntriesConfigData[index], cisToWorkOn, deleteStrategyData.inputParams.scheduleStartTime);
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Found total of' + staleRelatedEntriesRecordsSysIds.length + ' stale related entries'));
var itemsToDelete = staleRelatedEntriesRecordsSysIds.splice(0, deleteStrategyData.maxRelatedEntriesToDeleteInIteration);
while (itemsToDelete.length > 0) {
deleteStrategyData.nodeLogger.debug(this._buildLogMessage('Deleting the following sysids: ' + JSON.stringify(itemsToDelete)));
this._deleteStaleRelatedEntries(relatedEntriesConfigData[index], itemsToDelete);
deleteStrategyData.nodeLogger.debug(this._buildLogMessage(itemsToDelete.length + ' ' + relatedEntriesConfigData[index].table + ' items were deleted'));
totalStaleRelatedEntriesDeleted += itemsToDelete.length;
itemsToDelete = staleRelatedEntriesRecordsSysIds.splice(0, deleteStrategyData.maxRelatedEntriesToDeleteInIteration);
}
}
cisToWorkOn = [];
return totalStaleRelatedEntriesDeleted;
},
_findStaleRelatedEntriesForCIs: function(relatedEntryConfigData, cisSysIds, scheduleStartTime) {
var relatedEntriesSysIds = this._getAllRelatedEntriesForCIs(relatedEntryConfigData, cisSysIds);
if (relatedEntriesSysIds.length == 0)
return [];
var staleRelatedEntriesRecordsSysIds = this._detectStaleRecordsInList(relatedEntriesSysIds, scheduleStartTime);
return staleRelatedEntriesRecordsSysIds;
},
_getAllRelatedEntriesForCIs: function(relatedEntryConfigData, cisSysIds) {
var relatedEntriesSysIds = [];
var relatedEntryGlideRecord = new GlideRecord(relatedEntryConfigData.table);
relatedEntryGlideRecord.addQuery(relatedEntryConfigData.referenced_field, 'IN', cisSysIds);
relatedEntryGlideRecord.query();
while (relatedEntryGlideRecord.next())
relatedEntriesSysIds.push(relatedEntryGlideRecord.getUniqueValue());
return relatedEntriesSysIds;
},
_detectStaleRecordsInList: function(relatedEntriesSysIds, scheduleStartTime) {
var staleRecordsSysIds = [];
var sysObjectSourceGlideRecord = new GlideRecord('sys_object_source');
sysObjectSourceGlideRecord.addQuery('name', this.discoverySource);
sysObjectSourceGlideRecord.addQuery('sys_updated_on', '<', scheduleStartTime);
sysObjectSourceGlideRecord.addQuery('target_sys_id', 'IN', relatedEntriesSysIds);
sysObjectSourceGlideRecord.query();
while (sysObjectSourceGlideRecord.next())
staleRecordsSysIds.push(sysObjectSourceGlideRecord.getValue('target_sys_id'));
return staleRecordsSysIds;
},
_deleteStaleRelatedEntries: function(relatedEntryConfigData, staleRecordsSysIds) {
var relatedEntryGlideRecord = new GlideRecord(relatedEntryConfigData.table);
relatedEntryGlideRecord.addQuery('sys_id', 'IN', staleRecordsSysIds);
relatedEntryGlideRecord.query();
relatedEntryGlideRecord.deleteMultiple();
},
_haveLeftOvers: function(itemsToHandle) {
return itemsToHandle > 0;
},
// reconcile the CI
_reconcileOnStrategy: function(deleteStrategyData, ciRef) {
var ci = new GlideRecord(deleteStrategyData.inputParams.targetCiClass);
if (!ci.get('sys_id', ciRef)) {
var logger = new DiscoveryLogger(deleteStrategyData.inputParams.statusSysId);
var message = 'No CI with sysid ' + ciRef + ' was found. Skipping deletion';
deleteStrategyData.nodeLogger.warn(this._buildLogMessage(message));
logger.warn(message);
return;
}
// remove all related entries first
if (deleteStrategyData.relatedEntries) {
for (var counter = 0; counter < deleteStrategyData.relatedEntries.length; counter ++) {
var relatedEntry = deleteStrategyData.relatedEntries[counter];
var relatedEntryCleanerGr = new GlideRecord(relatedEntry.table);
relatedEntryCleanerGr.addQuery(relatedEntry.referenced_field, ciRef);
relatedEntryCleanerGr.deleteMultiple();
}
}
var targetClassDeleteStrategy = deleteStrategyData.inputParams.ciTypeToStrategy.get(deleteStrategyData.inputParams.targetCiClass);
var deleteCI = this._getDeleteStrategyFunction(targetClassDeleteStrategy);
deleteCI(ci, deleteStrategyData.inputParams.scheduleStartTime);
var children = this._getChildren(ci, deleteStrategyData.inputParams.scheduleStartTime);
if (children.length > 0) {
gs.info('Processing ' + children.length + ' stale children of CI : ' + ciRef);
for (var index = 0; index < children.length; index++) {
var childCi = children[index];
var childCiRef = new GlideRecord(childCi.table);
if (childCiRef.get('sys_id', childCi.sysId)) {
var preferredStrategy = deleteStrategyData.inputParams.ciTypeToStrategy.get(childCi.table) ? deleteStrategyData.inputParams.ciTypeToStrategy.get(childCi.table) : targetClassDeleteStrategy;
deleteCI = this._getDeleteStrategyFunction(preferredStrategy);
deleteCI(childCiRef, deleteStrategyData.inputParams.scheduleStartTime);
}
}
}
},
_getDeleteStrategyFunction: function(deleteStrategyCode) {
return this.deleteStrategies[deleteStrategyCode].deletionFunction;
},
_findRelatedEntries: function(ciClass) {
var relatedEntries = [];
// process the related entries for the stale CI
var relatedEntriesGr = new GlideRecord('cmdb_related_entry');
var relJoinGr = relatedEntriesGr.addJoinQuery('cmdb_identifier', 'identifier', 'sys_id');
relJoinGr.addCondition('applies_to', ciClass);
relatedEntriesGr.query();
while (relatedEntriesGr.next()) {
var relatedEntry = {};
relatedEntry.table = relatedEntriesGr.getValue('table');
relatedEntry.referenced_field = relatedEntriesGr.getValue('referenced_field');
relatedEntries.push(relatedEntry);
}
return relatedEntries;
},
// mark a CI as absent
_markAbsent: function(ci) {
if (!ci.isValidField('operational_status') || !ci.isValidField('install_status'))
return;
gs.info('Marking CI ' + ci.sys_id + ' as absent');
if (ci.isValidField('state'))
ci.state = 'terminated';
ci.operational_status = '2'; // non-operational
ci.install_status = '100'; //absent
ci.update();
},
// mark a CI as retired
_markRetired: function(ci) {
if (!ci.isValidField('operational_status') || !ci.isValidField('install_status'))
return;
gs.info('Marking CI ' + ci.sys_id + ' as retired');
if (ci.isValidField('state'))
ci.state = 'terminated';
ci.operational_status = '6'; // retired
ci.install_status = '7'; // retired
ci.update();
},
// delete a CI
_deleteCI: function(ci, scheduleStartTime) {
this._deleteRelations(ci, scheduleStartTime);
gs.info('Deleting CI ' + ci.sys_id + ' from CMDB');
ci.deleteRecord();
},
// delete all relations for this CI
_deleteRelations: function(ci, scheduleStartTime) {
// for a CI that's being updated to stale, find the relationships that it's referenced in and
// remove that if the timestamp is not after the discovery schedule launch time
gs.info('Removing stale relationships for CI : ' + ci.sys_id);
var relationsGr = new GlideRecord('cmdb_rel_ci');
relationsGr.addQuery('parent', ci.sys_id);
relationsGr.addQuery('sys_updated_on', '<=', scheduleStartTime);
relationsGr.deleteMultiple(); // remove the stale relationship from the CMDB
},
// reconcile the children of a specific CI
_getChildren: function(ci, scheduleStartTime) {
var children = [];
var childRel = new GlideRecord('cmdb_rel_ci');
childRel.addQuery('parent', ci.sys_id);
childRel.addQuery('type.name', 'Contains::Contained by');
childRel.addQuery('child.operational_status', 'NOT IN', this._deleteLayerExcludes()); //filter out already reconciled CIs
childRel.addQuery('child.last_discovered', '<=', scheduleStartTime);
childRel.query();
while (childRel.next()) {
var childCI = {};
childCI.sysId = childRel.getValue('child');
childCI.table = childRel.child.sys_class_name + '';
children.push(childCI);
}
return children;
},
_getPathBetweenClasses: function(targetClass, sourceClass, path, level) {
if (level == null)
level = 0;
if (level > 4)
return null;
if(sourceClass == null || sourceClass == '')
return null;
var newPath = [];
if (path != null)
path.forEach(function(item) { newPath.push(item); } );
newPath.push(sourceClass);
var classHierarchy = SNC.ClassModel.getClassHierachy(sourceClass, 'cmdb_ci');
// Check host parents
var hostWalk = new GlideRecord('cmdb_metadata_hosting');
hostWalk.addQuery('parent_type', 'IN', classHierarchy);
hostWalk.addQeury('rel_type.name', 'Hosted on::Hosts');
hostWalk.query();
var possibleRelationPaths = [];
while (hostWalk.next()) {
if (SNC.ClassModel.isInstanceOf(targetClass, hostWalk.child_type)) {
newPath.push(hostWalk.child_type);
return newPath;
}
possibleRelationPaths.push(hostWalk.child_type);
}
for (var i = 0; i < possibleRelationPaths.length; i++) {
var hostChildPathResult = this._getPathBetweenClasses(targetClass, possibleRelationPaths[i], newPath, level + 1);
if (hostChildPathResult != null)
return hostChildPathResult;
}
// Check contains parents
var containWalk = new GlideRecord('cmdb_metadata_containment');
containWalk.addQuery('ci_type', 'IN', classHierarchy);
containWalk.addQeury('rel_type.name', 'Contains::Contained by');
containWalk.query();
possibleRelationPaths = [];
while (containWalk.next()) {
if (SNC.ClassModel.isInstanceOf(targetClass, containWalk.parent_id.ci_type)) {
newPath.push(containWalk.parent_id.ci_type);
return newPath;
}
possibleRelationPaths.push(containWalk.parent_id.ci_type);
}
for (i = 0; i < possibleRelationPaths.length; i++) {
var containChildPathResult = this._getPathBetweenClasses(targetClass, possibleRelationPaths[i], newPath, level + 1);
if (containChildPathResult != null)
return containChildPathResult;
}
return null;
},
_getRecordByID: function(sysId) {
var ciRecord = new GlideRecord('cmdb_ci');
ciRecord.get(sysId);
return ciRecord;
},
_addStaleConditions: function(currentQuery, positionInRelation, scheduleStartTime) {
currentQuery.addQuery(positionInRelation + '.last_discovered', '<=', scheduleStartTime);
},
_addNonStaleConditions: function(currentQuery, positionInRelation, scheduleStartTime) {
currentQuery.addQuery(positionInRelation + '.last_discovered', '>', scheduleStartTime);
},
_addDeleteLayerCommonConditions: function(currentQuery, positionInRelation) {
currentQuery.addQuery(positionInRelation + '.operational_status', 'NOT IN', this._deleteLayerExcludes());
currentQuery.addQuery(positionInRelation + '.discovery_source', 'IN', this._deleteLayerSources());
},
_enhanceDeleteLayerQuery: function(currentQuery, target, scheduleStartTime) {
return currentQuery;
},
_deleteLayerExcludes: function() {
return ['2', '6'];
},
_deleteLayerSources: function() {
return ['ServiceNow'];
},
_getDeleteStrategyCodeFromData: function(deleteStrategyData) {
return deleteStrategyData.inputParams.ciTypeToStrategy.get(deleteStrategyData.inputParams.targetCiClass);
},
_getStrategyNameByCode: function(deleteStrategyCode) {
return this.deleteStrategies[deleteStrategyCode].description || 'Unknown'
},
_buildLogMessage: function(message) {
return AbstractDeleteStrategy.LOGGER_NAME_LOG_PREFIX + '[' + this._getScriptTableName() + '_' + this._getScriptSysId() + '] ' + message;
},
_getScriptTableName: function() {
return 'sys_script_include';
},
_getScriptSysId: function() {
return '591e87c1db690010690cf9c31d9619f7';
},
type: 'AbstractDeleteStrategy'
};
// A template to create a new strategy script
AbstractDeleteStrategy.deleteStrategyTemplate = 'var sysparm_delete_strategy_name = Class.create(); \
\
sysparm_delete_strategy_name.prototype = Object.extendsObject(global.AbstractDeleteStrategy, { \
\
/* \
* Override if you want to implement your own custom search logic \
* This should return an array of the sysids to delete \
* \
* _getCIs: function(scheduleStartTime, ciClass, patternInputs, patternContext, nodeLogger) { \
* \t// To use nodeLogger: nodeLogger.[info\\warn\\error\\debug](this._buildLogMessage(message)); \
* }, \
*/ \
\
/* \
*_getRelationshipPaths: function() { \
* // a sublass can provide additional relationship paths to walk down ex: \
* \
* var additionalRelationPaths = {}; \
* \
* additionalRelationPaths[\'cmdb_ci_endpoint_block\'] = [ \
* \'cmdb_ci_endpoint_block\', \'cmdb_ci_storage_volume\', \'cmdb_ci_aws_datacenter\' \
* ]; \
* \
* return additionalRelationPaths; \
* }, \
*/ \
\
// Given a pattern\'s context, return the boundaries needed to identify stale CI\'s \
// MUST be implemented! \
_getBoundaryConditions: function(patternContext, nodeLogger) { \
// To use nodeLogger: nodeLogger.[info\\warn\\error\\debug](this._buildLogMessage(message)); \
var boundaries = []; \
return boundaries; \
}, \
\
_getScriptTableName: function() { \
return \'sn_discovery_delete_strategy\'; \
}, \
\
_getScriptSysId: function() { \
return \'sysparm_delete_strategy_sysid\'; \
}, \
\
type: \'sysparm_delete_strategy_name\' \
});';
Sys ID
591e87c1db690010690cf9c31d9619f7