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
@@ -235,10 +235,11 @@ module Flapjack
235
235
  end
236
236
 
237
237
  # drop notifications for
238
- def drop_notifications?(opts)
238
+ def drop_notifications?(opts = {})
239
239
  media = opts[:media]
240
240
  check = opts[:check]
241
241
  state = opts[:state]
242
+
242
243
  # build it and they will come
243
244
  @redis.exists("drop_alerts_for_contact:#{self.id}") ||
244
245
  (media && @redis.exists("drop_alerts_for_contact:#{self.id}:#{media}")) ||
@@ -248,7 +249,7 @@ module Flapjack
248
249
  @redis.exists("drop_alerts_for_contact:#{self.id}:#{media}:#{check}:#{state}"))
249
250
  end
250
251
 
251
- def update_sent_alert_keys(opts)
252
+ def update_sent_alert_keys(opts = {})
252
253
  media = opts[:media]
253
254
  check = opts[:check]
254
255
  state = opts[:state]
@@ -97,12 +97,12 @@ module Flapjack
97
97
 
98
98
  def self.find_all_with_checks(options)
99
99
  raise "Redis connection not set" unless redis = options[:redis]
100
- redis.keys("check:*").map {|s| s.sub(/^check:/, '').split(':', 2).first }.to_set
100
+ redis.zrange("current_entities", 0, -1)
101
101
  end
102
102
 
103
103
  def self.find_all_with_failing_checks(options)
104
104
  raise "Redis connection not set" unless redis = options[:redis]
105
- redis.zrange("failed_checks", 0, -1).map {|s| s.split(':', 2).first }.to_set
105
+ Flapjack::Data::EntityCheck.find_all_failing_by_entity(:redis => redis).keys
106
106
  end
107
107
 
108
108
  def contacts
@@ -119,7 +119,7 @@ module Flapjack
119
119
  end
120
120
 
121
121
  def check_list
122
- @redis.keys("check:#{@name}:*").map {|k| k =~ /^check:#{@name}:(.+)$/; $1}
122
+ @redis.zrange("current_checks:#{@name}", 0, -1)
123
123
  end
124
124
 
125
125
  def check_count
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'yajl/json_gem'
3
+ require 'oj'
4
4
 
5
5
  require 'flapjack/patches'
6
6
 
@@ -53,6 +53,67 @@ module Flapjack
53
53
  self.new(entity, check, :redis => redis)
54
54
  end
55
55
 
56
+ def self.find_all_for_entity_name(entity_name, options = {})
57
+ raise "Redis connection not set" unless redis = options[:redis]
58
+ redis.zrange("current_checks:#{entity_name}", 0, -1)
59
+ end
60
+
61
+ def self.find_all(options = {})
62
+ raise "Redis connection not set" unless redis = options[:redis]
63
+ self.conflate_to_keys(self.find_all_by_entity(:redis => redis))
64
+ end
65
+
66
+ def self.find_all_by_entity(options = {})
67
+ raise "Redis connection not set" unless redis = options[:redis]
68
+ d = {}
69
+ redis.zrange("current_entities", 0, -1).each {|entity|
70
+ d[entity] = redis.zrange("current_checks:#{entity}", 0, -1)
71
+ }
72
+ d
73
+ end
74
+
75
+ def self.count_all(options = {})
76
+ raise "Redis connection not set" unless redis = options[:redis]
77
+ redis.zrange("current_entities", 0, -1).inject(0) {|memo, entity|
78
+ memo + redis.zcount("current_checks:#{entity}", '-inf', '+inf')
79
+ }
80
+ end
81
+
82
+ def self.find_all_failing(options = {})
83
+ raise "Redis connection not set" unless redis = options[:redis]
84
+ self.conflate_to_keys(self.find_all_failing_by_entity(:redis => redis))
85
+ end
86
+
87
+ def self.find_all_failing_by_entity(options = {})
88
+ raise "Redis connection not set" unless redis = options[:redis]
89
+ redis.zrange("failed_checks", 0, -1).inject({}) do |memo, key|
90
+ entity, check = key.split(':', 2)
91
+ if !!redis.zscore("current_checks:#{entity}", check)
92
+ memo[entity] ||= []
93
+ memo[entity] << check
94
+ end
95
+ memo
96
+ end
97
+ end
98
+
99
+ def self.count_all_failing(options = {})
100
+ raise "Redis connection not set" unless redis = options[:redis]
101
+ redis.zrange("failed_checks", 0, -1).count do |key|
102
+ entity, check = key.split(':', 2)
103
+ !!redis.zscore("current_checks:#{entity}", check)
104
+ end
105
+ end
106
+
107
+ def self.conflate_to_keys(entity_checks_hash)
108
+ result = []
109
+ entity_checks_hash.each {|entity, checks|
110
+ checks.each {|check|
111
+ result << "#{entity}:#{check}"
112
+ }
113
+ }
114
+ result
115
+ end
116
+
56
117
  def entity_name
57
118
  entity.name
58
119
  end
@@ -69,7 +130,7 @@ module Flapjack
69
130
  end
70
131
 
71
132
  # return data about current maintenance (scheduled or unscheduled, as specified)
72
- def current_maintenance(opts)
133
+ def current_maintenance(opts = {})
73
134
  sched = opts[:scheduled] ? 'scheduled' : 'unscheduled'
74
135
  ts = @redis.get("#{@key}:#{sched}_maintenance")
75
136
  return unless ts
@@ -79,14 +140,14 @@ module Flapjack
79
140
  }
