auxesis-flapjack 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,186 @@
1
+ Flapjack
2
+ ========
3
+
4
+ Flapjack is highly scalable and distributed monitoring system.
5
+
6
+ It understands the Nagios plugin format, and can easily be scaled
7
+ from 1 server to 1000.
8
+
9
+
10
+
11
+ Setup dependencies (Ubuntu Hardy)
12
+ ---------------------------------
13
+
14
+ Add the following lines to `/etc/apt/sources.list`
15
+
16
+ deb http://ppa.launchpad.net/ubuntu-ruby-backports/ubuntu hardy main
17
+ deb http://ppa.launchpad.net/auxesis/ppa/ubuntu hardy main
18
+
19
+ Add GPG keys for the repos:
20
+
21
+ sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 288BA53BCB7DA731
22
+
23
+ Update your package list:
24
+
25
+ sudo aptitude update
26
+
27
+ Install rubygems + beanstalkd:
28
+
29
+ sudo apt-get install rubygems beanstalkd
30
+
31
+ Set `ENABLED=1` in `/etc/default/beanstalkd`.
32
+
33
+ Start beanstalkd:
34
+
35
+ sudo /etc/init.d/beanstalkd start
36
+
37
+
38
+ Setup dependencies (everyone else)
39
+ ----------------------------------
40
+
41
+ Install the following software through your package manager or from source:
42
+
43
+ - beanstalkd (from http://xph.us/software/beanstalkd/)
44
+ - libevent (from http://monkey.org/~provos/libevent/)
45
+
46
+
47
+ Installation
48
+ ------------
49
+
50
+ From the checked out code, build + install the gem:
51
+
52
+ rake build
53
+ sudo gem install pkg/flapjack-<latest>.gem
54
+
55
+ Then run the magic configuration script to set up init scripts:
56
+
57
+ sudo install-flapjack-systemwide
58
+
59
+ The script will prompt you if it wants to do anything destructive.
60
+
61
+
62
+ Running
63
+ -------
64
+
65
+ Make sure beanstalkd is running.
66
+
67
+ You'll want to set up `/etc/flapjack/recipients.yaml` so notifications can be sent via
68
+ `flapjack-notifier`:
69
+
70
+ - :name: Jane Doe
71
+ :email: "jane@doe.com"
72
+ :phone: "+61 444 222 111"
73
+ :pager: "61444222111"
74
+ :jid: "jane@doe.com"
75
+
76
+ Start up a cluster of workers:
77
+
78
+ flapjack-worker-manager start
79
+
80
+ This will spin up 5 workers in the background. You can specify how many workers
81
+ to start:
82
+
83
+ flapjack-worker-manager start --workers=10
84
+
85
+ Each of the `flapjack-worker`s will output to syslog (check in /var/log/messages).
86
+
87
+ Start up the notifier:
88
+
89
+ flapjack-notifier --recipients /etc/flapjack/recipients.yaml
90
+
91
+ Currently there are email and XMPP notifiers.
92
+
93
+ You'll want to get a copy of (http://github.com/auxesis/flapjack-admin/)[flapjack-admin]
94
+ to set up some checks, then run its' populator to get them into Flapjack.
95
+
96
+ What things do
97
+ --------------
98
+
99
+ * `flapjack-worker` => executes checks, reports results
100
+ * `flapjack-worker-manager` => starts/stops a cluster of `flapjack-worker`
101
+ * `flapjack-notifier` => gets results, notifies people if necessary
102
+ * `flapjack-stats` => gets stats from beanstalkd tubes (useful for benchmarks + performance analysis)
103
+
104
+
105
+ init scripts
106
+ ------------
107
+
108
+ You can use the provided init scripts to start Flapjack on boot.
109
+
110
+ To start:
111
+
112
+ /etc/init.d/flapjack-workers start
113
+ /etc/init.d/flapjack-notifier start
114
+
115
+ To set Flapjack to start on boot (Ubuntu):
116
+
117
+ sudo update-rc.d flapjack-workers defaults
118
+ sudo update-rc.d flapjack-notifier defaults
119
+
120
+ Config for the init scripts can be found in `/etc/defaults`.
121
+
122
+
123
+
124
+ Developing
125
+ ----------
126
+
127
+ You can write your own notifiers and place them in `lib/flapjack/notifiers/`.
128
+
129
+ Your notifier just needs to implement the `notify` method, and take in a hash:
130
+
131
+ class Sms
132
+ def initialize(opts={})
133
+ # you may want to set from address here
134
+ end
135
+
136
+ def notify(opts={})
137
+ who = opts[:who]
138
+ result = opts[:result]
139
+ # sms to your hearts content
140
+ end
141
+ end
142
+
143
+
144
+ Testing
145
+ -------
146
+
147
+ Tests are in `spec/`.
148
+
149
+ To run tests:
150
+
151
+ $ rake spec
152
+
153
+
154
+ Architecture
155
+ ------------
156
+
157
+ -------------------
158
+ | web interface / |
159
+ | dsl / flat file |
160
+ -------------------
161
+ /
162
+ |
163
+ |
164
+ ------------- ------------
165
+ | populator |--- -----| notifier |
166
+ ------------- | | ------------
167
+ | |
168
+ --------------
169
+ | beanstalkd |
170
+ --------------
171
+ |
172
+ -----------------------------------
173
+ | | |
174
+ ---------- ---------- ----------
175
+ | worker | | worker | | worker |
176
+ ---------- ---------- ----------
177
+
178
+
179
+ - populator determines checks that need to occur, and puts checks onto `jobs` tube
180
+ - populator fetches jobs from a database
181
+ - populator can be swapped out to get checks from a dsl or flat files
182
+ - workers pop a check off the beanstalk, perform check, put result onto `results` tube,
183
+ re-add check to `jobs` tube with a delay.
184
+ - notifier pops results off `results` tube, notifies as necessary
185
+
186
+
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'fileutils'
5
+ require 'spec/rake/spectask'
6
+
7
+
8
+ Spec::Rake::SpecTask.new do |t|
9
+ t.spec_opts = ["--options", "spec/spec.opts"]
10
+ end
11
+
12
+
13
+ desc "freeze deps"
14
+ task :deps do
15
+
16
+ deps = {'beanstalk-client' => ">= 1.0.2",
17
+ 'log4r' => ">= 1.0.5",
18
+ 'xmpp4r-simple' => ">= 0.8.8",
19
+ 'mailfactory' => ">= 1.4.0"}
20
+
21
+ puts "\ninstalling dependencies. this will take a few minutes."
22
+
23
+ deps.each_pair do |dep, version|
24
+ puts "\ninstalling #{dep} (#{version})"
25
+ system("gem install #{dep} --version '#{version}' -i gems --no-rdoc --no-ri")
26
+ end
27
+
28
+ end
29
+
30
+ desc "generate list of files for gemspec"
31
+ task "gengemfiles" do
32
+ executables = `git ls-files bin/*`.split.map {|bin| bin.gsub(/^bin\//, '')}
33
+ files = `git ls-files`.split.delete_if {|file| file =~ /^(spec\/|\.gitignore)/}
34
+ puts
35
+ puts "Copy and paste into flapjack.gemspec:"
36
+ puts
37
+ puts " s.executables = #{executables.inspect}"
38
+ puts " s.files = #{files.inspect}"
39
+ puts
40
+ puts
41
+ end
42
+
43
+ desc "build gem"
44
+ task :build do
45
+ system("gem build flapjack.gemspec")
46
+
47
+ FileUtils.mkdir_p('pkg')
48
+ puts
49
+ puts "Flapjack gems:"
50
+ Dir.glob("flapjack-*.gem").each do |gem|
51
+ dest = File.join('pkg', gem)
52
+ FileUtils.mv gem, dest
53
+ puts " " + dest
54
+ end
55
+ end
56
+
57
+
58
+ if require 'yard'
59
+
60
+ YARD::Rake::YardocTask.new do |t|
61
+ t.files = ['lib/**/*.rb']
62
+ t.options = ['--output-dir=doc/', '--readme=README.md']
63
+ end
64
+
65
+ end
data/TODO.md ADDED
@@ -0,0 +1,10 @@
1
+ * daemonise notifier
2
+ * provide config file for notifier
3
+ * specify which notifier plugins to load
4
+ * specify configuration for plugins (from address/xmmp login)
5
+ * write init scripts for notifier/worker-manager
6
+ * hook notifier into web interface
7
+ * update status of checks
8
+ * relationships + cascading notifications
9
+ * package with pallet
10
+ * generate .deb/.rpms
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+
5
+ require 'rubygems'
6
+ require 'beanstalk-client'
7
+ require 'ostruct'
8
+ require 'optparse'
9
+ require 'log4r'
10
+ require 'log4r/outputter/syslogoutputter'
11
+ require 'flapjack/result'
12
+ require 'flapjack/notifier'
13
+ require 'flapjack/notifiers/mailer'
14
+ require 'flapjack/notifiers/xmpp'
15
+ require 'daemons'
16
+ require 'flapjack/patches'
17
+
18
+ # command line options are in here
19
+ require 'flapjack/cli/notifier'
20
+
21
+ # boot up the notifier
22
+ @options = Flapjack::NotifierOptions.parse(ARGV)
23
+
24
+ ncli = Flapjack::NotifierCLI.new
25
+ ncli.setup_loggers
26
+ ncli.setup_recipients(:filename => @options.recipients)
27
+
28
+ ncli.log.debug("Loading Mailer notifier")
29
+ mailer = Flapjack::Notifiers::Mailer.new
30
+ ncli.log.debug("Loading XMPP notifier")
31
+ xmpp = Flapjack::Notifiers::Xmpp.new(:jid => "flapjack-test@jabber.org", :password => "test")
32
+ ncli.log.debug("Bringing up notifier subsystem")
33
+ ncli.notifier = Notifier.new(:logger => ncli.log,
34
+ :notifiers => [mailer], #, xmpp],
35
+ :recipients => ncli.recipients)
36
+
37
+ begin
38
+
39
+ ncli.results_queue = Beanstalk::Pool.new(["#{@options.host}:#{@options.port}"], 'results')
40
+ ncli.log.info("established connection to beanstalkd on #{@options.host}...")
41
+
42
+ # process results
43
+ ncli.process_loop
44
+
45
+ rescue Beanstalk::NotConnected
46
+ ncli.log.error("Couldn't connect to the Beanstalk!")
47
+
48
+ timeout = 5
49
+ ncli.log.error("Retrying in #{timeout} seconds.")
50
+ sleep timeout
51
+
52
+ ncli.log.error("Retrying...")
53
+ retry
54
+ end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+
5
+ require 'rubygems'
6
+ require 'daemons'
7
+ require 'fileutils'
8
+ require 'flapjack/cli/notifier_manager'
9
+ require 'flapjack/patches' # for Daemons
10
+
11
+ # reassign ARGV so we don't mess with it directly
12
+ args = ARGV
13
+ args << '--help' if args.size == 0
14
+ options = Flapjack::NotifierManagerOptions.parse(args)
15
+
16
+ worker_path = File.join(File.dirname(__FILE__), 'flapjack-notifier')
17
+
18
+ # set up pid dir
19
+ pid_dir = "/var/run/flapjack"
20
+ FileUtils.mkdir_p(pid_dir)
21
+
22
+ daemon_args = (args + ['--', '--beanstalk', options.host,
23
+ '--port', options.port.to_s,
24
+ '--recipients', options.recipients])
25
+
26
+ Daemons.run(worker_path, :ARGV => daemon_args,
27
+ :multiple => false,
28
+ :dir_mode => :normal,
29
+ :dir => pid_dir)
30
+
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'beanstalk-client'
5
+
6
+ begin
7
+ beanstalk = Beanstalk::Pool.new(['localhost:11300'])
8
+ loop do
9
+ time = Time.now
10
+ beanstalk.list_tubes['localhost:11300'].each do |tube_name|
11
+ next if tube_name == 'default'
12
+ stats = beanstalk.stats_tube(tube_name)
13
+ puts "#{time.to_i} [#{tube_name}] total: #{stats['total-jobs']}, waiting: #{stats['current-waiting']}, ready: #{stats['current-jobs-ready']}"
14
+ end
15
+ sleep 1
16
+ end
17
+ rescue Beanstalk::NotConnected
18
+ puts "beanstalk isn't up!"
19
+ exit 2
20
+ end
21
+
22
+
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+
5
+ require 'rubygems'
6
+ require 'log4r'
7
+ require 'log4r/outputter/syslogoutputter'
8
+ require 'flapjack/cli/worker'
9
+
10
+ at_exit do
11
+ puts "Shutting down"
12
+ end
13
+
14
+ trap("INT") do
15
+ puts "Caught shutdown signal, cleaning up."
16
+ exit
17
+ end
18
+
19
+ @options = Flapjack::WorkerOptions.parse(ARGV)
20
+ @worker = Flapjack::Worker.new(:host => @options.host, :port => @options.port)
21
+
22
+ begin
23
+ @worker.process_loop
24
+ rescue Beanstalk::NotConnected
25
+ puts "Couldn't connect to the beanstalk!"
26
+
27
+ timeout = 5
28
+ puts "Retrying in #{timeout} seconds"
29
+ sleep timeout
30
+
31
+ puts "Retrying..."
32
+ retry
33
+ end
34
+
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+
5
+ require 'rubygems'
6
+ require 'daemons'
7
+ require 'fileutils'
8
+ require 'flapjack/cli/worker_manager'
9
+ require 'flapjack/patches' # for Daemons
10
+
11
+ # reassign ARGV so we don't mess with it directly
12
+ args = ARGV
13
+ args << '--help' if args.size == 0
14
+ options = WorkerManagerOptions.parse(args)
15
+
16
+ worker_path = File.join(File.dirname(__FILE__), 'flapjack-worker')
17
+
18
+ # set up pid dir
19
+ pid_dir = "/var/run/flapjack"
20
+ FileUtils.mkdir_p(pid_dir)
21
+
22
+ # spin up a number of workers (5 is the default).
23
+ options.workers.times do |n|
24
+ # we fork for each worker, as Daemons.run backgrounds this script.
25
+ fork do
26
+ Daemons.run(worker_path,
27
+ :ARGV => (args + %w(-- -b localhost)),
28
+ :multiple => true,
29
+ :dir_mode => :normal,
30
+ :dir => pid_dir)
31
+ end
32
+ end
33
+
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # 28 05 2009
4
+ # Lindsay Holmwood <lindsay@holmwood.id.au>
5
+ #
6
+ # Copies various scripts to appropriate places so Flapjack can be
7
+ # started as a daemon.
8
+ #
9
+ # I abhor these sort of scripts. It's only a stopgap until Flapjack
10
+ # gets packaged properly.
11
+ #
12
+ # If you would like to package, please let me know! Patches welcome.
13
+ #
14
+
15
+ unless ENV["USERNAME"] == "root"
16
+ puts 'You need to be root to run this script'
17
+ exit 1
18
+ end
19
+
20
+ # prompt/warn the user what is about to happen
21
+ puts "This script sets up Flapjack to run as a daemon. Specifically it: "
22
+ puts
23
+ puts " * installs init scripts and configs into /etc"
24
+ puts " * sets up /var/run/flapjack "
25
+ puts " * sets Flapjack to run on boot"
26
+ puts
27
+ puts "It will prompt you if it wants to overwrite an existing file."
28
+ puts
29
+ print "Do you want to continue? [y/n] "
30
+ continue = gets.strip
31
+ puts
32
+
33
+ unless continue == "y"
34
+ puts "Exiting."
35
+ exit
36
+ end
37
+
38
+ # setup
39
+ system("mkdir -p /var/run/flapjack")
40
+ system("chmod a+rw /var/run/flapjack")
41
+
42
+ etc_path=`gem contents flapjack |grep etc`.split.first.gsub(/etc\/.+/, 'etc').strip
43
+ system("cp -aiv #{etc_path}/* /etc")
44
+
45
+ # set sequence number to 50 so beanstalkd has a chance to boot
46
+ system("update-rc.d flapjack-workers defaults 50")
47
+ system("update-rc.d flapjack-notifier defaults 50")
48
+
49
+ puts
50
+ puts "Setup complete!"
51
+ puts
52
+ puts "You may want to customise /etc/flapjack/recipients.yaml"
53
+
54
+
@@ -0,0 +1,15 @@
1
+ # Defaults for flapjack-notifier initscript
2
+ # sourced by /etc/init.d/flapjack-notifier
3
+
4
+ # set enabled to 1 if you want the init script to start flapjack-notifier
5
+ ENABLED=1
6
+
7
+ # recipients file
8
+ RECIPIENTS=/etc/flapjack/recipients.yaml
9
+
10
+ # address beanstalkd is bound to
11
+ BEANSTALKD_ADDR=localhost
12
+
13
+ # port beanstalk is listening on
14
+ BEANSTALKD_PORT=11300
15
+
@@ -0,0 +1,14 @@
1
+ # Defaults for flapjack-workers initscript
2
+ # sourced by /etc/init.d/flapjack-workers
3
+
4
+ # set enabled to 1 if you want the init script to start flapjack-workers
5
+ ENABLED=1
6
+
7
+ # address beanstalkd is bound to
8
+ BEANSTALKD_ADDR=localhost
9
+
10
+ # port beanstalk is listening on
11
+ BEANSTALKD_PORT=11300
12
+
13
+ # number of workers to spin up
14
+ WORKERS=5
@@ -0,0 +1,10 @@
1
+ - :name: Jane Doe
2
+ :email: "jane@doe.com"
3
+ :phone: "+1 444 333 888"
4
+ :pager: "1444333888"
5
+ :jid: "jane@doe.com"
6
+ - :name: John Doe
7
+ :email: "john@doe.com"
8
+ :phone: "+1 483 401 501"
9
+ :pager: "1888777333"
10
+ :jid: "john@doe.com"
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ #
3
+ # Copyright (c) 2009 Lindsay Holmwood <lindsay@holmwood.id.au>
4
+ #
5
+ # flapjack-notifier
6
+ # Boots flapjack-notifier, check executors for Flapjack.
7
+ #
8
+
9
+ PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local:/usr/local/sbin:/usr/local/bin
10
+
11
+ if [ -f /etc/default/flapjack-notifier ]; then
12
+ . /etc/default/flapjack-notifier
13
+ fi
14
+
15
+ # Default return value
16
+ RETVAL=0
17
+
18
+ if [ ! $(which flapjack-notifier-manager) ]; then
19
+ echo "Error: flapjack-notifier-manager isn't on your path."
20
+ echo "Refusing to start!"
21
+ exit 1
22
+ fi
23
+
24
+ # Evaluate command
25
+ case "$1" in
26
+ start)
27
+ flapjack-notifier-manager start --recipients $RECIPIENTS \
28
+ --port $BEANSTALKD_PORT --beanstalk $BEANSTALKD_ADDR
29
+ RETVAL=$?
30
+ ;;
31
+ stop)
32
+ flapjack-notifier-manager stop --recipients $RECIPIENTS \
33
+ --port $BEANSTALKD_PORT --beanstalk $BEANSTALKD_ADDR
34
+ RETVAL=$?
35
+ ;;
36
+ restart)
37
+ flapjack-notifier-manager restart --recipients $RECIPIENTS \
38
+ --port $BEANSTALKD_PORT --beanstalk $BEANSTALKD_ADDR
39
+ RETVAL=$?
40
+ ;;
41
+ *)
42
+ echo "Usage: flapjack-notifier {start|stop|restart}"
43
+ exit 1
44
+ ;;
45
+ esac
46
+
47
+ exit $RETVAL
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+ #
3
+ # Copyright (c) 2009 Lindsay Holmwood <lindsay@holmwood.id.au>
4
+ #
5
+ # flapjack-workers
6
+ # Boots flapjack-workers, check executors for Flapjack.
7
+ #
8
+
9
+ PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local:/usr/local/sbin:/usr/local/bin
10
+
11
+ if [ -f /etc/default/flapjack-workers ]; then
12
+ . /etc/default/flapjack-workers
13
+ fi
14
+
15
+ # Default return value
16
+ RETVAL=0
17
+
18
+ if [ ! $(which flapjack-worker-manager) ]; then
19
+ echo "flapjack-worker-manager isn't on your path."
20
+ echo "Refusing to start."
21
+ exit 1
22
+ fi
23
+
24
+ # Evaluate command
25
+ case "$1" in
26
+ start)
27
+ flapjack-worker-manager start --workers $WORKERS
28
+ RETVAL=$?
29
+ ;;
30
+ stop)
31
+ flapjack-worker-manager stop --workers $WORKERS
32
+ RETVAL=$?
33
+ ;;
34
+ restart)
35
+ flapjack-worker-manager restart --workers $WORKERS
36
+ RETVAL=$?
37
+ ;;
38
+ *)
39
+ echo "Usage: flapjack-workers {start|stop|restart}"
40
+ exit 1
41
+ ;;
42
+ esac
43
+
44
+ exit $RETVAL
data/flapjack.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'flapjack'
3
+ s.version = '0.3.8'
4
+ s.date = '2009-05-26'
5
+
6
+ s.summary = "a scalable and distributed monitoring system"
7
+ s.description = "Flapjack is highly scalable and distributed monitoring system. It understands the Nagios plugin format, and can easily be scaled from 1 server to 1000."
8
+
9
+ s.authors = ['Lindsay Holmwood']
10
+ s.email = 'lindsay@holmwood.id.au'
11
+ s.homepage = 'http://flapjack-project.com'
12
+ s.has_rdoc = false
13
+
14
+ s.add_dependency('daemons', '>= 1.0.10')
15
+ s.add_dependency('beanstalk-client', '>= 1.0.2')
16
+ s.add_dependency('log4r', '>= 1.0.5')
17
+ s.add_dependency('xmpp4r-simple', '>= 0.8.8')
18
+ s.add_dependency('mailfactory', '>= 1.4.0')
19
+
20
+ s.bindir = "bin"
21
+ s.executables = ["flapjack-notifier", "flapjack-notifier-manager", "flapjack-stats", "flapjack-worker", "flapjack-worker-manager", "install-flapjack-systemwide"]
22
+ s.files = ["README.md", "Rakefile", "TODO.md", "bin/flapjack-notifier", "bin/flapjack-notifier-manager", "bin/flapjack-stats", "bin/flapjack-worker", "bin/flapjack-worker-manager", "bin/install-flapjack-systemwide", "etc/default/flapjack-notifier", "etc/default/flapjack-workers", "etc/flapjack/recipients.yaml", "etc/init.d/flapjack-notifier", "etc/init.d/flapjack-workers", "flapjack.gemspec", "lib/flapjack/cli/notifier.rb", "lib/flapjack/cli/notifier_manager.rb", "lib/flapjack/cli/worker.rb", "lib/flapjack/cli/worker_manager.rb", "lib/flapjack/notifier.rb", "lib/flapjack/notifiers/mailer.rb", "lib/flapjack/notifiers/xmpp.rb", "lib/flapjack/patches.rb", "lib/flapjack/result.rb"]
23
+ end
24
+
25
+
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'ostruct'
5
+ require 'optparse'
6
+ require 'log4r'
7
+ require 'log4r/outputter/syslogoutputter'
8
+
9
+ module Flapjack
10
+ class NotifierOptions
11
+ def self.parse(args)
12
+ options = OpenStruct.new
13
+ opts = OptionParser.new do |opts|
14
+ # the available command line options
15
+ opts.on('-b', '--beanstalk HOST', 'location of the beanstalkd') do |host|
16
+ options.host = host
17
+ end
18
+ opts.on('-p', '--port PORT', 'beanstalkd port') do |port|
19
+ options.port = port.to_i
20
+ end
21
+ opts.on('-r', '--recipients FILE', 'recipients file') do |recipients|
22
+ options.recipients = recipients.to_s
23
+ end
24
+ opts.on_tail("-h", "--help", "Show this message") do
25
+ puts opts
26
+ exit
27
+ end
28
+ end
29
+
30
+ # parse the options
31
+ begin
32
+ opts.parse!(args)
33
+ rescue OptionParser::MissingArgument => e
34
+ # if an --option is missing it's argument
35
+ puts e.message.capitalize + "\n\n"
36
+ puts opts
37
+ exit 1
38
+ end
39
+
40
+ # default the host + port
41
+ options.host ||= 'localhost'
42
+ options.port ||= 11300
43
+
44
+ @errors = []
45
+ # check that recipients file exists
46
+ unless File.exists?(options.recipients.to_s)
47
+ @errors << "The specified recipients file doesn't exist!"
48
+ end
49
+
50
+ # if there are errors, print them out and exit
51
+ if @errors.size > 0
52
+ puts "Errors:"
53
+ @errors.each do |error|
54
+ puts " - #{error}"
55
+ end
56
+ puts
57
+ puts opts
58
+ exit 2
59
+ end
60
+
61
+ options
62
+ end
63
+ end
64
+
65
+ class NotifierCLI
66
+ attr_accessor :log, :recipients, :notifier, :results_queue
67
+ attr_accessor :condition
68
+
69
+ def initialize
70
+ @log = Log4r::Logger.new("notifier")
71
+ end
72
+
73
+ def setup_loggers
74
+ @log.add(Log4r::StdoutOutputter.new('notifier'))
75
+ @log.add(Log4r::SyslogOutputter.new('notifier'))
76
+ end
77
+
78
+ def setup_recipients(opts={})
79
+
80
+ if opts[:yaml]
81
+ yaml = opts[:yaml]
82
+ else
83
+ opts[:filename] ||= File.join(Dir.pwd, "recipients.yaml")
84
+ yaml = YAML::load(File.read(opts[:filename]))
85
+ end
86
+
87
+ @recipients = yaml.map do |r|
88
+ OpenStruct.new(r)
89
+ end
90
+ end
91
+
92
+ def process_loop
93
+ @log.info("Processing results...")
94
+ loop do
95
+ process_result
96
+ end
97
+ end
98
+
99
+ def process_result
100
+ @log.debug("Waiting for new result...")
101
+ result_job = @results_queue.reserve
102
+ result = Result.new(YAML::load(result_job.body))
103
+
104
+ @log.info("Processing result for check '#{result.id}'")
105
+ if result.warning? || result.critical?
106
+ @log.info("Notifying on check '#{result.id}'")
107
+ @notifier.notify!(result)
108
+ end
109
+
110
+ @log.debug("Deleting result for check '#{result.id}' from queue")
111
+ result_job.delete
112
+ end
113
+
114
+ end
115
+
116
+ end
@@ -0,0 +1,64 @@
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
+ end
31
+
32
+ begin
33
+ opts.parse!(args)
34
+ rescue => e
35
+ puts e.message.capitalize + "\n\n"
36
+ puts opts
37
+ exit 1
38
+ end
39
+
40
+ # defaults
41
+ options.host ||= "localhost"
42
+ options.port ||= 11300
43
+
44
+ unless options.recipients =~ /.+/
45
+ puts "You must specify a recipients file!\n\n"
46
+ puts opts
47
+ exit 2
48
+ end
49
+
50
+ unless File.exists?(options.recipients)
51
+ puts "The specified recipients file doesn't exist!"
52
+ exit 2
53
+ end
54
+
55
+ unless %w(start stop restart).include?(args[0])
56
+ puts opts
57
+ exit 1
58
+ end
59
+
60
+ options
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,128 @@
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_tail("-h", "--help", "Show this message") do
25
+ puts opts
26
+ exit
27
+ end
28
+ end
29
+
30
+ # parse the options
31
+ begin
32
+ opts.parse!(args)
33
+ rescue OptionParser::MissingArgument => e
34
+ # if an --option is missing it's argument
35
+ puts e.message.capitalize + "\n\n"
36
+ puts opts
37
+ exit 1
38
+ end
39
+
40
+ # default the port
41
+ options.host ||= 'localhost'
42
+ options.port ||= 11300
43
+
44
+ options
45
+ end
46
+ end
47
+
48
+ class Worker
49
+
50
+ attr_accessor :jobs, :results, :log
51
+
52
+ def initialize(opts={})
53
+ @jobs = Beanstalk::Pool.new(["#{opts[:host]}:#{opts[:port]}"], 'jobs')
54
+ @results = Beanstalk::Pool.new(["#{opts[:host]}:#{opts[:port]}"], 'results')
55
+
56
+ if opts[:logger]
57
+ @log = opts[:logger]
58
+ else
59
+ @log = Log4r::Logger.new('worker')
60
+ @log.add(Log4r::StdoutOutputter.new('worker'))
61
+ @log.add(Log4r::SyslogOutputter.new('worker'))
62
+ end
63
+ end
64
+
65
+ def process_loop
66
+ @log.info("Booting main loop...")
67
+ loop do
68
+ process_check
69
+ end
70
+ end
71
+
72
+ def process_check
73
+ # get next check off the beanstalk
74
+ job, check = get_check()
75
+
76
+ # do the actual check
77
+ result, retval = perform_check(check.command)
78
+
79
+ # report the results of the check
80
+ report_check(:result => result, :retval => retval, :check => check)
81
+
82
+ # create another job for the check, delete current job
83
+ cleanup_job(:job => job, :check => check)
84
+ end
85
+
86
+ def perform_check(cmd)
87
+ command = "sh -c '#{cmd}'"
88
+ @log.debug("Executing check: \"#{command}\"")
89
+ result = `#{command}`
90
+ retval = $?.exitstatus
91
+
92
+ return result, retval
93
+ end
94
+
95
+ def report_check(opts={})
96
+ raise ArgumentError unless (opts[:result] && opts[:retval] && opts[:check])
97
+
98
+ @log.debug "Reporting results for check id #{opts[:check].id}."
99
+ @results.yput({:id => opts[:check].id,
100
+ :output => opts[:result],
101
+ :retval => opts[:retval].to_i})
102
+ end
103
+
104
+ def cleanup_job(opts={})
105
+ raise ArgumentError unless (opts[:job] && opts[:check])
106
+
107
+ # add job back onto stack
108
+ @log.debug("Putting check back onto beanstalk.")
109
+ @jobs.yput(opts[:check].to_h, 65536, opts[:check].frequency)
110
+
111
+ # once we're done, clean up
112
+ @log.debug("Deleting job.")
113
+ opts[:job].delete
114
+ end
115
+
116
+ def get_check
117
+ @log.debug("Waiting for check...")
118
+ job = @jobs.reserve
119
+ check = Check.new(YAML::load(job.body))
120
+ @log.info("Got check with id #{check.id}")
121
+
122
+ return job, check
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'ostruct'
5
+ require 'optparse'
6
+
7
+ class WorkerManagerOptions
8
+ def self.parse(args)
9
+ options = OpenStruct.new
10
+ opts = OptionParser.new do |opts|
11
+ opts.banner = "Usage: flapjack-worker-manager <command> [options]"
12
+ opts.separator " "
13
+ opts.separator " where <command> is one of:"
14
+ opts.separator " start start a worker"
15
+ opts.separator " stop stop all workers"
16
+ opts.separator " restart restart workers"
17
+ opts.separator " "
18
+ opts.separator " and [options] are:"
19
+
20
+ opts.on('-w', '--workers N', 'number of workers to spin up') do |workers|
21
+ options.workers = workers.to_i
22
+ end
23
+ end
24
+
25
+ begin
26
+ opts.parse!(args)
27
+ rescue
28
+ puts e.message.capitalize + "\n\n"
29
+ puts opts
30
+ exit 1
31
+ end
32
+
33
+ options.workers ||= 5
34
+
35
+ unless %w(start stop restart).include?(args[0])
36
+ puts opts
37
+ exit 1
38
+ end
39
+
40
+ options
41
+ end
42
+ end
43
+
44
+
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ostruct'
4
+
5
+ class Notifier
6
+
7
+ attr_reader :recipients, :log
8
+
9
+ def initialize(opts={})
10
+ @log = opts[:logger]
11
+ raise "you have to specify a logger" unless @log
12
+ @recipients = (opts[:recipients] || [])
13
+
14
+ @notifiers = []
15
+ if opts[:notifiers]
16
+ opts[:notifiers].each do |n|
17
+ @notifiers << n
18
+ @log.info("using the #{n.class.to_s.split("::").last} notifier")
19
+ end
20
+ else
21
+ @log.warning("there are no notifiers")
22
+ end
23
+ end
24
+
25
+ def notify!(result)
26
+ @notifiers.each do |n|
27
+ @recipients.each do |recipient|
28
+ @log.info("Notifying #{recipient.name} via #{n.class} about check #{result.id}")
29
+ n.notify(:result => result, :who => recipient)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'net/smtp'
5
+ require 'mailfactory'
6
+
7
+ module Flapjack
8
+ module Notifiers
9
+
10
+ class Mailer
11
+
12
+ def notify(opts={})
13
+ raise unless (opts[:who] && opts[:result])
14
+
15
+ mail = MailFactory.new
16
+ mail.to = opts[:who].email
17
+ mail.from = "notifications@flapjack-project.com"
18
+ mail.subject = "Check: #{opts[:result].id}, Status: #{opts[:result].status}"
19
+ mail.text = <<-DESC
20
+ Check #{opts[:result].id} returned the status "#{opts[:result].status}".
21
+
22
+ Here was the output:
23
+ #{opts[:result].output}
24
+
25
+ You can respond to this issue at:
26
+ http://localhost:4000/issue/#{opts[:result].object_id * -1}
27
+ DESC
28
+
29
+ Net::SMTP.start('localhost') do |smtp|
30
+ smtp.sendmail(mail.to_s, mail.from, mail.to)
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'xmpp4r-simple'
4
+
5
+ module Flapjack
6
+ module Notifiers
7
+ class Xmpp
8
+
9
+ def initialize(opts={})
10
+
11
+ @jid = opts[:jid]
12
+ @password = opts[:password]
13
+ unless @jid && @password
14
+ raise ArgumentError, "You have to provide a username and password"
15
+ end
16
+ @xmpp = Jabber::Simple.new(@jid, @password)
17
+
18
+ end
19
+
20
+ def notify(opts={})
21
+
22
+ raise unless opts[:who] && opts[:result]
23
+
24
+ message = <<-DESC
25
+ Check #{opts[:result].id} returned the status "#{opts[:result].status}".
26
+ http://localhost:4000/checks/#{opts[:result].id}
27
+ DESC
28
+
29
+ @xmpp.deliver(opts[:who].jid, message)
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,17 @@
1
+ require 'ostruct'
2
+ require 'daemons'
3
+
4
+ class OpenStruct
5
+ def to_h
6
+ @table
7
+ end
8
+ end
9
+
10
+ module Daemons
11
+ class PidFile
12
+ # we override this method so creating pid files is fork-safe
13
+ def filename
14
+ File.join(@dir, "#{@progname}#{Process.pid}.pid")
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ostruct'
4
+
5
+ ##
6
+ # Representation of a check result, as it's popped off the beanstalk.
7
+ #
8
+ # Provides several convience methods for querying the status of a result.
9
+ #
10
+ # Convenience methods are used by the Notifier to determine whether a
11
+ # notification needs to be sent out.
12
+ class Result < OpenStruct
13
+
14
+ # Whether a check returns an ok status.
15
+ def ok?
16
+ self.retval == 0
17
+ end
18
+
19
+ # Whether a check has a warning status.
20
+ def warning?
21
+ self.retval == 1
22
+ end
23
+
24
+ # Whether a check has a critical status.
25
+ def critical?
26
+ self.retval == 2
27
+ end
28
+
29
+ # Human readable representation of the check's return value.
30
+ def status
31
+ case self.retval
32
+ when 0 ; "ok"
33
+ when 1 ; "warning"
34
+ when 2 ; "critical"
35
+ end
36
+ end
37
+
38
+ # The id of a check result.
39
+ def id
40
+ # openstruct won't respond, so we have to manually define it
41
+ @table[:id]
42
+ end
43
+
44
+ end
45
+
46
+ class Check < Result
47
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: auxesis-flapjack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.8
5
+ platform: ruby
6
+ authors:
7
+ - Lindsay Holmwood
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-26 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: daemons
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.10
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: beanstalk-client
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.2
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: log4r
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.5
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: xmpp4r-simple
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.8.8
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: mailfactory
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 1.4.0
64
+ version:
65
+ description: Flapjack is highly scalable and distributed monitoring system. It understands the Nagios plugin format, and can easily be scaled from 1 server to 1000.
66
+ email: lindsay@holmwood.id.au
67
+ executables:
68
+ - flapjack-notifier
69
+ - flapjack-notifier-manager
70
+ - flapjack-stats
71
+ - flapjack-worker
72
+ - flapjack-worker-manager
73
+ - install-flapjack-systemwide
74
+ extensions: []
75
+
76
+ extra_rdoc_files: []
77
+
78
+ files:
79
+ - README.md
80
+ - Rakefile
81
+ - TODO.md
82
+ - bin/flapjack-notifier
83
+ - bin/flapjack-notifier-manager
84
+ - bin/flapjack-stats
85
+ - bin/flapjack-worker
86
+ - bin/flapjack-worker-manager
87
+ - bin/install-flapjack-systemwide
88
+ - etc/default/flapjack-notifier
89
+ - etc/default/flapjack-workers
90
+ - etc/flapjack/recipients.yaml
91
+ - etc/init.d/flapjack-notifier
92
+ - etc/init.d/flapjack-workers
93
+ - flapjack.gemspec
94
+ - lib/flapjack/cli/notifier.rb
95
+ - lib/flapjack/cli/notifier_manager.rb
96
+ - lib/flapjack/cli/worker.rb
97
+ - lib/flapjack/cli/worker_manager.rb
98
+ - lib/flapjack/notifier.rb
99
+ - lib/flapjack/notifiers/mailer.rb
100
+ - lib/flapjack/notifiers/xmpp.rb
101
+ - lib/flapjack/patches.rb
102
+ - lib/flapjack/result.rb
103
+ has_rdoc: false
104
+ homepage: http://flapjack-project.com
105
+ post_install_message:
106
+ rdoc_options: []
107
+
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: "0"
115
+ version:
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: "0"
121
+ version:
122
+ requirements: []
123
+
124
+ rubyforge_project:
125
+ rubygems_version: 1.2.0
126
+ signing_key:
127
+ specification_version: 2
128
+ summary: a scalable and distributed monitoring system
129
+ test_files: []
130
+