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
@@ -25,7 +25,7 @@ module Flapjack
25
25
  raise "Redis connection not set" unless redis = options[:redis]
26
26
 
27
27
  redis.keys('contact:*').inject([]) {|ret, k|
28
- k =~ /^contact:(\d+)$/
28
+ k =~ /^contact:(.*)$/
29
29
  id = $1
30
30
  contact = self.find_by_id(id, :redis => redis)
31
31
  ret << contact if contact
@@ -129,6 +129,12 @@ module Flapjack
129
129
  merge('service_key' => service_key)
130
130
  end
131
131
 
132
+ def set_pagerduty_credentials(details)
133
+ @redis.hset("contact_media:#{self.id}", 'pagerduty', details['service_key'])
134
+ @redis.hmset("contact_pagerduty:#{self.id}",
135
+ *['subdomain', 'username', 'password'].collect {|f| [f, details[f]]})
136
+ end
137
+
132
138
  # NB ideally contacts_for:* keys would scope the entity and check by an
133
139
  # input source, for namespacing purposes
134
140
  def entities(options = {})
@@ -211,6 +217,7 @@ module Flapjack
211
217
  end
212
218
 
213
219
  def set_interval_for_media(media, interval)
220
+ return if 'pagerduty'.eql?(media)
214
221
  if interval.nil?
215
222
  @redis.hdel("contact_media_intervals:#{self.id}", media)
216
223
  return
@@ -225,6 +232,7 @@ module Flapjack
225
232
  end
226
233
 
227
234
  def set_rollup_threshold_for_media(media, threshold)
235
+ return if 'pagerduty'.eql?(media)
228
236
  if threshold.nil?
229
237
  @redis.hdel("contact_media_rollup_thresholds:#{self.id}", media)
230
238
  return
@@ -234,12 +242,8 @@ module Flapjack
234
242
  end
235
243
 
236
244
  def set_address_for_media(media, address)
245
+ return if 'pagerduty'.eql?(media)
237
246
  @redis.hset("contact_media:#{self.id}", media, address)
238
- if media == 'pagerduty'
239
- # FIXME - work out what to do when changing the pagerduty service key (address)
240
- # probably best solution is to remove the need to have the username and password
241
- # and subdomain as pagerduty's updated api's mean we don't them anymore I think...
242
- end
243
247
  self.media = @redis.hgetall("contact_media:#{@id}")
244
248
  end
245
249
 
@@ -312,10 +316,13 @@ module Flapjack
312
316
  key = "contact_alerting_checks:#{self.id}:media:#{media}"
313
317
  cleaned = 0
314
318
  alerting_checks_for_media(media).each do |check|
319
+ entity_check = Flapjack::Data::EntityCheck.for_event_id(check, :redis => @redis)
315
320
  next unless Flapjack::Data::EntityCheck.state_for_event_id?(check, :redis => @redis) == 'ok' ||
316
321
  Flapjack::Data::EntityCheck.in_unscheduled_maintenance_for_event_id?(check, :redis => @redis) ||
317
- Flapjack::Data::EntityCheck.in_scheduled_maintenance_for_event_id?(check, :redis => @redis)
322
+ Flapjack::Data::EntityCheck.in_scheduled_maintenance_for_event_id?(check, :redis => @redis) ||
323
+ !entity_check.contacts.map {|c| c.id}.include?(self.id)
318
324
 
325
+ # FIXME: why can't i get this logging when called from notifier (notification.rb)?
319
326
  @logger.debug("removing from alerting checks for #{self.id}/#{media}: #{check}") if @logger
320
327
  remove_alerting_check_for_media(media, check)
321
328
  cleaned += 1
@@ -30,9 +30,10 @@ module Flapjack
30
30
  # TODO probably shouldn't always be creating on query -- work out when this should be happening
31
31
  def self.for_event_id(event_id, options = {})
32
32
  raise "Redis connection not set" unless redis = options[:redis]
33
+ logger = options[:logger]
33
34
  entity_name, check = event_id.split(':', 2)
34
- self.new(Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis, :create => true), check,
35
- :redis => redis)
35
+ self.new(Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis, :create => true, :logger => true), check,
36
+ :redis => redis, :logger => logger)
36
37
  end