80
141
  end
81
142
 
82
- def create_unscheduled_maintenance(opts = {})
83
- end_unscheduled_maintenance if in_unscheduled_maintenance?
143
+ def create_unscheduled_maintenance(start_time, duration, opts = {})
144
+ raise ArgumentError, 'start time must be provided as a Unix timestamp' unless start_time && start_time.is_a?(Integer)
145
+ raise ArgumentError, 'duration in seconds must be provided' unless duration && duration.is_a?(Integer) && (duration > 0)
84
146
 
85
- start_time = opts[:start_time] # unix timestamp
86
- duration = opts[:duration] # seconds
87
147
  summary = opts[:summary]
88
148
  time_remaining = (start_time + duration) - Time.now.to_i
89
149
  if time_remaining > 0
150
+ end_unscheduled_maintenance(start_time) if in_unscheduled_maintenance?
90
151
  @redis.setex("#{@key}:unscheduled_maintenance", time_remaining, start_time)
91
152
  end
92
153
  @redis.zadd("#{@key}:unscheduled_maintenances", duration, start_time)
@@ -96,19 +157,14 @@ module Flapjack
96
157
  end
97
158
 
98
159
  # ends any unscheduled maintenance
99
- def end_unscheduled_maintenance(opts = {})
100
- defaults = {
101
- :end_time => Time.now.to_i
102
- }
103
- options = defaults.merge(opts)
104
- end_time = options[:end_time]
160
+ def end_unscheduled_maintenance(end_time)
161
+ raise ArgumentError, 'end time must be provided as a Unix timestamp' unless end_time && end_time.is_a?(Integer)
105
162
 
106
163
  if (um_start = @redis.get("#{@key}:unscheduled_maintenance"))
107
164
  duration = end_time - um_start.to_i
108
165
  @logger.debug("ending unscheduled downtime for #{@key} at #{Time.at(end_time).to_s}") if @logger
109
166
  @redis.del("#{@key}:unscheduled_maintenance")
110
- @redis.zadd("#{@key}:unscheduled_maintenances", duration, um_start)
111
- @redis.zadd("#{@key}:sorted_unscheduled_maintenance_timestamps", um_start, um_start)
167
+ @redis.zadd("#{@key}:unscheduled_maintenances", duration, um_start) # updates existing UM 'score'
112
168
  else
113
169
  @logger.debug("end_unscheduled_maintenance called for #{@key} but none found") if @logger
114
170
  end
