sensu 0.6.0-x86-mswin32

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.
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
+