flapjack 0.5.5 → 0.6.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. data/.gitignore +10 -0
  2. data/.rbenv-version +1 -0
  3. data/.rspec +10 -0
  4. data/Gemfile +18 -0
  5. data/Guardfile +14 -0
  6. data/README.md +152 -173
  7. data/Rakefile +53 -150
  8. data/bin/flapjack +72 -0
  9. data/bin/flapjack-nagios-receiver +111 -0
  10. data/bin/flapjack-nagios-receiver-control +15 -0
  11. data/bin/flapjack-netsaint-parser +0 -2
  12. data/bin/flapjack-populator +133 -16
  13. data/bin/install-flapjack-systemwide +2 -2
  14. data/config.ru +11 -0
  15. data/dist/etc/init.d/flapjack +46 -0
  16. data/dist/etc/init.d/flapjack-nagios-receiver +36 -0
  17. data/doc/GLOSSARY.md +19 -0
  18. data/etc/flapjack_config.yaml.example +90 -0
  19. data/features/events.feature +132 -0
  20. data/features/notifications.feature +57 -0
  21. data/features/packaging-lintian.feature +5 -3
  22. data/features/steps/events_steps.rb +164 -0
  23. data/features/steps/flapjack-importer_steps.rb +2 -5
  24. data/features/steps/flapjack-worker_steps.rb +13 -6
  25. data/features/steps/notifications_steps.rb +178 -0
  26. data/features/steps/packaging-lintian_steps.rb +14 -0
  27. data/features/steps/time_travel_steps.rb +34 -0
  28. data/features/support/env.rb +63 -36
  29. data/flapjack.gemspec +35 -186
  30. data/lib/flapjack.rb +2 -0
  31. data/lib/flapjack/api.rb +274 -0
  32. data/lib/flapjack/api/entity_check_presenter.rb +184 -0
  33. data/lib/flapjack/api/entity_presenter.rb +66 -0
  34. data/lib/flapjack/cli/worker_manager.rb +1 -2
  35. data/lib/flapjack/configuration.rb +11 -0
  36. data/lib/flapjack/coordinator.rb +288 -0
  37. data/lib/flapjack/daemonizing.rb +186 -0
  38. data/lib/flapjack/data/contact.rb +45 -0
  39. data/lib/flapjack/data/entity.rb +89 -0
  40. data/lib/flapjack/data/entity_check.rb +396 -0
  41. data/lib/flapjack/data/event.rb +144 -0
  42. data/lib/flapjack/data/notification.rb +13 -0
  43. data/lib/flapjack/executive.rb +289 -0
  44. data/lib/flapjack/filters/acknowledgement.rb +39 -0
  45. data/lib/flapjack/filters/{any_parents_failed.rb → base.rb} +6 -4
  46. data/lib/flapjack/filters/delays.rb +53 -0
  47. data/lib/flapjack/filters/detect_mass_client_failures.rb +44 -0
  48. data/lib/flapjack/filters/ok.rb +25 -5
  49. data/lib/flapjack/filters/scheduled_maintenance.rb +17 -0
  50. data/lib/flapjack/filters/unscheduled_maintenance.rb +17 -0
  51. data/lib/flapjack/jabber.rb +294 -0
  52. data/lib/flapjack/notification/common.rb +23 -0
  53. data/lib/flapjack/notification/email.rb +107 -0
  54. data/lib/flapjack/notification/email/alert.html.haml +48 -0
  55. data/lib/flapjack/notification/email/alert.text.erb +14 -0
  56. data/lib/flapjack/notification/sms.rb +42 -0
  57. data/lib/flapjack/notification/sms/messagenet.rb +49 -0
  58. data/lib/flapjack/notifier_engine.rb +4 -4
  59. data/lib/flapjack/notifiers/mailer/mailer.rb +6 -7
  60. data/lib/flapjack/notifiers/xmpp/xmpp.rb +12 -12
  61. data/lib/flapjack/pagerduty.rb +230 -0
  62. data/lib/flapjack/patches.rb +108 -19
  63. data/lib/flapjack/persistence/data_mapper/models/check.rb +5 -3
  64. data/lib/flapjack/persistence/data_mapper/models/check_template.rb +2 -0
  65. data/lib/flapjack/persistence/data_mapper/models/event.rb +2 -0
  66. data/lib/flapjack/persistence/data_mapper/models/node.rb +3 -1
  67. data/lib/flapjack/persistence/data_mapper/models/related_check.rb +3 -1
  68. data/lib/flapjack/pikelet.rb +56 -0
  69. data/lib/flapjack/transports/beanstalkd.rb +1 -1
  70. data/lib/flapjack/transports/result.rb +6 -6
  71. data/lib/flapjack/utility.rb +46 -0
  72. data/lib/flapjack/version.rb +5 -0
  73. data/lib/flapjack/web.rb +198 -0
  74. data/lib/flapjack/web/views/acknowledge.haml +55 -0
  75. data/lib/flapjack/web/views/check.haml +162 -0
  76. data/lib/flapjack/web/views/index.haml +92 -0
  77. data/lib/flapjack/web/views/self_stats.haml +56 -0
  78. data/lib/flapjack/{applications/worker.rb → worker/application.rb} +0 -0
  79. data/lib/flapjack/worker/cli.rb +49 -0
  80. data/{spec → spec.old}/check_sandbox/echo +0 -0
  81. data/{spec → spec.old}/check_sandbox/sandboxed_check +0 -0
  82. data/{spec → spec.old}/configs/flapjack-notifier-couchdb.ini +0 -0
  83. data/{spec → spec.old}/configs/flapjack-notifier.ini +0 -0
  84. data/{spec → spec.old}/configs/recipients.ini +0 -0
  85. data/{spec → spec.old}/helpers.rb +0 -0
  86. data/{spec → spec.old}/inifile_spec.rb +0 -0
  87. data/{spec → spec.old}/mock-notifiers/mock/init.rb +0 -0
  88. data/{spec → spec.old}/mock-notifiers/mock/mock.rb +0 -0
  89. data/{spec → spec.old}/notifier-directories/spoons/testmailer/init.rb +0 -0
  90. data/{spec → spec.old}/notifier_application_spec.rb +0 -0
  91. data/{spec → spec.old}/notifier_filters_spec.rb +0 -0
  92. data/{spec → spec.old}/notifier_options_multiplexer_spec.rb +0 -0
  93. data/{spec → spec.old}/notifier_options_spec.rb +0 -0
  94. data/{spec → spec.old}/notifier_spec.rb +0 -0
  95. data/{spec → spec.old}/notifiers/mailer_spec.rb +0 -0
  96. data/{spec → spec.old}/notifiers/xmpp_spec.rb +0 -0
  97. data/{spec → spec.old}/persistence/datamapper_spec.rb +0 -0
  98. data/{spec → spec.old}/persistence/mock_persistence_backend.rb +0 -0
  99. data/{spec → spec.old}/simple.ini +0 -0
  100. data/{spec → spec.old}/spec.opts +0 -0
  101. data/{spec → spec.old}/test-filters/blocker.rb +0 -0
  102. data/{spec → spec.old}/test-filters/mock.rb +0 -0
  103. data/{spec → spec.old}/transports/beanstalkd_spec.rb +0 -0
  104. data/{spec → spec.old}/transports/mock_transport.rb +0 -0
  105. data/{spec → spec.old}/worker_application_spec.rb +0 -0
  106. data/{spec → spec.old}/worker_options_spec.rb +0 -0
  107. data/spec/lib/flapjack/api/entity_check_presenter_spec.rb +117 -0
  108. data/spec/lib/flapjack/api/entity_presenter_spec.rb +92 -0
  109. data/spec/lib/flapjack/api_spec.rb +170 -0
  110. data/spec/lib/flapjack/coordinator_spec.rb +16 -0
  111. data/spec/lib/flapjack/data/entity_check_spec.rb +398 -0
  112. data/spec/lib/flapjack/data/entity_spec.rb +71 -0
  113. data/spec/lib/flapjack/data/event_spec.rb +6 -0
  114. data/spec/lib/flapjack/executive_spec.rb +59 -0
  115. data/spec/lib/flapjack/filters/acknowledgement_spec.rb +6 -0
  116. data/spec/lib/flapjack/filters/delays_spec.rb +6 -0
  117. data/spec/lib/flapjack/filters/detect_mass_client_failures_spec.rb +6 -0
  118. data/spec/lib/flapjack/filters/ok_spec.rb +6 -0
  119. data/spec/lib/flapjack/filters/scheduled_maintenance_spec.rb +6 -0
  120. data/spec/lib/flapjack/filters/unscheduled_maintenance_spec.rb +6 -0
  121. data/spec/lib/flapjack/jabber_spec.rb +150 -0
  122. data/spec/lib/flapjack/notification/email_spec.rb +6 -0
  123. data/spec/lib/flapjack/notification/sms_spec.rb +6 -0
  124. data/spec/lib/flapjack/pikelet_spec.rb +28 -0
  125. data/spec/lib/flapjack/web_spec.rb +188 -0
  126. data/spec/spec_helper.rb +44 -0
  127. data/spec/support/profile_all_formatter.rb +44 -0
  128. data/spec/support/uncolored_doc_formatter.rb +9 -0
  129. data/tasks/events.rake +85 -0
  130. data/tmp/acknowledge.rb +14 -0
  131. data/tmp/create_config_yaml.rb +16 -0
  132. data/tmp/create_events_failure.rb +33 -0
  133. data/tmp/create_events_ok.rb +33 -0
  134. data/tmp/create_events_ok_fail_ack_ok.rb +54 -0
  135. data/tmp/create_events_ok_failure.rb +40 -0
  136. data/tmp/create_events_ok_failure_ack.rb +54 -0
  137. data/tmp/dummy_entities.json +1 -0
  138. data/tmp/generate_nagios_test_hosts.rb +16 -0
  139. data/tmp/parse_config_yaml.rb +7 -0
  140. data/tmp/redis_delete_all_keys.rb +11 -0
  141. data/tmp/test_entities.json +1 -0
  142. metadata +482 -221
  143. data/TODO.md +0 -36
  144. data/VERSION +0 -1
  145. data/bin/flapjack-benchmark +0 -50
  146. data/bin/flapjack-notifier +0 -21
  147. data/bin/flapjack-notifier-manager +0 -43
  148. data/bin/flapjack-stats +0 -27
  149. data/bin/flapjack-worker +0 -13
  150. data/bin/flapjack-worker-manager +0 -35
  151. data/dist/etc/init.d/flapjack-notifier +0 -47
  152. data/dist/etc/init.d/flapjack-workers +0 -44
  153. data/features/flapjack-notifier-manager.feature +0 -19
  154. data/features/flapjack-worker-manager.feature +0 -27
  155. data/features/flapjack-worker.feature +0 -27
  156. data/features/netsaint-config-converter.feature +0 -126
  157. data/features/persistence/couch.feature +0 -105
  158. data/features/persistence/sqlite3.feature +0 -105
  159. data/features/persistence/steps/couch_steps.rb +0 -25
  160. data/features/persistence/steps/generic_steps.rb +0 -102
  161. data/features/persistence/steps/sqlite3_steps.rb +0 -13
  162. data/features/steps/flapjack-notifier-manager_steps.rb +0 -24
  163. data/features/steps/flapjack-worker-manager_steps.rb +0 -48
  164. data/lib/flapjack/applications/notifier.rb +0 -222
  165. data/lib/flapjack/cli/notifier.rb +0 -108
  166. data/lib/flapjack/cli/notifier_manager.rb +0 -86
  167. data/lib/flapjack/cli/worker.rb +0 -51