@@ -118,10 +174,11 @@ module Flapjack
118
174
  # TODO: consider adding some validation to the data we're adding in here
119
175
  # eg start_time is a believable unix timestamp (not in the past and not too
120
176
  # far in the future), duration is within some bounds...
121
- def create_scheduled_maintenance(opts = {})
122
- start_time = opts[:start_time] # unix timestamp
123
- duration = opts[:duration] # seconds
124
- summary = opts[:summary]
177
+ def create_scheduled_maintenance(start_time, duration, opts = {})
178
+ raise ArgumentError, 'start time must be provided as a Unix timestamp' unless start_time && start_time.is_a?(Integer)
179
+ raise ArgumentError, 'duration in seconds must be provided' unless duration && duration.is_a?(Integer) && (duration > 0)
180
+
181
+ summary = opts[:summary]
125
182
  @redis.zadd("#{@key}:scheduled_maintenances", duration, start_time)
126
183
  @redis.set("#{@key}:#{start_time}:scheduled_maintenance:summary", summary)
127
184
 
@@ -131,6 +188,30 @@ module Flapjack
131
188
  update_current_scheduled_maintenance(:revalidate => true)
132
189
  end
133
190
 
191
+ # if not in scheduled maintenance, looks in scheduled maintenance list for a check to see if
192
+ # current state should be set to scheduled maintenance, and sets it as appropriate
193
+ def update_current_scheduled_maintenance(opts = {})
194
+ if opts[:revalidate]
195
+ @redis.del("#{@key}:scheduled_maintenance")
196
+ else
197
+ return if in_scheduled_maintenance?
198
+ end
199
+
200
+ # are we within a scheduled maintenance period?
201
+ current_time = Time.now.to_i
202
+ current_sched_ms = maintenances(nil, nil, :scheduled => true).select {|sm|
203
+ (sm[:start_time] <= current_time) && (current_time < sm[:end_time])
204
+ }
205
+ return if current_sched_ms.empty?
206
+
207
+ # yes! so set current scheduled maintenance
208
+ # if multiple scheduled maintenances found, find the end_time furthest in the future
209
+ most_futuristic = current_sched_ms.max {|sm| sm[:end_time] }
210
+ start_time = most_futuristic[:start_time]
211
+ duration = most_futuristic[:duration]
212
+ @redis.setex("#{@key}:scheduled_maintenance", duration.to_i, start_time)
213
+ end
214
+
134
215
  # TODO allow summary to be changed as part of the termination
135
216
  def end_scheduled_maintenance(start_time)
136
217
  raise ArgumentError, 'start time must be supplied as a Unix timestamp' unless start_time && start_time.is_a?(Integer)
@@ -162,32 +243,8 @@ module Flapjack
162
243
 
163
244
  return true
164
245
  end
165
-
166
- false
167
- end
168
246
 
169
- # if not in scheduled maintenance, looks in scheduled maintenance list for a check to see if
170
- # current state should be set to scheduled maintenance, and sets it as appropriate
171
- def update_current_scheduled_maintenance(opts = {})
172
- if opts[:revalidate]
173
- @redis.del("#{@key}:scheduled_maintenance")
174
- else
175
- return if in_scheduled_maintenance?
176
- end
177
-
178
- # are we within a scheduled maintenance period?
179
- current_time = Time.now.to_i
180
- current_sched_ms = maintenances(nil, nil, :scheduled => true).select {|sm|
181
- (sm[:start_time] <= current_time) && (current_time < sm[:end_time])
182
- }
183
- return if current_sched_ms.empty?
184
-
185
- # yes! so set current scheduled maintenance
186
- # if multiple scheduled maintenances found, find the end_time furthest in the future
187
- most_futuristic = current_sched_ms.max {|sm| sm[:end_time] }
188
- start_time = most_futuristic[:start_time]
189
- duration = most_futuristic[:duration]
190
- @redis.setex("#{@key}:scheduled_maintenance", duration.to_i, start_time)
247
+ false
191
248
  end
