flapjack 0.7.18 → 0.7.19
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.
- data/CHANGELOG.md +7 -0
- data/bin/flapjack +3 -0
- data/bin/flapjack-nagios-receiver +4 -1
- data/bin/flapjack-netsaint-parser +2 -1
- data/bin/flapjack-populator +6 -3
- data/bin/receive-events +3 -1
- data/bin/simulate-failed-check +2 -1
- data/etc/flapjack_config.yaml.example +20 -0
- data/features/events.feature +1 -1
- data/features/events_check_names.feature +1 -1
- data/features/notification_rules.feature +1 -1
- data/features/notifications.feature +1 -1
- data/features/steps/events_steps.rb +18 -17
- data/features/steps/flapjack-netsaint-parser_steps.rb +1 -2
- data/features/steps/notifications_steps.rb +14 -1
- data/features/support/env.rb +27 -10
- data/flapjack.gemspec +1 -3
- data/lib/flapjack/coordinator.rb +30 -20
- data/lib/flapjack/data/contact.rb +3 -2
- data/lib/flapjack/data/entity.rb +3 -3
- data/lib/flapjack/data/entity_check.rb +116 -43
- data/lib/flapjack/data/event.rb +10 -10
- data/lib/flapjack/data/message.rb +3 -6
- data/lib/flapjack/data/notification.rb +122 -57
- data/lib/flapjack/data/notification_rule.rb +11 -11
- data/lib/flapjack/filters/acknowledgement.rb +2 -2
- data/lib/flapjack/filters/ok.rb +1 -1
- data/lib/flapjack/gateways/api/entity_check_presenter.rb +1 -0
- data/lib/flapjack/gateways/api/entity_methods.rb +4 -6
- data/lib/flapjack/gateways/api/rack/json_params_parser.rb +1 -1
- data/lib/flapjack/gateways/email.rb +3 -5
- data/lib/flapjack/gateways/email/{alert.html.haml → alert.html.erb} +0 -0
- data/lib/flapjack/gateways/jabber.rb +66 -35
- data/lib/flapjack/gateways/oobetet.rb +5 -7
- data/lib/flapjack/gateways/pagerduty.rb +7 -7
- data/lib/flapjack/gateways/web.rb +101 -41
- data/lib/flapjack/gateways/web/public/css/flapjack.css +1 -1
- data/lib/flapjack/gateways/web/views/{_css.haml → _css.html.erb} +2 -1
- data/lib/flapjack/gateways/web/views/_foot.html.erb +3 -0
- data/lib/flapjack/gateways/web/views/_head.html.erb +4 -0
- data/lib/flapjack/gateways/web/views/_nav.html.erb +9 -0
- data/lib/flapjack/gateways/web/views/check.html.erb +204 -0
- data/lib/flapjack/gateways/web/views/checks.html.erb +77 -0
- data/lib/flapjack/gateways/web/views/contact.html.erb +114 -0
- data/lib/flapjack/gateways/web/views/contacts.html.erb +42 -0
- data/lib/flapjack/gateways/web/views/entities.html.erb +39 -0
- data/lib/flapjack/gateways/web/views/entity.html.erb +67 -0
- data/lib/flapjack/gateways/web/views/index.html.erb +27 -0
- data/lib/flapjack/gateways/web/views/self_stats.html.erb +97 -0
- data/lib/flapjack/logger.rb +71 -23
- data/lib/flapjack/notifier.rb +157 -0
- data/lib/flapjack/patches.rb +1 -41
- data/lib/flapjack/pikelet.rb +4 -2
- data/lib/flapjack/{executive.rb → processor.rb} +32 -145
- data/lib/flapjack/version.rb +1 -1
- data/spec/lib/flapjack/coordinator_spec.rb +134 -71
- data/spec/lib/flapjack/data/contact_spec.rb +1 -0
- data/spec/lib/flapjack/data/entity_check_spec.rb +146 -30
- data/spec/lib/flapjack/data/entity_spec.rb +4 -4
- data/spec/lib/flapjack/data/event_spec.rb +4 -4
- data/spec/lib/flapjack/data/message_spec.rb +2 -3
- data/spec/lib/flapjack/data/notification_spec.rb +13 -19
- data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +2 -2
- data/spec/lib/flapjack/gateways/jabber_spec.rb +34 -0
- data/spec/lib/flapjack/gateways/pagerduty_spec.rb +0 -2
- data/spec/lib/flapjack/gateways/web/views/{check.haml_spec.rb → check.html.erb_spec.rb} +2 -2
- data/spec/lib/flapjack/gateways/web/views/{contact.haml_spec.rb → contact.html.erb_spec.rb} +3 -3
- data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +14 -0
- data/spec/lib/flapjack/gateways/web_spec.rb +20 -8
- data/spec/lib/flapjack/logger_spec.rb +30 -28
- data/spec/lib/flapjack/notifier_spec.rb +6 -0
- data/spec/lib/flapjack/pikelet_spec.rb +8 -8
- data/spec/lib/flapjack/{executive_spec.rb → processor_spec.rb} +4 -4
- data/spec/spec_helper.rb +1 -13
- data/spec/support/erb_view_helper.rb +23 -0
- data/tasks/profile.rake +1 -1
- data/tmp/acknowledge.rb +3 -1
- data/tmp/create_event_ok.rb +3 -1
- data/tmp/create_event_unknown.rb +3 -1
- data/tmp/create_events_failure.rb +3 -1
- data/tmp/create_events_ok.rb +3 -1
- data/tmp/create_events_ok_fail_ack_ok.rb +3 -1
- data/tmp/create_events_ok_failure.rb +3 -1
- data/tmp/create_events_ok_failure_ack.rb +3 -1
- data/tmp/test_json_post.rb +5 -3
- data/tmp/test_notification_rules_api.rb +5 -3
- metadata +32 -61
- data/lib/flapjack/gateways/web/views/_foot.haml +0 -8
- data/lib/flapjack/gateways/web/views/_head.haml +0 -10
- data/lib/flapjack/gateways/web/views/_nav.haml +0 -14
- data/lib/flapjack/gateways/web/views/check.haml +0 -191
- data/lib/flapjack/gateways/web/views/checks.haml +0 -49
- data/lib/flapjack/gateways/web/views/contact.haml +0 -85
- data/lib/flapjack/gateways/web/views/contacts.haml +0 -30
- data/lib/flapjack/gateways/web/views/entities.haml +0 -28
- data/lib/flapjack/gateways/web/views/entity.haml +0 -50
- data/lib/flapjack/gateways/web/views/index.haml +0 -32
- data/lib/flapjack/gateways/web/views/self_stats.haml +0 -70
- data/spec/lib/flapjack/gateways/web/views/index.haml_spec.rb +0 -13
- data/spec/support/haml_view_helper.rb +0 -15
|
@@ -235,10 +235,11 @@ module Flapjack
|
|
|
235
235
|
end
|
|
236
236
|
|
|
237
237
|
# drop notifications for
|
|
238
|
-
def drop_notifications?(opts)
|
|
238
|
+
def drop_notifications?(opts = {})
|
|
239
239
|
media = opts[:media]
|
|
240
240
|
check = opts[:check]
|
|
241
241
|
state = opts[:state]
|
|
242
|
+
|
|
242
243
|
# build it and they will come
|
|
243
244
|
@redis.exists("drop_alerts_for_contact:#{self.id}") ||
|
|
244
245
|
(media && @redis.exists("drop_alerts_for_contact:#{self.id}:#{media}")) ||
|
|
@@ -248,7 +249,7 @@ module Flapjack
|
|
|
248
249
|
@redis.exists("drop_alerts_for_contact:#{self.id}:#{media}:#{check}:#{state}"))
|
|
249
250
|
end
|
|
250
251
|
|
|
251
|
-
def update_sent_alert_keys(opts)
|
|
252
|
+
def update_sent_alert_keys(opts = {})
|
|
252
253
|
media = opts[:media]
|
|
253
254
|
check = opts[:check]
|
|
254
255
|
state = opts[:state]
|
data/lib/flapjack/data/entity.rb
CHANGED
|
@@ -97,12 +97,12 @@ module Flapjack
|
|
|
97
97
|
|
|
98
98
|
def self.find_all_with_checks(options)
|
|
99
99
|
raise "Redis connection not set" unless redis = options[:redis]
|
|
100
|
-
redis.
|
|
100
|
+
redis.zrange("current_entities", 0, -1)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def self.find_all_with_failing_checks(options)
|
|
104
104
|
raise "Redis connection not set" unless redis = options[:redis]
|
|
105
|
-
|
|
105
|
+
Flapjack::Data::EntityCheck.find_all_failing_by_entity(:redis => redis).keys
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def contacts
|
|
@@ -119,7 +119,7 @@ module Flapjack
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def check_list
|
|
122
|
-
@redis.
|
|
122
|
+
@redis.zrange("current_checks:#{@name}", 0, -1)
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def check_count
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'oj'
|
|
4
4
|
|
|
5
5
|
require 'flapjack/patches'
|
|
6
6
|
|
|
@@ -53,6 +53,67 @@ module Flapjack
|
|
|
53
53
|
self.new(entity, check, :redis => redis)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
def self.find_all_for_entity_name(entity_name, options = {})
|
|
57
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
58
|
+
redis.zrange("current_checks:#{entity_name}", 0, -1)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.find_all(options = {})
|
|
62
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
63
|
+
self.conflate_to_keys(self.find_all_by_entity(:redis => redis))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.find_all_by_entity(options = {})
|
|
67
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
68
|
+
d = {}
|
|
69
|
+
redis.zrange("current_entities", 0, -1).each {|entity|
|
|
70
|
+
d[entity] = redis.zrange("current_checks:#{entity}", 0, -1)
|
|
71
|
+
}
|
|
72
|
+
d
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.count_all(options = {})
|
|
76
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
77
|
+
redis.zrange("current_entities", 0, -1).inject(0) {|memo, entity|
|
|
78
|
+
memo + redis.zcount("current_checks:#{entity}", '-inf', '+inf')
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.find_all_failing(options = {})
|
|
83
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
84
|
+
self.conflate_to_keys(self.find_all_failing_by_entity(:redis => redis))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def self.find_all_failing_by_entity(options = {})
|
|
88
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
89
|
+
redis.zrange("failed_checks", 0, -1).inject({}) do |memo, key|
|
|
90
|
+
entity, check = key.split(':', 2)
|
|
91
|
+
if !!redis.zscore("current_checks:#{entity}", check)
|
|
92
|
+
memo[entity] ||= []
|
|
93
|
+
memo[entity] << check
|
|
94
|
+
end
|
|
95
|
+
memo
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.count_all_failing(options = {})
|
|
100
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
101
|
+
redis.zrange("failed_checks", 0, -1).count do |key|
|
|
102
|
+
entity, check = key.split(':', 2)
|
|
103
|
+
!!redis.zscore("current_checks:#{entity}", check)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def self.conflate_to_keys(entity_checks_hash)
|
|
108
|
+
result = []
|
|
109
|
+
entity_checks_hash.each {|entity, checks|
|
|
110
|
+
checks.each {|check|
|
|
111
|
+
result << "#{entity}:#{check}"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
result
|
|
115
|
+
end
|
|
116
|
+
|
|
56
117
|
def entity_name
|
|
57
118
|
entity.name
|
|
58
119
|
end
|
|
@@ -69,7 +130,7 @@ module Flapjack
|
|
|
69
130
|
end
|
|
70
131
|
|
|
71
132
|
# return data about current maintenance (scheduled or unscheduled, as specified)
|
|
72
|
-
def current_maintenance(opts)
|
|
133
|
+
def current_maintenance(opts = {})
|
|
73
134
|
sched = opts[:scheduled] ? 'scheduled' : 'unscheduled'
|
|
74
135
|
ts = @redis.get("#{@key}:#{sched}_maintenance")
|
|
75
136
|
return unless ts
|
|
@@ -79,14 +140,14 @@ module Flapjack
|
|
|
79
140
|
}
|
|
80
141
|
end
|
|
81
142
|
|
|
82
|
-
def create_unscheduled_maintenance(opts = {})
|
|
83
|
-
|
|
143
|
+
def create_unscheduled_maintenance(start_time, duration, opts = {})
|
|
144
|
+
raise ArgumentError, 'start time must be provided as a Unix timestamp' unless start_time && start_time.is_a?(Integer)
|
|
145
|
+
raise ArgumentError, 'duration in seconds must be provided' unless duration && duration.is_a?(Integer) && (duration > 0)
|
|
84
146
|
|
|
85
|
-
start_time = opts[:start_time] # unix timestamp
|
|
86
|
-
duration = opts[:duration] # seconds
|
|
87
147
|
summary = opts[:summary]
|
|
88
148
|
time_remaining = (start_time + duration) - Time.now.to_i
|
|
89
149
|
if time_remaining > 0
|
|
150
|
+
end_unscheduled_maintenance(start_time) if in_unscheduled_maintenance?
|
|
90
151
|
@redis.setex("#{@key}:unscheduled_maintenance", time_remaining, start_time)
|
|
91
152
|
end
|
|
92
153
|
@redis.zadd("#{@key}:unscheduled_maintenances", duration, start_time)
|
|
@@ -96,19 +157,14 @@ module Flapjack
|
|
|
96
157
|
end
|
|
97
158
|
|
|
98
159
|
# ends any unscheduled maintenance
|
|
99
|
-
def end_unscheduled_maintenance(
|
|
100
|
-
|
|
101
|
-
:end_time => Time.now.to_i
|
|
102
|
-
}
|
|
103
|
-
options = defaults.merge(opts)
|
|
104
|
-
end_time = options[:end_time]
|
|
160
|
+
def end_unscheduled_maintenance(end_time)
|
|
161
|
+
raise ArgumentError, 'end time must be provided as a Unix timestamp' unless end_time && end_time.is_a?(Integer)
|
|
105
162
|
|
|
106
163
|
if (um_start = @redis.get("#{@key}:unscheduled_maintenance"))
|
|
107
164
|
duration = end_time - um_start.to_i
|
|
108
165
|
@logger.debug("ending unscheduled downtime for #{@key} at #{Time.at(end_time).to_s}") if @logger
|
|
109
166
|
@redis.del("#{@key}:unscheduled_maintenance")
|
|
110
|
-
@redis.zadd("#{@key}:unscheduled_maintenances", duration, um_start)
|
|
111
|
-
@redis.zadd("#{@key}:sorted_unscheduled_maintenance_timestamps", um_start, um_start)
|
|
167
|
+
@redis.zadd("#{@key}:unscheduled_maintenances", duration, um_start) # updates existing UM 'score'
|
|
112
168
|
else
|
|
113
169
|
@logger.debug("end_unscheduled_maintenance called for #{@key} but none found") if @logger
|
|
114
170
|
end
|
|
@@ -118,10 +174,11 @@ module Flapjack
|
|
|
118
174
|
# TODO: consider adding some validation to the data we're adding in here
|
|
119
175
|
# eg start_time is a believable unix timestamp (not in the past and not too
|
|
120
176
|
# far in the future), duration is within some bounds...
|
|
121
|
-
def create_scheduled_maintenance(opts = {})
|
|
122
|
-
|
|
123
|
-
duration
|
|
124
|
-
|
|
177
|
+
def create_scheduled_maintenance(start_time, duration, opts = {})
|
|
178
|
+
raise ArgumentError, 'start time must be provided as a Unix timestamp' unless start_time && start_time.is_a?(Integer)
|
|
179
|
+
raise ArgumentError, 'duration in seconds must be provided' unless duration && duration.is_a?(Integer) && (duration > 0)
|
|
180
|
+
|
|
181
|
+
summary = opts[:summary]
|
|
125
182
|
@redis.zadd("#{@key}:scheduled_maintenances", duration, start_time)
|
|
126
183
|
@redis.set("#{@key}:#{start_time}:scheduled_maintenance:summary", summary)
|
|
127
184
|
|
|
@@ -131,6 +188,30 @@ module Flapjack
|
|
|
131
188
|
update_current_scheduled_maintenance(:revalidate => true)
|
|
132
189
|
end
|
|
133
190
|
|
|
191
|
+
# if not in scheduled maintenance, looks in scheduled maintenance list for a check to see if
|
|
192
|
+
# current state should be set to scheduled maintenance, and sets it as appropriate
|
|
193
|
+
def update_current_scheduled_maintenance(opts = {})
|
|
194
|
+
if opts[:revalidate]
|
|
195
|
+
@redis.del("#{@key}:scheduled_maintenance")
|
|
196
|
+
else
|
|
197
|
+
return if in_scheduled_maintenance?
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# are we within a scheduled maintenance period?
|
|
201
|
+
current_time = Time.now.to_i
|
|
202
|
+
current_sched_ms = maintenances(nil, nil, :scheduled => true).select {|sm|
|
|
203
|
+
(sm[:start_time] <= current_time) && (current_time < sm[:end_time])
|
|
204
|
+
}
|
|
205
|
+
return if current_sched_ms.empty?
|
|
206
|
+
|
|
207
|
+
# yes! so set current scheduled maintenance
|
|
208
|
+
# if multiple scheduled maintenances found, find the end_time furthest in the future
|
|
209
|
+
most_futuristic = current_sched_ms.max {|sm| sm[:end_time] }
|
|
210
|
+
start_time = most_futuristic[:start_time]
|
|
211
|
+
duration = most_futuristic[:duration]
|
|
212
|
+
@redis.setex("#{@key}:scheduled_maintenance", duration.to_i, start_time)
|
|
213
|
+
end
|
|
214
|
+
|
|
134
215
|
# TODO allow summary to be changed as part of the termination
|
|
135
216
|
def end_scheduled_maintenance(start_time)
|
|
136
217
|
raise ArgumentError, 'start time must be supplied as a Unix timestamp' unless start_time && start_time.is_a?(Integer)
|
|
@@ -162,32 +243,8 @@ module Flapjack
|
|
|
162
243
|
|
|
163
244
|
return true
|
|
164
245
|
end
|
|
165
|
-
|
|
166
|
-
false
|
|
167
|
-
end
|
|
168
246
|
|
|
169
|
-
|
|
170
|
-
# current state should be set to scheduled maintenance, and sets it as appropriate
|
|
171
|
-
def update_current_scheduled_maintenance(opts = {})
|
|
172
|
-
if opts[:revalidate]
|
|
173
|
-
@redis.del("#{@key}:scheduled_maintenance")
|
|
174
|
-
else
|
|
175
|
-
return if in_scheduled_maintenance?
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# are we within a scheduled maintenance period?
|
|
179
|
-
current_time = Time.now.to_i
|
|
180
|
-
current_sched_ms = maintenances(nil, nil, :scheduled => true).select {|sm|
|
|
181
|
-
(sm[:start_time] <= current_time) && (current_time < sm[:end_time])
|
|
182
|
-
}
|
|
183
|
-
return if current_sched_ms.empty?
|
|
184
|
-
|
|
185
|
-
# yes! so set current scheduled maintenance
|
|
186
|
-
# if multiple scheduled maintenances found, find the end_time furthest in the future
|
|
187
|
-
most_futuristic = current_sched_ms.max {|sm| sm[:end_time] }
|
|
188
|
-
start_time = most_futuristic[:start_time]
|
|
189
|
-
duration = most_futuristic[:duration]
|
|
190
|
-
@redis.setex("#{@key}:scheduled_maintenance", duration.to_i, start_time)
|
|
247
|
+
false
|
|
191
248
|
end
|
|
192
249
|
|
|
193
250
|
# returns nil if no previous state; this must be considered as a possible
|
|
@@ -248,6 +305,22 @@ module Flapjack
|
|
|
248
305
|
|
|
249
306
|
def last_update=(timestamp)
|
|
250
307
|
@redis.hset("check:#{@key}", 'last_update', timestamp)
|
|
308
|
+
@redis.zadd("current_checks:#{entity.name}", timestamp, check)
|
|
309
|
+
@redis.zadd("current_entities", timestamp, entity.name)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# disables a check (removes currency)
|
|
313
|
+
def disable!
|
|
314
|
+
@logger.debug("disabling check [#{@key}]") if @logger
|
|
315
|
+
@redis.zrem("current_checks:#{entity.name}", check)
|
|
316
|
+
if @redis.zcount("current_checks:#{entity.name}", '-inf', '+inf') == 0
|
|
317
|
+
@redis.zrem("current_checks:#{entity.name}", check)
|
|
318
|
+
@redis.zrem("current_entities", entity.name)
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def enabled?
|
|
323
|
+
!! @redis.zscore("current_checks:#{entity.name}", check)
|
|
251
324
|
end
|
|
252
325
|
|
|
253
326
|
def last_change
|
data/lib/flapjack/data/event.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'oj'
|
|
4
4
|
|
|
5
5
|
module Flapjack
|
|
6
6
|
module Data
|
|
7
7
|
class Event
|
|
8
8
|
|
|
9
|
-
attr_accessor :previous_state, :previous_state_duration
|
|
9
|
+
attr_accessor :counter, :previous_state, :previous_state_duration
|
|
10
10
|
|
|
11
11
|
attr_reader :check, :summary, :details, :acknowledgement_id
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ module Flapjack
|
|
|
20
20
|
# Calling next with :block => false, will return a nil if there are no
|
|
21
21
|
# events on the queue.
|
|
22
22
|
#
|
|
23
|
-
def self.next(opts={})
|
|
23
|
+
def self.next(queue, opts = {})
|
|
24
24
|
raise "Redis connection not set" unless redis = opts[:redis]
|
|
25
25
|
|
|
26
26
|
defaults = { :block => true,
|
|
@@ -31,23 +31,23 @@ module Flapjack
|
|
|
31
31
|
if options[:archive_events]
|
|
32
32
|
dest = "events_archive:#{Time.now.utc.strftime "%Y%m%d%H"}"
|
|
33
33
|
if options[:block]
|
|
34
|
-
raw = redis.brpoplpush(
|
|
34
|
+
raw = redis.brpoplpush(queue, dest, 0)
|
|
35
35
|
else
|
|
36
|
-
raw = redis.rpoplpush(
|
|
36
|
+
raw = redis.rpoplpush(queue, dest)
|
|
37
37
|
return unless raw
|
|
38
38
|
end
|
|
39
39
|
redis.expire(dest, options[:events_archive_maxage])
|
|
40
40
|
else
|
|
41
41
|
if options[:block]
|
|
42
|
-
raw = redis.brpop(
|
|
42
|
+
raw = redis.brpop(queue, 0)[1]
|
|
43
43
|
else
|
|
44
|
-
raw = redis.rpop(
|
|
44
|
+
raw = redis.rpop(queue)
|
|
45
45
|
return unless raw
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
begin
|
|
49
|
-
parsed = ::
|
|
50
|
-
rescue => e
|
|
49
|
+
parsed = ::Oj.load( raw )
|
|
50
|
+
rescue Oj::Error => e
|
|
51
51
|
if options[:logger]
|
|
52
52
|
options[:logger].warn("Error deserialising event json: #{e}, raw json: #{raw.inspect}")
|
|
53
53
|
end
|
|
@@ -65,7 +65,7 @@ module Flapjack
|
|
|
65
65
|
raise "Redis connection not set" unless redis = opts[:redis]
|
|
66
66
|
|
|
67
67
|
evt['time'] = Time.now.to_i if evt['time'].nil?
|
|
68
|
-
redis.rpush('events', ::
|
|
68
|
+
redis.rpush('events', ::Oj.dump(evt))
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
# Provide a count of the number of events on the queue to be processed.
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
# contact+media recipient.
|
|
6
6
|
|
|
7
7
|
require 'flapjack/data/contact'
|
|
8
|
-
require 'flapjack/data/notification'
|
|
9
8
|
|
|
10
9
|
module Flapjack
|
|
11
10
|
module Data
|
|
@@ -15,8 +14,8 @@ module Flapjack
|
|
|
15
14
|
|
|
16
15
|
def self.for_contact(contact, opts = {})
|
|
17
16
|
self.new(:contact => contact,
|
|
18
|
-
:
|
|
19
|
-
:
|
|
17
|
+
:medium => opts[:medium],
|
|
18
|
+
:address => opts[:address],
|
|
20
19
|
:duration => opts[:duration])
|
|
21
20
|
end
|
|
22
21
|
|
|
@@ -36,15 +35,13 @@ module Flapjack
|
|
|
36
35
|
'contact_first_name' => contact.first_name,
|
|
37
36
|
'contact_last_name' => contact.last_name}
|
|
38
37
|
c['duration'] = duration if duration
|
|
39
|
-
|
|
40
|
-
c.merge(@notification_contents)
|
|
38
|
+
c
|
|
41
39
|
end
|
|
42
40
|
|
|
43
41
|
private
|
|
44
42
|
|
|
45
43
|
def initialize(opts = {})
|
|
46
44
|
@contact = opts[:contact]
|
|
47
|
-
@notification_contents = opts[:notification_contents]
|
|
48
45
|
@medium = opts[:medium]
|
|
49
46
|
@address = opts[:address]
|
|
50
47
|
@duration = opts[:duration]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
+
require 'oj'
|
|
4
|
+
|
|
3
5
|
require 'flapjack/data/contact'
|
|
4
6
|
require 'flapjack/data/event'
|
|
5
7
|
require 'flapjack/data/message'
|
|
@@ -8,52 +10,110 @@ module Flapjack
|
|
|
8
10
|
module Data
|
|
9
11
|
class Notification
|
|
10
12
|
|
|
11
|
-
attr_reader :
|
|
12
|
-
:default_timezone, :last_state
|
|
13
|
-
|
|
14
|
-
def self.for_event(event, opts = {})
|
|
15
|
-
self.new(:event => event,
|
|
16
|
-
:type => opts[:type],
|
|
17
|
-
:max_notified_severity => opts[:max_notified_severity],
|
|
18
|
-
:contacts => opts[:contacts],
|
|
19
|
-
:default_timezone => opts[:default_timezone],
|
|
20
|
-
:last_state => opts[:last_state],
|
|
21
|
-
:logger => opts[:logger])
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def messages
|
|
25
|
-
return [] if contacts.nil? || contacts.empty?
|
|
13
|
+
attr_reader :type, :event_id, :event_state, :event_count
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
def self.type_for_event(event)
|
|
16
|
+
case event.type
|
|
17
|
+
when 'service'
|
|
18
|
+
case event.state
|
|
19
|
+
when 'ok'
|
|
20
|
+
'recovery'
|
|
21
|
+
when 'warning', 'critical', 'unknown'
|
|
22
|
+
'problem'
|
|
23
|
+
end
|
|
24
|
+
when 'action'
|
|
25
|
+
case event.state
|
|
26
|
+
when 'acknowledgement'
|
|
27
|
+
'acknowledgement'
|
|
28
|
+
when 'test_notifications'
|
|
29
|
+
'test'
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
'unknown'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
def self.severity_for_event(event, max_notified_severity)
|
|
37
|
+
if ([event.state, max_notified_severity] & ['critical', 'unknown', 'test_notifications']).any?
|
|
31
38
|
'critical'
|
|
32
|
-
elsif [
|
|
39
|
+
elsif [event.state, max_notified_severity].include?('warning')
|
|
33
40
|
'warning'
|
|
34
41
|
else
|
|
35
42
|
'ok'
|
|
36
43
|
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.add(queue, event, opts = {})
|
|
47
|
+
raise "Redis connection not set" unless redis = opts[:redis]
|
|
48
|
+
|
|
49
|
+
last_state = opts[:last_state] || {}
|
|
50
|
+
|
|
51
|
+
notif = {'event_id' => event.id,
|
|
52
|
+
'state' => event.state,
|
|
53
|
+
'summary' => event.summary,
|
|
54
|
+
'last_state' => last_state[:state],
|
|
55
|
+
'last_summary' => last_state[:summary],
|
|
56
|
+
'details' => event.details,
|
|
57
|
+
'time' => event.time,
|
|
58
|
+
'duration' => event.duration || nil,
|
|
59
|
+
'type' => opts[:type] || type_for_event(event),
|
|
60
|
+
'severity' => opts[:severity],
|
|
61
|
+
'count' => event.counter }
|
|
62
|
+
|
|
63
|
+
redis.rpush(queue, Oj.dump(notif))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.next(queue, opts = {})
|
|
67
|
+
raise "Redis connection not set" unless redis = opts[:redis]
|
|
68
|
+
|
|
69
|
+
defaults = { :block => true }
|
|
70
|
+
options = defaults.merge(opts)
|
|
71
|
+
|
|
72
|
+
if options[:block]
|
|
73
|
+
raw = redis.blpop(queue, 0)[1]
|
|
74
|
+
else
|
|
75
|
+
raw = redis.lpop(queue)
|
|
76
|
+
return unless raw
|
|
77
|
+
end
|
|
78
|
+
begin
|
|
79
|
+
parsed = ::Oj.load( raw )
|
|
80
|
+
rescue Oj::Error => e
|
|
81
|
+
if options[:logger]
|
|
82
|
+
options[:logger].warn("Error deserialising notification json: #{e}, raw json: #{raw.inspect}")
|
|
83
|
+
end
|
|
84
|
+
return nil
|
|
85
|
+
end
|
|
86
|
+
self.new( parsed )
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def contents
|
|
90
|
+
@contents ||= {'event_id' => @event_id,
|
|
91
|
+
'state' => @event_state,
|
|
92
|
+
'summary' => @event_summary,
|
|
93
|
+
'last_state' => @last_event_state,
|
|
94
|
+
'last_summary' => @last_event_summary,
|
|
95
|
+
'details' => @event_details,
|
|
96
|
+
'time' => @event_time,
|
|
97
|
+
'duration' => @event_duration,
|
|
98
|
+
'notification_type' => @type,
|
|
99
|
+
'event_count' => @event_count
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def messages(contacts, opts = {})
|
|
104
|
+
return [] if contacts.nil? || contacts.empty?
|
|
37
105
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
'summary' => event.summary,
|
|
41
|
-
'last_state' => @last_state ? @last_state[:state] : nil,
|
|
42
|
-
'last_summary' => @last_state ? @last_state[:summary] : nil,
|
|
43
|
-
'details' => event.details,
|
|
44
|
-
'time' => event.time,
|
|
45
|
-
'duration' => event.duration || nil,
|
|
46
|
-
'notification_type' => type,
|
|
47
|
-
'max_notified_severity' => max_notified_severity }
|
|
106
|
+
default_timezone = opts[:default_timezone]
|
|
107
|
+
logger = opts[:logger]
|
|
48
108
|
|
|
49
109
|
@messages ||= contacts.collect {|contact|
|
|
50
110
|
contact_id = contact.id
|
|
51
111
|
rules = contact.notification_rules
|
|
52
112
|
media = contact.media
|
|
53
113
|
|
|
54
|
-
|
|
114
|
+
logger.debug "considering messages for contact id #{contact_id} #{@event_id} #{@event_state} (media) #{media.inspect}"
|
|
55
115
|
rlen = rules.length
|
|
56
|
-
|
|
116
|
+
logger.debug "found #{rlen} rule#{(rlen == 1) ? '' : 's'} for contact"
|
|
57
117
|
|
|
58
118
|
media_to_use = if rules.empty?
|
|
59
119
|
media
|
|
@@ -61,55 +121,54 @@ module Flapjack
|
|
|
61
121
|
# matchers are rules of the contact that have matched the current event
|
|
62
122
|
# for time and entity
|
|
63
123
|
matchers = rules.select do |rule|
|
|
64
|
-
rule.match_entity?(event_id) &&
|
|
124
|
+
rule.match_entity?(@event_id) &&
|
|
65
125
|
rule_occurring_now?(rule, :contact => contact, :default_timezone => default_timezone)
|
|
66
126
|
end
|
|
67
127
|
|
|
68
|
-
|
|
128
|
+
logger.debug "#{matchers.length} matchers remain for this contact:"
|
|
69
129
|
matchers.each do |matcher|
|
|
70
|
-
|
|
130
|
+
logger.debug "matcher: #{matcher.to_json}"
|
|
71
131
|
end
|
|
72
132
|
|
|
73
133
|
# delete any matchers for all entities if there are more specific matchers
|
|
74
134
|
if matchers.any? {|matcher| matcher.is_specific? }
|
|
75
135
|
|
|
76
|
-
|
|
136
|
+
logger.debug("general removal: found #{matchers.length} entity specific matchers")
|
|
77
137
|
num_matchers = matchers.length
|
|
78
138
|
|
|
79
139
|
matchers.reject! {|matcher| !matcher.is_specific? }
|
|
80
140
|
|
|
81
141
|
if num_matchers != matchers.length
|
|
82
|
-
|
|
142
|
+
logger.debug("notification: removal of general matchers when entity specific matchers are present: number of matchers changed from #{num_matchers} to #{matchers.length} for contact id: #{contact_id}")
|
|
83
143
|
end
|
|
84
144
|
end
|
|
85
145
|
|
|
86
146
|
# delete media based on blackholes
|
|
87
|
-
next if matchers.any? {|matcher| matcher.blackhole?(event_state) }
|
|
147
|
+
next if matchers.any? {|matcher| matcher.blackhole?(@event_state) }
|
|
88
148
|
|
|
89
|
-
|
|
149
|
+
logger.debug "notification: num matchers after removing blackhole matchers: #{matchers.size}"
|
|
90
150
|
|
|
91
151
|
rule_media = matchers.collect{|matcher|
|
|
92
|
-
matcher.media_for_severity(severity)
|
|
152
|
+
matcher.media_for_severity(@severity)
|
|
93
153
|
}.flatten.uniq
|
|
94
154
|
|
|
95
|
-
|
|
96
|
-
rule_media = rule_media.
|
|
155
|
+
logger.debug "notification: collected media_for_severity(#{@severity}): #{rule_media}"
|
|
156
|
+
rule_media = rule_media.reject {|medium|
|
|
97
157
|
contact.drop_notifications?(:media => medium,
|
|
98
|
-
:check => event_id,
|
|
99
|
-
:state => event_state)
|
|
158
|
+
:check => @event_id,
|
|
159
|
+
:state => @event_state)
|
|
100
160
|
}
|
|
101
161
|
|
|
102
|
-
|
|
162
|
+
logger.debug "notification: media after contact_drop?: #{rule_media}"
|
|
103
163
|
|
|
104
164
|
media.select {|medium, address| rule_media.include?(medium) }
|
|
105
165
|
end
|
|
106
166
|
|
|
107
|
-
|
|
167
|
+
logger.debug "notification: media_to_use: #{media_to_use}"
|
|
108
168
|
|
|
109
169
|
media_to_use.each_pair.inject([]) { |ret, (k, v)|
|
|
110
170
|
m = Flapjack::Data::Message.for_contact(contact,
|
|
111
|
-
|
|
112
|
-
:medium => k, :address => v)
|
|
171
|
+
:medium => k, :address => v)
|
|
113
172
|
ret << m
|
|
114
173
|
ret
|
|
115
174
|
}
|
|
@@ -118,15 +177,21 @@ module Flapjack
|
|
|
118
177
|
|
|
119
178
|
private
|
|
120
179
|
|
|
180
|
+
# created from parsed JSON, so opts keys are in strings
|
|
121
181
|
def initialize(opts = {})
|
|
122
|
-
|
|
123
|
-
@
|
|
124
|
-
@
|
|
125
|
-
@
|
|
126
|
-
@
|
|
127
|
-
@
|
|
128
|
-
@
|
|
129
|
-
|
|
182
|
+
@event_id = opts['event_id']
|
|
183
|
+
@event_state = opts['state']
|
|
184
|
+
@event_summary = opts['summary']
|
|
185
|
+
@event_details = opts['details']
|
|
186
|
+
@event_time = opts['time']
|
|
187
|
+
@event_duration = opts['duration']
|
|
188
|
+
@event_count = opts['count']
|
|
189
|
+
|
|
190
|
+
@last_event_state = opts['last_state']
|
|
191
|
+
@last_event_summary = opts['last_summary']
|
|
192
|
+
|
|
193
|
+
@type = opts['type']
|
|
194
|
+
@severity = opts['severity']
|
|
130
195
|
end
|
|
131
196
|
|
|
132
197
|
# # time restrictions match?
|
|
@@ -135,11 +200,11 @@ module Flapjack
|
|
|
135
200
|
# considered to be in the timezone of the contact
|
|
136
201
|
def rule_occurring_now?(rule, options = {})
|
|
137
202
|
contact = options[:contact]
|
|
138
|
-
|
|
203
|
+
def_tz = options[:default_timezone]
|
|
139
204
|
|
|
140
205
|
return true if rule.time_restrictions.nil? or rule.time_restrictions.empty?
|
|
141
206
|
|
|
142
|
-
timezone = contact.timezone(:default =>
|
|
207
|
+
timezone = contact.timezone(:default => def_tz)
|
|
143
208
|
usertime = timezone.now
|
|
144
209
|
|
|
145
210
|
rule.time_restrictions.any? do |tr|
|