-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCheck-Performance-Counters.ps1
434 lines (355 loc) · 18.8 KB
/
Check-Performance-Counters.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# Get-Performance-Counters.ps1
#
# A script to collect performance counters and return nagios based statuses for them. This is written with NCPA in mind, however could be used for any agent or without one.
# Spenser Reinhardt
# Nagios Enterprises LLC
# Copyright 2014
# License GPLv2 - I reserve the right to change the licensing at any time without prior notification, past versions maintain the licensing at that point in time.
# I also extend all usage, duplication, and modification rights to Nagios Enterprises LLC, without the forceful inclusion of GPL licensing.
# Default settings for warning\crit strings. Need to see if I can leave them blank unless set and verify if they exist instead of default valules
[String]$DefaultString = "ABCD123"
[Int64]$DefaultInt = -99
[Int64]$State_OK = 0
[Int64]$State_Warn = 1
[Int64]$State_Crit = 2
[Int64]$State_Unknown = 3
# Counter "struct" to store values in a single object as we progress through the script and functions. (far easier to
# pass function to function, and it's not c so no pointers afaik)
$CounterStruct = @{}
[String]$CounterStruct.Hostname = ""
[String]$CounterStruct.Counter = ""
[String]$CounterStruct.Label = ""
[Int]$CounterStruct.Time = 1
[Int64]$CounterStruct.ExitCode = $State_Unknown
[String]$CounterStruct.OutputString = "Unknown: There was an error processing performance counter data"
[String]$CounterStruct.OkString = $DefaultString
[String]$CounterStruct.WarnString = $DefaultString
[String]$CounterStruct.CritString = $DefaultString
[Int64]$CounterStruct.WarnHigh = $DefaultInt.ToInt64($null)
[Int64]$CounterStruct.CritHigh = $DefaultInt.ToInt64($null)
[Int64]$CounterStruct.WarnLow = $DefaultInt.ToInt64($null)
[Int64]$CounterStruct.CritLow = $DefaultInt.ToInt64($null)
$CounterStruct.Result
# Function to write output and exit properly per nagios guidelines.
Function Write-Output-Message {
Param ( [Parameter(Mandatory=$true)]$Return )
# Begin output message
Switch ( $Return.ExitCode ) {
0 { $Return.OutputString = "OK: " }
1 { $Return.OutputString = "Warning: " }
2 { $Return.OutputString = "Critical: " }
3 { $Return.OutputString = "Unknown: " }
default { $Return.OutputString = "Unknown: Failed to process exit code"; Exit $State_Unknown }
}
If ( $Return.Label -eq "" ) {
$Return.OutputString += "Counter $($Return.Counter.ToString()) returned results"
} Else {
$Return.OutputString += "$($Return.Label.ToString()) returned results"
}
# Process result
$Return.Result | ForEach-Object {
$Return.OutputString += " $($_.CookedValue.ToString())"
}
$Return.OutputString += " | "
[int]$c = 0
$Return.Result | ForEach-Object {
# Handle counters or labels
If ( $Return.Label -eq "" ) {
$Return.OutputString += "`'Counter $c`'="
} Else {
$Return.OutputString += "`'$($Return.Label.ToString()) $c`'="
}
# Handle adding counter values and warn\crit values for perfdata
If ( ($_.CookedValue.GetType().Name -eq "Int") -or ($_.CookedValue.GetType().Name -eq "Double") ) {
$Return.OutputString += "$($_.CookedValue.ToInt64($null));"
If ($Return.WarnHigh.ToInt64($null) -ne $DefaultInt) { $Return.OutputString += "$($Return.WarnHigh.ToInt64($null));" }
ElseIf ($Return.WarnLow.ToInt64($null) -ne $DefaultInt) { $Return.OutputString += "$($Return.WarnLow.ToInt64($null));" }
Else { $Return.OutputString += ";" }
If ($Return.CritHigh -ne $DefaultInt) { $Return.OutputString += "$($Return.CritHigh.ToInt64($null));" }
Else { $Return.OutputString += ";" }
$Return.OutputString += "; "
}
$c++;
}
Write-Output $Return.OutputString
Exit $Return.ExitCode
} # End Write-Output-Message
# Function Get-Counter-List - For getting a listing of performance counters on this or a remote system, also possible to filter based on fuzzy matching.
# Mostly for future use cases. Not in use presently.
Function Get-Counter-List {
Param (
[Parameter(Mandatory=$false)][String]$ComputerName,
[Parameter(Mandatory=$false)][String]$CounterType
)
# Create initial command
$Command = "Get-Counter -ListSet *"
# If a computer name was provided
If ( $CounterType ) { $Command += "$CounterType*" }
ElseIf ( $ComputerName ) { $Command += " -ComputerName $ComputerName" }
# Append select to command before execution
$Command += " | Select-Object -ExpandProperty Counter"
# Invoke command and store result
$Return = Invoke-Expression $Command
# Validate $Return was set and return with it if it was.
If ( $Return ) { Return $Return }
# Fallthrough
Return "Failed"
} # End Get-Counter-List
# Function to get performance counters from the system and clobber them into our structure and use case, $Return should be $CounterStruct
Function Get-PerfCounter {
Param (
[Parameter(Mandatory=$true)]$Return
)
$Command = "Get-Counter -Counter `'$($Return.Counter.tostring())`'"
# If any additional params were provided add to command string with flag.
If ( $Return.HostName ) { $Command += " -ComputerName `'$([string]$Return.HostName)`'" }
If ( $Return.Time -ne 1 ) { $Command += " -SampleInterval $([int]$Return.Time)" }
# Push counter samples into return.result, as they contain all(?) relevant data opposed to the whole counter
$Return.Result = $(Invoke-Expression $Command).CounterSamples
Return $Return
} # End Get-PerfCounter
# Function to check results against provided warning and critical values and determine exit code\output message
# Note: By absolutely no means, is this a completed fully fledged thresholds compliant function.
# It will be replaced with a future include that will be much more comprehensive.
Function Get-ExitCode {
Param ( [Parameter(Mandatory=$True)]$Return )
[Boolean]$ExitSet = $false
# Determine exit code by checking cooked values from counter
$Return.Result | ForEach-Object {
$Type = $_.CookedValue.GetType().name
# Start with type of cooked value
# TODO - Look into doing a for\foreach-object loop for this, but it gets tricky when we may not know the object name of what we are checking against
If ( $Type -eq "String" ) {
# Check OK string
If ( ($Return.OkString -ne $DefaultString) -and ($_ -eq $Return.OkString) ) {
# Only need to check if exitset is not true, otherwise we may have warning or critical already set
If ( $ExitSet -eq $false ) {
$Return.ExitCode = $State_OK
$ExitSet = $true
}
}
# Check warning string
ElseIf ( ($Return.WarnString -ne $DefaultString) -and ($_ -eq $Return.WarnString) ) {
# Check exitset and if so, check if greater than previously set code
If ($Return.ExitCode -lt $State_Warn) {
$Return.ExitCode = $State_Warn
$ExitSet = $true
}
}
# Check critical string
ElseIf ( ($Return.CritString -ne $DefaultString) -and ($_ -eq $Return.CritString) ) {
#Check exitset and if so, check if greater than previously set code
If ($Return.ExitCode -lt $State_Crit) {
$Return.ExitCode = $State_Crit
$ExitSet = $true
}
}
#Else if no string is set for checking, and exitset is false
ElseIf ( ($Return.OkString -eq $DefaultString) -and ($Return.WarnString -eq $DefaultString) -and ($Return.CritString -eq $DefaultString) -and ($ExitSet -eq $false) ) {
$Return.ExitCode = $State_OK
$ExitSet = $true
}
} # end string statements and begin int\double statements
ElseIf ( ($Type -like "Double") -or ($Type -like "Int") ) {
# Check OK - These are GROSS... but without the include there isn't a better way of handling it.
# Checking if low and high values are default still and if not then should fit result < low || high < result, then we can use this as status as is outside
# the give range. We should expect that a single value for thresholds will currently be set as a high counter not low.
#value.compareto($lesserVal) = 1 (value > lesserval)
#value.compareto($equalVal) = 0 (value == equalval)
#value.compareto($greaterVal) = -1 (value < greaterval)
# if we are lower than critlow or higher than crit high, crit
If ( (($Return.CritLow.CompareTo($DefaultInt) -ne 0) -and ($_.CookedValue.ToInt64($null).CompareTo($Return.CritLow) -le 0)) -or (($Return.CritHigh.CompareTo($DefaultInt) -ne 0) -and ($_.CookedValue.ToInt64($null).CompareTo($Return.CritHigh) -ge 0)) ) {
If ( (($Return.ExitCode.ToInt64($null).CompareTo($State_Crit) -gt 0) -and ($ExitSet -eq $true)) -or ($ExitSet -eq $false) ) {
$Return.ExitCode = $State_Crit
$ExitSet = $true
}
}
# if we are lower than warnlow or higher than warn high, warn
ElseIf ( (($Return.WarnLow.CompareTo($DefaultInt) -ne 0) -and ($_.CookedValue.ToInt64($null).CompareTo($Return.WarnLow) -le 0)) -or (($Return.WarnHigh.CompareTo($DefaultInt) -ne 0) -and ($_.CookedValue.ToInt64($null).CompareTo($return.WarnHigh) -ge 0)) ) {
If ( (($Return.ExitCode.ToInt64($null).CompareTo($State_Warn) -gt 0) -and ($ExitSet -eq $true)) -or ($ExitSet -eq $false) ) {
$Return.ExitCode = $State_Warn
$ExitSet = $true
}
}
# if all thresholds are still default, OK
ElseIf ( ($Return.WarnLow.CompareTo($DefaultInt) -eq 0) -and ($Return.WarnHigh.CompareTo($DefaultInt) -eq 0) -and ($Return.CritLow.CompareTo($DefaultInt) -eq 0) -and ($Return.CritHigh.CompareTo($DefaultInt) -eq 0) ) {
If ( $ExitSet -eq $false ) {
$Return.ExitCode = $State_OK
$ExitSet = $true
}
}
# If none of these were caught, we must be within OK range, and not using default thresholds
ElseIf ( $ExitSet -eq $false ) {
$Return.ExitCode = $State_OK
$ExitSet = $true
}
} # End ifelse for double\int
} # End for loop on cooked counters
#Return $return as we are done
Return $Return
} # End Function Get-ExitCode
# Function to check strings for invalid and potentially malicious chars. Since we are creating and executing commands dynamically with "eval", we need to be sure
# nothing funky can happen.... ie executing unintentional\multiple commands from a single string. Sorry users, I don't trust you. :)
Function Check-Strings {
Param ( [Parameter(Mandatory=$True)][string]$String )
# `, `n, |, ; are bad, I think we can leave {}, @, and $ at this point.
$BadChars=@("``", "|", ";", "`n")
$BadChars | ForEach-Object {
If ( $String.Contains("$_") ) {
Write-Host "Unknown: String contains illegal characters."
Exit $State_Unknown
}
} # end for
Return $true
} # end check-strings
# Function to handle args in a nagios style fasion.
Function Process-Args {
Param (
[Parameter(Mandatory=$True)]$Args,
[Parameter(Mandatory=$True)]$Return
)
If ( $Args.Count -lt 2 ) {
Write-Help
}
For ( $i = 0; $i -lt $Args.count-1; $i++ ) {
$CurrentArg = $Args[$i].ToString()
$Value = $Args[$i+1]
If ($CurrentArg -cmatch "-H") {
If (Check-Strings $Value) {
$Return.Hostname = $Value
}
}
ElseIf ($CurrentArg -match "--Hostname") {
If (Check-Strings $Value) {
$Return.Hostname = $Value
}
}
ElseIf ($CurrentArg -cmatch "-n") {
If (Check-Strings $Value) {
$Return.Counter = $Value
}
}
ElseIf ($CurrentArg -match "--Counter-Name") {
ElseIf (Check-Strings $Value) {
$Return.Counter = $Value
}
}
ElseIf ($CurrentArg -cmatch "-l") {
If (Check-Strings $Value) {
$Return.Label = $Value
}
}
ElseIf ($CurrentArg -match "--Label") {
If (Check-Strings $Value) {
$Return.Label = $Value
}
}
ElseIf ($CurrentArg -cmatch "-t") {
If (Check-Strings $Value) {
$Return.Time = $Value
}
}
ElseIf ($CurrentArg -match "--Time") {
If (Check-Strings $Value ){
$Return.Time = $Value
}
}
ElseIf ($CurrentArg -cmatch "-w") {
If (Check-Strings $Value) {
If ( $Value.GetType().Name -like "String" ) {
If ( $Value.Contains(":") ) {
$Value = $Value.Split(":")
If (!$Value[0].Equals("")) { $Return.WarnLow = $Value[0].ToInt64($null) }
If (!$Value[1].Equals("")) { $Return.WarnHigh = $Value[1].ToInt64($null) }
}
Else { $Return.WarnString = $Value }
}
ElseIf ( ($Value.GetType().Name -like "Int32" ) -or ($Value.GetType().Name -like "Int64" ) -or ($Value.GetType().Name -eq "Double") ) {
$Return.WarnHigh = $Value.ToInt64($null)
}
}
}
ElseIf ($CurrentArg -match "--Warning") {
If (Check-Strings $Value) {
If ( $Value.GetType().Name -like "String" ) {
If ( $Value.Contains(":") ) {
$Value = $Value.Split(":")
If (!$Value[0].Equals("")) { $Return.WarnLow = $Value[0].ToInt64($null) }
If (!$Value[1].Equals("")) { $Return.WarnHigh = $Value[1].ToInt64($null) }
}
Else { $Return.WarnString = $Value }
}
ElseIf ( ($Value.GetType().Name -like "Int32" ) -or ($Value.GetType().Name -like "Int64" ) -or ($Value.GetType().Name -eq "Double") ) {
$Return.WarnHigh = $Value.ToInt64($null)
}
}
}
ElseIf ($CurrentArg -cmatch "-c") {
If (Check-Strings $Value) {
If ( $Value.GetType().Name -eq "String" ) {
If ( $Value.Contains(":") ) {
$Value = $Value.Split(":")
If (!$Value[0].Equals("")) { $Return.CritLow = $Value[0] }
If (!$Value[1].Equals("")) { $Return.CritHigh = $Value[1] }
}
Else { $Return.CritString = $Value }
}
ElseIf ( ($Value.GetType().Name -like "Int32" ) -or ($Value.GetType().Name -like "Int64" ) -or ($Value.GetType().Name -like "Double") ) {
$Return.CritHigh = $Value.ToInt64($null)
}
}
}
ElseIf ($CurrentArg -match "--Critical") {
If (Check-Strings $Value) {
If ( $Value.GetType().Name -eq "String" ) {
If ( $Value.Contains(":") ) {
$Value = $Value.Split(":")
If (!$Value[0].Equals("")) { $Return.CritLow = $Value[0] }
If (!$Value[1].Equals("")) { $Return.CritHigh = $Value[1] }
}
Else { $Return.CritString = $Value }
}
ElseIf ( ($Value.GetType().Name -like "Int32" ) -or ($Value.GetType().Name -like "Int64" ) -or ($Value.GetType().Name -like "Double") ) {
$Return.CritHigh = $Value.ToInt64($null)
}
}
}
ElseIf ($CurrentArg -cmatch "-h") { Write-Help }
ElseIf ($CurrentArg -match "--help") { Write-Help }
} # End for loop
Return $Return
} # End Process Args
# Function to write help output
Function Write-Help {
Write-Output "Check-Performance-Counters.ps1:`n`tThis script is designed to check performance counters and return them in a nagios style output."
Write-Output "`tPresently this script only supports Powershell v3 and newer. Additions for older variants may be included in the future.`n"
Write-Output "Arguments:"
write-output "`t-H | --Hostname ) Optional hostname of remote system."
Write-Output "`t-n | --Counter-Name) Name of performance counter to collect."
Write-Output "`t-l | --Label) Name of label for counters, opposed to Counter[n], in output message"
Write-Output "`t-t | --Time ) Time in seconds for sample interval."
Write-Output "`t-w | --Warning ) Warning string or number to check against. Somewhat matches plugins threshold guidelines"
Write-Output "`t-c | --Critial ) Critical string or number to check against. Somewhat matches plugins threshold guidelines"
Write-Output "`t-h | --Help ) Print this help output."
Exit 3
} # end Write-Help
# Main function to kick off functionality
Function Check-Performance-Counters {
Param (
[Parameter(Mandatory=$True)]$Args,
[Parameter(Mandatory=$True)]$CounterStruct
)
# If older than PS v3 write help and exit.
If ( $PSVersionTable.PSVersion.Major -lt 3 ) { Write-Help }
# Process arguments and insert into counter struct
$CounterStruct = Process-Args $Args $CounterStruct
# Attempt to get performance counter information
$CounterStruct = Get-PerfCounter $CounterStruct
$CounterStruct = Get-ExitCode $CounterStruct
Write-Output-Message $CounterStruct
# If we somehow get here, something is wrong
Write-Output "Unknown: Something happened with the script."
Exit $State_Unknown
}
# Execute main block
Check-Performance-Counters $args $CounterStruct