192
249
 
193
250
  # returns nil if no previous state; this must be considered as a possible
@@ -248,6 +305,22 @@ module Flapjack
248
305
 
249
306
  def last_update=(timestamp)
250
307
  @redis.hset("check:#{@key}", 'last_update', timestamp)
308
+ @redis.zadd("current_checks:#{entity.name}", timestamp, check)
309
+ @redis.zadd("current_entities", timestamp, entity.name)
310
+ end
311
+
312
+ # disables a check (removes currency)
313
+ def disable!
314
+ @logger.debug("disabling check [#{@key}]") if @logger
315
+ @redis.zrem("current_checks:#{entity.name}", check)
316
+ if @redis.zcount("current_checks:#{entity.name}", '-inf', '+inf') == 0
317
+ @redis.zrem("current_checks:#{entity.name}", check)
318
+ @redis.zrem("current_entities", entity.name)
319
+ end
320
+ end
321
+
322
+ def enabled?
323
+ !! @redis.zscore("current_checks:#{entity.name}", check)
251
324
  end
252
325
 
253
326
  def last_change
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'yajl/json_gem'
3
+ require 'oj'
4
4
 
5
5
  module Flapjack
6
6
  module Data
7
7
  class Event
8
8
 
9
- attr_accessor :previous_state, :previous_state_duration
9
+ attr_accessor :counter, :previous_state, :previous_state_duration
10
10
 
11
11
  attr_reader :check, :summary, :details, :acknowledgement_id
12
12
 
@@ -20,7 +20,7 @@ module Flapjack
20
20
  # Calling next with :block => false, will return a nil if there are no
21
21
  # events on the queue.
22
22
  #
23
- def self.next(opts={})
23
+ def self.next(queue, opts = {})
24
24
  raise "Redis connection not set" unless redis = opts[:redis]
25
25
 
26
26
  defaults = { :block => true,
@@ -31,23 +31,23 @@ module Flapjack
31
31
  if options[:archive_events]
32
32
  dest = "events_archive:#{Time.now.utc.strftime "%Y%m%d%H"}"
33
33
  if options[:block]
34
- raw = redis.brpoplpush('events', dest, 0)
34
+ raw = redis.brpoplpush(queue, dest, 0)
35
35
  else
36
- raw = redis.rpoplpush('events', dest)
36
+ raw = redis.rpoplpush(queue, dest)
37
37
  return unless raw
38
38
  end
39
39
  redis.expire(dest, options[:events_archive_maxage])
40
40
  else
41
41
  if options[:block]
42
- raw = redis.brpop('events', 0)[1]
42
+ raw = redis.brpop(queue, 0)[1]
43
43
  else
44
- raw = redis.rpop('events')
44
+ raw = redis.rpop(queue)
45
45
  return unless raw
46
46
  end
47
47
  end
48
48
  begin
49
- parsed = ::JSON.parse( raw )
50
- rescue => e
49
+ parsed = ::Oj.load( raw )
50
+ rescue Oj::Error => e
51
51
  if options[:logger]
52
52
  options[:logger].warn("Error deserialising event json: #{e}, raw json: #{raw.inspect}")
53
53
  end
@@ -65,7 +65,7 @@ module Flapjack
65
65
  raise "Redis connection not set" unless redis = opts[:redis]
66
66
 
67
67
  evt['time'] = Time.now.to_i if evt['time'].nil?
68
- redis.rpush('events', ::Yajl::Encoder.encode(evt))
68
+ redis.rpush('events', ::Oj.dump(evt))
69
69
  end
70
70
 
71
71
  # Provide a count of the number of events on the queue to be processed.
@@ -5,7 +5,6 @@
5
5
  # contact+media recipient.
6
6
 
7
7
  require 'flapjack/data/contact'
8
- require 'flapjack/data/notification'
9
8
 
10
9
  module Flapjack
11
10
  module Data
@@ -15,8 +14,8 @@ module Flapjack
15
14
 
16
15
  def self.for_contact(contact, opts = {})
17
16
  self.new(:contact => contact,
18
- :notification_contents => opts[:notification_contents],
19
- :medium => opts[:medium], :address => opts[:address],
17
+ :medium => opts[:medium],
18
+ :address => opts[:address],
20
19
  :duration => opts[:duration])
21
20
  end
22
21
 
@@ -36,15 +35,13 @@ module Flapjack
36
35
  'contact_first_name' => contact.first_name,
37
36
  'contact_last_name' => contact.last_name}
