flapjack 0.7.18 → 0.7.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/CHANGELOG.md +7 -0
  2. data/bin/flapjack +3 -0
  3. data/bin/flapjack-nagios-receiver +4 -1
  4. data/bin/flapjack-netsaint-parser +2 -1
  5. data/bin/flapjack-populator +6 -3
  6. data/bin/receive-events +3 -1
  7. data/bin/simulate-failed-check +2 -1
  8. data/etc/flapjack_config.yaml.example +20 -0
  9. data/features/events.feature +1 -1
  10. data/features/events_check_names.feature +1 -1
  11. data/features/notification_rules.feature +1 -1
  12. data/features/notifications.feature +1 -1
  13. data/features/steps/events_steps.rb +18 -17
  14. data/features/steps/flapjack-netsaint-parser_steps.rb +1 -2
  15. data/features/steps/notifications_steps.rb +14 -1
  16. data/features/support/env.rb +27 -10
  17. data/flapjack.gemspec +1 -3
  18. data/lib/flapjack/coordinator.rb +30 -20
  19. data/lib/flapjack/data/contact.rb +3 -2
  20. data/lib/flapjack/data/entity.rb +3 -3
  21. data/lib/flapjack/data/entity_check.rb +116 -43
  22. data/lib/flapjack/data/event.rb +10 -10
  23. data/lib/flapjack/data/message.rb +3 -6
  24. data/lib/flapjack/data/notification.rb +122 -57
  25. data/lib/flapjack/data/notification_rule.rb +11 -11
  26. data/lib/flapjack/filters/acknowledgement.rb +2 -2
  27. data/lib/flapjack/filters/ok.rb +1 -1
  28. data/lib/flapjack/gateways/api/entity_check_presenter.rb +1 -0
  29. data/lib/flapjack/gateways/api/entity_methods.rb +4 -6
  30. data/lib/flapjack/gateways/api/rack/json_params_parser.rb +1 -1
  31. data/lib/flapjack/gateways/email.rb +3 -5
  32. data/lib/flapjack/gateways/email/{alert.html.haml → alert.html.erb} +0 -0
  33. data/lib/flapjack/gateways/jabber.rb +66 -35
  34. data/lib/flapjack/gateways/oobetet.rb +5 -7
  35. data/lib/flapjack/gateways/pagerduty.rb +7 -7
  36. data/lib/flapjack/gateways/web.rb +101 -41
  37. data/lib/flapjack/gateways/web/public/css/flapjack.css +1 -1
  38. data/lib/flapjack/gateways/web/views/{_css.haml → _css.html.erb} +2 -1
  39. data/lib/flapjack/gateways/web/views/_foot.html.erb +3 -0
  40. data/lib/flapjack/gateways/web/views/_head.html.erb +4 -0
  41. data/lib/flapjack/gateways/web/views/_nav.html.erb +9 -0
  42. data/lib/flapjack/gateways/web/views/check.html.erb +204 -0
  43. data/lib/flapjack/gateways/web/views/checks.html.erb +77 -0
  44. data/lib/flapjack/gateways/web/views/contact.html.erb +114 -0
  45. data/lib/flapjack/gateways/web/views/contacts.html.erb +42 -0
  46. data/lib/flapjack/gateways/web/views/entities.html.erb +39 -0
  47. data/lib/flapjack/gateways/web/views/entity.html.erb +67 -0
  48. data/lib/flapjack/gateways/web/views/index.html.erb +27 -0
  49. data/lib/flapjack/gateways/web/views/self_stats.html.erb +97 -0
  50. data/lib/flapjack/logger.rb +71 -23
  51. data/lib/flapjack/notifier.rb +157 -0
  52. data/lib/flapjack/patches.rb +1 -41
  53. data/lib/flapjack/pikelet.rb +4 -2
  54. data/lib/flapjack/{executive.rb → processor.rb} +32 -145
  55. data/lib/flapjack/version.rb +1 -1
  56. data/spec/lib/flapjack/coordinator_spec.rb +134 -71
  57. data/spec/lib/flapjack/data/contact_spec.rb +1 -0
  58. data/spec/lib/flapjack/data/entity_check_spec.rb +146 -30
  59. data/spec/lib/flapjack/data/entity_spec.rb +4 -4
  60. data/spec/lib/flapjack/data/event_spec.rb +4 -4
  61. data/spec/lib/flapjack/data/message_spec.rb +2 -3
  62. data/spec/lib/flapjack/data/notification_spec.rb +13 -19
  63. data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +2 -2
  64. data/spec/lib/flapjack/gateways/jabber_spec.rb +34 -0
  65. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +0 -2
  66. data/spec/lib/flapjack/gateways/web/views/{check.haml_spec.rb → check.html.erb_spec.rb} +2 -2
  67. data/spec/lib/flapjack/gateways/web/views/{contact.haml_spec.rb → contact.html.erb_spec.rb} +3 -3
  68. data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +14 -0
  69. data/spec/lib/flapjack/gateways/web_spec.rb +20 -8
  70. data/spec/lib/flapjack/logger_spec.rb +30 -28
  71. data/spec/lib/flapjack/notifier_spec.rb +6 -0
  72. data/spec/lib/flapjack/pikelet_spec.rb +8 -8
  73. data/spec/lib/flapjack/{executive_spec.rb → processor_spec.rb} +4 -4
  74. data/spec/spec_helper.rb +1 -13
  75. data/spec/support/erb_view_helper.rb +23 -0
  76. data/tasks/profile.rake +1 -1
  77. data/tmp/acknowledge.rb +3 -1
  78. data/tmp/create_event_ok.rb +3 -1
  79. data/tmp/create_event_unknown.rb +3 -1
  80. data/tmp/create_events_failure.rb +3 -1
  81. data/tmp/create_events_ok.rb +3 -1
  82. data/tmp/create_events_ok_fail_ack_ok.rb +3 -1
  83. data/tmp/create_events_ok_failure.rb +3 -1
  84. data/tmp/create_events_ok_failure_ack.rb +3 -1
  85. data/tmp/test_json_post.rb +5 -3
  86. data/tmp/test_notification_rules_api.rb +5 -3
  87. metadata +32 -61
  88. data/lib/flapjack/gateways/web/views/_foot.haml +0 -8
  89. data/lib/flapjack/gateways/web/views/_head.haml +0 -10
  90. data/lib/flapjack/gateways/web/views/_nav.haml +0 -14
  91. data/lib/flapjack/gateways/web/views/check.haml +0 -191
  92. data/lib/flapjack/gateways/web/views/checks.haml +0 -49
  93. data/lib/flapjack/gateways/web/views/contact.haml +0 -85
  94. data/lib/flapjack/gateways/web/views/contacts.haml +0 -30
  95. data/lib/flapjack/gateways/web/views/entities.haml +0 -28
  96. data/lib/flapjack/gateways/web/views/entity.haml +0 -50
  97. data/lib/flapjack/gateways/web/views/index.haml +0 -32
  98. data/lib/flapjack/gateways/web/views/self_stats.haml +0 -70
  99. data/spec/lib/flapjack/gateways/web/views/index.haml_spec.rb +0 -13
  100. data/spec/support/haml_view_helper.rb +0 -15
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'yajl/json_gem'
3
+ require 'oj'
4
4
  require 'active_support/time'
