<# .SYNOPSIS Converts any kind of Icinga threshold with provided units to the lowest base of the unit which makes sense. It does support the Icinga plugin language, like ~:30, @10:40, 15:30, ... The conversion does currently support the following units: Size: B, KB, MB, GB, TB, PT, KiB, MiB, GiB, TiB, PiB Time: ms, s, m, h, d w, M, y .DESCRIPTION Converts any kind of Icinga threshold with provided units to the lowest base of the unit. It does support the Icinga plugin language, like ~:30, @10:40, 15:30, ... You can also provide date time values in the format of "yyyy/MM/dd HH:mm:ss" and use Icinga for Windows plugin thresholds in combination. You have to escape the ':' inside the date time value with a '`' to ensure the correct conversion. Example: 2024/08/19 12`:42`:00 The conversion does currently support the following units: Size: B, KB, MB, GB, TB, PT, KiB, MiB, GiB, TiB, PiB Time: ms, s, m, h, d w, M, y .FUNCTIONALITY Converts values with units to the lowest unit of this category. Accepts Icinga Thresholds. .PARAMETER Threshold The threshold value to convert. Supports Icinga plugin threshold syntax. .PARAMETER RawValue If set, the function will return the raw value without any conversion. This is required for match/not match checks as example .EXAMPLE PS>Convert-IcingaPluginThresholds -Threshold '20d'; Name Value ---- ----- EndRange Unit s StartRange Threshold 1728000 Mode 0 Raw 20d IsDateTime False Value 1728000 .EXAMPLE PS>Convert-IcingaPluginThresholds -Threshold '5GB'; Name Value ---- ----- EndRange Unit B StartRange Threshold 5000000000 Mode 0 Raw 5GB IsDateTime False Value 5000000000 .EXAMPLE PS>Convert-IcingaPluginThresholds -Threshold '10MB:20MB'; Name Value ---- ----- EndRange 20000000 Unit B StartRange 10000000 Threshold 10000000:20000000 Mode 3 Raw 10MB:20MB IsDateTime False Value .EXAMPLE PS>Convert-IcingaPluginThresholds -Threshold '10m:1h'; Name Value ---- ----- EndRange 3600 Unit s StartRange 600 Threshold 600:3600 Mode 3 Raw 10m:1h Value .EXAMPLE PS>Convert-IcingaPluginThresholds -Threshold '@10m:1h'; Name Value ---- ----- EndRange 3600 Unit s StartRange 600 Threshold @600:3600 Mode 4 Raw @10m:1h IsDateTime False Value .EXAMPLE PS>Convert-IcingaPluginThresholds -Threshold '~:1M'; Name Value ---- ----- EndRange Unit s StartRange Threshold ~:2592000 Mode 2 Raw ~:1M IsDateTime False Value 2592000 .EXAMPLE PS>Convert-IcingaPluginThresholds -Threshold '@2024/08/19 12`:42`:00:2024/08/19 12`:42`:00'; Name Value ---- ----- EndRange 133685377200000000 Unit s StartRange 133685377200000000 Threshold @133685377200000000:133685377200000000 Mode 4 Raw @2024/08/19 12`:42`:00:2024/08/19 12`:42`:00 IsDateTime True Value .INPUTS System.String .OUTPUTS System.Hashtable .LINK https://github.com/Icinga/icinga-powershell-framework #> function Convert-IcingaPluginThresholds() { param ( [string]$Threshold = $null, [switch]$RawValue = $false ); [hashtable]$RetValue = @{ 'Raw' = $Threshold; 'Unit' = ''; 'Threshold' = $null; 'Value' = $null; 'StartRange' = $null; 'EndRange' = $null; 'Mode' = $IcingaEnums.IcingaThresholdMethod.Default; 'IsDateTime' = $FALSE; }; # Always ensure Value is set, because we just want the raw value if ([string]::IsNullOrEmpty($Threshold) -or $RawValue) { $RetValue.Value = $Threshold; return $RetValue; } # Always ensure we are using correct digits $Threshold = $Threshold.Replace(',', '.'); [array]$Content = @(); if ($Threshold.Contains(':')) { # If we have more than one ':' inside our string, lets check if this is a date time value # In case it is convert it properly to a FileTime we can work with later on if ([Regex]::Matches($Threshold, '`:').Count -gt 1) { [bool]$HasTilde = $FALSE; [bool]$HasAt = $FALSE; if ($Threshold.Contains('@')) { $HasAt = $TRUE; } elseif ($Threshold.Contains('~')) { $HasTilde = $TRUE; } $Threshold = $Threshold.Replace('`:', '!').Replace('~', '').Replace('@', ''); [array]$DatimeValueArray = $Threshold.Split(':'); try { [array]$DateTimeValue = @(); if ([string]::IsNullOrEmpty($DatimeValueArray[0]) -eq $FALSE) { [array]$DateTimeValue += ([DateTime]::ParseExact($DatimeValueArray[0].Replace('!', ':'), 'yyyy\/MM\/dd HH:mm:ss', $null)).ToFileTime(); } if ([string]::IsNullOrEmpty($DatimeValueArray[1]) -eq $FALSE) { [array]$DateTimeValue += ([DateTime]::ParseExact($DatimeValueArray[1].Replace('!', ':'), 'yyyy\/MM\/dd HH:mm:ss', $null)).ToFileTime(); } if ($DateTimeValue.Count -gt 1) { $Threshold = [string]::Join(':', $DateTimeValue); $RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Between; if ($HasAt) { $Threshold = [string]::Format('@{0}', $Threshold); } } elseif ($DatimeValueArray.Count -gt 1) { if ($HasTilde) { $Threshold = [string]::Format('~:{0}', $DateTimeValue[0]); } else { $Threshold = [string]::Format('{0}:', $DateTimeValue[0]); } } else { $Threshold = $DateTimeValue[0]; } $RetValue.Unit = 's'; $RetValue.IsDateTime = $TRUE; } catch { $RetValue.Threshold = $Threshold.Replace('!', '`:'); Exit-IcingaThrowException -CustomMessage ([string]::Format('Could not convert the provided threshold value {0} to a valid Icinga for Windows range', $RetValue.Raw)) -ExceptionType 'Input' -ExceptionThrown $IcingaExceptions.Inputs.InvalidThresholdValue -Force; return $RetValue; } } else { $RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Between; } $Content = $Threshold.Split(':'); if ($Content.Count -eq 2 -And ([string]::IsNullOrEmpty($Content[1]))) { $RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Lower; } } else { $Content += $Threshold; } [array]$ConvertedValue = @(); foreach ($ThresholdValue in $Content) { [bool]$HasTilde = $FALSE; [bool]$HasAt = $FALSE; [bool]$Negate = $FALSE; $Value = ''; $WorkUnit = ''; if ($ThresholdValue.Contains('~')) { $ThresholdValue = $ThresholdValue.Replace('~', ''); $HasTilde = $TRUE; $RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Greater; } elseif ($ThresholdValue.Contains('@')) { $HasAt = $TRUE; $ThresholdValue = $ThresholdValue.Replace('@', ''); $RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Outside; } if ($ThresholdValue[0] -eq '-' -And $ThresholdValue.Length -ge 1) { $Negate = $TRUE; $ThresholdValue = $ThresholdValue.Substring(1, $ThresholdValue.Length - 1); } if (($ThresholdValue -Match "(^-?[0-9]+)((\.|\,)[0-9]+)?(B|KB|MB|GB|TB|PT|KiB|MiB|GiB|TiB|PiB)$")) { $WorkUnit = 'B'; if ([string]::IsNullOrEmpty($RetValue.Unit) -eq $FALSE -And $RetValue.Unit -ne $WorkUnit) { Exit-IcingaThrowException -ExceptionType 'Input' -ExceptionThrown $IcingaExceptions.Inputs.MultipleUnitUsage -Force; } $Value = (Convert-Bytes -Value $ThresholdValue -Unit $WorkUnit).Value; $RetValue.Unit = $WorkUnit; } elseif (($ThresholdValue -Match "(^-?[0-9]+)((\.|\,)[0-9]+)?(ms|s|m|h|d|w|M|y)$")) { $WorkUnit = 's'; if ([string]::IsNullOrEmpty($RetValue.Unit) -eq $FALSE -And $RetValue.Unit -ne $WorkUnit) { Exit-IcingaThrowException -ExceptionType 'Input' -ExceptionThrown $IcingaExceptions.Inputs.MultipleUnitUsage -Force; } $Value = (ConvertTo-Seconds -Value $ThresholdValue); $RetValue.Unit = $WorkUnit; } elseif (($ThresholdValue -Match "(^[\d\.]*) ?(%)")) { $WorkUnit = '%'; $Value = ([string]$ThresholdValue).Replace(' ', '').Replace('%', ''); $RetValue.Unit = $WorkUnit; } elseif (($ThresholdValue -Match "(^-?[0-9]+)((\.|\,)[0-9]+)?(Kbit|Mbit|Gbit|Tbit|Pbit|Ebit|Zbit|Ybit)$")) { $Value = $Matches[1]; $RetValue.Unit = $Matches[4]; } else { # Load all other units/values generically [string]$StrNumeric = ''; [bool]$FirstChar = $TRUE; [bool]$Delimiter = $FALSE; foreach ($entry in ([string]($ThresholdValue)).ToCharArray()) { if ((Test-Numeric $entry) -Or ($entry -eq '.' -And $Delimiter -eq $FALSE)) { $StrNumeric += $entry; $FirstChar = $FALSE; if ($entry -eq '.') { $Delimiter = $TRUE; } } else { if ([string]::IsNullOrEmpty($RetValue.Unit) -And $FirstChar -eq $FALSE) { $RetValue.Unit = $entry; } else { $StrNumeric = ''; $RetValue.Unit = ''; break; } } } if ([string]::IsNullOrEmpty($StrNumeric)) { $Value = $ThresholdValue; } else { $Value = [decimal]$StrNumeric; } } if ((Test-Numeric $Value) -And $Negate) { $Value = $Value * -1; } elseif ($Negate) { $Value = [string]::Format('-{0}', $Value); } if ($HasTilde) { $ConvertedValue += [string]::Format('~{0}', $Value); } elseif ($HasAt) { $ConvertedValue += [string]::Format('@{0}', $Value); } else { $ConvertedValue += $Value; } } [string]$Value = [string]::Join(':', $ConvertedValue); switch ($RetValue.Mode) { $IcingaEnums.IcingaThresholdMethod.Default { $RetValue.Value = $ConvertedValue[0]; if ([string]::IsNullOrEmpty($RetValue.Value)) { Exit-IcingaThrowException -CustomMessage ([string]::Format('Could not convert the provided threshold value {0} to a valid Icinga for Windows range', $RetValue.Raw)) -ExceptionType 'Input' -ExceptionThrown $IcingaExceptions.Inputs.InvalidThresholdValue -Force; return $RetValue; } break; }; $IcingaEnums.IcingaThresholdMethod.Lower { $RetValue.Value = $ConvertedValue[0]; if ([string]::IsNullOrEmpty($RetValue.Value)) { Exit-IcingaThrowException -CustomMessage ([string]::Format('Could not convert the provided threshold value {0} to a valid Icinga for Windows range', $RetValue.Raw)) -ExceptionType 'Input' -ExceptionThrown $IcingaExceptions.Inputs.InvalidThresholdValue -Force; return $RetValue; } break; }; $IcingaEnums.IcingaThresholdMethod.Greater { $RetValue.Value = $ConvertedValue[1]; if ([string]::IsNullOrEmpty($RetValue.Value)) { Exit-IcingaThrowException -CustomMessage ([string]::Format('Could not convert the provided threshold value {0} to a valid Icinga for Windows range', $RetValue.Raw)) -ExceptionType 'Input' -ExceptionThrown $IcingaExceptions.Inputs.InvalidThresholdValue -Force; return $RetValue; } break; }; $IcingaEnums.IcingaThresholdMethod.Between { $RetValue.StartRange = [decimal]$ConvertedValue[0]; $RetValue.EndRange = [decimal]$ConvertedValue[1]; if ([string]::IsNullOrEmpty($RetValue.StartRange) -Or [string]::IsNullOrEmpty($RetValue.EndRange)) { Exit-IcingaThrowException -CustomMessage ([string]::Format('Could not convert the provided threshold value {0} to a valid Icinga for Windows range', $RetValue.Raw)) -ExceptionType 'Input' -ExceptionThrown $IcingaExceptions.Inputs.InvalidThresholdValue -Force; return $RetValue; } break; }; $IcingaEnums.IcingaThresholdMethod.Outside { $RetValue.StartRange = [decimal]($ConvertedValue[0].Replace('@', '')); $RetValue.EndRange = [decimal]$ConvertedValue[1]; if ([string]::IsNullOrEmpty($RetValue.StartRange) -Or [string]::IsNullOrEmpty($RetValue.EndRange)) { Exit-IcingaThrowException -CustomMessage ([string]::Format('Could not convert the provided threshold value {0} to a valid Icinga for Windows range', ($RetValue.Raw))) -ExceptionType 'Input' -ExceptionThrown $IcingaExceptions.Inputs.InvalidThresholdValue -Force; return $RetValue; } break; }; } if ([string]::IsNullOrEmpty($Value) -eq $FALSE -And $Value.Contains(':') -eq $FALSE) { if ((Test-Numeric $Value)) { $RetValue.Threshold = [decimal]$Value; $RetValue.Value = [decimal]$Value; return $RetValue; } } # Always ensure we are using correct digits $Value = ([string]$Value).Replace(',', '.'); $RetValue.Threshold = $Value; return $RetValue; }