38
37
  c['duration'] = duration if duration
39
- return c if @notification_contents.nil?
40
- c.merge(@notification_contents)
38
+ c
41
39
  end
42
40
 
43
41
  private
44
42
 
45
43
  def initialize(opts = {})
46
44
  @contact = opts[:contact]
47
- @notification_contents = opts[:notification_contents]
48
45
  @medium = opts[:medium]
49
46
  @address = opts[:address]
50
47
  @duration = opts[:duration]
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'oj'
4
+
3
5
  require 'flapjack/data/contact'
4
6
  require 'flapjack/data/event'
5
7
  require 'flapjack/data/message'
@@ -8,52 +10,110 @@ module Flapjack
8
10
  module Data
9
11
  class Notification
10
12
 
11
- attr_reader :event, :type, :max_notified_severity, :contacts,
12
- :default_timezone, :last_state
13
-
14
- def self.for_event(event, opts = {})
15
- self.new(:event => event,
16
- :type => opts[:type],
17
- :max_notified_severity => opts[:max_notified_severity],
18
- :contacts => opts[:contacts],
19
- :default_timezone => opts[:default_timezone],
20
- :last_state => opts[:last_state],
21
- :logger => opts[:logger])
22
- end
23
-
24
- def messages
25
- return [] if contacts.nil? || contacts.empty?
13
+ attr_reader :type, :event_id, :event_state, :event_count
26
14
 
27
- event_id = event.id
28
- event_state = event.state
15
+ def self.type_for_event(event)
16
+ case event.type
17
+ when 'service'
18
+ case event.state
19
+ when 'ok'
20
+ 'recovery'
21
+ when 'warning', 'critical', 'unknown'
22
+ 'problem'
23
+ end
24
+ when 'action'
25
+ case event.state
26
+ when 'acknowledgement'
27
+ 'acknowledgement'
28
+ when 'test_notifications'
29
+ 'test'
30
+ end
31
+ else
32
+ 'unknown'
33
+ end
34
+ end
29
35
 
30
- severity = if ([event_state, max_notified_severity] & ['critical', 'unknown', 'test_notifications']).any?
36
+ def self.severity_for_event(event, max_notified_severity)
37
+ if ([event.state, max_notified_severity] & ['critical', 'unknown', 'test_notifications']).any?
31
38
  'critical'
32
- elsif [event_state, max_notified_severity].include?('warning')
39
+ elsif [event.state, max_notified_severity].include?('warning')
33
40
  'warning'
34
41
  else
35
42
  'ok'
36
43
  end