5
5
  require 'ice_cube'
6
6
  require 'flapjack/utility'
@@ -129,11 +129,11 @@ module Flapjack
129
129
  json_rule_data = {
130
130
  :id => rule_data[:id].to_s,
131
131
  :contact_id => rule_data[:contact_id].to_s,
132
- :entities => Yajl::Encoder.encode(rule_data[:entities]),
133
- :entity_tags => Yajl::Encoder.encode(rule_data[:entity_tags]),
134
- :time_restrictions => Yajl::Encoder.encode(rule_data[:time_restrictions]),
135
- :warning_media => Yajl::Encoder.encode(rule_data[:warning_media]),
136
- :critical_media => Yajl::Encoder.encode(rule_data[:critical_media]),
132
+ :entities => Oj.dump(rule_data[:entities]),
133
+ :entity_tags => Oj.dump(rule_data[:entity_tags]),
134
+ :time_restrictions => Oj.dump(rule_data[:time_restrictions]),
135
+ :warning_media => Oj.dump(rule_data[:warning_media]),
136
+ :critical_media => Oj.dump(rule_data[:critical_media]),
137
137
  :warning_blackhole => rule_data[:warning_blackhole],
138
138
  :critical_blackhole => rule_data[:critical_blackhole],
139
139
  }
@@ -284,11 +284,11 @@ module Flapjack
284
284
  rule_data = @redis.hgetall("notification_rule:#{@id}")
285
285
 
286
286
  @contact_id = rule_data['contact_id']
287
- @entity_tags = Yajl::Parser.parse(rule_data['entity_tags'] || '')
288
- @entities = Yajl::Parser.parse(rule_data['entities'] || '')
289
- @time_restrictions = Yajl::Parser.parse(rule_data['time_restrictions'] || '')
290
- @warning_media = Yajl::Parser.parse(rule_data['warning_media'] || '')
291
- @critical_media = Yajl::Parser.parse(rule_data['critical_media'] || '')
287
+ @entity_tags = Oj.load(rule_data['entity_tags'] || '')
288
+ @entities = Oj.load(rule_data['entities'] || '')
289
+ @time_restrictions = Oj.load(rule_data['time_restrictions'] || '')
290
+ @warning_media = Oj.load(rule_data['warning_media'] || '')
291
+ @critical_media = Oj.load(rule_data['critical_media'] || '')
292
292
  @warning_blackhole = ((rule_data['warning_blackhole'] || 'false').downcase == 'true')