37
38
 
38
39
  # TODO probably shouldn't always be creating on query -- work out when this should be happening
@@ -533,7 +534,7 @@ module Flapjack
533
534
  end
534
535
 
535
536
  entity.contacts + contact_ids.collect {|c_id|
536
- Flapjack::Data::Contact.find_by_id(c_id, :redis => @redis)
537
+ Flapjack::Data::Contact.find_by_id(c_id, :redis => @redis, :logger => @logger)
537
538
  }.compact
538
539
  end
539
540
 
@@ -61,10 +61,8 @@ module Flapjack
61
61
  'last_state' => last_state[:state],
62
62
  'last_summary' => last_state[:summary],
63
63
  'state_duration' => opts[:state_duration],
64
-
65
64
  'type' => opts[:type] || type_for_event(event),
66
65
  'severity' => opts[:severity],
67
-
68
66
  'tags' => tag_data }
69
67
 
70
68
  redis.rpush(queue, Oj.dump(notif))
@@ -94,11 +92,15 @@ module Flapjack
94
92
  end
95
93
 
96
94
  def ok?
97
- @state && ['ok', 'up'].include?(@state.downcase)
95
+ @state && ['ok', 'up'].include?(@state)
98
96
  end
99
97
 
100
98
  def acknowledgement?
101
- @state && ['acknowledgement'].include?(@state.downcase)
99
+ @state && ['acknowledgement'].include?(@state)
100
+ end
101
+
102
+ def test?
103
+ @state && ['test_notifications'].include?(@state)
102
104
  end
103
105
 
104
106
  def contents
@@ -123,7 +125,7 @@ module Flapjack
123
125
  default_timezone = opts[:default_timezone]
124
126
  logger = opts[:logger]
125
127
 
126
- @messages ||= contacts.collect {|contact|
128
+ @messages ||= contacts.collect do |contact|
127
129
  contact_id = contact.id
128
130
  rules = contact.notification_rules
129
131
  media = contact.media
@@ -195,36 +197,39 @@ module Flapjack
195
197
  logger.debug "media_to_use: #{media_to_use}"
196
198
 
197
199
  # here begins rollup madness
198
- media_to_use.each_pair.inject([]) { |ret, (media, address)|
200
+ media_to_use.each_pair.inject([]) do |ret, (media, address)|
199
201
  rollup_type = nil
200
202
 
201
- contact.add_alerting_check_for_media(media, @event_id) unless ok? || acknowledgement?
203
+ contact.add_alerting_check_for_media(media, @event_id) unless ok? || acknowledgement? || test?
202
204
 
203
205
  # expunge checks in (un)scheduled maintenance from the alerting set
204
206
  cleaned = contact.clean_alerting_checks_for_media(media)
205
207
  logger.debug("cleaned alerting checks for #{media}: #{cleaned}")
206
208
 
207
- alerting_checks = contact.count_alerting_checks_for_media(media)
208
- rollup_threshold = contact.rollup_threshold_for_media(media)
209
- case
210
- when rollup_threshold.nil?
211
- # back away slowly
212
- when alerting_checks >= rollup_threshold
213
- next ret if contact.drop_rollup_notifications_for_media?(media)
214
- contact.update_sent_rollup_alert_keys_for_media(media, :delete => ok?)
215
- rollup_type = 'problem'
216
- when (alerting_checks + cleaned >= rollup_threshold)
217
- # alerting checks was just cleaned such that it is now below the rollup threshold
218
- rollup_type = 'recovery'
209
+ # pagerduty is an example of a medium which should never be rolled up
210
+ unless ['pagerduty'].include?(media)
211
+ alerting_checks = contact.count_alerting_checks_for_media(media)
212
+ rollup_threshold = contact.rollup_threshold_for_media(media)
213
+ case
214
+ when rollup_threshold.nil?
215
+ # back away slowly
216
+ when alerting_checks >= rollup_threshold
217
+ next ret if contact.drop_rollup_notifications_for_media?(media)
218
+ contact.update_sent_rollup_alert_keys_for_media(media, :delete => ok?)
219
+ rollup_type = 'problem'
220
+ when (alerting_checks + cleaned >= rollup_threshold)
221
+ # alerting checks was just cleaned such that it is now below the rollup threshold
222
+ rollup_type = 'recovery'
223
+ end
224
+ logger.debug "rollup decisions: #{@event_id} #{@state} #{media} #{address} rollup_type: #{rollup_type}"
219
225
  end
220
- logger.debug "rollup decisions: #{@event_id} #{@state} #{media} #{address} rollup_type: #{rollup_type}"
221
226
 
222
227
  m = Flapjack::Data::Message.for_contact(contact,
223
228
  :medium => media, :address => address, :rollup => rollup_type)
224
229
  ret << m
225
230
  ret
226
- }
227
- }.compact.flatten
231
+ end
232
+ end.compact.flatten # @messages ||= contacts.collect do ...
228
233
  end