@@ -1,13 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
1
3
  module Flapjack
2
4
  module Filters
3
- class AnyParentsFailed
5
+ module Base
4
6
  def initialize(opts={})
5
- @log = opts[:log]
7
+ @log = opts[:log]
6
8
  @persistence = opts[:persistence]
7
9
  end
8
10
 
9
- def block?(result)
10
- @persistence.any_parents_failed?(result)
11
+ def name
12
+ self.class.to_s.split('::').last
11
13
  end
12
14
  end
13
15
  end
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'flapjack/data/entity_check'
4
+ require 'flapjack/filters/base'
5
+
6
+ module Flapjack
7
+ module Filters
8
+
9
+ # * If the service event’s state is a failure, and the time since the ok => failure state change
10
+ # is below a threshold (e.g. 30 seconds), then don't alert
11
+ # * If the service event’s state is a failure, and the time since the last alert is below a
12
+ # threshold (5 minutes), then don’t alert
13
+ class Delays
14
+ include Base
15
+
16
+ def block?(event)
17
+ failure_delay = 30
18
+ resend_delay = 300
19
+
20
+ result = false
21
+
22
+ if (event.type == 'service') and (event.critical? or event.warning?)
23
+
24
+ entity_check = Flapjack::Data::EntityCheck.for_event_id(event.id, :redis => @persistence)
25
+ current_time = Time.now.to_i
26
+
27
+ if entity_check.failed?
28
+ last_problem_alert = entity_check.last_problem_notification
29
+ last_change = entity_check.last_change
30
+
31
+ current_failure_duration = current_time - last_change
32
+ time_since_last_alert = current_time - last_problem_alert unless last_problem_alert.nil?
33
+ @log.debug("Filter: Delays: last_problem_alert: #{last_problem_alert.to_s}, last_change: #{last_change.to_s}, current_failure_duration: #{current_failure_duration}, time_since_last_alert: #{time_since_last_alert.to_s}")
34
+ if (current_failure_duration < failure_delay)
35
+ result = true
36
+ @log.debug("Filter: Delays: blocking because duration of current failure (#{current_failure_duration}) is less than failure_delay (#{failure_delay})")
37
+ elsif !last_problem_alert.nil? && (time_since_last_alert < resend_delay)
38
+ result = true
39
+ @log.debug("Filter: Delays: blocking because time since last alert for current problem (#{time_since_last_alert}) is less than resend_delay (#{resend_delay})")
40
+ else
41
+ @log.debug("Filter: Delays: not blocking because neither of the time comparison conditions were met")
42
+ end
43
+ else
44
+ @log.debug("Filter: Delays: entity_check.failed? returned false ...")
45
+ end
46
+ end
47
+
48
+ @log.debug("Filter: Delays: #{result ? "block" : "pass"}")
49
+ result
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'flapjack/filters/base'
4
+
5
+ module Flapjack
6
+ module Filters
7
+
8
+ # * If the service event’s state is a failure, and the total number of failing client checks is
9
+ # over a threshold (e.g. 10 checks are failing), then set a meta flag noting the threshold has
10
+ # been tripped, and generate an event for this meta check
11
+ # * If the service event’s state is ok, and the meta flag is set, and the total number of
12
+ # failing client checks is less than a threshold (eg 10), then unset the flag, and generate an
13
+ # event for this meta check
14
+ class DetectMassClientFailures
15
+ include Base
16
+
17
+ def block?(event)
18
+ client_mass_fail_threshold = 10
19
+ timestamp = Time.now.to_i
20
+
21
+ if event.type == 'service'
22
+ client_fail_count = @persistence.zcount("failed_checks:#{event.client}", '-inf', '+inf')
23
+
24
+ if client_fail_count >= client_mass_fail_threshold
25
+ # set the flag
26
+ # FIXME: perhaps implement this with tagging
27
+ @persistence.add("mass_failed_client:#{event.client}", timestamp)
28
+ @persistence.zadd("mass_failure_events_client:#{event.client}", 0, timestamp)
29
+ else
30
+ # unset the flag
31
+ start_mf = @persistence.get("mass_failed_client:#{event.client}")
32
+ duration = Time.now.to_i - start_mf.to_i
33
+ @persistence.del("mass_failed_client:#{event.client}")
34
+ @persistence.zadd("mass_failure_events_client:#{event.client}", duration, start_mf)
35
+ end
36
+ end
37
+
38
+ result = false
39
+ @log.debug("Filter: DetectMassClientFailures: #{result ? "block" : "pass"}")
40
+ result
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,12 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'flapjack/filters/base'
4
+
1
5
  module Flapjack
