flapjack 0.7.28 → 0.7.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/features/notification_rules.feature +25 -25
  4. data/features/rollup.feature +38 -18
  5. data/features/steps/events_steps.rb +10 -5
  6. data/features/steps/notifications_steps.rb +8 -4
  7. data/lib/flapjack/data/alert.rb +207 -0
  8. data/lib/flapjack/data/contact.rb +14 -7
  9. data/lib/flapjack/data/entity_check.rb +4 -3
  10. data/lib/flapjack/data/notification.rb +28 -27
  11. data/lib/flapjack/gateways/api/contact_methods.rb +32 -12
  12. data/lib/flapjack/gateways/email.rb +49 -53
  13. data/lib/flapjack/gateways/email/alert.html.erb +15 -15
  14. data/lib/flapjack/gateways/email/alert.text.erb +15 -15
  15. data/lib/flapjack/gateways/email/alert_subject.text.erb +3 -13
  16. data/lib/flapjack/gateways/email/rollup.html.erb +6 -6
  17. data/lib/flapjack/gateways/email/rollup.text.erb +7 -7
  18. data/lib/flapjack/gateways/email/rollup_subject.text.erb +1 -19
  19. data/lib/flapjack/gateways/jabber.rb +57 -47
  20. data/lib/flapjack/gateways/jabber/alert.text.erb +12 -0
  21. data/lib/flapjack/gateways/jabber/rollup.text.erb +2 -0
  22. data/lib/flapjack/gateways/pagerduty.rb +60 -30
  23. data/lib/flapjack/gateways/pagerduty/alert.text.erb +10 -0
  24. data/lib/flapjack/gateways/sms_messagenet.rb +29 -36
  25. data/lib/flapjack/gateways/sms_messagenet/alert.text.erb +4 -14
  26. data/lib/flapjack/gateways/sms_messagenet/rollup.text.erb +2 -34
  27. data/lib/flapjack/gateways/web.rb +23 -14
  28. data/lib/flapjack/gateways/web/views/check.html.erb +16 -11
  29. data/lib/flapjack/gateways/web/views/contact.html.erb +58 -16
  30. data/lib/flapjack/gateways/web/views/self_stats.html.erb +80 -71
  31. data/lib/flapjack/notifier.rb +8 -2
  32. data/lib/flapjack/pikelet.rb +17 -3
  33. data/lib/flapjack/processor.rb +0 -1
  34. data/lib/flapjack/redis_pool.rb +1 -1
  35. data/lib/flapjack/utility.rb +13 -0
  36. data/lib/flapjack/version.rb +1 -1
  37. data/spec/lib/flapjack/data/contact_spec.rb +44 -29
  38. data/spec/lib/flapjack/gateways/api/contact_methods_spec.rb +24 -4
  39. data/spec/lib/flapjack/gateways/email_spec.rb +0 -5
  40. data/spec/lib/flapjack/gateways/jabber_spec.rb +5 -1
  41. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +5 -2
  42. data/spec/lib/flapjack/gateways/{sms_messagenet.spec.rb → sms_messagenet_spec.rb} +16 -12
  43. data/spec/lib/flapjack/gateways/web_spec.rb +1 -1
  44. data/spec/spec_helper.rb +28 -6
  45. metadata +43 -89
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a8a6bbb199b138fb0da3af9765f5f0fe7b2ad552
4
+ data.tar.gz: 3dbd00e9e7e63978726cb0f1e78fc96639f1762b
5
+ SHA512:
6
+ metadata.gz: 981b9061652d0a75e5e32cb82590c20eeca4a7b2d624aad076097c8a39c799a0850110e67fe080afa6ffac5dbe16d39184d06014db3d3cdbf1c469353425019f
7
+ data.tar.gz: cad93481bc1dfd36c636ea84a0e7d59a9e5b1c926e046cf15b35a58cf4d4d06545bdd600b0cfe4d3e343ab6298944b40efc6ea89747355da04c456db56f46058
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## Flapjack Changelog
2
2
 