229
234
 
230
235
  private
@@ -238,16 +243,12 @@ module Flapjack
238
243
  @time = opts['time']
239
244
  @count = opts['count']
240
245
  @duration = opts['duration']
241
-
242
246
  @last_state = opts['last_state']
243
247
  @last_summary = opts['last_summary']
244
248
  @state_duration = opts['state_duration']
245
-
246
249
  @type = opts['type']
247
250
  @severity = opts['severity']
248
-
249
- tags = opts['tags']
250
- @tags = tags.is_a?(Array) ? Flapjack::Data::TagSet.new(tags) : nil
251
+ @tags = opts['tags'].is_a?(Array) ? Flapjack::Data::TagSet.new(opts['tags']) : nil
251
252
  end
252
253
 
253
254
  # # time restrictions match?
@@ -97,8 +97,11 @@ module Flapjack
97
97
  # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts
98
98
  app.get '/contacts' do
99
99
  content_type :json
100
-
101
- Flapjack::Data::Contact.all(:redis => redis).to_json
100
+ "[" +
101
+ Flapjack::Data::Contact.all(:redis => redis).map do |contact|
102
+ contact.to_json
103
+ end.join(',') +
104
+ "]"
102
105
  end
103
106
 
104
107
  # Returns the core information about the specified contact
@@ -233,19 +236,36 @@ module Flapjack
233
236
 
234
237
  contact = find_contact(params[:contact_id])
235
238
  errors = []
236
- if params[:address].nil?
237
- errors << "no address for '#{params[:id]}' media"
238
- end
239
239
 
240
- halt err(403, *errors) unless errors.empty?
240
+ if 'pagerduty'.eql?(params[:id])
241
+ errors = [:service_key, :subdomain, :username, :password].inject([]) do |memo, pdp|
242
+ memo << "no #{pdp.to_s} for 'pagerduty' media" if params[pdp].nil?
243
+ memo
244
+ end
245
+
246
+ halt err(403, *errors) unless errors.empty?
247
+
248
+ contact.set_pagerduty_credentials('service_key' => params[:service_key],
249
+ 'subdomain' => params[:subdomain],
250
+ 'username' => params[:username],
251
+ 'password' => params[:password])
241
252
 
242
- contact.set_address_for_media(params[:id], params[:address])
243
- contact.set_interval_for_media(params[:id], params[:interval])
244
- contact.set_rollup_threshold_for_media(params[:id], params[:rollup_threshold])
253
+ contact.pagerduty_credentials.to_json
254
+ else
255
+ if params[:address].nil?
256
+ errors << "no address for '#{params[:id]}' media"
257
+ end
258
+
259
+ halt err(403, *errors) unless errors.empty?
245
260
 