2
6
  module Filters
7
+
8
+ # * If the service event’s state is ok and the previous state was ok, don’t alert
9
+ # * If the service event's state is ok and there is unscheduled downtime set, end the unscheduled
10
+ # downtime
3
11
  class Ok
4
- def initialize(opts={})
5
- @log = opts[:log]
6
- end
12
+ include Base
13
+
14
+ def block?(event)
15
+ result = false
16
+
17
+ if event.ok?
18
+ if event.previous_state == 'ok'
19
+ @log.debug("Filter: Ok: existing state was ok, and the previous state was ok, so blocking")
20
+ result = true
21
+ end
22
+
23
+ # end any unscheduled downtime
24
+ entity_check = Flapjack::Data::EntityCheck.for_event_id(event.id, :redis => @persistence)
25
+ entity_check.end_unscheduled_maintenance
26
+ end
7
27
 
8
- def block?(result)
9
- !result.warning? || !result.critical?
28
+ @log.debug("Filter: Ok: #{result ? "block" : "pass"}")
29
+ result
10
30
  end
11
31
  end
12
32
  end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'flapjack/filters/base'
4
+
5
+ module Flapjack
6
+ module Filters
7
+ class ScheduledMaintenance
8
+ include Base
9
+
10
+ def block?(event)
11
+ result = @persistence.exists("#{event.id}:scheduled_maintenance")
12
+ @log.debug("Filter: Scheduled Maintenance: #{result ? "block" : "pass"}")
13
+ result
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'flapjack/filters/base'
4
+
5
+ module Flapjack
6
+ module Filters
7
+ class UnscheduledMaintenance
8
+ include Base
9
+
10
+ def block?(event)
11
+ result = @persistence.exists("#{event.id}:unscheduled_maintenance")
12
+ @log.debug("Filter: Unscheduled Maintenance: #{result ? "block" : "pass"}")
13
+ result
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'socket'
4
+
5
+ require 'eventmachine'
6
+ # the redis/synchrony gems need to be required in this particular order, see
7
+ # the redis-rb README for details
8
+ require 'hiredis'
9
+ require 'em-synchrony'
10
+ require 'redis/connection/synchrony'
11
+ require 'redis'
12
+
13
+ require 'chronic_duration'
14
+
15
+ require 'blather/client/client'
16
+ require 'em-synchrony/fiber_iterator'
17
+ require 'yajl/json_gem'
18
+
19
+ require 'flapjack/data/entity_check'
20
+ require 'flapjack/pikelet'
21
+ require 'flapjack/utility'
22
+
23
+ module Flapjack
24
+
25
+ class Jabber < Blather::Client
26
+
27
+ include Flapjack::Pikelet
28
+ include Flapjack::Utility
29
+
30
+ log = Logger.new(STDOUT)
31
+ # log.level = Logger::DEBUG
32
+ log.level = Logger::INFO
33
+ Blather.logger = log
34
+
35
+ def setup
36
+ @redis = build_redis_connection_pool
37
+ @hostname = Socket.gethostname
38
+ @flapjack_jid = Blather::JID.new((@config['jabberid'] || 'flapjack') + '/' + @hostname)
39
+
40
+ super(@flapjack_jid, @config['password'], @config['server'], @config['port'].to_i)
41
+
42
+ logger.debug("Building jabber connection with jabberid: " +
43
+ @flapjack_jid.to_s + ", port: " + @config['port'].to_s +
44
+ ", server: " + @config['server'].to_s + ", password: " +
45
+ @config['password'].to_s)
46
+
47
+ register_handler :ready do |stanza|
48
+ EM.next_tick do
49
+ EM.synchrony do
50
+ on_ready(stanza)
51
+ end
52
+ end
53
+ end
54
+
55
+ register_handler :message, :groupchat?, :body => /^flapjack:\s+/ do |stanza|
56
+ EM.next_tick do
57
+ EM.synchrony do
58
+ on_groupchat(stanza)
59
+ end
60
+ end
61
+ end
62
+
63
+ register_handler :message, :chat? do |stanza|
64
+ EM.next_tick do
65
+ EM.synchrony do
66
+ on_chat(stanza)
67
+ end
68
+ end
69
+ end
70
+
71
+ register_handler :disconnected do |stanza|
72
+ ret = true
73
+ EM.next_tick do
74
+ EM.synchrony do
75
+ ret = on_disconnect(stanza)
76
+ end
77
+ end
78
+ ret
79
+ end
80
+ end
81
+
82
+ # Join the MUC Chat room after connecting.
83
+ def on_ready(stanza)
84
+ return if should_quit?
85
+ @redis_handler ||= build_redis_connection_pool
86
+ @connected_at = Time.now.to_i
87
+ logger.info("Jabber Connected")
88
+ @config['rooms'].each do |room|
89
+ logger.info("Joining room #{room}")
90
+ presence = Blather::Stanza::Presence.new
91
+ presence.from = @flapjack_jid
92
+ presence.to = Blather::JID.new("#{room}/#{@config['alias']}")
93
+ presence << "<x xmlns='http://jabber.org/protocol/muc'/>"
94
+ write presence
95
+ say(room, "flapjack jabber gateway started at #{Time.now}, hello!", :groupchat)
96
+ end
97
+ end
98
+
99
+ def interpreter(command)
100
+
101
+ msg = nil
102
+ action = nil
103
+ entity_check = nil
104
+ case
105
+ when command =~ /^ACKID\s+(\d+)(?:\s*(.*?)(?:\s*duration.*?(\d+.*\w+.*))?)$/i;
106
+ ackid = $1
107
+ comment = $2
108
+ duration_str = $3
109
+
110
+ error = nil
111
+ dur = nil
112
+
113
+ if comment.nil? || (comment.length == 0)
114
+ error = "please provide a comment, eg \"flapjack: ACKID #{$1} AL looking\""
115
+ elsif duration_str
116
+ # a fairly liberal match above, we'll let chronic_duration do the heavy lifting
117
+ dur = ChronicDuration.parse(duration_str)
118
+ end
119
+
120
+ four_hours = 4 * 60 * 60
121
+ duration = (dur.nil? || (dur <= 0) || (dur > four_hours)) ? four_hours : dur
122
+
123
+ event_id = @redis_handler.hget('unacknowledged_failures', ackid)
124
+
125
+ if event_id.nil?
126
+ error = "not found"
127
+ else
128
+ entity_check = Flapjack::Data::EntityCheck.for_event_id(event_id, :redis => @redis_handler)
129
+ error = "unknown entity" if entity_check.nil?
130
+ end
131
+
132
+ if error
133
+ msg = "ERROR - couldn't ACK #{ackid} - #{error}"
134
+ else
135
+ msg = "ACKing #{entity_check.check} on #{entity_check.entity_name} (#{ackid})"
136
+ action = Proc.new {
137
+ entity_check.create_acknowledgement('summary' => (comment || ''),
138
+ 'acknowledgement_id' => ackid, 'duration' => duration)
139
+ }
140
+ end
141
+
142
+ when command =~ /^help$/
143
+ msg = "commands: \n"
144
+ msg += " ACKID <id> <comment> [duration: <time spec>] \n"
145
+ msg += " identify \n"
146
+ msg += " help \n"
147
+
148
+ when command =~ /^identify$/
149
+ t = Process.times
150
+
151
+ msg = "Flapjack process #{Process.pid} on #{`hostname -f`.chomp} \n"
152
+ msg += "User CPU Time: #{t.utime}\n"
153
+ msg += "System CPU Time: #{t.stime}\n"
154
+ msg += `uname -a`.chomp + "\n"
155
+
156
+ when command =~ /^(.*)/
157
+ words = $1
158
+ msg = "what do you mean, '#{words}'? Type 'help' for a list of acceptable commands."
159
+
160
+ end
161
+
162
+ {:msg => msg, :action => action}
163
+ end
164
+
165
+ def on_groupchat(stanza)
166
+ return if should_quit?
167
+ logger.debug("groupchat message received: #{stanza.inspect}")
168
+
169
+ if stanza.body =~ /^flapjack:\s+(.*)/
170
+ command = $1
171
+ end
172
+
173
+ results = interpreter(command)
174
+ msg = results[:msg]
175
+ action = results[:action]
176
+
177
+ if msg || action
178
+ say(stanza.from.stripped, msg, :groupchat)
179
+ logger.debug("Sent to group chat: #{msg}")
180
+ action.call if action
181
+ end
182
+ end
183
+
184
+ def on_chat(stanza)
185
+ return if should_quit?
186
+ logger.debug("chat message received: #{stanza.inspect}")
187
+
188
+ results = interpreter(stanza.body)
189
+ msg = results[:msg]
190
+ action = results[:action]
191
+
192
+ if msg || action
193
+ say(stanza.from.stripped, msg, :chat)
194
+ logger.debug("Sent to #{stanza.from.stripped}: #{msg}")
195
+ action.call if action
196
+ end
197
+ end
198
+
199
+ # returning true to prevent the reactor loop from stopping
200
+ def on_disconnect(stanza)
201
+ return true if should_quit?
202
+ logger.warn("jabbers disconnected! reconnecting in 1 second ...")
203
+ EventMachine::Timer.new(1) do
204
+ connect # Blather::Client.connect
205
+ end
206
+ true
207
+ end
208
+
209
+ def say(to, msg, using = :chat)
210
+ @logger.debug("Sending a jabber message to: #{to.to_s}, using: #{using.to_s}, message: #{msg}")
211
+ write Blather::Stanza::Message.new(to, msg, using)
212
+ end
213
+
214
+ def add_shutdown_event(opts = {})
215
+ return unless redis = opts[:redis]
216
+ redis.rpush(@config['queue'], JSON.generate('notification_type' => 'shutdown'))
217
+ end
218
+
219
+ def main
220
+ logger.debug("New Jabber pikelet with the following options: #{@config.inspect}")
221
+
222
+ count_timer = EM::Synchrony.add_periodic_timer(30) do
223
+ logger.debug("connection count: #{EM.connection_count} #{Time.now.to_s}.#{Time.now.usec.to_s}")
224
+ end
225
+
226
+ keepalive_timer = EM::Synchrony.add_periodic_timer(60) do
227
+ logger.debug("calling keepalive on the jabber connection")
228
+ write(' ') if connected?
229
+ end
230
+
231
+ setup
232
+ connect # Blather::Client.connect
233
+
234
+ # simplified to use a single queue only as it makes the shutdown logic easier
235
+ queue = @config['queue']
236
+ events = {}
237
+
238
+ until should_quit?
239
+
240
+ # FIXME: should also check if presence has been established in any group chat rooms that are
241
+ # configured before starting to process events, otherwise the first few may get lost (send
242
+ # before joining the group chat rooms)
243
+ if connected?
244
+ logger.debug("jabber is connected so commencing blpop on #{queue}")
245
+ events[queue] = @redis.blpop(queue)
246
+ event = Yajl::Parser.parse(events[queue][1])
247
+ type = event['notification_type']
248
+ logger.debug('jabber notification event received')
249
+ logger.debug(event.inspect)
250
+ if 'shutdown'.eql?(type)
251
+ EM.next_tick do
252
+ # get delays without the next_tick
253
+ close # Blather::Client.close
254
+ end
255
+ # FIXME: should we also set something so should_quit? returns true
256
+ # to prevent retrieving more notifications from the queue while closing?
257
+ # or does close only return once the connection is really and truely closed?
258
+ else
259
+ entity, check = event['event_id'].split(':')
260
+ state = event['state']
261
+ summary = event['summary']
262
+ duration = event['duration'] ? time_period_in_words(event['duration']) : '4 hours'
263
+ address = event['address']
264
+
265
+ logger.debug("processing jabber notification address: #{address}, event: #{entity}:#{check}, state: #{state}, summary: #{summary}")
266
+
267
+ ack_str = event['event_count'] && !state.eql?('ok') && !'acknowledgement'.eql?(type) ?
268
+ "::: flapjack: ACKID #{event['event_count']} " : ''
269
+
270
+ maint_str = (type && 'acknowledgement'.eql?(type)) ?
271
+ "has been acknowledged, unscheduled maintenance created for #{duration}" :
272
+ "is #{state.upcase}"
273
+
274
+ msg = "#{type.upcase} #{ack_str}::: \"#{check}\" on #{entity} #{maint_str} ::: #{summary}"
275
+
276
+ chat_type = :chat
277
+ chat_type = :groupchat if @config['rooms'].include?(address)
278
+ EM.next_tick do
279
+ say(Blather::JID.new(address), msg, chat_type)
280
+ end
281
+ end
282
+ else
283
+ logger.debug("not connected, sleep 1 before retry")
284
+ EM::Synchrony.sleep(1)
285
+ end
286
+ end
287
+
288
+ count_timer.cancel
289
+ keepalive_timer.cancel
290
+ end
291
+
292
+ end
293
+ end
294
+