flapjack 1.2.1 → 1.2.2

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +11 -12
  4. data/CHANGELOG.md +10 -0
  5. data/Gemfile +0 -7
  6. data/Rakefile +0 -1
  7. data/bin/flapjack +2 -0
  8. data/etc/flapjack_config.yaml.example +20 -0
  9. data/features/ack_after_sched_maint.feature +1 -1
  10. data/features/cli.feature +1 -1
  11. data/features/notification_rules.feature +1 -1
  12. data/features/notifications.feature +0 -9
  13. data/features/rollup.feature +1 -1
  14. data/features/steps/events_steps.rb +20 -8
  15. data/features/steps/notifications_steps.rb +62 -75
  16. data/features/support/env.rb +17 -8
  17. data/flapjack.gemspec +4 -4
  18. data/lib/flapjack.rb +3 -0
  19. data/lib/flapjack/cli/import.rb +1 -0
  20. data/lib/flapjack/cli/maintenance.rb +1 -0
  21. data/lib/flapjack/cli/purge.rb +2 -0
  22. data/lib/flapjack/cli/receiver.rb +1 -0
  23. data/lib/flapjack/cli/simulate.rb +1 -0
  24. data/lib/flapjack/data/alert.rb +28 -1
  25. data/lib/flapjack/data/contact.rb +1 -1
  26. data/lib/flapjack/data/entity.rb +18 -8
  27. data/lib/flapjack/data/entity_check.rb +17 -0
  28. data/lib/flapjack/data/event.rb +33 -15
  29. data/lib/flapjack/data/migration.rb +46 -23
  30. data/lib/flapjack/filters/delays.rb +13 -6
  31. data/lib/flapjack/gateways/aws_sns.rb +115 -88
  32. data/lib/flapjack/gateways/aws_sns/alert.text.erb +2 -1
  33. data/lib/flapjack/gateways/email.rb +145 -135
  34. data/lib/flapjack/gateways/email/alert.html.erb +6 -4
  35. data/lib/flapjack/gateways/email/alert.text.erb +2 -0
  36. data/lib/flapjack/gateways/jabber.rb +61 -1
  37. data/lib/flapjack/gateways/jabber/alert.text.erb +1 -1
  38. data/lib/flapjack/gateways/pagerduty/alert.text.erb +1 -1
  39. data/lib/flapjack/gateways/sms_gammu.rb +119 -0
  40. data/lib/flapjack/gateways/sms_messagenet.rb +95 -67
  41. data/lib/flapjack/gateways/sms_messagenet/alert.text.erb +2 -1
  42. data/lib/flapjack/gateways/sms_twilio.rb +102 -74
  43. data/lib/flapjack/gateways/sms_twilio/alert.text.erb +2 -1
  44. data/lib/flapjack/logger.rb +1 -1
  45. data/lib/flapjack/notifier.rb +5 -14
  46. data/lib/flapjack/patches.rb +0 -58
  47. data/lib/flapjack/pikelet.rb +8 -78
  48. data/lib/flapjack/processor.rb +3 -1
  49. data/lib/flapjack/redis_pool.rb +2 -0
  50. data/lib/flapjack/version.rb +1 -1
  51. data/spec/lib/flapjack/data/contact_spec.rb +2 -2
  52. data/spec/lib/flapjack/data/entity_spec.rb +15 -0
  53. data/spec/lib/flapjack/data/event_spec.rb +2 -2
  54. data/spec/lib/flapjack/data/migration_spec.rb +11 -0
  55. data/spec/lib/flapjack/gateways/aws_sns_spec.rb +12 -8
  56. data/spec/lib/flapjack/gateways/email_spec.rb +56 -51
  57. data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +17 -12
  58. data/spec/lib/flapjack/gateways/sms_twilio_spec.rb +17 -12
  59. data/spec/lib/flapjack/pikelet_spec.rb +9 -23
  60. data/spec/lib/flapjack/redis_pool_spec.rb +1 -0
  61. data/tasks/profile.rake +25 -109
  62. metadata +37 -39
  63. data/Gemfile-ruby1.9 +0 -30
  64. data/Gemfile-ruby1.9.lock +0 -250
  65. data/tasks/benchmarks.rake +0 -237