246
- {'address' => contact.media[params[:id]],
247
- 'interval' => contact.media_intervals[params[:id]],
248
- 'rollup_threshold' => contact.media_rollup_thresholds[params[:id]]}.to_json
261
+ contact.set_address_for_media(params[:id], params[:address])
262
+ contact.set_interval_for_media(params[:id], params[:interval])
263
+ contact.set_rollup_threshold_for_media(params[:id], params[:rollup_threshold])
264
+
265
+ {'address' => contact.media[params[:id]],
266
+ 'interval' => contact.media_intervals[params[:id]],
267
+ 'rollup_threshold' => contact.media_rollup_thresholds[params[:id]]}.to_json
268
+ end
249
269
  end
250
270
 
251
271
  # delete a media of a contact
@@ -12,6 +12,7 @@ require 'em/protocols/smtpclient'
12
12
  require 'flapjack/utility'
13
13
 
14
14
  require 'flapjack/data/entity_check'
15
+ require 'flapjack/data/alert'
15
16
 
16
17
  module Flapjack
17
18
  module Gateways
@@ -31,46 +32,22 @@ module Flapjack
31
32
  end
32
33
 
33
34
  # TODO refactor to remove complexity
34
- def perform(notification)
35
- prepare( notification )
36
- deliver( notification )
35
+ def perform(contents)
36
+ @logger.debug "Woo, got an alert to send out: #{contents.inspect}"
37
+ alert = prepare(contents)
38
+ deliver(alert)
37
39
  end
38
40
 
39
41
  # sets a bunch of class instance variables for each email
40
- def prepare(notification)
41
- @logger.debug "Woo, got a notification to send out: #{notification.inspect}"
42
-
43
- # The instance variables are referenced by the templates, which
44
- # share the current binding context
45
- @notification_type = notification['notification_type']
46
- @notification_id = notification['id'] || SecureRandom.uuid
47
- @rollup = notification['rollup']
48
- @rollup_alerts = notification['rollup_alerts']
49
- @rollup_threshold = notification['rollup_threshold']
50
- @contact_first_name = notification['contact_first_name']
51
- @contact_last_name = notification['contact_last_name']
52
- @state = notification['state']
53
- @duration = notification['state_duration']
54
- @summary = notification['summary']
55
- @last_state = notification['last_state']
56
- @last_summary = notification['last_summary']
57
- @details = notification['details']
58
- @time = notification['time']
59
- @entity_name, @check = notification['event_id'].split(':', 2)
60
-
61
- entity_check = Flapjack::Data::EntityCheck.for_event_id(notification['event_id'],
62
- :redis => @redis)
63
-
64
- @in_unscheduled_maintenance = entity_check.in_scheduled_maintenance?
65
- @in_scheduled_maintenance = entity_check.in_unscheduled_maintenance?
66
-
42
+ def prepare(contents)
43
+ Flapjack::Data::Alert.new(contents, :logger => @logger)
67
44
  rescue => e
68
- @logger.error "Error preparing email to #{m_to}: #{e.class}: #{e.message}"
45
+ @logger.error "Error preparing email to #{contents['address']}: #{e.class}: #{e.message}"
69
46
  @logger.error e.backtrace.join("\n")
70
47
  raise
71
48
  end
72
49
 
73
- def deliver(notification)
50
+ def deliver(alert)
74
51
  host = @smtp_config ? @smtp_config['host'] : nil
75
52
  port = @smtp_config ? @smtp_config['port'] : nil
76
53
  starttls = @smtp_config ? !! @smtp_config['starttls'] : nil
@@ -85,21 +62,21 @@ module Flapjack
85
62
 
86
63
  m_from = "flapjack@#{@fqdn}"
87
64
  @logger.debug("flapjack_mailer: set from to #{m_from}")