3
+ # 0.7.29 - 2013-11-01
4
+ - Feature: Per contact media rollup complete gh-291 (@jessereynolds)
5
+ - Feature: Rollup - show alerting checks and rollup status for each media on contact page gh-326 (@jessereynolds)
6
+ - Feature: Make message templating more flexible and consistent gh-329 (@jessereynolds)
7
+ - Feature: Remove misleading internal metrics gh-344 (@jessereynolds)
8
+ - Bug: clear up pagerduty details API code gh-338 (@ali-graham)
9
+ - Bug: non-numeric contact ID not properly supported - thanks @avishai-ish-shalom gh-338 (@jessereynolds)
10
+ - Bug: GET /contacts gives you more than you bargained for gh-339 (@jessereynolds)
11
+ - Bug: SMS alerts being sent from separate environment gh-135 (@jessereynolds)
12
+
3
13
  # 0.7.28 - 2013-10-21
4
14
  - Feature: Per contact media rollup (summary) notifications - foundation (data, api, web UI etc) gh-291 (@jessereynolds)
5
15
  - Feature: flapper takes an IP to bind to gh-98 (@ali-graham)
@@ -4,51 +4,51 @@ Feature: Notification rules on a per contact basis
4
4
  Background:
5
5
  Given the following users exist:
6
6
  | id | first_name | last_name | email | sms | timezone |
7
- | 1 | Malak | Al-Musawi | malak@example.com | +61400000001 | Asia/Baghdad |
8
- | 2 | Imani | Farooq | imani@example.com | +61400000002 | Europe/Moscow |
9
- | 3 | Vera | Дурейко | vera@example.com | +61400000003 | Europe/Paris |
10
- | 4 | Lucia | Moretti | lucia@example.com | +61400000004 | Europe/Rome |
11
- | 5 | Wang Fang | Wong | fang@example.com | +61400000005 | Asia/Shanghai |
7
+ | c1 | Malak | Al-Musawi | malak@example.com | +61400000001 | Asia/Baghdad |
8
+ | c2 | Imani | Farooq | imani@example.com | +61400000002 | Europe/Moscow |
9
+ | c3 | Vera | Дурейко | vera@example.com | +61400000003 | Europe/Paris |
10
+ | c4 | Lucia | Moretti | lucia@example.com | +61400000004 | Europe/Rome |
11
+ | c5 | Wang Fang | Wong | fang@example.com | +61400000005 | Asia/Shanghai |
12
12
 
13
13
  And the following entities exist:
14
14
  | id | name | contacts |
15
- | 1 | foo | 1 |
16
- | 2 | bar | 1,2,3 |
17
- | 3 | baz | 1,3 |
18
- | 4 | buf | 1,2,3 |
19
- | 5 | foo-app-01.xyz | 4 |
15
+ | 1 | foo | c1 |
16
+ | 2 | bar | c1,c2,c3 |
17
+ | 3 | baz | c1,c3 |
18
+ | 4 | buf | c1,c2,c3 |
19
+ | 5 | foo-app-01.xyz | c4 |
20
20
 
21
- And user 1 has the following notification intervals:
21
+ And user c1 has the following notification intervals:
22
22
  | email | sms |
23
23
  | 15 | 60 |
24
24
 
25
- And user 2 has the following notification intervals:
25
+ And user c2 has the following notification intervals:
26
26
  | email | sms |
27
27
  | 15 | 60 |
28
28
 
29
- And user 3 has the following notification intervals:
29
+ And user c3 has the following notification intervals:
30
30
  | email | sms |
31
31
  | 15 | 60 |
32
32
 
33
- And user 4 has the following notification intervals:
33
+ And user c4 has the following notification intervals:
34
34
  | email | sms |
35
35
  | 15 | 60 |
36
36
 
37
- And user 1 has the following notification rules:
37
+ And user c1 has the following notification rules:
38
38
  | entities | unknown_media | warning_media | critical_media | warning_blackhole | critical_blackhole | time_restrictions |
39
39
  | | | email | sms,email | true | true | |
40
40
  | foo | | email | sms,email | | | 8-18 weekdays |
