Skip to content

Commit 96afdc7

Browse files
authored
Merge pull request #204 from Icinga:feature/forward_checks_to_internal_api
Feature: Adds experimental feature for internal API checks Adds experimental feature to forward checks executed by the Icinga Agent to an internal REST-Api, to reduce the performance impact on systems with lower ressources available
2 parents a5a401f + 7b847bd commit 96afdc7

19 files changed

+510
-77
lines changed

Diff for: doc/31-Changelog.md

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
2626
* [#206](https://github.com/Icinga/icinga-powershell-framework/pull/206) Fixes background service check daemon for collecting metrics over time which will no longer share data between configured checks which might cause higher CPU load and a possible memory leak
2727
* [#208](https://github.com/Icinga/icinga-powershell-framework/pull/208) Fixes `Convert-IcingaPluginThresholds` which sometimes did not return proper numeric usable values for our internal functions, causing issues on plugin calls. In addition the function now also supports the handling for % units.
2828

29+
### Experimental
30+
31+
* [#204](https://github.com/Icinga/icinga-powershell-framework/pull/204) Adds experimental feature to forward checks executed by the Icinga Agent to an internal REST-Api, to reduce the performance impact on systems with lower resources available
32+
2933
## 1.3.1 (2021-02-04)
3034

3135
[Issue and PRs](https://github.com/Icinga/icinga-powershell-framework/milestone/12?closed=1)

Diff for: icinga-powershell-framework.psd1

+26-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,19 @@
1515
'.\lib\core\logging\Write-IcingaConsoleOutput.psm1',
1616
'.\lib\core\logging\Write-IcingaConsoleNotice.psm1',
1717
'.\lib\core\logging\Write-IcingaConsoleWarning.psm1',
18-
'.\lib\core\tools\Read-IcingaFileContent.psm1'
18+
'.\lib\core\tools\Read-IcingaFileContent.psm1',
19+
'.\lib\core\framework\Invoke-IcingaInternalServiceCall.psm1',
20+
'.\lib\core\framework\Get-IcingaFrameworkApiChecks.psm1',
21+
'.\lib\daemon\Get-IcingaBackgroundDaemons.psm1',
22+
'.\lib\webserver\Enable-IcingaUntrustedCertificateValidation.psm1',
23+
'.\lib\core\logging\Write-IcingaEventMessage.psm1',
24+
'.\lib\icinga\plugin\Exit-IcingaExecutePlugin.psm1',
25+
'.\lib\icinga\exception\Exit-IcingaPluginNotInstalled.psm1',
26+
'.\lib\icinga\exception\Exit-IcingaThrowException.psm1',
27+
'.\lib\web\Disable-IcingaProgressPreference.psm1',
28+
'.\lib\core\tools\New-IcingaNewLine.psm1',
29+
'.\lib\core\logging\Write-IcingaConsolePlain.psm1',
30+
'.\lib\core\tools\Test-IcingaFunction.psm1'
1931
)
2032
FunctionsToExport = @(
2133
'Use-Icinga',
@@ -39,7 +51,19 @@
3951
'Write-IcingaConsoleOutput',
4052
'Write-IcingaConsoleNotice',
4153
'Write-IcingaConsoleWarning',
42-
'Read-IcingaFileContent'
54+
'Read-IcingaFileContent',
55+
'Invoke-IcingaInternalServiceCall',
56+
'Get-IcingaFrameworkApiChecks',
57+
'Get-IcingaBackgroundDaemons',
58+
'Enable-IcingaUntrustedCertificateValidation',
59+
'Write-IcingaEventMessage',
60+
'Exit-IcingaExecutePlugin',
61+
'Exit-IcingaPluginNotInstalled',
62+
'Exit-IcingaThrowException',
63+
'Disable-IcingaProgressPreference',
64+
'New-IcingaNewLine',
65+
'Write-IcingaConsolePlain',
66+
'Test-IcingaFunction'
4367
)
4468
CmdletsToExport = @('*')
4569
VariablesToExport = '*'

Diff for: icinga-powershell-framework.psm1

+13-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,21 @@ function Use-Icinga()
1313
param(
1414
[switch]$LibOnly = $FALSE,
1515
[switch]$Daemon = $FALSE,
16-
[switch]$DebugMode = $FALSE
16+
[switch]$DebugMode = $FALSE,
17+
[switch]$Minimal = $FALSE
1718
);
1819

20+
Disable-IcingaProgressPreference;
21+
22+
if ($Minimal) {
23+
# If we load the minimal Framework files, we have to ensure our enums are loaded
24+
Import-Module ([string]::Format('{0}\lib\icinga\exception\Icinga_IcingaExceptionEnums.psm1', $PSScriptRoot)) -Global;
25+
Import-Module ([string]::Format('{0}\lib\icinga\enums\Icinga_IcingaEnums.psm1', $PSScriptRoot)) -Global;
26+
Import-Module ([string]::Format('{0}\lib\core\logging\Icinga_EventLog_Enums.psm1', $PSScriptRoot)) -Global;
27+
28+
return;
29+
}
30+
1931
# Ensure we autoload the Icinga Plugin collection, provided by the external
2032
# module 'icinga-powershell-plugins'
2133
if (Get-Command 'Use-IcingaPlugins' -ErrorAction SilentlyContinue) {
@@ -61,7 +73,6 @@ function Use-Icinga()
6173
}
6274
}
6375
New-IcingaPerformanceCounterCache;
64-
Disable-IcingaProgressPreference;
6576

6677
# Enable DebugMode in case it is enabled in our config
6778
if (Get-IcingaFrameworkDebugMode) {
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<#
2+
.SYNOPSIS
3+
Adds a Cmdlet to an REST-Api endpoint which is either whitelisted or blacklisted.
4+
Whitelisted Cmdlets can be executed over API endpoints, blacklisted not.
5+
Use '*' for wildcard matches
6+
.DESCRIPTION
7+
Adds a Cmdlet to an REST-Api endpoint which is either whitelisted or blacklisted.
8+
Whitelisted Cmdlets can be executed over API endpoints, blacklisted not.
9+
Use '*' for wildcard matches
10+
.FUNCTIONALITY
11+
Enables or disables Cmdlets for REST-Api endpoints
12+
.EXAMPLE
13+
PS>Add-IcingaFrameworkApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'checker';
14+
.EXAMPLE
15+
PS>Add-IcingaFrameworkApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'checker' -Blacklist;
16+
.LINK
17+
https://github.com/Icinga/icinga-powershell-framework
18+
#>
19+
20+
function Add-IcingaFrameworkApiCommand()
21+
{
22+
param (
23+
[string]$Command = '',
24+
[string]$Endpoint = '',
25+
[switch]$Blacklist = $FALSE
26+
);
27+
28+
if ([string]::IsNullOrEmpty($Command) -Or [string]::IsNullOrEmpty($Endpoint)) {
29+
return;
30+
}
31+
32+
$Commands = $null;
33+
$ConfigPath = ([string]::Format('Framework.RESTApiCommands.{0}.Whitelist', $Endpoint));
34+
[array]$Values = @();
35+
36+
if ($Blacklist) {
37+
$ConfigPath = ([string]::Format('Framework.RESTApiCommands.{0}.Blacklist', $Endpoint));
38+
}
39+
40+
$Commands = Get-IcingaPowerShellConfig -Path $ConfigPath;
41+
42+
if ((Test-IcingaPowerShellConfigItem -ConfigObject $Commands -ConfigKey $Command)) {
43+
return;
44+
}
45+
46+
if ($null -ne $Commands) {
47+
$Values = $Commands;
48+
}
49+
50+
$Values += $Command;
51+
52+
Set-IcingaPowerShellConfig -Path $ConfigPath -Value $Values;
53+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<#
2+
.SYNOPSIS
3+
Disables the feature to forward all executed checks to an internal
4+
installed API to run them within a daemon
5+
.DESCRIPTION
6+
Disables the feature to forward all executed checks to an internal
7+
installed API to run them within a daemon
8+
.FUNCTIONALITY
9+
Disables the Icinga for Windows Api checks forwarded
10+
.EXAMPLE
11+
PS>Disable-IcingaFrameworkApiChecks;
12+
.LINK
13+
https://github.com/Icinga/icinga-powershell-framework
14+
#>
15+
16+
function Disable-IcingaFrameworkApiChecks()
17+
{
18+
Set-IcingaPowerShellConfig -Path 'Framework.Experimental.UseApiChecks' -Value $FALSE;
19+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<#
2+
.SYNOPSIS
3+
Enables the feature to forward all executed checks to an internal
4+
installed API to run them within a daemon
5+
.DESCRIPTION
6+
Enables the feature to forward all executed checks to an internal
7+
installed API to run them within a daemon
8+
.FUNCTIONALITY
9+
Enables the Icinga for Windows Api checks forwarded
10+
.EXAMPLE
11+
PS>Enable-IcingaFrameworkApiChecks;
12+
.LINK
13+
https://github.com/Icinga/icinga-powershell-framework
14+
#>
15+
16+
function Enable-IcingaFrameworkApiChecks()
17+
{
18+
Set-IcingaPowerShellConfig -Path 'Framework.Experimental.UseApiChecks' -Value $TRUE;
19+
20+
Write-IcingaConsoleWarning 'Experimental Feature: Please ensure to install the packages "icinga-powershell-restapi" and "icinga-powershell-apichecks", install the Icinga for Windows background service and also register the daemon with "Register-IcingaBackgroundDaemon -Command {0}". Afterwards all services will be executed by the background daemon in case it is running.' -Objects "'Start-IcingaWindowsRESTApi'";
21+
}

Diff for: lib/core/framework/Get-IcingaFrameworkApiChecks.psm1

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<#
2+
.SYNOPSIS
3+
Fetches the current enable/disable state of the feature
4+
for executing checks of the internal REST-Api
5+
.DESCRIPTION
6+
Fetches the current enable/disable state of the feature
7+
for executing checks of the internal REST-Api
8+
.FUNCTIONALITY
9+
Get the current API check execution configuration of the
10+
Icinga PowerShell Framework
11+
.EXAMPLE
12+
PS>Get-IcingaFrameworkApiChecks;
13+
.LINK
14+
https://github.com/Icinga/icinga-powershell-framework
15+
.OUTPUTS
16+
System.Boolean
17+
#>
18+
19+
function Get-IcingaFrameworkApiChecks()
20+
{
21+
$CodeCaching = Get-IcingaPowerShellConfig -Path 'Framework.Experimental.UseApiChecks';
22+
23+
if ($null -eq $CodeCaching) {
24+
return $FALSE;
25+
}
26+
27+
return $CodeCaching;
28+
}
+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
function Invoke-IcingaInternalServiceCall()
2+
{
3+
param (
4+
[string]$Command = '',
5+
[array]$Arguments = @()
6+
);
7+
8+
# If our Framework is running as daemon, never call our api
9+
if ($global:IcingaDaemonData.FrameworkRunningAsDaemon) {
10+
return;
11+
}
12+
13+
# If the API forward feature is disabled, do nothing
14+
if ((Get-IcingaFrameworkApiChecks) -eq $FALSE) {
15+
return;
16+
}
17+
18+
# Test our Icinga for Windows service. If the service is not installed or not running, execute the plugin locally
19+
$IcingaForWindowsService = (Get-Service 'icingapowershell' -ErrorAction SilentlyContinue);
20+
21+
if ($null -eq $IcingaForWindowsService -Or $IcingaForWindowsService.Status -ne 'Running') {
22+
return;
23+
}
24+
25+
# In case the REST-Api module ist not configured, do nothing
26+
$BackgroundDaemons = Get-IcingaBackgroundDaemons;
27+
28+
if ($null -eq $BackgroundDaemons -Or $BackgroundDaemons.ContainsKey('Start-IcingaWindowsRESTApi') -eq $FALSE) {
29+
return;
30+
}
31+
32+
# If neither 'icinga-powershell-restapi' or 'icinga-powershell-apichecks' is installed, execute the plugin locally
33+
if ((Test-IcingaFunction 'Invoke-IcingaApiChecksRESTCall') -eq $FALSE -Or (Test-IcingaFunction 'Start-IcingaWindowsRESTApi') -eq $FALSE) {
34+
return;
35+
}
36+
37+
$RestApiPort = 5668;
38+
[int]$Timeout = 30;
39+
$Daemon = $BackgroundDaemons['Start-IcingaWindowsRESTApi'];
40+
41+
# Fetch our deamon configuration
42+
if ($Daemon.ContainsKey('-Port')) {
43+
$RestApiPort = $Daemon['-Port'];
44+
} elseif ($Daemon.ContainsKey('Port')) {
45+
$RestApiPort = $Daemon['Port'];
46+
}
47+
if ($Daemon.ContainsKey('-Timeout')) {
48+
$Timeout = $Daemon['-Timeout'];
49+
} elseif ($Daemon.ContainsKey('Timeout')) {
50+
$Timeout = $Daemon['Timeout'];
51+
}
52+
53+
Enable-IcingaUntrustedCertificateValidation -SuppressMessages;
54+
55+
[hashtable]$CommandArguments = @{ };
56+
[hashtable]$DebugArguments = @{ };
57+
[hashtable]$ConvertedArgs = @{ };
58+
[int]$ArgumentIndex = 0;
59+
60+
# Resolve our array arguments provided by $args and build proper check arguments
61+
while ($ArgumentIndex -lt $Arguments.Count) {
62+
$Value = $Arguments[$ArgumentIndex];
63+
[string]$Argument = [string]$Value;
64+
$ArgumentValue = $null;
65+
66+
if ($Value[0] -eq '-') {
67+
if (($ArgumentIndex + 1) -lt $Arguments.Count) {
68+
[string]$NextValue = $Arguments[$ArgumentIndex + 1];
69+
if ($NextValue[0] -eq '-') {
70+
$ArgumentValue = $TRUE;
71+
} else {
72+
$ArgumentValue = $Arguments[$ArgumentIndex + 1];
73+
}
74+
} else {
75+
$ArgumentValue = $TRUE;
76+
}
77+
} else {
78+
$ArgumentIndex += 1;
79+
continue;
80+
}
81+
82+
$Argument = $Argument.Replace('-', '');
83+
84+
$ConvertedArgs.Add($Argument, $ArgumentValue);
85+
$ArgumentIndex += 1;
86+
}
87+
88+
# Now queue the check inside our REST-Api
89+
try {
90+
$ApiResult = Invoke-WebRequest -Method POST -UseBasicParsing -Uri ([string]::Format('https://localhost:{0}/v1/checker?command={1}', $RestApiPort, $Command)) -Body ($CommandArguments | ConvertTo-Json -Depth 100) -ContentType 'application/json' -TimeoutSec $Timeout;
91+
} catch {
92+
# Something went wrong -> fallback to local execution
93+
$ExMsg = $_.Exception.message;
94+
# Fallback to execute plugin locally
95+
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects $ExMsg, $Command, $DebugArguments;
96+
return;
97+
}
98+
99+
# Resolve our result from the API
100+
$IcingaResult = ConvertFrom-Json -InputObject $ApiResult;
101+
$IcingaCR = '';
102+
103+
# In case we didn't receive a check result, fallback to local execution
104+
if ($null -eq $IcingaResult.$Command.checkresult) {
105+
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects 'The check result for the executed command was empty', $Command, $DebugArguments;
106+
return;
107+
}
108+
109+
$IcingaCR = ($IcingaResult.$Command.checkresult.Replace("`r`n", "`n"));
110+
111+
if ($IcingaResult.$Command.perfdata.Count -ne 0) {
112+
$IcingaCR += ' | ';
113+
foreach ($perfdata in $IcingaResult.$Command.perfdata) {
114+
$IcingaCR += $perfdata;
115+
}
116+
}
117+
118+
# Print our response and exit with the provide exit code
119+
Write-IcingaConsolePlain $IcingaCR;
120+
exit $IcingaResult.$Command.exitcode;
121+
}
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<#
2+
.SYNOPSIS
3+
Removes a Cmdlet from an REST-Api endpoints whitelist or blacklist.
4+
.DESCRIPTION
5+
Removes a Cmdlet from an REST-Api endpoints whitelist or blacklist.
6+
.FUNCTIONALITY
7+
Removes Cmdlets for REST-Api endpoints
8+
.EXAMPLE
9+
PS>Remove-IcingaFrameworkApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'checker';
10+
.EXAMPLE
11+
PS>Add-IcingaFrameworkApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'checker' -Blacklist;
12+
.LINK
13+
https://github.com/Icinga/icinga-powershell-framework
14+
#>
15+
16+
function Remove-IcingaFrameworkApiCommand()
17+
{
18+
param (
19+
[string]$Command = '',
20+
[string]$Endpoint = '',
21+
[switch]$Blacklist = $FALSE
22+
);
23+
24+
if ([string]::IsNullOrEmpty($Command) -Or [string]::IsNullOrEmpty($Endpoint)) {
25+
return;
26+
}
27+
28+
$Commands = $null;
29+
$ConfigPath = ([string]::Format('Framework.RESTApiCommands.{0}.Whitelist', $Endpoint));
30+
[array]$Values = @();
31+
32+
if ($Blacklist) {
33+
$ConfigPath = ([string]::Format('Framework.RESTApiCommands.{0}.Blacklist', $Endpoint));
34+
}
35+
36+
$Commands = Get-IcingaPowerShellConfig -Path $ConfigPath;
37+
38+
if ($null -eq $Commands) {
39+
return;
40+
}
41+
42+
foreach ($element in $Commands) {
43+
if ($element.ToLower() -ne $Command.ToLower()) {
44+
$Values += $element;
45+
}
46+
}
47+
48+
Set-IcingaPowerShellConfig -Path $ConfigPath -Value $Values;
49+
}

0 commit comments

Comments
 (0)