88
- m_reply_to = m_from
89
- m_to = notification['address']
90
-
91
65
 
92
- mail = prepare_email(:from => m_from,
93
- :to => m_to)
66
+ mail = prepare_email(:from => m_from,
67
+ :to => alert.address,
68
+ :message_id => "<#{alert.notification_id}@#{@fqdn}>",
69
+ :alert => alert)
94
70
 
95
71
  smtp_args = {:from => m_from,
96
- :to => m_to,
72
+ :to => alert.address,
97
73
  :content => "#{mail.to_s}\r\n.\r\n",
98
74
  :domain => @fqdn,
99
75
  :host => host || 'localhost',
100
76
  :port => port || 25,
101
77
  :starttls => starttls}
102
78
  smtp_args.merge!(:auth => auth) if auth
79
+
103
80
  email = EM::P::SmtpClient.send(smtp_args)
104
81
 
105
82
  response = EM::Synchrony.sync(email)
@@ -107,45 +84,64 @@ module Flapjack
107
84
  # http://tools.ietf.org/html/rfc821#page-36 SMTP response codes
108
85
  if response && response.respond_to?(:code) &&
109
86
  ((response.code == 250) || (response.code == 251))
110
- @logger.info "Email sending succeeded"
87
+ alert.record_send_success!
111
88
  @sent += 1
112
89
  else
113
90
  @logger.error "Email sending failed"
114
91
  end
115
92
 
116
- @logger.info "Email response: #{response.inspect}"
93
+ @logger.debug "Email response: #{response.inspect}"
117
94
 
118
95
  rescue => e
119
- @logger.error "Error delivering email to #{m_to}: #{e.class}: #{e.message}"
96
+ @logger.error "Error generating or delivering email to #{alert.address}: #{e.class}: #{e.message}"
120
97
  @logger.error e.backtrace.join("\n")
121
98
  raise
122
99
  end
123
100
 
124
101
  private
125
102
 
103
+ # returns a Mail object
126
104
  def prepare_email(opts = {})
127
105
  from = opts[:from]
128
106
  to = opts[:to]
129
- message_id = "<#{@notification_id}@#{@fqdn}>"
107
+ message_id = opts[:message_id]
108
+ alert = opts[:alert]
130
109
 
131
110
  message_type = case
132
- when @rollup
111
+ when alert.rollup
133
112
  'rollup'
134
113
  else
135
114
  'alert'
136
115
  end
137
116
 
138
- subject_template = ERB.new(File.read(File.dirname(__FILE__) +
139
- "/email/#{message_type}_subject.text.erb"), nil, '-')
117
+ mydir = File.dirname(__FILE__)
118
+
119
+ subject_template_path = mydir + "/email/#{message_type}_subject.text.erb"
120
+ text_template_path = mydir + "/email/#{message_type}.text.erb"
121
+ html_template_path = mydir + "/email/#{message_type}.html.erb"
140
122
 
141
- text_template = ERB.new(File.read(File.dirname(__FILE__) +
142
- "/email/#{message_type}.text.erb"), nil, '-')
123
+ subject_template = ERB.new(File.read(subject_template_path), nil, '-')
124
+ text_template = ERB.new(File.read(text_template_path), nil, '-')
125
+ html_template = ERB.new(File.read(html_template_path), nil, '-')
143
126
 
144
- html_template = ERB.new(File.read(File.dirname(__FILE__) +
145
- "/email/#{message_type}.html.erb"), nil, '-')
127
+ @alert = alert
128
+ bnd = binding
146
129
 
147
- bnd = binding
148
- subject = subject_template.result(bnd).chomp
130
+ # do some intelligence gathering in case an ERB execution blows up
131
+ begin
132
+ erb_to_be_executed = subject_template_path
133
+ subject = subject_template.result(bnd).chomp
134
+
135
+ erb_to_be_executed = text_template_path
136
+ body_text = text_template.result(bnd)
137
+
138
+ erb_to_be_executed = html_template_path
139
+ body_html = html_template.result(bnd)
140
+ rescue => e
141
+ @logger.error "Error while excuting ERBs for an email: " +
142
+ "ERB being executed: #{erb_to_be_executed}"
143
+ raise
144
+ end
149
145
 