44
+ end
45
+
46
+ def self.add(queue, event, opts = {})
47
+ raise "Redis connection not set" unless redis = opts[:redis]
48
+
49
+ last_state = opts[:last_state] || {}
50
+
51
+ notif = {'event_id' => event.id,
52
+ 'state' => event.state,
53
+ 'summary' => event.summary,
54
+ 'last_state' => last_state[:state],
55
+ 'last_summary' => last_state[:summary],
56
+ 'details' => event.details,
57
+ 'time' => event.time,
58
+ 'duration' => event.duration || nil,
59
+ 'type' => opts[:type] || type_for_event(event),
60
+ 'severity' => opts[:severity],
61
+ 'count' => event.counter }
62
+
63
+ redis.rpush(queue, Oj.dump(notif))
64
+ end
65
+
66
+ def self.next(queue, opts = {})
67
+ raise "Redis connection not set" unless redis = opts[:redis]
68
+
69
+ defaults = { :block => true }
70
+ options = defaults.merge(opts)
71
+
72
+ if options[:block]
73
+ raw = redis.blpop(queue, 0)[1]
74
+ else
75
+ raw = redis.lpop(queue)
76
+ return unless raw
77
+ end
78
+ begin
79
+ parsed = ::Oj.load( raw )
80
+ rescue Oj::Error => e
81
+ if options[:logger]
82
+ options[:logger].warn("Error deserialising notification json: #{e}, raw json: #{raw.inspect}")
83
+ end
84
+ return nil
85
+ end
86
+ self.new( parsed )
87
+ end
88
+
89
+ def contents
90
+ @contents ||= {'event_id' => @event_id,
91
+ 'state' => @event_state,
92
+ 'summary' => @event_summary,
93
+ 'last_state' => @last_event_state,
94
+ 'last_summary' => @last_event_summary,
95
+ 'details' => @event_details,
96
+ 'time' => @event_time,
97
+ 'duration' => @event_duration,
98
+ 'notification_type' => @type,
99
+ 'event_count' => @event_count
100
+ }
101
+ end
102
+
103
+ def messages(contacts, opts = {})
104
+ return [] if contacts.nil? || contacts.empty?
37
105
 
38
- contents = {'event_id' => event_id,
39
- 'state' => event_state,
40
- 'summary' => event.summary,
41
- 'last_state' => @last_state ? @last_state[:state] : nil,
42
- 'last_summary' => @last_state ? @last_state[:summary] : nil,
43
- 'details' => event.details,
44
- 'time' => event.time,
45
- 'duration' => event.duration || nil,
46
- 'notification_type' => type,
47
- 'max_notified_severity' => max_notified_severity }
106
+ default_timezone = opts[:default_timezone]
107
+ logger = opts[:logger]
48
108
 
