@@ -2,7 +2,9 @@ package main
2
2
3
3
import (
4
4
"fmt"
5
+ "strconv"
5
6
"strings"
7
+ "time"
6
8
7
9
human "github.com/dustin/go-humanize"
8
10
"github.com/sensu-community/sensu-plugin-sdk/sensu"
@@ -23,10 +25,57 @@ type Config struct {
23
25
IncludeReadOnly bool
24
26
FailOnError bool
25
27
HumanReadable bool
28
+ MetricsMode bool
29
+ ExtraTags []string
30
+ }
31
+
32
+ type MetricGroup struct {
33
+ Comment string
34
+ Type string
35
+ Name string
36
+ Metrics []Metric
37
+ }
38
+
39
+ func (g * MetricGroup ) AddMetric (tags map [string ]string , value float64 , timeNow int64 ) {
40
+ g .Metrics = append (g .Metrics , Metric {
41
+ Tags : tags ,
42
+ Timestamp : timeNow ,
43
+ Value : value ,
44
+ })
45
+ }
46
+ func (g * MetricGroup ) Output () {
47
+ var output string
48
+ metricName := strings .Replace (g .Name , "." , "_" , - 1 )
49
+ fmt .Printf ("# HELP %s [%s] %s\n " , metricName , g .Type , g .Comment )
50
+ fmt .Printf ("# TYPE %s %s\n " , metricName , g .Type )
51
+ for _ , m := range g .Metrics {
52
+ tagStr := ""
53
+ for tag , tvalue := range m .Tags {
54
+ if len (tagStr ) > 0 {
55
+ tagStr = tagStr + ","
56
+ }
57
+ tagStr = tagStr + tag + "=\" " + tvalue + "\" "
58
+ }
59
+ if len (tagStr ) > 0 {
60
+ tagStr = "{" + tagStr + "}"
61
+ }
62
+ output = strings .Join (
63
+ []string {metricName + tagStr , fmt .Sprintf ("%v" , m .Value ), strconv .FormatInt (m .Timestamp , 10 )}, " " )
64
+ fmt .Println (output )
65
+ }
66
+ fmt .Println ("" )
67
+ }
68
+
69
+ type Metric struct {
70
+ Tags map [string ]string
71
+ Timestamp int64
72
+ Value float64
26
73
}
27
74
28
75
var (
29
- plugin = Config {
76
+ tags = map [string ]string {}
77
+ extraTags = map [string ]string {}
78
+ plugin = Config {
30
79
PluginConfig : sensu.PluginConfig {
31
80
Name : "check-disk-usage" ,
32
81
Short : "Cross platform disk usage check for Sensu" ,
@@ -125,6 +174,22 @@ var (
125
174
Usage : "print sizes in powers of 1024 (default false)" ,
126
175
Value : & plugin .HumanReadable ,
127
176
},
177
+ {
178
+ Path : "metrics" ,
179
+ Env : "" ,
180
+ Argument : "metrics" ,
181
+ Default : false ,
182
+ Usage : "Output metrics instead of human readable output" ,
183
+ Value : & plugin .MetricsMode ,
184
+ },
185
+ {
186
+ Path : "tags" ,
187
+ Env : "" ,
188
+ Argument : "tags" ,
189
+ Default : []string {},
190
+ Usage : "Comma separated list of additional metrics tags using key=value format." ,
191
+ Value : & plugin .ExtraTags ,
192
+ },
128
193
}
129
194
)
130
195
@@ -143,6 +208,15 @@ func checkArgs(event *types.Event) (int, error) {
143
208
if plugin .Warning >= plugin .Critical {
144
209
return sensu .CheckStateCritical , fmt .Errorf ("--warning value can not be greater than or equal to --critical value" )
145
210
}
211
+ for _ , tagString := range plugin .ExtraTags {
212
+ fmt .Println (tagString )
213
+ parts := strings .Split (tagString , `=` )
214
+ if len (parts ) == 2 {
215
+ extraTags [parts [0 ]] = parts [1 ]
216
+ } else {
217
+ return sensu .CheckStateCritical , fmt .Errorf ("Failed to parse input tag: %s" , tagString )
218
+ }
219
+ }
146
220
return sensu .CheckStateOK , nil
147
221
}
148
222
@@ -152,12 +226,56 @@ func executeCheck(event *types.Event) (int, error) {
152
226
warnings int
153
227
)
154
228
229
+ timeNow := time .Now ().Unix ()
155
230
parts , err := disk .Partitions (plugin .IncludePseudo )
156
231
if err != nil {
157
232
return sensu .CheckStateCritical , fmt .Errorf ("Failed to get partitions, error: %v" , err )
158
233
}
159
234
235
+ metricGroups := map [string ]* MetricGroup {
236
+ "disk.critical" : & MetricGroup {
237
+ Name : "disk.critical" ,
238
+ Type : "GAUGE" ,
239
+ Comment : "non-zero value indicates mountpoint usage is above critical threshold" ,
240
+ Metrics : []Metric {},
241
+ },
242
+ "disk.warning" : & MetricGroup {
243
+ Name : "disk.warning" ,
244
+ Type : "GAUGE" ,
245
+ Comment : "non-zero value indicates mountpoint usage is above warning threshold" ,
246
+ Metrics : []Metric {},
247
+ },
248
+ "disk.percent_used" : & MetricGroup {
249
+ Name : "disk.percent_usage" ,
250
+ Type : "GAUGE" ,
251
+ Comment : "Percentage of mounted volume used" ,
252
+ Metrics : []Metric {},
253
+ },
254
+ "disk.total_bytes" : & MetricGroup {
255
+ Name : "disk.total_bytes" ,
256
+ Type : "GAUGE" ,
257
+ Comment : "Total size in bytes of mounted volumed" ,
258
+ Metrics : []Metric {},
259
+ },
260
+ "disk.used_bytes" : & MetricGroup {
261
+ Name : "disk.used_bytes" ,
262
+ Type : "GAUGE" ,
263
+ Comment : "Used size in bytes of mounted volumed" ,
264
+ Metrics : []Metric {},
265
+ },
266
+ "disk.free_bytes" : & MetricGroup {
267
+ Name : "disk.free_bytes" ,
268
+ Type : "GAUGE" ,
269
+ Comment : "Free size in bytes of mounted volumed" ,
270
+ Metrics : []Metric {},
271
+ },
272
+ }
273
+
160
274
for _ , p := range parts {
275
+ tags = map [string ]string {}
276
+ for key , value := range extraTags {
277
+ tags [key ] = value
278
+ }
161
279
// Ignore excluded (or non-included) file system types
162
280
if ! isValidFSType (p .Fstype ) {
163
281
continue
@@ -173,13 +291,16 @@ func executeCheck(event *types.Event) (int, error) {
173
291
continue
174
292
}
175
293
294
+ tags ["mountpoint" ] = p .Mountpoint
176
295
device := p .Mountpoint
177
296
s , err := disk .Usage (device )
178
297
if err != nil {
179
298
if plugin .FailOnError {
180
299
return sensu .CheckStateCritical , fmt .Errorf ("Failed to get disk usage for %s, error: %v" , device , err )
181
300
}
182
- fmt .Printf ("%s UNKNOWN: %s - error: %v\n " , plugin .PluginConfig .Name , device , err )
301
+ if ! plugin .MetricsMode {
302
+ fmt .Printf ("%s UNKNOWN: %s - error: %v\n " , plugin .PluginConfig .Name , device , err )
303
+ }
183
304
continue
184
305
}
185
306
@@ -189,23 +310,67 @@ func executeCheck(event *types.Event) (int, error) {
189
310
}
190
311
191
312
// implement magic factor for larger file systems?
192
- fmt .Printf ("%s " , plugin .PluginConfig .Name )
313
+ crit := 0
314
+ warn := 0
193
315
if s .UsedPercent >= plugin .Critical {
194
316
criticals ++
195
- fmt .Printf ("CRITICAL: " )
196
- } else if s .UsedPercent >= plugin .Warning {
317
+ crit = 1
318
+ }
319
+ if s .UsedPercent >= plugin .Warning {
197
320
warnings ++
198
- fmt .Printf (" WARNING: " )
321
+ warn = 1
322
+ }
323
+ metricGroups ["disk.critical" ].AddMetric (tags , float64 (crit ), timeNow )
324
+ metricGroups ["disk.warning" ].AddMetric (tags , float64 (warn ), timeNow )
325
+ if ! plugin .MetricsMode {
326
+ fmt .Printf ("%s " , plugin .PluginConfig .Name )
327
+ if crit > 0 {
328
+ fmt .Printf ("CRITICAL: " )
329
+ } else if warn > 0 {
330
+ fmt .Printf (" WARNING: " )
331
+ } else {
332
+ fmt .Printf (" OK: " )
333
+ }
334
+ if plugin .HumanReadable {
335
+ fmt .Printf ("%s %.2f%% - Total: %s, Used: %s, Free: %s\n " ,
336
+ p .Mountpoint , s .UsedPercent , human .IBytes (s .Total ), human .IBytes (s .Used ), human .IBytes (s .Free ))
337
+ } else {
338
+ fmt .Printf ("%s %.2f%% - Total: %s, Used: %s, Free: %s\n " ,
339
+ p .Mountpoint , s .UsedPercent , human .Bytes (s .Total ), human .Bytes (s .Used ), human .Bytes (s .Free ))
340
+ }
341
+ }
342
+ metricGroups ["disk.percent_used" ].AddMetric (tags , float64 (s .UsedPercent ), timeNow )
343
+ metricGroups ["disk.total_bytes" ].AddMetric (tags , float64 (s .Total ), timeNow )
344
+ metricGroups ["disk.used_bytes" ].AddMetric (tags , float64 (s .Used ), timeNow )
345
+ metricGroups ["disk.free_bytes" ].AddMetric (tags , float64 (s .Free ), timeNow )
346
+ }
347
+ tags = map [string ]string {}
348
+ for key , value := range extraTags {
349
+ tags [key ] = value
350
+ }
351
+ tags ["mountpoint" ] = "any"
352
+ anyCritical := func () float64 {
353
+ if criticals > 0 {
354
+ return 1
199
355
} else {
200
- fmt . Printf ( " OK: " )
356
+ return 0
201
357
}
202
- if plugin .HumanReadable {
203
- fmt .Printf ("%s %.2f%% - Total: %s, Used: %s, Free: %s\n " , p .Mountpoint , s .UsedPercent , human .IBytes (s .Total ), human .IBytes (s .Used ), human .IBytes (s .Free ))
358
+ }()
359
+ metricGroups ["disk.critical" ].AddMetric (tags , anyCritical , timeNow )
360
+ anyWarning := func () float64 {
361
+ if warnings > 0 {
362
+ return 1
204
363
} else {
205
- fmt .Printf ("%s %.2f%% - Total: %s, Used: %s, Free: %s\n " , p .Mountpoint , s .UsedPercent , human .Bytes (s .Total ), human .Bytes (s .Used ), human .Bytes (s .Free ))
364
+ return 0
365
+ }
366
+ }()
367
+ metricGroups ["disk.warning" ].AddMetric (tags , anyWarning , timeNow )
368
+ if plugin .MetricsMode {
369
+ for _ , g := range metricGroups {
370
+ g .Output ()
206
371
}
207
- }
208
372
373
+ }
209
374
if criticals > 0 {
210
375
return sensu .CheckStateCritical , nil
211
376
} else if warnings > 0 {
0 commit comments