@@ -32,10 +32,12 @@
32
32
  <td><%= @alert.state_title_case %></td>
33
33
  </tr>
34
34
 
35
- <tr>
36
- <td><strong>Summary</strong></td>
37
- <td><%= @alert.summary %></td>
38
- </tr>
35
+ <% if @alert.summary %>
36
+ <tr>
37
+ <td><strong>Summary</strong></td>
38
+ <td><%= @alert.summary %></td>
39
+ </tr>
40
+ <% end %>
39
41
 
40
42
  <% if @alert.details %>
41
43
  <tr>
@@ -5,7 +5,9 @@ Monitoring has detected the following:
5
5
  Entity: <%= @alert.entity %>
6
6
  Check: <%= @alert.check %>
7
7
  State: <%= @alert.state_title_case %>
8
+ <% if @alert.summary -%>
8
9
  Summary: <%= @alert.summary %>
10
+ <% end -%>
9
11
  <% if @alert.details -%>
10
12
  Details: <%= @alert.details %>
11
13
  <% end -%>
@@ -241,6 +241,7 @@ module Flapjack
241
241
  msg = "commands: \n" +
242
242
  " ACKID <id> <comment> [duration: <time spec>]\n" +
243
243
  " ack entities /pattern/ <comment> [duration: <time spec>]\n" +
244
+ " maint entities /pattern/ <comment> [(start-in|start-at): <time spec>] [duration: <time spec>]\n" +
244
245
  " status entities /pattern/\n" +
245
246
  " ack checks /check_pattern/ on /entity_pattern/ <comment> [duration: <time spec>]\n" +
246
247
  " status checks /check_pattern/ on /entity_pattern/\n" +
@@ -423,6 +424,66 @@ module Flapjack
423
424
  end
424
425
  end
425
426
 
427
+ when /^(?:maint )?entities\s+\/(.+)\/(?:\s*(.*?)(?:\s*start-in:.*?(\w+.*?))?(?:\s*start-at:.*?(\w+.*?))?(?:\s*duration:.*?(\w+.*?))?)$/i
428
+ entity_pattern = $1.strip
429
+ comment = $2 ? $2.strip : nil
430
+ start_in = $3 ? $3.strip : nil
431
+ start_at = $4 ? $4.strip : nil
432
+ duration_str = $5 ? $5.strip : '1 hour'
433
+ maint_duration = ChronicDuration.parse(duration_str)
434
+ entity_names = Flapjack::Data::Entity.find_all_name_matching(entity_pattern, :redis => @redis)
435
+
436
+ if comment.nil? || (comment.length == 0)
437
+ comment = "#{from}: Set via chatbot"
438
+ else
439
+ comment = "#{from}: #{comment}"
440
+ end
441
+
442
+ maint_start = case
443
+ when start_in
444
+ Time.now.to_i + ChronicDuration.parse(start_in)
445
+ when start_at
446
+ Chronic.parse(start_at).to_i
447
+ else
448
+ Time.now.to_i
449
+ end
450
+
451
+ if entity_names
452
+ number_found = entity_names.length
453
+ case
454
+ when number_found == 0
455
+ msg = "found no entities matching /#{entity_pattern}/"
456
+ when number_found >= 1
457
+ entities = entity_names.map {|name|
458
+ Flapjack::Data::Entity.find_by_name(name, :redis => @redis)
459
+ }.compact.inject({}) {|memo, entity|
460
+ memo[entity] = entity.check_list.sort
461
+ memo
462
+ }
463
+ if entities.length >= 1
464
+ entities.each_pair do |entity,check_list|
465
+ check_list.each do |check|
466
+ entity_check = Flapjack::Data::EntityCheck.for_entity(entity, check, :redis => @redis)
467
+ entity_check.create_scheduled_maintenance(
468
+ maint_start,
469
+ maint_duration,
470
+ :summary => comment,
471
+ )
472
+ entity_check.update_current_scheduled_maintenance
473
+ end
474
+ end
475
+ msg = entities.inject("Maint list:\n") {|memo,kv|
476
+ kv[1].each {|e| memo << "#{kv[0].name}:#{e}\n" }
477
+ memo
478
+ }
479
+ else
480
+ msg = "found no matching entities with active checks"
481
+ end
482
+ else
483
+ msg = "that doesn't seem to be a valid pattern - /#{pattern}/"
484
+ end
485
+ end
486
+
426
487
  when /^(?:status )?entities\s+\/(.+)\/.*$/im
