flapjack 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'open-uri'
4
+
5
+ page = ARGV[0]
6
+ string = ARGV[1]
7
+
8
+ exit 2 unless page && string
9
+
10
+ body = open(page).read
11
+ if body =~ /#{string}/
12
+ exit 0
13
+ else
14
+ exit 2
15
+ end
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'ostruct'
5
+ require 'optparse'
6
+ require 'log4r'
7
+ require 'log4r/outputter/syslogoutputter'
8
+ require File.join(File.dirname(__FILE__), '..', 'database')
9
+ require File.join(File.dirname(__FILE__), '..', 'notifier')
10
+ require File.join(File.dirname(__FILE__), '..', 'result')
11
+
12
+ module Flapjack
13
+ class NotifierOptions
14
+ def self.parse(args)
15
+ options = OpenStruct.new
16
+ opts = OptionParser.new do |opts|
17
+ # the available command line options
18
+ opts.on('-b', '--beanstalk HOST', 'location of the beanstalkd') do |host|
19
+ options.host = host
20
+ end
21
+ opts.on('-p', '--port PORT', 'beanstalkd port') do |port|
22
+ options.port = port.to_i
23
+ end
24
+ opts.on('-r', '--recipients FILE', 'recipients file') do |recipients|
25
+ options.recipients = recipients.to_s
26
+ end
27
+ opts.on('-c', '--config FILE', 'config file') do |config|
28
+ options.config_filename = config.to_s
29
+ end
30
+ opts.on('-d', '--database-uri URI', 'location of the checks database') do |db|
31
+ options.database_uri = db.to_s
32
+ end
33
+ opts.on_tail("-h", "--help", "Show this message") do
34
+ puts opts
35
+ exit
36
+ end
37
+ end
38
+
39
+ # parse the options
40
+ begin
41
+ opts.parse!(args)
42
+ rescue OptionParser::MissingArgument => e
43
+ # if an --option is missing it's argument
44
+ puts e.message.capitalize + "\n\n"
45
+ puts opts
46
+ exit 1
47
+ end
48
+
49
+ # default the host + port
50
+ options.host ||= 'localhost'
51
+ options.port ||= 11300
52
+
53
+ @errors = []
54
+ # check that recipients file exists
55
+ if options.recipients
56
+ unless File.exists?(options.recipients.to_s)
57
+ @errors << "The specified recipients file doesn't exist!"
58
+ end
59
+ else
60
+ @errors << "You need to specify a recipients file with [-r|--recipients]."
61
+ end
62
+
63
+ # check that config file exists
64
+ if options.config_filename
65
+ unless File.exists?(options.config_filename.to_s)
66
+ @errors << "The specified config file doesn't exist!"
67
+ end
68
+ else
69
+ options.config_filename ||= "/etc/flapjack/flapjack-notifier.yaml"
70
+ unless File.exists?(options.config_filename.to_s)
71
+ @errors << "The default config file (#{options.config_filename}) doesn't exist!"
72
+ @errors << "Set one up, or specify one with [-c|--config]."
73
+ end
74
+ end
75
+
76
+ # if there are errors, print them out and exit
77
+ if @errors.size > 0
78
+ puts "Errors:"
79
+ @errors.each do |error|
80
+ puts " - #{error}"
81
+ end
82
+ puts
83
+ puts opts
84
+ exit 2
85
+ end
86
+
87
+ options
88
+ end
89
+ end
90
+
91
+ class NotifierCLI
92
+ attr_accessor :log, :recipients, :results_queue, :config
93
+ attr_accessor :notifier, :notifiers
94
+ attr_accessor :condition
95
+
96
+ def initialize(opts={})
97
+ @log = opts[:logger]
98
+ @log ||= Log4r::Logger.new("notifier")
99
+ end
100
+
101
+ def setup_loggers
102
+ @log.add(Log4r::StdoutOutputter.new('notifier'))
103
+ @log.add(Log4r::SyslogOutputter.new('notifier'))
104
+ end
105
+
106
+ def setup_recipients(opts={})
107
+
108
+ if opts[:yaml]
109
+ yaml = opts[:yaml]
110
+ else
111
+ opts[:filename] ||= File.join(Dir.pwd, "recipients.yaml")
112
+ yaml = YAML::load(File.read(opts[:filename]))
113
+ end
114
+
115
+ # FIXME: add error detection for passing in dumb things
116
+
117
+ @recipients = yaml.map do |r|
118
+ OpenStruct.new(r)
119
+ end
120
+ end
121
+
122
+ def setup_config(opts={})
123
+ if opts[:yaml]
124
+ yaml = opts[:yaml]
125
+ else
126
+ opts[:filename] ||= File.join(Dir.pwd, "flapjack-notifier.yaml")
127
+ yaml = YAML::load(File.read(opts[:filename]))
128
+ end
129
+
130
+ @config = OpenStruct.new(yaml)
131
+ end
132
+
133
+ def setup_database(opts={})
134
+ uri = (opts[:database_uri] || @config.database_uri)
135
+ raise ArgumentError, "database URI wasn't specified!" unless uri
136
+ DataMapper.setup(:default, uri)
137
+ validate_database
138
+ end
139
+
140
+ def validate_database
141
+ begin
142
+ DataMapper.repository(:default).adapter.execute("SELECT 'id' FROM 'checks';")
143
+ rescue Sqlite3Error
144
+ @log.warning("The specified database doesn't appear to have any structure!")
145
+ @log.warning("You need to investigate this.")
146
+ end
147
+ # FIXME: add more rescues in here!
148
+ end
149
+
150
+ def initialize_notifiers(opts={})
151
+ notifiers_directory = opts[:notifiers_directory]
152
+ notifiers_directory ||= File.expand_path(File.join(File.dirname(__FILE__), '..', 'notifiers'))
153
+
154
+ raise ArgumentError, "notifiers directory doesn't exist!" unless File.exists?(notifiers_directory)
155
+
156
+ @notifiers = []
157
+
158
+ @config.notifiers.each_pair do |notifier, config|
159
+ filename = File.join(notifiers_directory, notifier.to_s, 'init')
160
+ if File.exists?(filename + '.rb')
161
+ @log.debug("Loading the #{notifier.to_s.capitalize} notifier")
162
+ require filename
163
+ config.merge!(:logger => @log, :website_uri => @config.website_uri)
164
+ @notifiers << Flapjack::Notifiers.const_get("#{notifier.to_s.capitalize}").new(config)
165
+ else
166
+ @log.warning("Flapjack::Notifiers::#{notifier.to_s.capitalize} doesn't exist!")
167
+ end
168
+ end
169
+
170
+ @notifiers
171
+ end
172
+
173
+ # Sets up notifier to do the grunt work of notifying people when checks
174
+ # return badly.
175
+ #
176
+ # Accepts a list of recipients (:recipients) and a logger (:logger) as
177
+ # arguments. If neither of these are specified, it will default to an
178
+ # existing list of recipients and the current logger.
179
+ #
180
+ # Sets up and returns @notifier, an instance of Flapjack::Notifier
181
+ def setup_notifier(opts={})
182
+ recipients = (opts[:recipients] || @recipients)
183
+ log = (opts[:logger] || @log)
184
+ initialize_notifiers
185
+ notifiers = @notifiers # should we accept a list of notifiers?
186
+ @notifier = Flapjack::Notifier.new(:logger => log,
187
+ :notifiers => notifiers,
188
+ :recipients => recipients)
189
+ end
190
+
191
+ def process_loop
192
+ @log.info("Processing results...")
193
+ loop do
194
+ process_result
195
+ end
196
+ end
197
+
198
+ # FIXME: we're doing a lookup twice - optimise out
199
+ def save_result(result)
200
+ if check = Check.get(result.id)
201
+ check.status = result.retval
202
+ check.save
203
+ else
204
+ @log.error("Got result for check #{result.id}, but it doesn't exist!")
205
+ end
206
+ end
207
+
208
+ # FIXME: we're doing a lookup twice - optimise out
209
+ def any_parents_warning_or_critical?(result)
210
+ if check = Check.get(result.id)
211
+ check.worst_parent_status > 0 ? true : false
212
+ else
213
+ @log.error("Got result for check #{result.id}, but it doesn't exist!")
214
+ end
215
+ end
216
+
217
+ def process_result
218
+ @log.debug("Waiting for new result...")
219
+ result_job = @results_queue.reserve # blocks until a job is reserved
220
+ result = Flapjack::Result.new(YAML::load(result_job.body))
221
+
222
+ @log.info("Processing result for check '#{result.id}'")
223
+ if result.warning? || result.critical?
224
+ if any_parents_warning_or_critical?(result)
225
+ @log.info("Not notifying on check '#{result.id}' as parent is failing.")
226
+ else
227
+ @log.info("Notifying on check '#{result.id}'")
228
+ @notifier.notify!(result)
229
+ end
230
+
231
+ @log.info("Creating event for check '#{result.id}'")
232
+ event = Event.new(:check_id => result.id)
233
+ raise unless event.save
234
+ end
235
+
236
+ @log.info("Storing status of check in database")
237
+ save_result(result)
238
+
239
+ @log.debug("Deleting result for check '#{result.id}' from queue")
240
+ result_job.delete
241
+ end
242
+
243
+ end
244
+
245
+ end
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'ostruct'
5
+ require 'optparse'
6
+
7
+ module Flapjack
8
+ class NotifierManagerOptions
9
+ def self.parse(args)
10
+ options = OpenStruct.new
11
+ opts = OptionParser.new do |opts|
12
+ opts.banner = "Usage: flapjack-notifier-manager <command> [options]"
13
+ opts.separator " "
14
+ opts.separator " where <command> is one of:"
15
+ opts.separator " start start a worker"
16
+ opts.separator " stop stop all workers"
17
+ opts.separator " restart restart workers"
18
+ opts.separator " "
19
+ opts.separator " and [options] are:"
20
+
21
+ opts.on('-b', '--beanstalk HOST', 'location of the beanstalkd') do |host|
22
+ options.host = host
23
+ end
24
+ opts.on('-p', '--port PORT', 'beanstalkd port') do |port|
25
+ options.port = port.to_s
26
+ end
27
+ opts.on('-r', '--recipients FILE', 'recipients file') do |recipients|
28
+ options.recipients = File.expand_path(recipients.to_s)
29
+ end
30
+ opts.on('-c', '--config FILE', 'config file') do |config|
31
+ options.config_filename = config.to_s
32
+ end
33
+ end
34
+
35
+ begin
36
+ opts.parse!(args)
37
+ rescue => e
38
+ puts e.message.capitalize + "\n\n"
39
+ puts opts
40
+ exit 1
41
+ end
42
+
43
+ # defaults
44
+ options.host ||= "localhost"
45
+ options.port ||= 11300
46
+
47
+ @errors = []
48
+
49
+ unless ARGV[0] == "stop"
50
+ if options.recipients
51
+ unless File.exists?(options.recipients)
52
+ @errors << "The specified recipients file doesn't exist!"
53
+ end
54
+ else
55
+ @errors << "You must specify a recipients file!"
56
+ end
57
+
58
+ if options.config_filename
59
+ unless File.exists?(options.config_filename)
60
+ @errors << "The specified config file dosen't exist!"
61
+ end
62
+ else
63
+ @errors << "You must specify a config file!"
64
+ end
65
+ end
66
+
67
+ if @errors.size > 0
68
+ puts "Errors:"
69
+ @errors.each do |error|
70
+ puts " - #{error}"
71
+ end
72
+ puts
73
+ puts opts
74
+ exit 2
75
+ end
76
+
77
+ unless %w(start stop restart).include?(args[0])
78
+ puts opts
79
+ exit 1
80
+ end
81
+
82
+ options
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'beanstalk-client'
5
+ require 'ostruct'
6
+ require 'optparse'
7
+ require 'log4r'
8
+ require 'log4r/outputter/syslogoutputter'
9
+ require 'flapjack/result'
10
+ require 'flapjack/patches'
11
+
12
+ module Flapjack
13
+ class WorkerOptions
14
+ def self.parse(args)
15
+ options = OpenStruct.new
16
+ opts = OptionParser.new do |opts|
17
+ # the available command line options
18
+ opts.on('-b', '--beanstalk HOST', 'location of the beanstalkd') do |host|
19
+ options.host = host
20
+ end
21
+ opts.on('-p', '--port PORT', 'beanstalkd port') do |port|
22
+ options.port = port.to_i
23
+ end
24
+ opts.on('-c', '--checks-directory DIR', 'sandboxed check directory') do |dir|
25
+ options.checks_directory = dir.to_s
26
+ end
27
+ opts.on_tail("-h", "--help", "Show this message") do
28
+ puts opts
29
+ exit
30
+ end
31
+ end
32
+
33
+ # parse the options
34
+ begin
35
+ opts.parse!(args)
36
+ rescue OptionParser::MissingArgument => e
37
+ # if an --option is missing it's argument
38
+ puts e.message.capitalize + "\n\n"
39
+ puts opts
40
+ exit 1
41
+ end
42
+
43
+ # default the port
44
+ options.host ||= 'localhost'
45
+ options.port ||= 11300
46
+ options.checks_directory ||= File.join(File.dirname(__FILE__), '..', 'checks')
47
+
48
+ options
49
+ end
50
+ end
51
+
52
+ class Worker
53
+
54
+ attr_accessor :jobs, :results, :log
55
+
56
+ def initialize(opts={})
57
+ @jobs = Beanstalk::Pool.new(["#{opts[:host]}:#{opts[:port]}"], 'jobs')
58
+ @results = Beanstalk::Pool.new(["#{opts[:host]}:#{opts[:port]}"], 'results')
59
+ @sandbox = (opts[:check_directory] || File.expand_path(File.join(File.dirname(__FILE__), '..', 'checks')))
60
+
61
+ if opts[:logger]
62
+ @log = opts[:logger]
63
+ else
64
+ @log = Log4r::Logger.new('worker')
65
+ @log.add(Log4r::StdoutOutputter.new('worker'))
66
+ @log.add(Log4r::SyslogOutputter.new('worker'))
67
+ end
68
+ end
69
+
70
+ def process_loop
71
+ @log.info("Booting main loop...")
72
+ loop do
73
+ process_check
74
+ end
75
+ end
76
+
77
+ def process_check
78
+ # get next check off the beanstalk
79
+ job, check = get_check()
80
+
81
+ # do the actual check
82
+ result, retval = perform_check(check.command)
83
+
84
+ # report the results of the check
85
+ report_check(:result => result, :retval => retval, :check => check)
86
+
87
+ # create another job for the check, delete current job
88
+ cleanup_job(:job => job, :check => check)
89
+ end
90
+
91
+ def perform_check(cmd)
92
+ command = "sh -c '#{@sandbox}/#{cmd}'"
93
+ @log.debug("Executing check: \"#{command}\"")
94
+ result = `#{command}`
95
+ retval = $?.exitstatus
96
+
97
+ return result, retval
98
+ end
99
+
100
+ def report_check(opts={})
101
+ raise ArgumentError unless (opts[:result] && opts[:retval] && opts[:check])
102
+
103
+ @log.debug "Reporting results for check id #{opts[:check].id}."
104
+ @results.yput({:id => opts[:check].id,
105
+ :output => opts[:result],
106
+ :retval => opts[:retval].to_i})
107
+ end
108
+
109
+ def cleanup_job(opts={})
110
+ raise ArgumentError unless (opts[:job] && opts[:check])
111
+
112
+ # add job back onto stack
113
+ @log.debug("Putting check back onto beanstalk.")
114
+ @jobs.yput(opts[:check].to_h, 65536, opts[:check].frequency)
115
+
116
+ # FIXME: what happens when power goes out here?
117
+
118
+ # once we're done, clean up
119
+ @log.debug("Deleting job.")
120
+ opts[:job].delete
121
+ end
122
+
123
+ def get_check
124
+ @log.debug("Waiting for check...")
125
+ job = @jobs.reserve
126
+ # FIXME: maybe wrap Result as Job now that Check is reserved?
127
+ check = Result.new(YAML::load(job.body))
128
+ @log.info("Got check with id #{check.id}")
129
+
130
+ return job, check
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+