hostMinLong = ip2long(hostMin)
hostMaxLong = ip2long(hostMax)
System.debug("hostMin = "+hostMin+" ("+hostMinLong+")")
System.debug("hostMax = "+hostMax+" ("+hostMaxLong+")")
// Verification que les scopes sont bien dans le cidr
System.debug("check ipPools In CIDR: ")
for each(ipPool in ipPools){
result = chkRangeInScope(ipPool,hostMin,hostMax,true) // true: affiche debug
if(result != "succeed"){ // Failed (string)
System.error(result)
throw result // Si le pool n'est pas dans le cidr, pas la peine de regarder le reste
}
}
System.log(ipPools.length+" ipPools: "+JSON.stringify(ipPools).replace(/."ipBegin"/g,"\n{ipBegin\""))
// reduction overlapp des ipPools
reducedIpPools = reduceOverlapp(ipPools) ; System.debug("reducedIpPools = "+reducedIpPools.join(", "))
// Transformation des ipPools en un json de busyRanges optimisés (version ipLong)
forbiddenLongRangesPools = translateIntervalInRange(hostMinLong,hostMaxLong,reducedIpPools)
System.debug("forbiddenLongRangesPools = "+JSON.stringify(forbiddenLongRangesPools))
System.debug("Pool nbAvailableIP: "+(numberOfHost - nbAvailableIP))
// Nouveau json, reflet du précédent, mais avec des IP au lieu des ipLong
// Pour qu'il soit au même format que les autres ranges d'exclusions
forbiddenRangesPool = []
for each (ipLong in forbiddenLongRangesPools){
newLine = new Properties
newLine.ipBegin = long2ip(ipLong.ipBegin) ; newLine.ipEnd = long2ip(ipLong.ipEnd)
forbiddenRangesPool.push(newLine)
}
System.debug("forbiddenRangesPool = "+JSON.stringify(forbiddenRangesPool))
// Verif listOfBusyRange are all in cidr
if(!listOfBusyRange){ listOfBusyRange = [] }
System.log("listOfBusyRange is "+listOfBusyRange.length+" long")
System.debug("listOfBusyRange:"+JSON.stringify(listOfBusyRange).replace(/."ipBegin"/g,"\n{ipBegin\""))
// verif, bien dans le cidr
for each(range in listOfBusyRange){
result = chkRangeInScope(range,hostMin,hostMax,true) // false: quiet: no debug
if(result != "succeed"){ // Failed (string)
System.error("listOfBusyRange check :"+result)
Errors.push("listOfBusyRange check :"+result)
}
}
otherExclusionRanges = []
// transformation de "listOfBusyIPs" en busyRanges: Array Of {"ipBegin":"xx.xx.xx.xx","ipEnd":"xx.xx.xx.xx"}
if(!listOfBusyIPs){ listOfBusyIPs = [] }
System.log("listOfBusyIPs = ["+listOfBusyIPs.join(", ")+"]")
for each (ip in listOfBusyIPs){
newLine = new Properties
newLine.ipBegin = ip ; newLine.ipEnd = ip
otherExclusionRanges.push(newLine)
}
Errors = []
// Verification de tous les busyRanges dans le cidr
for each(range in otherExclusionRanges){
result = chkRangeInScope(range,hostMin,hostMax,true) // false: quiet: no debug
if(result != "succeed"){ // Failed (string)
System.error("listOfBusyIPs check :"+result)
Errors.push("listOfBusyIPs check :"+result)
}
}
// transformation de "frozzenIPs" en busyRanges: Array Of {"ipBegin":"xx.xx.xx.xx","ipEnd":"xx.xx.xx.xx"}
frozzenIPsRanges = []
if(!frozzenIPs){ frozzenIPs = [] }
System.log("frozzenIPs = ["+frozzenIPs.join(", ")+"]")
for each (ip in frozzenIPs){
newLine = new Properties
newLine.ipBegin = ip ; newLine.ipEnd = ip
frozzenIPsRanges.push(newLine)
}
// Verification des "frozzenIPsRanges" dans le cidr
for each(range in frozzenIPsRanges){
result = chkRangeInScope(range,hostMin,hostMax,true) // false: quiet: no debug
if(result != "succeed"){ // Failed (string)
System.error("frozzenIPs check: "+result)
Errors.push("frozzenIPs check: "+result)
}
}
// Générer erreur si il y en a
if(Errors.length > 0){
throw Errors.join(",")// inutile d'aller plus loin si les vérifications précédentes n'ont pas abouties
}else{
System.log("All inputs checked OK")
}
// constitution de allExclusionRanges
allExclusionRanges = forbiddenRangesPool.concat(listOfBusyRange).concat(otherExclusionRanges).concat(frozzenIPsRanges)
System.debug("allExclusionRanges = "+JSON.stringify(allExclusionRanges).replace(/."ipBegin"/g,"\n{ipBegin\""))
// reduce overlapp sur allExclusionRanges:
reducedAllExclusionRanges = reduceOverlapp(allExclusionRanges)
System.debug("reducedAllExclusionRanges = "+reducedAllExclusionRanges.join(", "))
// Transformation de reducedAllExclusionRanges en "autorizedRanges" optimisés
autorizedRanges = translateIntervalInRange(hostMinLong,hostMaxLong,reducedAllExclusionRanges)
System.log("nbAvailableIP = "+nbAvailableIP)
// Trouver la première IP disponible dans les ranges constitués
for(I in autorizedRanges){
interval = resultArray[I]
nextFreeIP = long2ip(interval.ipBegin)
System.log("For external net "+externalNetworkName+" scope
"+scopeNumber+", NextFreeIP = "+nextFreeIP+"
("+interval.ipBegin+")")
break
}
if(!nextFreeIP){
message = "No more available IP address in the scope "+scopeNumber+" of "+externalNetworkName
throw message
}
//////////////////////////////////////////////// FONCTIONS /////////////////////////////////////////////
// Fonction Supression overlapp:
function
reduceOverlapp(_ranges){ // _ranges ArrayType:
[{"ipBegin":"10.130.31.100","ipEnd":"10.130.31.105"},{"ipBegin":"10.130.31.107","ipEnd":"10.130.31.120"}]
// 1) Création d'une liste composite clé=(ipBegin|ipEnd) value = ip à partir de "exclusionRanges"
// Ex:
[{"ipBegin":"10.130.31.100"},{"ipEnd":"10.130.31.105"},{"ipBegin":"10.130.31.107"},{"ipEnd":"10.130.31.120"},{"ipBegin":"10.130.31.104"},{"ipEnd":"10.130.31.104"}
etc...
compositLimits = []
for each(LINE in _ranges){
limit = new Properties
limit.ipBegin = LINE.ipBegin
compositLimits.push(limit)
limit = new Properties
limit.ipEnd = LINE.ipEnd
compositLimits.push(limit)
} // ; System.debug("compositLimits\n"+JSON.stringify(compositLimits).replace(/,/g,"\n"))
// 2) Trie de cette liste sur les valeur d'IP sans tenir compte de la clé (ipBegin ou ipEnd)
sortedCompositLimits = compositLimits.sort(sortByIp) ;
System.debug("sortedCompositLimits\n"+JSON.stringify(sortedCompositLimits).replace(/,/g,"\n"))
// 3) Reduction overlap.
// Posons que:
"[" représente "ipBegin" et
que "]" représente "ipEnd".
// Avant reduction:
// [.....[.]....]......[.][....[.[.].]...]
// Après reduction:
// [............]......[.][..............]
reduced = []
count = 0
bascul = true
for each(borne in sortedCompositLimits){
if(borne.ipBegin){ count ++ }//; System.debug("count = "+count) }
if(borne.ipEnd){ count -- }//; System.debug("count = "+count) }
if(count == 1 && bascul){ bascul =
!bascul ; reduced.push(borne) }//; System.debug("push
"+JSON.stringify(borne)) }
if(count == 0 && !bascul){ bascul =
!bascul ; reduced.push(borne) }//; System.debug("push
"+JSON.stringify(borne)) }
}
System.debug("Exclusion RANGES (Après reduction
overlapp):"+JSON.stringify(reduced).replace(/."ipBegin"/g,"\n{ipBegin\""))
//
4) transformation de l'objet "reduced" en une suite de nombres
représentants les IP: [ 1 , 2 , 3 , 4 ] -(Grace à la
fonction ipLong)
IPLongList = []
for each(borne in reduced){
key = Object.keys(borne)[0]
IPLongList.push(ip2long(borne[key]))
}
//System.debug("Long ranges\n"+JSON.stringify(IPLongList).replace(/,/g,"\n"))
return IPLongList
}
//
Fonction transformant, au ls -lrt
sein d'un un pool délimité par ipLongBegin et
ipLongEnd, des Ranges constitués par une suite d'ipLongs représentant
des intervals se suivants sans se recouvrir,
// en un json definissant des intervals opposés de type {"ipBegin":"10.130.31.100","ipEnd":"10.130.31.105"}
function translateIntervalInRange(ipLongBegin,ipLongEnd,ArrayOfIpLong){
// Partant de "ArrayOfIpLong" Exemple [1 , 2 , 3 , 4] On y ajoute le "ipLongBegin" 0 au debut, et le "ipLongEnd" 5 à la fin:
// Résultat: [0 , 1 , 2 , 3 , 4 , 5]
ArrayOfIpLong.unshift(ipLongBegin)
ArrayOfIpLong.push(ipLongEnd) //System.debug("Long ranges in
scope\n"+JSON.stringify(ArrayOfIpLong).replace(/,/g,"\n"))
// Ensuite, notre tableau [0 , 1 , 2 , 3 , 4 , 5] ( soit [0[1-2][3-4]5] ) va donner [0 , 0 , 5 , 5] ( soit [0-0][5-5] )
// c'est à dire: 2 IP disponibles: 0 et 5
// Mais la fonction est reversibe et [0 , 0 , 5 , 5] va donner [0
, 1 , 4 , 5] soit [0[1-4]5] une forme optimisé de [0[1-2][3-4]5]
nbAvailableIP = 0
bascul = false
resultArray = []
line = new Properties
for (I = 0 ; I < ArrayOfIpLong.length ; I++){
bascul = (!bascul)
value = ArrayOfIpLong[I]
if(I == 0){ // Si indice = 0, c'est l'ip de debut du pool
line.ipBegin = value // 1er ip de debut du premier interval autorisé
}else{
if(I == ArrayOfIpLong.length - 1){ // Si c'est l'IP de fin du dernier interval interdit
line.ipEnd = value // L'IP suivante sera l'IP de fin du dernier interval autorisé (La dernière), celle de la fin du pool
if(line.ipBegin <= line.ipEnd){ // Si il y a plus d'une IP dans ce dernier interval
nbAvailableIP += (line.ipEnd - line.ipBegin +
1) // MAJ du Compteur d'IP
System.debug("Add oposit range
["+long2ip(line.ipBegin)+" - "+long2ip(line.ipEnd)+"]") // debug
resultArray.push(line) ; line = new Properties
// ajout de ce dernier intervall dans le json
}
}else{ // il s'agit de l'IP de debut ou de fin d'un des interval interdit
if(bascul){ // Ici c'est l'ip de fin d'un interval interdit
line.ipBegin = value + 1 // Donc l'ip suivante est l'ip de debut d'un interval autorisé
}else{ // Ici c'est l'ip de debut d'un interval interdit
line.ipEnd = value - 1 // Donc l'ip précédente est l'ip de fin d'un interval autorisé
if(line.ipBegin <= line.ipEnd){ // Si il y a plus d'une IP dans ce dernier interval
nbAvailableIP +=
(line.ipEnd - line.ipBegin + 1) // MAJ du Compteur d'IP
System.debug("Add oposit
range ["+long2ip(line.ipBegin)+" - "+long2ip(line.ipEnd)+"]") // debug
resultArray.push(line) ;
line = new Properties // ajout ce dernier intervall dans le json
}
}
}
}
}
//System.warn("nbAvailableIP = "+nbAvailableIP)
System.debug("translated = "+JSON.stringify(resultArray))
return resultArray
}
function sortByIp(a,b){
//diff = ip2long(a.ipBegin) - ip2long(b.ipBegin)
//System.debug("diff = "+diff)
A = a.ipBegin ; if(!A){ A = a.ipEnd }
B = b.ipBegin ; if(!B){ B = b.ipEnd }
diff = ip2long(A) - ip2long(B)
if(diff > 0){ return 1 }
if(diff < 0){ return -1 }
if(diff == 0){ return 0 }
}
// V&rifie la validité d'un range dans un autre range (_scope est un composite)
function chkRangeInScope(_scope,_hostMin,_hostMax,debug){
errListLimits = [] // sortie en failed
// verif interval positif
if(ip2long(_scope.ipBegin) > ip2long(_scope.ipEnd)){
errListLimits.push("ipBegin
("+_scope.ipBegin+") must be lower than ipEnd ("+_scope.ipEnd+")")
}
// verif address de debut dans l'intervalle
result = chkIpforLimits(_scope.ipBegin,_hostMin,_hostMax)
if(result != "succeed"){ // Failed
errListLimits.push(result)
}
// verif address de fin dans l'intervalle
result = chkIpforLimits(_scope.ipEnd,_hostMin,_hostMax)
if(result != "succeed"){ // Failed
errListLimits.push(result)
}
// L'heure du bilan:
if(errListLimits.length > 0){
System.warn("chkRangeInScope(): failed")
return errListLimits.join(", ") //Failed: return error (string)
} else {
if(debug){ System.debug("chkRangeInScope():
["+_scope.ipBegin+" - "+_scope.ipEnd+"] is included in ["+_hostMin+" -
"+_hostMax+"]") }
return "succeed"
}
}
// Vérifie qu'une ip est bien entre deux ipMin et ipMax
function chkIpforLimits(ip,ipMin,ipMax){
ER = [] ; var myURL = new URL()
if(!myURL.isValidIPv4Address(ip)){
return "ip \""+ip+"\": Invalid IPv4 addr"
}else{
ipLong = ip2long(ip) ; ipMinLong = ip2long(ipMin) ; ipMaxLong = ip2long(ipMax)
//System.debug(ip+"="+ipLong+" "+ipMin+" = "+ipMinLong+" "+ipMax+" = "+ipMaxLong+" "+typeof(ipLong))
if(ipLong < ipMinLong){
ER.push("ipBegin "+ip+" must be equal or higher than "+ipMin)
}
if(ipLong > ipMaxLong){
ER.push("ipEnd "+ip+" must be equal or lower than "+ipMax)
}
if(ER.length > 0){ // Failed
System.error(ER.join(", "))
return ER.join(", ")
} else { // Success
return "succeed"
}
}
}
function long2ip(l) {
with (Math) {
var ip1 = floor(l/pow(256,3));
var ip2 = floor((l%pow(256,3))/pow(256,2));
var ip3 = floor(((l%pow(256,3))%pow(256,2))/pow(256,1));
var ip4 = floor((((l%pow(256,3))%pow(256,2))%pow(256,1))/pow(256,0));
}
return ip1 + '.' + ip2 + '.' + ip3 + '.' + ip4;
}
function ip2long(_ip) {
var ips = _ip.split('.');
var _iplong = 0;
with (Math) {
_iplong = ips[0]*pow(256,3)+ips[1]*pow(256,2)+ips[2]*pow(256,1)+ips[3]*pow(256,0);
}
return _iplong;