41
41
  | bar | email | | sms,email | true | | |
42
42
  | baz | | email | sms,email | | | |
43
43
 
44
- And user 2 has the following notification rules:
44
+ And user c2 has the following notification rules:
45
45
  | entities | tags | warning_media | critical_media | warning_blackhole | critical_blackhole |
46
46
  | | | email | email | | |
47
47
  | | | sms | sms | | |
48
48
  | bar | | email | email,sms | | |
49
49
  | bar | wags | | | true | true |
50
50
 
51
- And user 3 has the following notification rules:
51
+ And user c3 has the following notification rules:
52
52
  | entities | warning_media | critical_media | warning_blackhole | critical_blackhole |
53
53
  | | email | email | | |
54
54
  | baz | sms | sms | | |
@@ -56,13 +56,13 @@ Feature: Notification rules on a per contact basis
56
56
  | buf | sms | sms | | |
57
57
  | bar | email | email | true | true |
58
58
 
59
- And user 4 has the following notification rules:
59
+ And user c4 has the following notification rules:
60
60
  | tags | warning_media | critical_media | time_restrictions |
61
61
  | | | | |
62
62
  | xyz, disk, util | sms | sms | |
63
63
  | xyz, ping | sms,email | sms,email | 8-18 weekdays |
64
64
 
65
- And user 5 has the following notification rules:
65
+ And user c5 has the following notification rules:
66
66
  | unknown_media | critical_media |
67
67
  | email | email, sms |
68
68
 
@@ -81,15 +81,15 @@ Feature: Notification rules on a per contact basis
81
81
  And a critical event is received
82
82
  Then 1 email alert should be queued for malak@example.com
83
83
  When the time is February 1 2013 12:00
84
- Then all alert dropping keys for user 1 should have expired
84
+ Then all alert dropping keys for user c1 should have expired
85
85
  When a critical event is received
86
86
  Then 2 email alerts should be queued for malak@example.com
87
87
  When the time is February 1 2013 17:59
88
- Then all alert dropping keys for user 1 should have expired
88
+ Then all alert dropping keys for user c1 should have expired
89
89
  When a critical event is received
90
90
  Then 3 email alerts should be queued for malak@example.com
91
91
  When the time is February 1 2013 18:01
92
- Then all alert dropping keys for user 1 should have expired
92
+ Then all alert dropping keys for user c1 should have expired
93
93
  When a critical event is received
94
94
  Then 3 email alerts should be queued for malak@example.com
95
95
 
@@ -395,15 +395,15 @@ Feature: Notification rules on a per contact basis
395
395
  And a critical event is received
396
396
  Then 1 sms alert should be queued for +61400000004
397
397
  When the time is February 1 2013 12:00
398
- Then all alert dropping keys for user 1 should have expired
398
+ Then all alert dropping keys for user c1 should have expired
399
399
  When a critical event is received
400
400
  Then 2 sms alerts should be queued for +61400000004
401
401
  When the time is February 1 2013 17:59
402
- Then all alert dropping keys for user 1 should have expired
402
+ Then all alert dropping keys for user c1 should have expired
403
403
  When a critical event is received
404
404
  Then 3 sms alerts should be queued for +61400000004
405
405
  When the time is February 1 2013 18:01
406
- Then all alert dropping keys for user 1 should have expired
406
+ Then all alert dropping keys for user c1 should have expired
407
407
  When a critical event is received
408
408
  Then 3 sms alerts should be queued for +61400000004
409
409
 
@@ -177,22 +177,42 @@ Feature: Rollup on a per contact, per media basis
177
177
  Then 1 sms alert of type problem and rollup problem should be queued for +61400000001
178
178
  And 2 sms alerts should be queued for +61400000001
179
179
 
