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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +28 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +94 -0
- data/Rakefile +9 -0
- data/bin/draw_stack_bars +76 -0
- data/bin/dump_nagios_env.sh +25 -0
- data/bin/get_ganglia_graph +82 -0
- data/bin/get_graph +50 -0
- data/bin/get_graphite_graph +58 -0
- data/bin/nagios-herald +6 -0
- data/bin/splunk_alert_frequency +54 -0
- data/contrib/nrpe-plugins/check_cpu_stats.sh +186 -0
- data/contrib/nrpe-plugins/check_disk.sh +34 -0
- data/contrib/nrpe-plugins/check_mem.pl +181 -0
- data/contrib/nrpe-plugins/nrpe-plugin-examples.md +11 -0
- data/docs/config.md +62 -0
- data/docs/example_alerts.md +48 -0
- data/docs/formatters.md +180 -0
- data/docs/helpers.md +12 -0
- data/docs/images/cpu_no_context.png +0 -0
- data/docs/images/cpu_with_context.png +0 -0
- data/docs/images/disk_space_no_context.png +0 -0
- data/docs/images/disk_space_with_context.png +0 -0
- data/docs/images/memory_high_no_context.png +0 -0
- data/docs/images/memory_high_with_context.png +0 -0
- data/docs/images/nagios-herald-formatter-content-example.png +0 -0
- data/docs/images/nagios-herald.png +0 -0
- data/docs/images/stack-bars.png +0 -0
- data/docs/images/vanilla-nagios.png +0 -0
- data/docs/messages.md +16 -0
- data/docs/nagios-config.md +74 -0
- data/docs/tools.md +79 -0
- data/etc/config.yml.example +14 -0
- data/etc/readme.md +2 -0
- data/lib/nagios-herald/config.rb +25 -0
- data/lib/nagios-herald/executor.rb +265 -0
- data/lib/nagios-herald/formatter_loader.rb +82 -0
- data/lib/nagios-herald/formatters/base.rb +524 -0
- data/lib/nagios-herald/formatters/check_cpu.rb +71 -0
- data/lib/nagios-herald/formatters/check_disk.rb +143 -0
- data/lib/nagios-herald/formatters/check_logstash.rb +155 -0
- data/lib/nagios-herald/formatters/check_memory.rb +42 -0
- data/lib/nagios-herald/formatters/example.rb +19 -0
- data/lib/nagios-herald/formatters.rb +1 -0
- data/lib/nagios-herald/helpers/ganglia_graph.rb +99 -0
- data/lib/nagios-herald/helpers/graphite_graph.rb +85 -0
- data/lib/nagios-herald/helpers/logstash_query.rb +125 -0
- data/lib/nagios-herald/helpers/splunk_alert_frequency.rb +170 -0
- data/lib/nagios-herald/helpers/splunk_query.rb +119 -0
- data/lib/nagios-herald/helpers/url_image.rb +76 -0
- data/lib/nagios-herald/helpers.rb +5 -0
- data/lib/nagios-herald/logging.rb +48 -0
- data/lib/nagios-herald/message_loader.rb +40 -0
- data/lib/nagios-herald/messages/base.rb +56 -0
- data/lib/nagios-herald/messages/email.rb +150 -0
- data/lib/nagios-herald/messages/irc.rb +58 -0
- data/lib/nagios-herald/messages/pager.rb +75 -0
- data/lib/nagios-herald/messages.rb +3 -0
- data/lib/nagios-herald/test_helpers/base_test_case.rb +82 -0
- data/lib/nagios-herald/util.rb +45 -0
- data/lib/nagios-herald/version.rb +3 -0
- data/lib/nagios-herald.rb +7 -0
- data/lib/stackbars/__init__.py +0 -0
- data/lib/stackbars/chart_utils.py +25 -0
- data/lib/stackbars/grouped_stackbars.py +97 -0
- data/lib/stackbars/pilfonts/Tahoma.ttf +0 -0
- data/lib/stackbars/pilfonts/aerial.ttf +0 -0
- data/lib/stackbars/pilfonts/arial_black.ttf +0 -0
- data/lib/stackbars/stackbar.py +100 -0
- data/nagios-herald.gemspec +33 -0
- data/test/env_files/check_cpu_idle.CRITICAL +199 -0
- data/test/env_files/check_cpu_iowait.WARNING +199 -0
- data/test/env_files/check_disk.CRITICAL +197 -0
- data/test/env_files/check_disk.CRITICAL_ICINGA +197 -0
- data/test/env_files/check_disk.RECOVERY +197 -0
- data/test/env_files/check_memory.CRITICAL +197 -0
- data/test/env_files/nagios_vars.EXAMPLE +197 -0
- data/test/unit/test_config.rb +31 -0
- data/test/unit/test_executor.rb +65 -0
- data/test/unit/test_formatter_base.rb +131 -0
- data/test/unit/test_formatter_check_cpu_idle_critical.rb +135 -0
- data/test/unit/test_formatter_check_memory.rb +135 -0
- data/test/unit/test_icinga_variables.rb +31 -0
- data/test/unit/test_logging.rb +35 -0
- data/test/unit/test_message_email.rb +69 -0
- data/test/unit/test_message_pager.rb +69 -0
- metadata +204 -0
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'nagios-herald'
|
2
|
+
require 'choice'
|
3
|
+
require 'app_conf'
|
4
|
+
|
5
|
+
module NagiosHerald
|
6
|
+
class Executor
|
7
|
+
include NagiosHerald::Logging
|
8
|
+
include NagiosHerald::Util
|
9
|
+
|
10
|
+
# Public: Parse the command line options.
|
11
|
+
#
|
12
|
+
# Returns a hash of the specified options and defaults as appropriate.
|
13
|
+
def parse_options
|
14
|
+
program_name = File.basename($0)
|
15
|
+
|
16
|
+
Choice.options do
|
17
|
+
header "Nagios Herald - Spread the word"
|
18
|
+
header ""
|
19
|
+
|
20
|
+
option :config_file do
|
21
|
+
short "-c"
|
22
|
+
long "--config-file"
|
23
|
+
desc "Specify an alternate location for the config file."
|
24
|
+
default File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'etc', 'config.yml'))
|
25
|
+
end
|
26
|
+
|
27
|
+
option :debug do
|
28
|
+
short "-d"
|
29
|
+
long "--debug"
|
30
|
+
desc "BE VERBOSE! B-E V-E-R-B-O-S-E!"
|
31
|
+
end
|
32
|
+
|
33
|
+
option :env do
|
34
|
+
short "-e"
|
35
|
+
long "--env-file"
|
36
|
+
desc "Path to a file containing environment variables to use for testing/debugging (i.e. nagios_vars)."
|
37
|
+
end
|
38
|
+
|
39
|
+
option :formatter_dir do
|
40
|
+
short "-F"
|
41
|
+
long "--formatter-dir"
|
42
|
+
desc "Formatter directory"
|
43
|
+
default nil
|
44
|
+
end
|
45
|
+
|
46
|
+
option :formatter_name do
|
47
|
+
short "-f"
|
48
|
+
long "--formatter"
|
49
|
+
desc "Formatter name"
|
50
|
+
default nil
|
51
|
+
end
|
52
|
+
|
53
|
+
option :formatter_dir do
|
54
|
+
short "-F"
|
55
|
+
long "--formatter-dir"
|
56
|
+
desc "Formatter directory"
|
57
|
+
default nil
|
58
|
+
end
|
59
|
+
|
60
|
+
option :logfile do
|
61
|
+
short "-l"
|
62
|
+
long "--logfile"
|
63
|
+
desc "Logfile location"
|
64
|
+
desc "Can be a file name or STDOUT (i.e. -l /tmp/output.log or -l STDOUT)"
|
65
|
+
desc "[DEFAULT] Uses the value of 'logfile' in the config or STDOUT if not defined."
|
66
|
+
default nil
|
67
|
+
end
|
68
|
+
|
69
|
+
option :message_type, :required => true do
|
70
|
+
short "-m"
|
71
|
+
long "--message-type"
|
72
|
+
desc "[REQUIRED] Type of message to deliver (i.e. email, IRC, pager)"
|
73
|
+
end
|
74
|
+
|
75
|
+
option :no_send do
|
76
|
+
short "-N"
|
77
|
+
long "--no-send"
|
78
|
+
desc "Output content to screen but do not send it"
|
79
|
+
end
|
80
|
+
|
81
|
+
option :notification_type do
|
82
|
+
short "-n"
|
83
|
+
long "--notification-type"
|
84
|
+
desc "NAGIOS_NOTIFICATION_TYPE to report - defaults to the nagios env variable."
|
85
|
+
desc "Valid options: PROBLEM, FLAPPINGSTART, RECOVERY, FLAPPINGSTOP, ACKNOWLEDGEMENT"
|
86
|
+
end
|
87
|
+
|
88
|
+
option :pager_mode do
|
89
|
+
short "-p"
|
90
|
+
long "--pager"
|
91
|
+
desc "Enable pager mode"
|
92
|
+
end
|
93
|
+
|
94
|
+
option :recipients do
|
95
|
+
short "-r"
|
96
|
+
long "--recipient"
|
97
|
+
desc "A recipient's email address. Specify multiple recipients with multiple '-r' arguments."
|
98
|
+
desc "If not specified, recipients are looked up in the ENV['NAGIOS_CONTACTEMAIL'] environment variable."
|
99
|
+
end
|
100
|
+
|
101
|
+
option :trace do
|
102
|
+
short "-t"
|
103
|
+
long "--trace"
|
104
|
+
desc "Show a full traceback on error"
|
105
|
+
default false
|
106
|
+
end
|
107
|
+
|
108
|
+
option :nagios_url do
|
109
|
+
short "-u"
|
110
|
+
long "--nagios-cgi-url"
|
111
|
+
desc "Nagios CGI url (used for acknowledgement links)"
|
112
|
+
end
|
113
|
+
|
114
|
+
option :replyto, :required => true do
|
115
|
+
short "-y"
|
116
|
+
long "--reply-to"
|
117
|
+
desc "[REQUIRED] Reply-to email address (i.e. nagios@example.com) used for acknowledgement replies."
|
118
|
+
end
|
119
|
+
|
120
|
+
footer ""
|
121
|
+
footer "EXAMPLES"
|
122
|
+
footer "--------"
|
123
|
+
footer "#{program_name} -r ops@example.com --env-file=test/env_files/nagios_vars -y nagios@example.com --formatter=check_disk"
|
124
|
+
footer ""
|
125
|
+
end
|
126
|
+
|
127
|
+
return Choice.choices
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# Public: Load environment variables from a file.
|
132
|
+
# This is useful for running controlled tests.
|
133
|
+
#
|
134
|
+
# Updates the ENV hash for each key/value pair.
|
135
|
+
def load_env_from_file(path)
|
136
|
+
File.readlines(path).each do |line|
|
137
|
+
values = line.split("=")
|
138
|
+
key = values[0]
|
139
|
+
value = values[1, values.length - 1 ].map {|v| v.strip() }.join('=')
|
140
|
+
ENV[key] = value
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Public: Instantiate a new FormatterLoader object.
|
145
|
+
#
|
146
|
+
# Returns a new FormatterLoader object.
|
147
|
+
def formatter_loader
|
148
|
+
@formatter_loader ||= NagiosHerald::FormatterLoader.new
|
149
|
+
end
|
150
|
+
|
151
|
+
# Public: Loads all formatter classes.
|
152
|
+
#
|
153
|
+
# Returns true.
|
154
|
+
def load_formatters
|
155
|
+
@formatters_loaded ||= formatter_loader.load_formatters
|
156
|
+
end
|
157
|
+
|
158
|
+
# Public: Instantiate a new MessageLoader object.
|
159
|
+
#
|
160
|
+
# Returns a new MessageLoader object.
|
161
|
+
def message_loader
|
162
|
+
@message_loader ||= NagiosHerald::MessageLoader.new
|
163
|
+
end
|
164
|
+
|
165
|
+
# Public: Loads all message classes.
|
166
|
+
#
|
167
|
+
# Returns true.
|
168
|
+
def load_messages
|
169
|
+
@messages_loaded ||= message_loader.load_messages
|
170
|
+
end
|
171
|
+
|
172
|
+
# Public: The main method from which notifications are generated and sent.
|
173
|
+
def announce
|
174
|
+
begin
|
175
|
+
@options = parse_options
|
176
|
+
rescue SystemExit
|
177
|
+
logger.fatal "Invalid or missing options\n"
|
178
|
+
exit 1
|
179
|
+
end
|
180
|
+
|
181
|
+
begin
|
182
|
+
# Load the environment if asked for it
|
183
|
+
load_env_from_file(@options.env) if @options.env
|
184
|
+
|
185
|
+
# Load the config for use globally
|
186
|
+
Config.load(@options)
|
187
|
+
|
188
|
+
# load the formatters and messages
|
189
|
+
load_formatters
|
190
|
+
load_messages
|
191
|
+
|
192
|
+
recipients = {}
|
193
|
+
if @options.recipients.nil?
|
194
|
+
contact_email = get_nagios_var("NAGIOS_CONTACTEMAIL")
|
195
|
+
contact_pager = get_nagios_var("NAGIOS_CONTACTPAGER")
|
196
|
+
recipients['email'] = contact_email if !contact_email.empty?
|
197
|
+
recipients['pager'] = contact_pager if !contact_pager.empty?
|
198
|
+
else
|
199
|
+
recipients['command_line'] = @options.recipients
|
200
|
+
end
|
201
|
+
nagios_notification_type = @options.notification_type.nil? ? get_nagios_var("NAGIOS_NOTIFICATIONTYPE") : @options.notification_type
|
202
|
+
|
203
|
+
# Log the host and service that's notifying (assuming we can read the environment)
|
204
|
+
state_type = get_nagios_var("NAGIOS_SERVICESTATE") != "" ? "SERVICE" : "HOST"
|
205
|
+
hostname = get_nagios_var("NAGIOS_HOSTNAME")
|
206
|
+
service_desc = get_nagios_var("NAGIOS_SERVICEDESC")
|
207
|
+
state = get_nagios_var("NAGIOS_#{state_type}STATE")
|
208
|
+
if !service_desc.nil? and !service_desc.empty?
|
209
|
+
logger.info "CHECK (SERVICE): #{hostname}/#{service_desc} is #{state}"
|
210
|
+
else
|
211
|
+
logger.info "CHECK (HOST): #{hostname} is #{state}"
|
212
|
+
end
|
213
|
+
|
214
|
+
recipients.each do |recipient_type, recipient|
|
215
|
+
contact_name = get_nagios_var("NAGIOS_CONTACTALIAS")
|
216
|
+
if recipient.nil? || recipient.eql?("")
|
217
|
+
logger.error "No recipient defined for #{contact_name} (type=#{recipient_type})!"
|
218
|
+
next
|
219
|
+
else
|
220
|
+
logger.info "Notifying #{contact_name} (#{recipient}, type=#{recipient_type})"
|
221
|
+
end
|
222
|
+
|
223
|
+
# bail if can't identify the message type because we don't know what sort of thing to send
|
224
|
+
if !Message.message_types.has_key?(@options.message_type)
|
225
|
+
logger.error "Unknown message type: '#{@options.message_type}'"
|
226
|
+
logger.error "I'm aware of the following message types:"
|
227
|
+
sorted_message_types = Message.message_types.sort_by {|message_type, message_class| message_type}
|
228
|
+
sorted_message_types.each do |message_type|
|
229
|
+
logger.error " + #{message_type.first}"
|
230
|
+
end
|
231
|
+
exit 1
|
232
|
+
end
|
233
|
+
message_class = Message.message_types[@options.message_type]
|
234
|
+
message = message_class.new(recipient, @options)
|
235
|
+
|
236
|
+
formatter_class = Formatter.formatters[@options.formatter_name]
|
237
|
+
if formatter_class.nil?
|
238
|
+
logger.info "Undefined formatter. Defaulting to the base formatter."
|
239
|
+
formatter_class = NagiosHerald::Formatter # default to the base formatter
|
240
|
+
end
|
241
|
+
formatter = formatter_class.new(@options)
|
242
|
+
|
243
|
+
formatter.generate_message_content
|
244
|
+
message.content = formatter.content
|
245
|
+
message.send
|
246
|
+
formatter.clean_sandbox # clean house
|
247
|
+
end
|
248
|
+
rescue Exception => e
|
249
|
+
logger.fatal "#{e.class}: #{e.message}"
|
250
|
+
logger.fatal "COMMAND LINE #{File.basename $0} #{ARGV.join(" ")}"
|
251
|
+
if @options[:trace]
|
252
|
+
e.backtrace.each do |line|
|
253
|
+
logger.fatal "TRACE #{line}"
|
254
|
+
end
|
255
|
+
else
|
256
|
+
logger.fatal "Use --trace for backtrace."
|
257
|
+
end
|
258
|
+
raise e if @options[:trace] || e.is_a?(SystemExit)
|
259
|
+
exit 1
|
260
|
+
end
|
261
|
+
exit 0
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# This is why open source rocks my worl'! I lifted this pattern of loading classes
|
2
|
+
# from the good folks at Chef. The gist is that a given directory contains a set
|
3
|
+
# of class files that we want to load into the namespace for use later.
|
4
|
+
# This allows us to build up a hash of the classes where the key is the class file
|
5
|
+
# name and the value is the class itself. We can then instantiate an object of a
|
6
|
+
# given class dynamically.
|
7
|
+
# See https://github.com/opscode/chef/blob/11-stable/lib/chef/knife.rb#L79#L83 for
|
8
|
+
# an example of this in action via the #inherited method.
|
9
|
+
module NagiosHerald
|
10
|
+
class FormatterLoader
|
11
|
+
include NagiosHerald::Logging
|
12
|
+
|
13
|
+
attr_accessor :builtin_formatter_path
|
14
|
+
attr_accessor :custom_formatter_path
|
15
|
+
|
16
|
+
# Public: Initialize the formatter loader module.
|
17
|
+
# Adds the path to the built-in formatters to the module variable @formatter_paths.
|
18
|
+
# Adds the path to custom formatters if that option is passed to `nagios-herald`
|
19
|
+
# via '-F|--formatter-dir' on the command line or via the 'formatter-dir'
|
20
|
+
# option in the configuration file.
|
21
|
+
def initialize
|
22
|
+
@builtin_formatter_path = File.expand_path("formatters", File.dirname(__FILE__))
|
23
|
+
@custom_formatter_path = Config.config['formatter_dir'] if Config.config['formatter_dir']
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Enumerate the available formatter class files.
|
27
|
+
#
|
28
|
+
# Returns a hash of the class files' absolute paths whose
|
29
|
+
# key is the formatter file's basename and the
|
30
|
+
# value is the full path to the file.
|
31
|
+
def enum_formatter_class_files
|
32
|
+
formatter_class_files = {}
|
33
|
+
|
34
|
+
# Iterate over the builtin formatters first.
|
35
|
+
builtin_formatter_class_files = Dir.glob(File.expand_path("*.rb", @builtin_formatter_path))
|
36
|
+
builtin_formatter_class_files.each do |builtin_formatter_class_file|
|
37
|
+
formatter_class_files[File.basename(builtin_formatter_class_file)] = builtin_formatter_class_file
|
38
|
+
end
|
39
|
+
# If we've been told about custom formatters, add them to the hash.
|
40
|
+
# If there are conflicts, naively merge them, with the custom formatters getting
|
41
|
+
# priority.
|
42
|
+
if @custom_formatter_path
|
43
|
+
custom_formatters = {}
|
44
|
+
custom_formatter_class_files = Dir.glob(File.expand_path("*.rb", @custom_formatter_path))
|
45
|
+
custom_formatter_class_files.each do |custom_formatter_class_file|
|
46
|
+
custom_formatters[File.basename(custom_formatter_class_file)] = custom_formatter_class_file
|
47
|
+
end
|
48
|
+
formatter_class_files.merge!(custom_formatters)
|
49
|
+
end
|
50
|
+
formatter_class_files
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: An array of class files' paths.
|
54
|
+
#
|
55
|
+
# Returns an array of class files' paths.
|
56
|
+
def formatter_class_files
|
57
|
+
@formatter_class_files ||= enum_formatter_class_files
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Load the formatters into the namespace.
|
61
|
+
# A formatter can then easily be instantiated later.
|
62
|
+
#
|
63
|
+
# Returns nothing but loads the classes into the namespace.
|
64
|
+
def load_formatters
|
65
|
+
if formatter_class_files.empty?
|
66
|
+
if @custom_formatter_path
|
67
|
+
puts "#{$0}: No formatters were found in '#{@builtin_formatter_path}'" \
|
68
|
+
" or '#{@custom_formatter_path}' (as defined by the 'formatter_dir' option)!"
|
69
|
+
else
|
70
|
+
puts "#{$0}: No formatters were found in '#{@builtin_formatter_path}'!"
|
71
|
+
end
|
72
|
+
exit 1
|
73
|
+
else
|
74
|
+
formatter_class_files.each do |formatter_class, formatter_class_file|
|
75
|
+
Kernel.load formatter_class_file
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|