Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental: Adds experimental feature for internal API checks #204

Merged
merged 1 commit into from
Feb 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/31-Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
* [#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
* [#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.

### Experimental

* [#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

## 1.3.1 (2021-02-04)

[Issue and PRs](https://github.com/Icinga/icinga-powershell-framework/milestone/12?closed=1)
Expand Down
28 changes: 26 additions & 2 deletions icinga-powershell-framework.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,19 @@
'.\lib\core\logging\Write-IcingaConsoleOutput.psm1',
'.\lib\core\logging\Write-IcingaConsoleNotice.psm1',
'.\lib\core\logging\Write-IcingaConsoleWarning.psm1',
'.\lib\core\tools\Read-IcingaFileContent.psm1'
'.\lib\core\tools\Read-IcingaFileContent.psm1',
'.\lib\core\framework\Invoke-IcingaInternalServiceCall.psm1',
'.\lib\core\framework\Get-IcingaFrameworkApiChecks.psm1',
'.\lib\daemon\Get-IcingaBackgroundDaemons.psm1',
'.\lib\webserver\Enable-IcingaUntrustedCertificateValidation.psm1',
'.\lib\core\logging\Write-IcingaEventMessage.psm1',
'.\lib\icinga\plugin\Exit-IcingaExecutePlugin.psm1',
'.\lib\icinga\exception\Exit-IcingaPluginNotInstalled.psm1',
'.\lib\icinga\exception\Exit-IcingaThrowException.psm1',
'.\lib\web\Disable-IcingaProgressPreference.psm1',
'.\lib\core\tools\New-IcingaNewLine.psm1',
'.\lib\core\logging\Write-IcingaConsolePlain.psm1',
'.\lib\core\tools\Test-IcingaFunction.psm1'
)
FunctionsToExport = @(
'Use-Icinga',
Expand All @@ -39,7 +51,19 @@
'Write-IcingaConsoleOutput',
'Write-IcingaConsoleNotice',
'Write-IcingaConsoleWarning',
'Read-IcingaFileContent'
'Read-IcingaFileContent',
'Invoke-IcingaInternalServiceCall',
'Get-IcingaFrameworkApiChecks',
'Get-IcingaBackgroundDaemons',
'Enable-IcingaUntrustedCertificateValidation',
'Write-IcingaEventMessage',
'Exit-IcingaExecutePlugin',
'Exit-IcingaPluginNotInstalled',
'Exit-IcingaThrowException',
'Disable-IcingaProgressPreference',
'New-IcingaNewLine',
'Write-IcingaConsolePlain',
'Test-IcingaFunction'
)
CmdletsToExport = @('*')
VariablesToExport = '*'
Expand Down
15 changes: 13 additions & 2 deletions icinga-powershell-framework.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,21 @@ function Use-Icinga()
param(
[switch]$LibOnly = $FALSE,
[switch]$Daemon = $FALSE,
[switch]$DebugMode = $FALSE
[switch]$DebugMode = $FALSE,
[switch]$Minimal = $FALSE
);

Disable-IcingaProgressPreference;

if ($Minimal) {
# If we load the minimal Framework files, we have to ensure our enums are loaded
Import-Module ([string]::Format('{0}\lib\icinga\exception\Icinga_IcingaExceptionEnums.psm1', $PSScriptRoot)) -Global;
Import-Module ([string]::Format('{0}\lib\icinga\enums\Icinga_IcingaEnums.psm1', $PSScriptRoot)) -Global;
Import-Module ([string]::Format('{0}\lib\core\logging\Icinga_EventLog_Enums.psm1', $PSScriptRoot)) -Global;

return;
}

# Ensure we autoload the Icinga Plugin collection, provided by the external
# module 'icinga-powershell-plugins'
if (Get-Command 'Use-IcingaPlugins' -ErrorAction SilentlyContinue) {
Expand Down Expand Up @@ -61,7 +73,6 @@ function Use-Icinga()
}
}
New-IcingaPerformanceCounterCache;
Disable-IcingaProgressPreference;

# Enable DebugMode in case it is enabled in our config
if (Get-IcingaFrameworkDebugMode) {
Expand Down
53 changes: 53 additions & 0 deletions lib/core/framework/Add-IcingaFrameworkApiCommand.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<#
.SYNOPSIS
Adds a Cmdlet to an REST-Api endpoint which is either whitelisted or blacklisted.
Whitelisted Cmdlets can be executed over API endpoints, blacklisted not.
Use '*' for wildcard matches
.DESCRIPTION
Adds a Cmdlet to an REST-Api endpoint which is either whitelisted or blacklisted.
Whitelisted Cmdlets can be executed over API endpoints, blacklisted not.
Use '*' for wildcard matches
.FUNCTIONALITY
Enables or disables Cmdlets for REST-Api endpoints
.EXAMPLE
PS>Add-IcingaFrameworkApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'checker';
.EXAMPLE
PS>Add-IcingaFrameworkApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'checker' -Blacklist;
.LINK
https://github.com/Icinga/icinga-powershell-framework
#>

function Add-IcingaFrameworkApiCommand()
{
param (
[string]$Command = '',
[string]$Endpoint = '',
[switch]$Blacklist = $FALSE
);

if ([string]::IsNullOrEmpty($Command) -Or [string]::IsNullOrEmpty($Endpoint)) {
return;
}

$Commands = $null;
$ConfigPath = ([string]::Format('Framework.RESTApiCommands.{0}.Whitelist', $Endpoint));
[array]$Values = @();

if ($Blacklist) {
$ConfigPath = ([string]::Format('Framework.RESTApiCommands.{0}.Blacklist', $Endpoint));
}

$Commands = Get-IcingaPowerShellConfig -Path $ConfigPath;

if ((Test-IcingaPowerShellConfigItem -ConfigObject $Commands -ConfigKey $Command)) {
return;
}

if ($null -ne $Commands) {
$Values = $Commands;
}

$Values += $Command;

Set-IcingaPowerShellConfig -Path $ConfigPath -Value $Values;
}
19 changes: 19 additions & 0 deletions lib/core/framework/Disable-IcingaFrameworkApiChecks.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<#
.SYNOPSIS
Disables the feature to forward all executed checks to an internal
installed API to run them within a daemon
.DESCRIPTION
Disables the feature to forward all executed checks to an internal
installed API to run them within a daemon
.FUNCTIONALITY
Disables the Icinga for Windows Api checks forwarded
.EXAMPLE
PS>Disable-IcingaFrameworkApiChecks;
.LINK
https://github.com/Icinga/icinga-powershell-framework
#>

function Disable-IcingaFrameworkApiChecks()
{
Set-IcingaPowerShellConfig -Path 'Framework.Experimental.UseApiChecks' -Value $FALSE;
}
21 changes: 21 additions & 0 deletions lib/core/framework/Enable-IcingaFrameworkApiChecks.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<#
.SYNOPSIS
Enables the feature to forward all executed checks to an internal
installed API to run them within a daemon
.DESCRIPTION
Enables the feature to forward all executed checks to an internal
installed API to run them within a daemon
.FUNCTIONALITY
Enables the Icinga for Windows Api checks forwarded
.EXAMPLE
PS>Enable-IcingaFrameworkApiChecks;
.LINK
https://github.com/Icinga/icinga-powershell-framework
#>

function Enable-IcingaFrameworkApiChecks()
{
Set-IcingaPowerShellConfig -Path 'Framework.Experimental.UseApiChecks' -Value $TRUE;

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'";
}
28 changes: 28 additions & 0 deletions lib/core/framework/Get-IcingaFrameworkApiChecks.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<#
.SYNOPSIS
Fetches the current enable/disable state of the feature
for executing checks of the internal REST-Api
.DESCRIPTION
Fetches the current enable/disable state of the feature
for executing checks of the internal REST-Api
.FUNCTIONALITY
Get the current API check execution configuration of the
Icinga PowerShell Framework
.EXAMPLE
PS>Get-IcingaFrameworkApiChecks;
.LINK
https://github.com/Icinga/icinga-powershell-framework
.OUTPUTS
System.Boolean
#>

function Get-IcingaFrameworkApiChecks()
{
$CodeCaching = Get-IcingaPowerShellConfig -Path 'Framework.Experimental.UseApiChecks';

if ($null -eq $CodeCaching) {
return $FALSE;
}

return $CodeCaching;
}
121 changes: 121 additions & 0 deletions lib/core/framework/Invoke-IcingaInternalServiceCall.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
function Invoke-IcingaInternalServiceCall()
{
param (
[string]$Command = '',
[array]$Arguments = @()
);

# If our Framework is running as daemon, never call our api
if ($global:IcingaDaemonData.FrameworkRunningAsDaemon) {
return;
}

# If the API forward feature is disabled, do nothing
if ((Get-IcingaFrameworkApiChecks) -eq $FALSE) {
return;
}

# Test our Icinga for Windows service. If the service is not installed or not running, execute the plugin locally
$IcingaForWindowsService = (Get-Service 'icingapowershell' -ErrorAction SilentlyContinue);

if ($null -eq $IcingaForWindowsService -Or $IcingaForWindowsService.Status -ne 'Running') {
return;
}

# In case the REST-Api module ist not configured, do nothing
$BackgroundDaemons = Get-IcingaBackgroundDaemons;

if ($null -eq $BackgroundDaemons -Or $BackgroundDaemons.ContainsKey('Start-IcingaWindowsRESTApi') -eq $FALSE) {
return;
}

# If neither 'icinga-powershell-restapi' or 'icinga-powershell-apichecks' is installed, execute the plugin locally
if ((Test-IcingaFunction 'Invoke-IcingaApiChecksRESTCall') -eq $FALSE -Or (Test-IcingaFunction 'Start-IcingaWindowsRESTApi') -eq $FALSE) {
return;
}

$RestApiPort = 5668;
[int]$Timeout = 30;
$Daemon = $BackgroundDaemons['Start-IcingaWindowsRESTApi'];

# Fetch our deamon configuration
if ($Daemon.ContainsKey('-Port')) {
$RestApiPort = $Daemon['-Port'];
} elseif ($Daemon.ContainsKey('Port')) {
$RestApiPort = $Daemon['Port'];
}
if ($Daemon.ContainsKey('-Timeout')) {
$Timeout = $Daemon['-Timeout'];
} elseif ($Daemon.ContainsKey('Timeout')) {
$Timeout = $Daemon['Timeout'];
}

Enable-IcingaUntrustedCertificateValidation -SuppressMessages;

[hashtable]$CommandArguments = @{ };
[hashtable]$DebugArguments = @{ };
[hashtable]$ConvertedArgs = @{ };
[int]$ArgumentIndex = 0;

# Resolve our array arguments provided by $args and build proper check arguments
while ($ArgumentIndex -lt $Arguments.Count) {
$Value = $Arguments[$ArgumentIndex];
[string]$Argument = [string]$Value;
$ArgumentValue = $null;

if ($Value[0] -eq '-') {
if (($ArgumentIndex + 1) -lt $Arguments.Count) {
[string]$NextValue = $Arguments[$ArgumentIndex + 1];
if ($NextValue[0] -eq '-') {
$ArgumentValue = $TRUE;
} else {
$ArgumentValue = $Arguments[$ArgumentIndex + 1];
}
} else {
$ArgumentValue = $TRUE;
}
} else {
$ArgumentIndex += 1;
continue;
}

$Argument = $Argument.Replace('-', '');

$ConvertedArgs.Add($Argument, $ArgumentValue);
$ArgumentIndex += 1;
}

# Now queue the check inside our REST-Api
try {
$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;
} catch {
# Something went wrong -> fallback to local execution
$ExMsg = $_.Exception.message;
# Fallback to execute plugin locally
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects $ExMsg, $Command, $DebugArguments;
return;
}

# Resolve our result from the API
$IcingaResult = ConvertFrom-Json -InputObject $ApiResult;
$IcingaCR = '';

# In case we didn't receive a check result, fallback to local execution
if ($null -eq $IcingaResult.$Command.checkresult) {
Write-IcingaEventMessage -Namespace 'Framework' -EventId 1553 -Objects 'The check result for the executed command was empty', $Command, $DebugArguments;
return;
}

$IcingaCR = ($IcingaResult.$Command.checkresult.Replace("`r`n", "`n"));

if ($IcingaResult.$Command.perfdata.Count -ne 0) {
$IcingaCR += ' | ';
foreach ($perfdata in $IcingaResult.$Command.perfdata) {
$IcingaCR += $perfdata;
}
}

# Print our response and exit with the provide exit code
Write-IcingaConsolePlain $IcingaCR;
exit $IcingaResult.$Command.exitcode;
}
49 changes: 49 additions & 0 deletions lib/core/framework/Remove-IcingaFrameworkApiCommand.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<#
.SYNOPSIS
Removes a Cmdlet from an REST-Api endpoints whitelist or blacklist.
.DESCRIPTION
Removes a Cmdlet from an REST-Api endpoints whitelist or blacklist.
.FUNCTIONALITY
Removes Cmdlets for REST-Api endpoints
.EXAMPLE
PS>Remove-IcingaFrameworkApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'checker';
.EXAMPLE
PS>Add-IcingaFrameworkApiCommand -Command 'Invoke-IcingaCheck*' -Endpoint 'checker' -Blacklist;
.LINK
https://github.com/Icinga/icinga-powershell-framework
#>

function Remove-IcingaFrameworkApiCommand()
{
param (
[string]$Command = '',
[string]$Endpoint = '',
[switch]$Blacklist = $FALSE
);

if ([string]::IsNullOrEmpty($Command) -Or [string]::IsNullOrEmpty($Endpoint)) {
return;
}

$Commands = $null;
$ConfigPath = ([string]::Format('Framework.RESTApiCommands.{0}.Whitelist', $Endpoint));
[array]$Values = @();

if ($Blacklist) {
$ConfigPath = ([string]::Format('Framework.RESTApiCommands.{0}.Blacklist', $Endpoint));
}

$Commands = Get-IcingaPowerShellConfig -Path $ConfigPath;

if ($null -eq $Commands) {
return;
}

foreach ($element in $Commands) {
if ($element.ToLower() -ne $Command.ToLower()) {
$Values += $element;
}
}

Set-IcingaPowerShellConfig -Path $ConfigPath -Value $Values;
}
Loading