180
- # @time
181
- # Scenario: Contact ceases to be a contact on an entity that they were being alerted for
182
- # Given check 'ping' for entity 'foo' is in an ok state
183
- # And check 'ping' for entity 'baz' is in an ok state
184
- # When a critical event is received for check 'ping' on entity 'foo'
185
- # And 1 minute passes
186
- # And a critical event is received for check 'ping' on entity 'foo'
187
- # Then 1 sms alerts of type problem and rollup none should be queued for +61400000001
188
- # When 5 minutes passes
189
- # And a critical event is received for check 'ping' on entity 'baz'
190
- # And 1 minute passes
191
- # And a critical event is received for check 'ping' on entity 'baz'
192
- # Then 1 sms alert of type problem and rollup problem should be queued for +61400000001
193
- # And 2 sms alerts should be queued for +61400000001
194
- # When 1 minute passes
195
- # And user 1 ceases to be a contact of entity 'foo'
196
- # And a critical event is received for check 'ping' on entity 'baz'
197
- # Then 1 sms alert of rollup recovery should be queued for +61400000001
180
+ @time
181
+ Scenario: Contact ceases to be a contact on an entity that they were being alerted for
182
+ Given check 'ping' for entity 'foo' is in an ok state
183
+ And check 'ping' for entity 'baz' is in an ok state
184
+ When a critical event is received for check 'ping' on entity 'foo'
185
+ And 1 minute passes
186
+ And a critical event is received for check 'ping' on entity 'foo'
187
+ Then 1 sms alerts of type problem and rollup none should be queued for +61400000001
188
+ When 5 minutes passes
189
+ And a critical event is received for check 'ping' on entity 'baz'
190
+ And 1 minute passes
191
+ And a critical event is received for check 'ping' on entity 'baz'
192
+ Then 1 sms alert of type problem and rollup problem should be queued for +61400000001
193
+ And 1 sms alerts of type problem and rollup none should be queued for +61400000001
194
+ And 2 sms alerts should be queued for +61400000001
195
+ When 20 minute passes
196
+ And user 1 ceases to be a contact of entity 'foo'
197
+ And a critical event is received for check 'ping' on entity 'baz'
198
+ Then 1 sms alert of rollup recovery should be queued for +61400000001
199
+
200
+ @time
201
+ Scenario: Test notification to not contribute to rollup
202
+ Given check 'ping' for entity 'foo' is in an ok state
203
+ And check 'ping' for entity 'baz' is in an ok state
204
+ When a critical event is received for check 'ping' on entity 'foo'
205
+ And 1 minute passes
206
+ And a critical event is received for check 'ping' on entity 'foo'
207
+ Then 1 sms alert of type problem and rollup none should be queued for +61400000001
208
+ And 1 sms alert should be queued for +61400000001
209
+ When 1 minute passes
210
+ And a test event is received for check 'sausage' on entity 'foo'
211
+ Then 1 sms alert of type problem and rollup none should be queued for +61400000001
212
+ And 1 sms alert of type test and rollup none should be queued for +61400000001
213
+ And 2 sms alerts should be queued for +61400000001
214
+ When 20 minutes passes
215
+ And a critical event is received for check 'ping' on entity 'foo'
216
+ Then 2 sms alerts of type problem and rollup none should be queued for +61400000001
217
+ And 3 sms alerts should be queued for +61400000001
198
218
 
@@ -301,7 +301,7 @@ Then /^show me the (\w+ )*log$/ do |adjective|
301
301
  puts @logger.messages.join("\n")
302
302
  end
303
303
 
304
- Then /^dump notification rules for user (\d+)$/ do |contact|
304
+ Then /^dump notification rules for user (\S+)$/ do |contact|
305
305
  rule_ids = @redis.smembers("contact_notification_rules:#{contact}")
306
306
  puts "There #{(rule_ids.length == 1) ? 'is' : 'are'} #{rule_ids.length} notification rule#{(rule_ids.length == 1) ? '' : 's'} for user #{contact}:"