427
488
  entity_pattern = $1 ? $1.strip : nil
428
489
  entity_names = Flapjack::Data::Entity.find_all_name_matching(entity_pattern, :redis => @redis)
@@ -766,4 +827,3 @@ module Flapjack
766
827
 
767
828
  end
768
829
  end
769
-
@@ -7,6 +7,6 @@
7
7
  <% if ['acknowledgement'].include?(@alert.type) -%>
8
8
  has been acknowledged, unscheduled maintenance created for <%= time_period_in_words(@alert.acknowledgement_duration) -%>
9
9
  <% end -%>
10
- <% if @alert.summary && @alert.summary.length > 0 -%>
10
+ <% if @alert.summary && !@alert.summary.empty? -%>
11
11
  , <%= @alert.summary -%>
12
12
  <% end -%>
@@ -5,6 +5,6 @@
5
5
  <% if ['acknowledgement'].include?(@alert.type) -%>
6
6
  has been acknowledged, unscheduled maintenance created for <%= time_period_in_words(@alert.acknowledgement_duration) -%>
7
7
  <% end -%>
8
- <% if @alert.summary && @alert.summary.length > 0 -%>
8
+ <% if @alert.summary && !@alert.summary.empty? -%>
9
9
  , <%= @alert.summary -%>
10
10
  <% end -%>
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'em-synchrony'
4
+ require "em-synchrony/mysql2"
5
+ require 'flapjack/data/alert'
6
+ require 'flapjack/utility'
7
+
8
+ module Flapjack
9
+ module Gateways
10
+ class SmsGammu
11
+ INSERT_QUERY = <<-SQL
12
+ INSERT INTO outbox (InsertIntoDB, TextDecoded, DestinationNumber, CreatorID, Class)
13
+ VALUES ('%s', '%s', '%s', '%s', %s)
14
+ SQL
15
+
16
+ include Flapjack::Utility
17
+
18
+ def initialize(opts = {})
19
+ @config = opts[:config]
20
+ @logger = opts[:logger]
21
+ @redis_config = opts[:redis_config] || {}
22
+
23
+ if @config.nil? || (@config.respond_to?(:empty?) && @config.empty?)
24
+ @logger.error "sms_gammu config is missing"
25
+ return
26
+ end
27
+
28
+ @redis = Flapjack::RedisPool.new(:config => @redis_config, :size => 1, :logger => @logger)
29
+
30
+ @logger.info("starting")
31
+ @logger.debug("new sms_gammu gateway pikelet with the following options: #{@config.inspect}")
32
+
33
+ @sent = 0
34
+ end
35
+
36
+ def stop
37
+ @logger.info("stopping")
38
+ @should_quit = true
39
+
40
+ redis_uri = @redis_config[:path] ||
41
+ "redis://#{@redis_config[:host] || '127.0.0.1'}:#{@redis_config[:port] || '6379'}/#{@redis_config[:db] || '0'}"
42
+ shutdown_redis = EM::Hiredis.connect(redis_uri)
43
+ shutdown_redis.rpush(@config['queue'], Flapjack.dump_json('notification_type' => 'shutdown'))
44
+ end
45
+
46
+ def start
47
+ @db = Mysql2::EM::Client.new(:host => @config["mysql_host"],
48
+ :database => @config["mysql_database"],
49
+ :username => @config["mysql_username"],
50
+ :password => @config["mysql_password"])
51
+
52
+ queue = @config['queue']
53
+
54
+ until @should_quit
55
+ begin
56
+ @logger.debug("sms_gammu gateway is going into blpop mode on #{queue}")
57
+ deliver( Flapjack::Data::Alert.next(queue, :redis => @redis, :logger => @logger) )
58
+ rescue => e
59
+ @logger.error "Error generating or dispatching SMS Gammu message: #{e.class}: #{e.message}\n" +
60
+ e.backtrace.join("\n")
61
+ end
62
+ end
63
+ end
64
+
65
+ def deliver(alert)
66
+ @alert = alert
67
+ address = @alert.address
68
+ from = @config["from"]
69
+ message = prepare_message
70
+ errors = []
71
+
72
+ [[from, "SMS from address is missing"],
73
+ [address, "SMS address is missing"]].each do |val_err|
74
+
75
+ next unless val_err.first.nil? || (val_err.first.respond_to?(:empty?) && val_err.first.empty?)
76
+ errors << val_err.last
77
+ end
78
+
79
+ unless errors.empty?
80
+ errors.each {|err| @logger.error err }
81
+ return
82
+ end
83
+
84
+ send_message(message, from, address)
85
+ rescue => e
86
+ @logger.error "Error generating or delivering sms to #{contents['address']}: #{e.class}: #{e.message}"
87
+ @logger.error e.backtrace.join("\n")
88
+ raise
89
+ end
90
+
91
+ def prepare_message
92
+ message_type = @alert.rollup ? 'rollup' : 'alert'
93
+ template_path = @config['templates']["#{message_type}.text"]
94
+ template = ERB.new(File.read(template_path), nil, '-')
95
+
96
+ begin
97
+ message = template.result(binding).chomp
98
+ truncate(message, 159)
99
+ rescue => e
100
+ @logger.error "Error while excuting the ERB for an sms: " +
101
+ "ERB being executed: #{template_path}"
102
+ raise
103
+ end
104
+ end
105
+
106
+ def send_message(message, from, to)
107
+ begin
108
+ @db.query(INSERT_QUERY % [Time.now, message, to, from, 1])
109
+ @sent += 1
110
+ @alert.record_send_success!
111
+ @logger.debug "Sent SMS via Gammu"
112
+ rescue => e
113
+ @logger.error "Failed to send SMS via Gammu: #{e.class}, #{e.message}"
114
+ end
115
+ end
116
+
117
+ end
118
+ end
119
+ end
@@ -4,6 +4,8 @@ require 'em-synchrony'
4
4
  require 'em-synchrony/em-http'
