Name
sn_em_tbac.EvtMgmtTagBasedDefinitionToACR
Description
Generates alert correlation rule from Alert Clustering Definition and Alert Clustering Tags
Script
var EvtMgmtTagBasedDefinitionToACR = Class.create();
EvtMgmtTagBasedDefinitionToACR.prototype = {
type: 'EvtMgmtTagBasedDefinitionToACR',
initialize: function() {
this.tbacUtils = new sn_em_tbac.EvtMgmtTagBasedAlertClusteringUtils();
this.ACR_NAME_PREFIX = '[Tag Based]' + ' '; // Prefix for referenced alert correlation rule for easy identification.
this.ACR_DESCRIPTION_PREPEND = 'This alert correlation rule and its advanced mode script were created automatically by Tag Based Alert Clustering Engine.\n\n\
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\
!!! ANY MANUAL MODIFICATION TO THIS SCRIPT MAY BE OVERRIDDEN !!!\n\
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n';
},
/**
* On creation or update of Definition with Tags:
* 1. Create alert correlation rule if it doesn't exist.
* 2. Update the script of the alert correlation rule - whether it exists or being created.
* @param definitionRecord - A Definition record. Reference from M2M table (current.alert_clustering_definition) or from Definitions table (current)
* @returns {String} - sys_id of the created/updated alert correlation rule
*/
upsertACR: function(definitionRecord) {
// Get the alert correlation rule that's associated with the definition
var acr = new GlideRecord('em_alert_correlation_rule');
acr.get(definitionRecord.alert_correlation_rule);
// Create alert correlation rule if it doesn't exist
if (!acr.isValidRecord()) {
acr.initialize();
acr.setNewGuidValue(gs.generateGUID());
acr.advanced = true; // Always advanced
acr.relationship_type = "1"; // "1" - No relationship
}
// When the virtual alerts feature is active, create a virtual alert
acr.generate_virtual_alerts = this.tbacUtils.isVirtualAlertFeatureActive();
// Update ACR fields according to Definition fields even if exists
acr.active = definitionRecord.getValue('active');
acr.name = this.ACR_NAME_PREFIX + definitionRecord.getValue('name');
acr.description = this.getDescriptionForACR(definitionRecord.getUniqueValue(), definitionRecord.sys_domain.getDisplayValue(), definitionRecord.getValue('sys_domain'), definitionRecord.getValue('name'), definitionRecord.getValue('description'), definitionRecord.sys_overrides.getDisplayValue(), definitionRecord.getValue('sys_overrides'));
acr.advanced_filter = definitionRecord.getValue('alert_filter');
acr.order = definitionRecord.getValue('order');
acr.time_difference = definitionRecord.getValue('time_difference');
acr.sys_domain = definitionRecord.getValue('sys_domain');
acr.rule_origin = "2"; // "2" - Tag Based Alert Clustering Engine
acr.override_group_description = definitionRecord.getValue('override_group_description');
acr.custom_group_description = definitionRecord.getValue('custom_group_description');
var acrSysId = acr.getUniqueValue() ? acr.getUniqueValue() : String(definitionRecord.alert_correlation_rule);
// Get an updated ACR script and set it to the ACR
acr.script = this.getACRScript(
acrSysId,
definitionRecord.getUniqueValue(),
definitionRecord.getValue('name'),
definitionRecord.sys_domain.getDisplayValue(),
definitionRecord.getValue('sys_domain'),
definitionRecord.getValue('alert_filter'),
definitionRecord.getValue('compared_alerts_limit'),
definitionRecord.getValue('time_difference'),
definitionRecord.sys_overrides.getDisplayValue(),
definitionRecord.getValue('sys_overrides')
);
// acr.update() inserts a new ACR if it doesn't exist, or updates it if it does.
return acr.update(); // Return the sys_id of the created/updated alert correlation rule
},
/* Returns description for the alert correlation rule that will be created.
* This description includes:
* 1. The Definition sys_id, sys_domain and name that created the ACR.
* 2. The description of the Definition.
* 3. The Tags that were used in the definition and for the ACR.
*/
getDescriptionForACR: function(definitionSysId, definitionDomainName, definitionDomainId, definitionName, definitionDescription, definitionOverridesName, definitionOverridesId) {
var definitionDetails = [
'sys_id: ' + definitionSysId,
'domain: ' + definitionDomainName + ' (sys_domain: ' + definitionDomainId + ')',
'overrides: ' + definitionOverridesName + ' (sys_overrides: ' + definitionOverridesId + ')',
];
var description = this.ACR_DESCRIPTION_PREPEND + 'From Definition: "' + definitionName + '", ' + definitionDetails.join(', ');
if (definitionDescription) {
description += '\n' + "Definition's description:\n" + definitionDescription;
}
var tagsData = this.tbacUtils.getTagsDataForDefinition(definitionSysId, definitionDomainId);
if (tagsData.hasTags()) {
var tags = tagsData.getAllTags();
description += '\n\n' + 'Definition\'s Tags:';
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
var tagDetails = [
'name and sys_id: ' + tag.name + ' (sys_id: ' + tag.sys_id + ')', // Tag name and sys_id
'domain: ' + tag.sys_domain_display_value + ' (sys_domain: ' + tag.sys_domain + ')', // Tag domain
'overrides: ' + tag.sys_overrides_display_value + ' (sys_overrides: ' + tag.sys_overrides + ')' // Tag overrides
];
description += '\n' + (i + 1) + '. ' + tagDetails.join(', ');
}
}
return description;
},
getACRScript: function(acrSysId, definitionSysId, definitionName, definitionDomainName, definitionDomainId, alertFilter, comparedAlertsLimit, timeDifference, domainOverridesName, domainOverridesId) {
var tagsData = this.tbacUtils.getTagsDataForDefinition(definitionSysId, definitionDomainId);
if (!tagsData.hasTags()) {
// Definition without tags is invalid and can't exist, return empty string for its script.
return '';
}
// Modify and enrich the base alert correlation rule script with every tag's unique data.
var enrich = {
beforeQuery: '',
query: '',
afterQuery: '',
loopStart: '',
comparison: ''
};
// The field to compare tags with source alert tags.
var alertTagsField = this.tbacUtils.getAlertTagsField();
var allTags = tagsData.tags;
for (var tagsSource in allTags) {
// classifiedTags - arrays of [em_alert tags], [cmdb_ci tags], [additional_info tags].
var classifiedTags = allTags[tagsSource];
// Skip empty arrays without tags.
if (!classifiedTags.length) continue;
var query = '';
for (var i in classifiedTags) {
// Get the Tag data (a TagEntity instance).
var tagEntity = classifiedTags[i];
// Add the name and sys_id of the tag for debugging purposes.
var tagDebuggingComment = '\n// Tag enrichment step: "<step>" | From the tag named: "' + tagEntity.name + '", with sys_id: ' + tagEntity.sys_id + ' from domain: ' + tagEntity.sys_domain_display_value + ' (sys_domain: ' + tagEntity.sys_domain + ') and overrides: ' + tagEntity.sys_overrides_display_value + ' (sys_overrides: ' + tagEntity.sys_overrides + ')' + '\n';
// Before query
if (tagEntity.getBeforeQuery()) {
enrich.beforeQuery += tagDebuggingComment.replace('<step>', 'Before Query');
enrich.beforeQuery += tagEntity.getBeforeQuery();
}
// Query
if (tagEntity.getQuery()) {
query += tagDebuggingComment.replace('<step>', 'Query');
query += tagEntity.getQuery();
}
// After query
if (tagEntity.getAfterQuery()) {
enrich.afterQuery += tagDebuggingComment.replace('<step>', 'After Query');
enrich.afterQuery += (tagEntity.getAfterQuery() || '');
}
// Loop start
if (tagEntity.getLoopStart()) {
enrich.loopStart += tagDebuggingComment.replace('<step>', 'Loop Start');
enrich.loopStart += tagEntity.getLoopStart();
}
// Comparison
if (tagEntity.getComparison()) {
// tag enrichment debugging comment should be implemented in getComparison() according to the Tag's match method, field/key, etc.
// AND (&&) operator is the only supported operator at the moment.
enrich.comparison += tagEntity.getComparison() + ' && \n\n';
}
}
if (query) {
// Wrap queries in gs.getProperty that will allow to skip them when their value is false.
// Properties are:
// evt_mgmt.sn_em_tbac.use_additional_info_tags_in_query
// evt_mgmt.sn_em_tbac.use_cmdb_ci_tags_in_alerts_query
// evt_mgmt.sn_em_tbac.use_em_alert_tags_in_query
// evt_mgmt.sn_em_tbac.use_alert_tags_tags_in_query
enrich.query +=
"// Tags from the " + tagsSource + " source (source = " + tagsSource + ") \n\
if (gs.getProperty('evt_mgmt.sn_em_tbac.use_" + tagsSource + "_tags_in_query', 'true') === 'true') { \n\
" + query + "\
} \n\n";
}
}
if (enrich.comparison.lastIndexOf('&&')) {
// Remove the last && string that was appended.
enrich.comparison = enrich.comparison.substring(0, enrich.comparison.lastIndexOf('&&'));
}
// Escape characters that may break the generated ACR script, see the attached image in DEF0229745 for example.
definitionName = this.tbacUtils.stripAndEscapeChars(definitionName);
// This is the final ACR script. Initialized with base script and being enriched with data from tags.
var acrScript = "\
/** \n\
* IMPORTANT NOTICE: \n\
* This script is AUTO-GENERATED from Tag Based Alert Clustering Definition: '" + definitionName + "' with sys_id: " + definitionSysId + " from domain " + definitionDomainName + " sys_domain: " + definitionDomainId + " and overrides: " + domainOverridesName + " sys_overrides: " + domainOverridesId + "\n\
* /sn_em_tbac_alert_clustering_definitions.do?sys_id=" + definitionSysId + " \n\
* \n\
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! \n\
* !!! ANY MANUAL MODIFICATION TO THIS SCRIPT MAY BE OVERRIDDEN !!! \n\
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! \n\
*/ \n\
(function findCorrelatedAlerts(currentAlert){ \n\
try {\n\
if (!currentAlert || !(currentAlert instanceof GlideRecord)) {\n\
throw('currentAlert argument is missing or invalid');\n\
}\n\
if (!currentAlert.getValue('initial_remote_time')) {\n\
throw('currentAlert with sys_id:' + currentAlert.getUniqueValue() + ' and number ' + currentAlert.getValue('number') + ' does not have a value for its \"initial_remote_time\" field.');\n\
}\n\
var t = {\n\
start: Date.now() // Measure milliseconds (ms) at the beginning of the script. \n\
};\n\n\
var acrUtils;\n\
if (typeof(EvtMgmtAlertCorrelationRulesUtils) != 'undefined') {\n\
acrUtils = new EvtMgmtAlertCorrelationRulesUtils();\n\
}\n\n\
var tagBasedComparators = new sn_em_tbac.EvtMgmtTagBasedComparators();\n";
if (tagsData.hasCMDBKeyTags()) {
acrScript += "// There are CMDB Key tags, so we must get the CMDB tags for this alert.\n\
var tbacCmdbKeyUtils = new sn_em_tbac.EvtMgmtTagBasedCmdbKeyUtils();\n\
var currentCiKeyValues = tbacCmdbKeyUtils.getCiCmdbKeyValues(currentAlert.getValue('cmdb_ci'));\n\n";
}
acrScript += "// Check whether the property of case sensitivity is set. It's true by default, which means the filter of the alert is case sensitive.\n\
var isCorrelationCaseSensitive = gs.getProperty('sa_analytics.correlation_case_sensitive', 'true');\n\n\
var alertManager = new SNC.AlertManager(); \n\n\
// acrScriptDebug collects debug information that will be logged if the value of the property '" + this.tbacUtils.LOG_ACR_DEBUG_PROPERTY + "' is set to true. \n\
var acrScriptDebug = '\"sa_analytics.correlation_case_sensitive\" property is set to: ' + isCorrelationCaseSensitive + '\\n\\n';\n\
acrScriptDebug += 'Alert correlation rule debug information for definition [code]<a href=\"sn_em_tbac_alert_clustering_definitions.do?sys_id=" + definitionSysId + "\" target=\"_blank\">" + definitionName + " (sys_id: " + definitionSysId + ")</a>[/code] from domain " + definitionDomainName + " (sys_domain: " + definitionDomainId + "):\\n';\n\
acrScriptDebug += 'Clustering timeframe is: " + timeDifference + " minutes (" + timeDifference * 1000 * 60 + " ms).\\n';\n\
acrScriptDebug += '\\nExamined (created or reopened) alert: [code]<a href=\"em_alert.do?sys_id=' + currentAlert.getUniqueValue() + '\" target=\"_blank\">' + currentAlert.getValue('number') + ', sys_id: ' + currentAlert.getUniqueValue() + '</a>[/code].\\n';\n\n\
// FROM DEFINITION CONFIG - Clustering timeframe: " + timeDifference + " minutes (" + timeDifference * 1000 * 60 + " ms). \n\
var alertsTimeDiff = new GlideDateTime(currentAlert.getValue('initial_remote_time')); \n\
alertsTimeDiff.subtract(" + timeDifference * 1000 * 60 + "); \n\n\
alertManager.addStep('Alert correlation rule advanced script - Before alerts query'); // Add slow steps before query \n\n\
" + enrich.beforeQuery + "\n\n\
// Query alerts to compare against. \n\
var gr = new GlideRecord('em_alert'); \n\
gr.addNotNullQuery('initial_remote_time'); \n\
gr.addQuery('initial_remote_time', '>=', alertsTimeDiff.getValue());\n\
gr.addQuery('sys_id', '!=', currentAlert.getUniqueValue());\n\
// FROM DEFINITION CONFIG - Encoded alert filter: " + alertFilter + ". \n\
gr.addEncodedQuery('" + alertFilter + "'); \n\n\
/* ADDITIONAL QUERIES FROM TAGS */ \n\
" + enrich.query + "\n\n\
// If virtual alerts feature is not active then use the legacy queries. \n\
if (!acrUtils || !acrUtils.isVirtualAlertFeatureActive()){\n\
acrScriptDebug += '\\nVirtual alerts for ACR feature is INACTIVE.\\n\\n';\n\
gr.addQuery('state', 'IN', 'Open,Reopen'); \n\
gr.addQuery('correlation_rule_group', 'IN', '0,1'); // 0 = None (potential parent) | 1 = Primary alert (parent) | 2 = Secondary \n\
gr.addQuery('group_source', 'IN', '2,6'); // 2 = Rules-Based, 6 = None \n\
}\n\
// Else, if virtual alerts feature is active, then query the child alerts of relevant primary alerts. \n\
else {\n\
acrScriptDebug += '\\nVirtual alerts for ACR feature is ACTIVE.\\n\\n';\n\
// 1. Init a shared query to be used for ungrouped alerts query and parent alerts query.\n\
var sharedEncodedQuery = gr.getEncodedQuery();\n\
\n\
// 2. Find OPEN or REOPEN ungrouped alerts\n\
var ungroupedAlertsGr = new GlideRecord('em_alert');\n\
ungroupedAlertsGr.addQuery('state', 'IN', 'Open,Reopen');\n\
ungroupedAlertsGr.addQuery('correlation_group', '0'); // 0 = None (potential parent)\n\
ungroupedAlertsGr.addEncodedQuery(sharedEncodedQuery); // Add the shared encoded query to the ungrouped alerts query.\n\
// Don't query ungroupedAlertsGr on purpose, we'd like to extract the encoded query and use it later\n\
acrScriptDebug += '\\nThe encoded query to retrieve ungrouped alerts is: \\n ' + ungroupedAlertsGr.getEncodedQuery() + '\\n\\n';\n\
\n\
// 3. Find virtual alerts to group to - but we can't query them directly because values in their fields change,\n\
// so tags comparison may fail for them.\n\
// Instead, find a representative secondary alert (childs) in their groups and compare the tags to it.\n\
var childGr;\n\
if (gs.getProperty('evt_mgmt.update_agg_group_record_with_main_alert', 'true') == 'true') {\n\
// If the 'evt_mgmt.update_agg_group_record_with_main_alert' property is enabled, it means \n\
// that the group representative (main alert) can be queried directly and to be used to compare \n\
// against the tags, but they need to be fetched first from em_agg_group \n\
var creationTimeToQueryGroupsInMinutesProperty = gs.getProperty('evt_mgmt.acr_groups_creation_time_to_query_from_in_minutes', '1440'); // 1440 minutes (one day) by default\n\
var creationTimeToQueryGroupsInMs = parseInt(creationTimeToQueryGroupsInMinutesProperty, 10) * 60 * 1000; // In milliseconds\n\
var creationTimeToQueryGroupsGdt = new GlideDateTime();\n\
creationTimeToQueryGroupsGdt.subtract(creationTimeToQueryGroupsInMs);\n\
// Get Tag Cluster groups that were created X time ago, depending on the property value\n\
var aggGroupGr = new GlideRecord('em_agg_group');\n\
aggGroupGr.addQuery('group_status', '1'); // Active \n\
aggGroupGr.addQuery('source_rule', '" + acrSysId + "');\n\
aggGroupGr.addQuery('source', '11'); // Tag Cluster groups\n\
aggGroupGr.addNotNullQuery('main_alert_id'); // Main alerts are defined\n\
aggGroupGr.addQuery('sys_created_on', '>=', creationTimeToQueryGroupsGdt.getValue());\n\
aggGroupGr.orderByDesc('sys_created_on'); // Latest first\n\
acrScriptDebug += '\\nThe em_agg_group query to retrieve main alerts of tag-cluster groups is: \\n ' + aggGroupGr.getEncodedQuery() + '\\n\\n';\n\
aggGroupGr.query();\n\
var mainAlertsIds = [];\n\
while (aggGroupGr.next()) {\n\
var mainAlertId = aggGroupGr.getValue('main_alert_id');\n\
mainAlertsIds.push(mainAlertId);\n\
}\n\
if (mainAlertsIds.length) {\n\
childGr = new GlideRecord('em_alert');\n\
childGr.addEncodedQuery(sharedEncodedQuery); // Add the shared encoded query to the child alerts query.\n\
childGr.addQuery('sys_id', 'IN', mainAlertsIds.join(','));\n\
// Don't query childGr on purpose, we'd like to extract the encoded query and use it later\n\
}\n\
} else {\n\
// When the 'evt_mgmt.update_agg_group_record_with_main_alert' property is disabled\n\
// then we must get a representative in a different way - aggregate groups and get a single alert from them.\n\
// GR for only OPEN parent alerts, we DON'T want to consider reopen parent alerts\n\
// Use GlideAggregate instead of GlideRecord for efficiency, and narrow results to a single alert within each group.\n\
gs.error('" + this.tbacUtils.logsPrefix + " - \"" + this.ACR_NAME_PREFIX + definitionName + "\" ERROR: Virtual alerts for correlation rules is ACTIVE but the property \"evt_mgmt.update_agg_group_record_with_main_alert\" is DISABLED. Grouping will not be done as expected - a workaround with GlideAggregate is needed.'); \n\
}\n\
acrScriptDebug += '\\nThe encoded query to retrieve child alerts is: \\n ' + childGr.getEncodedQuery() + '\\n\\n';\n\
// Init GR to use the encoded queries we have constructed, with ^NQ (NEW QUERY) relation.\n\
// This ^NQ relation is not achievable with a single GlideRecord without encoded queries as it doesn't have this method.\n\
gr = new GlideRecord('em_alert');\n\
if (childGr) {\n\
gr.addEncodedQuery(ungroupedAlertsGr.getEncodedQuery() + '^NQ' + childGr.getEncodedQuery());\n\
} else {\n\
gr.addEncodedQuery(ungroupedAlertsGr.getEncodedQuery());\n\
}\n\
}\n\n\
// FROM DEFINITION CONFIG - Maximum number alerts to compare against is: " + comparedAlertsLimit + " alerts. \n\
gr.setLimit(" + comparedAlertsLimit + "); \n\n\
gr.orderBy('initial_remote_time'); \n\
acrScriptDebug += '\\nThe encoded query to retrieve alerts to work on is: \\n ' + gr.getEncodedQuery() + '\\n\\n';\n\
gr.query(); \n\n\
t.queryEnd = Date.now(); // Measure milliseconds (ms) after querying compared alerts.\n\n\
acrScriptDebug += '\\nNumber of queried alerts: ' + gr.getRowCount() + ', the definition comparedAlertsLimit value is: " + comparedAlertsLimit + ".\\n';\n\
if (gr.getRowCount() >= " + comparedAlertsLimit + ") { \n\
acrScriptDebug += 'Queried alerts limit HAS BEEN REACHED.\\n';\n\
gs.warning('" + this.tbacUtils.logsPrefix + " - \"" + this.ACR_NAME_PREFIX + definitionName + "\" alert correlation rule has reached the limit of alerts to query: " + comparedAlertsLimit + ".'); \n\
} \n\n\
/* GROUPING LOGIC */\n\
// Initialize primary alert as currentAlert, may be overwritten ahead \n\
var primaryAlert = { \n\
sysId: currentAlert.getUniqueValue(), \n\
initialRemoteTime: currentAlert.getValue('initial_remote_time'), \n\
number: currentAlert.getValue('number'), \n\
}; \n\
var secondaries = []; \n\n";
if (tagsData.hasAdditionalInfoTags()) {
acrScript += "// Some tags include comparisons on additional_info, so extract it from currentAlert. \n";
acrScript += "var currentAddInfo = JSON.parse(currentAlert.getValue('additional_info')); \n";
acrScript += "var grAddInfo = {};\n";
}
if (tagsData.hasAlertTagsTags()) {
acrScript += "// Some tags include comparisons on sn_alert_tags, so extract it from currentAlert. \n";
acrScript += "var currentAlertTags = JSON.parse(currentAlert.getValue('" + alertTagsField + "')); \n";
acrScript += "var grAlertTags = {};\n";
}
acrScript += "\
" + enrich.afterQuery + "\n\n\
alertManager.addStep('Alert correlation rule advanced script - After alerts query, before loop'); // Add slow steps after query, before loop \n\n\
acrScriptDebug += 'List of queried alerts:\\n';\n\n\
// Create a GlideFilter instance to determine whether values need to be compared with case-sensitivity or not.\n\
var letterCaseGlideFilter = new GlideFilter('" + alertFilter + "', 'filterCondition'); \n\
if (isCorrelationCaseSensitive == 'false') {\n\
letterCaseGlideFilter.setCaseSensitive(false);\n\
}\n\n\
while (gr.next()) { \n\
if (!letterCaseGlideFilter.match(gr, true)) { \n\
acrScriptDebug += 'Skipping [code]<a href=\"em_alert.do?sys_id=' + gr.getUniqueValue() + '\" target=\"_blank\">' + gr.getValue('number') + '</a>[/code]';\n\
acrScriptDebug += ' ' + 'because of a case-sensitivity mismatch between the alert\\'s values and the definition filter\\n'; \n\
continue; \n\
} \n\n\
acrScriptDebug += '[code]<a href=\"em_alert.do?sys_id=' + gr.getUniqueValue() + '\" target=\"_blank\">' + gr.getValue('number') + '</a>[/code]';\n";
if (tagsData.hasAdditionalInfoTags()) {
acrScript += "// Some tags include comparisons on additional_info, so extract it from gr. \n";
acrScript += "grAddInfo = JSON.parse(gr.getValue('additional_info')) || {}; \n\n";
}
if (tagsData.hasCMDBKeyTags()) {
acrScript += "// Some tags include comparisons on cmdb_key_value, so extract it from gr.\n";
acrScript += "var otherCiKeyValues = tbacCmdbKeyUtils.getCiCmdbKeyValues(gr.getValue('cmdb_ci'));\n";
}
if (tagsData.hasAlertTagsTags()) {
acrScript += "// Some tags include comparisons on sn_alert_tags, so extract it from gr.\n";
acrScript += "grAlertTags = JSON.parse(gr.getValue('" + alertTagsField + "')) || {}; \n\n";
}
acrScript += "\
" + enrich.loopStart + "\n\n\
// Check if the currentAlert and the compared alert should be grouped \n\
// Compare currentAlert and gr alert values by tags \n\
// Currently, only AND operator between the tags is supported \n\
if (\n\
" + enrich.comparison + "\
\n){ \n\
// Reaching here means that the alerts should be grouped. \n\
// Replace current primary alert if: \n\
// 1. The compared alert initial_remote_time is smaller, \n\
// 2. Or, the initial_remote_time is equal, but the alert Number is smaller, as fallback. \n\
var isEarlier = gr.getValue('initial_remote_time') < primaryAlert.initialRemoteTime; \n\
var isSameTimeButSmallerNumber = (gr.getValue('initial_remote_time') == primaryAlert.initialRemoteTime) && (gr.getValue('number') < primaryAlert.number); \n\
var secondary = { \n\
sysId: gr.getUniqueValue(), \n\
initialRemoteTime: gr.getValue('initial_remote_time'), \n\
number: gr.getValue('number'), \n\
}; \n\
if (isEarlier || isSameTimeButSmallerNumber) { \n\
acrScriptDebug += ' - potentially primary';\n\
secondaries.push(primaryAlert); // Current primary alert should be replaced with compared alert, make it secondary. \n\
primaryAlert = secondary ; \n\
} else { \n\
acrScriptDebug += ' - secondary';\n\
secondaries.push(secondary); \n\
} \n\
} // end if \n\
acrScriptDebug += ','; // Separate between the alerts in the list. \n\
} // end while \n\n\
t.loopEnd = Date.now(); // Measure milliseconds (ms) after looping through compared alerts.\n\n\
alertManager.addStep('Alert correlation rule advanced script - After loop'); // Add slow steps after loop \n\n\
// Remove primary from secondaries, just in case it's there \n\
secondaries = secondaries.filter(function(secondaryAlertSysId) { \n\
return secondaryAlertSysId != primaryAlert.sysId; \n\
}); \n\n\
var group = {};\n\
var secondariesArray = secondaries.map(function(secondary) { return secondary.sysId; });\n\
if (acrUtils && acrUtils.isVirtualAlertFeatureActive()){\n\
group = {\n\
'ALERTS_SYS_IDS': [primaryAlert.sysId].concat(secondariesArray)\n\
};\n\
} else { \n\
// Set group result \n\
group = { \n\
'PRIMARY': [primaryAlert.sysId], \n\
'SECONDARY': secondariesArray \n\
};\n\
} \n\n\
t.end = Date.now(); // Measure milliseconds (ms) at the end of the script.\n\n\
// Log the alert correlation rule details if the debugging property is set to true.\n\
if(gs.getProperty('" + this.tbacUtils.LOG_ACR_DEBUG_PROPERTY + "', 'false') == 'true') {\n\
var readablePrimary = '[code]<a href=\"em_alert.do?sys_id=' + primaryAlert.sysId + '\" target=\"_blank\">' + primaryAlert.number + '</a>[/code]';\n\
var readableSecondaries = secondaries.map(function(secondary) { return '[code]<a href=\"em_alert.do?sys_id=' + secondary.sysId + '\" target=\"_blank\">' + secondary.number + '</a>[/code]'; });\n\
acrScriptDebug += '\\n\\nRESULTS:\\nPrimary alert: ' + readablePrimary;\n\
acrScriptDebug += '\\nSecondary alerts: ' + readableSecondaries.join(',');\n\
acrScriptDebug += '\\n\\nFinal alerts group structure (final returned structure):\\n' + JSON.stringify(group);\n\
acrScriptDebug += '\\n\\nPerformance (run time in ms):\\n';\n\
acrScriptDebug += 'Total runtime: ' + (t.end - t.start) + 'ms.\\n';\n\
acrScriptDebug += 'Compared alerts query time: ' + (t.queryEnd - t.start) + 'ms.\\n';\n\
acrScriptDebug += 'Loop compared alerts time: ' + (t.loopEnd - t.queryEnd) + 'ms.\\n';\n\
acrScriptDebug += '\\n\\In order to stop these debug messages - change the property \"" + this.tbacUtils.LOG_ACR_DEBUG_PROPERTY + "\" to \"false\".\\n';\n\
gs.info('" + this.tbacUtils.logsPrefix + "\\n' + acrScriptDebug);\n\
}\n\n\
//The alerts will be grouped under a virtual alert, and that the primary alert will be set dynamically to the alert of the highest severity.\n\
return JSON.stringify(group); \n\
} catch (e) {\n\
var evtMgmtCommons = new EvtMgmtCommons();\n\
gs.error('" + this.tbacUtils.logsPrefix + " - \"" + this.ACR_NAME_PREFIX + definitionName + "\" An exception was thrown in alert correlation rule with sys_id: " + acrSysId + " for alert with sys_id: ' + currentAlert.getUniqueValue() + '. Exception: ' + evtMgmtCommons.getExceptionMessage(e, true)); \n\
// Returns an empty object to continue to next rules\n\
var group = { \n\
'PRIMARY': [], \n\
'SECONDARY': [], \n\
'ALERTS_SYS_IDS': [] \n\
};\n\
return JSON.stringify(group);\n\
}\n\
})(currentAlert); \n\
";
return acrScript;
},
};
Sys ID
21c4a01db76830107c038229ce11a910