307
307
  rule_ids.each {|rule_id|
@@ -338,7 +338,7 @@ Given /^the following users exist:$/ do |contacts|
338
338
  end
339
339
  end
340
340
 
341
- Given /^user (\d+) has the following notification intervals:$/ do |contact_id, intervals|
341
+ Given /^user (\S+) has the following notification intervals:$/ do |contact_id, intervals|
342
342
  contact = Flapjack::Data::Contact.find_by_id(contact_id, :redis => @redis)
343
343
  intervals.hashes.each do |interval|
344
344
  contact.set_interval_for_media('email', interval['email'].to_i * 60)
@@ -346,7 +346,7 @@ Given /^user (\d+) has the following notification intervals:$/ do |contact_id, i
346
346
  end
347
347
  end
348
348
 
349
- Given /^user (\d+) has the following notification rollup thresholds:$/ do |contact_id, rollup_thresholds|
349
+ Given /^user (\S+) has the following notification rollup thresholds:$/ do |contact_id, rollup_thresholds|
350
350
  contact = Flapjack::Data::Contact.find_by_id(contact_id, :redis => @redis)
351
351
  rollup_thresholds.hashes.each do |rollup_threshold|
352
352
  contact.set_rollup_threshold_for_media('email', rollup_threshold['email'].to_i)
@@ -354,7 +354,7 @@ Given /^user (\d+) has the following notification rollup thresholds:$/ do |conta
354
354
  end
355
355
  end
356
356
 
357
- Given /^user (\d+) has the following notification rules:$/ do |contact_id, rules|
357
+ Given /^user (\S+) has the following notification rules:$/ do |contact_id, rules|
358
358
  contact = Flapjack::Data::Contact.find_by_id(contact_id, :redis => @redis)
359
359
  timezone = contact.timezone
360
360
 
@@ -400,7 +400,7 @@ Given /^user (\d+) has the following notification rules:$/ do |contact_id, rules
400
400
  end
401
401
  end
402
402
 
403
- Then /^all alert dropping keys for user (\d+) should have expired$/ do |contact_id|
403
+ Then /^all alert dropping keys for user (\S+) should have expired$/ do |contact_id|
404
404
  @redis.keys("drop_alerts_for_contact:#{contact_id}*").should be_empty
405
405
  end
406
406
 
@@ -426,3 +426,8 @@ Then /^(\w+) (\w+) alert(?:s)?(?: of)?(?: type (\w+))?(?: and)?(?: rollup (\w+))
426
426
  }.length.should == num_queued.to_i
427
427
  end
428
428
 
429
+ When(/^user (\S+) ceases to be a contact of entity '(.*)'$/) do |contact_id, entity|
430
+ entity = Flapjack::Data::Entity.find_by_name(entity, :redis => @redis)
431
+ @redis.srem("contacts_for:#{entity.id}", contact_id)
432
+ end
433
+
@@ -109,12 +109,14 @@ Given /^a user SMS notification has been queued for entity '([\w\.\-]+)'$/ do |e
109
109
  @sms_notification = {'notification_type' => 'problem',
110
110
  'contact_first_name' => 'John',
111
111
  'contact_last_name' => 'Smith',
112
- 'state' => 'CRITICAL',
112
+ 'state' => 'critical',
113
113
  'summary' => 'Socket timeout after 10 seconds',
114
114
  'time' => Time.now.to_i,
115
115
  'event_id' => "#{entity}:ping",
116
116
  'address' => '+61412345678',
117
- 'id' => 1}
117
+ 'id' => 1,
118
+ 'state_duration' => 30,
119
+ 'duration' => 45}
118
120
  end
119
121
 
120
122
  Given /^a user email notification has been queued for entity '([\w\.\-]+)'$/ do |entity|
@@ -124,12 +126,14 @@ Given /^a user email notification has been queued for entity '([\w\.\-]+)'$/ do
124
126
  @email_notification = {'notification_type' => 'problem',
125
127
  'contact_first_name' => 'John',
126
128
  'contact_last_name' => 'Smith',
127
- 'state' => 'CRITICAL',
129
+ 'state' => 'critical',
128
130
  'summary' => 'Socket timeout after 10 seconds',
129
131
  'time' => Time.now.to_i,
130
132
  'event_id' => "#{entity}:ping",
131
133
  'address' => 'johns@example.dom',
132
- 'id' => 2}
134
+ 'id' => 2,
135
+ 'state_duration' => 30,
136
+ 'duration' => 3600}
133
137
  end