5
5
  require 'active_support/inflector'
6
6
 
7
+ require 'flapjack/redis_pool'
8
+
7
9
  require 'flapjack/data/alert'
8
10
  require 'flapjack/utility'
9
11
 
@@ -13,95 +15,121 @@ module Flapjack
13
15
 
14
16
  MESSAGENET_DEFAULT_URL = 'https://www.messagenet.com.au/dotnet/Lodge.asmx/LodgeSMSMessage'
15
17
 
16
- class << self
17
-
18
- include Flapjack::Utility
18
+ include Flapjack::Utility
19
19
 
20
- def start
21
- @sent = 0
22
- end
20
+ def initialize(opts = {})
21
+ @config = opts[:config]
22
+ @logger = opts[:logger]
23
+ @redis_config = opts[:redis_config] || {}
24
+ @redis = Flapjack::RedisPool.new(:config => @redis_config, :size => 1, :logger => @logger)
23
25
 
24
- def perform(contents)
25
- @logger.debug "Woo, got a notification to send out: #{contents.inspect}"
26
- alert = Flapjack::Data::Alert.new(contents, :logger => @logger)
26
+ @logger.info("starting")
27
+ @logger.debug("new sms_messagenet gateway pikelet with the following options: #{@config.inspect}")
27
28
 