150
146
  @logger.debug("preparing email to: #{to}, subject: #{subject}, message-id: #{message_id}")
151
147
 
@@ -157,12 +153,12 @@ module Flapjack
157
153
  message_id message_id
158
154
 
159
155
  text_part do
160
- body text_template.result(bnd)
156
+ body body_text
161
157
  end
162
158
 
163
159
  html_part do
164
160
  content_type 'text/html; charset=UTF-8'
165
- body html_template.result(bnd)
161
+ body body_html
166
162
  end
167
163
  end
168
164
 
@@ -11,7 +11,7 @@
11
11
  }
12
12
  </style>
13
13
 
14
- <p>Hi <%= @contact_first_name%></p>
14
+ <p>Hi <%= @alert.contact_first_name%></p>
15
15
 
16
16
  <p>Monitoring has detected the following:</p>
17
17
 
@@ -19,56 +19,56 @@
19
19
  <tbody>
20
20
  <tr>
21
21
  <td><strong>Entity</strong></td>
22
- <td><%= @entity_name %></td>
22
+ <td><%= @alert.entity %></td>
23
23
  </tr>
24
24
 
25
25
  <tr>
26
26
  <td><strong>Check</strong></td>
27
- <td><%= @check %></td>
27
+ <td><%= @alert.check %></td>
28
28
  </tr>
29
29
 
30
30
  <tr>
31
31
  <td><strong>State</strong></td>
32
- <td><%= ['ok'].include?(@state) ? @state.upcase : @state.titleize %></td>
32
+ <td><%= @alert.state_title_case %></td>
33
33
  </tr>
34
34
 
35
35
  <tr>
36
36
  <td><strong>Summary</strong></td>
37
- <td><%= @summary %></td>
37
+ <td><%= @alert.summary %></td>
38
38
  </tr>
39
39
 
40
- <% if @details %>
40
+ <% if @alert.details %>
41
41
  <tr>
42
42
  <td><strong>Details</strong></td>
43
- <td><%= @details %></td>
43
+ <td><%= @alert.details %></td>
44
44
  </tr>
45
45
  <% end %>
46
46
 
47
- <% if @time %>
47
+ <% if @alert.time %>
48
48
  <tr>
49
49
  <td><strong>Time</strong></td>
50
- <td><%= Time.at(@time.to_i).to_s %></td>
50
+ <td><%= Time.at(@alert.time.to_i).to_s %></td>
51
51
  </tr>
52
52
  <% end %>
53
53
 
54
- <% if @duration && @duration > 40 %>
54
+ <% if @alert.state_duration && @alert.state_duration > 40 %>
55
55
  <tr>
56
56
  <td><strong>Duration</strong></td>
57
- <td><%= ChronicDuration.output(@duration) %></td>
57
+ <td><%= ChronicDuration.output(@alert.state_duration) %></td>
58
58
  </tr>
59
59
  <% end %>
60
60
 
61
- <% if @last_state %>
61
+ <% if @alert.last_state %>
62
62
  <tr>
63
63
  <td><strong>Previous State</strong></td>
64
- <td><%= ['ok'].include?(@last_state) ? @last_state.upcase : @last_state.titleize %></td>
64
+ <td><%= @alert.last_state_title_case %></td>
65
65
  </tr>
66
66
  <% end %>
67
67
 
68
- <% if @last_summary %>
68
+ <% if @alert.last_summary %>
69
69
  <tr>
70
70
  <td><strong>Previous Summary</strong></td>
71
- <td><%= @last_summary %></td>
71
+ <td><%= @alert.last_summary %></td>
72
72
  </tr>
73
73
  <% end %>
74
74