mcproc 2016.2.20
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/Announce.txt +135 -0
- data/Gemfile +9 -0
- data/History.txt +469 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +185 -0
- data/TODO.md +37 -0
- data/bin/mcproc +134 -0
- data/doc/intro.asciidoc +20 -0
- data/doc/mcproc.asciidoc +1592 -0
- data/ext/god/.gitignore +5 -0
- data/ext/god/extconf.rb +56 -0
- data/ext/god/kqueue_handler.c +133 -0
- data/ext/god/netlink_handler.c +182 -0
- data/lib/god.rb +780 -0
- data/lib/god/behavior.rb +52 -0
- data/lib/god/behaviors/clean_pid_file.rb +21 -0
- data/lib/god/behaviors/clean_unix_socket.rb +21 -0
- data/lib/god/behaviors/notify_when_flapping.rb +51 -0
- data/lib/god/cli/command.rb +268 -0
- data/lib/god/cli/run.rb +170 -0
- data/lib/god/cli/version.rb +23 -0
- data/lib/god/compat19.rb +33 -0
- data/lib/god/condition.rb +96 -0
- data/lib/god/conditions/always.rb +36 -0
- data/lib/god/conditions/complex.rb +86 -0
- data/lib/god/conditions/cpu_usage.rb +80 -0
- data/lib/god/conditions/degrading_lambda.rb +52 -0
- data/lib/god/conditions/disk_usage.rb +32 -0
- data/lib/god/conditions/file_mtime.rb +28 -0
- data/lib/god/conditions/file_touched.rb +44 -0
- data/lib/god/conditions/flapping.rb +128 -0
- data/lib/god/conditions/http_response_code.rb +184 -0
- data/lib/god/conditions/lambda.rb +25 -0
- data/lib/god/conditions/memory_usage.rb +82 -0
- data/lib/god/conditions/process_exits.rb +66 -0
- data/lib/god/conditions/process_running.rb +63 -0
- data/lib/god/conditions/socket_responding.rb +142 -0
- data/lib/god/conditions/tries.rb +44 -0
- data/lib/god/configurable.rb +57 -0
- data/lib/god/contact.rb +114 -0
- data/lib/god/contacts/airbrake.rb +44 -0
- data/lib/god/contacts/campfire.rb +121 -0
- data/lib/god/contacts/email.rb +130 -0
- data/lib/god/contacts/hipchat.rb +117 -0
- data/lib/god/contacts/jabber.rb +75 -0
- data/lib/god/contacts/prowl.rb +57 -0
- data/lib/god/contacts/scout.rb +55 -0
- data/lib/god/contacts/sensu.rb +59 -0
- data/lib/god/contacts/slack.rb +98 -0
- data/lib/god/contacts/statsd.rb +46 -0
- data/lib/god/contacts/twitter.rb +51 -0
- data/lib/god/contacts/webhook.rb +74 -0
- data/lib/god/driver.rb +238 -0
- data/lib/god/errors.rb +24 -0
- data/lib/god/event_handler.rb +112 -0
- data/lib/god/event_handlers/dummy_handler.rb +13 -0
- data/lib/god/event_handlers/kqueue_handler.rb +17 -0
- data/lib/god/event_handlers/netlink_handler.rb +13 -0
- data/lib/god/logger.rb +109 -0
- data/lib/god/metric.rb +87 -0
- data/lib/god/process.rb +381 -0
- data/lib/god/registry.rb +32 -0
- data/lib/god/simple_logger.rb +59 -0
- data/lib/god/socket.rb +113 -0
- data/lib/god/sugar.rb +62 -0
- data/lib/god/sys_logger.rb +45 -0
- data/lib/god/system/portable_poller.rb +42 -0
- data/lib/god/system/process.rb +50 -0
- data/lib/god/system/slash_proc_poller.rb +92 -0
- data/lib/god/task.rb +552 -0
- data/lib/god/timeline.rb +25 -0
- data/lib/god/trigger.rb +43 -0
- data/lib/god/watch.rb +340 -0
- data/mcproc.gemspec +192 -0
- data/test/configs/child_events/child_events.god +44 -0
- data/test/configs/child_events/simple_server.rb +3 -0
- data/test/configs/child_polls/child_polls.god +37 -0
- data/test/configs/child_polls/simple_server.rb +12 -0
- data/test/configs/complex/complex.god +59 -0
- data/test/configs/complex/simple_server.rb +3 -0
- data/test/configs/contact/contact.god +118 -0
- data/test/configs/contact/simple_server.rb +3 -0
- data/test/configs/daemon_events/daemon_events.god +37 -0
- data/test/configs/daemon_events/simple_server.rb +8 -0
- data/test/configs/daemon_events/simple_server_stop.rb +11 -0
- data/test/configs/daemon_polls/daemon_polls.god +17 -0
- data/test/configs/daemon_polls/simple_server.rb +6 -0
- data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
- data/test/configs/degrading_lambda/tcp_server.rb +15 -0
- data/test/configs/keepalive/keepalive.god +9 -0
- data/test/configs/keepalive/keepalive.rb +12 -0
- data/test/configs/lifecycle/lifecycle.god +25 -0
- data/test/configs/matias/matias.god +50 -0
- data/test/configs/real.rb +59 -0
- data/test/configs/running_load/running_load.god +16 -0
- data/test/configs/stop_options/simple_server.rb +12 -0
- data/test/configs/stop_options/stop_options.god +39 -0
- data/test/configs/stress/simple_server.rb +3 -0
- data/test/configs/stress/stress.god +15 -0
- data/test/configs/task/logs/.placeholder +0 -0
- data/test/configs/task/task.god +26 -0
- data/test/configs/test.rb +61 -0
- data/test/configs/usr1_trapper.rb +10 -0
- data/test/helper.rb +172 -0
- data/test/suite.rb +6 -0
- data/test/test_airbrake.rb +14 -0
- data/test/test_behavior.rb +18 -0
- data/test/test_campfire.rb +22 -0
- data/test/test_condition.rb +52 -0
- data/test/test_conditions_disk_usage.rb +50 -0
- data/test/test_conditions_http_response_code.rb +109 -0
- data/test/test_conditions_process_running.rb +40 -0
- data/test/test_conditions_socket_responding.rb +176 -0
- data/test/test_conditions_tries.rb +67 -0
- data/test/test_contact.rb +109 -0
- data/test/test_driver.rb +26 -0
- data/test/test_email.rb +34 -0
- data/test/test_event_handler.rb +82 -0
- data/test/test_god.rb +710 -0
- data/test/test_god_system.rb +201 -0
- data/test/test_handlers_kqueue_handler.rb +16 -0
- data/test/test_hipchat.rb +23 -0
- data/test/test_jabber.rb +29 -0
- data/test/test_logger.rb +55 -0
- data/test/test_metric.rb +74 -0
- data/test/test_process.rb +263 -0
- data/test/test_prowl.rb +15 -0
- data/test/test_registry.rb +15 -0
- data/test/test_sensu.rb +11 -0
- data/test/test_slack.rb +57 -0
- data/test/test_socket.rb +34 -0
- data/test/test_statsd.rb +22 -0
- data/test/test_sugar.rb +42 -0
- data/test/test_system_portable_poller.rb +17 -0
- data/test/test_system_process.rb +30 -0
- data/test/test_task.rb +246 -0
- data/test/test_timeline.rb +37 -0
- data/test/test_trigger.rb +63 -0
- data/test/test_watch.rb +286 -0
- data/test/test_webhook.rb +22 -0
- metadata +475 -0
data/lib/god/task.rb
ADDED
@@ -0,0 +1,552 @@
|
|
1
|
+
module God
|
2
|
+
|
3
|
+
class Task
|
4
|
+
# Public: Gets/Sets the String name of the task.
|
5
|
+
attr_accessor :name
|
6
|
+
|
7
|
+
# Public: Gets/Sets the Numeric default interval to be used between poll
|
8
|
+
# events.
|
9
|
+
attr_accessor :interval
|
10
|
+
|
11
|
+
# Public: Gets/Sets the String group name of the task.
|
12
|
+
attr_accessor :group
|
13
|
+
|
14
|
+
# Public: Gets/Sets the Array of Symbol valid states for the state machine.
|
15
|
+
attr_accessor :valid_states
|
16
|
+
|
17
|
+
# Public: Gets/Sets the Symbol initial state of the state machine.
|
18
|
+
attr_accessor :initial_state
|
19
|
+
|
20
|
+
# Gets/Sets the Driver for this task.
|
21
|
+
attr_accessor :driver
|
22
|
+
|
23
|
+
# Public: Sets whether the task should autostart when god starts. Defaults
|
24
|
+
# to true (enabled).
|
25
|
+
attr_writer :autostart
|
26
|
+
|
27
|
+
# Returns true if autostart is enabled, false if not.
|
28
|
+
def autostart?
|
29
|
+
@autostart
|
30
|
+
end
|
31
|
+
|
32
|
+
# api
|
33
|
+
attr_accessor :state, :behaviors, :metrics, :directory
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@autostart ||= true
|
37
|
+
|
38
|
+
# initial state is unmonitored
|
39
|
+
self.state = :unmonitored
|
40
|
+
|
41
|
+
# the list of behaviors
|
42
|
+
self.behaviors = []
|
43
|
+
|
44
|
+
# the list of conditions for each action
|
45
|
+
self.metrics = {nil => [], :unmonitored => [], :stop => []}
|
46
|
+
|
47
|
+
# the condition -> metric lookup
|
48
|
+
self.directory = {}
|
49
|
+
|
50
|
+
# driver
|
51
|
+
self.driver = Driver.new(self)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Initialize the metrics to an empty state.
|
55
|
+
#
|
56
|
+
# Returns nothing.
|
57
|
+
def prepare
|
58
|
+
self.valid_states.each do |state|
|
59
|
+
self.metrics[state] ||= []
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Verify that the minimum set of configuration requirements has been met.
|
64
|
+
#
|
65
|
+
# Returns true if valid, false if not.
|
66
|
+
def valid?
|
67
|
+
valid = true
|
68
|
+
|
69
|
+
# A name must be specified.
|
70
|
+
if self.name.nil?
|
71
|
+
valid = false
|
72
|
+
applog(self, :error, "No name String was specified.")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Valid states must be specified.
|
76
|
+
if self.valid_states.nil?
|
77
|
+
valid = false
|
78
|
+
applog(self, :error, "No valid_states Array or Symbols was specified.")
|
79
|
+
end
|
80
|
+
|
81
|
+
# An initial state must be specified.
|
82
|
+
if self.initial_state.nil?
|
83
|
+
valid = false
|
84
|
+
applog(self, :error, "No initial_state Symbol was specified.")
|
85
|
+
end
|
86
|
+
|
87
|
+
valid
|
88
|
+
end
|
89
|
+
|
90
|
+
###########################################################################
|
91
|
+
#
|
92
|
+
# Advanced mode
|
93
|
+
#
|
94
|
+
###########################################################################
|
95
|
+
|
96
|
+
# Convert the given input into canonical hash form which looks like:
|
97
|
+
#
|
98
|
+
# { true => :state } or { true => :state, false => :otherstate }
|
99
|
+
#
|
100
|
+
# to - The Symbol or Hash destination.
|
101
|
+
#
|
102
|
+
# Returns the canonical Hash.
|
103
|
+
def canonical_hash_form(to)
|
104
|
+
to.instance_of?(Symbol) ? {true => to} : to
|
105
|
+
end
|
106
|
+
|
107
|
+
# Public: Define a transition handler which consists of a set of conditions
|
108
|
+
#
|
109
|
+
# start_states - The Symbol or Array of Symbols start state(s).
|
110
|
+
# end_states - The Symbol or Hash end states.
|
111
|
+
#
|
112
|
+
# Yields the Metric for this transition.
|
113
|
+
#
|
114
|
+
# Returns nothing.
|
115
|
+
def transition(start_states, end_states)
|
116
|
+
# Convert end_states into canonical hash form.
|
117
|
+
canonical_end_states = canonical_hash_form(end_states)
|
118
|
+
|
119
|
+
Array(start_states).each do |start_state|
|
120
|
+
# Validate start state.
|
121
|
+
unless self.valid_states.include?(start_state)
|
122
|
+
abort "Invalid state :#{start_state}. Must be one of the symbols #{self.valid_states.map{|x| ":#{x}"}.join(', ')}"
|
123
|
+
end
|
124
|
+
|
125
|
+
# Create a new metric to hold the task, end states, and conditions.
|
126
|
+
m = Metric.new(self, canonical_end_states)
|
127
|
+
|
128
|
+
if block_given?
|
129
|
+
# Let the config file define some conditions on the metric.
|
130
|
+
yield(m)
|
131
|
+
else
|
132
|
+
# Add an :always condition if no block was given.
|
133
|
+
m.condition(:always) do |c|
|
134
|
+
c.what = true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Populate the condition -> metric directory.
|
139
|
+
m.conditions.each do |c|
|
140
|
+
self.directory[c] = m
|
141
|
+
end
|
142
|
+
|
143
|
+
# Record the metric.
|
144
|
+
self.metrics[start_state] ||= []
|
145
|
+
self.metrics[start_state] << m
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Public: Define a lifecycle handler. Conditions that belong to a
|
150
|
+
# lifecycle are active as long as the process is being monitored.
|
151
|
+
#
|
152
|
+
# Returns nothing.
|
153
|
+
def lifecycle
|
154
|
+
# Create a new metric to hold the task and conditions.
|
155
|
+
m = Metric.new(self)
|
156
|
+
|
157
|
+
# Let the config file define some conditions on the metric.
|
158
|
+
yield(m)
|
159
|
+
|
160
|
+
# Populate the condition -> metric directory.
|
161
|
+
m.conditions.each do |c|
|
162
|
+
self.directory[c] = m
|
163
|
+
end
|
164
|
+
|
165
|
+
# Record the metric.
|
166
|
+
self.metrics[nil] << m
|
167
|
+
end
|
168
|
+
|
169
|
+
###########################################################################
|
170
|
+
#
|
171
|
+
# Lifecycle
|
172
|
+
#
|
173
|
+
###########################################################################
|
174
|
+
|
175
|
+
# Enable monitoring.
|
176
|
+
#
|
177
|
+
# Returns nothing.
|
178
|
+
def monitor
|
179
|
+
self.move(self.initial_state)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Disable monitoring.
|
183
|
+
#
|
184
|
+
# Returns nothing.
|
185
|
+
def unmonitor
|
186
|
+
self.move(:unmonitored)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Move to the given state.
|
190
|
+
#
|
191
|
+
# to_state - The Symbol representing the state to move to.
|
192
|
+
#
|
193
|
+
# Returns this Task.
|
194
|
+
def move(to_state)
|
195
|
+
if !self.driver.in_driver_context?
|
196
|
+
# Called from outside Driver. Send an async message to Driver.
|
197
|
+
self.driver.message(:move, [to_state])
|
198
|
+
else
|
199
|
+
# Called from within Driver. Record original info.
|
200
|
+
orig_to_state = to_state
|
201
|
+
from_state = self.state
|
202
|
+
|
203
|
+
# Log.
|
204
|
+
msg = "#{self.name} move '#{from_state}' to '#{to_state}'"
|
205
|
+
applog(self, :info, msg)
|
206
|
+
|
207
|
+
# Cleanup from current state.
|
208
|
+
self.driver.clear_events
|
209
|
+
self.metrics[from_state].each { |m| m.disable }
|
210
|
+
if to_state == :unmonitored
|
211
|
+
self.metrics[nil].each { |m| m.disable }
|
212
|
+
end
|
213
|
+
|
214
|
+
# Perform action.
|
215
|
+
self.action(to_state)
|
216
|
+
|
217
|
+
# Enable simple mode.
|
218
|
+
if [:start, :restart].include?(to_state) && self.metrics[to_state].empty?
|
219
|
+
to_state = :up
|
220
|
+
end
|
221
|
+
|
222
|
+
# Move to new state.
|
223
|
+
self.metrics[to_state].each { |m| m.enable }
|
224
|
+
|
225
|
+
# If no from state, enable lifecycle metric.
|
226
|
+
if from_state == :unmonitored
|
227
|
+
self.metrics[nil].each { |m| m.enable }
|
228
|
+
end
|
229
|
+
|
230
|
+
# Set state.
|
231
|
+
self.state = to_state
|
232
|
+
|
233
|
+
# Broadcast to interested TriggerConditions.
|
234
|
+
Trigger.broadcast(self, :state_change, [from_state, orig_to_state])
|
235
|
+
|
236
|
+
# Log.
|
237
|
+
msg = "#{self.name} moved '#{from_state}' to '#{to_state}'"
|
238
|
+
applog(self, :info, msg)
|
239
|
+
end
|
240
|
+
|
241
|
+
self
|
242
|
+
end
|
243
|
+
|
244
|
+
# Notify the Driver that an EventCondition has triggered.
|
245
|
+
#
|
246
|
+
# condition - The Condition.
|
247
|
+
#
|
248
|
+
# Returns nothing.
|
249
|
+
def trigger(condition)
|
250
|
+
self.driver.message(:handle_event, [condition])
|
251
|
+
end
|
252
|
+
|
253
|
+
def signal(sig)
|
254
|
+
# noop
|
255
|
+
end
|
256
|
+
|
257
|
+
###########################################################################
|
258
|
+
#
|
259
|
+
# Actions
|
260
|
+
#
|
261
|
+
###########################################################################
|
262
|
+
|
263
|
+
def method_missing(sym, *args)
|
264
|
+
unless (sym.to_s =~ /=$/)
|
265
|
+
super
|
266
|
+
end
|
267
|
+
|
268
|
+
base = sym.to_s.chop.intern
|
269
|
+
|
270
|
+
unless self.valid_states.include?(base)
|
271
|
+
super
|
272
|
+
end
|
273
|
+
|
274
|
+
self.class.send(:attr_accessor, base)
|
275
|
+
self.send(sym, *args)
|
276
|
+
end
|
277
|
+
|
278
|
+
# Perform the given action.
|
279
|
+
#
|
280
|
+
# a - The Symbol action.
|
281
|
+
# c - The Condition.
|
282
|
+
#
|
283
|
+
# Returns this Task.
|
284
|
+
def action(a, c = nil)
|
285
|
+
if !self.driver.in_driver_context?
|
286
|
+
# Called from outside Driver. Send an async message to Driver.
|
287
|
+
self.driver.message(:action, [a, c])
|
288
|
+
else
|
289
|
+
# Called from within Driver.
|
290
|
+
if self.respond_to?(a)
|
291
|
+
command = self.send(a)
|
292
|
+
|
293
|
+
case command
|
294
|
+
when String
|
295
|
+
msg = "#{self.name} #{a}: #{command}"
|
296
|
+
applog(self, :info, msg)
|
297
|
+
|
298
|
+
system(command)
|
299
|
+
when Proc
|
300
|
+
msg = "#{self.name} #{a}: lambda"
|
301
|
+
applog(self, :info, msg)
|
302
|
+
|
303
|
+
command.call
|
304
|
+
else
|
305
|
+
raise NotImplementedError
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
###########################################################################
|
312
|
+
#
|
313
|
+
# Events
|
314
|
+
#
|
315
|
+
###########################################################################
|
316
|
+
|
317
|
+
def attach(condition)
|
318
|
+
case condition
|
319
|
+
when PollCondition
|
320
|
+
self.driver.schedule(condition, 0)
|
321
|
+
when EventCondition, TriggerCondition
|
322
|
+
condition.register
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def detach(condition)
|
327
|
+
case condition
|
328
|
+
when PollCondition
|
329
|
+
condition.reset
|
330
|
+
when EventCondition, TriggerCondition
|
331
|
+
condition.deregister
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
###########################################################################
|
336
|
+
#
|
337
|
+
# Registration
|
338
|
+
#
|
339
|
+
###########################################################################
|
340
|
+
|
341
|
+
def register!
|
342
|
+
# override if necessary
|
343
|
+
end
|
344
|
+
|
345
|
+
def unregister!
|
346
|
+
driver.shutdown
|
347
|
+
end
|
348
|
+
|
349
|
+
###########################################################################
|
350
|
+
#
|
351
|
+
# Handlers
|
352
|
+
#
|
353
|
+
###########################################################################
|
354
|
+
|
355
|
+
# Evaluate and handle the given poll condition. Handles logging
|
356
|
+
# notifications, and moving to the new state if necessary.
|
357
|
+
#
|
358
|
+
# condition - The Condition to handle.
|
359
|
+
#
|
360
|
+
# Returns nothing.
|
361
|
+
def handle_poll(condition)
|
362
|
+
# Lookup metric.
|
363
|
+
metric = self.directory[condition]
|
364
|
+
|
365
|
+
# Run the test.
|
366
|
+
begin
|
367
|
+
result = condition.test
|
368
|
+
rescue Object => e
|
369
|
+
cname = condition.class.to_s.split('::').last
|
370
|
+
message = format("Unhandled exception in %s condition - (%s): %s\n%s",
|
371
|
+
cname, e.class, e.message, e.backtrace.join("\n"))
|
372
|
+
applog(self, :error, message)
|
373
|
+
result = false
|
374
|
+
end
|
375
|
+
|
376
|
+
# Log.
|
377
|
+
messages = self.log_line(self, metric, condition, result)
|
378
|
+
|
379
|
+
# Notify.
|
380
|
+
if result && condition.notify
|
381
|
+
self.notify(condition, messages.last)
|
382
|
+
end
|
383
|
+
|
384
|
+
# After-condition.
|
385
|
+
condition.after
|
386
|
+
|
387
|
+
# Get the destination.
|
388
|
+
dest =
|
389
|
+
if result && condition.transition
|
390
|
+
# Condition override.
|
391
|
+
condition.transition
|
392
|
+
else
|
393
|
+
# Regular.
|
394
|
+
metric.destination && metric.destination[result]
|
395
|
+
end
|
396
|
+
|
397
|
+
# Transition or reschedule.
|
398
|
+
if dest
|
399
|
+
# Transition.
|
400
|
+
begin
|
401
|
+
self.move(dest)
|
402
|
+
rescue EventRegistrationFailedError
|
403
|
+
msg = self.name + ' Event registration failed, moving back to previous state'
|
404
|
+
applog(self, :info, msg)
|
405
|
+
|
406
|
+
dest = self.state
|
407
|
+
retry
|
408
|
+
end
|
409
|
+
else
|
410
|
+
# Reschedule.
|
411
|
+
self.driver.schedule(condition)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
# Asynchronously evaluate and handle the given event condition. Handles
|
416
|
+
# logging notifications, and moving to the new state if necessary.
|
417
|
+
#
|
418
|
+
# condition - The Condition to handle.
|
419
|
+
#
|
420
|
+
# Returns nothing.
|
421
|
+
def handle_event(condition)
|
422
|
+
# Lookup metric.
|
423
|
+
metric = self.directory[condition]
|
424
|
+
|
425
|
+
# Log.
|
426
|
+
messages = self.log_line(self, metric, condition, true)
|
427
|
+
|
428
|
+
# Notify.
|
429
|
+
if condition.notify
|
430
|
+
self.notify(condition, messages.last)
|
431
|
+
end
|
432
|
+
|
433
|
+
# Get the destination.
|
434
|
+
dest =
|
435
|
+
if condition.transition
|
436
|
+
# Condition override.
|
437
|
+
condition.transition
|
438
|
+
else
|
439
|
+
# Regular.
|
440
|
+
metric.destination && metric.destination[true]
|
441
|
+
end
|
442
|
+
|
443
|
+
if dest
|
444
|
+
self.move(dest)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
# Determine whether a trigger happened.
|
449
|
+
#
|
450
|
+
# metric - The Metric.
|
451
|
+
# result - The Boolean result from the condition's test.
|
452
|
+
#
|
453
|
+
# Returns Boolean
|
454
|
+
def trigger?(metric, result)
|
455
|
+
metric.destination && metric.destination[result]
|
456
|
+
end
|
457
|
+
|
458
|
+
# Log info about the condition and return the list of messages logged.
|
459
|
+
#
|
460
|
+
# watch - The Watch.
|
461
|
+
# metric - The Metric.
|
462
|
+
# condition - The Condition.
|
463
|
+
# result - The Boolean result of the condition test evaluation.
|
464
|
+
#
|
465
|
+
# Returns the Array of String messages.
|
466
|
+
def log_line(watch, metric, condition, result)
|
467
|
+
status =
|
468
|
+
if self.trigger?(metric, result)
|
469
|
+
"[trigger]"
|
470
|
+
else
|
471
|
+
"[ok]"
|
472
|
+
end
|
473
|
+
|
474
|
+
messages = []
|
475
|
+
|
476
|
+
# Log info if available.
|
477
|
+
if condition.info
|
478
|
+
Array(condition.info).each do |condition_info|
|
479
|
+
messages << "#{watch.name} #{status} #{condition_info} (#{condition.base_name})"
|
480
|
+
applog(watch, :info, messages.last)
|
481
|
+
end
|
482
|
+
else
|
483
|
+
messages << "#{watch.name} #{status} (#{condition.base_name})"
|
484
|
+
applog(watch, :info, messages.last)
|
485
|
+
end
|
486
|
+
|
487
|
+
# Log.
|
488
|
+
debug_message = watch.name + ' ' + condition.base_name + " [#{result}] " + self.dest_desc(metric, condition)
|
489
|
+
applog(watch, :debug, debug_message)
|
490
|
+
|
491
|
+
messages
|
492
|
+
end
|
493
|
+
|
494
|
+
# Format the destination specification for use in debug logging.
|
495
|
+
#
|
496
|
+
# metric - The Metric.
|
497
|
+
# condition - The Condition.
|
498
|
+
#
|
499
|
+
# Returns the formatted String.
|
500
|
+
def dest_desc(metric, condition)
|
501
|
+
if condition.transition
|
502
|
+
{true => condition.transition}.inspect
|
503
|
+
else
|
504
|
+
if metric.destination
|
505
|
+
metric.destination.inspect
|
506
|
+
else
|
507
|
+
'none'
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
# Notify all recipients of the given condition with the specified message.
|
513
|
+
#
|
514
|
+
# condition - The Condition.
|
515
|
+
# message - The String message to send.
|
516
|
+
#
|
517
|
+
# Returns nothing.
|
518
|
+
def notify(condition, message)
|
519
|
+
spec = Contact.normalize(condition.notify)
|
520
|
+
unmatched = []
|
521
|
+
|
522
|
+
# Resolve contacts.
|
523
|
+
resolved_contacts =
|
524
|
+
spec[:contacts].inject([]) do |acc, contact_name_or_group|
|
525
|
+
cons = Array(God.contacts[contact_name_or_group] || God.contact_groups[contact_name_or_group])
|
526
|
+
unmatched << contact_name_or_group if cons.empty?
|
527
|
+
acc += cons
|
528
|
+
acc
|
529
|
+
end
|
530
|
+
|
531
|
+
# Warn about unmatched contacts.
|
532
|
+
unless unmatched.empty?
|
533
|
+
msg = "#{condition.watch.name} no matching contacts for '#{unmatched.join(", ")}'"
|
534
|
+
applog(condition.watch, :warn, msg)
|
535
|
+
end
|
536
|
+
|
537
|
+
# Notify each contact.
|
538
|
+
resolved_contacts.each do |c|
|
539
|
+
host = `hostname`.chomp rescue 'none'
|
540
|
+
begin
|
541
|
+
c.notify(message, Time.now, spec[:priority], spec[:category], host)
|
542
|
+
msg = "#{condition.watch.name} #{c.info ? c.info : "notification sent for contact: #{c.name}"} (#{c.base_name})"
|
543
|
+
applog(condition.watch, :info, msg % [])
|
544
|
+
rescue Exception => e
|
545
|
+
applog(condition.watch, :error, "#{e.message} #{e.backtrace}")
|
546
|
+
msg = "#{condition.watch.name} Failed to deliver notification for contact: #{c.name} (#{c.base_name})"
|
547
|
+
applog(condition.watch, :error, msg % [])
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|