28
- endpoint = @config["endpoint"] || MESSAGENET_DEFAULT_URL
29
- username = @config["username"]
30
- password = @config["password"]
29
+ @sent = 0
30
+ end
31
31
 
32
- address = alert.address
33
- notification_id = alert.notification_id
34
- message_type = alert.rollup ? 'rollup' : 'alert'
32
+ def stop
33
+ @logger.info("stopping")
34
+ @should_quit = true
35
35
 
36
- my_dir = File.dirname(__FILE__)
37
- sms_template_path = case
38
- when @config.has_key?('templates') && @config['templates']["#{message_type}.text"]
39
- @config['templates']["#{message_type}.text"]
40
- else
41
- my_dir + "/sms_messagenet/#{message_type}.text.erb"
42
- end
43
- sms_template = ERB.new(File.read(sms_template_path), nil, '-')
36
+ redis_uri = @redis_config[:path] ||
37
+ "redis://#{@redis_config[:host] || '127.0.0.1'}:#{@redis_config[:port] || '6379'}/#{@redis_config[:db] || '0'}"
38
+ shutdown_redis = EM::Hiredis.connect(redis_uri)
39
+ shutdown_redis.rpush(@config['queue'], Flapjack.dump_json('notification_type' => 'shutdown'))
40
+ end
44
41
 
45
- @alert = alert
46
- bnd = binding
42
+ def start
43
+ queue = @config['queue']
47
44
 
45
+ until @should_quit
48
46
  begin
49
- message = sms_template.result(bnd).chomp
47
+ @logger.debug("sms_messagenet gateway is going into blpop mode on #{queue}")
48
+ deliver( Flapjack::Data::Alert.next(queue, :redis => @redis, :logger => @logger) )
50
49
  rescue => e
51
- @logger.error "Error while excuting the ERB for an sms: " +
52
- "ERB being executed: #{sms_template_path}"
53
- raise
54
- end
55
-
56
- if @config.nil? || (@config.respond_to?(:empty?) && @config.empty?)
57
- @logger.error "Messagenet config is missing"
58
- return
50
+ @logger.error "Error generating or dispatching SMS Messagenet message: #{e.class}: #{e.message}\n" +
51
+ e.backtrace.join("\n")
59
52
  end
53
+ end
54
+ end
60
55
 
61
- errors = []
56
+ def deliver(alert)
57
+ endpoint = @config["endpoint"] || MESSAGENET_DEFAULT_URL
58
+ username = @config["username"]
59
+ password = @config["password"]
60
+
61
+ address = alert.address
62
+ notification_id = alert.notification_id
63
+ message_type = alert.rollup ? 'rollup' : 'alert'
64
+
65
+ my_dir = File.dirname(__FILE__)
66
+ sms_template_path = case
67
+ when @config.has_key?('templates') && @config['templates']["#{message_type}.text"]
68
+ @config['templates']["#{message_type}.text"]
69
+ else
70
+ my_dir + "/sms_messagenet/#{message_type}.text.erb"
71
+ end
72
+ sms_template = ERB.new(File.read(sms_template_path), nil, '-')
62
73
 
63
- safe_message = truncate(message, 159)
74
+ @alert = alert
75
+ bnd = binding
64
76
 
65
- [[username, "Messagenet username is missing"],
66
- [password, "Messagenet password is missing"],
67
- [address, "SMS address is missing"],
68
- [notification_id, "Notification id is missing"]].each do |val_err|
77
+ begin
78
+ message = sms_template.result(bnd).chomp
79
+ rescue => e
80
+ @logger.error "Error while excuting the ERB for an sms: " +
81
+ "ERB being executed: #{sms_template_path}"
82
+ raise
83
+ end
69
84
 
