Name
sn_agent.AgentDiscoveryClassification
Description
No description available
Script
var AgentDiscoveryClassification = Class.create();
AgentDiscoveryClassification.prototype = {
initialize: function(iType) {
this.DISCOVERY_CLASSY = 'discovery_classy';
this.setType(iType);
},
setType: function(iType) {
this.type = this.DISCOVERY_CLASSY;
if (iType)
this.type += '_' + iType;
},
setFullType: function(fullType) {
this.type = fullType;
},
loadClassification: function() {
var grClassy = new GlideRecord(this.type);
// Falling back to discovery_classy in a case we couldn't find the table resulted in unexpected behavior
if (!grClassy.isValid())
throw "Cannot use discovery classifiers, " + this.type + " table do not exist";
grClassy.addActiveQuery();
grClassy.orderBy('order');
grClassy.query();
return grClassy;
},
findCiType: function(map){
try {
var sdc = new global.ScopedDiscoveryClassification();
var rtn = sdc.findCiType(this.type, map);
// Hyper-V Server should have 2 conditions at-least
// If there is only 1 condition it is: name, does not contain, without Hyper-V - which is too generic and matches non-Hyper-V servers (like Windows 2012)
// This is present in Q patch 0 and patch 1.
// We moved the discovery_class_criteria_0d797153c313200031e65ad8cbba8fc3 which adds the condition: name, contains, Hyper V - in Q patch 2
// Should be able to remove lines 86-87 onces discovery_class_criteria_0d797153c313200031e65ad8cbba8fc3 there are no customer on QP2.
// This check simply adds the condition: name, contains, Hyper V in an hard coded fashion, until we can remove it.
// Just coping the discovery_class_criteria_0d797153c313200031e65ad8cbba8fc3 record to the scoped app cause that record to be own by ACC-F
if (rtn != 'cmdb_ci_hyper_v_server' || !this.shouldSkipHyperV(map.name))
return rtn;
// As we need to skip Hyper-V, we will evalute the classification in JavaScript with an additional condition
gs.warn("AgentDiscoveryClassification: Cannot use Hyper-V Classification, a critical missing condition is missing. Will try again");
var grClassy = this.find(map);
if (grClassy)
return grClassy.getValue('table');
} catch (e) {
gs.warn("AgentDiscoveryClassification: Could not find global.ScopedDiscoveryClassification() wrapper, using internal JS. Original exception = " + e);
try {
// Could not use wrapper - use internal JS
var gr = this.find(map);
if (gr)
return gr.getValue('table');
} catch (e) {
gs.error("AgentDiscoveryClassification: Could not classify. Original exception = " + e);
return "";
}
}
return "";
},
find : function(map) {
var grClassy = this.loadClassification();
if (grClassy.getRowCount() < 1)
return "";
// Search for the best match
while (grClassy.next()) {
var classySysId = grClassy.getValue('sys_id');
var isAllMatched = ("All" == grClassy.getValue('match_criteria'));
var classyConditions = this.getConditions(classySysId);
// Hyper-V Server should have 2 conditions at-least
// If there is only 1 condition it is: name, does not contain, without Hyper-V - which is too generic and matches non-Hyper-V servers (like Windows 2012)
// This is present in Q patch 0 and patch 1.
// We moved the discovery_class_criteria_0d797153c313200031e65ad8cbba8fc3 which adds the condition: name, contains, Hyper V - in Q patch 2
// Should be able to remove lines 86-87 onces discovery_class_criteria_0d797153c313200031e65ad8cbba8fc3 there are no customer on QP2.
// This check simply adds the condition: name, contains, Hyper V in an hard coded fashion, until we can remove it.
// Just coping the discovery_class_criteria_0d797153c313200031e65ad8cbba8fc3 record to the scoped app cause that record to be own by ACC-F
if (classySysId == 'c759f913c313200031e65ad8cbba8fd7' && this.shouldSkipHyperV(map.name))
continue;
if (this.matched(classyConditions, isAllMatched, map)) {
return grClassy;
}
}
return "";
},
shouldSkipHyperV: function(osName) {
// First evaluate the name contains "Hyper-V" then the condition to not contain "without Hyper-V" will be evluated in the classifier
if (osName && osName.indexOf('Hyper-V') > -1)
return false;
return true;
},
matched: function (conditions, isAll, map){
var numMatches = 0;
var processed = 0 ;
for (var i=0; i < conditions.length; i++) {
processed++;
var op = conditions[i].operator;
var name = conditions[i].name;
var dbValue = conditions[i].value;
var inputValue = map[name];
// We do want to have empty values...
if (inputValue == null || inputValue == undefined)
continue;
switch(op) {
case 'equals':
if (inputValue == dbValue)
numMatches++;
break;
case 'does not equals':
if (inputValue != dbValue)
numMatches++;
break;
case 'contains':
if (inputValue.indexOf(dbValue) > -1 )
numMatches++;
break;
case 'does not contain':
if (inputValue.indexOf(dbValue) < 0 )
numMatches++;
break;
case 'startsWith':
if (inputValue.startsWith(dbValue))
numMatches++;
break;
case 'does not start with':
if (!inputValue.startsWith(dbValue))
numMatches++;
break;
case 'endsWith':
if (inputValue.endsWith(dbValue))
numMatches++;
break;
case 'does not end with':
if (!inputValue.endsWith(dbValue))
numMatches++;
break;
case 'regex matches':
dbValue = this.replaceToDoAll(dbValue);
var regex = new RegExp(dbValue, "gm"); // global, multiline, dotall
if (inputValue.match(regex))
numMatches++;
break;
case 'regex does not match':
dbValue = this.replaceToDoAll(dbValue);
var regex2 = new RegExp(dbValue, "gm"); // global, multiline, dotall
if (inputValue.match(regex2))
numMatches++;
break;
case 'in IPs':
if (this.inIps(inputValue, dbValue))
numMatches++;
break;
default:
// Nothing here
}
if (!isAll && numMatches > 0)
return true;
}
if (processed > 0 && processed == numMatches)
return true;
return false;
},
getConditions: function(classySysId) {
var grClassyCriteria = new GlideRecord('discovery_class_criteria');
grClassyCriteria.addQuery('classy', classySysId);
grClassyCriteria.addActiveQuery();
grClassyCriteria.query();
var conditions = [];
while (grClassyCriteria.next()) {
var condition = {};
condition.name = grClassyCriteria.getValue('name');
condition.operator = grClassyCriteria.getValue('operator');
condition.value = grClassyCriteria.getValue('value');
conditions.push(condition);
}
return conditions;
},
/*
* JS does not have dot ALL
* Workaround: convert . to [\s\S] - not a space + is a space (including new lines)
* We need to preserve \.
*/
replaceToDoAll: function(str){
if (str.indexOf('.') > -1){
str = str.replaceAll('.', '[\\s\\S]'); // dot all in JS
str = str.replaceAll('\\[\\s\\S]', '\\.'); // return the original \.
}
return str;
},
/*
This method supports the following input:
ip: a string representaion of an IPv4 IP Address, i.e.: a.b.c.d
ipColl: a string represenation of IP Collection in the following structure (in parentheses = structure as is supported today)
1) single IP: 1.2.3.4 (IPNetworkV4.getIPNetworkInstance)
2) CIDR: 1.2.3.4/16 (IPNetworkV4.getIPNetworkInstance)
3) Netowrk and Mask: 1.2.3.4 / 255.254.0.0 (IPNetworkV4.getIPNetworkInstance)
4) Hexdecimal Netowrk and mask: 0x01010101/FF000000 (IPNetworkV4.getIPNetworkInstance)
5) IP Range: a.b.c.d-e.f.g.h (IPRangeV4.getIPRangeV4Instance)
6) Comma separated list of IPs: a.b.c.d[,e.f.g.h]... (IPList.getIPListInstance)
*/
inIps: function (ip, ipColl){
var arr;
if (ipColl.indexOf('/') > -1) {
arr = ipColl.split('/');
if (!arr[0] || !arr[1])
return false;
// Assume this is a network mask
var netmask = arr[1].trim();
if (arr[1].indexOf('.') < 0) {
// This is CIDR
netmask = global.NetworkDiscoveryUtil.netmaskFromCidrBitCount(netmask);
}
var ipAsInt = global.NetworkDiscoveryUtil.stringIpToInt(ip);
var networkAsInt;
var netmaskAsInt;
if (ipColl.startsWith('0x')){
// This is if the netowrk and mask are coded in hexdecimal fashion just like in IPNetworkV4.getIPNetworkInstance
// This should NOT be a common case
networkAsInt = parseInt(Number(arr[0]), 10);
netmaskAsInt = parseInt(Number('0x' + arr[1]), 10); // needs to add 0x
} else {
networkAsInt = global.NetworkDiscoveryUtil.stringIpToInt(arr[0].trim());
netmaskAsInt = global.NetworkDiscoveryUtil.stringIpToInt(netmask);
}
return (global.NetworkDiscoveryUtil.doesIpContainedInNetwork(ipAsInt, netmaskAsInt, networkAsInt));
}
else if (ipColl.indexOf('-') > -1){
// This is a range
arr = ipColl.split('-');
if (!arr[0] || !arr[1])
return false;
var startRange = arr[0].trim();
var endRange = arr[1].trim();
var intIp = global.NetworkDiscoveryUtil.stringIpToInt(ip);
var intStartRange = global.NetworkDiscoveryUtil.stringIpToInt(startRange);
var intEndRange = global.NetworkDiscoveryUtil.stringIpToInt(endRange);
// If ip is between the ranges - then true
if (intStartRange < intIp && intIp < intEndRange)
return true;
} else {
// This is a list of IPs or a single ip
// split and trim
var ips = ipColl.split(",").map(function(item) {
return item.trim();
});
if (ips.indexOf(ip) > -1)
return true;
}
return false;
},
loadWindowsClassificationMap: function(checkValues, clientIP) {
var map = {};
if (!checkValues.osInfo)
return map;
var osName = checkValues.osInfo.caption;
// Windows computer must starts with "Windows", for Windows 10 we remove Microsoft as prefix
if (osName && osName.startsWith('Microsoft'))
osName = osName.replaceAll('Microsoft', '').trim();
map.isVIP = 'false'; // For Discovery Calssification this is mandatory
map.name = osName;
map.osFullName = checkValues.osInfo.name;
var keys = Object.keys(checkValues.osInfo);
map.version = checkValues.osInfo.version;
map.buildNumber = checkValues.osInfo.buildNumber;
map.osArchitecture = checkValues.osInfo.osArchitecture;
map.osLanguage = checkValues.osInfo.osLanguage;
return map;
},
type: 'AgentDiscoveryClassification'
};
Sys ID
663f003affb820108ec45897d53bf1cf