134
138
 
135
139
  # NB using perform, the notifiers were accessing the wrong Redis DB number
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'active_support/inflector'
4
+ require 'flapjack/utility'
5
+
6
+ # Alert is the object ready to send to someone, complete with an address and all
7
+ # the data with which to render the text of the alert in the appropriate gateway
8
+ #
9
+ # It should possibly be renamed AlertPresenter
10
+
11
+ module Flapjack
12
+ module Data
13
+ class Alert
14
+
15
+ # from Flapjack::Data::Notification
16
+ attr_reader :event_id,
17
+ :state,
18
+ :summary,
19
+ :acknowledgement_duration,
20
+ :last_state,
21
+ :last_summary,
22
+ :state_duration,
23
+ :details,
24
+ :time,
25
+ :notification_type,
26
+ :event_count,
27
+ :tags
28
+
29
+ # from Flapjack::Data::Message
30
+ # :id,
31
+ attr_reader :media,
32
+ :address,
33
+ :rollup,
34
+ :contact_id,
35
+ :contact_first_name,
36
+ :contact_last_name
37
+
38
+ # from Flapjack::Notifier
39
+ attr_reader :rollup_threshold,
40
+ :rollup_alerts,
41
+ :in_scheduled_maintenance,
42
+ :in_unscheduled_maintenance
43
+
44
+ # from self
45
+ attr_reader :entity,
46
+ :check,
47
+ :notification_id
48
+
49
+ include Flapjack::Utility
50
+
51
+ def initialize(contents, opts)
52
+ raise "no logger supplied" unless @logger = opts[:logger]
53
+
54
+ @event_id = contents['event_id']
55
+ @state = contents['state']
56
+ @summary = contents['summary']
57
+ @acknowledgement_duration = contents['duration'] # SMELLY
58
+ @last_state = contents['last_state']
59
+ @last_summary = contents['last_summary']
60
+ @state_duration = contents['state_duration']
61
+ @details = contents['details']
62
+ @time = contents['time']
63
+ @notification_type = contents['notification_type']
64
+ @event_count = contents['event_count']
65
+ @tags = contents['tags']
66
+
67
+ @media = contents['media']
68
+ @address = contents['address']
69
+ @rollup = contents['rollup']
70
+ @contact_id = contents['contact_id']
71
+ @contact_first_name = contents['contact_first_name']
72
+ @contact_last_name = contents['contact_last_name']
73
+
74
+ @rollup_threshold = contents['rollup_threshold']
75
+ @rollup_alerts = contents['rollup_alerts']
76
+ @in_scheduled_maintenance = contents['in_scheduled_maintenance']
77
+ @in_unscheduled_maintenance = contents['in_unscheduled_maintenance']
78
+
79
+ @entity, @check = @event_id.split(':', 2)
80
+ @notification_id = contents['id'] || SecureRandom.uuid
81
+
82
+ allowed_states = ['ok', 'critical', 'warning', 'unknown', 'test_notifications', 'acknowledgement']
83
+ allowed_rollup_states = ['critical', 'warning', 'unknown']
84
+ raise "state #{@state.inspect} is invalid" unless
85
+ allowed_states.include?(@state)
86
+
87
+ raise "state_duration #{@state_duration.inspect} is invalid" unless
88
+ @state_duration && @state_duration.is_a?(Integer) && @state_duration >= 0
89
+
90
+ if @rollup_alerts
91
+ raise "rollup_alerts should be nil or a hash" unless @rollup_alerts.is_a?(Hash)
92
+ @rollup_alerts.each_pair do |check, details|
93
+ raise "duration of rollup_alerts['#{check}'] must be an integer" unless
94
+ details['duration'] && details['duration'].is_a?(Integer)
95
+ raise "state of rollup_alerts['#{check}'] is invalid" unless
96
+ details['state'] && allowed_rollup_states.include?(details['state'])
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ def type
103
+ case @rollup
104
+ when "problem"
105
+ "rollup_problem"
106
+ when "recovery"
107
+ "rollup_recovery"
108
+ else
109
+ @notification_type
110
+ end
111
+ end
112
+
113
+ def type_sentence_case
114
+ case type
115
+ when "rollup_problem"
116
+ "Problem summary"
117
+ when "rollup_recovery"
118
+ "Problem summaries finishing"
119
+ else
120
+ type.titleize
121
+ end
122
+ end
123
+
124
+ def state_title_case
125
+ ['ok'].include?(@state) ? @state.upcase : @state.titleize
126
+ end
127
+
128
+ def last_state_title_case
129
+ ['ok'].include?(@last_state) ? @last_state.upcase : @last_state.titleize
130
+ end
131
+
132
+ def rollup_alerts_by_state
133
+ ['critical', 'warning', 'unknown'].inject({}) do |memo, state|
134
+ alerts = rollup_alerts.find_all {|alert| alert[1]['state'] == state}
135
+ memo[state] = alerts
136
+ memo
137
+ end
138
+ end
139
+
140
+ def rollup_state_counts
141
+ rollup_alerts.inject({}) do |memo, alert|
142
+ memo[alert[1]['state']] = (memo[alert[1]['state']] || 0) + 1
143
+ memo
144
+ end
145
+ end
146
+
147
+ def rollup_states_summary
148
+ state_counts = rollup_state_counts
149
+ ['critical', 'warning', 'unknown'].inject([]) do |memo, state|
150
+ next memo unless rollup_state_counts[state]
151
+ memo << "#{state.titleize}: #{state_counts[state]}"
152
+ memo
153
+ end.join(', ')
154
+ end
155
+
156
+ # produces a textual list of checks that are failing broken down by state, eg:
157
+ # Critical: 'PING' on 'foo-app-01.example.com', 'SSH' on 'foo-app-01.example.com';
158
+ # Warning: 'Disk / Utilisation' on 'foo-app-02.example.com'
159
+ def rollup_states_detail_text(opts)
160
+ max_checks = opts[:max_checks_per_state]
161
+ rollup_alerts_by_state.inject([]) do |memo, state|
162
+ state_titleized = state[0].titleize
163
+ alerts = max_checks && max_checks > 0 ? state[1][0..(max_checks - 1)] : state[1]
164
+ next memo if alerts.empty?
165
+ checks = alerts.map {|alert| alert[0]}
166
+ checks << '...' if checks.length < rollup_state_counts[state[0]]
167
+ memo << "#{state[0].titleize}: #{checks.join(', ')}"
168
+ memo
169
+ end.join('; ')
170
+ end
171
+
172
+ def to_s
173
+ msg = "Alert via #{media}:#{address} to contact #{contact_id} (#{contact_first_name} #{contact_last_name}): "
174
+ msg += type_sentence_case
175
+ if rollup
176
+ msg += " - #{rollup_states_summary} (#{rollup_states_detail_text(:max_checks_per_state => 3)})"
177
+ else
178
+ msg += " - '#{check}' on #{entity}"
179
+ unless ['acknowledgement', 'test'].include?(type)
180
+ msg += " is #{state_title_case}"
181
+ end
182
+ if ['acknowledgement'].include?(type)
183
+ msg += " has been acknowledged, unscheduled maintenance created for "
184
+ msg += time_period_in_words(acknowledgement_duration)
185
+ end
186
+ if summary && summary.length > 0
187
+ msg += " - #{summary}"
188
+ end
189
+ end
190
+ end
191
+
192
+ def record_send_success!
193
+ @logger.info "Sent alert successfully: #{to_s}"
194
+ end
195
+
196
+ # TODO: perhaps move message send failure porting to this method
197
+ # to avoid duplication in the gateways, and to more easily allow
198
+ # better error reporting on message generation / send failure
199
+ #def record_send_failure!(opts)
200
+ # exception = opts[:exception]
201
+ # message = opts[:message]
202
+ # @logger.error "Error sending an alert! #{alert}"
203
+ #end
204
+
205
+ end
206
+ end
207
+ end