293
293
  @critical_blackhole = ((rule_data['critical_blackhole'] || 'false').downcase == 'true')
294
294
  end
@@ -21,8 +21,8 @@ module Flapjack
21
21
  if ec.nil?
22
22
  @logger.error "Filter: Acknowledgement: unknown entity for event '#{event.id}'"
23
23
  else
24
- ec.create_unscheduled_maintenance(:start_time => timestamp,
25
- :duration => (event.duration || (4 * 60 * 60)),
24
+ ec.create_unscheduled_maintenance(timestamp,
25
+ (event.duration || (4 * 60 * 60)),
26
26
  :summary => event.summary)
27
27
  message = "unscheduled maintenance created for #{event.id}"
28
28
  end
@@ -40,7 +40,7 @@ module Flapjack
40
40
  end
41
41
 
42
42
  # end any unscheduled downtime
43
- entity_check.end_unscheduled_maintenance
43
+ entity_check.end_unscheduled_maintenance(Time.now.to_i)
44
44
  end
45
45
 
46
46
  @logger.debug("Filter: Ok: #{result ? "block" : "pass"}")
@@ -21,6 +21,7 @@ module Flapjack
21
21
  def status
22
22
  {'name' => @entity_check.check,
23
23
  'state' => @entity_check.state,
24
+ 'enabled' => @entity_check.enabled?,
24
25
  'summary' => @entity_check.summary,
25
26
  'details' => @entity_check.details,
26
27
  'in_unscheduled_maintenance' => @entity_check.in_unscheduled_maintenance?,
@@ -222,8 +222,8 @@ module Flapjack
222
222
  halt( err(403, "start time must be provided") ) unless start_time
223
223
 
224
224
  act_proc = proc {|entity_check|
225
- entity_check.create_scheduled_maintenance(:start_time => start_time,
226
- :duration => params[:duration].to_i, :summary => params[:summary])
225
+ entity_check.create_scheduled_maintenance(start_time,
226
+ params[:duration].to_i, :summary => params[:summary])
227
227
  }
228
228
 
229
229
  bulk_api_check_action(entities, checks, act_proc)
@@ -272,10 +272,8 @@ module Flapjack
272
272
  opts = {}
273
273
  proc {|entity_check| entity_check.end_scheduled_maintenance(start_time.to_i) }
274
274
  when 'unscheduled_maintenances'
275
- end_time = validate_and_parsetime(params[:end_time])
276
- opts = {}
277
- opts[:end_time] = end_time.to_i if end_time
278
- proc {|entity_check| entity_check.end_unscheduled_maintenance(opts) }
275
+ end_time = validate_and_parsetime(params[:end_time]) || Time.now
276
+ proc {|entity_check| entity_check.end_unscheduled_maintenance(end_time.to_i) }
279
277
  end
280
278
 
281
279
  bulk_api_check_action(entities, checks, act_proc)
@@ -9,7 +9,7 @@ module Rack
9
9
  env['rack.request.form_input'] = env['rack.input']
10
10
  data = env['rack.input'].read
11
11
  env['rack.input'].rewind
12
- env['rack.request.form_hash'] = data.empty? ? {} : JSON.parse(data)
12
+ env['rack.request.form_hash'] = data.empty? ? {} : Oj.load(data)
13
13
  end
14
14
  app.call(env)
15
15
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'mail'
4
4
  require 'erb'
5
- require 'haml'
6
5
  require 'socket'
7
6
 
8
7
  require 'em-synchrony'
@@ -113,10 +112,9 @@ module Flapjack
113
112
  text_template = ERB.new(File.read(File.dirname(__FILE__) +
114
113
  '/email/alert.text.erb'))
115
114
 
116
- haml_engine = Haml::Engine.new(File.read(File.dirname(__FILE__) +
117
- '/email/alert.html.haml'))
115
+ html_template = ERB.new(File.read(File.dirname(__FILE__) +
116
+ '/email/alert.html.erb'))
118
117
 
119
- mail_scope = self
120
118
  bnd = binding
121
119
 
122
120
  # this part is the only use of the mail gem -- maybe this can be done
@@ -133,7 +131,7 @@ module Flapjack
133
131
 
134
132
  html_part do
135
133
  content_type 'text/html; charset=UTF-8'
136
- body haml_engine.render(mail_scope)
134
+ body html_template.result(bnd)
137
135
  end
138
136
  end
139
137
  end
@@ -10,11 +10,9 @@ require 'em-synchrony'
10
10
  require 'redis/connection/synchrony'
11
11
  require 'redis'
12
12
 
13
- require 'chronic_duration'
14
-
15
13
  require 'blather/client/client'
16
- require 'em-synchrony/fiber_iterator'
17
- require 'yajl/json_gem'
14
+ require 'chronic_duration'
15
+ require 'oj'
18
16
 
19
17
  require 'flapjack/data/entity_check'
20
18
  require 'flapjack/redis_pool'
@@ -28,11 +26,27 @@ module Flapjack
28
26
  class Jabber < Blather::Client
29
27
  include Flapjack::Utility
30
28
 
31
- log = Logger.new(STDOUT)
32
- # log.level = Logger::DEBUG
33
- log.level = Logger::INFO
29
+ log = ::Logger.new(STDOUT)
30
+ log.level = ::Logger::INFO
34
31
  Blather.logger = log
35
32
 
33
+ # TODO if we use 'xmpp4r' rather than 'blather', port this to 'rexml'
34
+ class TextHandler < Nokogiri::XML::SAX::Document
35
+ def initialize
36
+ @chunks = []
37
+ end
38
+
39
+ attr_reader :chunks
40
+
41
+ def cdata_block(string)
42
+ characters(string)
43
+ end
44
+
45
+ def characters(string)
46
+ @chunks << string.strip if string.strip != ""
47
+ end
48
+ end
49
+
36
50
  def initialize(opts = {})
37
51
  @config = opts[:config]
38
52
  @redis_config = opts[:redis_config]
@@ -49,7 +63,7 @@ module Flapjack
49
63
 
50
64
  def stop
51
65
  @should_quit = true
52
- @redis.rpush(@config['queue'], JSON.generate('notification_type' => 'shutdown'))
66
+ @redis.rpush(@config['queue'], Oj.dump('notification_type' => 'shutdown'))
53
67
  end
54
68
 
55
69
  def setup
@@ -116,12 +130,18 @@ module Flapjack
116
130
  end
117
131
  end
118
132
 
119
- def interpreter(command)
133
+ def interpreter(command_raw)
120
134
  msg = nil
121
135
  action = nil
122
136
  entity_check = nil
123
- case
124
- when command =~ /^ACKID\s+(\d+)(?:\s*(.*?)(?:\s*duration:.*?(\w+.*))?)$/i;
137
+
138
+ th = TextHandler.new
139
+ parser = Nokogiri::HTML::SAX::Parser.new(th)
140
+ parser.parse(command_raw)
141
+ command = th.chunks.join(' ')
142
+
143
+ case command
144
+ when /^ACKID\s+(\d+)(?:\s*(.*?)(?:\s*duration:.*?(\w+.*))?)$/i
125
145
  ackid = $1
126
146
  comment = $2
127
147
  duration_str = $3
@@ -171,16 +191,16 @@ module Flapjack
171
191
  }
172
192
  end
173
193
 
174
- when command =~ /^help$/
194
+ when /^help$/i
175
195
  msg = "commands: \n" +
176
196
  " ACKID <id> <comment> [duration: <time spec>] \n" +
177
197
  " find entities matching /pattern/ \n" +
178
198
  " test notifications for <entity>[:<check>] \n" +
179
- " tell me about <entity>[:<check>]" +
199
+ " tell me about <entity>[:<check>] \n" +
180
200
  " identify \n" +
181
201
  " help \n"
182
202
 
183
- when command =~ /^identify$/
203
+ when /^identify$/i
184
204
  t = Process.times
185
205
  fqdn = `/bin/hostname -f`.chomp
186
206
  pid = Process.pid
@@ -190,7 +210,7 @@ module Flapjack
190
210
  "System CPU Time: #{t.stime}\n" +
191
211
  `uname -a`.chomp + "\n"
192
212
 
193
- when command =~ /^test notifications for\s+([a-z0-9\-\.]+)(?::(.+))?$/i
213
+ when /^test notifications for\s+([a-z0-9\-\.]+)(?::(.+))?$/i
194
214
  entity_name = $1
195
215
  check_name = $2 || 'test'
196
216
 
@@ -203,7 +223,7 @@ module Flapjack
203
223
  msg = "yeah, no I can't see #{entity_name} in my systems"
204
224
  end
205
225
 
206
- when command =~ /^tell me about\s+([a-z0-9\-\.]+)(?::(.+))?$+/
226
+ when /^tell me about\s+([a-z0-9\-\.]+)(?::(.+))?$+/i
207
227
  entity_name = $1
208
228
  check_name = $2
209
229
 
@@ -216,27 +236,38 @@ module Flapjack
216
236
  get_details = proc {|entity_check|
217
237
  sched = entity_check.current_maintenance(:scheduled => true)
218
238
  unsched = entity_check.current_maintenance(:unscheduled => true)
239
+ out = ''
219
240
 
220
- if (sched || unsched) && check_name.nil?
241
+ if check_name.nil?
221
242
  check = entity_check.check
222
- msg += "---\n#{entity_name}:#{check}\n"
243
+ out += "---\n#{entity_name}:#{check}\n"
223
244
  end
224
245
 
225
- unless sched.nil?
226
- start = Time.at(sched[:start_time])
227
- finish = Time.at(sched[:start_time] + sched[:duration])
228
- remain = time_period_in_words( (finish - current_time).ceil )
229
- # TODO a simpler time format?
230
- msg += "Currently in scheduled maintenance: #{start} -> #{finish} (#{remain} remaining)\n"
231
- end
246
+ if sched.nil? && unsched.nil?
247
+ out += "Not in scheduled or unscheduled maintenance.\n"
248
+ else
249
+ if sched.nil?
250
+ out += "Not in scheduled maintenance.\n"
251
+ else
252
+ start = Time.at(sched[:start_time])
253
+ finish = Time.at(sched[:start_time] + sched[:duration])
254
+ remain = time_period_in_words( (finish - current_time).ceil )
255
+ # TODO a simpler time format?
256
+ out += "In scheduled maintenance: #{start} -> #{finish} (#{remain} remaining)\n"
257
+ end
232
258
 
233
- unless unsched.nil?
234
- start = Time.at(unsched[:start_time])
235
- finish = Time.at(unsched[:start_time] + unsched[:duration])
236
- remain = time_period_in_words( (finish - current_time).ceil )
237
- # TODO a simpler time format?
238
- msg += "Currently in unscheduled maintenance: #{start} -> #{finish} (#{remain} remaining)\n"
259
+ if unsched.nil?
260
+ out += "Not in unscheduled maintenance.\n"
261
+ else
262
+ start = Time.at(unsched[:start_time])
263
+ finish = Time.at(unsched[:start_time] + unsched[:duration])
264
+ remain = time_period_in_words( (finish - current_time).ceil )
265
+ # TODO a simpler time format?
266
+ out += "In unscheduled maintenance: #{start} -> #{finish} (#{remain} remaining)\n"
267
+ end
239
268
  end
269
+
270
+ out
240
271
  }
241
272
 
242
273
  check_names = check_name.nil? ? entity.check_list.sort : [check_name]
@@ -247,14 +278,14 @@ module Flapjack
247
278
  check_names.each do |check|
248
279
  entity_check = Flapjack::Data::EntityCheck.for_entity(entity, check, :redis => @redis)
249
280
  next if entity_check.nil?
250
- get_details.call(entity_check)
281
+ msg += get_details.call(entity_check)
251
282
  end
252
283
  end
253
284
  else
254
285
  msg = "hmmm, I can't see #{entity_name} in my systems"
255
286
  end
256
287
 
257
- when command =~ /^(find )?entities matching\s+\/(.*)\/.*$/i
288
+ when /^(find )?entities matching\s+\/(.*)\/.*$/i
258
289
  pattern = $2.chomp.strip
259
290
  entity_list = Flapjack::Data::Entity.find_all_name_matching(pattern, :redis => @redis)
260
291
 
@@ -279,7 +310,7 @@ module Flapjack
279
310
  msg = "that doesn't seem to be a valid pattern - /#{pattern}/"
280
311
  end
281
312
 
282
- when command =~ /^(.*)/
313
+ when /^(.*)/
283
314
  words = $1
284
315
  msg = "what do you mean, '#{words}'? Type 'help' for a list of acceptable commands."
285
316
 
@@ -399,7 +430,7 @@ module Flapjack
399
430
  if connected?
400
431
  @logger.debug("jabber is connected so commencing blpop on #{queue}")
401
432
  events[queue] = @redis.blpop(queue, 0)
402
- event = Yajl::Parser.parse(events[queue][1])
433
+ event = Oj.load(events[queue][1])
403
434
  type = event['notification_type'] || 'unknown'
404
435
  @logger.debug('jabber notification event received')
405
436
  @logger.debug(event.inspect)
@@ -4,8 +4,7 @@ require 'socket'
4
4
  require 'eventmachine'
5
5
  require 'em-synchrony'
6
6
  require 'blather/client/client'
7
- require 'em-synchrony/fiber_iterator'
8
- require 'yajl/json_gem'
7
+ require 'oj'
9
8
 
10
9
  require 'flapjack/utility'
11
10
 
@@ -16,9 +15,8 @@ module Flapjack
16
15
  class Oobetet < Blather::Client
17
16
  include Flapjack::Utility
18
17
 
19
- log = Logger.new(STDOUT)
20
- # log.level = Logger::DEBUG
21
- log.level = Logger::INFO
18
+ log = ::Logger.new(STDOUT)
19
+ log.level = ::Logger::INFO
22
20
  Blather.logger = log
23
21
 
24
22
  def initialize(opts = {})
@@ -212,9 +210,9 @@ module Flapjack
212
210
  end
213
211
 
214
212
  def send_pagerduty_event(event)
215
- options = { :body => Yajl::Encoder.encode(event) }
213
+ options = { :body => Oj.dump(event) }
216
214
  http = EM::HttpRequest.new(@pagerduty_events_api_url).post(options)
217
- response = Yajl::Parser.parse(http.response)
215
+ response = Oj.load(http.response)
218
216
  status = http.response_header.status
219
217
  @logger.debug "send_pagerduty_event got a return code of #{status.to_s} - #{response.inspect}"
220
218
  [status, response]
@@ -3,7 +3,7 @@
3
3
  require 'em-synchrony'
4
4
  require 'em-synchrony/em-http'
5
5
 
6
- require 'yajl/json_gem'
6
+ require 'oj'
7
7
 
8
8
  require 'flapjack/data/entity_check'
9
9
  require 'flapjack/data/global'
@@ -32,7 +32,7 @@ module Flapjack
32
32
  def stop
33
33
  @logger.info("stopping")
34
34
  @should_quit = true
35
- @redis.rpush(@config['queue'], JSON.generate('notification_type' => 'shutdown'))
35
+ @redis.rpush(@config['queue'], Oj.dump('notification_type' => 'shutdown'))
36
36
  end
37
37
 
38
38
  def start
@@ -56,7 +56,7 @@ module Flapjack
56
56
  until @should_quit
57
57
  @logger.debug("pagerduty gateway is going into blpop mode on #{queue}")
58
58
  events[queue] = @redis.blpop(queue, 0)
59
- event = Yajl::Parser.parse(events[queue][1])
59
+ event = Oj.load(events[queue][1])
60
60
  type = event['notification_type']
61
61
  @logger.debug("pagerduty notification event popped off the queue: " + event.inspect)
62
62
  unless 'shutdown'.eql?(type)
@@ -134,9 +134,9 @@ module Flapjack
134
134
  end
135
135
 
136
136
  def send_pagerduty_event(event)
137
- options = { :body => Yajl::Encoder.encode(event) }
137
+ options = { :body => Oj.dump(event) }
138
138
  http = EM::HttpRequest.new(PAGERDUTY_EVENTS_API_URL).post(options)
139
- response = Yajl::Parser.parse(http.response)
139
+ response = Oj.load(http.response)
140
140
  status = http.response_header.status
141
141
  @logger.debug "send_pagerduty_event got a return code of #{status.to_s} - #{response.inspect}"
142
142
  [status, response]
@@ -215,8 +215,8 @@ module Flapjack
215
215
 
216
216
  http = EM::HttpRequest.new(url).get(options)
217
217
  begin
218
- response = Yajl::Parser.parse(http.response)
219
- rescue Yajl::ParseError
218
+ response = Oj.load(http.response)
219
+ rescue Oj::Error
220
220
  @logger.error("failed to parse json from a post to #{url} ... response headers and body follows...")
221
221
  return nil
222
222
  end
@@ -3,8 +3,9 @@
3
3
  require 'chronic'
4
4
  require 'chronic_duration'
5
5
  require 'sinatra/base'
6
- require 'haml'
6
+ require 'erb'
7
7
  require 'rack/fiber_pool'
8
+ require 'json'
8
9
 
9
10
  require 'flapjack/rack_logger'
10
11
 
@@ -61,6 +62,16 @@ module Flapjack
61
62
  set :views, settings.root + '/web/views'
62
63
  set :public_folder, settings.root + '/web/public'
63
64
 
65
+ helpers do
66
+ def h(text)
67
+ ERB::Util.h(text)
68
+ end
69
+
70
+ def u(text)
71
+ ERB::Util.u(text)
72
+ end
73
+ end
74
+
64
75
  def redis
65
76
  self.class.instance_variable_get('@redis')
66
77
  end
@@ -72,39 +83,48 @@ module Flapjack
72
83
  get '/' do
73
84
  check_stats
74
85
  entity_stats
75
- haml :index
86
+
87
+ erb 'index.html'.to_sym
76
88
  end
77
89
 
78
90
  get '/checks_all' do
79
91
  check_stats
80
92
  @adjective = 'all'
81
93
 
82
- # TODO (?) recast as Entity.all do |e|; e.checks.do |ec|; ...
83
- @states = redis.keys('*:*:states').map { |r|
84
- entity, check = r.sub(/:states$/, '').split(':', 2)
85
- [entity, check] + entity_check_state(entity, check)
86
- }.compact.sort_by {|parts| parts }
94
+ checks_by_entity = Flapjack::Data::EntityCheck.find_all_by_entity(:redis => redis)
95
+ @states = checks_by_entity.keys.inject({}) {|result, entity|
96
+ result[entity] = checks_by_entity[entity].sort.map {|check|
97
+ [check] + entity_check_state(entity, check)
98
+ }
99
+ result
100
+ }
101
+ @entities_sorted = checks_by_entity.keys.sort
87
102
 
88
- haml :checks
103
+ erb 'checks.html'.to_sym
89
104
  end
90
105
 
91
106
  get '/checks_failing' do
92
107
  check_stats
93
108
  @adjective = 'failing'
94
109
 
95
- @states = redis.zrange('failed_checks', 0, -1).map {|key|
96
- parts = key.split(':', 2)
97
- [parts[0], parts[1]] + entity_check_state(parts[0], parts[1])
98
- }.compact.sort_by {|parts| parts}
110
+ checks_by_entity = Flapjack::Data::EntityCheck.find_all_failing_by_entity(:redis => redis)
111
+ @states = checks_by_entity.keys.inject({}) {|result, entity|
112
+ result[entity] = checks_by_entity[entity].sort.map {|check|
113
+ [check] + entity_check_state(entity, check)
114
+ }
115
+ result
116
+ }
117
+ @entities_sorted = checks_by_entity.keys.sort
99
118
 
100
- haml :checks
119
+ erb 'checks.html'.to_sym
101
120
  end
102
121
 
103
122
  get '/self_stats' do
104
123
  self_stats
105
124
  entity_stats
106
125
  check_stats
107
- haml :self_stats
126
+
127
+ erb 'self_stats.html'.to_sym
108
128
  end
109
129
 
110
130
  get '/self_stats.json' do
@@ -124,13 +144,6 @@ module Flapjack
124
144
  'failure' => @event_counters['failure'].to_i,
125
145
  'action' => @event_counters['action'].to_i,
126
146
  }
