flapjack 0.7.14 → 0.7.15
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 +10 -0
- data/etc/flapjack_config.yaml.example +1 -0
- data/features/events.feature +5 -0
- data/features/notification_rules.feature +1 -1
- data/features/steps/events_steps.rb +28 -13
- data/features/steps/notifications_steps.rb +1 -1
- data/lib/flapjack/coordinator.rb +3 -1
- data/lib/flapjack/data/contact.rb +8 -6
- data/lib/flapjack/data/entity_check.rb +78 -113
- data/lib/flapjack/data/event.rb +54 -65
- data/lib/flapjack/data/notification.rb +5 -1
- data/lib/flapjack/executive.rb +42 -38
- data/lib/flapjack/filters/acknowledgement.rb +5 -5
- data/lib/flapjack/filters/base.rb +2 -2
- data/lib/flapjack/filters/delays.rb +11 -11
- data/lib/flapjack/filters/detect_mass_client_failures.rb +8 -8
- data/lib/flapjack/filters/ok.rb +6 -6
- data/lib/flapjack/filters/scheduled_maintenance.rb +2 -2
- data/lib/flapjack/filters/unscheduled_maintenance.rb +3 -2
- data/lib/flapjack/gateways/api.rb +374 -277
- data/lib/flapjack/gateways/api/entity_check_presenter.rb +52 -21
- data/lib/flapjack/gateways/api/entity_presenter.rb +14 -9
- data/lib/flapjack/gateways/email.rb +7 -0
- data/lib/flapjack/gateways/email/alert.html.haml +13 -1
- data/lib/flapjack/gateways/email/alert.text.erb +5 -4
- data/lib/flapjack/gateways/jabber.rb +90 -34
- data/lib/flapjack/gateways/pagerduty.rb +6 -2
- data/lib/flapjack/gateways/web.rb +13 -8
- data/lib/flapjack/gateways/web/views/check.haml +70 -45
- data/lib/flapjack/gateways/web/views/checks.haml +1 -1
- data/lib/flapjack/gateways/web/views/entity.haml +1 -1
- data/lib/flapjack/patches.rb +9 -2
- data/lib/flapjack/pikelet.rb +14 -10
- data/lib/flapjack/utility.rb +10 -4
- data/lib/flapjack/version.rb +1 -1
- data/spec/lib/flapjack/coordinator_spec.rb +19 -5
- data/spec/lib/flapjack/data/entity_check_spec.rb +3 -30
- data/spec/lib/flapjack/data/event_spec.rb +96 -1
- data/spec/lib/flapjack/executive_spec.rb +5 -11
- data/spec/lib/flapjack/gateways/api/entity_check_presenter_spec.rb +22 -3
- data/spec/lib/flapjack/gateways/api/entity_presenter_spec.rb +30 -15
- data/spec/lib/flapjack/gateways/api_spec.rb +552 -186
- data/spec/lib/flapjack/gateways/email_spec.rb +2 -0
- data/spec/lib/flapjack/gateways/jabber_spec.rb +5 -4
- data/spec/lib/flapjack/gateways/pagerduty_spec.rb +3 -2
- data/spec/lib/flapjack/gateways/web_spec.rb +17 -12
- data/spec/lib/flapjack/pikelet_spec.rb +5 -2
- metadata +4 -5
- data/config.ru +0 -11
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## Flapjack Changelog
|
2
2
|
|
3
|
+
# 0.7.15 - 2013-06-27
|
4
|
+
- Feature: Show acknowledgement duration on web interface, queryable via jabber gh-159 (@ali-graham)
|
5
|
+
- Feature: More info on check state in email gh-207 (@ali-graham)
|
6
|
+
- Feature: Bulk API operations gh-123 (@ali-graham)
|
7
|
+
- Bug: You can't remove an interval from a contact's media once it has one gh-153 (@ali-graham)
|
8
|
+
- Bug: Fix jabber identify boot time gh-172 (@ali-graham)
|
9
|
+
- Bug: Don't pluralise singular time periods in jabber messages gh-209 (@ali-graham)
|
10
|
+
- Bug: PUT /contacts/ID/media/MEDIA returns previous value for address gh-152 (@ali-graham)
|
11
|
+
- Bug: 'last update' shows large numbers of seconds gh-157 (@ali-graham)
|
12
|
+
|
3
13
|
# 0.7.14 - 2013-06-19
|
4
14
|
- Bug: Display of checks on web ui with a colon in their name is screwed gh-213 (@jessereynolds)
|
5
15
|
- Bug: show last critical, warning, unknown notificaiton times in web ui gh-211 (@jessereynolds)
|
data/features/events.feature
CHANGED
@@ -390,3 +390,8 @@ Feature: events
|
|
390
390
|
# When 10 seconds passes
|
391
391
|
# And an ok event is received
|
392
392
|
# Then a notification should not be generated
|
393
|
+
|
394
|
+
Scenario: scheduled maintenance created for initial check reference
|
395
|
+
Given the check has no state
|
396
|
+
When an ok event is received
|
397
|
+
Then scheduled maintenance should be generated
|
@@ -269,7 +269,7 @@ Feature: Notification rules on a per contact basis
|
|
269
269
|
And 1 sms alert should be queued for +61400000001
|
270
270
|
@time
|
271
271
|
Scenario: Critical straight after test
|
272
|
-
Given the check is check 'ping' on entity '
|
272
|
+
Given the check is check 'ping' on entity 'baz'
|
273
273
|
And the check is in an ok state
|
274
274
|
When a test event is received
|
275
275
|
Then 1 email alert should be queued for malak@example.com
|
@@ -153,9 +153,23 @@ Given /^an entity '([\w\.\-]+)' exists$/ do |entity|
|
|
153
153
|
:redis => @redis )
|
154
154
|
end
|
155
155
|
|
156
|
+
Given /^the check is check '(.*)' on entity '([\w\.\-]+)'$/ do |check, entity|
|
157
|
+
@check = check
|
158
|
+
@entity = entity
|
159
|
+
end
|
160
|
+
|
161
|
+
Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') has no state$/ do |check, entity|
|
162
|
+
check ||= @check
|
163
|
+
entity ||= @entity
|
164
|
+
remove_unscheduled_maintenance(entity, check)
|
165
|
+
remove_scheduled_maintenance(entity, check)
|
166
|
+
remove_notifications(entity, check)
|
167
|
+
@redis.hdel("check:#{@key}", 'state')
|
168
|
+
end
|
169
|
+
|
156
170
|
Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in an ok state$/ do |check, entity|
|
157
|
-
check
|
158
|
-
entity
|
171
|
+
check ||= @check
|
172
|
+
entity ||= @entity
|
159
173
|
remove_unscheduled_maintenance(entity, check)
|
160
174
|
remove_scheduled_maintenance(entity, check)
|
161
175
|
remove_notifications(entity, check)
|
@@ -163,8 +177,8 @@ Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in an ok s
|
|
163
177
|
end
|
164
178
|
|
165
179
|
Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in a critical state$/ do |check, entity|
|
166
|
-
check
|
167
|
-
entity
|
180
|
+
check ||= @check
|
181
|
+
entity ||= @entity
|
168
182
|
remove_unscheduled_maintenance(entity, check)
|
169
183
|
remove_scheduled_maintenance(entity, check)
|
170
184
|
remove_notifications(entity, check)
|
@@ -172,27 +186,22 @@ Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in a criti
|
|
172
186
|
end
|
173
187
|
|
174
188
|
Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in scheduled maintenance$/ do |check, entity|
|
175
|
-
check
|
176
|
-
entity
|
189
|
+
check ||= @check
|
190
|
+
entity ||= @entity
|
177
191
|
remove_unscheduled_maintenance(entity, check)
|
178
192
|
set_scheduled_maintenance(entity, check)
|
179
193
|
end
|
180
194
|
|
181
195
|
# TODO set the state directly rather than submit & drain
|
182
196
|
Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in unscheduled maintenance$/ do |check, entity|
|
183
|
-
check
|
184
|
-
entity
|
197
|
+
check ||= @check
|
198
|
+
entity ||= @entity
|
185
199
|
remove_scheduled_maintenance(entity, check)
|
186
200
|
set_critical_state(entity, check)
|
187
201
|
submit_acknowledgement(entity, check)
|
188
202
|
drain_events # TODO these should only be in When clauses
|
189
203
|
end
|
190
204
|
|
191
|
-
Given /^the check is check '(.*)' on entity '([\w\.\-]+)'$/ do |check, entity|
|
192
|
-
@check = check
|
193
|
-
@entity = entity
|
194
|
-
end
|
195
|
-
|
196
205
|
When /^an ok event is received(?: for check '([\w\.\-]+)' on entity '([\w\.\-]+)')?$/ do |check, entity|
|
197
206
|
check ||= @check
|
198
207
|
entity ||= @entity
|
@@ -259,6 +268,12 @@ Then /^a notification should be generated(?: for check '([\w\.\-]+)' on entity '
|
|
259
268
|
found.should be_true
|
260
269
|
end
|
261
270
|
|
271
|
+
Then /^scheduled maintenance should be generated(?: for check '([\w\.\-]+)' on entity '([\w\.\-]+)')?$/ do |check, entity|
|
272
|
+
check ||= @check
|
273
|
+
entity ||= @entity
|
274
|
+
@redis.get("#{entity}:#{check}:scheduled_maintenance").should_not be_nil
|
275
|
+
end
|
276
|
+
|
262
277
|
Then /^show me the (\w+ )*log$/ do |adjective|
|
263
278
|
puts "the #{adjective}log:"
|
264
279
|
puts @logger.messages.join("\n")
|
@@ -66,7 +66,7 @@ When /^an event notification is generated for entity '([\w\.\-]+)'$/ do |entity|
|
|
66
66
|
'entity' => entity,
|
67
67
|
'check' => 'ping')
|
68
68
|
entity_check = Flapjack::Data::EntityCheck.for_entity_name(entity, 'ping', :redis => @redis)
|
69
|
-
@app.send(:generate_notification_messages, event, entity_check)
|
69
|
+
@app.send(:generate_notification_messages, event, entity_check, Time.now.to_i)
|
70
70
|
end
|
71
71
|
|
72
72
|
Then /^an SMS notification for entity '([\w\.\-]+)' should be queued for the user$/ do |entity|
|
data/lib/flapjack/coordinator.rb
CHANGED
@@ -35,6 +35,8 @@ module Flapjack
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def start(options = {})
|
38
|
+
@boot_time = Time.now
|
39
|
+
|
38
40
|
EM.synchrony do
|
39
41
|
add_pikelets(pikelets(@config.all))
|
40
42
|
setup_signals if options[:signals]
|
@@ -131,7 +133,7 @@ module Flapjack
|
|
131
133
|
start_piks = []
|
132
134
|
pikelets_data.each_pair do |type, cfg|
|
133
135
|
next unless pikelet = Flapjack::Pikelet.create(type,
|
134
|
-
:config => cfg, :redis_config => @redis_options)
|
136
|
+
:config => cfg, :redis_config => @redis_options, :boot_time => @boot_time)
|
135
137
|
start_piks << pikelet
|
136
138
|
@pikelets << pikelet
|
137
139
|
end
|
@@ -16,7 +16,7 @@ module Flapjack
|
|
16
16
|
|
17
17
|
class Contact
|
18
18
|
|
19
|
-
attr_accessor :id, :first_name, :last_name, :email, :media, :pagerduty_credentials
|
19
|
+
attr_accessor :id, :first_name, :last_name, :email, :media, :media_intervals, :pagerduty_credentials
|
20
20
|
|
21
21
|
TAG_PREFIX = 'contact_tag'
|
22
22
|
|
@@ -76,6 +76,7 @@ module Flapjack
|
|
76
76
|
self.first_name, self.last_name, self.email =
|
77
77
|
@redis.hmget("contact:#{@id}", 'first_name', 'last_name', 'email')
|
78
78
|
self.media = @redis.hgetall("contact_media:#{@id}")
|
79
|
+
self.media_intervals = @redis.hgetall("contact_media_intervals:#{self.id}")
|
79
80
|
|
80
81
|
# similar to code in instance method pagerduty_credentials
|
81
82
|
if service_key = @redis.hget("contact_media:#{@id}", 'pagerduty')
|
@@ -199,10 +200,6 @@ module Flapjack
|
|
199
200
|
@redis.del("notification_rule:#{rule.id}")
|
200
201
|
end
|
201
202
|
|
202
|
-
def media_intervals
|
203
|
-
@redis.hgetall("contact_media_intervals:#{self.id}")
|
204
|
-
end
|
205
|
-
|
206
203
|
# how often to notify this contact on the given media
|
207
204
|
# return 15 mins if no value is set
|
208
205
|
def interval_for_media(media)
|
@@ -211,8 +208,12 @@ module Flapjack
|
|
211
208
|
end
|
212
209
|
|
213
210
|
def set_interval_for_media(media, interval)
|
214
|
-
|
211
|
+
if interval.nil?
|
212
|
+
@redis.hdel("contact_media_intervals:#{self.id}", media)
|
213
|
+
return
|
214
|
+
end
|
215
215
|
@redis.hset("contact_media_intervals:#{self.id}", media, interval)
|
216
|
+
self.media_intervals = @redis.hgetall("contact_media_intervals:#{self.id}")
|
216
217
|
end
|
217
218
|
|
218
219
|
def set_address_for_media(media, address)
|
@@ -222,6 +223,7 @@ module Flapjack
|
|
222
223
|
# probably best solution is to remove the need to have the username and password
|
223
224
|
# and subdomain as pagerduty's updated api's mean we don't them anymore I think...
|
224
225
|
end
|
226
|
+
self.media = @redis.hgetall("contact_media:#{@id}")
|
225
227
|
end
|
226
228
|
|
227
229
|
def remove_media(media)
|
@@ -22,6 +22,9 @@ module Flapjack
|
|
22
22
|
STATE_CRITICAL = 'critical'
|
23
23
|
STATE_UNKNOWN = 'unknown'
|
24
24
|
|
25
|
+
NOTIFICATION_STATES = [:problem, :warning, :critical, :unknown,
|
26
|
+
:recovery, :acknowledgement]
|
27
|
+
|
25
28
|
attr_accessor :entity, :check
|
26
29
|
|
27
30
|
# TODO probably shouldn't always be creating on query -- work out when this should be happening
|
@@ -76,35 +79,9 @@ module Flapjack
|
|
76
79
|
}
|
77
80
|
end
|
78
81
|
|
79
|
-
# TODO move to Event
|
80
|
-
def create_acknowledgement(options = {})
|
81
|
-
event = { 'type' => 'action',
|
82
|
-
'state' => 'acknowledgement',
|
83
|
-
'summary' => options['summary'],
|
84
|
-
'duration' => options['duration'],
|
85
|
-
'acknowledgement_id' => options['acknowledgement_id'],
|
86
|
-
'entity' => entity.name,
|
87
|
-
'check' => check
|
88
|
-
}
|
89
|
-
Flapjack::Data::Event.add(event, :redis => @redis)
|
90
|
-
end
|
91
|
-
|
92
|
-
# TODO move to Event
|
93
|
-
def test_notifications(options = {})
|
94
|
-
event = { 'type' => 'action',
|
95
|
-
'state' => 'test_notifications',
|
96
|
-
'summary' => options['summary'],
|
97
|
-
'details' => options['details'],
|
98
|
-
'entity' => entity.name,
|
99
|
-
'check' => check
|
100
|
-
}
|
101
|
-
Flapjack::Data::Event.add(event, :redis => @redis)
|
102
|
-
end
|
103
|
-
|
104
|
-
# FIXME: need to add summary to summary of existing unscheduled maintenance if there is
|
105
|
-
# one, and extend duration / expiry time, instead of creating a separate unscheduled
|
106
|
-
# outage as we are doing now...
|
107
82
|
def create_unscheduled_maintenance(opts = {})
|
83
|
+
end_unscheduled_maintenance if in_unscheduled_maintenance?
|
84
|
+
|
108
85
|
start_time = opts[:start_time] # unix timestamp
|
109
86
|
duration = opts[:duration] # seconds
|
110
87
|
summary = opts[:summary]
|
@@ -217,39 +194,43 @@ module Flapjack
|
|
217
194
|
@redis.hget("check:#{@key}", 'state')
|
218
195
|
end
|
219
196
|
|
220
|
-
def update_state(
|
221
|
-
return unless
|
197
|
+
def update_state(new_state, options = {})
|
198
|
+
return unless [STATE_OK, STATE_WARNING,
|
199
|
+
STATE_CRITICAL, STATE_UNKNOWN].include?(new_state)
|
200
|
+
|
222
201
|
timestamp = options[:timestamp] || Time.now.to_i
|
223
|
-
client = options[:client]
|
224
202
|
summary = options[:summary]
|
225
203
|
details = options[:details]
|
226
204
|
count = options[:count]
|
227
205
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
206
|
+
if self.state != new_state
|
207
|
+
client = options[:client]
|
208
|
+
|
209
|
+
# Note the current state (for speedy lookups)
|
210
|
+
@redis.hset("check:#{@key}", 'state', new_state)
|
211
|
+
|
212
|
+
# FIXME: rename to last_state_change?
|
213
|
+
@redis.hset("check:#{@key}", 'last_change', timestamp)
|
214
|
+
case state
|
215
|
+
when STATE_WARNING, STATE_CRITICAL, STATE_UNKNOWN
|
216
|
+
@redis.zadd('failed_checks', timestamp, @key)
|
217
|
+
# FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
|
218
|
+
@redis.zadd("failed_checks:client:#{client}", timestamp, @key) if client
|
219
|
+
else
|
220
|
+
@redis.zrem("failed_checks", @key)
|
221
|
+
# FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
|
222
|
+
@redis.zrem("failed_checks:client:#{client}", @key) if client
|
223
|
+
end
|
224
|
+
end
|
233
225
|
|
234
|
-
# Retain
|
226
|
+
# Retain event data for entity:check pair
|
235
227
|
@redis.rpush("#{@key}:states", timestamp)
|
236
|
-
@redis.set("#{@key}:#{timestamp}:state",
|
228
|
+
@redis.set("#{@key}:#{timestamp}:state", new_state)
|
237
229
|
@redis.set("#{@key}:#{timestamp}:summary", summary) if summary
|
238
230
|
@redis.set("#{@key}:#{timestamp}:details", details) if details
|
239
231
|
@redis.set("#{@key}:#{timestamp}:count", count) if count
|
240
232
|
|
241
233
|
@redis.zadd("#{@key}:sorted_state_timestamps", timestamp, timestamp)
|
242
|
-
|
243
|
-
case state
|
244
|
-
when STATE_WARNING, STATE_CRITICAL, STATE_UNKNOWN
|
245
|
-
@redis.zadd('failed_checks', timestamp, @key)
|
246
|
-
# FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
|
247
|
-
@redis.zadd("failed_checks:client:#{client}", timestamp, @key) if client
|
248
|
-
else
|
249
|
-
@redis.zrem("failed_checks", @key)
|
250
|
-
# FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
|
251
|
-
@redis.zrem("failed_checks:client:#{client}", @key) if client
|
252
|
-
end
|
253
234
|
end
|
254
235
|
|
255
236
|
def last_update
|
@@ -268,61 +249,31 @@ module Flapjack
|
|
268
249
|
lc.to_i
|
269
250
|
end
|
270
251
|
|
271
|
-
def
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
def last_warning_notification
|
278
|
-
lwn = @redis.get("#{@key}:last_warning_notification")
|
279
|
-
return unless (lwn && lwn =~ /^\d+$/)
|
280
|
-
lwn.to_i
|
281
|
-
end
|
282
|
-
|
283
|
-
def last_critical_notification
|
284
|
-
lcn = @redis.get("#{@key}:last_critical_notification")
|
285
|
-
return unless (lcn && lcn =~ /^\d+$/)
|
286
|
-
lcn.to_i
|
287
|
-
end
|
288
|
-
|
289
|
-
def last_unknown_notification
|
290
|
-
lcn = @redis.get("#{@key}:last_unknown_notification")
|
291
|
-
return unless (lcn && lcn =~ /^\d+$/)
|
292
|
-
lcn.to_i
|
293
|
-
end
|
294
|
-
|
295
|
-
def last_recovery_notification
|
296
|
-
lrn = @redis.get("#{@key}:last_recovery_notification")
|
297
|
-
return unless (lrn && lrn =~ /^\d+$/)
|
298
|
-
lrn.to_i
|
299
|
-
end
|
300
|
-
|
301
|
-
def last_acknowledgement_notification
|
302
|
-
lan = @redis.get("#{@key}:last_acknowledgement_notification")
|
303
|
-
return unless (lan && lan =~ /^\d+$/)
|
304
|
-
lan.to_i
|
252
|
+
def last_notification_for_state(state)
|
253
|
+
return unless NOTIFICATION_STATES.include?(state)
|
254
|
+
ln = @redis.get("#{@key}:last_#{state.to_s}_notification")
|
255
|
+
return {:timestamp => nil, :summary => nil} unless (ln && ln =~ /^\d+$/)
|
256
|
+
{ :timestamp => ln.to_i,
|
257
|
+
:summary => @redis.get("#{@key}:#{ln.to_i}:summary") }
|
305
258
|
end
|
306
259
|
|
307
260
|
def last_notifications_of_each_type
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
:acknowledgement => last_acknowledgement_notification }
|
313
|
-
ln
|
261
|
+
NOTIFICATION_STATES.inject({}) do |memo, state|
|
262
|
+
memo[state] = last_notification_for_state(state) unless (state == :problem)
|
263
|
+
memo
|
264
|
+
end
|
314
265
|
end
|
315
266
|
|
316
267
|
def max_notified_severity_of_current_failure
|
317
|
-
last_recovery =
|
268
|
+
last_recovery = last_notification_for_state(:recovery)[:timestamp] || 0
|
318
269
|
|
319
|
-
last_critical =
|
270
|
+
last_critical = last_notification_for_state(:critical)[:timestamp]
|
320
271
|
return STATE_CRITICAL if last_critical && (last_critical > last_recovery)
|
321
272
|
|
322
|
-
last_warning =
|
273
|
+
last_warning = last_notification_for_state(:warning)[:timestamp]
|
323
274
|
return STATE_WARNING if last_warning && (last_warning > last_recovery)
|
324
275
|
|
325
|
-
last_unknown =
|
276
|
+
last_unknown = last_notification_for_state(:unknown)[:timestamp]
|
326
277
|
return STATE_UNKNOWN if last_unknown && (last_unknown > last_recovery)
|
327
278
|
|
328
279
|
nil
|
@@ -331,15 +282,16 @@ module Flapjack
|
|
331
282
|
# unpredictable results if there are multiple notifications of different
|
332
283
|
# types sent at the same time
|
333
284
|
def last_notification
|
334
|
-
nils = { :type => nil, :timestamp => nil }
|
285
|
+
nils = { :type => nil, :timestamp => nil, :summary => nil }
|
286
|
+
|
335
287
|
lne = last_notifications_of_each_type
|
336
|
-
ln = lne.delete_if {|type,
|
337
|
-
if ln.find {|type,
|
338
|
-
ln = ln.delete_if {|type,
|
288
|
+
ln = lne.delete_if {|type, notif| notif[:timestamp].nil? || notif[:timestamp].to_i <= 0 }
|
289
|
+
if ln.find {|type, notif| type == :warning or type == :critical}
|
290
|
+
ln = ln.delete_if {|type, notif| type == :problem }
|
339
291
|
end
|
340
|
-
return nils
|
341
|
-
lns = ln.sort_by { |type,
|
342
|
-
{ :type => lns[0], :timestamp => lns[1] }
|
292
|
+
return nils if ln.empty?
|
293
|
+
lns = ln.sort_by { |type, notif| notif[:timestamp] }.last
|
294
|
+
{ :type => lns[0], :timestamp => lns[1][:timestamp], :summary => lns[1][:summary] }
|
343
295
|
end
|
344
296
|
|
345
297
|
def event_count_at(timestamp)
|
@@ -373,20 +325,37 @@ module Flapjack
|
|
373
325
|
# from midnight to 11:59:59 PM. Pass nil for either end to leave that
|
374
326
|
# side unbounded.
|
375
327
|
def historical_states(start_time, end_time, opts = {})
|
376
|
-
start_time
|
377
|
-
end_time
|
328
|
+
start_time = '-inf' if start_time.to_i <= 0
|
329
|
+
end_time = '+inf' if end_time.to_i <= 0
|
330
|
+
|
331
|
+
args = ["#{@key}:sorted_state_timestamps"]
|
332
|
+
|
378
333
|
order = opts[:order]
|
379
|
-
|
380
|
-
|
334
|
+
if (order && 'desc'.eql?(order.downcase))
|
335
|
+
query = :zrevrangebyscore
|
336
|
+
args += [end_time.to_s, start_time.to_s]
|
337
|
+
else
|
338
|
+
query = :zrangebyscore
|
339
|
+
args += [start_time.to_s, end_time.to_s]
|
340
|
+
end
|
341
|
+
|
342
|
+
if opts[:limit] && (opts[:limit].to_i > 0)
|
343
|
+
args << {:limit => [0, opts[:limit]]}
|
344
|
+
end
|
345
|
+
|
346
|
+
state_ts = @redis.send(query, *args)
|
381
347
|
|
382
348
|
state_data = nil
|
383
349
|
|
384
350
|
@redis.multi do |r|
|
385
351
|
state_data = state_ts.collect {|ts|
|
386
|
-
{:timestamp
|
387
|
-
:state
|
388
|
-
:summary
|
389
|
-
:details
|
352
|
+
{:timestamp => ts.to_i,
|
353
|
+
:state => r.get("#{@key}:#{ts}:state"),
|
354
|
+
:summary => r.get("#{@key}:#{ts}:summary"),
|
355
|
+
:details => r.get("#{@key}:#{ts}:details"),
|
356
|
+
# :count => r.get("#{@key}:#{ts}:count"),
|
357
|
+
# :check_latency => r.get("#{@key}:#{ts}:check_latency")
|
358
|
+
}
|
390
359
|
}
|
391
360
|
end
|
392
361
|
|
@@ -405,7 +374,7 @@ module Flapjack
|
|
405
374
|
# if any.
|
406
375
|
def historical_state_before(timestamp)
|
407
376
|
pos = @redis.zrank("#{@key}:sorted_state_timestamps", timestamp)
|
408
|
-
return if pos < 1
|
377
|
+
return if pos.nil? || pos < 1
|
409
378
|
ts = @redis.zrange("#{@key}:sorted_state_timestamps", pos - 1, pos)
|
410
379
|
return if ts.nil? || ts.empty?
|
411
380
|
{:timestamp => ts.first.to_i,
|
@@ -475,10 +444,6 @@ module Flapjack
|
|
475
444
|
@key = "#{entity.name}:#{check}"
|
476
445
|
end
|
477
446
|
|
478
|
-
def validate_state(state)
|
479
|
-
[STATE_OK, STATE_WARNING, STATE_CRITICAL, STATE_UNKNOWN].include?(state)
|
480
|
-
end
|
481
|
-
|
482
447
|
end
|
483
448
|
|
484
449
|
end
|