nagios-herald 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG.md +11 -0
  5. data/CONTRIBUTING.md +28 -0
  6. data/Gemfile +5 -0
  7. data/LICENSE +21 -0
  8. data/README.md +94 -0
  9. data/Rakefile +9 -0
  10. data/bin/draw_stack_bars +76 -0
  11. data/bin/dump_nagios_env.sh +25 -0
  12. data/bin/get_ganglia_graph +82 -0
  13. data/bin/get_graph +50 -0
  14. data/bin/get_graphite_graph +58 -0
  15. data/bin/nagios-herald +6 -0
  16. data/bin/splunk_alert_frequency +54 -0
  17. data/contrib/nrpe-plugins/check_cpu_stats.sh +186 -0
  18. data/contrib/nrpe-plugins/check_disk.sh +34 -0
  19. data/contrib/nrpe-plugins/check_mem.pl +181 -0
  20. data/contrib/nrpe-plugins/nrpe-plugin-examples.md +11 -0
  21. data/docs/config.md +62 -0
  22. data/docs/example_alerts.md +48 -0
  23. data/docs/formatters.md +180 -0
  24. data/docs/helpers.md +12 -0
  25. data/docs/images/cpu_no_context.png +0 -0
  26. data/docs/images/cpu_with_context.png +0 -0
  27. data/docs/images/disk_space_no_context.png +0 -0
  28. data/docs/images/disk_space_with_context.png +0 -0
  29. data/docs/images/memory_high_no_context.png +0 -0
  30. data/docs/images/memory_high_with_context.png +0 -0
  31. data/docs/images/nagios-herald-formatter-content-example.png +0 -0
  32. data/docs/images/nagios-herald.png +0 -0
  33. data/docs/images/stack-bars.png +0 -0
  34. data/docs/images/vanilla-nagios.png +0 -0
  35. data/docs/messages.md +16 -0
  36. data/docs/nagios-config.md +74 -0
  37. data/docs/tools.md +79 -0
  38. data/etc/config.yml.example +14 -0
  39. data/etc/readme.md +2 -0
  40. data/lib/nagios-herald/config.rb +25 -0
  41. data/lib/nagios-herald/executor.rb +265 -0
  42. data/lib/nagios-herald/formatter_loader.rb +82 -0
  43. data/lib/nagios-herald/formatters/base.rb +524 -0
  44. data/lib/nagios-herald/formatters/check_cpu.rb +71 -0
  45. data/lib/nagios-herald/formatters/check_disk.rb +143 -0
  46. data/lib/nagios-herald/formatters/check_logstash.rb +155 -0
  47. data/lib/nagios-herald/formatters/check_memory.rb +42 -0
  48. data/lib/nagios-herald/formatters/example.rb +19 -0
  49. data/lib/nagios-herald/formatters.rb +1 -0
  50. data/lib/nagios-herald/helpers/ganglia_graph.rb +99 -0
  51. data/lib/nagios-herald/helpers/graphite_graph.rb +85 -0
  52. data/lib/nagios-herald/helpers/logstash_query.rb +125 -0
  53. data/lib/nagios-herald/helpers/splunk_alert_frequency.rb +170 -0
  54. data/lib/nagios-herald/helpers/splunk_query.rb +119 -0
  55. data/lib/nagios-herald/helpers/url_image.rb +76 -0
  56. data/lib/nagios-herald/helpers.rb +5 -0
  57. data/lib/nagios-herald/logging.rb +48 -0
  58. data/lib/nagios-herald/message_loader.rb +40 -0
  59. data/lib/nagios-herald/messages/base.rb +56 -0
  60. data/lib/nagios-herald/messages/email.rb +150 -0
  61. data/lib/nagios-herald/messages/irc.rb +58 -0
  62. data/lib/nagios-herald/messages/pager.rb +75 -0
  63. data/lib/nagios-herald/messages.rb +3 -0
  64. data/lib/nagios-herald/test_helpers/base_test_case.rb +82 -0
  65. data/lib/nagios-herald/util.rb +45 -0
  66. data/lib/nagios-herald/version.rb +3 -0
  67. data/lib/nagios-herald.rb +7 -0
  68. data/lib/stackbars/__init__.py +0 -0
  69. data/lib/stackbars/chart_utils.py +25 -0
  70. data/lib/stackbars/grouped_stackbars.py +97 -0
  71. data/lib/stackbars/pilfonts/Tahoma.ttf +0 -0
  72. data/lib/stackbars/pilfonts/aerial.ttf +0 -0
  73. data/lib/stackbars/pilfonts/arial_black.ttf +0 -0
  74. data/lib/stackbars/stackbar.py +100 -0
  75. data/nagios-herald.gemspec +33 -0
  76. data/test/env_files/check_cpu_idle.CRITICAL +199 -0
  77. data/test/env_files/check_cpu_iowait.WARNING +199 -0
  78. data/test/env_files/check_disk.CRITICAL +197 -0
  79. data/test/env_files/check_disk.CRITICAL_ICINGA +197 -0
  80. data/test/env_files/check_disk.RECOVERY +197 -0
  81. data/test/env_files/check_memory.CRITICAL +197 -0
  82. data/test/env_files/nagios_vars.EXAMPLE +197 -0
  83. data/test/unit/test_config.rb +31 -0
  84. data/test/unit/test_executor.rb +65 -0
  85. data/test/unit/test_formatter_base.rb +131 -0
  86. data/test/unit/test_formatter_check_cpu_idle_critical.rb +135 -0
  87. data/test/unit/test_formatter_check_memory.rb +135 -0
  88. data/test/unit/test_icinga_variables.rb +31 -0
  89. data/test/unit/test_logging.rb +35 -0
  90. data/test/unit/test_message_email.rb +69 -0
  91. data/test/unit/test_message_pager.rb +69 -0
  92. metadata +204 -0