127
- # 'instance' => {
128
- # 'total' => @event_counters_instance['all'].to_i,
129
- # 'ok' => @event_counters_instance['ok'].to_i,
130
- # 'failure' => @event_counters_instance['failure'].to_i,
131
- # 'action' => @event_counters_instance['action'].to_i,
132
- # 'average' => @event_rate_all.to_i,
133
- # }
134
147
  },
135
148
  'total_keys' => @dbsize,
136
149
  'uptime' => @uptime_string,
@@ -144,24 +157,26 @@ module Flapjack
144
157
  entity_stats
145
158
  @adjective = 'all'
146
159
  @entities = Flapjack::Data::Entity.find_all_with_checks(:redis => redis)
147
- haml :entities
160
+
161
+ erb 'entities.html'.to_sym
148
162
  end
149
163
 
150
164
  get '/entities_failing' do
151
165
  entity_stats
152
166
  @adjective = 'failing'
153
167
  @entities = Flapjack::Data::Entity.find_all_with_failing_checks(:redis => redis)
154
- haml :entities
168
+
169
+ erb 'entities.html'.to_sym
155
170
  end
156
171
 
157
172
  get '/entity/:entity' do
158
173
  @entity = params[:entity]
159
174
  entity_stats
160
- @states = redis.keys("#{@entity}:*:states").map { |r|
161
- check = r.sub(/^#{@entity}:/, '').sub(/:states$/, '')
162
- [@entity, check] + entity_check_state(@entity, check)
163
- }.compact.sort_by {|parts| parts }
164
- haml :entity
175
+ @states = Flapjack::Data::EntityCheck.find_all_for_entity_name(@entity, :redis => redis).sort.map { |check|
176
+ [check] + entity_check_state(@entity, check)
177
+ }.sort_by {|parts| parts }
178
+
179
+ erb 'entity.html'.to_sym
165
180
  end
166
181
 
167
182
  get '/check' do
@@ -176,11 +191,14 @@ module Flapjack
176
191
  last_change = entity_check.last_change
177
192
 
178
193
  @check_state = entity_check.state
194
+ @check_enabled = entity_check.enabled?
179
195
  @check_last_update = entity_check.last_update
180
196
  @check_last_change = last_change
181
197
  @check_summary = entity_check.summary
182
198
  @check_details = entity_check.details
183
- @last_notifications = entity_check.last_notifications_of_each_type
199
+
200
+ @last_notifications = last_notification_data(entity_check)
201
+
184
202
  @scheduled_maintenances = entity_check.maintenances(nil, nil, :scheduled => true)
185
203
  @acknowledgement_id = entity_check.failed? ?
186
204
  entity_check.event_count_at(entity_check.last_change) : nil
@@ -193,7 +211,7 @@ module Flapjack
193
211
  @state_changes = entity_check.historical_states(nil, Time.now.to_i,
194
212
  :order => 'desc', :limit => 20)
195
213
 
196
- haml :check
214
+ erb 'check.html'.to_sym
197
215
  end
198
216
 
199
217
  post '/acknowledgements/:entity/:check' do
@@ -256,14 +274,23 @@ module Flapjack
256
274
  redirect back
257
275
  end
258
276
 
277
+ # delete a check (actually just disables it)
278
+ delete '/checks/:entity/:check' do
279
+ entity_check = get_entity_check(params[:entity], params[:check])
280
+ return 404 if entity_check.nil?
281
+
282
+ entity_check.disable!
283
+ redirect back
284
+ end
285
+
259
286
  get '/contacts' do
260
287
  #self_stats
261
288
  @contacts = Flapjack::Data::Contact.all(:redis => redis)
262
- haml :contacts
289
+
290
+ erb 'contacts.html'.to_sym
263
291
  end
264
292
 
265
293
  get "/contacts/:contact" do
266
- #self_stats
267
294
  contact_id = params[:contact]
268
295
 
269
296
  if contact_id
@@ -279,17 +306,22 @@ module Flapjack
279
306
  @pagerduty_credentials = @contact.pagerduty_credentials
280
307
  end
281
308
 
309
+ # FIXME: intersect with current checks, or push down to Contact.entities
282
310
  @entities_and_checks = @contact.entities(:checks => true).sort_by {|ec|
283
311
  ec[:entity].name
284
312
  }
285
313
 
286
- haml :contact
314
+ erb 'contact.html'.to_sym
287
315
  end
288
316
 
289
317
  protected
290
318
 
291
- def render_haml(file, scope)
292
- Haml::Engine.new(File.read(File.dirname(__FILE__) + '/web/views/' + file)).render(scope)
319
+ # TODO cache constructed erb object to improve performance -- check mtime
320
+ # to know when to refresh; would need to synchronize accesses to the cache,
321
+ # to lock out reads while it's being refreshed
322
+ def render_erb(file, bind)
323
+ erb = ERB.new(File.read(File.dirname(__FILE__) + '/web/views/' + file))
324
+ erb.result(bind)
293
325
  end
294
326
 
295
327
  private
@@ -307,18 +339,33 @@ module Flapjack
307
339
  return if entity.nil?
308
340
  entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
309
341
  check, :redis => redis)
