flapjack 0.5.5 → 0.6.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+