sensu 0.6.0-x86-mswin32

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *~
3
+ \#*
4
+ dump.rdb
5
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,58 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sensu (0.5.14)
5
+ amqp (= 0.7.4)
6
+ async_sinatra
7
+ em-hiredis
8
+ em-syslog
9
+ json
10
+ thin
11
+ uuidtools
12
+
13
+ GEM
14
+ remote: http://rubygems.org/
15
+ specs:
16
+ amqp (0.7.4)
17
+ eventmachine
18
+ async_sinatra (0.5.0)
19
+ rack (>= 1.2.1)
20
+ sinatra (>= 1.0)
21
+ callsite (0.0.6)
22
+ bundler (~> 1.0.0)
23
+ daemons (1.1.4)
24
+ em-hiredis (0.1.0)
25
+ hiredis (~> 0.3.0)
26
+ em-syslog (0.0.2)
27
+ eventmachine
28
+ em-ventually (0.1.2)
29
+ callsite (~> 0.0.5)
30
+ eventmachine
31
+ eventmachine (0.12.10)
32
+ hiredis (0.3.2)
33
+ json (1.6.1)
34
+ mime-types (1.16)
35
+ minitest (2.6.0)
36
+ rack (1.3.3)
37
+ rake (0.9.2)
38
+ rest-client (1.6.7)
39
+ mime-types (>= 1.16)
40
+ sinatra (1.2.6)
41
+ rack (~> 1.1)
42
+ tilt (< 2.0, >= 1.2.2)
43
+ thin (1.2.11)
44
+ daemons (>= 1.0.9)
45
+ eventmachine (>= 0.12.6)
46
+ rack (>= 1.0.0)
47
+ tilt (1.3.3)
48
+ uuidtools (2.1.2)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ em-ventually
55
+ minitest
56
+ rake
57
+ rest-client
58
+ sensu!
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Sonian Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.org ADDED
@@ -0,0 +1,61 @@
1
+ * Welcome to Sensu
2
+ Sensu is a monitoring system framework, handling the remote execution of checks.
3
+
4
+ Checks can utilize user created plugins, returning an exit status code and outputting to STDOUT.
5
+
6
+ Check results are handled by user created handlers.
7
+
8
+ Documentation can be found [[https://github.com/sonian/sensu/wiki][here]].
9
+
10
+ * License
11
+ Sensu is released under the [[https://github.com/sonian/sensu/blob/master/MIT-LICENSE.txt][MIT license]].
12
+
13
+ * Contributing
14
+ - [[http://help.github.com/fork-a-repo/][Fork]] [[https://github.com/sonian/sensu][Sensu]]
15
+ - Use a [[https://github.com/dchelimsky/rspec/wiki/Topic-Branches][topic branch]]
16
+ - Create a [[http://help.github.com/send-pull-requests/][pull request]]
17
+
18
+ Keep it simple.
19
+
20
+ ** Readme Driven Development
21
+ *** A Client Will
22
+ - Have a set of attributes to describe it, including its responsibilities
23
+ - Send keep-alives to a server
24
+ - Subscribe to a queue bound to a set of fanout exchanges, determined by its responsibilities
25
+ - Substitute tokens in check commands with their matching client attribute
26
+ - Report when it does not have a client attribute for token substitution
27
+ - Receive checks from subscriptions, execute them, and publish the results to a queue with its client name
28
+ - Not allow overlapping check executions of the same name
29
+ - Report when it is unaware of a check it received from a subscription
30
+
31
+ *** A Server Will
32
+ - Subscribe to a queue for check results, another for keep-alives
33
+ - Pull keep-alives, storing client details
34
+ - Publish checks on defined intervals to their associated fanout exchanges
35
+ - Pull check results, storing the latest events for clients, a good result will flush a previous event for that client
36
+ - Create an event when it stops receiving keep-alives from a client, a new keep-alive for the client will clear the event
37
+ - Trigger a event handler, either the default handler or one specified for the check, providing it with a JSON event file
38
+
39
+ *** An API Will
40
+ - List all current events
41
+ - List all clients and their attributes
42
+ - Show a client and its attributes
43
+ - Remove a client and resolve associated events
44
+
45
+ *** A Plugin Will
46
+ - Output to STDOUT
47
+ - Return a valid exit status code
48
+
49
+ *** A Handler Will
50
+ - Accept a command line argument "-f", for an event file path
51
+ - Parse the JSON event file
52
+ - Handle the event as it wishes
53
+
54
+ ** Testing
55
+ *** Requirements
56
+ - RabbitMQ (default configuration)
57
+ - Redis (default configuration)
58
+
59
+ *** MiniTest
60
+
61
+ : rake test
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ task :default => 'test'
5
+
6
+ Rake::TestTask.new do |test|
7
+ test.pattern = "test/*_test.rb"
8
+ end
data/bin/sensu-api ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ raise "The Sensu API does not run on Windows" if RUBY_PLATFORM.downcase =~ /mswin|mingw32|windows/
4
+
5
+ $: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
6
+ require 'sensu/api'
7
+
8
+ options = Sensu::Config.read_arguments(ARGV)
9
+ Sensu::API.run(options)
data/bin/sensu-client ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+ require 'sensu/client'
5
+
6
+ options = Sensu::Config.read_arguments(ARGV)
7
+ Sensu::Client.run(options)
data/bin/sensu-server ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ raise "The Sensu Server does not run on Windows" if RUBY_PLATFORM.downcase =~ /mswin|mingw32|windows/
4
+
5
+ $: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
6
+ require 'sensu/server'
7
+
8
+ options = Sensu::Config.read_arguments(ARGV)
9
+ Sensu::Server.run(options)
data/lib/sensu.rb ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Sensu
4
+ end
data/lib/sensu/api.rb ADDED
@@ -0,0 +1,138 @@
1
+ require File.join(File.dirname(__FILE__), 'config')
2
+ require 'sinatra/async'
3
+ require 'em-hiredis'
4
+
5
+ module Sensu
6
+ class API < Sinatra::Base
7
+ register Sinatra::Async
8
+
9
+ def self.run(options={})
10
+ EM.run do
11
+ self.setup(options)
12
+ self.run!(:port => @settings['api']['port'])
13
+
14
+ Signal.trap('INT') do
15
+ EM.stop
16
+ end
17
+
18
+ Signal.trap('TERM') do
19
+ EM.stop
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.setup(options={})
25
+ config = Sensu::Config.new(options)
26
+ @settings = config.settings
27
+ set :redis, EM::Hiredis.connect('redis://' + @settings['redis']['host'] + ':' + @settings['redis']['port'].to_s)
28
+ connection = AMQP.connect(symbolize_keys(@settings['rabbitmq']))
29
+ set :amq, MQ.new(connection)
30
+ end
31
+
32
+ helpers do
33
+ include Rack::Utils
34
+ alias_method :conn, :settings
35
+ end
36
+
37
+ before do
38
+ content_type 'application/json'
39
+ end
40
+
41
+ aget '/clients' do
42
+ current_clients = Array.new
43
+ conn.redis.smembers('clients').callback do |clients|
44
+ unless clients.empty?
45
+ clients.each_with_index do |client, index|
46
+ conn.redis.get('client:' + client).callback do |client_json|
47
+ current_clients.push(JSON.parse(client_json))
48
+ body current_clients.to_json if index == clients.size-1
49
+ end
50
+ end
51
+ else
52
+ body current_clients.to_json
53
+ end
54
+ end
55
+ end
56
+
57
+ aget '/client/:id' do |client|
58
+ conn.redis.get('client:' + client).callback do |client_json|
59
+ status 404 if client_json.nil?
60
+ body client_json
61
+ end
62
+ end
63
+
64
+ aget '/events' do
65
+ current_events = Hash.new
66
+ conn.redis.smembers('clients').callback do |clients|
67
+ unless clients.empty?
68
+ clients.each_with_index do |client, index|
69
+ conn.redis.hgetall('events:' + client).callback do |events|
70
+ client_events = Hash[*events]
71
+ client_events.each do |key, value|
72
+ client_events[key] = JSON.parse(value)
73
+ end
74
+ current_events[client] = client_events unless client_events.empty?
75
+ body current_events.to_json if index == clients.size-1
76
+ end
77
+ end
78
+ else
79
+ body current_events.to_json
80
+ end
81
+ end
82
+ end
83
+
84
+ adelete '/client/:id' do |client|
85
+ conn.redis.sismember('clients', client).callback do |client_exists|
86
+ unless client_exists == 0
87
+ conn.redis.exists('events:' + client).callback do |events_exist|
88
+ unless events_exist == 0
89
+ conn.redis.hgetall('events:' + client).callback do |events|
90
+ Hash[*events].keys.each do |check_name|
91
+ check = {'name' => check_name, 'issued' => Time.now.to_i, 'status' => 0, 'output' => 'client is being removed...'}
92
+ conn.amq.queue('results').publish({'client' => client, 'check' => check}.to_json)
93
+ end
94
+ EM.add_timer(8) do
95
+ conn.redis.srem('clients', client)
96
+ conn.redis.del('events:' + client)
97
+ conn.redis.del('client:' + client)
98
+ end
99
+ end
100
+ else
101
+ conn.redis.srem('clients', client)
102
+ conn.redis.del('events:' + client)
103
+ conn.redis.del('client:' + client)
104
+ end
105
+ status 204
106
+ body ''
107
+ end
108
+ else
109
+ status 404
110
+ body ''
111
+ end
112
+ end
113
+ end
114
+
115
+ apost '/test/client' do
116
+ attributes = '{
117
+ "name": "test",
118
+ "address": "localhost",
119
+ "subscriptions": [
120
+ "foo",
121
+ "bar"
122
+ ]
123
+ }'
124
+ conn.redis.set('client:test', attributes).callback do
125
+ conn.redis.sadd('clients', 'test')
126
+ status 201
127
+ body ''
128
+ end
129
+ end
130
+
131
+ apost '/test/event' do
132
+ conn.redis.hset('events:test', 'test', {'status' => 2, 'output' => 'CRITICAL :: test', 'occurrences' => 1}.to_json).callback do
133
+ status 201
134
+ body ''
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,104 @@
1
+ require File.join(File.dirname(__FILE__), 'config')
2
+
3
+ module Sensu
4
+ class Client
5
+ def self.run(options={})
6
+ EM.run do
7
+ client = self.new(options)
8
+ client.setup_amqp
9
+ client.setup_keepalives
10
+ client.setup_subscriptions
11
+ client.monitor_queues
12
+
13
+ Signal.trap('INT') do
14
+ EM.stop
15
+ end
16
+
17
+ Signal.trap('TERM') do
18
+ EM.stop
19
+ end
20
+ end
21
+ end
22
+
23
+ def initialize(options={})
24
+ config = Sensu::Config.new(:config_file => options[:config_file])
25
+ @settings = config.settings
26
+ @checks_in_progress = Array.new
27
+ end
28
+
29
+ def setup_amqp
30
+ connection = AMQP.connect(symbolize_keys(@settings['rabbitmq']))
31
+ @amq = MQ.new(connection)
32
+ @keepalive_queue = @amq.queue('keepalives')
33
+ @result_queue = @amq.queue('results')
34
+ end
35
+
36
+ def setup_keepalives
37
+ @keepalive_queue.publish(@settings['client'].merge({'timestamp' => Time.now.to_i}).to_json)
38
+ EM.add_periodic_timer(30) do
39
+ @keepalive_queue.publish(@settings['client'].merge({'timestamp' => Time.now.to_i}).to_json)
40
+ end
41
+ end
42
+
43
+ def execute_check(check)
44
+ result = {'client' => @settings['client']['name'], 'check' => check}
45
+ if @settings['checks'].has_key?(check['name'])
46
+ unless @checks_in_progress.include?(check['name'])
47
+ @checks_in_progress.push(check['name'])
48
+ unmatched_tokens = Array.new
49
+ command = @settings['checks'][check['name']]['command'].gsub(/:::(.*?):::/) do
50
+ key = $1.to_s
51
+ unmatched_tokens.push(key) unless @settings['client'].has_key?(key)
52
+ @settings['client'][key].to_s
53
+ end
54
+ if unmatched_tokens.empty?
55
+ execute = proc do
56
+ IO.popen(command + ' 2>&1') do |io|
57
+ result['check']['output'] = io.read
58
+ end
59
+ result['check']['status'] = $?.exitstatus
60
+ result
61
+ end
62
+ publish = proc do |result|
63
+ @result_queue.publish(result.to_json)
64
+ @checks_in_progress.delete(result['check']['name'])
65
+ end
66
+ EM.defer(execute, publish)
67
+ else
68
+ result['check']['status'] = 3
69
+ result['check']['output'] = 'Missing client attributes: ' + unmatched_tokens.join(', ')
70
+ @result_queue.publish(result.to_json)
71
+ @checks_in_progress.delete(check['name'])
72
+ end
73
+ end
74
+ else
75
+ result['check']['status'] = 3
76
+ result['check']['output'] = 'Unknown check'
77
+ @result_queue.publish(result.to_json)
78
+ @checks_in_progress.delete(check['name'])
79
+ end
80
+ end
81
+
82
+ def setup_subscriptions
83
+ @check_queue = @amq.queue(UUIDTools::UUID.random_create.to_s, :exclusive => true)
84
+ @settings['client']['subscriptions'].each do |exchange|
85
+ @check_queue.bind(@amq.fanout(exchange))
86
+ end
87
+ @check_queue.subscribe do |check_json|
88
+ check = JSON.parse(check_json)
89
+ execute_check(check)
90
+ end
91
+ end
92
+
93
+ def monitor_queues
94
+ EM.add_periodic_timer(5) do
95
+ unless @check_queue.subscribed?
96
+ @check_queue.delete
97
+ EM.add_timer(1) do
98
+ setup_subscriptions
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,62 @@
1
+ require 'rubygems' if RUBY_VERSION < '1.9.0'
2
+ require 'optparse'
3
+ require 'json'
4
+ require 'uuidtools'
5
+ require 'amqp'
6
+ require 'em/syslog'
7
+ require File.join(File.dirname(__FILE__), 'helpers')
8
+
9
+ module Sensu
10
+ class Config
11
+ attr_accessor :settings
12
+
13
+ def initialize(options={})
14
+ config_file = options[:config_file] || '/etc/sensu/config.json'
15
+ @settings = JSON.parse(File.open(config_file, 'r').read)
16
+ validate_config
17
+ end
18
+
19
+ def validate_config
20
+ @settings['checks'].each do |name, info|
21
+ unless info['interval'].is_a?(Integer) && info['interval'] > 0
22
+ raise 'configuration invalid, missing interval for check ' + name
23
+ end
24
+ unless info['command'].is_a?(String)
25
+ raise 'configuration invalid, missing command for check ' + name
26
+ end
27
+ unless info['subscribers'].is_a?(Array) && info['subscribers'].count > 0
28
+ raise 'configuration invalid, missing subscribers for check ' + name
29
+ end
30
+ end
31
+ unless @settings['client']['name'].is_a?(String)
32
+ raise 'configuration invalid, client must have a name'
33
+ end
34
+ unless @settings['client']['address'].is_a?(String)
35
+ raise 'configuration invalid, client must have an address (ip or hostname)'
36
+ end
37
+ unless @settings['client']['subscriptions'].is_a?(Array) && @settings['client']['subscriptions'].count > 0
38
+ raise 'configuration invalid, client must have subscriptions'
39
+ end
40
+ end
41
+
42
+ def self.read_arguments(arguments)
43
+ options = Hash.new
44
+ optparse = OptionParser.new do |opts|
45
+ opts.on('-h', '--help', 'Display this screen') do
46
+ puts opts
47
+ exit
48
+ end
49
+ options[:worker] = false
50
+ opts.on('-w', '--worker', 'Only consume jobs, no check publishing (default: false)') do
51
+ options[:worker] = true
52
+ end
53
+ options[:config_file] = nil
54
+ opts.on('-c', '--config FILE', 'Sensu JSON config FILE (default: /etc/sensu/config.json)') do |file|
55
+ options[:config_file] = file
56
+ end
57
+ end
58
+ optparse.parse!(arguments)
59
+ options
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,18 @@
1
+ def symbolize_keys(item)
2
+ case item
3
+ when Array
4
+ item.map do |i|
5
+ symbolize_keys(i)
6
+ end
7
+ when Hash
8
+ Hash[
9
+ item.map do |key, value|
10
+ k = key.is_a?(String) ? key.to_sym : key
11
+ v = symbolize_keys(value)
12
+ [k,v]
13
+ end
14
+ ]
15
+ else
16
+ item
17
+ end
18
+ end
@@ -0,0 +1,186 @@
1
+ require File.join(File.dirname(__FILE__), 'config')
2
+ require 'em-hiredis'
3
+
4
+ module Sensu
5
+ class Server
6
+ attr_accessor :redis, :is_worker
7
+ alias :redis_connection :redis
8
+
9
+ def self.run(options={})
10
+ EM.threadpool_size = 15
11
+ EM.run do
12
+ server = self.new(options)
13
+ server.setup_logging
14
+ server.setup_redis
15
+ server.setup_amqp
16
+ server.setup_keepalives
17
+ server.setup_results
18
+ unless server.is_worker
19
+ server.setup_publisher
20
+ server.setup_keepalive_monitor
21
+ end
22
+ server.monitor_queues
23
+
24
+ Signal.trap('INT') do
25
+ EM.stop
26
+ end
27
+
28
+ Signal.trap('TERM') do
29
+ EM.stop
30
+ end
31
+ end
32
+ end
33
+
34
+ def initialize(options={})
35
+ config = Sensu::Config.new(:config_file => options[:config_file])
36
+ @settings = config.settings
37
+ @is_worker = options[:worker]
38
+ end
39
+
40
+ def setup_logging
41
+ EM.syslog_setup(@settings['syslog']['host'], @settings['syslog']['port'])
42
+ end
43
+
44
+ def setup_redis
45
+ @redis = EM::Hiredis.connect('redis://' + @settings['redis']['host'] + ':' + @settings['redis']['port'].to_s)
46
+ end
47
+
48
+ def setup_amqp
49
+ connection = AMQP.connect(symbolize_keys(@settings['rabbitmq']))
50
+ @amq = MQ.new(connection)
51
+ end
52
+
53
+ def setup_keepalives
54
+ @keepalive_queue = @amq.queue('keepalives')
55
+ @keepalive_queue.subscribe do |keepalive_json|
56
+ client = JSON.parse(keepalive_json)['name']
57
+ @redis.set('client:' + client, keepalive_json).callback do
58
+ @redis.sadd('clients', client)
59
+ end
60
+ end
61
+ end
62
+
63
+ def handle_event(event)
64
+ handler = proc do
65
+ result = Hash.new
66
+ IO.popen(@settings['handlers'][event['check']['handler']] + ' 2>&1', 'r+') do |io|
67
+ io.write(JSON.pretty_generate(event))
68
+ io.close_write
69
+ result['output'] = io.read
70
+ end
71
+ result['status'] = $?.exitstatus
72
+ result
73
+ end
74
+ report = proc do |result|
75
+ EM.debug('handled :: ' + event['check']['handler'] + ' :: ' + result['status'].to_s + ' :: ' + result['output'])
76
+ end
77
+ EM.defer(handler, report)
78
+ end
79
+
80
+ def process_result(result)
81
+ @redis.get('client:' + result['client']).callback do |client_json|
82
+ unless client_json.nil?
83
+ client = JSON.parse(client_json)
84
+ check = result['check']
85
+ check.merge!(@settings['checks'][check['name']]) if @settings['checks'].has_key?(check['name'])
86
+ check['handler'] ||= 'default'
87
+ event = {'client' => client, 'check' => check, 'occurrences' => 1}
88
+ if check['type'] == 'metric'
89
+ handle_event(event)
90
+ else
91
+ @redis.hget('events:' + client['name'], check['name']).callback do |event_json|
92
+ previous_event = event_json ? JSON.parse(event_json) : nil
93
+ if previous_event && check['status'] == 0
94
+ @redis.hdel('events:' + client['name'], check['name'])
95
+ event['action'] = 'resolve'
96
+ handle_event(event)
97
+ elsif check['status'] > 0
98
+ if previous_event && check['status'] == previous_event['status']
99
+ event['occurrences'] = previous_event['occurrences'] += 1
100
+ end
101
+ @redis.hset('events:' + client['name'], check['name'], {
102
+ 'status' => check['status'],
103
+ 'output' => check['output'],
104
+ 'occurrences' => event['occurrences']
105
+ }.to_json).callback do
106
+ event['action'] = 'create'
107
+ handle_event(event)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ def setup_results
117
+ @result_queue = @amq.queue('results')
118
+ @result_queue.subscribe do |result_json|
119
+ result = JSON.parse(result_json)
120
+ process_result(result)
121
+ end
122
+ end
123
+
124
+ def setup_publisher(options={})
125
+ exchanges = Hash.new
126
+ stagger = options[:test] ? 0 : 7
127
+ @settings['checks'].each_with_index do |(name, details), index|
128
+ EM.add_timer(stagger*index) do
129
+ details['subscribers'].each do |exchange|
130
+ if exchanges[exchange].nil?
131
+ exchanges[exchange] = @amq.fanout(exchange)
132
+ end
133
+ interval = options[:test] ? 0.5 : details['interval']
134
+ EM.add_periodic_timer(interval) do
135
+ exchanges[exchange].publish({'name' => name, 'issued' => Time.now.to_i}.to_json)
136
+ EM.debug('published :: ' + exchange + ' :: ' + name)
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ def setup_keepalive_monitor
144
+ EM.add_periodic_timer(30) do
145
+ @redis.smembers('clients').callback do |clients|
146
+ clients.each do |client_id|
147
+ @redis.get('client:' + client_id).callback do |client_json|
148
+ client = JSON.parse(client_json)
149
+ time_since_last_check = Time.now.to_i - client['timestamp']
150
+ result = {'client' => client['name'], 'check' => {'name' => 'keepalive', 'issued' => Time.now.to_i}}
151
+ case
152
+ when time_since_last_check >= 180
153
+ result['check']['status'] = 2
154
+ result['check']['output'] = 'No keep-alive sent from host in over 180 seconds'
155
+ @result_queue.publish(result.to_json)
156
+ when time_since_last_check >= 120
157
+ result['check']['status'] = 1
158
+ result['check']['output'] = 'No keep-alive sent from host in over 120 seconds'
159
+ @result_queue.publish(result.to_json)
160
+ else
161
+ @redis.hexists('events:' + client_id, 'keepalive').callback do |exists|
162
+ if exists == 1
163
+ result['check']['status'] = 0
164
+ result['check']['output'] = 'Keep-alive sent from host'
165
+ @result_queue.publish(result.to_json)
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ def monitor_queues
176
+ EM.add_periodic_timer(5) do
177
+ unless @keepalive_queue.subscribed?
178
+ setup_keepalives
179
+ end
180
+ unless @result_queue.subscribed?
181
+ setup_results
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
data/sensu.gemspec ADDED
@@ -0,0 +1,42 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "sensu"
3
+ s.version = "0.6.0"
4
+ s.authors = ["Sean Porter", "Justin Kolberg"]
5
+ s.email = ["sean.porter@sonian.net", "justin.kolberg@sonian.net"]
6
+ s.homepage = "https://github.com/sonian/sensu"
7
+ s.summary = %q{A server monitoring framework}
8
+ s.description = %q{A server monitoring framework using the publish-subscribe model}
9
+ s.license = "MIT"
10
+ s.has_rdoc = false
11
+
12
+ case ENV['BUILD']
13
+ when "mingw"
14
+ s.platform = "x86-mingw32"
15
+ when "mswin"
16
+ s.platform = "x86-mswin32"
17
+ else
18
+ s.platform = Gem::Platform::RUBY
19
+ end
20
+
21
+ s.add_dependency("eventmachine", "1.0.0.beta.4.1") if s.platform =~ /mswin|mingw32|windows/
22
+
23
+ s.add_dependency("amqp", "0.7.4")
24
+ s.add_dependency("json")
25
+ s.add_dependency("uuidtools")
26
+ s.add_dependency("em-syslog")
27
+
28
+ unless s.platform =~ /mswin|mingw32|windows/
29
+ s.add_dependency("em-hiredis")
30
+ s.add_dependency("async_sinatra")
31
+ s.add_dependency("thin")
32
+ end
33
+
34
+ s.add_development_dependency('rake')
35
+ s.add_development_dependency('minitest')
36
+ s.add_development_dependency('em-ventually')
37
+ s.add_development_dependency('rest-client')
38
+
39
+ s.files = `git ls-files`.split("\n").reject {|f| f =~ /(dist|test)/}
40
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
41
+ s.require_paths = ["lib"]
42
+ end
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sensu
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.6.0
6
+ platform: x86-mswin32
7
+ authors:
8
+ - Sean Porter
9
+ - Justin Kolberg
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-09-27 00:00:00 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: amqp
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - "="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.7.4
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: uuidtools
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: em-syslog
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :runtime
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: em-hiredis
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ type: :runtime
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: async_sinatra
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ type: :runtime
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: thin
84
+ prerelease: false
85
+ requirement: &id007 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "0"
91
+ type: :runtime
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: rake
95
+ prerelease: false
96
+ requirement: &id008 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: "0"
102
+ type: :development
103
+ version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: minitest
106
+ prerelease: false
107
+ requirement: &id009 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: "0"
113
+ type: :development
114
+ version_requirements: *id009
115
+ - !ruby/object:Gem::Dependency
116
+ name: em-ventually
117
+ prerelease: false
118
+ requirement: &id010 !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: "0"
124
+ type: :development
125
+ version_requirements: *id010
126
+ - !ruby/object:Gem::Dependency
127
+ name: rest-client
128
+ prerelease: false
129
+ requirement: &id011 !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: "0"
135
+ type: :development
136
+ version_requirements: *id011
137
+ description: A server monitoring framework using the publish-subscribe model
138
+ email:
139
+ - sean.porter@sonian.net
140
+ - justin.kolberg@sonian.net
141
+ executables:
142
+ - sensu-api
143
+ - sensu-client
144
+ - sensu-server
145
+ extensions: []
146
+
147
+ extra_rdoc_files: []
148
+
149
+ files:
150
+ - .gitignore
151
+ - Gemfile
152
+ - Gemfile.lock
153
+ - MIT-LICENSE.txt
154
+ - README.org
155
+ - Rakefile
156
+ - bin/sensu-api
157
+ - bin/sensu-client
158
+ - bin/sensu-server
159
+ - lib/sensu.rb
160
+ - lib/sensu/api.rb
161
+ - lib/sensu/client.rb
162
+ - lib/sensu/config.rb
163
+ - lib/sensu/helpers.rb
164
+ - lib/sensu/server.rb
165
+ - sensu.gemspec
166
+ homepage: https://github.com/sonian/sensu
167
+ licenses:
168
+ - MIT
169
+ post_install_message:
170
+ rdoc_options: []
171
+
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ none: false
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: "0"
180
+ required_rubygems_version: !ruby/object:Gem::Requirement
181
+ none: false
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: "0"
186
+ requirements: []
187
+
188
+ rubyforge_project:
189
+ rubygems_version: 1.8.8
190
+ signing_key:
191
+ specification_version: 3
192
+ summary: A server monitoring framework
193
+ test_files: []
194
+