342
+ summary = entity_check.summary
343
+ summary = summary[0..76] + '...' unless summary.length < 81
310
344
  latest_notif =
311
345
  {:problem => entity_check.last_notification_for_state(:problem)[:timestamp],
312
346
  :recovery => entity_check.last_notification_for_state(:recovery)[:timestamp],
313
347
  :acknowledgement => entity_check.last_notification_for_state(:acknowledgement)[:timestamp]
314
348
  }.max_by {|n| n[1] || 0}
349
+
350
+ lc = entity_check.last_change
351
+ last_change = lc ? ChronicDuration.output(Time.now.to_i - lc.to_i,
352
+ :format => :short, :keep_zero => true, :units => 2) : 'never'
353
+
354
+ lu = entity_check.last_update
355
+ last_update = lu ? ChronicDuration.output(Time.now.to_i - lu.to_i,
356
+ :format => :short, :keep_zero => true, :units => 2) : 'never'
357
+
358
+ ln = latest_notif[1]
359
+ last_notified = ln ? ChronicDuration.output(Time.now.to_i - ln.to_i,
360
+ :format => :short, :keep_zero => true, :units => 2) + ", #{latest_notif[0]}" : 'never'
361
+
315
362
  [(entity_check.state || '-'),
316
- (entity_check.last_change || '-'),
317
- (entity_check.last_update || '-'),
363
+ (summary || '-'),
364
+ last_change,
365
+ last_update,
318
366
  entity_check.in_unscheduled_maintenance?,
319
367
  entity_check.in_scheduled_maintenance?,
320
- latest_notif[0],
321
- latest_notif[1]
368
+ last_notified
322
369
  ]
323
370
  end
324
371
 
@@ -347,10 +394,23 @@ module Flapjack
347
394
  end
348
395
 
349
396
  def check_stats
350
- @count_all_checks = redis.keys('check:*:*').length
351
- @count_failing_checks = redis.zcard 'failed_checks'
397
+ # FIXME: move this logic to Flapjack::Data::EntityCheck
398
+ @count_all_checks = Flapjack::Data::EntityCheck.count_all(:redis => redis)
399
+ @count_failing_checks = Flapjack::Data::EntityCheck.count_all_failing(:redis => redis)
352
400
  end
353
401
 
402
+ def last_notification_data(entity_check)
403
+ last_notifications = entity_check.last_notifications_of_each_type
404
+ [:critical, :warning, :unknown, :recovery, :acknowledgement].inject({}) do |memo, type|
405
+ if last_notifications[type] && last_notifications[type][:timestamp]
406
+ t = Time.at(last_notifications[type][:timestamp])
407
+ memo[t] = {:time => t.to_s,
408
+ :relative => relative_time_ago(t) + " ago",
409
+ :summary => last_notifications[type][:summary]}
410
+ end
411
+ memo
412
+ end
413
+ end
354
414
 
355
415
  end
356
416