diff --git a/core/config.ps1 b/core/config.ps1 deleted file mode 100644 index 0f80ab4..0000000 --- a/core/config.ps1 +++ /dev/null @@ -1,142 +0,0 @@ -param( - [string]$AddKey = '', - [Object]$AddValue = '', - [string]$GetConfig = '', - [string]$RemoveConfig = '', - [boolean]$ListConfig = $FALSE, - [boolean]$Reload = $FALSE -); - -function ClassConfig() -{ - param( - [string]$AddKey = '', - [Object]$AddValue = '', - [string]$GetConfig = '', - [string]$RemoveConfig = '', - [boolean]$ListConfig = $FALSE, - [boolean]$Reload = $FALSE - ); - - $instance = New-Object -TypeName PSObject; - - $instance | Add-Member -membertype NoteProperty -name 'ConfigDirectory' -value (Join-Path $Icinga2.App.RootPath -ChildPath 'agent\config'); - $instance | Add-Member -membertype NoteProperty -name 'ConfigFile' -value (Join-Path $instance.ConfigDirectory -ChildPath 'config.conf'); - - $instance | Add-Member -membertype ScriptMethod -name 'Init' -value { - if ($ListConfig) { - return $this.DumpConfig(); - } - - if ($Reload) { - return $this.ReloadConfig(); - } - - if ([string]::IsNullOrEmpty($GetConfig) -eq $FALSE) { - return $this.GetAttribute(); - } - - if ([string]::IsNullOrEmpty($AddKey) -eq $FALSE) { - return $this.SetAttribute(); - } - - if ([string]::IsNullOrEmpty($RemoveConfig) -eq $FALSE) { - return $this.RemoveAttribute(); - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - '{ Invalid or insufficient arguments specified. }' - ); - return 1; - } - - $instance | Add-Member -membertype ScriptMethod -name 'ReloadConfig' -value { - $Icinga2.Config = & (Join-Path $Icinga2.App.RootPath -ChildPath '\core\include\Config.ps1'); - } - - $instance | Add-Member -membertype ScriptMethod -name 'WriteConfig' -value { - If ((Test-Path ($this.ConfigDirectory)) -eq $FALSE) { - $Icinga2.Log.WriteConsole( - $Icinga2.Enums.LogState.Warning, - 'Config Directory is not present. Please run "Icinga-Setup" for the base installation' - ); - return 1; - } - $config = ConvertTo-Json $Icinga2.Config -Depth 100; - [System.IO.File]::WriteAllText($this.ConfigFile, $config); - return 0; - } - - $instance | Add-Member -membertype ScriptMethod -name 'DumpConfig' -value { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - ([string]::Format('Config location: {0}', $this.ConfigFile)) - ); - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - $Icinga2.Config - ); - return 0; - } - - $instance | Add-Member -membertype ScriptMethod -name 'GetAttribute' -value { - return $Icinga2.Config.$GetConfig; - } - - $instance | Add-Member -membertype ScriptMethod -name 'SetAttribute' -value { - $value = $AddValue; - - if ([string]::IsNullOrEmpty($AddValue)) { - $value = $null; - } - - if ([bool]($Icinga2.Config.PSobject.Properties.Name -eq $AddKey) -eq $FALSE) { - $Icinga2.Config | Add-Member -membertype NoteProperty -name $AddKey -value $value; - } else { - $Icinga2.Config.$AddKey = $value; - } - - if ($this.WriteConfig() -eq 0) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - ([string]::Format('{0} Set config attribute "{1}" to "{2}. {3}', '{', $AddKey, $value, '}')) - ); - return 0; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - ([string]::Format('{0} Unable to write config file to disk. Failed to update attribute "{1}" to "{2}. {3}', '{', $AddKey, $value, '}')) - ); - return 1; - } - - $instance | Add-Member -membertype ScriptMethod -name 'RemoveAttribute' -value { - if ([bool]($Icinga2.Config.PSobject.Properties.Name -eq $RemoveConfig) -eq $TRUE) { - $Icinga2.Config.PSobject.Members.Remove($RemoveConfig); - if ($this.WriteConfig() -eq 0) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - ([string]::Format('{0} Successfully removed config attribute "{1}" {2}', '{', $RemoveConfig, '}')) - ); - return 0; - } - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - ([string]::Format('{0} Config attribute "{1}" was removed, but storing the new config file failed. {2}', '{', $RemoveConfig, '}')) - ); - return 1; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - ([string]::Format('{0} Unable to remove attribute "{1}". Attribute not found {2}', '{', $RemoveConfig, '}')) - ); - return 1; - } - - return $instance.Init(); -} - -return ClassConfig -AddKey $AddKey -AddValue $AddValue -GetConfig $GetConfig -RemoveConfig $RemoveConfig -ListConfig $ListConfig -Reload $Reload; \ No newline at end of file diff --git a/core/include/APIResponse.ps1 b/core/include/APIResponse.ps1 deleted file mode 100644 index 12137f3..0000000 --- a/core/include/APIResponse.ps1 +++ /dev/null @@ -1,100 +0,0 @@ -$APIResponse = New-Object -TypeName PSObject; - -$APIResponse | Add-Member -membertype NoteProperty -name 'static' -value $FALSE; -$APIResponse | Add-Member -membertype NoteProperty -name 'statuscode' -value 200; -$APIResponse | Add-Member -membertype NoteProperty -name 'message' -value ''; -$APIResponse | Add-Member -membertype NoteProperty -name 'content' -value $null; -$APIResponse | Add-Member -membertype NoteProperty -name 'authheader' -value ''; - -$APIResponse | Add-Member -membertype ScriptMethod -name 'setContent' -value { - param([object]$content); - - $this.content = $content; -} - -$APIResponse | Add-Member -membertype ScriptMethod -name 'CustomBadRequest' -value { - param([string]$message); - - $this.statuscode = 400; - $this.message = $message; -} - -$APIResponse | Add-Member -membertype ScriptMethod -name 'InternalServerError' -value { - $this.statuscode = 500; - $this.message = 'An internal server error occured while parsing your request.'; -} - -$APIResponse | Add-Member -membertype ScriptMethod -name 'HTTPSRequired' -value { - $this.statuscode = 403; - $this.message = 'This API only supports connections over HTTPS.'; -} - -$APIResponse | Add-Member -membertype ScriptMethod -name 'AuthenticationRequired' -value { - $this.statuscode = 401; - $this.message = 'You require to login in order to access this ressource.'; - $this.authheader = [string]::Format( - 'WWW-Authenticate: Basic realm="Icinga Windows Daemon"{0}', - "`r`n" - ); -} - -$APIResponse | Add-Member -membertype ScriptMethod -name 'CompileMessage' -value { - # If our message is empty, do nothing - if ([string]::IsNullOrEmpty($this.message)) { - return; - } - - # In case we assigned custom content, do not override this content - if ($this.content -ne $null) { - return; - } - - $this.content = @{ - response = $this.statuscode; - message = $this.message; - }; -} - -$APIResponse | Add-Member -membertype ScriptMethod -name 'Compile' -value { - - $this.CompileMessage(); - - [string]$ContentLength = ''; - [string]$HTMLContent = ''; - if ($this.content -ne $null) { - $json = ConvertTo-Json $this.content -Depth 100 -Compress; - $bytes = [System.Text.Encoding]::UTF8.GetBytes($json); - $HTMLContent = [System.Text.Encoding]::UTF8.GetString($bytes); - if ($bytes.Length -gt 0) { - $ContentLength = [string]::Format( - 'Content-Length: {0}{1}', - $bytes.Length, - "`r`n" - ); - } - } - - return -Join( - [string]::Format( - 'HTTP/1.1 {0} {1}{2}', - $this.statuscode, - $Icinga2.Enums.HttpStatusCodes.$this.statuscode, - "`r`n" - ), - [string]::Format( - 'Server: {0}{1}', - (Get-WmiObject Win32_ComputerSystem).Name, - "`r`n" - ), - [string]::Format( - 'Content-Type: application/json{0}', - "`r`n" - ), - $this.authheader, - $ContentLength, - "`r`n", - $HTMLContent - ); -} - -return $APIResponse; \ No newline at end of file diff --git a/core/include/App.ps1 b/core/include/App.ps1 deleted file mode 100644 index 8bc5082..0000000 --- a/core/include/App.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -# App configuration -$App = @{ - LogSeverity = [PSCustomObject]@{ - PSTypeName = "LogSeverity" - Info = 0 - Warning = 1 - Error = 2 - Exception = 3 - Debug = 4 - }; - RootPath = $_InternalTempVariables.RootPath; - ModuleName = $_InternalTempVariables.ModuleName; -} - -return $App; \ No newline at end of file diff --git a/core/include/Checker.ps1 b/core/include/Checker.ps1 deleted file mode 100644 index 97720eb..0000000 --- a/core/include/Checker.ps1 +++ /dev/null @@ -1,105 +0,0 @@ -$Checker = New-Object -TypeName PSObject; - -$Checker | Add-Member -membertype NoteProperty -name 'os' -value ''; -$Checker | Add-Member -membertype NoteProperty -name 'version' -value ''; -$Checker | Add-Member -membertype NoteProperty -name 'fqdn' -value ''; -$Checker | Add-Member -membertype NoteProperty -name 'bind' -value 'wdt'; -$Checker | Add-Member -membertype NoteProperty -name 'time_offset' -value 0; - -$Checker | Add-Member -membertype ScriptMethod -name 'Start' -value { - - $Icinga2.PidManager.StopProcessByBind($this.bind); - - Start-Sleep 1; - - $Icinga2.PidManager.CreatePidFile($this.bind); - - $WindowsInformations = Get-CimInstance Win32_OperatingSystem; - $this.version = $WindowsInformations.CimInstanceProperties['Version'].Value; - $this.os = $WindowsInformations.CimInstanceProperties['Caption'].Value; - $this.fqdn = [System.Net.Dns]::GetHostEntry('localhost').HostName; - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Starting checker component of module.' - ); - - $Icinga2.ClientProtocol.setFQDN($this.fqdn); - $Icinga2.Cache.Checker.ModuleScheduler = @{ }; - - while($true) { - - $StopWatchHandler = [System.Diagnostics.StopWatch]::StartNew() - $this.ScheduleWindowsHello($FALSE); - $this.UpdateModuleTimer(); - $Icinga2.ClientJobs.ParseJobResults(); - - # This part will help us to keep the gap between module execution as low as possible - # We will check how many seconds have been passed while the modules were executed - # This value will then be added to our module timings, ensuring that in general - # they will become executed right on time - $StopWatchHandler.Stop(); - $this.time_offset = [math]::Round($StopWatchHandler.Elapsed.TotalSeconds, 0); - $Icinga2.ClientJobs.AddTicks($this.time_offset); - - Start-Sleep -Seconds 1; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Stopping checker component of module.' - ); -} - -$Checker | Add-Member -membertype ScriptMethod -name 'UpdateModuleTimer' -value { - if ($Icinga2.Cache.Checker.ModuleConfig -eq $null) { - return; - } - - foreach ($module in $Icinga2.Cache.Checker.ModuleConfig.Keys) { - if ($Icinga2.Cache.Checker.ModuleScheduler.ContainsKey($module) -eq $FALSE) { - $Icinga2.Cache.Checker.ModuleScheduler.Add($module, 0); - } else { - $Icinga2.Cache.Checker.ModuleScheduler[$module] += (1 + $this.time_offset); - - if ($Icinga2.Cache.Checker.ModuleScheduler[$module] -ge $Icinga2.Cache.Checker.ModuleConfig[$module]) { - $Icinga2.Cache.Checker.ModuleScheduler[$module] = 0; - $this.ScheduleModuleJob($module); - } - } - } - $this.time_offset = 0; -} - -$Checker | Add-Member -membertype ScriptMethod -name 'ScheduleModuleJob' -value { - param([string]$module); - - $Icinga2.ClientJobs.ScheduleJob($module); -} - -$Checker | Add-Member -membertype ScriptMethod -name 'ScheduleWindowsHello' -value { - param([bool]$force); - $this.WriteLogOutput($Icinga2.ClientJobs.WindowsHello( - $this.os, - $this.fqdn, - $this.version, - $force - )); -} - -$Checker | Add-Member -membertype ScriptMethod -name 'WriteLogOutput' -value { - param($response); - - if ($response -ne $null) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - $response - ); - } -} - -$Checker | Add-Member -membertype ScriptMethod -name 'Stop' -value { - $Icinga2.PidManager.StopProcessByBind($this.bind); -} - -return $Checker; \ No newline at end of file diff --git a/core/include/ClientJobs.ps1 b/core/include/ClientJobs.ps1 deleted file mode 100644 index d54c084..0000000 --- a/core/include/ClientJobs.ps1 +++ /dev/null @@ -1,292 +0,0 @@ -$ClientJobs = New-Object -TypeName PSObject; - -$ClientJobs | Add-Member -membertype NoteProperty -name 'hello_counter' -value 0; -$ClientJobs | Add-Member -membertype NoteProperty -name 'module_scheduler' -value @( ); -$ClientJobs | Add-Member -membertype NoteProperty -name 'module_output' -value $null; - -$ClientJobs | Add-Member -membertype ScriptMethod -name 'AddTicks' -value { - param([int]$ticks); - - $this.hello_counter += $ticks; -} - -$ClientJobs | Add-Member -membertype ScriptMethod -name 'WindowsHello' -value { - param([string]$os, [string]$fqdn, [string]$version, [bool]$force); - - if ($this.hello_counter -ge 30) { - $this.hello_counter = 0; - } - - if ($this.hello_counter -eq 0 -Or $force -eq $TRUE) { - [hashtable]$hello = @{ - 'os' = $os; - 'fqdn' = $fqdn; - 'version' = $version; - 'port' = $Icinga2.Config.'tcp.socket.port'; - }; - - [string]$token = $this.getAuthToken(); - if ([string]::isNullOrEmpty($token) -eq $FALSE) { - $hello.Add( - 'modules', - (New-Icinga-Monitoring -ListModules) - ) - } - - $response = $Icinga2.ClientProtocol.NewRequest( - @('X-Windows-Hello: 1'), - ($hello | ConvertTo-Json -Depth 2 -Compress), - $Icinga2.Config.'checker.server.host', - $token - ); - - $this.hello_counter += 1; - - if ($response -eq $null) { - return $null; - } - - try { - $json = $response | ConvertFrom-Json; - $Icinga2.Cache.Checker.Authenticated = $TRUE; - $Icinga2.ClientProtocol.parseWindowsHelloResponse($json); - return $null; - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - $_.Exception.Message - ); - } - - $Icinga2.Cache.Checker.Authenticated = $FALSE; - - return $response; - } - - $this.hello_counter += 1; - - return $null; -} - -$ClientJobs | Add-Member -membertype ScriptMethod -name 'getAuthToken' -value { - [string]$token = ''; - if ($Icinga2.Cache.Checker.Authenticated -eq $TRUE -And $Icinga2.Cache.Checker.AuthToken -ne $null) { - $token = [string]::Format('?token={0}', $Icinga2.Cache.Checker.AuthToken); - } - - return $token; -} - -$ClientJobs | Add-Member -membertype ScriptMethod -name 'ScheduleJob' -value { - param([string]$module); - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Scheduling execution check for module: {0}', - $module - ) - ); - - $this.module_scheduler += $module; - return; - - # This would be the best, but will cause too much overhead and system load - Start-Job -Name $module -ScriptBlock { - return New-Icinga-Monitoring -include $args[0]; - } -ArgumentList $module; -} - -$ClientJobs | Add-Member -membertype ScriptMethod -name 'ParseJobResults' -value { - - if ($this.module_scheduler.Count -eq 0) { - return; - } - - $moduleOutput = New-Icinga-Monitoring -Include ($this.module_scheduler) -Config $Icinga2.Cache.Checker.ModuleArguments; - - [string]$token = $this.getAuthToken(); - - [string]$modules = $this.module_scheduler -Join "," - - if ([string]::isNullOrEmpty($token) -eq $TRUE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - 'Unable to send job results to server. No auth token is specified' - ); - $this.FlushModuleCache($TRUE); - return; - } - - if ($Icinga2.ClientProtocol.GetConnectionState($Icinga2.Config.'checker.server.host') -eq $FALSE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'Module results for "{0}" will not be send to {1}. A previous connection failed. Re-Trying in some seconds...', - $modules, - $Icinga2.Config.'checker.server.host' - ) - ); - $this.FlushModuleCache($TRUE); - return; - } - - $this.module_output = ($moduleOutput | ConvertTo-Json -Depth 100 -Compress); - - $response = $Icinga2.ClientProtocol.NewRequest( - @('X-Windows-Result: 1'), - $this.module_output, - $Icinga2.Config.'checker.server.host', - [string]::Format( - '{0}&results=1', - $token - ) - ); - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Send modules {0} results to server. Received result: {1}', - $modules, - $response - ) - ); - - $this.ParseResponse($response); - return; - - # This would be the best, but will cause too much overhead and system load - [hashtable]$moduleOutput = @{ }; - - Get-Job -State Completed | Where-Object { - $moduleOutput.Add( - $_.Name, - (Receive-Job -Id $_.Id) - ); - Remove-Job -Id $_.Id; - }; - - [string]$token = $this.getAuthToken(); - - if ([string]::isNullOrEmpty($token) -eq $TRUE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - 'Unable to send job results to server. No auth token is specified' - ); - return; - } - - if ($Icinga2.ClientProtocol.GetConnectionState($Icinga2.Config.'checker.server.host') -eq $FALSE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'Module results for "{0}" will not be send to {1}. A previous connection failed. Re-Trying in some seconds...', - $modules, - $Icinga2.Config.'checker.server.host' - ) - ); - return; - } - - $response = $Icinga2.ClientProtocol.NewRequest( - @('X-Windows-Result: 1'), - ($moduleOutput | ConvertTo-Json -Depth 100 -Compress), - $Icinga2.Config.'checker.server.host', - [string]::Format( - '{0}&results=1', - $token - ) - ); - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Send modules {0} results to server. Received result: {1}', - ($moduleOutput | Out-String), - $response - ) - ); -} - -$ClientJobs | Add-Member -membertype ScriptMethod -name 'FlushModuleCache' -value { - param([bool]$flush); - - if ($flush -eq $TRUE) { - foreach($module in $this.module_scheduler) { - $Icinga2.Utils.Modules.FlushModuleCache($module); - } - } - - $this.module_scheduler = @(); -} - -$ClientJobs | Add-Member -membertype ScriptMethod -name 'ParseResponse' -value { - param([string]$response); - - if ([string]::IsNullOrEmpty($response) -eq $TRUE) { - $this.FlushModuleCache($TRUE); - return; - } - - # Re-Try to send the informations once in case we are not authorized - if ($response -eq '401') { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - 'Received Unauthorized (401) response. Trying to re-send results after requesting permission.' - ); - $Icinga2.Checker.ScheduleWindowsHello($TRUE); - [string]$token = $this.getAuthToken(); - $response = $Icinga2.ClientProtocol.NewRequest( - @('X-Windows-Result: 1'), - $this.module_output, - $Icinga2.Config.'checker.server.host', - [string]::Format( - '{0}&results=1', - $token - ) - ); - - if ([string]::IsNullOrEmpty($response) -eq $TRUE) { - $this.FlushModuleCache($TRUE); - return; - } - } - - try { - $json = ConvertFrom-Json $response -ErrorAction Stop -WarningAction Stop; - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - [string]::Format( - 'Received invalid JSON response from request: {0}', - $response - ) - ); - $this.FlushModuleCache($TRUE); - return; - } - - try { - if ($json.response -ne $null) { - if ($json.response -ne 200) { - $this.FlushModuleCache($TRUE); - return; - } - } - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - [string]::Format( - 'Failed to properly parse JSON response: {0} . Exception Message: {1}', - $response, - $_.Exception.Message - ) - ); - $this.FlushModuleCache($TRUE); - return; - } - - $this.FlushModuleCache($FALSE); -} - -return $ClientJobs; \ No newline at end of file diff --git a/core/include/ClientProtocol.ps1 b/core/include/ClientProtocol.ps1 deleted file mode 100644 index e0fbbe3..0000000 --- a/core/include/ClientProtocol.ps1 +++ /dev/null @@ -1,225 +0,0 @@ -$ClientProtocol = New-Object -TypeName PSObject; - -$ClientProtocol | Add-Member -membertype NoteProperty -name 'fqdn' -value ''; - -$ClientProtocol | Add-Member -membertype ScriptMethod -name 'setFQDN' -value { - param([string]$fqdn); - - $this.fqdn = $fqdn; -} - -$ClientProtocol | Add-Member -membertype ScriptMethod -name 'SetConnectionState' -value { - param([string]$remoteAddress, [bool]$reachable); - - if ($Icinga2.Cache.Checker.RemoteServices -eq $null) { - $Icinga2.Cache.Checker.RemoteServices = @{ }; - } - - if ($Icinga2.Cache.Checker.RemoteServices.ContainsKey($remoteAddress) -eq $FALSE) { - $Icinga2.Cache.Checker.RemoteServices.Add($remoteAddress, $reachable); - return; - } - - $Icinga2.Cache.Checker.RemoteServices[$remoteAddress] = $reachable; -} - -$ClientProtocol | Add-Member -membertype ScriptMethod -name 'GetConnectionState' -value { - param([string]$remoteAddress); - - if ($Icinga2.Cache.Checker.RemoteServices.ContainsKey($remoteAddress) -eq $FALSE) { - return $TRUE; - } - - return $Icinga2.Cache.Checker.RemoteServices[$remoteAddress]; -} - -$ClientProtocol | Add-Member -membertype ScriptMethod -name 'NewRequest' -value { - param([array]$headers, [string]$content, [string]$remoteAddress, [string]$url); - - $url = [string]::Format( - '{0}{1}', - $remoteAddress, - $url - ); - - $httpRequest = [System.Net.HttpWebRequest]::Create( - $url - ); - $httpRequest.Method = 'POST'; - $httpRequest.Accept = 'application/json'; - $httpRequest.ContentType = 'application/json'; - $httpRequest.Headers.Add( - [string]::Format( - 'X-Windows-CheckResult: {0}', - $this.fqdn - ) - ); - - # Add possible custom header - foreach ($header in $headers) { - $httpRequest.Headers.Add($header); - } - $httpRequest.TimeOut = 60000; - - # If we are using self-signed certificates for example, the HTTP request will - # fail caused by the SSL certificate. With this we can allow even faulty - # certificates. This should be used with caution - if (-Not $Icinga2.Config.'checker.ssl.verify') { - [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } - } - - try { - # Only send data in case we want to send some data - if ($content -ne '') { - $transmitBytes = [System.Text.Encoding]::UTF8.GetBytes($content); - $httpRequest.ContentLength = $transmitBytes.Length; - [System.IO.Stream]$httpOutput = [System.IO.Stream]$httpRequest.GetRequestStream() - $httpOutput.Write($transmitBytes, 0, $transmitBytes.Length) - $httpOutput.Close() - } - } catch [System.Net.WebException] { - $this.SetConnectionState($remoteAddress, $FALSE); - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - [string]::Format('Exception while trying to connect to "{0}". Possible a connection error. Message: {1}', - $url, - $_.Exception.Message - ) - ); - return $null; - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - $_.Exception.Message - ); - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - $_.Exception.StackTrace - ); - return $null; - } - - try { - - $this.SetConnectionState($remoteAddress, $TRUE); - return $this.readResponseStream($httpRequest.GetResponse()); - - } catch [System.Net.WebException] { - # Print an exception message and the possible body in case we received one - # to make troubleshooting easier - [string]$errorResponse = $this.readResponseStream($_.Exception.Response); - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - $_.Exception.Message - ); - if ($errorResponse -ne '') { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - $errorResponse - ); - } - - $exceptionMessage = $_.Exception.Response; - if ($exceptionMessage.StatusCode) { - return [int][System.Net.HttpStatusCode]$exceptionMessage.StatusCode; - } else { - return 900; - } - } - - return $null; -} - -$ClientProtocol | Add-Member -membertype ScriptMethod -name 'readResponseStream' -value { - param([System.Object]$response); - - try { - if ($response) { - $responseStream = $response.getResponseStream(); - $streamReader = New-Object IO.StreamReader($responseStream); - $result = $streamReader.ReadToEnd(); - $response.close() - $streamReader.close() - - return $result; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - 'The received response from the remote server is NULL. This might be caused by SSL errors or wrong Webserver configuration.' - ); - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - $_.Exception.Message - ); - } - - return $null; -} - -$ClientProtocol | Add-Member -membertype ScriptMethod -name 'parseWindowsHelloResponse' -value { - param($json); - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Remote Server Output: {0}', - $json - ) - ); - - $Icinga2.Cache.Checker.AuthToken = $json.token; - if ($Icinga2.Cache.Checker.ModuleConfig -eq $null) { - $Icinga2.Cache.Checker.ModuleConfig = @{}; - } - - $Icinga2.Cache.Checker.ModuleArguments = $json.module_arguments; - - [hashtable]$activeModules = @{}; - - foreach ($module in $json.modules) { - if ($Icinga2.Cache.Checker.ModuleConfig.ContainsKey($module.name)) { - $Icinga2.Cache.Checker.ModuleConfig[$module.name] = $module.check_interval; - } else { - $Icinga2.Cache.Checker.ModuleConfig.Add($module.name, $module.check_interval); - } - $activeModules.Add($module.name, $TRUE); - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Adding module {0} with check intervall {1}', - $module.name, - $module.check_interval - ) - ); - } - - # We might have disabled some modules. Lets handle this by setting the - # execution timer to -1 - foreach ($module in $Icinga2.Cache.Checker.ModuleConfig.Keys) { - if ($activeModules.ContainsKey($module) -eq $FALSE) { - $activeModules.Add($module, $FALSE); - } - } - - # We require a second loop to ensure we won't crash because of a changed hashtable - foreach($module in $activeModules.Keys) { - if ($activeModules[$module] -eq $FALSE) { - if ($Icinga2.Cache.Checker.ModuleConfig.ContainsKey($module)) { - $Icinga2.Cache.Checker.ModuleConfig.Remove($module); - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Disabling module {0}', - $module - ) - ); - } - } - } -} - -return $ClientProtocol; \ No newline at end of file diff --git a/core/include/Config.ps1 b/core/include/Config.ps1 deleted file mode 100644 index 17266eb..0000000 --- a/core/include/Config.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -# Inetrnal variable to store the root directory path -[string]$RootDirectory = ''; - -# In case we load the module for the first time, this variable contains the root path -# of our module -if ($_InternalTempVariables -ne $null) { - $RootDirectory = $_InternalTempVariables.RootPath; -} else { - # In case we want to reload the configuration, we simply can access the namespace - # variable we already loaded - $RootDirectory = $Icinga2.App.RootPath; -} - -# Build the Config directory and file path -[string]$ConfigDirectory = (Join-Path $RootDirectory -ChildPath 'agent\config'); -[string]$ConfigFile = (Join-Path $ConfigDirectory -ChildPath 'config.conf'); - -# In case the config file does not exist, return an empty hashtable -if ((Test-Path ($ConfigFile)) -eq $FALSE) { - return ('{ }' | ConvertFrom-Json); -} - -# Return the content of the file as objects (config is stored as JSON) -return ([System.IO.File]::ReadAllText($ConfigFile) | ConvertFrom-Json); \ No newline at end of file diff --git a/core/include/Enums.ps1 b/core/include/Enums.ps1 deleted file mode 100644 index d97bdab..0000000 --- a/core/include/Enums.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -<# - # This script will provide 'Enums' we can use within our module to - # easier access constants and to maintain a better overview of the - # entire components - #> - -[hashtable]$LogState = @{ - Info = 0; - Warning = 1; - Error = 2; - Exception = 3; - Debug = 4; -}; - -[hashtable]$LogSeverity = @{ - 0 = 'Info'; - 1 = 'Warning'; - 2 = 'Error'; - 3 = 'Exception'; - 4 = 'Debug'; -}; - -[hashtable]$EventLogType = @{ - 0 = 'Information'; - 1 = 'Warning'; - 2 = 'Error'; - 3 = 'Error'; - 4 = 'Information'; -}; - -[hashtable]$LogColor = @{ - 0 = 'DarkGreen'; - 1 = 'Yellow'; - 2 = 'Red'; - 3 = 'DarkRed'; - 4 = 'Magenta'; -}; - -[hashtable]$ServiceStatus = @{ - 'NotInstalled' = 'The Icinga service for this module is not installed. Please run Install-Icinga to install the service.'; - 'Running' = 'The Icinga service is running.'; - 'Stopped' = 'The Icinga service is not running.'; - 'Starting' = 'The Icinga service is about to start.'; - 'Stopping' = 'The Icinga service is shutting down.'; -} - -[hashtable]$SCErrorCodes = @{ - 5 = 'Failed to execute Icinga 2 Service operation: Permission denied.'; - 1053 = 'Failed to start the Icinga 2 Service: The Service did not respond in time to the start or operation request.'; - 1056 = 'Failed to start the Icinga 2 Service: The Service is already running.'; - 1060 = 'Failed to apply action for Icinga 2 Service: The Service is not installed.'; - 1062 = 'Failed to stop the Icinga 2 Service: The Service is not running.'; - 1072 = 'Failed to uninstall the Icinga 2 Service: The Service is already marked for deletion.'; - 1073 = 'Failed to install the Icinga 2 Service: The Service is already installed.'; -}; - -[hashtable]$HttpStatusCodes = @{ - 200 = 'Ok'; - 400 = 'Bad Request'; - 401 = 'Unauthorized'; - 403 = 'Forbidden'; - 404 = 'Not Found' - 500 = 'Internal Server Error'; -}; - -<# - # Once we defined a new enum hashtable above, simply add it to this list - # to make it available within the entire module. - # - # Example usage: - # $Icinga2.Enums.LogState.Info - #> -[hashtable]$Enums = @{ - LogSeverity = $LogSeverity; - EventLogType = $EventLogType; - LogColor = $LogColor; - LogState = $LogState; - ServiceStatus = $ServiceStatus; - SCErrorCodes = $SCErrorCodes; - HttpStatusCodes = $HttpStatusCodes; -} - -return $Enums; \ No newline at end of file diff --git a/core/include/Log.ps1 b/core/include/Log.ps1 deleted file mode 100644 index 356aae0..0000000 --- a/core/include/Log.ps1 +++ /dev/null @@ -1,128 +0,0 @@ -<# - # Handle the entire logging process of the module by sending the events - # to console, the event log and if configured into an own log file. - # This entire script will return a 'function' handler, dealing with - # all events. - # To create log events, simply use the following example: - # - # $Icinga2.Log.Write($Icinga2.Enums.LogState.Info, 'This is a info message'); - #> - -$IcingaLogger = New-Object -TypeName PSObject; - -$IcingaLogger | Add-Member -membertype NoteProperty -name 'noconsole' -value $FALSE; - -$IcingaLogger | Add-Member -membertype ScriptMethod -name 'DisableConsole' -value { - $this.noconsole = $TRUE; -} - -$IcingaLogger | Add-Member -membertype ScriptMethod -name 'Write' -value { - param($Severity, [string]$Message); - - # Only write debug output if enabled - if ($Severity -eq $Icinga2.Enums.LogState.Debug -And $Icinga2.Config.'logger.debug' -eq $FALSE) { - return; - } - - [string]$SeverityToString = $this.GetSeverityAsString($Severity); - - # Format a timestamp to get to know the exact date and time. Example: 2017-13-07 22:09:13.263.263 - $timestamp = Get-Date -Format "yyyy-dd-MM HH:mm:ss.fff"; - [string]$LogMessage = [string]::Format('{0} [{1}]: {2}', $timestamp, $SeverityToString, $Message); - - $this.WriteConsole($Severity, $LogMessage); - $this.WriteEventLog($Severity, $Message); - $this.WriteLogFile($Severity, $LogMessage); -} - -$IcingaLogger | Add-Member -membertype ScriptMethod -name 'GetConsoleColorFromSeverity' -value { - param([int]$Severity); - - if ($Icinga2.Enums.LogColor.ContainsKey($Severity) -eq $FALSE) { - return 'White'; - } - - return $Icinga2.Enums.LogColor[$Severity]; -} - -$IcingaLogger | Add-Member -membertype ScriptMethod -name 'GetSeverityAsString' -value { - param([int]$Severity); - - if ($Icinga2.Enums.LogSeverity.ContainsKey($Severity) -eq $FALSE) { - return 'Undefined'; - } - - return $Icinga2.Enums.LogSeverity[$Severity]; -} - -$IcingaLogger | Add-Member -membertype ScriptMethod -name 'WriteLogFile' -value { - param([int]$Severity, [string]$Message); - - [string]$LogDirectory = $Icinga2.Config.'logger.directory'; - - if ([string]::IsNullOrEmpty($LogDirectory)) { - return; - } - - if (-Not (Test-Path $LogDirectory)) { - New-Item $LogDirectory -ItemType Directory | Out-Null; - - # Something went wrong while trying to create the directory - if (-Not (Test-Path $LogDirectory)) { - $this.WriteConsole($Icinga2.Enums.LogState.Error, - [string]::Format('Failed to create logfile directory at location "{0}"', $LogDirectory) - ) - return; - } - } - - [string]$LogFile = Join-Path $LogDirectory -ChildPath 'icinga2.log'; - - try { - $LogStream = New-Object System.IO.FileStream( - $LogFile, - [System.IO.FileMode]::Append, - [System.IO.FileAccess]::Write, - [IO.FileShare]::Read - ); - $LogWriter = New-Object System.IO.StreamWriter($LogStream); - $LogWriter.writeLine($Message); - } catch { - $this.WriteConsole($Icinga2.Enums.LogState.Error, - [string]::Format('Failed to write into logfile: "{0}"', $_.Exception.Message) - ) - } finally { - $LogWriter.Dispose(); - } -} - -$IcingaLogger | Add-Member -membertype ScriptMethod -name 'WriteEventLog' -value { - param([int]$Severity, [string]$Message); - - try { - Write-EventLog -LogName "Application" ` - -Source $Icinga2.Service.servicedisplayname ` - -EventID (1000 + $Severity) ` - -EntryType $Icinga2.Enums.EventLogType.$Severity ` - -Message $Message ` - -Category $Severity ` - -ErrorAction Stop; - } catch { - $this.WriteLogFile( - $Icinga2.Enums.LogState.Error, - $_.Exception.Message - ); - } -} - -$IcingaLogger | Add-Member -membertype ScriptMethod -name 'WriteConsole' -value { - param([int]$Severity, [string]$Message); - - if ($this.noconsole) { - return; - } - - Write-Host $Message -ForegroundColor ($this.GetConsoleColorFromSeverity($Severity)) -} - -return $IcingaLogger; \ No newline at end of file diff --git a/core/include/NetworkProtocol.ps1 b/core/include/NetworkProtocol.ps1 deleted file mode 100644 index 6b96296..0000000 --- a/core/include/NetworkProtocol.ps1 +++ /dev/null @@ -1,192 +0,0 @@ -$NetworkProtocol = New-Object -TypeName PSObject; - -$NetworkProtocol | Add-Member -membertype NoteProperty -name 'static' -value $FALSE; -$NetworkProtocol | Add-Member -membertype NoteProperty -name 'sslstream' -value $null; -$NetworkProtocol | Add-Member -membertype NoteProperty -name 'networkstream' -value $null; -$NetworkProtocol | Add-Member -membertype NoteProperty -name 'encrypted' -value $null; - -$NetworkProtocol | Add-Member -membertype ScriptMethod -name 'Create' -value { - param($Stream); - - $this.networkstream = $Stream; - $this.sslstream = $this.CreateSSLStream($Stream); -} - -$NetworkProtocol | Add-Member -membertype ScriptMethod -name 'CreateSSLStream' -value { - param($Stream); - - try { - $sslStream = New-Object System.Net.Security.SslStream( - $Stream, - $false - ) - $sslStream.AuthenticateAsServer( - $Icinga2.Cache.Certificates.Server, - 0, - [System.Security.Authentication.SslProtocols]::Tls, - 1 - ); - $sslStream.ReadTimeout = 2000; - $this.encrypted = $TRUE; - - return $sslStream; - } catch [System.IO.IOException] { - # Exceptions which occure when connecting from HTTP to this API, as we force HTTPS - # Use the client's non-ssl stream to inform the user about our forced - # HTTPS handling and set an internal variable to not send any data - # to our client over HTTP - $this.encrypted = $FALSE; - $Stream.ReadTimeout = 2000; - $Stream.WriteTimeout = 2000; - return $Stream; - } catch [System.NotSupportedException] { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - 'The used SSL certificate is not providing a linked private key and cannot be used as Server certificate' - ); - } catch { - # Handle every other error here - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - $_.Exception.Message - ); - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - $_.Exception - ); - } - - return $null; -} - -$NetworkProtocol | Add-Member -membertype ScriptMethod -name 'ReadMessage' -value { - param([int]$BytesToRead) - - # If we have no bytes to read, do nothing - if ($BytesToRead -eq 0) { - return $null; - } - - [string]$content = ''; - [SecureString]$SecureContent = $null; - [int]$TotalMessageSize = 0; - # Define our buffer size to ensure we read a certain - # amount of bytes each read attempt only - [int]$BufferSize = 1024; - # If we read the message and don't know if there is a content available - # we have to read until we reach EOF. In case we have the exact size - # of the content, we can read the possible rest - [bool]$IsSizeKnown = $FALSE; - - # This will allow us to read a fixed amount of data from our - # stream and terminate the operation afterwards - if ($BytesToRead -gt 0) { - $BufferSize = $BytesToRead; - $IsSizeKnown = $TRUE; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - 'Reading new message from TCP NetworkStream of Client' - ); - - # Handle errors while reading the SSL Stream - try { - # Read the stream as long as we receive data from it - while ($true) { - - # Create a new byte array with a fixed buffer size - [byte[]]$bytes = New-Object byte[] $BufferSize; - - # Read the actual data from the stream - $bytesRead = $this.sslstream.Read($bytes, 0, $bytes.Length); - $TotalMessageSize += $bytesRead; - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Reading {0} bytes from TCP connection', - $bytesRead - ) - ); - - # Build a output message from our received as string and perform - # possible required cleanup in addition - if ($bytesRead -ne 0) { - [string]$message = [System.Text.Encoding]::UTF8.GetString($bytes); - - # In case we receive a larger message, append the message content - # to our string value - $content = -Join( - $content, - $message - ); - - # Ensure our output string is always matching the correct length - # but only apply this in case we are unsure about the real length - if ($IsSizeKnown -eq $FALSE) { - $content = $content.Substring( - 0, - $TotalMessageSize - ); - } - - # EOF reached or the amount of bytes to read was known - # and we should abort here - if ($content.Contains("`r`n`r`n") -Or $IsSizeKnown) { - break; - } - } else { - break; - } - } - } catch { - # Might be good too remove this, as errors will occure very likely in case the SSLStream - # timed out after 2 seconds because no new data have been received. - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - [string]::Format( - 'Failed to read Message from stream: {0}', - $_.Exception.Message - ) - ); - - return $null; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Finished reading {0} bytes from current NetworkMessage', - $TotalMessageSize - ) - ); - - # In case we read no message from the stream, we should do nothing - if ($TotalMessageSize -eq 0) { - return $null; - } - - $SecureContent = $Icinga2.Utils.SecureString.ConvertTo($content); - $content = $null; - - return $SecureContent; -} - -$NetworkProtocol | Add-Member -membertype ScriptMethod -name 'WriteMessage' -value { - param($message); - - try { - $bytes = [System.Text.Encoding]::UTF8.GetBytes($message); - $Client.SendBufferSize = $bytes.Length; - $this.sslstream.Write($bytes, 0, $bytes.Length); - $this.sslstream.Flush(); - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - 'Failed to write message into data stream' - ); - } -} - -return $NetworkProtocol; \ No newline at end of file diff --git a/core/include/PidManager.ps1 b/core/include/PidManager.ps1 deleted file mode 100644 index 5c9770e..0000000 --- a/core/include/PidManager.ps1 +++ /dev/null @@ -1,149 +0,0 @@ -$PidManager = New-Object -TypeName PSObject; - -$PidManager | Add-Member -membertype ScriptMethod -name 'PidExists' -value { - param([string]$bind); - - [string]$PidFile = $this.PidFileName($bind); - - return (Test-Path ($this.FullPidPath($PidFile))); -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'CreatePidFile' -value { - param([string]$bind); - - [string]$PidFile = $this.PidFileName($bind); - - Add-Content -Path ($this.FullPidPath($PidFile)) -Value $pid; -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'PidFileName' -value { - param([string]$bind); - - return [string]::Format( - 'icingabind{0}.pid', - $bind - ); -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'FullPidPath' -value { - param([string]$PidFile); - - return (Join-Path $Icinga2.App.RootPath -ChildPath ( - [string]::Format( - '\agent\state\{0}', - $PidFile - ) - )); -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'ProcessID' -value { - param([string]$FullPidFile); - - if ((Test-Path $FullPidFile) -eq $FALSE) { - return 0; - } - - return Get-Content -Path $FullPidFile; -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'GetPIDByBind' -value { - param([string]$bind); - - return $this.ProcessID( - $this.FullPidPath( - $this.PidFileName( - $bind - ) - ) - ); -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'GetPIDPathByBind' -value { - param([string]$bind); - - return $this.FullPidPath( - $this.PidFileName( - $bind - ) - ); -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'RemovePidFile' -value { - param([string]$FullPidPath, [string]$bind); - - [string]$PidFile = $this.PidFileName($bind); - - if (Test-Path $FullPidPath) { - Remove-Item $FullPidPath | Out-Null; - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'Removing PID-File "{0}" for bind "{1}"', - $PidFile, - $bind - ) - ); - } else { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'PID File "{0}" for bind "{1}" does not exist and could therefor not be removed', - $PidFile, - $bind - ) - ); - } -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'PidProcess' -value { - param([int]$ProcessID); - - if ($ProcessID -eq 0) { - return $null; - } - - # Look for the Process over WMI, as we might run as Service User and require - # to fetch the entire scope of running processes - $ProcessList = Get-WmiObject Win32_Process | Select-Object ProcessName, ProcessId -ErrorAction Stop; - - foreach ($process in $ProcessList) { - if ($process.ProcessId -eq $ProcessID) { - if ($process.ProcessName -eq 'powershell.exe') { - return $process; - } - } - } - - return $null; -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'StopProcessByBind' -value { - param([string]$bind); - - if ($this.PidExists($bind)) { - $ProcessId = $this.GetPIDByBind($bind); - $this.ShutdownProcess($ProcessId); - $this.RemovePidFile( - $this.GetPIDPathByBind($bind), - $bind - ); - } -} - -$PidManager | Add-Member -membertype ScriptMethod -name 'ShutdownProcess' -value { - param($ProcessID); - - # Close possible PowerShell instances - if ($Icinga2.PidManager.PidProcess($ProcessID) -ne $null) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'Trying to terminate process with PID "{0}"', - $ProcessID - ) - ); - Stop-Process -Id $ProcessID -Force; - } -} - -return $PidManager; \ No newline at end of file diff --git a/core/include/ServerProtocol.ps1 b/core/include/ServerProtocol.ps1 deleted file mode 100644 index b19660f..0000000 --- a/core/include/ServerProtocol.ps1 +++ /dev/null @@ -1,223 +0,0 @@ -$ServerProtocoll = New-Object -TypeName PSObject; - -$ServerProtocoll | Add-Member -membertype NoteProperty -name 'static' -value $FALSE; -$ServerProtocoll | Add-Member -membertype NoteProperty -name 'Client' -value $Null; -$ServerProtocoll | Add-Member -membertype NoteProperty -name 'Network' -value (Get-Icinga-Lib -Include 'NetworkProtocol'); -$ServerProtocoll | Add-Member -membertype NoteProperty -name 'Response' -value (Get-Icinga-Lib -Include 'APIResponse'); -$ServerProtocoll | Add-Member -membertype NoteProperty -name 'Timer' -value $Null; -$ServerProtocoll | Add-Member -membertype NoteProperty -name 'Message' -value $Null; -$ServerProtocoll | Add-Member -membertype NoteProperty -name 'Commands' -value @{}; - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'Create' -value { - param([System.Net.Sockets.TcpClient]$Client); - - $this.Client = $Client; - $this.Client.SendTimeout = 2000 - $this.Client.NoDelay = $TRUE; - - $this.Timer = [System.Diagnostics.Stopwatch]::StartNew(); - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - 'New incoming TCP Client connection' - ); - - $this.Network.Create($Client.GetStream()); - - # Just in case we received connections over HTTP, send a short answer message - # back and close the client request - if ($this.Network.encrypted -eq $FALSE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - 'Received client connection over HTTP. Rejecting client request.' - ); - $this.Response.HTTPSRequired(); - $this.Network.WriteMessage( - $this.Response.Compile() - ); - $this.Close(); - return $FALSE; - } - - return $TRUE; -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'ParseRequest' -value { - # Tell our network protocol to read all messages until - # EOF is reached - [SecureString]$message = $this.Network.ReadMessage(-1); - - if ($message -eq $null) { - return; - } - - [hashtable]$ApiMessage = $Icinga2.Utils.WebHelper.ParseApiMessage($message); - - if ($ApiMessage -eq $null -Or $ApiMessage.Count -eq 0) { - $this.SendInternalServerError(); - return; - } - - if ($Icinga2.Config.'authentication.enabled') { - [int]$Authenticated = $Icinga2.Utils.AuthHelper.Login( - $ApiMessage.credentials.user, - $ApiMessage.credentials.password, - $ApiMessage.credentials.domain - ); - if ($Authenticated -eq 0) { - $this.SendAuthenticationRequired(); - return; - } - } - - if ($ApiMessage.headers.ContainsKey('content-length')) { - [int]$ContentLength = ($ApiMessage.headers['content-length'] - $ApiMessage.content.Length); - $ApiMessage.content += $Icinga2.Utils.SecureString.ConvertFrom( - $this.Network.ReadMessage( - $ContentLength - ) - ); - } - - $this.Message = $ApiMessage; - $this.ParseQuery(); - $this.ExecuteQuery(); -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'ParseQuery' -value { - [string]$QueryString = $this.Message.base.query; - if ($QueryString[0] -eq '?') { - $QueryString = $QueryString.Substring( - 1, - $QueryString.Length - 1 - ); - } - - [array]$SplitCommand = $QueryString.Split('&'); - foreach ($command in $SplitCommand) { - [hashtable]$data = $Icinga2.Utils.WebHelper.ParseUrlCommand($command); - if ($this.Commands.ContainsKey(($data.GetEnumerator() | Select-Object -First 1).Key) -eq $FALSE) { - $this.Commands += $data; - } - } -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'ExecuteQuery' -value { - switch($this.IsUrlPathValid(0)) { - '' { - switch($this.IsUrlPathValid(1)) { - 'v1' { - switch($this.IsUrlPathValid(2)) { - 'data' { - $this.ParseDataV1(); - }; - 'modules' { - $this.ParseModulesV1(); - }; - default { - $this.SendBadRequest( - 'Unsupported Cmdlets specified. The following Cmdlets are supported: data, modules' - ); - }; - } - }; - default { - $this.SendBadRequest( - 'Unsupported API version specified. The following versions are supported: v1' - ); - }; - } - }; - default { - $this.SendInternalServerError(); - }; - } -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'GetExecutionTime' -value { - return $this.Timer.Elapsed.TotalSeconds; -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'ParseDataV1' -value { - [hashtable]$data = - @{ - data = New-Icinga-Monitoring -Include $this.Commands.include -Exclude $this.Commands.exclude; - execution = $this.GetExecutionTime(); - }; - - $this.Response.setContent($data); - $this.SendOkResponse(); -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'ParseModulesV1' -value { - [hashtable]$modules = - @{ - modules = New-Icinga-Monitoring -ListModules $TRUE; - execution = $this.GetExecutionTime(); - }; - - $this.Response.setContent($modules); - $this.SendOkResponse(); -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'IsUrlPathValid' -value { - param([int]$Index); - - [string]$path = $this.Message.base.segments[$Index]; - - if ([string]::IsNullOrEmpty($path) -eq $TRUE) { - return 'default'; - } - - return $path.Replace('/', ''); -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'SendOkResponse' -value { - $this.Network.WriteMessage( - $this.Response.Compile() - ); - $this.Close(); -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'SendInternalServerError' -value { - $this.Response.InternalServerError(); - $this.Network.WriteMessage( - $this.Response.Compile() - ); - $this.Close(); -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'SendAuthenticationRequired' -value { - $this.Response.AuthenticationRequired(); - $this.Network.WriteMessage( - $this.Response.Compile() - ); - $this.Close(); -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'SendBadRequest' -value { - param([string]$message); - - $this.Response.CustomBadRequest($message); - $this.Network.WriteMessage( - $this.Response.Compile() - ); - $this.Close(); -} - -$ServerProtocoll | Add-Member -membertype ScriptMethod -name 'Close' -value { - try { - $this.Timer.Stop(); - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - 'Closing TCP Client connection' - ) - $this.Client.Close(); - $this.Client.Dispose() - $this.Client = $Null; - } catch { - # Nothing to handle. If the connection is closed already, ignore it. - } -} - -return $ServerProtocoll; \ No newline at end of file diff --git a/core/include/Service.ps1 b/core/include/Service.ps1 deleted file mode 100644 index f8a3658..0000000 --- a/core/include/Service.ps1 +++ /dev/null @@ -1,183 +0,0 @@ -$Service = New-Object -TypeName PSObject; - -$Service | Add-Member -membertype NoteProperty -name 'servicename' -value 'IcingaWindowsModule'; -$Service | Add-Member -membertype NoteProperty -name 'servicedisplayname' -value 'Icinga Windows Service'; - -$Service | Add-Member -membertype ScriptMethod -name 'Install' -value { - param([string]$ServiceBinaryPath); - - if ([string]::IsNullOrEmpty($ServiceBinaryPath) -eq $TRUE) { - return 'Please specify a valid service binary path.'; - } - - # Test if our binary does exist - if (-Not (Test-Path $ServiceBinaryPath)) { - return ([string]::Format( - 'Failed to install the Icinga service. The service binary specified at "{0}" does not exist.', - $ServiceBinaryPath - )); - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Trying to install Icinga 2 Service...' - ); - - # Now add the script root which we require to include to the service - $ServiceBinaryPath = [string]::Format( - '{0} \"{1}\"', - $ServiceBinaryPath, - (Join-Path -Path $Icinga2.App.RootPath -ChildPath $Icinga2.App.ModuleName) - ); - - $result = & sc.exe create $this.servicename binPath= "$ServiceBinaryPath" DisplayName= $this.servicedisplayname start= auto; - - if ($this.HandleServiceError($LASTEXITCODE) -eq $TRUE) { - return $FALSE; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Successfully installed the Icinga 2 Windows Service.' - ); - - return $TRUE; -} - -$Service | Add-Member -membertype ScriptMethod -name 'Uninstall' -value { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Trying to uninstall Icinga Service...' - ); - - # Stop the service before uninstalling it - $this.Stop(); - - $result = & sc.exe delete $this.servicename; - - if ($this.HandleServiceError($LASTEXITCODE) -eq $TRUE) { - return $FALSE; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Successfully uninstalled the Icinga 2 Windows Service.' - ); - - return $TRUE; -} - -$Service | Add-Member -membertype ScriptMethod -name 'Start' -value { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Trying to start Icinga 2 Service...' - ); - - $result = & sc.exe start $this.servicename; - - if ($this.HandleServiceError($LASTEXITCODE) -eq $TRUE) { - return; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Successfully started the Icinga 2 Service.' - ); - - $this.QueryStatus(); -} - -$Service | Add-Member -membertype ScriptMethod -name 'Stop' -value { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Trying to stop Icinga 2 Service...' - ); - - $result = & sc.exe stop ($this.servicename); - - if ($this.HandleServiceError($LASTEXITCODE) -eq $TRUE) { - return; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Successfully stopped the Icinga 2 Service.' - ); - - $this.QueryStatus(); -} - -$Service | Add-Member -membertype ScriptMethod -name 'Restart' -value { - $this.Stop(); - # Wait two seconds before starting the service again - Start-Sleep -Seconds 2; - $this.Start(); -} - -$Service | Add-Member -membertype ScriptMethod -name 'QueryStatus' -value { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - 'Waiting to query the proper Icinga 2 Service status...' - ); - Start-Sleep -Seconds 1; - - $this.Status(); -} - -$Service | Add-Member -membertype ScriptMethod -name 'Status' -value { - $ServiceStatus = (Get-WMIObject win32_service -Filter ( - [string]::Format( - "Name='{0}'", - ($this.servicename) - ) - )).State; - - if ([string]::IsNullOrEmpty($ServiceStatus) -eq $TRUE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - $Icinga2.Enums.ServiceStatus.NotInstalled - ); - - return; - } - - if ($Icinga2.Enums.ServiceStatus.ContainsKey($ServiceStatus)) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - $Icinga2.Enums.ServiceStatus.$ServiceStatus - ); - } else { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'The Icinga service status is {0}', - $ServiceStatus - ) - ); - } -} - -$Service | Add-Member -membertype ScriptMethod -name 'HandleServiceError' -value { - param([int]$ErrorCode); - - # Nothing to do as no error occured - if ($ErrorCode -eq 0) { - return $FALSE; - } - - if ($Icinga2.Enums.SCErrorCodes.ContainsKey($ErrorCode)) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - $Icinga2.Enums.SCErrorCodes.$ErrorCode - ); - } else { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - ([string]::Format('Failed to execute operation for Icinga 2 Service: {0}', $result)) - ); - } - - return $TRUE; -} - -return $Service; \ No newline at end of file diff --git a/core/include/System.ps1 b/core/include/System.ps1 deleted file mode 100644 index d1bfbb5..0000000 --- a/core/include/System.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -$SystemCPU = Get-CimInstance -ClassName 'Win32_Processor'; - -[int]$NumberOfCPUCores = 0; -[int]$NumberOfCPUThreads = 0; - -if (($SystemCPU.NumberOfCores).GetType() -is [Object]) { - $SystemCPU.NumberOfCores | Foreach { $NumberOfCPUCores += $_; }; -} else { - $NumberOfCPUCores = $SystemCPU.NumberOfCores; -} - -if (($SystemCPU.NumberOfLogicalProcessors).GetType() -is [Object]) { - $SystemCPU.NumberOfLogicalProcessors | Foreach { $NumberOfCPUThreads += $_; }; -} else { - $NumberOfCPUThreads = $SystemCPU.NumberOfCores; -} - -[hashtable]$Overview = @{ - 'NumberOfCPUCores' = $NumberOfCPUCores; - 'NumberOfCPUThreads' = $NumberOfCPUThreads; -}; - -return $Overview; \ No newline at end of file diff --git a/core/include/TCPDaemon.ps1 b/core/include/TCPDaemon.ps1 deleted file mode 100644 index 270c7f1..0000000 --- a/core/include/TCPDaemon.ps1 +++ /dev/null @@ -1,76 +0,0 @@ -$TCPDaemon = New-Object -TypeName PSObject; - -$TCPDaemon | Add-Member -membertype ScriptMethod -name 'Start' -value { - - [int]$Port = $Icinga2.Config.'tcp.socket.port'; - - if (-Not $Icinga2.Cache.Certificates.Server) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - [string]::Format( - 'Unable to start TCP socket daemon for port {0}. No valid SSL certificate was loaded.', - $Port - ) - ); - return; - } - - if ($Icinga2.TCPSocket.IsSocketOpenAndValid($Port) -eq $TRUE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'A PowerShell instance for socket on port "{0}" is already running with PID "{1}"', - $Port, - $Icinga2.PidManager.GetPIDByBind($port) - ) - ); - return; - } - $TCPSocket = $Icinga2.TCPSocket.CreateTCPSocket($Port); - - if ($TCPSocket -eq $null) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - [string]::Format( - 'Failed to start TCP socket on port "{0}"', - $Port - ) - ) - return; - } - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'Starting new API listener on port "{0}"', - $Port - ) - ); - - while($true) { - [System.Net.Sockets.TcpClient]$client = $TCPSocket.AcceptTcpClient(); - - $ServerProtocol = Get-Icinga-Lib -Include 'ServerProtocol'; - if ($ServerProtocol.Create($Client) -eq $FALSE) { - continue; - } - $ServerProtocol.ParseRequest(); - } - - $Icinga2.TCPSocket.CloseTCPSocket($Port); -} - -$TCPDaemon | Add-Member -membertype ScriptMethod -name 'Stop' -value { - if ($Icinga2.Utils.AdminShell.IsAdminShell() -eq $FALSE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - 'Please run this shell as Administrator in order to stop daemon processes' - ); - return; - } - [int]$Port = $Icinga2.Config.'tcp.socket.port'; - $Icinga2.TCPSocket.CloseTCPSocket($Port); - -} - -return $TCPDaemon; \ No newline at end of file diff --git a/core/include/TCPSocket.ps1 b/core/include/TCPSocket.ps1 deleted file mode 100644 index cbc8e89..0000000 --- a/core/include/TCPSocket.ps1 +++ /dev/null @@ -1,161 +0,0 @@ -$TCPSocket = New-Object -TypeName PSObject; - -$TCPSocket | Add-Member -membertype ScriptMethod -name 'CreateTCPSocket' -value { - param([int]$port); - - [string]$PidFile = $Icinga2.PidManager.PidFileName($port); - - try { - $TCPSocket = [System.Net.Sockets.TcpListener]$port; - $TCPSocket.Start(); - $Icinga2.Cache.Sockets.Add($PidFile, $TCPSocket); - $Icinga2.PidManager.CreatePidFile($port); - - return $TCPSocket; - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - [string]::Format( - 'Failed to create TCP socket on port "{0}": {1}', - $port, - $_.Exception.Message - ) - ); - } - - return $null; -} - -# Properly close sockets, flush PID Files or terminate PowerShell instances as owner of sockets -$TCPSocket | Add-Member -membertype ScriptMethod -name 'CloseTCPSocket' -value { - param([int]$port); - - [string]$PidFile = $Icinga2.PidManager.PidFileName($port); - [bool]$IsExternalSocket = $FALSE; - - # Clear our Socket cache - # In case the socket does not exist, create a new socket - if ($Icinga2.Cache.Sockets.ContainsKey($PidFile) -eq $FALSE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'The socket of port "{0}" is not part of this PowerShell instance', - $port - ) - ); - $IsExternalSocket = $TRUE; - } else { - try { - $TCPSocket = $Icinga2.Cache.Sockets.$PidFile; - $TCPSocket.Stop(); - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'Closing TCP socket on port "{0}"', - $port - ) - ); - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - [string]::Format( - 'Failed to close TCP socket on port "{0}": {1}', - $port, - $_.Exception.Message - ) - ); - } - - $Icinga2.Cache.Sockets.Remove($PidFile); - } - - # Delete the PID file from disk in case it exists - [string]$FullPidPath = $Icinga2.PidManager.FullPidPath($PidFile); - [int]$ProcessID = $Icinga2.PidManager.ProcessID($FullPidPath); - - $Icinga2.PidManager.RemovePidFile($FullPidPath); - - # Close possible PowerShell instances - if ($Icinga2.PidManager.PidProcess($ProcessID) -ne $null) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'Trying to terminate process with PID "{0}"', - $ProcessID - ) - ); - Stop-Process -Id $ProcessID -Force; - } -} - -$TCPSocket | Add-Member -membertype ScriptMethod -name 'IsSocketOpenAndValid' -value { - param([int]$port); - - [string]$PidFile = $Icinga2.PidManager.PidFileName($port); - - [string]$FullPidPath = $Icinga2.PidManager.FullPidPath($PidFile); - - if ((Test-Path $FullPidPath) -eq $FALSE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'No PID-File found for TCP socket on port "{0}". Trying to close socket...', - $port - ) - ); - - # Even when the PID-File does not exist, try to gracefull shutdown the socket - $this.CloseTCPSocket($port); - return $FALSE; - } - - [int]$ProcessID = $Icinga2.PidManager.ProcessID($FullPidPath); - - if ([string]::IsNullOrEmpty($ProcessID) -eq $TRUE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'No process ID found for socket on port "{0}". Trying to close socket anyway...', - $port - ) - ); - - # Even when the PID-File does not exist, try to gracefull shutdown the socket - $this.CloseTCPSocket($port); - return $FALSE; - } - - try { - # Look for the Process over WMI, as we might run as Service User and require - # to fetch the entire scope of running processes - if ($Icinga2.PidManager.PidProcess($ProcessID) -ne $null) { - return $TRUE; - } - - # Socket does not exist or is not valid. Perform a cleanup and return false - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'The socket configuration for port "{0}" is not valid. Performing cleanup...', - $port - ) - ); - - $this.CloseTCPSocket($port); - return $FALSE; - } catch [System.Exception] { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - [string]::Format( - 'Exception while trying to lookup the process for socket port "{0}": {1}...', - $port, - $_.Exception.Message - ) - ); - } - - $this.CloseTCPSocket($port); - return $FALSE; -} - -return $TCPSocket; \ No newline at end of file diff --git a/core/include/Utils.ps1 b/core/include/Utils.ps1 deleted file mode 100644 index 5ea6af0..0000000 --- a/core/include/Utils.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -# Provide a collection of utility functions for the module -[hashtable]$Utils = @{}; - -Get-ChildItem (Join-Path -Path $PSScriptRoot -ChildPath '\utils\') -Filter *.ps1 | - Foreach-Object { - $path = $_.FullName; - $name = $_.Name.Replace('.ps1', ''); - - $Utils.Add($name, (& $path)); - } - -return $Utils; \ No newline at end of file diff --git a/core/include/utils/AdminShell.ps1 b/core/include/utils/AdminShell.ps1 deleted file mode 100644 index 5961ea4..0000000 --- a/core/include/utils/AdminShell.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -$AdminShell = New-Object -TypeName PSObject; -$AdminShell | Add-Member -membertype ScriptMethod -name 'IsAdminShell' -value { - $CurrentIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent(); - $WindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($CurrentIdentity); - - if (-Not $WindowsPrincipal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) { - return $FALSE; - } - return $TRUE; -} - -return $AdminShell; \ No newline at end of file diff --git a/core/include/utils/AuthHelper.ps1 b/core/include/utils/AuthHelper.ps1 deleted file mode 100644 index 9509bbc..0000000 --- a/core/include/utils/AuthHelper.ps1 +++ /dev/null @@ -1,63 +0,0 @@ -Add-Type -AssemblyName System.DirectoryServices.AccountManagement; - -$AuthHelper = New-Object -TypeName PSObject; - -<# - # This function will allow us to authenticate against either a - # Domain Controller or the local machine the module runs on. - # For security reasons, Username and Password have to be - # stored within a SecureString. If no Domain is specified, - # a login will always be attempted to the local machine - #> -$AuthHelper | Add-Member -membertype ScriptMethod -name 'Login' -value { - param([SecureString]$UserName, [SecureString]$Password, [String]$Domain); - - # Base handling: We try to authenticate against a local user on the machine - [string]$AuthMethod = [System.DirectoryServices.AccountManagement.ContextType]::Machine; - [string]$AuthDomain = $env:COMPUTERNAME; - - # If we specify a domain, we should authenticate against our Domain - if ([string]::IsNullOrEmpty($Domain) -eq $FALSE) { - $AuthMethod = [System.DirectoryServices.AccountManagement.ContextType]::Domain; - $AuthDomain = $Domain; - } - - try { - # Create an Account Management object based on the above determined settings - $AccountService = New-Object System.DirectoryServices.AccountManagement.PrincipalContext( - $AuthMethod, - $AuthDomain - ); - } catch { - # Regardless of the error, print the message and return false to prevent further execution - $Icinga2.Log.Write($Icinga2.Enums.LogState.Exception, $_.Exception.Message); - return 0; - } - - # In case we couldn't setup the Account Service, always return false - if ($AccountService -eq $null) { - return 0; - } - - try { - # Try to authenticate and either return true or false as integer - [int]$AuthResult = [int]($AccountService.ValidateCredentials( - $Icinga2.Utils.SecureString.ConvertFrom($UserName), - $Icinga2.Utils.SecureString.ConvertFrom($Password) - )); - - return $AuthResult; - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Failed to authenticate with the provided user credentials. Error: {0}', - $_.Exception.Message - ) - ); - } - - return 0; -} - -return $AuthHelper; \ No newline at end of file diff --git a/core/include/utils/IniParser.ps1 b/core/include/utils/IniParser.ps1 deleted file mode 100644 index 8fad7f0..0000000 --- a/core/include/utils/IniParser.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -<# - # Helper class allowing to read INI files basicly - # and return the content as Hashtable - #> - -$IniParser = New-Object -TypeName PSObject; - -$IniParser | Add-Member -membertype ScriptMethod -name 'LoadFromArray' -value { - param([array]$content, [bool]$CutLastSpace); - - [hashtable]$IniContent = @{}; - [string]$IniKey = ''; - [string]$SubIniKey = ''; - - # First, loop all lines of our NTP config - foreach ($item in $content) { - # At first we require to parse the section argument for the config - if ($item.Contains('[')) { - $IniKey = $item.Replace('[', '').Replace(']', ''); - $IniContent.Add($IniKey, @{ }); - continue; - } - - if ([string]::IsNullOrEmpty($item) -eq $TRUE) { - continue; - } - - # In case our entry does not contain ':', we are not loading a config entry - if ($item.Contains(':') -eq $FALSE) { - $SubIniKey = $item; - $IniContent[$IniKey].Add($SubIniKey, @{ }); - continue; - } - - # Now as we found an config entry point, split the result at first to get - # the key of our config. Afterwards we load the value by removing all - # spaces before the actual value - [array]$ConfigData = $item.Split(':'); - [string]$ConfigKey = $ConfigData[0]; - [string]$ConfigValue = $item.Substring($item.IndexOf(':') + 1, $item.Length - $item.IndexOf(':') - 1); - - # Some INI files (like NTP) add additional details behind the values if they - # are configured by Local or Remote for example. With this we can cut these - # informations out, idependently from our configured OS language - if ($CutLastSpace -eq $TRUE) { - $ConfigValue = $ConfigValue.Substring(0, $ConfigValue.LastIndexOf(' ')); - } - - while ($ConfigValue[0] -eq ' ') { - $ConfigValue = $ConfigValue.Substring(1, $ConfigValue.Length - 1); - } - - # It could happen that within a section keys are being overwritten again - # We should take care of this and update a possible added key with the - # next configured values to receive only the correct configuration as result - # as it is interpreted by the time service - if ([string]::IsNullOrEmpty($SubIniKey) -eq $TRUE) { - if ($IniContent[$IniKey].ContainsKey($ConfigKey) -eq $FALSE) { - $IniContent[$IniKey].Add($ConfigKey, $ConfigValue); - } else { - $IniContent[$IniKey][$ConfigKey] = $ConfigValue; - } - } else { - if ($IniContent[$IniKey][$SubIniKey].ContainsKey($ConfigKey) -eq $FALSE) { - $IniContent[$IniKey][$SubIniKey].Add($ConfigKey, $ConfigValue); - } else { - $IniContent[$IniKey][$SubIniKey][$ConfigKey] = $ConfigValue; - } - } - } - - return $IniContent; -} - -return $IniParser; \ No newline at end of file diff --git a/core/include/utils/Modules.ps1 b/core/include/utils/Modules.ps1 deleted file mode 100644 index dc75cbb..0000000 --- a/core/include/utils/Modules.ps1 +++ /dev/null @@ -1,154 +0,0 @@ -<# - # Helper class for accessing and handling modules in a - # more easier and managed way - #> - -$Modules = New-Object -TypeName PSObject; - -$Modules | Add-Member -membertype ScriptMethod -name 'LoadIncludes' -value { - param([string]$modulename, $Config); - - $modulename = $modulename.ToLower(); - $modulename = $modulename.Replace('.ps1', ''); - - [string]$ModuleDir = Join-Path ` - -Path $Icinga2.App.RootPath ` - -ChildPath ( - [string]::Format( - '\modules\include\{0}', - $modulename - ) - ) - - [hashtable]$ModuleIndludes = @{}; - - if ( (Test-Path $ModuleDir) -eq $FALSE) { - return $ModuleIndludes; - } - - Get-ChildItem $ModuleDir -Filter *.ps1 | - Foreach-Object { - [string]$name = $_.Name.ToLower().Replace( - '.ps1', - '' - ); - try { - $ModuleIndludes.Add( - $name, - (& $_.FullName -Config $Config) - ); - } catch { - $ModuleIndludes.Add( - $name, - [string]::Format( - 'Failed to execute include "{0}" for module "{1}". Exception: {2}', - $name, - $modulename, - $_.Exception.Message - ) - ); - } - } - - return $ModuleIndludes; -} - -$Modules | Add-Member -membertype ScriptMethod -name 'FlushModuleCache' -value { - param([string]$modulename); - - if ($Icinga2.Cache.Modules.ContainsKey($modulename) -eq $FALSE) { - return; - } - - $Icinga2.Cache.Modules[$modulename] = @{ }; -} - -$Modules | Add-Member -membertype ScriptMethod -name 'AddCacheElement' -value { - param([string]$modulename, [string]$cachename, $value); - - if ($Icinga2.Cache.Modules.ContainsKey($modulename) -eq $FALSE) { - $Icinga2.Cache.Modules.Add($modulename, @{ }); - } - - if ($Icinga2.Cache.Modules[$modulename].ContainsKey($cachename) -eq $FALSE) { - $Icinga2.Cache.Modules[$modulename].Add($cachename, $value); - } else { - $Icinga2.Cache.Modules[$modulename][$cachename] = $value; - } -} - -$Modules | Add-Member -membertype ScriptMethod -name 'GetCacheElement' -value { - param([string]$modulename, [string]$cachename); - - if ($Icinga2.Cache.Modules.ContainsKey($modulename) -eq $FALSE) { - return @{ }; - } - - if ($Icinga2.Cache.Modules[$modulename].ContainsKey($cachename) -eq $FALSE) { - return @{ }; - } - - return $Icinga2.Cache.Modules[$modulename][$cachename]; -} - -$Modules | Add-Member -membertype ScriptMethod -name 'GetHashtableDiff' -value { - param([hashtable]$new, [hashtable]$cache, [array]$addkeys); - - [hashtable]$DiffTable = @{ - FullList = @{ }; - Removed = @( ); - Added = $null; - Modified = @{ }; - } - - if ($cache -eq $null -or $cache.Count -eq 0) { - $DiffTable.FullList = $new; - } else { - # Each additional call will only send the diffs to the server - $int = 0; - foreach ($cachedProcess in $cache.Keys) { - $oldProcess = $cache[$cachedProcess]; - - # In case a service is no longer present on our system, send the process Id - # only so we can delete it from our database - if ($new.ContainsKey($cachedProcess) -eq $FALSE) { - $DiffTable['Removed'] += $oldProcess.ProcessId; - } else { - # If we know about a process, only send the values which have been updated - # since the last check - $newProcess = $new[$cachedProcess]; - - foreach ($entry in $newProcess.Keys) { - $oldValue = $oldProcess[$entry]; - $newValue = $newProcess[$entry]; - - if ($oldValue -ne $newValue) { - if ($DiffTable['Modified'].ContainsKey($cachedProcess) -eq $FALSE) { - $DiffTable['Modified'].Add($cachedProcess, @{ }); - } - $DiffTable['Modified'][$cachedProcess].Add($entry, $newValue); - } - } - - if ($DiffTable['Modified'].ContainsKey($cachedProcess) -eq $TRUE) { - foreach($entry in $addkeys) { - if ($DiffTable['Modified'][$cachedProcess].ContainsKey($entry) -eq $FALSE -and - $newProcess.ContainsKey($entry) -eq $TRUE) { - - $DiffTable['Modified'][$cachedProcess].Add($entry, $newProcess[$entry]); - } - } - } - - $new.Remove($cachedProcess); - } - } - - # All other processes are new and should be added - $DiffTable['Added'] = $new; - } - - return $DiffTable; -} - -return $Modules; \ No newline at end of file diff --git a/core/include/utils/SSL.ps1 b/core/include/utils/SSL.ps1 deleted file mode 100644 index a0f893d..0000000 --- a/core/include/utils/SSL.ps1 +++ /dev/null @@ -1,111 +0,0 @@ - -$SSL = New-Object -TypeName PSObject; -$SSL | Add-Member -membertype ScriptMethod -name 'LoadServerCertificate' -value { - - if ((Get-Icinga-Setup) -eq $FALSE) { - $Icinga2.Log.WriteConsole( - $Icinga2.Enums.LogState.Warning, - 'The module has not been configured yet. Skipping certificate loading' - ); - return; - } - - try { - $CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store( - $Icinga2.Config.'certstore.name', - $Icinga2.Config.'certstore.location' - ); - $CertStore.Open("ReadOnly"); - - $ServerCertificate = $null; - - [string]$CertName = $Icinga2.Config.'certstore.certificate.name'; - [string]$CertThumbprint = $Icinga2.Config.'certstore.certificate.thumbprint'; - - # Try to discover the certificate based on our FQDN - if ([string]::IsNullOrEmpty($CertName) -eq $TRUE -And [string]::IsNullOrEmpty($CertThumbprint) -eq $TRUE) { - $CertName = [System.Net.Dns]::GetHostEntry('localhost').HostName; - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Trying to discover certificate for this host with FQDN "{0}"', - $CertName - ) - ); - } - - foreach ($cert in $CertStore.Certificates) { - if ([string]::IsNullOrEmpty($CertThumbprint) -eq $FALSE) { - if ($CertThumbprint.ToLower() -eq $cert.Thumbprint.ToLower()) { - $ServerCertificate = $cert; - break; - } - } - - if ([string]::IsNullOrEmpty($CertName) -eq $FALSE) { - [string]$CNCertName = [string]::Format('CN={0}', $CertName.ToLower()); - if ($CNCertName.ToLower() -eq $cert.Subject.ToLower()) { - $ServerCertificate = $cert; - - try { - $result = Test-Certificate -Cert $cert -ErrorAction SilentlyContinue -WarningAction SilentlyContinue; - if ($result -eq $FALSE) { - continue; - } - } catch { - continue; - } - - break; - } - } - } - - $certificate = $null; - - if ($ServerCertificate -ne $null) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Using certificate "{0}" with thumbprint "{1}"', - $ServerCertificate.Subject, - $ServerCertificate.Thumbprint - ) - ); - $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2; - $certificate.Import($ServerCertificate.RawData) - } - - $CertStore.Close(); - - return $certificate; - } catch [System.ComponentModel.Win32Exception] { - # This error occures in case we provide a cert store and location which is not accessable - # from our current user. We have to simply drop everything and close every possible - # connection - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - 'SSL-Error: Unable to access provided certificate from the user space this module is started with.' - ); - } catch [System.NotSupportedException] { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - 'The used SSL certificate is not providing a linked private key and cannot be used as Server certificate' - ); - } catch { - # Handle every other error here - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - $_.Exception.Message - ); - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - $_.Exception - ); - } - - return $null; -} - -return $SSL; \ No newline at end of file diff --git a/core/include/utils/SecureString.ps1 b/core/include/utils/SecureString.ps1 deleted file mode 100644 index 52848e9..0000000 --- a/core/include/utils/SecureString.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -<# - # Helper class allowing to easily convert strings into SecureStrings - # and vice-versa - #> - -$SecureString = New-Object -TypeName PSObject; - -$SecureString | Add-Member -membertype ScriptMethod -name 'ConvertTo' -value { - param([string]$string); - - [SecureString]$SecureString = ConvertTo-SecureString -AsPlainText $string -Force; - - return $SecureString; -} - -$SecureString | Add-Member -membertype ScriptMethod -name 'ConvertFrom' -value { - param([SecureString]$SecureString); - - if ($SecureString -eq $null) { - return ''; - } - - [IntPtr]$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString) - [string]$String = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) - - return $String; -} - -return $SecureString; \ No newline at end of file diff --git a/core/include/utils/WebHelper.ps1 b/core/include/utils/WebHelper.ps1 deleted file mode 100644 index dfdd2dd..0000000 --- a/core/include/utils/WebHelper.ps1 +++ /dev/null @@ -1,296 +0,0 @@ -$WebHelper = New-Object -TypeName PSObject; - -$WebHelper | Add-Member -membertype ScriptMethod -name 'ParseApiMessage' -value { - param([SecureString]$message); - - try { - [hashtable]$HeaderContent = @{}; - - if ($message -eq $null) { - return $HeaderContent; - } - - [string]$HTMLMessage = $Icinga2.Utils.SecureString.ConvertFrom($message); - - [string]$HTMLContent = ''; - if ($HTMLMessage.Contains("`r`n`r`n")) { - [int]$EOFIndex = $HTMLMessage.IndexOf("`r`n`r`n") + 4; - $HTMLContent = $HTMLMessage.Substring( - $EOFIndex, - $HTMLMessage.Length - $EOFIndex - ); - # Remove the content from our message - if ([string]::IsNullOrEmpty($HTMLContent) -eq $FALSE) { - $HTMLMessage = $HTMLMessage.Replace($HTMLContent, ''); - } - } - - [array]$SingleHeaders = $HTMLMessage.Split("`r`n"); - $HTMLMessage = $null; - - # At first read the method, the http call and the protocol - $HeaderContent.Add( - 'base', - $this.ParseMethodAndUrl($SingleHeaders[0]) - ); - # Now add possible content to our hashtable - $HeaderContent.Add( - 'content', - $HTMLContent - ); - # Drop the first entry of the array, as we no longer require it - $SingleHeaders = $SingleHeaders | Select-Object -Skip 1; - - # Read the headers from the array - $HeaderContent.Add( - 'headers', - $this.ParseHeaders($SingleHeaders) - ); - # Flush the array from the memory - $SingleHeaders = $null; - - if ($HeaderContent.headers.ContainsKey('Authorization')) { - $HeaderContent.Add( - 'credentials', - $this.ParseUserCredentials( - $HeaderContent.headers.Authorization - ) - ); - $HeaderContent.headers.Remove('Authorization'); - } - - return $HeaderContent; - } - catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Exception, - [string]::Format( - 'Failed to parse HTTP content and headers. Error {0}', - $_.Exception.Message - ) - ); - } - - return $null; -} - -$WebHelper | Add-Member -membertype ScriptMethod -name 'ParseMethodAndUrl' -value { - param([string]$message); - - [array]$Content = $message.Split(' '); - [uri]$UriData = [uri][string]::Format( - 'https://localhost{0}', - $Content[1] - ); - - return @{ - method = $Content[0]; - call = [System.Web.HttpUtility]::UrlDecode( - $Content[1] - ); - protocol = $Content[2]; - segments = $UriData.Segments; - query = [System.Web.HttpUtility]::UrlDecode( - $UriData.Query - ); - } -} - -$WebHelper | Add-Member -membertype ScriptMethod -name 'ParseHeaders' -value { - param([array]$message); - - [hashtable]$Headers = @{}; - - foreach ($header in $message) { - [string]$HeaderName = ''; - [string]$HeaderValue = ''; - # Skip empty array values - if ([string]::IsNullOrEmpty($header) -eq $FALSE) { - if ($header.Contains(':')) { - [array]$Element = $header.Split(':'); - $HeaderName = $Element[0]; - - if ($Element.Count -gt 1) { - for ($i = 1; $i -le ($Element.Count - 1); $i++) { - $HeaderValue = -Join( - $HeaderValue, - $Element[$i], - ':' - ); - } - } - - # Remove the last added ':' at the end of the string - if ($HeaderValue.Length -gt 1) { - $HeaderValue = $HeaderValue.Substring( - 0, - $HeaderValue.Length - 1 - ); - } - - # In case the first letter of our value is a space, remove it - while ($HeaderValue[0] -eq ' ') { - $HeaderValue = $HeaderValue.Substring( - 1, - $HeaderValue.Length - 1 - ); - } - - if ($Headers.ContainsKey($HeaderName) -eq $FALSE) { - # We have to modify the Authorization header value a little more and also - # ensure we store it as secure string within our module - if ($HeaderName -eq 'Authorization') { - [array]$AuthArray = $HeaderValue.Split(' '); - # TODO: Shall we handle each auth type differently? - $Headers.Add( - $HeaderName, - $Icinga2.Utils.SecureString.ConvertTo($AuthArray[1]) - ); - $AuthArray = $null; - } else { - $Headers.Add($HeaderName, $HeaderValue); - } - } - } else { - $Headers.Add($header, $null); - } - } - } - - return $Headers; -} - -$WebHelper | Add-Member -membertype ScriptMethod -name 'ParseUserCredentials' -value { - param([SecureString]$Base64String); - - [hashtable]$Credentials = @{}; - - if ($Base64String -eq $null) { - return $Credentials; - } - - # Convert the Base64 Secure String back to a normal string - [string]$PlainAuth = [System.Text.Encoding]::UTF8.GetString( - [System.Convert]::FromBase64String( - $Icinga2.Utils.SecureString.ConvertFrom($Base64String) - ) - ); - $Base64String = $null; - - # If no ':' is within the string, the credential data is not properly formated - if ($PlainAuth.Contains(':') -eq $FALSE) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - 'Received invalid formated credentials as Base64 encoded.' - ); - $PlainAuth = $null; - return $Credentials; - } - - try { - # Build our User Data and Password from the string - [string]$UserData = $PlainAuth.Substring( - 0, - $PlainAuth.IndexOf(':') - ); - $Credentials.Add( - 'password', - $Icinga2.Utils.SecureString.ConvertTo( - $PlainAuth.Substring( - $PlainAuth.IndexOf(':') + 1, - $PlainAuth.Length - $UserData.Length - 1 - ) - ) - ); - - $PlainAuth = $null; - - # Extract a possible domain - if ($UserData.Contains('\')) { - # Split the auth string on the '\' - [array]$AuthData = $UserData.Split('\'); - # First value of the array is the Domain, second is the Username - $Credentials.Add('domain', $AuthData[0]); - $Credentials.Add( - 'user', - $Icinga2.Utils.SecureString.ConvertTo( - $AuthData[1] - ) - ); - $AuthData = $null; - } else { - $Credentials.Add('domain', $null); - $Credentials.Add( - 'user', - $Icinga2.Utils.SecureString.ConvertTo( - $UserData - ) - ); - } - - $UserData = $null; - } catch { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Error, - 'Failed to handle authentication request. An exception occured while processing the request.' - ); - } - - return $Credentials; -} - -$WebHelper | Add-Member -membertype ScriptMethod -name 'ParseUrlCommand' -value { - param([string]$Argument); - - [hashtable]$StructuredCommand = @{}; - # If no = is included, we don't need to handle anything special - if ($Argument.Contains('=') -eq $FALSE) { - $StructuredCommand.Add($Argument, $null); - return $StructuredCommand; - } - - [int]$SeperatorIndex = $Argument.IndexOf('='); - [string]$Command = $Argument.Substring( - 0, - $SeperatorIndex - ); - $Command = $Command.Replace(' ', '').ToLower(); - - [array]$Values = @(); - [string]$Value = $Argument.Substring( - $SeperatorIndex + 1, - $Argument.Length - $SeperatorIndex - 1 - ); - - if ($Value.Contains(',') -eq $TRUE) { - [array]$Entries = $Value.Split(','); - foreach ($entry in $Entries) { - [string]$ParsedValue = $entry; - # Cut spaces before names of inputs - while ($ParsedValue[0] -eq ' ') { - $ParsedValue = $ParsedValue.Substring( - 1, - $ParsedValue.Length - 1 - ); - } - - # Cut spaces after names of inputs - while ($ParsedValue[$ParsedValue.Length - 1] -eq ' ') { - $ParsedValue = $ParsedValue.Substring( - 0, - $ParsedValue.Length - 1 - ); - } - $Values += $ParsedValue.ToLower(); - } - } else { - $Values += $Value.Replace(' ', '').ToLower(); - } - - # Filter out duplicate entries - $Values = $Values | Select-Object -Unique; - $StructuredCommand.Add($Command, $Values); - return $StructuredCommand; -} - -return $WebHelper; \ No newline at end of file diff --git a/core/init.ps1 b/core/init.ps1 deleted file mode 100644 index 99b7756..0000000 --- a/core/init.ps1 +++ /dev/null @@ -1,100 +0,0 @@ -# This script will initialse the entire module configuration for easier usage -param ( - [string]$RootDirectory = '', - [string]$ModuleName = '' -); - -# Create an internal 'namespace' for our environment -Set-Variable -Name Icinga2 -Option Constant -Value @{ - Function = @( - 'Use-Icinga', - 'Import-IcingaLib', - 'Get-IcingaPluginDir', - 'Get-IcingaCustomPluginDir', - 'Get-IcingaCacheDir', - 'Get-IcingaPowerShellConfigDir', - 'Get-Icinga-Lib', - 'Get-Icinga-Object', - 'Get-Icinga-Service', - 'Start-Icinga-Service', - 'Stop-Icinga-Service', - 'Restart-Icinga-Service', - 'Install-Icinga-Service', - 'Uninstall-Icinga-Service', - 'Install-Icinga', - 'Get-Icinga-Setup', - 'Start-Icinga-Daemon', - 'Stop-Icinga-Daemon', - 'Start-Icinga-Checker', - 'Stop-Icinga-Checker', - 'Get-Icinga-Command', - 'New-Icinga-Monitoring', - 'Get-Icinga-Counter', - 'Get-Icinga-Config', - 'Set-Icinga-Config', - 'Remove-Icinga-Config', - 'New-Icinga-Config' - ); -} - -# Define temporary variables to store the main current root and module name -# Note: Never use this variables within the module besides inside '\core\includes\' -$_InternalTempVariables = @{ - RootPath = $RootDirectory; - ModuleName = $ModuleName; -} -# End definition of temporary variables - -# Load all PowerShell scripts within our '\core\include\' directory and add the content with the name -# of the script into our namespace -Get-ChildItem (Join-Path -Path $PSScriptRoot -ChildPath '\include\') -Filter *.ps1 | - Foreach-Object { - $path = $_.FullName; - $name = $_.Name.Replace('.ps1', ''); - - # Add variables to a global namespace. Should only be used within the - # same PowerShell instance - try { - $include = (& $path); - } catch { - Write-Host ( - [string]::Format( - 'Failed to execute core module "{0}". Exception: {1}', - $name, - $_.Exception.Message - ) - ); - } - - if ([bool]($include.PSobject.Properties.Name -eq 'static') -eq $FALSE -Or $include.static -eq $TRUE) { - $Icinga2.Add($name, $include); - } - } - -# Flush the internal temp variable cache -$_InternalTempVariables = $null; - -# Load our System.Web helper class -[Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null; - -$Icinga2.Add( - 'Cache', - @{ - # This will allow us to dynamicly initialise Performance Counters during - # startup to speed up actual checks later on. Of course counters will be - # cached anyway once they are executed, but will speed up first check - # executions for CPU Performance Counters for example - PerformanceCounter = @{ }; - # Pre-Load the Server SSL Certificate - Certificates = @{ Server = $Icinga2.Utils.SSL.LoadServerCertificate() }; - # Create a instance for storing TCP Sockets (in case we later want to listen in multi-sockets) - Sockets = @{ }; - # Store our checker configuration we receive from the remote endpoint - Checker = @{ }; - # This cache can be used for storing informations of modules to compare send informations - # as well as required data for a later execution of the same module again - Modules = @{ }; - } -); - -return $Icinga2; \ No newline at end of file diff --git a/core/monitoring.ps1 b/core/monitoring.ps1 deleted file mode 100644 index 3af9c6f..0000000 --- a/core/monitoring.ps1 +++ /dev/null @@ -1,118 +0,0 @@ -param( - [array]$Include = @(), - [array]$Exclude = @(), - [boolean]$ListModules = $FALSE, - $Config = $null, - [string]$AgentRoot = '' -) - -function ClassMonitoring() -{ - param( - [array]$Include = @(), - [array]$Exclude = @(), - [boolean]$ListModules = $FALSE, - $Config = $null, - [string]$AgentRoot = '' - ) - - [string]$ModuleDirectory = Join-Path $AgentRoot -ChildPath 'modules'; - [hashtable]$ModuleList = @{}; - [array]$AvailableModules = @(); - $ResultList = New-Object psobject -prop @{}; - - # Let's do a small fix here: We have defined 'include' within the URL, but - # we haven't specified any values. So lets assume we want to load all - # modules - if ($Include.Count -eq 1 -And [string]::IsNullOrEmpty($Include[0])) { - $Include[0] = '*'; - } - - # In case no includes are specified, lets include everything - if ($Include.Count -eq 0) { - $Include += '*'; - } - - <# - # In case no filter is specified, we asume we want to collect everything. - # Lets fetch all PowerShell Scripts within our module Directory - # Will also be used to return a list of installed modules - #> - if (($Include.Count -eq 1 -And $Include[0] -eq '*') -Or $ListModules) { - Get-ChildItem $ModuleDirectory -Filter *.ps1 | - Foreach-Object { - $path = $_.FullName - $name = $_.Name.Replace('.ps1', '').ToLower(); - - $ModuleList.Add($name, $path); - $AvailableModules += $name; - } - - if ($ListModules) { - return $AvailableModules; - } - } else { - # In case we provided a filter, try to locate these modules - foreach ($module in $Include) { - # Just to ensure we skip this argument in case it is provided - if ($module -eq '*') { - continue; - } - $module = $module.ToLower(); - [string]$file = [string]::Format('{0}.ps1', $module); - [string]$path = Join-Path $ModuleDirectory -ChildPath $file; - - if ($ModuleList.ContainsKey($module) -eq $FALSE) { - $ModuleList.Add($module, $path); - } - } - } - - foreach ($module in $Exclude) { - if ($ModuleList.ContainsKey($module)) { - $ModuleList.Remove($module); - } - } - - [System.Diagnostics.Stopwatch]$ModuleTimer = New-Object System.Diagnostics.Stopwatch; - # Now as we have our module list available, lets execute them to fetch informations - foreach ($module in $ModuleList.Keys) { - $ModuleTimer.Start(); - [string]$path = $ModuleList[$module]; - [hashtable]$ModuleResult = @{}; - $moduleConfig = $null; - - if ($Config -ne $null -AND $Config.$module -ne $null) { - $moduleConfig = $Config.$module; - } - - # First test if the specified module is available - if (Test-Path ($path)) { - try { - # If it is, execute the script and return the output - $ModuleResult.Add('output', (&$path -Config $moduleConfig)); - $ModuleResult.Add('response', 200); - $ModuleResult.Add('error', $null); - } catch { - # In case the script we tried to execute runs into a failure, return the exception message as result - $ModuleResult.Add('output', $null); - $ModuleResult.Add('response', 500); - $ModuleResult.Add('error', [string]::Format('Failed to execute module "{0}". Exeception: {1}', $module, $_.Exception.Message)); - } - } else { - # Include the module to our output with a small notify message - $ModuleResult.Add('output', $null); - $ModuleResult.Add('response', 404); - $ModuleResult.Add('error', 'Module not found'); - } - - $ModuleResult.Add('execution', $ModuleTimer.Elapsed.TotalSeconds); - $ModuleTimer.Stop(); - - $ResultList | Add-Member -Name $module -Type NoteProperty -Value $ModuleResult; - } - - return $ResultList; -} - -return ClassMonitoring -Include $Include -Exclude $Exclude -ListModules $ListModules -Config $Config -AgentRoot $AgentRoot; \ No newline at end of file diff --git a/core/perfcounter.ps1 b/core/perfcounter.ps1 deleted file mode 100644 index 99839be..0000000 --- a/core/perfcounter.ps1 +++ /dev/null @@ -1,577 +0,0 @@ -param( - [string]$Counter = '', - [string]$ListCounter = '', - [array]$CounterArray = @(), - [boolean]$ListCategories = $FALSE, - [boolean]$SkipWait = $FALSE, - # These arguments apply to CreateStructuredPerformanceCounterTable - # This is the category name we want to create a structured output - # Example: 'Network Interface' - [string]$CreateStructuredOutputForCategory = '', - # This is the hashtable of Performance Counters, created by - # PerformanceCounterArray - [hashtable]$StructuredCounterInput = @{}, - # This argument is just a helper to replace certain strings within - # a instance name with simply nothing. - # Example: 'HarddiskVolume1' => '1' - [array]$StructuredCounterInstanceCleanup = @() -); - -# This is our internal cache for Performance Counters already loaded -# In case the Icinga Agent is running as daemon, this hashtable is -# already initialised at the beginning. But if we run the Agent -# from the Powershell directly, we will require to build this cache -# within the environment to work properly and to receive valid data -if ($Icinga2.Cache.PerformanceCounter -eq $null) { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - 'Creating new performance counter cache' - ); - $Icinga2.Cache.PerformanceCounter = @{}; -} - -$Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format( - 'Performance Counter Cache content {0}', - ($Icinga2.Cache.PerformanceCounter | Out-String) - ) -); - -<# - # This function will provide a virtual object, containing an array - # of Performance Counters. The object has the following members: - # Name - # Value - # This will ensure we will not have to worry about looping an array - # of mutltiple instances within a counter handler, because this - # function will deal with everything, returning an hashtable - # containing the parent counter name including the values and - # samples for every single instance - #> -function PerformanceCounterArray() -{ - param( - [string]$FullName = '', - [array]$PerformanceCounters = @() - ); - - $pc_instance = New-Object -TypeName PSObject; - $pc_instance | Add-Member -membertype NoteProperty -name 'FullName' -value $FullName; - $pc_instance | Add-Member -membertype NoteProperty -name 'Counters' -value $PerformanceCounters; - - $pc_instance | Add-Member -membertype ScriptMethod -name 'Name' -value { - return $this.FullName; - } - - $pc_instance | Add-Member -membertype ScriptMethod -name 'Value' -value { - [hashtable]$CounterResults = @{}; - - foreach ($counter in $this.Counters) { - $CounterResults.Add($counter.Name(), $counter.Value()); - } - - return $CounterResults; - } - - return $pc_instance; -} - -<# - # This function will create a custom Performance Counter object with - # already initialised counters, which can be accessed with the - # following members: - # Name - # Value - # Like the PerformanceCounterArray, this will allow to fetch the - # current values of a single counter instance including the name - # of the counter. Within the PerformanceCounterArray function, - # objects created by this function are used. - #> -function PerformanceCounterObject() -{ - param( - [string]$FullName = '', - [string]$Category = '', - [string]$Instance = '', - [string]$Counter = '', - [boolean]$SkipWait = $FALSE - ); - - $pc_instance = New-Object -TypeName PSObject; - $pc_instance | Add-Member -membertype NoteProperty -name 'FullName' -value $FullName; - $pc_instance | Add-Member -membertype NoteProperty -name 'Category' -value $Category; - $pc_instance | Add-Member -membertype NoteProperty -name 'Instance' -value $Instance; - $pc_instance | Add-Member -membertype NoteProperty -name 'Counter' -value $Counter; - $pc_instance | Add-Member -membertype NoteProperty -name 'PerfCounter' -value $Counter; - $pc_instance | Add-Member -membertype NoteProperty -name 'SkipWait' -value $SkipWait; - - $pc_instance | Add-Member -membertype ScriptMethod -name 'Init' -value { - - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Debug, - [string]::Format('Creating new Counter for Category {0} with Instance {1} and Counter {2}. Full Name "{3}"', - $this.Category, - $this.Instance, - $this.Counter, - $this.FullName - ) - ); - - # Create the Performance Counter object we want to access - $this.PerfCounter = New-Object System.Diagnostics.PerformanceCounter; - $this.PerfCounter.CategoryName = $this.Category; - $this.PerfCounter.CounterName = $this.Counter; - - # Only add an instance in case it is defined - if ([string]::IsNullOrEmpty($this.Instance) -eq $FALSE) { - $this.PerfCounter.InstanceName = $this.Instance - } - - # Initialise the counter - try { - $this.PerfCounter.NextValue() | Out-Null; - } catch { - # Nothing to do here, will be handled later - } - - <# - # For some counters we require to wait a small amount of time to receive proper data - # Other counters do not need these informations and we do also not require to wait - # for every counter we use, once the counter is initialised within our environment. - # This will allow us to skip the sleep to speed up loading counters - #> - if ($this.SkipWait -eq $FALSE) { - Start-Sleep -Milliseconds 500; - } - } - - # Return the name of the counter as string - $pc_instance | Add-Member -membertype ScriptMethod -name 'Name' -value { - return $this.FullName; - } - - <# - # Return a hashtable containting the counter value including the - # Sample values for the counter itself. In case we run into an error, - # keep the counter construct but add an error message in addition. - #> - $pc_instance | Add-Member -membertype ScriptMethod -name 'Value' -value { - [hashtable]$CounterData = @{}; - - try { - [string]$CounterType = $this.PerfCounter.CounterType; - $CounterData.Add('value', $this.PerfCounter.NextValue()); - $CounterData.Add('sample', $this.PerfCounter.NextSample()); - $CounterData.Add('help', $this.PerfCounter.CounterHelp); - $CounterData.Add('type', $CounterType); - $CounterData.Add('error', $null); - } catch { - $CounterData = @{}; - $CounterData.Add('value', $null); - $CounterData.Add('sample', $null); - $CounterData.Add('help', $null); - $CounterData.Add('type', $null); - $CounterData.Add('error', $_.Exception.Message); - } - - return $CounterData; - } - - # Initialiste the entire counter and internal handlers - $pc_instance.Init(); - - # Return this custom object - return $pc_instance; -} - -<# - # If some informations are missing, it could happen that - # we are unable to create a Performance Counter. - # In this case we will use this Null Object, containing - # the same member functions but allowing us to maintain - # stability without unwanted exceptions - #> - function PerformanceCounterNullObject() - { - param( - [string]$FullName = '', - [string]$ErrorMessage = '' - ); - - $pc_instance = New-Object -TypeName PSObject; - $pc_instance | Add-Member -membertype NoteProperty -name 'FullName' -value $FullName; - $pc_instance | Add-Member -membertype NoteProperty -name 'ErrorMessage' -value $ErrorMessage; - - $pc_instance | Add-Member -membertype ScriptMethod -name 'Name' -value { - return $this.FullName; - } - - $pc_instance | Add-Member -membertype ScriptMethod -name 'Value' -value { - [hashtable]$ErrorMessage = @{}; - - $ErrorMessage.Add('value', $null); - $ErrorMessage.Add('sample', $null); - $ErrorMessage.Add('help', $null); - $ErrorMessage.Add('type', $null); - $ErrorMessage.Add('error', $this.ErrorMessage); - - return $ErrorMessage; - } - - return $pc_instance; - } - - <# - # This function will make monitoring an entire list of - # Performance counters even more easier. We simply provide - # an array of Performance Counters to this module - # and we will receive a construct-save result of an - # hashtable with all performance counters including - # the corresponding values. In that case the code - # size decreases for larger modules. - # Example: - $counter = Get-Icinga-Counter -CounterArray @( - '\Memory\Available Bytes', - '\Memory\% Committed Bytes In Use' - ); - #> - function CreatePerformanceCounterResult() - { - param( - [array]$CounterArray = @() - ) - - [hashtable]$CounterResult = @{}; - [bool]$RequireSleep = $FALSE; - foreach ($counter in $CounterArray) { - # We want to speed up things with loading, so we will check if a specified - # Counter is already cached within our hashtable. If it is not, we sleep - # at the end of the function the required 500ms and don't have to wait - # NumOfCounters * 500 milliseconds for the first runs. This will speed - # up the general loading of counters and will not require some fancy - # pre-caching / configuration handler - if ($Icinga2.Cache.PerformanceCounter -ne $null) { - if ($Icinga2.Cache.PerformanceCounter.ContainsKey($counter) -eq $FALSE) { - $RequireSleep = $TRUE; - } - } - $obj = CreatePerformanceCounter -Counter $counter -SkipWait $TRUE; - if ($CounterResult.ContainsKey($obj.Name()) -eq $FALSE) { - $CounterResult.Add($obj.Name(), $obj.Value()); - } - } - - # Above we initialse ever single counter and we only require a sleep once - # in case a new, yet unknown counter was added - if ($RequireSleep) { - Start-Sleep -Milliseconds 500; - - # Agreed, this is some sort of code duplication but it wouldn't make - # any sense to create a own function for this. Why are we doing - # this anway? - # Simple: In case we found counters which have yet not been initialised - # we did this above. Now we have waited 500 ms to receive proper - # values from these counters. As the previous generated result - # might have contained counters with 0 results, we will now - # check all counters again to receive the proper values. - # Agreed, might sound like a overhead, but the impact only - # applies to the first call of the module with the counters. - # This 'duplication' however decreased the execution from - # certain modules from 25s to 1s on the first run. Every - # additional run is then beeing executed within 0.x s - # which sounds like a very good performance and solution - $CounterResult = @{}; - foreach ($counter in $CounterArray) { - $obj = CreatePerformanceCounter -Counter $counter -SkipWait $TRUE; - if ($CounterResult.ContainsKey($obj.Name()) -eq $FALSE) { - $CounterResult.Add($obj.Name(), $obj.Value()); - } - } - } - - return $CounterResult; - } - -<# - # This is the main function which is called from this script, constructing our counters - # and loading possible sub-instances from our Performance Counter. - # It will return either an PerformanceCounterObject or PerformanceCounterArray - # which both contain the same members, allowing us to dynamicly use the objects - # without having to worry about exception. - #> -function CreatePerformanceCounter() -{ - param( - [string]$Counter = '', - [boolean]$SkipWait = $FALSE - ); - - # Simply use the counter name, like - # \Paging File(_total)\% Usage - if ([string]::IsNullOrEmpty($Counter) -eq $TRUE) { - return (PerformanceCounterNullObject -FullName $Counter -ErrorMessage 'Failed to initialise counter, as no counter was specified.'); - } - - [array]$CounterArray = $Counter.Split('\'); - [string]$UseCounterCategory = ''; - [string]$UseCounterName = ''; - [string]$UseCounterInstance = ''; - - # If we add the counter as it should be - # \Paging File(_total)\% Usage - # the first array element will be an empty string we can skip - # Otherwise the name was wrong and we should not continue - if (-Not [string]::IsNullOrEmpty($CounterArray[0])) { - return (PerformanceCounterNullObject -FullName $Counter -ErrorMessage ([string]::Format('Failed to deserialize counter "{0}". It seems the leading "\" is missing.', $Counter))); - } - - # In case our Performance Counter is containing instances, we should split - # The content and read the instance and counter category out - if ($CounterArray[1].Contains('(')) { - [array]$TmpCounter = $CounterArray[1].Split('('); - $UseCounterCategory = $TmpCounter[0]; - $UseCounterInstance = $TmpCounter[1].Replace(')', ''); - } else { - # Otherwise we only require the category - $UseCounterCategory = $CounterArray[1]; - } - - # At last get the actual counter containing our values - $UseCounterName = $CounterArray[2]; - - # Now as we know how the counter path is constructed and has been splitted into - # the different values, we need to know how to handle the instances of the counter - - # If we specify a instance with (*) we want the module to automaticly fetch all - # instances for this counter. This will result in an PerformanceCounterArray - # which contains the parent name including counters for all instances that - # have been found - if ($UseCounterInstance -eq '*') { - # In case we already loaded the counters once, return the finished array - if ($Icinga2.Cache.PerformanceCounter.ContainsKey($Counter) -eq $TRUE) { - return (PerformanceCounterArray -FullName $Counter -PerformanceCounters $Icinga2.Cache.PerformanceCounter[$Counter]); - } - - # If we need to build the array, load all instances from the counters and - # create single performance counters and add them to a custom array and - # later to a custom object - try { - [array]$AllCountersIntances = @(); - $CounterInstances = New-Object System.Diagnostics.PerformanceCounterCategory($UseCounterCategory); - foreach ($instance in $CounterInstances.GetInstanceNames()) { - [string]$NewCounterName = $Counter.Replace('*', $instance); - $NewCounter = PerformanceCounterObject -FullName $NewCounterName -Category $UseCounterCategory -Counter $UseCounterName -Instance $instance -SkipWait $SkipWait; - $AllCountersIntances += $NewCounter; - } - } catch { - return (PerformanceCounterNullObject -FullName $Counter -ErrorMessage ([string]::Format('Failed to deserialize instances for counter "{0}". Exception: "{1}".', $Counter, $_.Exception.Message))); - } - - # Add the parent counter including the array of Performance Counters to our - # caching mechanism and return the PerformanceCounterArray object for usage - # within the monitoring modules - $Icinga2.Cache.PerformanceCounter.Add($Counter, $AllCountersIntances); - return (PerformanceCounterArray -FullName $Counter -PerformanceCounters $AllCountersIntances); - } else { - # This part will handle the counters without any instances as well as - # specificly assigned instances, like (_Total) CPU usage. - - # In case we already have the counter within our cache, return the - # cached informations - if ($Icinga2.Cache.PerformanceCounter.ContainsKey($Counter) -eq $TRUE) { - return $Icinga2.Cache.PerformanceCounter[$Counter]; - } - - # If the cache is not present yet, create the Performance Counter object, - # and add it to our cache - $NewCounter = PerformanceCounterObject -FullName $Counter -Category $UseCounterCategory -Counter $UseCounterName -Instance $UseCounterInstance -SkipWait $SkipWait; - $Icinga2.Cache.PerformanceCounter.Add($Counter, $NewCounter); - } - - # This function will always return non-instance counters or - # specificly defined instance counters. Performance Counter Arrays - # are returned within their function. This is just to ensure that the - # function looks finished from developer point of view - return $Icinga2.Cache.PerformanceCounter[$Counter]; -} - -# -# This function will get handy in case we want to fetch Counters -# which have instances which might be helpful to group by their -# instances name. This will apply to Disk and Network Interface -# outputs for example, as it would be helpful to combine all -# counter results for a specific disk / interface in one -# result for easier working with these informations -# -function CreateStructuredPerformanceCounterTable -{ - param( - [string]$CounterCategory = '', - [hashtable]$PerformanceCounterHash = @{}, - [array]$InstanceNameCleanupArray = @() - ) - - # The storage variables we require to store our data - [array]$AvailableInstances = @(); - [hashtable]$StructuredCounterData = @{}; - - # With this little trick we can fetch all instances we have and get their unique name - $CounterInstances = New-Object System.Diagnostics.PerformanceCounterCategory($CounterCategory); - foreach ($instance in $CounterInstances.GetInstanceNames()) { - # For some counters we require to apply a 'cleanup' for the instance name - # Example Disks: Some disks are stored with the name - # 'HarddiskVolume1' - # To be able to map the volume correctly to disks, we require to remove - # 'HarddiskVolume' so only '1' will remain, which allows us to map the - # volume correctly afterwards - [string]$CleanInstanceName = $instance; - foreach ($cleanup in $InstanceNameCleanupArray) { - $CleanInstanceName = $CleanInstanceName.Replace($cleanup, ''); - } - $AvailableInstances += $CleanInstanceName; - } - - # Now let the real magic begin. - - # At first we will loop all instances of our Performance Counters, which means all - # instances we have found above. We build a new hashtable then to list the instances - # by their individual name and all corresponding counters as children - # This allows us a structured output with all data for each instance - foreach ($instance in $AvailableInstances) { - - # First build a hashtable for each instance to add data to later - $StructuredCounterData.Add($instance, @{}); - - # Now we need to loop all return values from our Performance Counters - foreach ($InterfaceCounter in $PerformanceCounterHash.Keys) { - # As we just looped the parent counter (Instance *), we now need to - # loop the actual counters for each instance - foreach ($interface in $PerformanceCounterHash[$InterfaceCounter]) { - # Finally let's loop through all the results which contain the values - # to build our new, structured hashtable - foreach ($entry in $interface.Keys) { - # Match the counters based on our current parent index - # (the instance name we want to add the values as children). - if ($entry.Contains('(' + $instance + ')')) { - # To ensure we don't transmit the entire counter name, - # we only want to include the name of the actual counter. - # There is no need to return - # \Network Interface(Desktopadapter Intel[R] Gigabit CT)\Bytes Received/sec - # the naming - # Bytes Received/sec - # is enough - [array]$TmpOutput = $entry.Split('\'); - [string]$OutputName = $TmpOutput[$TmpOutput.Count - 1]; - - # Now add the actual value to our parent instance with the - # improved value name, including the sample and counter value data - $StructuredCounterData[$instance].Add($OutputName, $interface[$entry]); - } - } - } - } - } - - return $StructuredCounterData; -} - -# -# This function will load all available Categories of Performance Counters -# from the registry and outputs them. This will ensure we can fetch the real -# english names instead of the localiced ones -# -function ListCounterCategories() -{ - $RegistryData = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009' ` - -Name 'counter' | Select-Object -ExpandProperty Counter; - [array]$Counters = @(); - - # Now lets loop our registry data and fetch only for counter categories - # Ignore everything else and drop the information - foreach ($counter in $RegistryData) { - # First filter out the ID's of the performance counter - if (-Not ($counter -match "^[\d\.]+$") -And [string]::IsNullOrEmpty($counter) -eq $FALSE) { - # Now check if the value we got is a counter category - if ([System.Diagnostics.PerformanceCounterCategory]::Exists($counter) -eq $TRUE) { - $Counters += $counter; - } - } - } - - return $Counters; -} - -# -# Provide the name of a category to fetch all available counters and -# if there are any instances assigned to it -# -function ListCountersFromCategory() -{ - param ([string]$CounterCategory); - - [hashtable]$counters = @{}; - try { - # At first create our Performance Counter object for the category we specified - $Category = New-Object System.Diagnostics.PerformanceCounterCategory($CounterCategory); - - # Now loop through all keys to find the name of available counters - foreach ($counter in $Category.ReadCategory().Keys) { - [string]$CounterInstanceAddition = ''; - - # As counters might also have instances (like interfaces, disks, paging file), we should - # try to load them as well - foreach ($instance in $Category.ReadCategory()[$counter].Keys) { - # If we do not match this magic string, we have multiple instances we can access - # to get informations for different disks, volumes and interfaces for example - if ($instance -ne 'systemdiagnosticsperfcounterlibsingleinstance') { - # Re-Write the name we return of the counter to something we can use directly - # within our modules to load data from. A returned counter will look like this - # for example: - # \PhysicalDisk(*)\avg. disk bytes/read - [string]$UsableCounterName = [string]::Format('\{0}(*)\{1}', $CounterCategory, $counter); - if ($counters.ContainsKey($UsableCounterName) -eq $TRUE) { - $counters[$UsableCounterName] += $Category.ReadCategory()[$counter][$instance]; - } else { - $counters.Add($UsableCounterName, @( $Category.ReadCategory()[$counter][$instance] )); - } - } else { - # For counters with no instances, we still require to return a re-build Performance Counter - # output, to make later usage in our modules very easy. This can look like this: - # \System\system up time - [string]$UsableCounterName = [string]::Format('\{0}\{1}', $CounterCategory, $counter); - $counters.Add($UsableCounterName, $null); - } - } - }; - } catch { - # In case we run into an error, return an error message - $counters.Add('error', $_.Exception.Message); - } - - return $counters; -} - -if ([string]::IsNullOrEmpty($CreateStructuredOutputForCategory) -eq $FALSE) { - return (CreateStructuredPerformanceCounterTable ` - -CounterCategory $CreateStructuredOutputForCategory ` - -PerformanceCounterHash $StructuredCounterInput ` - -InstanceNameCleanupArray $StructuredCounterInstanceCleanup - ) -} - -if ($ListCategories -eq $TRUE) { - return ListCounterCategories; -} - -if ([string]::IsNullOrEmpty($ListCounter) -eq $FALSE) { - return ListCountersFromCategory -CounterCategory $ListCounter; -} - -# Make things easier by simply proividing an array of Performance Counter -# Names we wish to monitor -if ($CounterArray.Count -ne 0) { - return (CreatePerformanceCounterResult -CounterArray $CounterArray); -} - -return CreatePerformanceCounter -Counter $Counter -SkipWait $SkipWait; \ No newline at end of file diff --git a/core/setup.ps1 b/core/setup.ps1 deleted file mode 100644 index f857875..0000000 --- a/core/setup.ps1 +++ /dev/null @@ -1,97 +0,0 @@ -param( - [bool]$IsAgentIntalled = $FALSE -) - -function ClassSetup() -{ - param( - [bool]$IsAgentIntalled = $FALSE - ); - - $instance = New-Object -TypeName PSObject; - - $instance | Add-Member -membertype NoteProperty -name 'BaseDirectory' -value (Join-Path $Icinga2.App.RootPath -ChildPath 'agent'); - - $instance | Add-Member -membertype ScriptMethod -name 'Init' -value { - $IsInstalled = Get-Icinga-Config -Key 'setup.installed'; - - if ($IsAgentIntalled) { - if ($IsInstalled -eq $FALSE -Or $IsInstalled -eq $null) { - return 0; - } - } - - $this.CreateDirectories('config'); - $this.CreateDirectories('state'); - - if ($IsInstalled -eq $FALSE -Or $IsInstalled -eq $null) { - $this.InstallEventLog(); - $this.CreateConfig(); - } - - # At this point for this module, we require to return 1 as 'true' - return 1; - } - - $instance | Add-Member -membertype ScriptMethod -name 'CreateDirectories' -value { - param([string]$directory); - - [string]$path = Join-Path $this.BaseDirectory -ChildPath $directory; - if (-Not (Test-Path $path)) { - New-Item $path -ItemType Directory | Out-Null; - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - ([string]::Format('Creating new directory "{0}"', $path)) - ); - } - } - - $instance | Add-Member -membertype ScriptMethod -name 'InstallEventLog' -value { - try { - New-EventLog -LogName Application -Source ($Icinga2.Service.servicedisplayname) -ErrorAction Stop; - $Icinga2.Log.WriteConsole( - $Icinga2.Enums.LogState.Info, - [string]::Format( - 'Successfully installed EventLog "{0}" for this module', - $Icinga2.Service.servicedisplayname - ) - ); - } catch { - $Icinga2.Log.WriteConsole( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'EventLog for "{0}" is already installed.', - $Icinga2.Service.servicedisplayname - ) - ); - } - } - - $instance | Add-Member -membertype ScriptMethod -name 'CreateConfig' -value { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Info, - '### Installing default configuration values ###' - ); - - Set-Icinga-Config -Key 'checker.server.host' -Value 'https://localhost/icingaweb2/windows/checkresult' | Out-Null; - Set-Icinga-Config -Key 'checker.ssl.verify' -Value $TRUE | Out-Null; - Set-Icinga-Config -Key 'tcp.socket.host' -Value 'localhost' | Out-Null; - Set-Icinga-Config -Key 'tcp.socket.port' -Value '5891' | Out-Null; - Set-Icinga-Config -Key 'service.name' -Value 'icinga2winservice' | Out-Null; - Set-Icinga-Config -Key 'service.displayname' -Value 'Icinga 2 Windows Service' | Out-Null; - Set-Icinga-Config -Key 'setup.installed' -Value $TRUE | Out-Null; - Set-Icinga-Config -Key 'certstore.name' -Value 'My' | Out-Null; - Set-Icinga-Config -Key 'certstore.location' -Value 'LocalMachine' | Out-Null; - Set-Icinga-Config -Key 'certstore.certificate.name' -Value '' | Out-Null; - Set-Icinga-Config -Key 'certstore.certificate.thumbprint' -Value '' | Out-Null; - Set-Icinga-Config -Key 'logger.directory' -Value '' | Out-Null; - Set-Icinga-Config -Key 'logger.debug' -Value $FALSE | Out-Null; - Set-Icinga-Config -Key 'authentication.enabled' -Value $FALSE | Out-Null; - Set-Icinga-Config -Key 'authentication.user' -Value '' | Out-Null; - Set-Icinga-Config -Key 'authentication.domain' -Value '' | Out-Null; - } - - return $instance.Init(); -} - -return ClassSetup -IsAgentIntalled $IsAgentIntalled; \ No newline at end of file diff --git a/modules/bios.ps1 b/modules/bios.ps1 deleted file mode 100644 index 11e842c..0000000 --- a/modules/bios.ps1 +++ /dev/null @@ -1,21 +0,0 @@ - - -function ClassBIOS -{ - # Lets load some bios informations - $BIOSInformation = Get-CimInstance Win32_BIOS; - [hashtable]$BIOSData = @{}; - - foreach ($bios_properties in $BIOSInformation) { - #$bios_datails = @{}; - foreach($bios in $bios_properties.CimInstanceProperties) { - #$bios_datails.Add($bios.Name, $bios.Value); - $BIOSData.Add($bios.Name, $bios.Value); - } - #$BIOSData.Add($bios_datails.DeviceID, $bios_datails); - } - - return $BIOSData; -} - -return ClassBIOS; \ No newline at end of file diff --git a/modules/certificates.ps1 b/modules/certificates.ps1 deleted file mode 100644 index bf6298d..0000000 --- a/modules/certificates.ps1 +++ /dev/null @@ -1,112 +0,0 @@ -param($Config = $null); - -function ClassCertificates() -{ - param($Config = $null); - - [hashtable]$CertStore = @{}; - [hashtable]$CertLocation = @{}; - [hashtable]$CertCounters = @{}; - - Set-Location 'cert:' | Out-Null; - $certs = Get-ChildItem -Recurse; - - foreach ($cert in $certs) { - if ($cert.LocationName) { - if ($CertStore.ContainsKey($cert.LocationName) -eq $FALSE) { - $CertStore.Add($cert.LocationName, @{}); - } - } - - if ($cert.IssuerName) { - [hashtable]$Certificate = @{}; - $Certificate.Add('Archived', $cert.Archived); - $Certificate.Add('HasPrivateKey', $cert.HasPrivateKey); - $Certificate.Add('IssuerName.Name', $cert.IssuerName.Name); - $Certificate.Add('IssuerName.Oid', $cert.IssuerName.Oid); - $Certificate.Add('NotAfter', $cert.NotAfter); - $Certificate.Add('NotBefore', $cert.NotBefore); - $Certificate.Add('SerialNumber', $cert.SerialNumber); - $Certificate.Add('SubjectName.Name', $cert.SubjectName.Name); - $Certificate.Add('SubjectOid.Oid', $cert.SubjectName.Oid); - $Certificate.Add('SignatureAlgorithm.Value', $cert.SignatureAlgorithm.Value); - $Certificate.Add('SignatureAlgorithm.FriendlyName', $cert.SignatureAlgorithm.FriendlyName); - $Certificate.Add('Thumbprint', $cert.Thumbprint); - $Certificate.Add('Version', $cert.Version); - $Certificate.Add('Issuer', $cert.Issuer); - $Certificate.Add('Subject', $cert.Subject); - $Certificate.Add('PSParentPath', $cert.PSParentPath); - $Certificate.Add('PSChildName', $cert.PSChildName); - $Certificate.Add('DnsNameList', $cert.DnsNameList); - - [string]$cert_store = (GetCertStore -CertPath $cert.PSPath); - [string]$cert_location = (GetCertLocation -CertPath $cert.PSPath); - - $Certificate.Add('CertStore', $cert_store); - $Certificate.Add('CertLocation', $cert_location); - - if ($CertLocation.ContainsKey($cert_location)) { - $CertLocation[$cert_location] += $Certificate; - } else { - $CertLocation.Add($cert_location, @( $Certificate )); - } - } - } - - foreach ($cert_arr in $CertLocation.Keys) { - foreach ($cert in $CertLocation[$cert_arr]) { - [string]$CertFullPathCache = [string]::Format( - '{0}\{1}\{2}', - $cert.CertStore, - $cert.CertLocation, - $cert.Thumbprint - ); - if ($CertCounters.ContainsKey($CertFullPathCache) -eq $FALSE) { - $CertCounters.Add($CertFullPathCache, 1); - } else { - $CertCounters[$CertFullPathCache] += 1; - } - if ($CertStore[$cert.CertStore].ContainsKey($cert.CertLocation)) { - [string]$CertThumbprintKey = $cert.Thumbprint; - if ($CertCounters[$CertFullPathCache] -gt 1) { - $CertThumbprintKey = [string]::Format( - '{0} ({1})', - $CertThumbprintKey, - $CertCounters[$CertFullPathCache] - ); - } - $CertStore[$cert.CertStore][$cert.CertLocation].Add($CertThumbprintKey, $cert); - } else { - $CertStore[$cert.CertStore].Add($cert.CertLocation, @{ $cert.Thumbprint = $cert }); - } - } - } - - return $CertStore -} - -function GetCertStore() -{ - param([string]$CertPath); - - $CertPath = $CertPath.Replace('Microsoft.PowerShell.Security\', ''); - $CertPath = $CertPath.Replace('Certificate::', ''); - - [array]$path = $CertPath.Split('\'); - - return $path[0]; -} - -function GetCertLocation() -{ - param([string]$CertPath); - - $CertPath = $CertPath.Replace('Microsoft.PowerShell.Security\', ''); - $CertPath = $CertPath.Replace('Certificate::', ''); - - [array]$path = $CertPath.Split('\'); - - return $path[1]; -} - -return ClassCertificates -Config $Config; \ No newline at end of file diff --git a/modules/cpu.ps1 b/modules/cpu.ps1 deleted file mode 100644 index 36a0f57..0000000 --- a/modules/cpu.ps1 +++ /dev/null @@ -1,18 +0,0 @@ -param($Config = $null); - -function ClassCPU -{ - param($Config = $null); - - # This will return a hashtable with every single counter - # We specify within the array - $counter = Get-Icinga-Counter -CounterArray @( - '\Processor(*)\% Processor Time', - '\System\Processor Queue Length', - '\System\Threads' - ); - - return $counter; -} - -return ClassCPU -Config $Config; \ No newline at end of file diff --git a/modules/disk.ps1 b/modules/disk.ps1 deleted file mode 100644 index b8097b7..0000000 --- a/modules/disk.ps1 +++ /dev/null @@ -1,96 +0,0 @@ -param($Config = $null); - -function ClassDisk() -{ - param($Config = $null); - # The storage variables we require to store our data - [hashtable]$StructuredDiskData = @{}; - - # This will return a hashtable with every single counter - # we specify within the array. Instead of returning all - # the values in the returned hashtable, we will rebuild - # the result a little to have a improved output which - # is more user friendly and allows us to check for - # certain disks / volumes in details with a simpler - # accessing possibility - $counter = Get-Icinga-Counter -CounterArray @( - '\PhysicalDisk(*)\% Disk Read Time', - '\PhysicalDisk(*)\Current Disk Queue Length', - '\PhysicalDisk(*)\Avg. Disk Bytes/Transfer', - '\PhysicalDisk(*)\Split IO/sec', - '\PhysicalDisk(*)\Disk Reads/sec', - '\PhysicalDisk(*)\Disk Writes/sec', - '\PhysicalDisk(*)\Disk Bytes/sec', - '\PhysicalDisk(*)\Avg. Disk Read Queue Length', - '\PhysicalDisk(*)\Avg. Disk sec/Write', - '\PhysicalDisk(*)\% Disk Time', - '\PhysicalDisk(*)\Avg. Disk sec/Transfer', - '\PhysicalDisk(*)\Avg. Disk Bytes/Write', - '\PhysicalDisk(*)\% Disk Write Time', - '\PhysicalDisk(*)\Avg. Disk Queue Length', - '\PhysicalDisk(*)\Disk Write Bytes/sec', - '\PhysicalDisk(*)\Avg. Disk sec/Read', - '\PhysicalDisk(*)\Disk Read Bytes/sec', - '\PhysicalDisk(*)\Disk Transfers/sec', - '\PhysicalDisk(*)\% Idle Time', - '\PhysicalDisk(*)\Avg. Disk Write Queue Length', - '\PhysicalDisk(*)\Avg. Disk Bytes/Read' - ); - - $logicalCounter = Get-Icinga-Counter -CounterArray @( - '\LogicalDisk(*)\Free Megabytes', - '\LogicalDisk(*)\% Free Space' - ); - - # This function will help us to build a structured output based on - # volumes / disks found within the instances. We will use our - # LogicalDisk as 'index' to assign our performance Counters to. - # In addition we then provide the hashtable of counters we fetched - # above. Last but not least we cleanup the instances name to replace - # 'HarddiskVolume1' for '1' for example, to ensure the mapping of disk - # informations is working as intended - [hashtable]$DiskData = Get-Icinga-Counter ` - -CreateStructuredOutputForCategory 'PhysicalDisk' ` - -StructuredCounterInput $counter; - - foreach ($counters in $logicalCounter.Keys) { - foreach ($counter in $logicalCounter[$counters].Keys) { - [string]$instance = $counter; - if ($instance.Contains('(') -And $instance.Contains(')')) { - [int]$bracketStart = $instance.IndexOf('(') + 1; - [int]$bracketEnd = $instance.IndexOf(')'); - $instance = $instance.Substring($bracketStart, $bracketEnd - $bracketStart); - $instanceArray = $counter.Split('\'); - $counterName = $instanceArray[$instanceArray.Length - 1]; - foreach ($disk in $DiskData.Keys) { - if ($disk.Contains($instance)) { - $DiskData[$disk].Add( - $counterName, - $logicalCounter[$counters][$counter] - ); - } - } - } - } - } - - # Rewrite our output a little to make it more user friendly - # This is unique for disks, as we want to remove the ':' from - # Drive Letters and add back the HarddiskVolume label to volumes - # to prevent having only a numeric table keys. Example: - # '1' => 'HarddiskVolume1' - foreach ($disk in $DiskData.Keys) { - $NewKey = $disk.Replace(':', ''); - if ($NewKey -match "^[\d\.]+$") { - $NewKey = [string]::Format('HarddiskVolume{0}', $NewKey); - } - if ($NewKey[0] -match "^[\d\.]+$") { - $NewKey = $NewKey.Substring(2, $NewKey.Length - 2); - } - $StructuredDiskData.Add($NewKey, $DiskData[$disk]); - } - - return $StructuredDiskData; -} - -return ClassDisk -Config $Config; \ No newline at end of file diff --git a/modules/hardware.ps1 b/modules/hardware.ps1 deleted file mode 100644 index ea47044..0000000 --- a/modules/hardware.ps1 +++ /dev/null @@ -1,6 +0,0 @@ -param($Config = $null); - -return $Icinga2.Utils.Modules.LoadIncludes( - $MyInvocation.MyCommand.Name, - $Config -); \ No newline at end of file diff --git a/modules/include/hardware/cpu.ps1 b/modules/include/hardware/cpu.ps1 deleted file mode 100644 index d813c1b..0000000 --- a/modules/include/hardware/cpu.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -param($Config = $null); -# -# Fetch the CPU Hardware informations -# - -# Lets load some additional CPU informations, besides current performance counters -# It might be useful to get more details about the hardware itself -$CPUInformations = Get-CimInstance Win32_Processor; -[hashtable]$PhysicalCPUData = @{}; - -foreach ($cpu_properties in $CPUInformations) { - $cpu_datails = @{}; - foreach($cpu_core in $cpu_properties.CimInstanceProperties) { - $cpu_datails.Add($cpu_core.Name, $cpu_core.Value); - } - $PhysicalCPUData.Add($cpu_datails.DeviceID, $cpu_datails); -} - -return $PhysicalCPUData; \ No newline at end of file diff --git a/modules/include/hardware/disks.ps1 b/modules/include/hardware/disks.ps1 deleted file mode 100644 index fa0c6c0..0000000 --- a/modules/include/hardware/disks.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -param($Config = $null); -# -# Fetch the Disk Hardware informations -# - -# Lets load some additional disk informations, besides current data -# It might be useful to get more details about the hardware itself -$DisksInformations = Get-CimInstance Win32_DiskDrive; - -[hashtable]$PhysicalDiskData = @{}; - -foreach ($disk_properties in $DisksInformations) { - $disk_datails = @{}; - foreach($disk in $disk_properties.CimInstanceProperties) { - $disk_datails.Add($disk.Name, $disk.Value); - } - $disk_datails.Add('DriveReference', @()); - $PhysicalDiskData.Add($disk_datails.DeviceID, $disk_datails); -} - -$DiskPartitionInfo = Get-WmiObject Win32_DiskDriveToDiskPartition; - -[hashtable]$MapDiskPartitionToLogicalDisk = @{}; - -foreach ($item in $DiskPartitionInfo) { - [string]$diskPartition = $item.Dependent.SubString( - $item.Dependent.LastIndexOf('=') + 1, - $item.Dependent.Length - $item.Dependent.LastIndexOf('=') - 1 - ); - $diskPartition = $diskPartition.Replace('"', ''); - - [string]$physicalDrive = $item.Antecedent.SubString( - $item.Antecedent.LastIndexOf('\') + 1, - $item.Antecedent.Length - $item.Antecedent.LastIndexOf('\') - 1 - ) - $physicalDrive = $physicalDrive.Replace('"', ''); - - $MapDiskPartitionToLogicalDisk.Add($diskPartition, $physicalDrive); -} - -$LogicalDiskInfo = Get-WmiObject Win32_LogicalDiskToPartition; - -foreach ($item in $LogicalDiskInfo) { - [string]$driveLetter = $item.Dependent.SubString( - $item.Dependent.LastIndexOf('=') + 1, - $item.Dependent.Length - $item.Dependent.LastIndexOf('=') - 1 - ); - $driveLetter = $driveLetter.Replace('"', ''); - - [string]$diskPartition = $item.Antecedent.SubString( - $item.Antecedent.LastIndexOf('=') + 1, - $item.Antecedent.Length - $item.Antecedent.LastIndexOf('=') - 1 - ) - $diskPartition = $diskPartition.Replace('"', ''); - - if ($MapDiskPartitionToLogicalDisk.ContainsKey($diskPartition)) { - foreach ($disk in $PhysicalDiskData.Keys) { - [string]$DiskId = $disk.SubString( - $disk.LastIndexOf('\') + 1, - $disk.Length - $disk.LastIndexOf('\') - 1 - ); - - if ($DiskId.ToLower() -eq $MapDiskPartitionToLogicalDisk[$diskPartition].ToLower()) { - $PhysicalDiskData[$disk]['DriveReference'] += $driveLetter; - } - } - } -} - -return $PhysicalDiskData; \ No newline at end of file diff --git a/modules/include/hardware/memory.ps1 b/modules/include/hardware/memory.ps1 deleted file mode 100644 index a47825e..0000000 --- a/modules/include/hardware/memory.ps1 +++ /dev/null @@ -1,59 +0,0 @@ -param($Config = $null); -# -# Fetch the Memory Hardware informations -# - -# Lets load some additional memory informations, besides current performance counters -# It might be useful to get more details about the hardware itself -$MemoryInformations = Get-CimInstance Win32_PhysicalMemory; -$capacity = $MemoryInformations | Measure-Object -Property capacity -Sum; - -# Lets load the details from our RAM modules -[hashtable]$PhysicalMemoryData = @{}; - -$PhysicalMemoryData.Add('Modules', $capacity.Count); - -foreach($memory_object in $MemoryInformations) { - $memory_datails = @{}; - $memory_datails.Add('caption', $memory_object.Caption); - $memory_datails.Add('desc', $memory_object.Description); - $memory_datails.Add('name', $memory_object.Name); - $memory_datails.Add('install_date', $memory_object.InstallDate); - $memory_datails.Add('status', $memory_object.Status); - $memory_datails.Add('creation_class_name', $memory_object.CreationClassName); - $memory_datails.Add('manufacturer', $memory_object.Manufacturer); - $memory_datails.Add('model', $memory_object.Model); - $memory_datails.Add('other_identifiying_info', $memory_object.OtherIdentifyingInfo); - $memory_datails.Add('part_number', $memory_object.PartNumber); - $memory_datails.Add('powered_on', $memory_object.PoweredOn); - $memory_datails.Add('serial_number', $memory_object.SerialNumber); - $memory_datails.Add('sku', $memory_object.SKU); - $memory_datails.Add('tag', $memory_object.Tag); - $memory_datails.Add('version', $memory_object.Version); - $memory_datails.Add('hot_swappable', $memory_object.HotSwappable); - $memory_datails.Add('removable', $memory_object.Removable); - $memory_datails.Add('replaceable', $memory_object.Replaceable); - $memory_datails.Add('form_factor', $memory_object.FormFactor); - $memory_datails.Add('bank_label', $memory_object.BankLabel); - $memory_datails.Add('capacity', $memory_object.Capacity); - $memory_datails.Add('data_width', $memory_object.DataWidth); - $memory_datails.Add('interleave_position', $memory_object.InterleavePosition); - $memory_datails.Add('memory_type', $memory_object.MemoryType); - $memory_datails.Add('position_in_row', $memory_object.PositionInRow); - $memory_datails.Add('speed', $memory_object.Speed); - $memory_datails.Add('total_width', $memory_object.TotalWidth); - $memory_datails.Add('attributes', $memory_object.Attributes); - $memory_datails.Add('configured_clock_speed', $memory_object.ConfiguredClockSpeed); - $memory_datails.Add('configured_voltage', $memory_object.ConfiguredVoltage); - $memory_datails.Add('device_locator', $memory_object.DeviceLocator); - $memory_datails.Add('interleave_data_depth', $memory_object.InterleaveDataDepth); - $memory_datails.Add('max_voltage', $memory_object.MaxVoltage); - $memory_datails.Add('min_voltage', $memory_object.MinVoltage); - $memory_datails.Add('smbios_memory_type', $memory_object.SMBIOSMemoryType); - $memory_datails.Add('type_detail', $memory_object.TypeDetail); - $memory_datails.Add('ps_computer_name', $memory_object.PSComputerName); - - $PhysicalMemoryData.Add($memory_object.Tag, $memory_datails); -} - -return $PhysicalMemoryData; \ No newline at end of file diff --git a/modules/include/updates/hotfixes.ps1 b/modules/include/updates/hotfixes.ps1 deleted file mode 100644 index 7dba95a..0000000 --- a/modules/include/updates/hotfixes.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -param($Config = $null); - -[hashtable]$HotfixInfo = @{}; -[hashtable]$HotfixNameCache = @{}; - -# First fetch all of our hotfixes -$Hotfixes = Get-Hotfix; - -foreach ($property in $Hotfixes) { - [hashtable]$HotfixData = @{}; - foreach ($hotfix in $property.Properties) { - $HotfixData.Add($hotfix.Name, $hotfix.Value); - } - - [string]$name = [string]::Format('{0} [{1}]', $HotfixData.HotFixID, $HotfixData.InstalledOn); - - if ($HotfixNameCache.ContainsKey($name) -eq $FALSE) { - $HotfixNameCache.Add($name, 1); - } else { - $HotfixNameCache[$name] += 1; - $name = [string]::Format('{0} ({1})', $name, $HotfixNameCache[$name]); - } - - $HotfixInfo.Add($name, $HotfixData); -} - -return $HotfixInfo; \ No newline at end of file diff --git a/modules/include/updates/pending.ps1 b/modules/include/updates/pending.ps1 deleted file mode 100644 index e0726fd..0000000 --- a/modules/include/updates/pending.ps1 +++ /dev/null @@ -1,76 +0,0 @@ -param($Config = $null); - -[hashtable]$PendingUpdates = @{}; -[hashtable]$PendingUpdateNameCache = @{}; -# Fetch all informations about installed updates and add them -$WindowsUpdates = New-Object -ComObject "Microsoft.Update.Session"; -$SearchIndex = $WindowsUpdates.CreateUpdateSearcher(); - -try { - # Get a list of current pending updates which are not yet installed on the system - $Pending = $SearchIndex.Search("IsInstalled=0"); - $PendingUpdates.Add('count', $Pending.Updates.Count); - - foreach ($update in $Pending.Updates) { - [hashtable]$PendingUpdateDetails = @{}; - $PendingUpdateDetails.Add('Title', $update.Title); - $PendingUpdateDetails.Add('Deadline', $update.Deadline); - $PendingUpdateDetails.Add('Description', $update.Description); - $PendingUpdateDetails.Add('IsBeta', $update.IsBeta); - $PendingUpdateDetails.Add('IsDownloaded', $update.IsDownloaded); - $PendingUpdateDetails.Add('IsHidden', $update.IsHidden); - $PendingUpdateDetails.Add('IsInstalled', $update.IsInstalled); - $PendingUpdateDetails.Add('IsMandatory', $update.IsMandatory); - $PendingUpdateDetails.Add('IsUninstallable', $update.IsUninstallable); - $PendingUpdateDetails.Add('Languages', $update.Languages); - $PendingUpdateDetails.Add('LastDeploymentChangeTime', $update.LastDeploymentChangeTime); - $PendingUpdateDetails.Add('MaxDownloadSize', $update.MaxDownloadSize); - $PendingUpdateDetails.Add('MinDownloadSize', $update.MinDownloadSize); - $PendingUpdateDetails.Add('MoreInfoUrls', $update.MoreInfoUrls); - $PendingUpdateDetails.Add('MsrcSeverity', $update.MsrcSeverity); - $PendingUpdateDetails.Add('RecommendedCpuSpeed', $update.RecommendedCpuSpeed); - $PendingUpdateDetails.Add('RecommendedHardDiskSpace', $update.RecommendedHardDiskSpace); - $PendingUpdateDetails.Add('RecommendedMemory', $update.RecommendedMemory); - $PendingUpdateDetails.Add('ReleaseNotes', $update.ReleaseNotes); - $PendingUpdateDetails.Add('SecurityBulletinIDs', $update.SecurityBulletinIDs); - $PendingUpdateDetails.Add('SupersededUpdateIDs', $update.SupersededUpdateIDs); - $PendingUpdateDetails.Add('SupportUrl', $update.SupportUrl); - $PendingUpdateDetails.Add('Type', $update.Type); - $PendingUpdateDetails.Add('UninstallationNotes', $update.UninstallationNotes); - $PendingUpdateDetails.Add('UninstallationBehavior', $update.UninstallationBehavior); - $PendingUpdateDetails.Add('UninstallationSteps', $update.UninstallationSteps); - $PendingUpdateDetails.Add('KBArticleIDs', $update.KBArticleIDs); - $PendingUpdateDetails.Add('DeploymentAction', $update.DeploymentAction); - $PendingUpdateDetails.Add('DownloadPriority', $update.DownloadPriority); - $PendingUpdateDetails.Add('RebootRequired', $update.RebootRequired); - $PendingUpdateDetails.Add('IsPresent', $update.IsPresent); - $PendingUpdateDetails.Add('CveIDs', $update.CveIDs); - $PendingUpdateDetails.Add('BrowseOnly', $update.BrowseOnly); - $PendingUpdateDetails.Add('PerUser', $update.PerUser); - $PendingUpdateDetails.Add('AutoSelection', $update.AutoSelection); - $PendingUpdateDetails.Add('AutoDownload', $update.AutoDownload); - - [string]$name = [string]::Format('{0} [{1}]', $update.Title, $update.LastDeploymentChangeTime); - - if ($PendingUpdateNameCache.ContainsKey($name) -eq $FALSE) { - $PendingUpdateNameCache.Add($name, 1); - } else { - $PendingUpdateNameCache[$name] += 1; - $name = [string]::Format('{0} ({1})', $name, $PendingUpdateNameCache[$name]); - } - - $PendingUpdates.Add($name, $PendingUpdateDetails); - } -} catch { - if ($PendingUpdates.ContainsKey('Count') -eq $FALSE) { - $PendingUpdates.Add('count', 0); - } else { - $PendingUpdates['count'] = 0; - } - $PendingUpdates.Add('error', [string]::Format( - 'Failed to query Windows Update server: {0}', - $_.Exception.Message - )); -} - -return $PendingUpdates; \ No newline at end of file diff --git a/modules/include/updates/updates.ps1 b/modules/include/updates/updates.ps1 deleted file mode 100644 index 4432fbf..0000000 --- a/modules/include/updates/updates.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -param($Config = $null); - -# Fetch all informations about installed updates and add them -$WindowsUpdates = New-Object -ComObject "Microsoft.Update.Session"; -$SearchIndex = $WindowsUpdates.CreateUpdateSearcher(); -[hashtable]$UpdateList = @{}; -[hashtable]$UpdateInstalled = @{}; -[hashtable]$UpdateUninstalled = @{}; -[hashtable]$UpdateOther = @{}; - -# Operation ID's -# 1: Installed -# 2: Uninstalled -# 3: Other - -# At first get a list of our Windows Update history -$Updates = $SearchIndex.QueryHistory(0, $SearchIndex.GetTotalHistoryCount()) | - Select-Object Operation, ResultCode, HResult, Date, Title, Description, ServiceID, SupportUrl; - -foreach ($update in $Updates) { - [string]$UpdateKey = [string]::Format('{0} [{1}|{2}]', $update.Title, $update.Date, $update.HResult); - switch ($update.Operation) { - 1 { - if ($UpdateInstalled.ContainsKey($UpdateKey) -eq $FALSE) { - $UpdateInstalled.Add($UpdateKey, $update); - } else { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'Unable to add update "{0}" to update list. The key with content "{1}" is already present', - $UpdateKey, - $update - ) - ); - } - }; - 2 { - if ($UpdateUninstalled.ContainsKey($UpdateKey) -eq $FALSE) { - $UpdateUninstalled.Add($UpdateKey, $update); - } else { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'Unable to add update "{0}" to update list. The key with content "{1}" is already present', - $UpdateKey, - $update - ) - ); - } - }; - default { - if ($UpdateOther.ContainsKey($UpdateKey) -eq $FALSE) { - $UpdateOther.Add($UpdateKey, $update); - } else { - $Icinga2.Log.Write( - $Icinga2.Enums.LogState.Warning, - [string]::Format( - 'Unable to add update "{0}" to update list. The key with content "{1}" is already present', - $UpdateKey, - $update - ) - ); - } - }; - } -} - -$UpdateList.Add('installed', $UpdateInstalled); -$UpdateList.Add('uninstalled', $UpdateUninstalled); -$UpdateList.Add('other', $UpdateOther); - -return $UpdateList; \ No newline at end of file diff --git a/modules/memory.ps1 b/modules/memory.ps1 deleted file mode 100644 index 95dca6d..0000000 --- a/modules/memory.ps1 +++ /dev/null @@ -1,32 +0,0 @@ - - -param($Config = $null); - -function ClassMemory -{ - param($Config = $null); - # This will return a hashtable with every single counter - # We specify within the array - $counter = Get-Icinga-Counter -CounterArray @( - '\Memory\Available Bytes', - '\Memory\% Committed Bytes In Use', - '\Memory\Committed Bytes', - '\Memory\Cache Bytes', - '\Memory\Pool Nonpaged Bytes', - '\Memory\Pages/sec', - '\Memory\Page Reads/sec', - '\Memory\Page Writes/sec', - '\Memory\Pages Input/sec', - '\Memory\Pages Output/sec', - '\Paging File(*)\% Usage', - '\Paging File(*)\% Usage Peak' - ); - - # Lets load some additional memory informations, besides current performance counters - $ComputerInformation = Get-CimInstance Win32_ComputerSystem; - $counter.Add('\Memory\Physical Memory Total Bytes', @{ 'value' = $ComputerInformation.TotalPhysicalMemory; 'sample' = @{ }; }); - - return $counter; -} - -return ClassMemory -Config $Config; \ No newline at end of file diff --git a/modules/network.ps1 b/modules/network.ps1 deleted file mode 100644 index b22a470..0000000 --- a/modules/network.ps1 +++ /dev/null @@ -1,154 +0,0 @@ -param($Config = $null); - -function ClassNetwork -{ - param($Config = $null); - # The storage variables we require to store our data - [hashtable]$NetworkData = @{}; - [hashtable]$StructuredInterfaceData = @{}; - - $CachedNetwork = $Icinga2.Utils.Modules.GetCacheElement( - $MyInvocation.MyCommand.Name, - 'NetworkData' - ); - - # This will return a hashtable with every single counter - # we specify within the array. Instead of returning all - # the values in the returned hashtable, we will rebuild - # the result a little to have a improved output which - # is more user friendly and allows us to check for - # certain interfaces in detail with a simpler - # accessing possibility - $counter = Get-Icinga-Counter -CounterArray @( - '\Network Interface(*)\Bytes Received/sec', - '\Network Interface(*)\Bytes Sent/sec', - '\Network Interface(*)\Packets Received Unicast/sec', - '\Network Interface(*)\Packets Sent Unicast/sec', - '\Network Interface(*)\Packets Received Non-Unicast/sec', - '\Network Interface(*)\Packets Sent Non-Unicast/sec', - '\Network Interface(*)\Packets Outbound Errors', - '\Network Interface(*)\Packets Sent/sec', - '\Network Interface(*)\TCP RSC Exceptions/sec', - '\Network Interface(*)\Packets Outbound Discarded', - '\Network Interface(*)\TCP RSC Coalesced Packets/sec', - '\Network Interface(*)\Bytes Total/sec', - '\Network Interface(*)\Current Bandwidth', - '\Network Interface(*)\Packets Received Unknown', - '\Network Interface(*)\TCP Active RSC Connections', - '\Network Interface(*)\Offloaded Connections', - '\Network Interface(*)\Packets/sec', - '\Network Interface(*)\Packets Received Errors', - '\Network Interface(*)\Packets Received/sec', - '\Network Interface(*)\Packets Received Discarded', - '\Network Interface(*)\Output Queue Length', - '\Network Interface(*)\TCP RSC Average Packet Size' - ); - - # This function will help us to build a structured output based on - # interfaces found within the instances. We will use our - # Network Interface as 'index' to assign our performance Counters to. - # In addition we then provide the hashtable of counters we fetched - # above. - $StructuredInterfaceData = Get-Icinga-Counter ` - -CreateStructuredOutputForCategory 'Network Interface' ` - -StructuredCounterInput $counter; - - $NetworkData.Add('interfaces', $StructuredInterfaceData); - - # Add additional details to our interfaces, like MAC Address, Interface Index and current connection status - $NetworkAdapter = Get-WMIObject Win32_NetworkAdapter; - - [hashtable]$DuplicateIDCache = @{}; - - foreach ($adapter in $NetworkAdapter) { - # The Performance Counter return values in brackets with [], while WMI returns () - # In addition, WMI uses / within Interface names, while Perf Counter uses _ - # We need to take care about this here - [string]$AdapterName = $adapter.Name.Replace('(', '[').Replace(')', ']'); - [string]$AdapterName = $AdapterName.Replace('/', '_'); - [string]$AdapterName = $AdapterName.Replace('#', '_'); - - # To ensure duplicate interface names will not cause this module - # to crash, we will have to build up a cache and add numeric - # additions. - if ($DuplicateIDCache.ContainsKey($AdapterName) -eq $FALSE) { - $DuplicateIDCache.Add($AdapterName, 0); - } - - # In case we add adapters we have no performance counters for, - # create a new hashtable object for the name - if ($StructuredInterfaceData.ContainsKey($AdapterName) -eq $FALSE) { - $StructuredInterfaceData.Add($AdapterName, @{}); - } else { - # In case the interface does already exist, check if we require - # to rename the interface with a index ID in addition. As we - # have to ensure Performance Counters are added to Physical Adapters - # only, we will focus in these indexes. All other instances will - # receive a follow-up ID. - [int]$ID = $DuplicateIDCache[$AdapterName] + 1; - if ($adapter.PhysicalAdapter -eq $FALSE) { - $ID += 1; - $DuplicateIDCache[$AdapterName] = $ID; - } else { - # Physical Adapters always have unique names, therefor we should - # always use index 1 for these to ensure Performance Counter data - # is added to the correct interfaces - $ID = 1; - } - - # Only add index ID's to interfaces in case we are not equal 1 - if ($ID -ne 1) { - $AdapterName = [string]::Format('{0} ({1})', - $AdapterName, - $ID - ); - $StructuredInterfaceData.Add($AdapterName, @{}); - } - } - - # Add the WMI informations to this interface - $StructuredInterfaceData[$AdapterName].Add('NetConnectionID', $adapter.NetConnectionID); - $StructuredInterfaceData[$AdapterName].Add('InterfaceIndex', $adapter.InterfaceIndex); - $StructuredInterfaceData[$AdapterName].Add('NetConnectionStatus', $adapter.NetConnectionStatus); - $StructuredInterfaceData[$AdapterName].Add('DeviceID', $adapter.DeviceID); - $StructuredInterfaceData[$AdapterName].Add('MACAddress', $adapter.MACAddress); - $StructuredInterfaceData[$AdapterName].Add('ServiceName', $adapter.ServiceName); - $StructuredInterfaceData[$AdapterName].Add('Speed', $adapter.Speed); - $StructuredInterfaceData[$AdapterName].Add('AdapterType', $adapter.AdapterType); - $StructuredInterfaceData[$AdapterName].Add('NetworkAddresses', $adapter.NetworkAddresses); - $StructuredInterfaceData[$AdapterName].Add('Manufacturer', $adapter.Manufacturer); - $StructuredInterfaceData[$AdapterName].Add('PNPDeviceID', $adapter.PNPDeviceID); - $StructuredInterfaceData[$AdapterName].Add('PhysicalAdapter', $adapter.PhysicalAdapter); - } - - # In addition to our general network interface data, it might also - # be helpful to have a look on the configured routing table - $RoutingTable = Get-CimInstance -ClassName Win32_IP4RouteTable - [array]$RoutingData = @(); - - foreach ($tables in $RoutingTable) { - $routes = @{}; - foreach($route in $tables.CimInstanceProperties) { - $routes.Add($route.Name, $route.Value); - } - $RoutingData += $routes; - } - - $NetworkData.Add('routes', $RoutingData); - - $Icinga2.Utils.Modules.AddCacheElement( - $MyInvocation.MyCommand.Name, - 'NetworkData', - $NetworkData - ); - - return $Icinga2.Utils.Modules.GetHashtableDiff( - $NetworkData.Clone(), - $CachedNetwork.Clone() - ); - - # At the end simply return the entire hashtable - return $NetworkData; -} - -return ClassNetwork -Config $Config; \ No newline at end of file diff --git a/modules/ntp.ps1 b/modules/ntp.ps1 deleted file mode 100644 index 211773a..0000000 --- a/modules/ntp.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -param($Config = $null); - -function ClassNTP -{ - param($Config = $null); - [hashtable]$NTPInformations = @{}; - # This will return a hashtable with every single counter - # we specify within the array - $counter = Get-Icinga-Counter -CounterArray @( - '\Windows Time service\clock frequency adjustment', - '\Windows Time service\ntp client time source count', - '\Windows Time service\ntp server outgoing responses', - '\Windows Time service\computed time offset', - '\Windows Time service\ntp roundtrip delay', - '\Windows Time service\ntp server incoming requests' - ); - $NTPInformations.Add('counter', $counter); - - # Load the source from which we receive our NTP config - $NTPInformations.Add('source', (&W32tm /query /source)); - - # Load the NTP config and parse it properly - $NTPInformations.Add( - 'config', - $Icinga2.Utils.IniParser.LoadFromArray( - (&W32tm /query /configuration), - $TRUE - ) - ); - - return $NTPInformations; -} - -return ClassNTP -Config $Config; \ No newline at end of file diff --git a/modules/process.ps1 b/modules/process.ps1 deleted file mode 100644 index 15cc71b..0000000 --- a/modules/process.ps1 +++ /dev/null @@ -1,113 +0,0 @@ -param($Config = $null); - -$CachedProcessList = $Icinga2.Utils.Modules.GetCacheElement( - $MyInvocation.MyCommand.Name, - 'ProcessList' -); - -$ProcessList = Get-WmiObject Win32_Process; -$ProcessPerfList = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process; - -$NumberOfCPUThreads = $Icinga2.System.NumberOfCPUThreads; - -[hashtable]$ProcessReference = @{}; -[hashtable]$Processes = @{}; -[hashtable]$ProcessValues = @{ - FullList = @{ }; - Removed = @( ); - Added = $null; - Modified = @{ }; -} - -foreach ($process in $ProcessList) { - [string]$ProcessKey = [string]::Format( - '{0} [{1}]', - $process.ProcessName, - $process.ProcessId - ); - - [hashtable]$ProcessInfo = @{}; - - $ProcessInfo.Add('Name', $process.Name); - $ProcessInfo.Add('ProcessId', $process.ProcessId); - $ProcessInfo.Add('Priority', $process.Priority); - $ProcessInfo.Add('PageFileUsage', $process.PageFileUsage); - $ProcessInfo.Add('ThreadCount', $process.ThreadCount); - $ProcessInfo.Add('KernelModeTime', $process.KernelModeTime); - $ProcessInfo.Add('UserModeTime', $process.UserModeTime); - $ProcessInfo.Add('WorkingSetSize', $process.WorkingSetSize); - $ProcessInfo.Add('CommandLine', $process.CommandLine); -<# - # These are not required by now - $ProcessInfo.Add('Caption', $process.Caption); - $ProcessInfo.Add('CreationClassName', $process.CreationClassName); - $ProcessInfo.Add('CreationDate', $process.CreationDate); - $ProcessInfo.Add('CSCreationClassName', $process.CSCreationClassName); - $ProcessInfo.Add('CSName', $process.CSName); - $ProcessInfo.Add('Description', $process.Description); - $ProcessInfo.Add('ExecutablePath', $process.ExecutablePath); - $ProcessInfo.Add('ExecutionState', $process.ExecutionState); - $ProcessInfo.Add('Handle', $process.Handle); - $ProcessInfo.Add('HandleCount', $process.HandleCount); - $ProcessInfo.Add('InstallDate', $process.InstallDate); - $ProcessInfo.Add('MaximumWorkingSetSize', $process.MaximumWorkingSetSize); - $ProcessInfo.Add('MinimumWorkingSetSize', $process.MinimumWorkingSetSize); - $ProcessInfo.Add('OSCreationClassName', $process.OSCreationClassName); - $ProcessInfo.Add('OSName', $process.OSName); - $ProcessInfo.Add('OtherOperationCount', $process.OtherOperationCount); - $ProcessInfo.Add('OtherTransferCount', $process.OtherTransferCount); - $ProcessInfo.Add('PageFaults', $process.PageFaults); - $ProcessInfo.Add('ParentProcessId', $process.ParentProcessId); - $ProcessInfo.Add('PeakPageFileUsage', $process.PeakPageFileUsage); - $ProcessInfo.Add('PeakVirtualSize', $process.PeakVirtualSize); - $ProcessInfo.Add('PeakWorkingSetSize', $process.PeakWorkingSetSize); - $ProcessInfo.Add('PrivatePageCount', $process.PrivatePageCount); - $ProcessInfo.Add('QuotaNonPagedPoolUsage', $process.QuotaNonPagedPoolUsage); - $ProcessInfo.Add('QuotaPagedPoolUsage', $process.QuotaPagedPoolUsage); - $ProcessInfo.Add('QuotaPeakNonPagedPoolUsage', $process.QuotaPeakNonPagedPoolUsage); - $ProcessInfo.Add('QuotaPeakPagedPoolUsage', $process.QuotaPeakPagedPoolUsage); - $ProcessInfo.Add('ReadOperationCount', $process.ReadOperationCount); - $ProcessInfo.Add('ReadTransferCount', $process.ReadTransferCount); - $ProcessInfo.Add('SessionId', $process.SessionId); - $ProcessInfo.Add('Status', $process.Status); - $ProcessInfo.Add('TerminationDate', $process.TerminationDate); - $ProcessInfo.Add('VirtualSize', $process.VirtualSize); - $ProcessInfo.Add('WindowsVersion', $process.WindowsVersion); - $ProcessInfo.Add('WriteOperationCount', $process.WriteOperationCount); - $ProcessInfo.Add('WriteTransferCount', $process.WriteTransferCount); -#> - $ProcessReference.Add($process.ProcessId, $ProcessKey); - $Processes.Add($ProcessKey, $ProcessInfo); -} - -foreach ($perfdata in $ProcessPerfList) { - if ($perfdata.Name -eq '_Total') { - continue; - } - if ($ProcessReference.ContainsKey($perfdata.IDProcess)) { - $Processes[$ProcessReference[$perfdata.IDProcess]].Add( - 'WorkingSetPrivate', - $perfdata.WorkingSetPrivate - ); - # Note: In order to get the correct CPU time in % we have to divide the - # Processor Time with the amount of threads installed on our CPU - $Processes[$ProcessReference[$perfdata.IDProcess]].Add( - 'PercentProcessorTime', - [math]::Round(($perfdata.PercentProcessorTime / $NumberOfCPUThreads), 2) - ); - } -} - -$Processes.Add('count', $Processes.count); - -$Icinga2.Utils.Modules.AddCacheElement( - $MyInvocation.MyCommand.Name, - 'ProcessList', - $Processes -); - -return $Icinga2.Utils.Modules.GetHashtableDiff( - $Processes.Clone(), - $CachedProcessList.Clone(), - @('ProcessId') -); \ No newline at end of file diff --git a/modules/services.ps1 b/modules/services.ps1 deleted file mode 100644 index 02ea528..0000000 --- a/modules/services.ps1 +++ /dev/null @@ -1,62 +0,0 @@ -param($Config = $null); - -function ClassService() -{ - param($Config = $null); - $services = Get-Service; - - [hashtable]$ServiceData = @{}; - - $CachedServiceData = $Icinga2.Utils.Modules.GetCacheElement( - $MyInvocation.MyCommand.Name, - 'ServiceData' - ); - - foreach ($service in $services) { - [hashtable]$ServiceInfo = @{}; - - $ServiceInfo.Add('display_name', $service.DisplayName); - $ServiceInfo.Add('service_name', $service.ServiceName); - $ServiceInfo.Add('can_pause_and_continue', $service.CanPauseAndContinue); - $ServiceInfo.Add('can_shutdown', $service.CanShutdown); - $ServiceInfo.Add('can_stop', $service.CanStop); - $ServiceInfo.Add('service_handle', $service.ServiceHandle); - $ServiceInfo.Add('status', $service.Status); - $ServiceInfo.Add('service_type', $service.ServiceType); - $ServiceInfo.Add('start_type', $service.StartType); - $ServiceInfo.Add('site', $service.Site); - $ServiceInfo.Add('container', $service.Container); - - [array]$DependentServices = $null; - foreach ($dependency in $service.DependentServices) { - if ($DependentServices -eq $null) { $DependentServices = @(); } - $DependentServices += $dependency.Name; - } - $ServiceInfo.Add('dependent_services', $DependentServices); - - [array]$DependentServices = $null; - foreach ($dependency in $service.ServicesDependedOn) { - if ($DependentServices -eq $null) { $DependentServices = @(); } - $DependentServices += $dependency.Name; - } - $ServiceInfo.Add('depends_on', $DependentServices); - - $ServiceData.Add($service.Name, $ServiceInfo); - } - - $Icinga2.Utils.Modules.AddCacheElement( - $MyInvocation.MyCommand.Name, - 'ServiceData', - $ServiceData - ); - - return $Icinga2.Utils.Modules.GetHashtableDiff( - $ServiceData.Clone(), - $CachedServiceData.Clone(), - @('service_name') - ); - - return $ServiceData; -} - -return ClassService -Config $Config; \ No newline at end of file diff --git a/modules/updates.ps1 b/modules/updates.ps1 deleted file mode 100644 index ea47044..0000000 --- a/modules/updates.ps1 +++ /dev/null @@ -1,6 +0,0 @@ -param($Config = $null); - -return $Icinga2.Utils.Modules.LoadIncludes( - $MyInvocation.MyCommand.Name, - $Config -); \ No newline at end of file diff --git a/modules/windows.ps1 b/modules/windows.ps1 deleted file mode 100644 index 057defe..0000000 --- a/modules/windows.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -param($Config = $null); - -function ClassWindows -{ - param($Config = $null); - $WindowsInformations = Get-CimInstance Win32_OperatingSystem; - - $windows_datails = @{}; - foreach($cpu_core in $WindowsInformations.CimInstanceProperties) { - $windows_datails.Add($cpu_core.Name, $cpu_core.Value); - } - - return $windows_datails; -} - -return ClassWindows -Config $Config; \ No newline at end of file