Skip to content

Commit 6b45918

Browse files
authored
Merge pull request #750 from Icinga:feature/rework_handling_for_processing_checks
Rework: Plugin and checker core handling for plugin evaluation Reworks the internal handling on how plugin thresholds are evaluated and the internal checker core, including on how performance metrics are generated
2 parents 5bbba96 + 295fa00 commit 6b45918

16 files changed

+789
-670
lines changed

doc/100-General/10-Changelog.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ documentation before upgrading to a new release.
77

88
Released closed milestones can be found on [GitHub](https://github.com/Icinga/icinga-powershell-framework/milestones?state=closed).
99

10-
## 1.13.0 (tbd)
10+
## 1.13.0 Beta-1 (2024-08-30)
1111

1212
[Issues and PRs](https://github.com/Icinga/icinga-powershell-framework/milestone/32)
1313

14+
### Notes
15+
16+
This beta release has reworked the entire handling on how thresholds and the checker core operate. For that reason, the `Beta-1` release will **not** include the `Metrics over Time` feature. The goal of this beta is to get an idea if the threshold handling is working as expected and evaluate the performance gains for the new check handling. The `Metrics over Time` feature will be re-implemented with an entire new background task and configurations in `Beta-2`.
17+
1418
### Bugfixes
1519

1620
* [#729](https://github.com/Icinga/icinga-powershell-framework/issues/729) Fixes `Update-Icinga` to print an error in case a component is not installed, instead of silently continue
@@ -27,6 +31,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
2731
* [#739](https://github.com/Icinga/icinga-powershell-framework/pull/739) Adds support to check the encoding of files to ensure we can properly load them and throw errors for unsupported encoding
2832
* [#740](https://github.com/Icinga/icinga-powershell-framework/pull/740) Adds new command `Invoke-IcingaForWindowsRESTApi` for easier API communication
2933
* [#742](https://github.com/Icinga/icinga-powershell-framework/pull/742) Adds support for the CPU provider to limit the CPU usage to 100% for each thread
34+
* [#750](https://github.com/Icinga/icinga-powershell-framework/pull/750) Reworks the internal handling on how plugin thresholds are evaluated and the internal checker core, including on how performance metrics are generated
3035

3136
## 1.12.3 (2024-04-24)
3237

lib/core/framework/Get-IcingaCheckSchedulerPerfData.psm1

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,19 @@
1010
Returns the last performance data output for executed plugins while the
1111
Framework is running as daemon
1212
.OUTPUTS
13-
System.Object
13+
System.String
1414
.LINK
1515
https://github.com/Icinga/icinga-powershell-framework
1616
#>
1717

1818
function Get-IcingaCheckSchedulerPerfData()
1919
{
20-
$PerfData = $Global:Icinga.Private.Scheduler.PerformanceData;
21-
[array]$Global:Icinga.Private.Scheduler.PerformanceData = @();
20+
[string]$PerfData = $Global:Icinga.Private.Scheduler.PerformanceData;
21+
[string]$Global:Icinga.Private.Scheduler.PerformanceData = '';
22+
23+
# Ensure we clear our PerfDataWriter cache and storage to have a clean base state for the next plugin execution
24+
$Global:Icinga.Private.Scheduler.PerfDataWriter.Cache.Clear() | Out-Null;
25+
$Global:Icinga.Private.Scheduler.PerfDataWriter.Storage.Clear() | Out-Null;
2226

2327
return $PerfData;
2428
}

lib/core/framework/Invoke-IcingaInternalServiceCall.psm1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function Invoke-IcingaInternalServiceCall()
8888
$IcingaCR = ($IcingaResult.$Command.checkresult.Replace("`r`n", "`n"));
8989

9090
if ($IcingaResult.$Command.perfdata.Count -ne 0) {
91-
$IcingaCR = [string]::Format('{0}{1}| {2}', $IcingaCR, "`r`n", ([string]::Join(' ', $IcingaResult.$Command.perfdata)));
91+
$IcingaCR = [string]::Format('{0}{1}| {2}', $IcingaCR, "`r`n", ([string]::Join('', $IcingaResult.$Command.perfdata)));
9292
}
9393

9494
if ($NoExit) {

lib/core/framework/New-IcingaEnvironmentVariable.psm1

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,13 @@ function New-IcingaEnvironmentVariable()
4040
'CheckData' = @{ };
4141
'ThresholdCache' = @{ };
4242
'CheckResults' = @();
43-
'PerformanceData' = @();
43+
'PerformanceData' = '';
4444
'PluginException' = $null;
4545
'ExitCode' = $null;
46+
'PerfDataWriter' = @{
47+
'Cache' = @{};
48+
'Storage' = (New-Object System.Text.StringBuilder);
49+
}
4650
}
4751
);
4852

lib/core/tools/Convert-IcingaPluginThresholds.psm1

Lines changed: 177 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
to the lowest base of the unit. It does support the Icinga
1515
plugin language, like ~:30, @10:40, 15:30, ...
1616
17+
You can also provide date time values in the format of "yyyy/MM/dd HH:mm:ss"
18+
and use Icinga for Windows plugin thresholds in combination. You have to escape
19+
the ':' inside the date time value with a '`' to ensure the correct conversion.
20+
21+
Example:
22+
2024/08/19 12`:42`:00
23+
1724
The conversion does currently support the following units:
1825
1926
Size: B, KB, MB, GB, TB, PT, KiB, MiB, GiB, TiB, PiB
@@ -26,43 +33,90 @@
2633
2734
Name Value
2835
---- -----
29-
Value 1728000
36+
EndRange
3037
Unit s
38+
StartRange
39+
Threshold 1728000
40+
Mode 0
41+
Raw 20d
42+
IsDateTime False
43+
Value 1728000
3144
.EXAMPLE
3245
PS>Convert-IcingaPluginThresholds -Threshold '5GB';
3346
3447
Name Value
3548
---- -----
36-
Value 5000000000
49+
EndRange
3750
Unit B
51+
StartRange
52+
Threshold 5000000000
53+
Mode 0
54+
Raw 5GB
55+
IsDateTime False
56+
Value 5000000000
3857
.EXAMPLE
3958
PS>Convert-IcingaPluginThresholds -Threshold '10MB:20MB';
4059
4160
Name Value
4261
---- -----
43-
Value 10000000:20000000
62+
EndRange 20000000
4463
Unit B
64+
StartRange 10000000
65+
Threshold 10000000:20000000
66+
Mode 3
67+
Raw 10MB:20MB
68+
IsDateTime False
69+
Value
4570
.EXAMPLE
4671
PS>Convert-IcingaPluginThresholds -Threshold '10m:1h';
4772
4873
Name Value
4974
---- -----
50-
Value 600:3600
75+
EndRange 3600
5176
Unit s
77+
StartRange 600
78+
Threshold 600:3600
79+
Mode 3
80+
Raw 10m:1h
81+
Value
5282
.EXAMPLE
5383
PS>Convert-IcingaPluginThresholds -Threshold '@10m:1h';
5484
5585
Name Value
5686
---- -----
57-
Value @600:3600
87+
EndRange 3600
5888
Unit s
89+
StartRange 600
90+
Threshold @600:3600
91+
Mode 4
92+
Raw @10m:1h
93+
IsDateTime False
94+
Value
5995
.EXAMPLE
60-
Convert-IcingaPluginThresholds -Threshold '~:1M';
96+
PS>Convert-IcingaPluginThresholds -Threshold '~:1M';
6197
6298
Name Value
6399
---- -----
64-
Value ~:2592000
100+
EndRange
101+
Unit s
102+
StartRange
103+
Threshold ~:2592000
104+
Mode 2
105+
Raw ~:1M
106+
IsDateTime False
107+
Value 2592000
108+
.EXAMPLE
109+
PS>Convert-IcingaPluginThresholds -Threshold '@2024/08/19 12`:42`:00:2024/08/19 12`:42`:00';
110+
Name Value
111+
---- -----
112+
EndRange 133685377200000000
65113
Unit s
114+
StartRange 133685377200000000
115+
Threshold @133685377200000000:133685377200000000
116+
Mode 4
117+
Raw @2024/08/19 12`:42`:00:2024/08/19 12`:42`:00
118+
IsDateTime True
119+
Value
66120
.INPUTS
67121
System.String
68122
.OUTPUTS
@@ -78,35 +132,80 @@ function Convert-IcingaPluginThresholds()
78132
);
79133

80134
[hashtable]$RetValue = @{
81-
'Unit' = '';
82-
'Value' = $null;
135+
'Raw' = $Threshold;
136+
'Unit' = '';
137+
'Threshold' = $null;
138+
'Value' = $null;
139+
'StartRange' = $null;
140+
'EndRange' = $null;
141+
'Mode' = $IcingaEnums.IcingaThresholdMethod.Default;
142+
'IsDateTime' = $FALSE;
83143
};
84144

85-
if ($null -eq $Threshold) {
145+
if ([string]::IsNullOrEmpty($Threshold)) {
86146
return $RetValue;
87147
}
88148

89149
# Always ensure we are using correct digits
90-
$Threshold = $Threshold.Replace(',', '.');
91-
92-
[array]$Content = @();
150+
$Threshold = $Threshold.Replace(',', '.');
151+
[array]$Content = @();
93152

94153
if ($Threshold.Contains(':')) {
95154
# If we have more than one ':' inside our string, lets check if this is a date time value
96155
# In case it is convert it properly to a FileTime we can work with later on
97-
if ([Regex]::Matches($Threshold, ":").Count -gt 1) {
156+
if ([Regex]::Matches($Threshold, '`:').Count -gt 1) {
157+
[bool]$HasTilde = $FALSE;
158+
[bool]$HasAt = $FALSE;
159+
160+
if ($Threshold.Contains('@')) {
161+
$HasAt = $TRUE;
162+
} elseif ($Threshold.Contains('~')) {
163+
$HasTilde = $TRUE;
164+
}
165+
166+
$Threshold = $Threshold.Replace('`:', '!').Replace('~', '').Replace('@', '');
167+
[array]$DatimeValueArray = $Threshold.Split(':');
168+
98169
try {
99-
$DateTimeValue = [DateTime]::ParseExact($Threshold, 'yyyy\/MM\/dd HH:mm:ss', $null);
100-
$RetValue.Value = $DateTimeValue.ToFileTime();
101-
$RetValue.Unit = 's';
170+
[array]$DateTimeValue = @();
171+
if ([string]::IsNullOrEmpty($DatimeValueArray[0]) -eq $FALSE) {
172+
[array]$DateTimeValue += ([DateTime]::ParseExact($DatimeValueArray[0].Replace('!', ':'), 'yyyy\/MM\/dd HH:mm:ss', $null)).ToFileTime();
173+
}
174+
if ([string]::IsNullOrEmpty($DatimeValueArray[1]) -eq $FALSE) {
175+
[array]$DateTimeValue += ([DateTime]::ParseExact($DatimeValueArray[1].Replace('!', ':'), 'yyyy\/MM\/dd HH:mm:ss', $null)).ToFileTime();
176+
}
177+
178+
if ($DateTimeValue.Count -gt 1) {
179+
$Threshold = [string]::Join(':', $DateTimeValue);
180+
$RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Between;
181+
182+
if ($HasAt) {
183+
$Threshold = [string]::Format('@{0}', $Threshold);
184+
}
185+
} elseif ($DatimeValueArray.Count -gt 1) {
186+
if ($HasTilde) {
187+
$Threshold = [string]::Format('~:{0}', $DateTimeValue[0]);
188+
} else {
189+
$Threshold = [string]::Format('{0}:', $DateTimeValue[0]);
190+
}
191+
} else {
192+
$Threshold = $DateTimeValue[0];
193+
}
194+
$RetValue.Unit = 's';
195+
$RetValue.IsDateTime = $TRUE;
102196
} catch {
103-
$RetValue.Value = $Threshold;
197+
$RetValue.Threshold = $Threshold.Replace('!', '`:');
198+
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;
199+
return $RetValue;
104200
}
105-
106-
return $RetValue;
201+
} else {
202+
$RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Between;
107203
}
108204

109205
$Content = $Threshold.Split(':');
206+
if ($Content.Count -eq 2 -And ([string]::IsNullOrEmpty($Content[1]))) {
207+
$RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Lower;
208+
}
110209
} else {
111210
$Content += $Threshold;
112211
}
@@ -123,10 +222,12 @@ function Convert-IcingaPluginThresholds()
123222

124223
if ($ThresholdValue.Contains('~')) {
125224
$ThresholdValue = $ThresholdValue.Replace('~', '');
126-
$HasTilde = $TRUE;
225+
$HasTilde = $TRUE;
226+
$RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Greater;
127227
} elseif ($ThresholdValue.Contains('@')) {
128-
$HasAt = $TRUE;
228+
$HasAt = $TRUE;
129229
$ThresholdValue = $ThresholdValue.Replace('@', '');
230+
$RetValue.Mode = $IcingaEnums.IcingaThresholdMethod.Outside;
130231
}
131232

132233
if ($ThresholdValue[0] -eq '-' -And $ThresholdValue.Length -ge 1) {
@@ -201,16 +302,67 @@ function Convert-IcingaPluginThresholds()
201302

202303
[string]$Value = [string]::Join(':', $ConvertedValue);
203304

305+
switch ($RetValue.Mode) {
306+
$IcingaEnums.IcingaThresholdMethod.Default {
307+
$RetValue.Value = $ConvertedValue[0];
308+
309+
if ([string]::IsNullOrEmpty($RetValue.Value)) {
310+
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;
311+
return $RetValue;
312+
}
313+
break;
314+
};
315+
$IcingaEnums.IcingaThresholdMethod.Lower {
316+
$RetValue.Value = $ConvertedValue[0];
317+
318+
if ([string]::IsNullOrEmpty($RetValue.Value)) {
319+
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;
320+
return $RetValue;
321+
}
322+
break;
323+
};
324+
$IcingaEnums.IcingaThresholdMethod.Greater {
325+
$RetValue.Value = $ConvertedValue[1];
326+
327+
if ([string]::IsNullOrEmpty($RetValue.Value)) {
328+
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;
329+
return $RetValue;
330+
}
331+
break;
332+
};
333+
$IcingaEnums.IcingaThresholdMethod.Between {
334+
$RetValue.StartRange = [decimal]$ConvertedValue[0];
335+
$RetValue.EndRange = [decimal]$ConvertedValue[1];
336+
337+
if ([string]::IsNullOrEmpty($RetValue.StartRange) -Or [string]::IsNullOrEmpty($RetValue.EndRange)) {
338+
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;
339+
return $RetValue;
340+
}
341+
break;
342+
};
343+
$IcingaEnums.IcingaThresholdMethod.Outside {
344+
$RetValue.StartRange = [decimal]($ConvertedValue[0].Replace('@', ''));
345+
$RetValue.EndRange = [decimal]$ConvertedValue[1];
346+
347+
if ([string]::IsNullOrEmpty($RetValue.StartRange) -Or [string]::IsNullOrEmpty($RetValue.EndRange)) {
348+
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;
349+
return $RetValue;
350+
}
351+
break;
352+
};
353+
}
354+
204355
if ([string]::IsNullOrEmpty($Value) -eq $FALSE -And $Value.Contains(':') -eq $FALSE) {
205356
if ((Test-Numeric $Value)) {
206-
$RetValue.Value = [decimal]$Value;
357+
$RetValue.Threshold = [decimal]$Value;
358+
$RetValue.Value = [decimal]$Value;
207359
return $RetValue;
208360
}
209361
}
210362

211363
# Always ensure we are using correct digits
212-
$Value = ([string]$Value).Replace(',', '.');
213-
$RetValue.Value = $Value;
364+
$Value = ([string]$Value).Replace(',', '.');
365+
$RetValue.Threshold = $Value;
214366

215367
return $RetValue;
216368
}

0 commit comments

Comments
 (0)