flapjack 0.7.14 → 0.7.15
Sign up to get free protection for your applications and to get access to all the features.
- 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
|