70
- next unless val_err.first.nil? || (val_err.first.respond_to?(:empty?) && val_err.first.empty?)
71
- errors << val_err.last
72
- end
85
+ if @config.nil? || (@config.respond_to?(:empty?) && @config.empty?)
86
+ @logger.error "Messagenet config is missing"
87
+ return
88
+ end
73
89
 
74
- unless errors.empty?
75
- errors.each {|err| @logger.error err }
76
- return
77
- end
90
+ errors = []
78
91
 
79
- query = {'Username' => username,
80
- 'Pwd' => password,
81
- 'PhoneNumber' => address,
82
- 'PhoneMessage' => safe_message}
92
+ safe_message = truncate(message, 159)
83
93
 
84
- http = EM::HttpRequest.new(endpoint).get(:query => query)
94
+ [[username, "Messagenet username is missing"],
95
+ [password, "Messagenet password is missing"],
96
+ [address, "SMS address is missing"],
97
+ [notification_id, "Notification id is missing"]].each do |val_err|
85
98
 
86
- @logger.debug "server response: #{http.response}"
99
+ next unless val_err.first.nil? || (val_err.first.respond_to?(:empty?) && val_err.first.empty?)
100
+ errors << val_err.last
101
+ end
87
102
 
88
- status = (http.nil? || http.response_header.nil?) ? nil : http.response_header.status
89
- if (status >= 200) && (status <= 206)
90
- @sent += 1
91
- alert.record_send_success!
92
- @logger.debug "Sent SMS via Messagenet, response status is #{status}, " +
93
- "notification_id: #{notification_id}"
94
- else
95
- @logger.error "Failed to send SMS via Messagenet, response status is #{status}, " +
96
- "notification_id: #{notification_id}"
97
- end
98
- rescue => e
99
- @logger.error "Error generating or delivering sms to #{contents['address']}: #{e.class}: #{e.message}"
100
- @logger.error e.backtrace.join("\n")
101
- raise
103
+ unless errors.empty?
104
+ errors.each {|err| @logger.error err }
105
+ return
102
106
  end
103
107
 
108
+ query = {'Username' => username,
109
+ 'Pwd' => password,
110
+ 'PhoneNumber' => address,
111
+ 'PhoneMessage' => safe_message}
112
+
113
+ http = EM::HttpRequest.new(endpoint).get(:query => query)
114
+
115
+ @logger.debug "server response: #{http.response}"
116
+
117
+ status = (http.nil? || http.response_header.nil?) ? nil : http.response_header.status
118
+ if (status >= 200) && (status <= 206)
119
+ @sent += 1
120
+ alert.record_send_success!
121
+ @logger.debug "Sent SMS via Messagenet, response status is #{status}, " +
122
+ "notification_id: #{notification_id}"
123
+ else
124
+ @logger.error "Failed to send SMS via Messagenet, response status is #{status}, " +
125
+ "notification_id: #{notification_id}"
126
+ end
127
+ rescue => e
128
+ @logger.error "Error generating or delivering sms to #{alert.address}: #{e.class}: #{e.message}"
129
+ @logger.error e.backtrace.join("\n")
130
+ raise
104
131
  end
132
+
105
133
  end
106
134
  end
107
135
  end
@@ -1,5 +1,6 @@
1
+ <% summary = @alert.summary -%>
1
2
  <%= @alert.type_sentence_case %>: '<%= @alert.check %>' on <%= @alert.entity -%>
2
3
  <% unless ['acknowledgement', 'test'].include?(@alert.notification_type) -%>
3
4
  is <%= @alert.state_title_case -%>
4
5
  <% end -%>
5
- at <%= Time.at(@alert.time).strftime('%-d %b %H:%M') %>, <%= @alert.summary -%>
6
+ at <%= Time.at(@alert.time).strftime('%-d %b %H:%M') %><%= (summary.nil? || summary.empty?) ? '' : ", #{summary}" -%>