@@ -0,0 +1,524 @@
1
+ # Formatter objects know best about how to create and format content.
2
+ # The Base class defines several variables and methods that can be used in subclasses.
3
+ # Nearly all of them can be overridden. Subclasses can also extend functionality and
4
+ # call on helpers.
5
+
6
+ # Dear Reader,
7
+ # There is a brittle, un-OOP pattern in this class, but it gets the job done
8
+ # because sometimes "working" is better than "elegant" or "correct".
9
+ # Your exercise, should you choose to take it on, is to devise a better way
10
+ # for a formatter to know which content to generate based on the message type.
11
+ # THIS IS ESPECIALLY IMPORTANT FOR SUBCLASSES THAT OVERRIDE METHODS!
12
+ # Hit me with your best shot.
13
+
14
+ require 'tmpdir'
15
+ require 'nagios-herald/logging'
16
+ require 'nagios-herald/util'
17
+ require 'nagios-herald/formatter_loader'
18
+
19
+ module NagiosHerald
20
+ class Formatter
21
+ include NagiosHerald::Logging
22
+ include NagiosHerald::Util
23
+
24
+ attr_accessor :content # all the content required to generate a message
25
+ attr_accessor :sandbox # @sandbox is the place to save attachments, possibly a tempdir
26
+ attr_accessor :state_type
27
+
28
+ def initialize(options)
29
+ @content = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) } # autovivify
30
+ @content[:attachments] = []
31
+ @content[:html]
32
+ @content[:subject] = ""
33
+ @content[:text]
34
+ @nagios_url = options[:nagios_url]
35
+ @sandbox = get_sandbox_path
36
+ @state_type = get_nagios_var("NAGIOS_SERVICESTATE") != "" ? "SERVICE" : "HOST"
37
+
38
+ end
39
+
40
+ def self.formatters
41
+ @@formatters ||= {}
42
+ end
43
+
44
+ # Public: When subclassed formatters are instantiated, add them to the @@formatters hash.
45
+ # The key is the downcased and snake_cased name of the class file (i.e. check_disk);
46
+ # the value is the actual class (i.e. CheckDisk) so that we can easily
47
+ # instantiate formatters when we know the formatter name.
48
+ # Learned this pattern thanks to the folks at Chef and @jonlives.
49
+ # See https://github.com/opscode/chef/blob/11-stable/lib/chef/knife.rb#L79#L83
50
+ #
51
+ # Returns the formatters hash.
52
+ def self.inherited(subclass)
53
+ subclass_base_name = subclass.name.split('::').last
54
+ subclass_base_name.gsub!(/[A-Z]/) { |s| "_" + s } # replace uppercase with underscore and lowercase
55
+ subclass_base_name.downcase!
56
+ subclass_base_name.sub!(/^_/, "") # strip the leading underscore
57
+ formatters[subclass_base_name] = subclass
58
+ end
59
+
60
+ # Public: Concatenates text content.
61
+ #
62
+ # section - The content section name whose text we'll concatenate
63
+ # text - The text we want to concatenate
64
+ #
65
+ # Example:
66
+ #
67
+ # add_text("state_detail", "Service is somewhere in Kansas")
68
+ #
69
+ # Returns the concatenated HTML for the given section.
70
+ def add_text(section, text)
71
+ # Ensure our key is a symbol, regardless if we're passed a string or symbol
72
+ section = section.to_sym
73
+ if @content[:text][section].nil? or @content[:text][section].empty?
74
+ @content[:text][section] = text
75
+ else
76
+ @content[:text][section] += text
77
+ end
78
+ end
79
+
80
+ # Public: Concatenates HTML content.
81
+ #
82
+ # section - The content section name whose HTML we'll concatenate
83
+ # text - The HTML we want to concatenate
84
+ #
85
+ # Example:
86
+ #
87
+ # add_html("state_detail", "Service is somewhere in Kansas")
88
+ #
89
+ # Returns the concatenated HTML for the given section.
90
+ def add_html(section, html)
91
+ # Ensure our key is a symbol, regardless if we're passed a string or symbol
92
+ section = section.to_sym
93
+ if @content[:html][section].nil? or @content[:html][section].empty?
94
+ @content[:html][section] = html
95
+ else
96
+ @content[:html][section] += html
97
+ end
98
+ end
99
+
100
+ # Public: Add an attachment's path to an array.
101
+ #
102
+ # path - The fully qualified path for a file attachment
103
+ #
104
+ # Example:
105
+ #
106
+ # add_attachment("/tmp/file-to-attach.txt")
107
+ #
108
+ # Returns the array of attachment paths.
109
+ def add_attachment(path)
110
+ #@attachments << path
111
+ @content[:attachments] << path
112
+ end
113
+
114
+ #
115
+ # format the content
116
+ #
117
+
118
+ # Public: Appends a newline in text and HTML format.
119
+ #
120
+ # section - The content section name that needs the line break
121
+ #
122
+ # Example
123
+ #
124
+ # line_break(additional_info)
125
+ #
126
+ # Appends text and HTML output to the appropriate sections in @content
127
+ def line_break(section)
128
+ add_text(section, "\n")
129
+ add_html(section, "<br>")
130
+ end
131
+
132
+ # Public: Formats the information about the host that's being alerted on.
133
+ # Generates text and HTML output.
134
+ def host_info
135
+ section = __method__
136
+ text = ""
137
+ html = ""
138
+ notification_type = get_nagios_var("NAGIOS_NOTIFICATIONTYPE")
139
+ hostname = get_nagios_var("NAGIOS_HOSTNAME")
140
+ service_desc = get_nagios_var("NAGIOS_SERVICEDESC")
141
+ text += "Host: #{hostname} "
142
+ html += "<br><b>Host</b>: #{hostname} "
143
+ if !service_desc.nil? and !service_desc.empty?
144
+ text += "Service: #{service_desc}\n"
145
+ html += "<b>Service</b>: #{service_desc}<br/>"
146
+ else
147
+ # we need a trailing newline if no service description
148
+ line_break(section)
149
+ end
150
+ add_text(section, text)
151
+ add_html(section, html)
152
+ line_break(section)
153
+ end
154
+
155
+ # Public: Formats information about the state of the thing being alerted on
156
+ # where 'thing' is either HOST or SERVICE.
157
+ # Generates text and HTML output.
158
+ def state_info
159
+ section = __method__
160
+ text = ""
161
+ html = ""
162
+ state = get_nagios_var("NAGIOS_#{@state_type}STATE")
163
+ duration = get_nagios_var("NAGIOS_#{@state_type}DURATION")
164
+ last_duration = get_nagios_var("NAGIOS_LAST#{@state_type}STATE")
165
+ attempts = get_nagios_var("NAGIOS_#{@state_type}ATTEMPT")
166
+ max_attempts = get_nagios_var("NAGIOS_MAX#{@state_type}ATTEMPTS")
167
+
168
+ text += "State is now: #{state} for #{duration} (was #{last_duration}) after #{attempts} / #{max_attempts} checks\n"
169
+
170
+ if state.eql? 'OK' or state.eql? 'UP'
171
+ html += "State is now: <b>#{state}</b> for <b>#{duration}</b> (was #{last_duration}) after <b>#{attempts} / #{max_attempts}</b> checks<br/>"
172
+ else
173
+ html += "State is now: <b><font style='color:red'>#{state}</font></b> for <b>#{duration}</b> (was #{last_duration}) after <b>#{attempts} / #{max_attempts}</b> checks<br/>"
174
+ end
175
+ add_text(section, text)
176
+ add_html(section, html)
177
+ line_break(section)
178
+ end
179
+
180
+ # Public: Formats information about the notification.
181
+ # Provides information such as the date and notification number.
182
+ # Generates text and HTML output.
183
+ def notification_info
184
+ section = __method__
185
+ text = ""
186
+ html = ""
187
+ date = get_nagios_var("NAGIOS_LONGDATETIME")
188
+ number = get_nagios_var("NAGIOS_NOTIFICATIONNUMBER")
189
+ text += "Notification sent at: #{date} (notification number #{number})\n\n"
190
+ html += "Notification sent at: #{date} (notification number #{number})<br><br>"
191
+ add_text(section, text)
192
+ add_html(section, html)
193
+ end
194
+
195
+ # Public: Formats information provided plugin's output.
196
+ # Generates text and HTML output.
197
+ def additional_info
198
+ section = __method__
199
+ text = ""
200
+ html = ""
201
+ output = get_nagios_var("NAGIOS_#{@state_type}OUTPUT")
202
+ if !output.nil? and !output.empty?
203
+ text += "Additional Info: #{unescape_text(output)}\n\n"
204
+ html += "<b>Additional Info</b>: #{output}<br><br>"
205
+ add_text(section, text)
206
+ add_html(section, html)
207
+ end
208
+ end
209
+
210
+ # Public: Formats information provided plugin's *long* output.
211
+ # Generates text and HTML output.
212
+ def additional_details
213
+ section = __method__
214
+ text = ""
215
+ html = ""
216
+ long_output = get_nagios_var("NAGIOS_LONG#{@state_type}OUTPUT")
217
+ if !long_output.nil? and !long_output.empty?
218
+ text += "Additional Details: #{unescape_text(long_output)}\n"
219
+ html += "<b>Additional Details</b>: <pre>#{unescape_text(long_output)}</pre><br><br>"
220
+ add_text(section, text)
221
+ add_html(section, html)
222
+ end
223
+ end
224
+
225
+ # Public: Formats the notes information for this alert.
226
+ # Generates text and HTML output.
227
+ def notes
228
+ section = __method__
229
+ text = ""
230
+ html = ""
231
+ notes = get_nagios_var("NAGIOS_#{@state_type}NOTES")
232
+ if !notes.nil? and !notes.empty?
233
+ text += "Notes: #{unescape_text(notes)}\n\n"
234
+ html += "<b>Notes</b>: #{notes}<br><br>"
235
+ end
236
+
237
+ notes_url = get_nagios_var("NAGIOS_#{@state_type}NOTESURL")
238
+ if !notes_url.nil? and !notes_url.empty?
239
+ text += "Notes URL: #{notes_url}\n\n"
240
+ html += "<b>Notes URL</b>: #{notes_url}<br><br>"
241
+ end
242
+ add_text(section, text)
243
+ add_html(section, html)
244
+ end
245
+
246
+ # Public: Formats the action URL for this alert.
247
+ # Generates text and HTML output.
248
+ def action_url
249
+ section = __method__
250
+ text = ""
251
+ html = ""
252
+ action_url = get_nagios_var("NAGIOS_#{@state_type}ACTIONURL")
253
+ if !action_url.nil? and !action_url.empty?
254
+ text += "Action URL: #{action_url}\n\n"
255
+ html += "<b>Action URL</b>: #{action_url}<br><br>"
256
+ end
257
+ add_text(section, text)
258
+ add_html(section, html)
259
+ end
260
+
261
+ # FIXME: Looks like a dupe of #additional_info (used in pager alerts, it seems)
262
+ def short_state_detail
263
+ section = __method__
264
+ text = ""
265
+ html = ""
266
+ output = get_nagios_var("NAGIOS_#{@state_type}OUTPUT")
267
+ text += "#{output}\n"
268
+ html += "#{output}<br>"
269
+ add_text(section, text)
270
+ add_html(section, html)
271
+ end
272
+
273
+ # Public: Formats the email recipients and URIs
274
+ # Generates text and HTML output.
275
+ def recipients_email_link
276
+ section = __method__
277
+ text = ""
278
+ html = ""
279
+ recipients = get_nagios_var("NAGIOS_NOTIFICATIONRECIPIENTS")
280
+ return if recipients.nil?
281
+ recipients_list = recipients.split(',')
282
+ text += "Sent to #{recipients}\n\n"
283
+ html += "Sent to #{recipients}<br><br>"
284
+ add_text(section, text)
285
+ add_html(section, html)
286
+ end
287
+
288
+ # Public: Formats the information about who ack'd the alert and when
289
+ # Generates text and HTML output.
290
+ def ack_info
291
+ section = __method__
292
+ text = ""
293
+ html = ""
294
+ date = get_nagios_var("NAGIOS_LONGDATETIME")
295
+ author = get_nagios_var("NAGIOS_#{@state_type}ACKAUTHOR")
296
+ comment = get_nagios_var("NAGIOS_#{@state_type}ACKCOMMENT")
297
+ hostname = get_nagios_var("NAGIOS_HOSTNAME")
298
+
299
+ text += "At #{date} #{author}"
300
+ html += "At #{date} #{author}"
301
+
302
+ if @state_type == "SERVICE"
303
+ desc = get_nagios_var("NAGIOS_SERVICEDESC")
304
+ text += " acknowledged #{hostname}/#{desc}.\n\n"
305
+ html += " acknowledged #{hostname}/#{desc}.<br><br>"
306
+ else
307
+ text += " acknowledged #{hostname}.\n\n"
308
+ html += " acknowledged #{hostname}.<br><br>"
309
+
310
+ end
311
+ text += "Comment: #{comment}" if comment
312
+ html += "Comment: #{comment}" if comment
313
+ add_text(section, text)
314
+ add_html(section, html)
315
+ end
316
+
317
+ # Public: Formats brief ack information.
318
+ # Useful for pager messages.
319
+ # Generates text and HTML output.
320
+ def short_ack_info
321
+ section = __method__
322
+ text = ""
323
+ html = ""
324
+ author = get_nagios_var("NAGIOS_#{@state_type}ACKAUTHOR")
325
+ comment = get_nagios_var("NAGIOS_#{@state_type}COMMENT")
326
+ hostname = get_nagios_var("NAGIOS_HOSTNAME")
327
+
328
+ text += "#{author} ack'd "
329
+ html += "#{author} ack'd "
330
+
331
+ if @state_type == "SERVICE"
332
+ desc = get_nagios_var("NAGIOS_SERVICEDESC")
333
+ text += "#{desc} on #{hostname}.\n"
334
+ html += "#{desc} on #{hostname}.<br>"
335
+ else
336
+ text += "#{hostname}.\n"
337
+ html += "#{hostname}.<br>"
338
+
339
+ end
340
+ text += "Comment: #{comment}" if comment
341
+ html += "Comment: #{comment}" if comment
342
+ add_text(section, text)
343
+ add_html(section, html)
344
+ end
345
+
346
+ # Public: Formats the URI one can click to acknowledge an alert (i.e. in an email)
347
+ # Generates text and HTML output.
348
+ def alert_ack_url
349
+ section = __method__
350
+ text = ""
351
+ html = ""
352
+ hostname = get_nagios_var("NAGIOS_HOSTNAME")
353
+ service_desc = get_nagios_var("NAGIOS_SERVICEDESC")
354
+
355
+ if service_desc != ""
356
+ url = "#{@nagios_url}/nagios/cgi-bin/cmd.cgi?cmd_typ=34&host=#{hostname}&service=#{service_desc}"
357
+ else
358
+ url = "#{@nagios_url}/nagios/cgi-bin/cmd.cgi?cmd_typ=33&host=#{hostname}"
359
+ end
360
+ url = URI.escape(url)
361
+ text += "Acknowledge this alert: #{url}\n"
362
+ text += "Alternatively, reply to this message with the word 'ack' in the body to acknowledge the alert.\n"
363
+ html += "Acknowledge this alert: #{url}<br>"
364
+ html += "Alternatively, <b>reply</b> to this message with the word '<b><font color='green'>ack</font></b>' in the body to acknowledge the alert.<br>"
365
+ add_text(section, text)
366
+ add_html(section, html)
367
+ end
368
+
369
+ #
370
+ # structural bits and content generation
371
+ #
372
+
373
+ # Public: Starts a format section's HTML <div> block.
374
+ #
375
+ # section - The name of the section whose HTML we'll start.
376
+ # *section_style_args - CSS-type attributes used to style the content.
377
+ #
378
+ # Example
379
+ #
380
+ # start_section("additional_details", "color:green")
381
+ #
382
+ # Generates HTML <div> block with the requested style.
383
+ def start_section(section, *section_style_args)
384
+ html = ""
385
+ if !section_style_args.nil?
386
+ style = section_style_args.join(';')
387
+ html += "<div style='#{style}'>"
388
+ else
389
+ html += "<div>"
390
+ end
391
+ add_html(section, html)
392
+ end
393
+
394
+ # Public: Ends a format section's HTML <div> block.
395
+ #
396
+ # section - The name of the section whose HTML we'll start.
397
+ #
398
+ # Example
399
+ #
400
+ # start_section("additional_details")
401
+ #
402
+ # Generates an ending HTML <div> tag.
403
+ def end_section(section)
404
+ add_html(section, "</div>")
405
+ end
406
+
407
+ # Public: Wrapper for starting a format section, calling the format method,
408
+ # and ending the section.
409
+ #
410
+ # section - The name of the section whose HTML we'll start.
411
+ # *section_style_args - A list of style attributes to be used in the <div> block for the section.
412
+ #
413
+ # Example:
414
+ #
415
+ # generate_section("additional_info", "color:green", "font-weight:bold") - Color all text green and bold it
416
+ # generate_section("additional_info") - Color all text green
417
+ #
418
+ # Calls the relevant section method to generate content.
419
+ def generate_section(section, *section_style_args)
420
+ start_section(section, *section_style_args)
421
+ self.send(section)
422
+ end_section(section)
423
+ end
424
+
425
+ # Public: Generate content for PROBLEM alerts.
426
+ def generate_problem_content
427
+ generate_section("host_info")
428
+ generate_section("state_info")
429
+ generate_section("additional_info")
430
+ generate_section("action_url")
431
+ generate_section("notes")
432
+ generate_section("additional_details")
433
+ generate_section("recipients_email_link")
434
+ generate_section("notification_info")
435
+ generate_section("alert_ack_url")
436
+ end
437
+
438
+ # Public: Generate content for RECOVERY alerts.
439
+ def generate_recovery_content
440
+ generate_section("host_info")
441
+ generate_section("state_info", "color:green")
442
+ generate_section("additional_info")
443
+ generate_section("action_url")
444
+ generate_section("notes")
445
+ generate_section("additional_details")
446
+ generate_section("recipients_email_link")
447
+ generate_section("notification_info")
448
+ end
449
+
450
+ # Public: Generate content for ACKNOWLEGEMENT alerts
451
+ def generate_ack_content
452
+ generate_section("host_info")
453
+ generate_section("ack_info")
454
+ end
455
+
456
+ # Public: Dispatch method to help generate content based on notification
457
+ # type.
458
+ #
459
+ # nagios_notification_type - One of any valid Nagios notification types.
460
+ #
461
+ # Example
462
+ #
463
+ # generate_content("PROBLEM")
464
+ #
465
+ def generate_content(nagios_notification_type)
466
+ case nagios_notification_type
467
+ when "PROBLEM", "FLAPPINGSTART"
468
+ generate_problem_content
469
+ when "RECOVERY", "FLAPPINGSTOP"
470
+ generate_recovery_content
471
+ when "ACKNOWLEDGEMENT"
472
+ generate_ack_content
473
+ else
474
+ logger.fatal "Invalid Nagios notification type! Expecting something like PROBLEM or RECOVERY. We got #{nagios_notification_type}."
475
+ exit 1
476
+ end
477
+ end
478
+
479
+ # Public: Generates a subject.
480
+ #
481
+ # Returns a subject.
482
+ def generate_subject
483
+ hostname = get_nagios_var("NAGIOS_HOSTNAME")
484
+ service_desc = get_nagios_var("NAGIOS_SERVICEDESC")
485
+ notification_type = get_nagios_var("NAGIOS_NOTIFICATIONTYPE")
486
+ state = get_nagios_var("NAGIOS_#{@state_type}STATE")
487
+
488
+ subject="#{hostname}"
489
+ subject += "/#{service_desc}" if service_desc != ""
490
+
491
+ if @state_type == "SERVICE"
492
+ subject="#{notification_type} Service #{subject} is #{state}"
493
+ else
494
+ subject="#{notification_type} Host #{subject} is #{state}"
495
+ end
496
+ @content[:subject] = subject
497
+ end
498
+
499
+ # Public: Generates content body.
500
+ #
501
+ # Call various formatting methods that each generate content for their given sections.
502
+ def generate_message_content
503
+ generate_subject
504
+ nagios_notification_type = get_nagios_var('NAGIOS_NOTIFICATIONTYPE')
505
+ generate_content(nagios_notification_type)
506
+ end
507
+
508
+ # Public: Creates a temporary directory in which to create files used in
509
+ # attachments.
510
+ #
511
+ # Returns the path to a temporary directory.
512
+ def get_sandbox_path
513
+ @sandbox = Dir.mktmpdir if @sandbox.nil?
514
+ return @sandbox
515
+ end
516
+
517
+ # Public: Does some housecleaning on the sandbox, if it exists.
518
+ def clean_sandbox
519
+ FileUtils.remove_entry @sandbox if File.directory?(@sandbox)
520
+ end
521
+
522
+ end
523
+ end
524
+
@@ -0,0 +1,71 @@
1
+ module NagiosHerald
2
+ class Formatter
3
+ class CheckCpu < NagiosHerald::Formatter
4
+ include NagiosHerald::Logging
5
+
6
+ # Public: Overrides Formatter::Base#additional_info.
7
+ # Colorizes the service output to highlight either the iowait or idle value.
8
+ #
9
+ # WARNING CPU iowait is > 0%: user=3.60% system=0.99% iowait=0.00% idle=95.41%
10
+ # CRITICAL CPU idle is < 100%: user=3.02% system=3.25% iowait=0.01% idle=93.72%
11
+ #
12
+ # Returns nothing. Updates the formatter content hash.
13
+ def additional_info
14
+ section = __method__
15
+ output = get_nagios_var("NAGIOS_#{@state_type}OUTPUT")
16
+ #if match = /(?<state>\w+ CPU) (?<metric>\w+) (?<threshold_and_stats>.*) (?<iowait>iowait=.*%) (?<idle>idle=.*%)/.match(output)
17
+ add_html(section, "<b>Additional Info</b>:<br>")
18
+ add_text(section, "Additional Info: ")
19
+ if match = /(?<state>\w+ CPU) (?<metric>iowait) (?<threshold_and_stats>.*) (?<iowait>iowait=.*%) (?<idle>idle=.*%)/.match(output)
20
+ iowait_info = "#{match[:state]} <b><font color='red'>#{match[:metric]}</font></b> "
21
+ iowait_info += "#{match[:threshold_and_stats]} <b><font color='red'>#{match[:iowait]}</font></b> "
22
+ iowait_info += "#{match[:idle]}"
23
+ add_html(section, iowait_info)
24
+ elsif match = /(?<state>\w+ CPU) (?<metric>idle) (?<threshold_and_stats>.*) (?<iowait>iowait=.*%) (?<idle>idle=.*%)/.match(output)
25
+ iowait_info = "#{match[:state]} <b><font color='red'>#{match[:metric]}</font></b> "
26
+ iowait_info += "#{match[:threshold_and_stats]} #{match[:iowait]} "
27
+ iowait_info += "<b><font color='red'>#{match[:idle]}</font></b>"
28
+ add_html(section, iowait_info)
29
+ else
30
+ add_html(section, output)
31
+ end
32
+ add_text(section, output) # nothing fancy to see for text
33
+ end
34
+
35
+ # Public: Overrides Formatter::Base#additional_details.
36
+ # Colorizes the `ps` output returned by the check_cpu_stats NRPE check.
37
+ # The output contains the top n processes by CPU similar to:
38
+ #
39
+ # TOP 5 PROCESSES BY CPU:
40
+ # %CPU TIME USER PID COMMAND
41
+ # 6.0 00:00:00 larry 32256 ps -eo %cpu,cputime,user,pid,args --sort -%cpu
42
+ # 0.7 06:22:09 nobody 12161 /usr/sbin/gmond
43
+ # 0.6 1-02:14:24 root 1424 [kipmi0]
44
+ # 0.5 00:49:52 10231 15079 mosh-server new -s -c 8 -l LANG=en_US.UTF-8
45
+ # 0.3 04:36:53 root 12996 /opt/extrahop/sbin/rpcapd -v -d -L -f /opt/extrahop/etc/rpcapd.ini
46
+ #
47
+ # Returns nothing. Updates the formatter content hash.
48
+ def additional_details
49
+ section = __method__
50
+ long_output = get_nagios_var("NAGIOS_LONG#{@state_type}OUTPUT")
51
+ lines = long_output.split('\n')
52
+ html = []
53
+ html << "<pre>"
54
+ html << lines[0] # TOP 5 PROCESSES BY CPU:
55
+ html << lines[1] # %CPU TIME USER PID COMMAND
56
+ html << "<font color='red'>#{lines[2]}</font>" # Color the first result red...
57
+ for i in 3..lines.length-1
58
+ html << "<font color='orange'>#{lines[i]}</font>" # ...and the remainder orange.
59
+ end
60
+ html << "</pre>"
61
+ output_string = html.join( "<br>" )
62
+ add_html(section, "<b>Additional Details</b>:")
63
+ add_html(section, output_string)
64
+ add_text(section, "Additional Details:\n#")
65
+ add_text(section, "#{unescape_text(long_output)}\n")
66
+ line_break(section)
67
+ end
68
+
69
+ end
70
+ end
71
+ end