├── .gitattributes ├── .gitignore ├── check_ms_win_disk_load.php ├── check_ms_win_disk_load.ps1 └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # ========================= 18 | # Operating System Files 19 | # ========================= 20 | 21 | # OSX 22 | # ========================= 23 | 24 | .DS_Store 25 | .AppleDouble 26 | .LSOverride 27 | 28 | # Icon must end with two \r 29 | Icon 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /check_ms_win_disk_load.php: -------------------------------------------------------------------------------- 1 | MACRO['DISP_HOSTNAME'] . " / " . $this->MACRO['DISP_SERVICEDESC'] . " \" "; 29 | $def[1] = ''; 30 | 31 | foreach ($DS as $i) { 32 | $def[1] .= rrd::def("var$i", $RRDFILE[1], $DS[$i], 'AVERAGE'); 33 | $def[1] .= rrd::line2("var$i", $colors[$i], "$NAME[$i]"); 34 | $def[1] .= rrd::gprint("var$i" ,'LAST' ,"Last\:%5.0lf%s$UNIT[1]"); 35 | $def[1] .= rrd::gprint("var$i" ,'MIN' ,"min\:%5.0lf%s$UNIT[1]"); 36 | $def[1] .= rrd::gprint("var$i" ,'MAX' ,"max\:%5.0lf%s$UNIT[1]"); 37 | $def[1] .= rrd::gprint("var$i" ,'AVERAGE' ,"avg\:%5.0lf%s$UNIT[1] \\r"); 38 | 39 | }; 40 | 41 | $def[1] .= rrd::comment("\\r"); 42 | $def[1] .= rrd::comment("Disk Load Monitoring on Windows\\r"); 43 | $def[1] .= rrd::comment("Command " . $TEMPLATE[1] . "\\r"); 44 | 45 | ?> 46 | -------------------------------------------------------------------------------- /check_ms_win_disk_load.ps1: -------------------------------------------------------------------------------- 1 | # Script name: check_ms_windows_disk_load.ps1 2 | # Version: v2.04.160115 3 | # Created on: 10/10/2014 4 | # Author: D'Haese Willem 5 | # Purpose: Check MS Windows disk load by using Powershell to get all disk load related counters from Windows 6 | # Performance Manager, computing averages for all gathered samples and calculating read / write rate, 7 | # number of reads / writes, read / write latency and read / write queue length. 8 | # On Github: https://github.com/OutsideIT/check_ms_win_disk_load 9 | # Copyright: 10 | # This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published 11 | # by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed 12 | # in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | # PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public 14 | # License along with this program. If not, see . 15 | 16 | #Requires –Version 2.0 17 | 18 | $DebugPreference = 'SilentlyContinue' 19 | $VerbosePreference = 'SilentlyContinue' 20 | [Int]$DefaultInt = -99 21 | 22 | $DiskStruct = New-object PSObject -Property @{ 23 | Hostname = ([System.Net.Dns]::GetHostByName((hostname.exe)).HostName).tolower(); 24 | DiskLetter = 'C'; 25 | Exitcode = 3; 26 | MaxSamples = 2; 27 | LogicalDiskId = 236; 28 | AvgDiskSecReadId = 208; 29 | AvgDiskSecReadValue = 0; 30 | AvgDiskSecWriteId = 210; 31 | AvgDiskSecWriteValue = 0; 32 | AvgDiskReadQueueId = 1402; 33 | AvgDiskReadQueueValue = 0; 34 | AvgDiskWriteQueueId = 1404; 35 | AvgDiskWriteQueueValue = 0; 36 | DiskReadsSecId = 214; 37 | DiskReadsSecValue = 0; 38 | DiskWritesSecId = 216; 39 | DiskWritesSecValue = 0; 40 | DiskReadBytesSecId = 220; 41 | DiskReadBytesSecValue = 0; 42 | DiskWriteBytesSecId = 222; 43 | DiskWriteBytesSecValue = 0; 44 | AvgDiskReadQueueWarn = $DefaultInt; 45 | AvgDiskReadQueueCrit = $DefaultInt; 46 | AvgDiskWriteQueueWarn = $DefaultInt; 47 | AvgDiskWriteQueueCrit = $DefaultInt; 48 | ReturnString = 'UNKNOWN: Please debug the script...' 49 | } 50 | 51 | #region Functions 52 | 53 | function Write-Log { 54 | [CmdletBinding()] 55 | param ( 56 | [parameter(Mandatory=$true)] 57 | [string]$Log, 58 | [parameter(Mandatory=$true)] 59 | [ValidateSet('Debug', 'Info', 'Warning', 'Error')] 60 | [string]$Severity, 61 | [parameter(Mandatory=$true)] 62 | [string]$Message 63 | ) 64 | $Now = Get-Date -Format 'yyyy-MM-dd HH:mm:ss,fff' 65 | if ($Log -eq 'Verbose') { 66 | Write-Verbose "${Now}: ${Severity}: $Message" 67 | } 68 | elseif ($Log -eq 'Debug') { 69 | Write-Debug "${Now}: ${Severity}: $Message" 70 | } 71 | elseif ($Log -eq 'Output') { 72 | Write-Host "${Now}: ${Severity}: $Message" 73 | } 74 | elseif ($Log -match '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::(?\d+))$' -or $Log -match "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") { 75 | $IpOrHost = $log.Split(':')[0] 76 | $Port = $log.Split(':')[1] 77 | if ($IpOrHost -match '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$') { 78 | $Ip = $IpOrHost 79 | } 80 | else { 81 | $Ip = ([System.Net.Dns]::GetHostAddresses($IpOrHost)).IPAddressToString 82 | } 83 | Try { 84 | $JsonObject = (New-Object PSObject | Add-Member -PassThru NoteProperty logdestination $Log | Add-Member -PassThru NoteProperty logtime $Now| Add-Member -PassThru NoteProperty severity $Severity | Add-Member -PassThru NoteProperty message $Message ) | ConvertTo-Json 85 | $JsonString = $JsonObject -replace "`n",' ' -replace "`r",' ' -replace ' ','' 86 | $Socket = New-Object System.Net.Sockets.TCPClient($Ap,$Port) 87 | $Stream = $Socket.GetStream() 88 | $Writer = New-Object System.IO.StreamWriter($Stream) 89 | $Writer.WriteLine($JsonString) 90 | $Writer.Flush() 91 | $Stream.Close() 92 | $Socket.Close() 93 | } 94 | catch { 95 | Write-Host "${Now}: Error: Something went wrong while trying to send message to Logstash server `"$Log`"." 96 | } 97 | Write-Host "${Now}: ${Severity}: Ip: $Ip Port: $Port JsonString: $JsonString" 98 | } 99 | elseif ($Log -match '^((([a-zA-Z]:)|(\\{2}\w+)|(\\{2}(?:(?:25[0-5]|2[0-4]\d|[01]\d\d|\d?\d)(?(?=\.?\d)\.)){4}))(\\(\w[\w ]*))*)') { 100 | if (Test-Path -Path $Log -pathType container){ 101 | Write-Host "${Now}: Error: Passed Path is a directory. Please provide a file." 102 | exit 1 103 | } 104 | elseif (!(Test-Path -Path $Log)) { 105 | try { 106 | New-Item -Path $Log -Type file -Force | Out-null 107 | } 108 | catch { 109 | $Now = Get-Date -Format 'yyyy-MM-dd HH:mm:ss,fff' 110 | Write-Host "${Now}: Error: Write-Log was unable to find or create the path `"$Log`". Please debug.." 111 | exit 1 112 | } 113 | } 114 | try { 115 | "${Now}: ${Severity}: $Message" | Out-File -filepath $Log -Append 116 | } 117 | catch { 118 | Write-Host "${Now}: Error: Something went wrong while writing to file `"$Log`". It might be locked." 119 | } 120 | } 121 | } 122 | 123 | Function Initialize-Args { 124 | Param ( 125 | [Parameter(Mandatory=$True)]$Args 126 | ) 127 | 128 | try { 129 | For ( $i = 0; $i -lt $Args.count; $i++ ) { 130 | $CurrentArg = $Args[$i].ToString() 131 | if ($i -lt $Args.Count-1) { 132 | $Value = $Args[$i+1]; 133 | If ($Value.Count -ge 2) { 134 | foreach ($Item in $Value) { 135 | Test-Strings $Item | Out-Null 136 | } 137 | } 138 | else { 139 | $Value = $Args[$i+1]; 140 | Test-Strings $Value | Out-Null 141 | } 142 | } else { 143 | $Value = '' 144 | }; 145 | 146 | switch -regex -casesensitive ($CurrentArg) { 147 | "^(-H|--Hostname)$" { 148 | if ($value -match "^[a-zA-Z0-9._-]+$") { 149 | $DiskStruct.Hostname = $value 150 | } else { 151 | throw "Hostname does not meet regex requirements (`"^[a-zA-Z0-9._-]+$`"). Value given is `"$value`"." 152 | } 153 | $i++ 154 | } 155 | "^(-dl|--DiskLetter)$" { 156 | if ($value -match "^[a-zA-Z]$") { 157 | $DiskStruct.DiskLetter = $value 158 | } else { 159 | throw "Diskletter does not meet regex requirements (`"^[a-zA-Z]$`"). Value given is `"$value`"." 160 | } 161 | $i++ 162 | } 163 | "^(-rqw|--ReadQueueWarn)$" { 164 | if (($value -match "^[\d]+$") -and ([int]$value -lt 999999)) { 165 | $DiskStruct.AvgDiskReadQueueWarn = $value 166 | } else { 167 | throw "Read queue warning does not meet regex requirements (`"^[\d]+$`"). Value given is `"$value`"." 168 | } 169 | $i++ 170 | } 171 | "^(-rqc|--ReadQueueCrit)$" { 172 | if (($value -match "^[\d]+$") -and ([int]$value -lt 999999)) { 173 | $DiskStruct.AvgDiskReadQueueCrit = $value 174 | } else { 175 | throw "Read queue critical does not meet regex requirements (`"^[\d]+$`"). Value given is `"$value`"." 176 | } 177 | $i++ 178 | } 179 | "^(-wqw|--WriteQueueWarn)$" { 180 | if (($value -match "^[\d]+$") -and ([int]$value -lt 999999)) { 181 | $DiskStruct.AvgDiskWriteQueueWarn = $value 182 | } else { 183 | throw "Write queue warning does not meet regex requirements (`"^[\d]+$`"). Value given is `"$value`"." 184 | } 185 | $i++ 186 | } 187 | "^(-wqc|--WriteQueueCrit)$" { 188 | if (($value -match "^[\d]+$") -and ([int]$value -lt 999999)) { 189 | $DiskStruct.AvgDiskWriteQueueCrit = $value 190 | } else { 191 | throw "Write queue critical does not meet regex requirements (`"^[\d]+$`"). Value given is `"$value`"." 192 | } 193 | $i++ 194 | } 195 | "^(-ms|--MaxSamples)$" { 196 | if (($value -match "^[\d]+$") -and ([int]$value -lt 100)) { 197 | $DiskStruct.MaxSamples = $value 198 | } else { 199 | throw "Write queue critical does not meet regex requirements (`"^[\d]+$`"). Value given is `"$value`"." 200 | } 201 | $i++ 202 | } 203 | "^(-h|--Help)$" { 204 | Write-Help 205 | } 206 | default { 207 | throw "Illegal arguments detected: $_" 208 | } 209 | } 210 | } 211 | } 212 | catch { 213 | Write-Host "Error: $_" 214 | Exit 2 215 | } 216 | } 217 | 218 | Function Test-Strings { 219 | Param ( [Parameter(Mandatory=$True)][string]$String ) 220 | $BadChars=@("``", '|', ';', "`n") 221 | $BadChars | ForEach-Object { 222 | If ( $String.Contains("$_") ) { 223 | Write-Host "Error: String `"$String`" contains illegal characters." 224 | Exit $DiskStruct.ExitCode 225 | } 226 | } 227 | Return $true 228 | } 229 | 230 | Function Write-Help { 231 | Write-Host @" 232 | check_ms_windows_disk_load.ps1: 233 | This script is designed to monitor Microsoft Windows disk load. 234 | Arguments: 235 | -H | --Hostname => Optional hostname of remote system, default is localhost, not yet tested on remote host. 236 | -dl | --DiskLetter => Diskletter to get data from. 237 | -rqw | --ReadQueueWarn => Warning threshold for read queue length. 238 | -rqc | --ReadQueueCrit => Critical threshold for read queue length. 239 | -wqw | --WriteQueueWarn => Warning threshold for write queue length. 240 | -wqc | --WriteQueueCrit => Critical threshold for write queue length. 241 | -ms | --MaxSamples => Amount of samples to take. 242 | -h | --Help => Print this help output. 243 | "@ 244 | Exit $DiskStruct.ExitCode; 245 | } 246 | 247 | function Get-PerformanceCounterID 248 | { 249 | param ( 250 | [Parameter(Mandatory=$true)] $Name 251 | ) 252 | if ($script:perfHash -eq $null) { 253 | Write-Progress -Activity 'Retrieving PerfIDs' -Status 'Working' 254 | $key = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage' 255 | $counters = (Get-ItemProperty -Path $key -Name Counter).Counter 256 | $script:perfHash = @{} 257 | $all = $counters.Count 258 | for($i = 0; $i -lt $all; $i+=2) { 259 | Write-Progress -Activity 'Retrieving PerfIDs' -Status 'Working' -PercentComplete ($i*100/$all) 260 | $script:perfHash.$($counters[$i+1]) = $counters[$i] 261 | } 262 | } 263 | $script:perfHash.$Name 264 | } 265 | 266 | Function Get-PerformanceCounterLocalName 267 | { 268 | param ( 269 | [UInt32]$ID, 270 | $ComputerName = $env:COMPUTERNAME 271 | ) 272 | $code = '[DllImport("pdh.dll", SetLastError=true, CharSet=CharSet.Unicode)] public static extern UInt32 PdhLookupPerfNameByIndex(string szMachineName, uint dwNameIndex, System.Text.StringBuilder szNameBuffer, ref uint pcchNameBufferSize);' 273 | $Buffer = New-Object System.Text.StringBuilder(1024) 274 | [UInt32]$BufferSize = $Buffer.Capacity 275 | $t = Add-Type -MemberDefinition $code -PassThru -Name PerfCounter -Namespace Utility 276 | $rv = $t::PdhLookupPerfNameByIndex($ComputerName, $id, $Buffer, [Ref]$BufferSize) 277 | if ($rv -eq 0) { 278 | $Buffer.ToString().Substring(0, $BufferSize-1) 279 | } 280 | else { 281 | Throw 'Get-PerformanceCounterLocalName : Unable to retrieve localized name. Check computer name and performance counter ID.' 282 | } 283 | } 284 | 285 | function Get-DiskLoadCounters 286 | { 287 | $PerfCounterArray = @() 288 | 289 | $LogicalDisk = Get-PerformanceCounterLocalName $DiskStruct.LogicalDiskId 290 | $AvgDiskSecRead = Get-PerformanceCounterLocalName $DiskStruct.AvgDiskSecReadId 291 | $PerfCounterArray += "\$LogicalDisk($($DiskStruct.DiskLetter):)\$AvgDiskSecRead" 292 | $AvgDiskSecWrite = Get-PerformanceCounterLocalName $DiskStruct.AvgDiskSecWriteId 293 | $PerfCounterArray += "\$LogicalDisk($($DiskStruct.DiskLetter):)\$AvgDiskSecWrite" 294 | $AvgDiskReadQueue = Get-PerformanceCounterLocalName $DiskStruct.AvgDiskReadQueueId 295 | $PerfCounterArray += "\$LogicalDisk($($DiskStruct.DiskLetter):)\$AvgDiskReadQueue" 296 | $AvgDiskWriteQueue = Get-PerformanceCounterLocalName $DiskStruct.AvgDiskWriteQueueId 297 | $PerfCounterArray += "\$LogicalDisk($($DiskStruct.DiskLetter):)\$AvgDiskWriteQueue" 298 | $AvgDiskReadsSec = Get-PerformanceCounterLocalName $DiskStruct.DiskReadsSecId 299 | $PerfCounterArray += "\$LogicalDisk($($DiskStruct.DiskLetter):)\$AvgDiskReadsSec" 300 | $AvgDiskWritesSec = Get-PerformanceCounterLocalName $DiskStruct.DiskWritesSecId 301 | $PerfCounterArray += "\$LogicalDisk($($DiskStruct.DiskLetter):)\$AvgDiskWritesSec" 302 | $AvgDiskReadBytesSec = Get-PerformanceCounterLocalName $DiskStruct.DiskReadBytesSecId 303 | $PerfCounterArray += "\$LogicalDisk($($DiskStruct.DiskLetter):)\$AvgDiskReadBytesSec" 304 | $AvgDiskWriteBytesSec = Get-PerformanceCounterLocalName $DiskStruct.DiskWriteBytesSecId 305 | $PerfCounterArray += "\$LogicalDisk($($DiskStruct.DiskLetter):)\$AvgDiskWriteBytesSec" 306 | $PfcValues = (Get-Counter $PerfCounterArray -MaxSamples $DiskStruct.MaxSamples) 307 | $AvgDiskSecReadValues = @() 308 | $AvgDiskSecWriteValues = @() 309 | $AvgDiskReadQueueValues = @() 310 | $AvgDiskWriteQueueValues = @() 311 | $AvgDiskReadsSecValues = @() 312 | $AvgDiskWritesSecValues = @() 313 | $AvgDiskReadBytesSecValues = @() 314 | $AvgDiskWriteBytesSecValues = @() 315 | for ($y=0; $y -lt $DiskStruct.MaxSamples; $y++) { 316 | $AvgDiskSecReadValues += $PfcValues[$y].CounterSamples[0].CookedValue 317 | $AvgDiskSecWriteValues += $PfcValues[$y].CounterSamples[1].CookedValue 318 | $AvgDiskReadQueueValues += $PfcValues[$y].CounterSamples[2].CookedValue 319 | $AvgDiskWriteQueueValues += $PfcValues[$y].CounterSamples[3].CookedValue 320 | $AvgDiskReadsSecValues += $PfcValues[$y].CounterSamples[4].CookedValue 321 | $AvgDiskWritesSecValues += $PfcValues[$y].CounterSamples[5].CookedValue 322 | $AvgDiskReadBytesSecValues += $PfcValues[$y].CounterSamples[6].CookedValue 323 | $AvgDiskWriteBytesSecValues += $PfcValues[$y].CounterSamples[7].CookedValue 324 | } 325 | $AvgObjDiskSecReadValues = $AvgDiskSecReadValues | Measure-Object -Average 326 | $AvgObjDiskSecWriteValues = $AvgDiskSecWriteValues | Measure-Object -Average 327 | $AvgObjDiskReadQueueValues = $AvgDiskReadQueueValues | Measure-Object -Average 328 | $AvgObjDiskWriteQueueValues = $AvgDiskWriteQueueValues | Measure-Object -Average 329 | $AvgObjDiskReadsSecValues = $AvgDiskReadsSecValues | Measure-Object -Average 330 | $AvgObjDiskWritesSecValues = $AvgDiskWritesSecValues | Measure-Object -Average 331 | $AvgObjDiskReadBytesSecValues = $AvgDiskReadBytesSecValues | Measure-Object -Average 332 | $AvgObjDiskWriteBytesSecValues = $AvgDiskWriteBytesSecValues | Measure-Object -Average 333 | $OldCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture 334 | [System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US' 335 | $DiskStruct.AvgDiskSecReadValue = [decimal]('{0:N5}' -f ($AvgObjDiskSecReadValues.average * 1000)) 336 | $DiskStruct.AvgDiskSecWriteValue = [decimal]('{0:N5}' -f ($AvgObjDiskSecWriteValues.average * 1000)) 337 | $DiskStruct.AvgDiskReadQueueValue = [decimal]('{0:N5}' -f ($AvgObjDiskReadQueueValues.average)) 338 | $DiskStruct.AvgDiskWriteQueueValue = [decimal]('{0:N5}' -f ($AvgObjDiskWriteQueueValues.average)) 339 | $DiskStruct.DiskReadsSecValue = [decimal]('{0:N5}' -f ($AvgObjDiskReadsSecValues.average)) 340 | $DiskStruct.DiskWritesSecValue = [decimal]('{0:N5}' -f ($AvgObjDiskWritesSecValues.average)) 341 | $DiskStruct.DiskReadBytesSecValue = [decimal]('{0:N5}' -f ($AvgObjDiskReadBytesSecValues.average / 1024 / 1024)) 342 | $DiskStruct.DiskWriteBytesSecValue = [decimal]('{0:N5}' -f ($AvgObjDiskWriteBytesSecValues.average / 1024 / 1024)) 343 | [System.Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture 344 | $ReadQueueCritThreshReached = $false 345 | $ReadQueueWarnThreshReached = $false 346 | $WriteQueueCritThreshReached = $false 347 | $WriteQueueWarnThreshReached = $false 348 | if ($DiskStruct.AvgDiskReadQueueCrit -ne $DefaultInt -and $DiskStruct.AvgDiskReadQueueValue -gt $DiskStruct.AvgDiskReadQueueCrit) { 349 | $DiskStruct.ExitCode = 2 350 | $ReadQueueCritThreshReached = $true 351 | $OutputReadQueue = "CRITICAL: Read Queue Threshold ($($DiskStruct.AvgDiskReadQueueCrit)) Passed!" 352 | } 353 | elseif ($DiskStruct.AvgDiskReadQueueWarn -ne $DefaultInt -and $DiskStruct.AvgDiskReadQueueValue -gt $DiskStruct.AvgDiskReadQueueWarn) { 354 | $DiskStruct.ExitCode = 1 355 | $ReadQueueWarnThreshReached = $true 356 | $OutputReadQueue = "WARNING: Read Queue Threshold ($($DiskStruct.AvgDiskReadQueueWarn)) Passed!" 357 | } 358 | if ($DiskStruct.AvgDiskWriteQueueCrit -ne $DefaultInt -and $DiskStruct.AvgDiskWriteQueueValue -gt $DiskStruct.AvgDiskWriteQueueCrit) { 359 | $DiskStruct.ExitCode = 2 360 | $WriteQueueCritThreshReached = $true 361 | $OutputWriteQueue = "CRITICAL: Write Queue Threshold ($($DiskStruct.AvgDiskWriteQueueCrit)) Passed!" 362 | } 363 | elseif ($DiskStruct.AvgDiskWriteQueueWarn -ne $DefaultInt -and $DiskStruct.AvgDiskWriteQueueValue -gt $DiskStruct.AvgDiskWriteQueueWarn) { 364 | $DiskStruct.ExitCode = 1 365 | $WriteQueueWarnThreshReached = $true 366 | $OutputWriteQueue = "WARNING: Write Queue Threshold ($($DiskStruct.AvgDiskWriteQueueWarn)) Passed!" 367 | } 368 | 369 | if ($ReadQueueCritThreshReached -eq $false -and $ReadQueueWarnThreshReached -eq $false -and $WriteQueueCritThreshReached -eq $false -and $WriteQueueWarnThreshReached -eq $false) { 370 | $DiskStruct.ReturnString = "OK: Drive $($DiskStruct.DiskLetter): Avg of $($DiskStruct.MaxSamples) samples: {Rate (Read: $($DiskStruct.DiskReadBytesSecValue)MB/s)(Write: $($DiskStruct.DiskWriteBytesSecValue)MB/s)} {Avg Nr of (Reads: $($DiskStruct.DiskReadsSecValue)r/s)(Writes: $($DiskStruct.DiskWritesSecValue)w/s)} {Latency (Read: $($DiskStruct.AvgDiskSecReadValue)ms)(Write: $($DiskStruct.AvgDiskSecWriteValue)ms)} {Queue Length (Read: $($DiskStruct.AvgDiskReadQueueValue)ql)(Write: $($DiskStruct.AvgDiskWriteQueueValue)ql)} | " 371 | $DiskStruct.ExitCode = 0 372 | } 373 | else { 374 | $DiskStruct.ReturnString = "$OutputReadQueue $OutputWriteQueue : Drive $($DiskStruct.DiskLetter): Avg of $($DiskStruct.MaxSamples) samples: {Rate (Read: $($DiskStruct.AvgDiskReadBytesSecValue)MB/s)(Write: $($DiskStruct.AvgDiskWriteBytesSecValue)MB/s)} {Avg Nr of (Reads: $($DiskStruct.DiskReadsSecValue)r/s)(Writes: $($DiskStruct.DiskWritesSecValue)w/s)} {Latency (Read: $($DiskStruct.AvgDiskSecReadValue)ms)(Write: $($DiskStruct.AvgDiskSecWriteValue)ms)} {Queue Length (Read: $($DiskStruct.AvgDiskReadQueueValue)ql)(Write: $($DiskStruct.AvgDiskWriteQueueValue)ql)} | " 375 | } 376 | 377 | $DiskStruct.ReturnString += "'Read_Latency'=$($DiskStruct.AvgDiskSecReadValue)ms " 378 | $DiskStruct.ReturnString += "'Write_Latency'=$($DiskStruct.AvgDiskSecWriteValue)ms " 379 | $DiskStruct.ReturnString += "'Read_Queue'=$($DiskStruct.AvgDiskReadQueueValue)ql " 380 | $DiskStruct.ReturnString += "'Write_Queue'=$($DiskStruct.AvgDiskWriteQueueValue)ql " 381 | $DiskStruct.ReturnString += "'Number_of_Reads'=$($DiskStruct.DiskReadsSecValue)r/s " 382 | $DiskStruct.ReturnString += "'Number_of_Writes'=$($DiskStruct.DiskWritesSecValue)w/s " 383 | $DiskStruct.ReturnString += "'Read_Rate'=$($DiskStruct.DiskReadBytesSecValue)MB/s " 384 | $DiskStruct.ReturnString += "'Write_Rate'=$($DiskStruct.DiskWriteBytesSecValue)MB/s " 385 | } 386 | 387 | #endregion Functions 388 | 389 | # Main function 390 | 391 | if ($Args) { 392 | if($Args[0].ToString() -ne "$ARG1$"){ 393 | if($Args.count -ge 1){Initialize-Args $Args} 394 | } 395 | } 396 | Get-DiskLoadCounters 397 | Write-Host $DiskStruct.ReturnString 398 | Exit $DiskStruct.ExitCode 399 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Nagios plugin to check disk load on a Microsoft Windows host. 2 | 3 | ### Idea 4 | 5 | Check MS Windows disk load by using this Powershell script to get all disk load related counters from Windows Performance 6 | Manager, computing averages for all gathered samples and calculating read / write rate, number of reads / writes, read / 7 | write latency and read / write queue length. 8 | 9 | ### Screenshot 10 | 11 | ![Disk Load Highcharts Graph 01](/../screenshots/check-ms-win-disk-load-graph-01.png?raw=true "Disk Load Highcharts Graph 01") 12 | 13 | ### Status 14 | 15 | Production ready. 16 | 17 | ### How To 18 | 19 | Please visit https://outsideit.net/check-ms-win-disk-load for more information on how to use this plugin. 20 | 21 | ### Help 22 | 23 | In case you find a bug or have a feature request, please make an issue on GitHub. 24 | 25 | ### On Nagios Exchange 26 | 27 | https://exchange.nagios.org/directory/Plugins/Operating-Systems/Windows-NRPE/Check-Microsoft-Windows-Disk-Load/details 28 | 29 | ### Copyright 30 | 31 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public 32 | License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later 33 | version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 34 | implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 35 | details at . --------------------------------------------------------------------------------