49
109
  @messages ||= contacts.collect {|contact|
50
110
  contact_id = contact.id
51
111
  rules = contact.notification_rules
52
112
  media = contact.media
53
113
 
54
- @logger.debug "considering messages for contact id #{contact_id} #{event_id} #{event_state} (media) #{media.inspect}"
114
+ logger.debug "considering messages for contact id #{contact_id} #{@event_id} #{@event_state} (media) #{media.inspect}"
55
115
  rlen = rules.length
56
- @logger.debug "found #{rlen} rule#{(rlen == 1) ? '' : 's'} for contact"
116
+ logger.debug "found #{rlen} rule#{(rlen == 1) ? '' : 's'} for contact"
57
117
 
58
118
  media_to_use = if rules.empty?
59
119
  media
@@ -61,55 +121,54 @@ module Flapjack
61
121
  # matchers are rules of the contact that have matched the current event
62
122
  # for time and entity
63
123
  matchers = rules.select do |rule|
64
- rule.match_entity?(event_id) &&
124
+ rule.match_entity?(@event_id) &&
65
125
  rule_occurring_now?(rule, :contact => contact, :default_timezone => default_timezone)
66
126
  end
67
127
 
68
- @logger.debug "#{matchers.length} matchers remain for this contact:"
128
+ logger.debug "#{matchers.length} matchers remain for this contact:"
69
129
  matchers.each do |matcher|
70
- @logger.debug "matcher: #{matcher.to_json}"
130
+ logger.debug "matcher: #{matcher.to_json}"
71
131
  end
72
132
 
73
133
  # delete any matchers for all entities if there are more specific matchers
74
134
  if matchers.any? {|matcher| matcher.is_specific? }
75
135
 
76
- @logger.debug("general removal: found #{matchers.length} entity specific matchers")
136
+ logger.debug("general removal: found #{matchers.length} entity specific matchers")
77
137
  num_matchers = matchers.length
78
138
 
79
139
  matchers.reject! {|matcher| !matcher.is_specific? }
80
140
 
81
141
  if num_matchers != matchers.length
82
- @logger.debug("notification: removal of general matchers when entity specific matchers are present: number of matchers changed from #{num_matchers} to #{matchers.length} for contact id: #{contact_id}")
142
+ logger.debug("notification: removal of general matchers when entity specific matchers are present: number of matchers changed from #{num_matchers} to #{matchers.length} for contact id: #{contact_id}")
83
143
  end
84
144
  end
85
145
 
86
146
  # delete media based on blackholes
87
- next if matchers.any? {|matcher| matcher.blackhole?(event_state) }
147
+ next if matchers.any? {|matcher| matcher.blackhole?(@event_state) }
88
148
 
89
- @logger.debug "notification: num matchers after removing blackhole matchers: #{matchers.size}"
149
+ logger.debug "notification: num matchers after removing blackhole matchers: #{matchers.size}"
90
150
 
91
151
  rule_media = matchers.collect{|matcher|
92
- matcher.media_for_severity(severity)
152
+ matcher.media_for_severity(@severity)
93
153
  }.flatten.uniq
94
154
 
95
- @logger.debug "notification: collected media_for_severity(#{severity}): #{rule_media}"
96
- rule_media = rule_media.flatten.uniq.reject {|medium|
155
+ logger.debug "notification: collected media_for_severity(#{@severity}): #{rule_media}"
156
+ rule_media = rule_media.reject {|medium|
97
157
  contact.drop_notifications?(:media => medium,
98
- :check => event_id,
99
- :state => event_state)
158
+ :check => @event_id,
159
+ :state => @event_state)
100
160
  }
101
161
 
102
- @logger.debug "notification: media after contact_drop?: #{rule_media}"
162
+ logger.debug "notification: media after contact_drop?: #{rule_media}"
103
163
 
104
164
  media.select {|medium, address| rule_media.include?(medium) }
105
165
  end
106
166
 
107
- @logger.debug "notification: media_to_use: #{media_to_use}"
167
+ logger.debug "notification: media_to_use: #{media_to_use}"
108
168
 
109
169
  media_to_use.each_pair.inject([]) { |ret, (k, v)|
110
170
  m = Flapjack::Data::Message.for_contact(contact,
111
- :notification_contents => contents,
112
- :medium => k, :address => v)
171
+ :medium => k, :address => v)
113
172
  ret << m
114
173
  ret
115
174
  }
@@ -118,15 +177,21 @@ module Flapjack
118
177
 
119
178
  private
120
179
 
180
+ # created from parsed JSON, so opts keys are in strings
121
181
  def initialize(opts = {})
122
- raise "Event not passed" unless event = opts[:event]
123
- @event = event
124
- @type = opts[:type]
125
- @max_notified_severity = opts[:max_notified_severity]
126
- @contacts = opts[:contacts]
127
- @default_timezone = opts[:default_timezone]
128
- @last_state = opts[:last_state]
129
- @logger = opts[:logger]
182
+ @event_id = opts['event_id']
183
+ @event_state = opts['state']
184
+ @event_summary = opts['summary']
185
+ @event_details = opts['details']
186
+ @event_time = opts['time']
187
+ @event_duration = opts['duration']
188
+ @event_count = opts['count']
189
+
190
+ @last_event_state = opts['last_state']
191
+ @last_event_summary = opts['last_summary']
192
+
193
+ @type = opts['type']
194
+ @severity = opts['severity']
130
195
  end
131
196
 
132
197
  # # time restrictions match?
@@ -135,11 +200,11 @@ module Flapjack
135
200
  # considered to be in the timezone of the contact
136
201
  def rule_occurring_now?(rule, options = {})
137
202
  contact = options[:contact]
138
- default_timezone = options[:default_timezone]
203
+ def_tz = options[:default_timezone]
139
204
 
140
205
  return true if rule.time_restrictions.nil? or rule.time_restrictions.empty?
141
206
 
142
- timezone = contact.timezone(:default => default_timezone)
207
+ timezone = contact.timezone(:default => def_tz)
143
208
  usertime = timezone.now
144
209
 
145
210
  rule.time_restrictions.any? do |tr|