sensu 0.8.0 → 0.8.4
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 +1 -0
- data/.travis.yml +12 -0
- data/Gemfile.lock +20 -15
- data/README.org +16 -9
- data/bin/sensu-api +1 -0
- data/bin/sensu-client +1 -0
- data/bin/sensu-server +1 -0
- data/lib/sensu.rb +1 -1
- data/lib/sensu/api.rb +48 -59
- data/lib/sensu/client.rb +30 -24
- data/lib/sensu/config.rb +55 -12
- data/lib/sensu/server.rb +28 -18
- data/sensu.gemspec +4 -6
- data/sensu.windows +3 -3
- metadata +15 -42
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sensu (0.8.
|
4
|
+
sensu (0.8.3)
|
5
5
|
amqp (= 0.7.4)
|
6
6
|
async_sinatra
|
7
|
+
cabin (= 0.1.7)
|
7
8
|
em-hiredis
|
8
|
-
em-syslog
|
9
9
|
hashie
|
10
10
|
json
|
11
11
|
rack (~> 1.3.4)
|
@@ -20,32 +20,39 @@ GEM
|
|
20
20
|
async_sinatra (0.5.0)
|
21
21
|
rack (>= 1.2.1)
|
22
22
|
sinatra (>= 1.0)
|
23
|
-
|
24
|
-
|
23
|
+
bacon (1.1.0)
|
24
|
+
cabin (0.1.7)
|
25
|
+
json
|
25
26
|
daemons (1.1.4)
|
27
|
+
diff-lcs (1.1.3)
|
26
28
|
em-hiredis (0.1.0)
|
27
29
|
hiredis (~> 0.3.0)
|
28
|
-
em-
|
29
|
-
|
30
|
-
em-ventually (0.1.2)
|
31
|
-
callsite (~> 0.0.5)
|
30
|
+
em-spec (0.2.5)
|
31
|
+
bacon
|
32
32
|
eventmachine
|
33
|
+
rspec (~> 2.6.0)
|
34
|
+
test-unit
|
33
35
|
eventmachine (0.12.10)
|
34
36
|
hashie (1.2.0)
|
35
37
|
hiredis (0.3.2)
|
36
38
|
json (1.6.1)
|
37
|
-
mime-types (1.16)
|
38
|
-
minitest (2.7.0)
|
39
39
|
rack (1.3.5)
|
40
40
|
rack-protection (1.1.4)
|
41
41
|
rack
|
42
42
|
rake (0.9.2)
|
43
|
-
|
44
|
-
|
43
|
+
rspec (2.6.0)
|
44
|
+
rspec-core (~> 2.6.0)
|
45
|
+
rspec-expectations (~> 2.6.0)
|
46
|
+
rspec-mocks (~> 2.6.0)
|
47
|
+
rspec-core (2.6.4)
|
48
|
+
rspec-expectations (2.6.0)
|
49
|
+
diff-lcs (~> 1.1.2)
|
50
|
+
rspec-mocks (2.6.0)
|
45
51
|
sinatra (1.3.1)
|
46
52
|
rack (~> 1.3, >= 1.3.4)
|
47
53
|
rack-protection (~> 1.1, >= 1.1.2)
|
48
54
|
tilt (~> 1.3, >= 1.3.3)
|
55
|
+
test-unit (2.4.0)
|
49
56
|
thin (1.2.11)
|
50
57
|
daemons (>= 1.0.9)
|
51
58
|
eventmachine (>= 0.12.6)
|
@@ -57,8 +64,6 @@ PLATFORMS
|
|
57
64
|
ruby
|
58
65
|
|
59
66
|
DEPENDENCIES
|
60
|
-
em-
|
61
|
-
minitest (~> 2.7.0)
|
67
|
+
em-spec
|
62
68
|
rake
|
63
|
-
rest-client
|
64
69
|
sensu!
|
data/README.org
CHANGED
@@ -1,28 +1,35 @@
|
|
1
1
|
* Welcome to Sensu
|
2
|
-
Sensu
|
2
|
+
Sensu, a monitoring framework that aims to be simple, malleable, and scalable.
|
3
3
|
|
4
4
|
[[https://github.com/sonian/sensu/raw/master/sensu-logo.png]]
|
5
5
|
|
6
|
-
|
6
|
+
[[http://portertech.ca/2011/11/01/sensu-a-monitoring-framework][Blog post]]
|
7
7
|
|
8
|
-
|
8
|
+
[[http://www.sonian.com/cloud-tools/cloud-monitoring-sensu/][Homepage]]
|
9
9
|
* Documentation
|
10
|
-
|
10
|
+
Find it [[https://github.com/sonian/sensu/wiki][HERE]].
|
11
|
+
* Other Projects
|
12
|
+
- [[https://github.com/sonian/sensu-plugins][Sensu Community Plugins]]
|
13
|
+
- [[https://github.com/sonian/sensu-dashboard][Sensu Dashboard]]
|
11
14
|
* License
|
12
15
|
Sensu is released under the [[https://github.com/sonian/sensu/blob/master/MIT-LICENSE.txt][MIT license]].
|
13
16
|
* Contributing
|
14
|
-
- [[http://help.github.com/fork-a-repo/][Fork]]
|
17
|
+
- [[http://help.github.com/fork-a-repo/][Fork]] Sensu
|
15
18
|
- Use a [[https://github.com/dchelimsky/rspec/wiki/Topic-Branches][topic branch]]
|
16
19
|
- Create a [[http://help.github.com/send-pull-requests/][pull request]]
|
17
20
|
|
18
21
|
Keep it simple.
|
19
22
|
** Testing
|
20
|
-
***
|
23
|
+
*** Travis CI
|
24
|
+
[[https://secure.travis-ci.org/sonian/sensu.png]]
|
25
|
+
*** System Dependencies
|
26
|
+
- Ruby (MRI) & Rubygems
|
21
27
|
- RabbitMQ
|
22
28
|
- Redis
|
23
29
|
*** Running
|
24
30
|
: bundle install
|
25
|
-
: rake
|
31
|
+
: rake
|
26
32
|
* Contributors
|
27
|
-
-
|
28
|
-
-
|
33
|
+
- [[http://twitter.com/portertech][Sean Porter]]
|
34
|
+
- [[http://twitter.com/amdprophet][Justin Kolberg]]
|
35
|
+
- [[http://twitter.com/kartar][James Turnbull]]
|
data/bin/sensu-api
CHANGED
data/bin/sensu-client
CHANGED
data/bin/sensu-server
CHANGED
data/lib/sensu.rb
CHANGED
data/lib/sensu/api.rb
CHANGED
@@ -13,10 +13,7 @@ module Sensu
|
|
13
13
|
|
14
14
|
%w[INT TERM].each do |signal|
|
15
15
|
Signal.trap(signal) do
|
16
|
-
|
17
|
-
EM.add_timer(1) do
|
18
|
-
EM.stop
|
19
|
-
end
|
16
|
+
self.stop(signal)
|
20
17
|
end
|
21
18
|
end
|
22
19
|
end
|
@@ -25,17 +22,19 @@ module Sensu
|
|
25
22
|
def self.setup(options={})
|
26
23
|
config = Sensu::Config.new(options)
|
27
24
|
@settings = config.settings
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
$logger = config.logger
|
26
|
+
$logger.debug('[setup] -- connecting to redis')
|
27
|
+
$redis = EM::Hiredis.connect('redis://' + @settings.redis.host + ':' + @settings.redis.port.to_s)
|
28
|
+
$logger.debug('[setup] -- connecting to rabbitmq')
|
32
29
|
connection = AMQP.connect(@settings.rabbitmq.to_hash.symbolize_keys)
|
33
|
-
|
30
|
+
$amq = MQ.new(connection)
|
34
31
|
end
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
def self.stop(signal)
|
34
|
+
$logger.warn('[process] -- ' + signal + ' -- stopping sensu api')
|
35
|
+
EM.add_timer(1) do
|
36
|
+
EM.stop
|
37
|
+
end
|
39
38
|
end
|
40
39
|
|
41
40
|
before do
|
@@ -43,12 +42,12 @@ module Sensu
|
|
43
42
|
end
|
44
43
|
|
45
44
|
aget '/clients' do
|
46
|
-
|
45
|
+
$logger.debug('[clients] -- ' + request.ip + ' -- GET -- request for client list')
|
47
46
|
current_clients = Array.new
|
48
|
-
|
47
|
+
$redis.smembers('clients').callback do |clients|
|
49
48
|
unless clients.empty?
|
50
49
|
clients.each_with_index do |client, index|
|
51
|
-
|
50
|
+
$redis.get('client:' + client).callback do |client_json|
|
52
51
|
current_clients.push(JSON.parse(client_json))
|
53
52
|
body current_clients.to_json if index == clients.size - 1
|
54
53
|
end
|
@@ -60,34 +59,34 @@ module Sensu
|
|
60
59
|
end
|
61
60
|
|
62
61
|
aget '/client/:name' do |client|
|
63
|
-
|
64
|
-
|
62
|
+
$logger.debug('[client] -- ' + request.ip + ' -- GET -- request for client -- ' + client)
|
63
|
+
$redis.get('client:' + client).callback do |client_json|
|
65
64
|
status 404 if client_json.nil?
|
66
65
|
body client_json
|
67
66
|
end
|
68
67
|
end
|
69
68
|
|
70
69
|
adelete '/client/:name' do |client|
|
71
|
-
|
72
|
-
|
70
|
+
$logger.debug('[client] -- ' + request.ip + ' -- DELETE -- request for client -- ' + client)
|
71
|
+
$redis.sismember('clients', client).callback do |client_exists|
|
73
72
|
unless client_exists == 0
|
74
|
-
|
73
|
+
$redis.exists('events:' + client).callback do |events_exist|
|
75
74
|
unless events_exist == 0
|
76
|
-
|
75
|
+
$redis.hgetall('events:' + client).callback do |events|
|
77
76
|
Hash[*events].keys.each do |check_name|
|
78
77
|
check = {:name => check_name, :issued => Time.now.to_i, :status => 0, :output => 'Client is being removed'}
|
79
|
-
|
78
|
+
$amq.queue('results').publish({:client => client, :check => check}.to_json)
|
80
79
|
end
|
81
80
|
EM.add_timer(5) do
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
$redis.srem('clients', client)
|
82
|
+
$redis.del('events:' + client)
|
83
|
+
$redis.del('client:' + client)
|
85
84
|
end
|
86
85
|
end
|
87
86
|
else
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
$redis.srem('clients', client)
|
88
|
+
$redis.del('events:' + client)
|
89
|
+
$redis.del('client:' + client)
|
91
90
|
end
|
92
91
|
status 204
|
93
92
|
body nil
|
@@ -100,12 +99,12 @@ module Sensu
|
|
100
99
|
end
|
101
100
|
|
102
101
|
aget '/events' do
|
103
|
-
|
102
|
+
$logger.debug('[events] -- ' + request.ip + ' -- GET -- request for event list')
|
104
103
|
current_events = Hash.new
|
105
|
-
|
104
|
+
$redis.smembers('clients').callback do |clients|
|
106
105
|
unless clients.empty?
|
107
106
|
clients.each_with_index do |client, index|
|
108
|
-
|
107
|
+
$redis.hgetall('events:' + client).callback do |events|
|
109
108
|
client_events = Hash[*events]
|
110
109
|
client_events.each do |key, value|
|
111
110
|
client_events[key] = JSON.parse(value)
|
@@ -121,8 +120,8 @@ module Sensu
|
|
121
120
|
end
|
122
121
|
|
123
122
|
aget '/event/:client/:check' do |client, check|
|
124
|
-
|
125
|
-
|
123
|
+
$logger.debug('[event] -- ' + request.ip + ' -- GET -- request for event -- ' + client + ' -- ' + check)
|
124
|
+
$redis.hgetall('events:' + client).callback do |events|
|
126
125
|
client_events = Hash[*events]
|
127
126
|
event = client_events[check]
|
128
127
|
status 404 if event.nil?
|
@@ -131,21 +130,21 @@ module Sensu
|
|
131
130
|
end
|
132
131
|
|
133
132
|
apost '/stash/*' do |path|
|
134
|
-
|
133
|
+
$logger.debug('[stash] -- ' + request.ip + ' -- POST -- request for stash -- ' + path)
|
135
134
|
begin
|
136
135
|
stash = JSON.parse(request.body.read)
|
137
136
|
rescue JSON::ParserError
|
138
137
|
status 400
|
139
138
|
body nil
|
140
139
|
end
|
141
|
-
|
140
|
+
$redis.set('stash:' + path, stash.to_json).callback do
|
142
141
|
status 201
|
143
142
|
body nil
|
144
143
|
end
|
145
144
|
end
|
146
145
|
|
147
146
|
apost '/stashes' do
|
148
|
-
|
147
|
+
$logger.debug('[stashes] -- ' + request.ip + ' -- POST -- request for multiple stashes')
|
149
148
|
begin
|
150
149
|
paths = JSON.parse(request.body.read)
|
151
150
|
rescue JSON::ParserError
|
@@ -155,7 +154,7 @@ module Sensu
|
|
155
154
|
stashes = Hash.new
|
156
155
|
if paths.is_a?(Array)
|
157
156
|
paths.each_with_index do |path, index|
|
158
|
-
|
157
|
+
$redis.get('stash:' + path).callback do |stash|
|
159
158
|
stashes[path] = JSON.parse(stash) unless stash.nil?
|
160
159
|
body stashes.to_json if index == paths.size - 1
|
161
160
|
end
|
@@ -167,18 +166,18 @@ module Sensu
|
|
167
166
|
end
|
168
167
|
|
169
168
|
aget '/stash/*' do |path|
|
170
|
-
|
171
|
-
|
169
|
+
$logger.debug('[stash] -- ' + request.ip + ' -- GET -- request for stash -- ' + path)
|
170
|
+
$redis.get('stash:' + path).callback do |stash|
|
172
171
|
status 404 if stash.nil?
|
173
172
|
body stash
|
174
173
|
end
|
175
174
|
end
|
176
175
|
|
177
176
|
adelete '/stash/*' do |path|
|
178
|
-
|
179
|
-
|
177
|
+
$logger.debug('[stash] -- ' + request.ip + ' -- DELETE -- request for stash -- ' + path)
|
178
|
+
$redis.exists('stash:' + path).callback do |stash_exist|
|
180
179
|
unless stash_exist == 0
|
181
|
-
|
180
|
+
$redis.del('stash:' + path).callback do
|
182
181
|
status 204
|
183
182
|
body nil
|
184
183
|
end
|
@@ -189,31 +188,21 @@ module Sensu
|
|
189
188
|
end
|
190
189
|
end
|
191
190
|
|
192
|
-
|
193
|
-
|
194
|
-
client
|
195
|
-
|
196
|
-
|
197
|
-
"subscriptions": [
|
198
|
-
"foo",
|
199
|
-
"bar"
|
200
|
-
]
|
201
|
-
}'
|
202
|
-
conn.redis.set('client:test', client).callback do
|
203
|
-
conn.redis.sadd('clients', 'test').callback do
|
204
|
-
conn.redis.hset('events:test', 'test', {
|
191
|
+
def self.test(options={})
|
192
|
+
self.setup(options)
|
193
|
+
$redis.set('client:test', @settings.client).callback do
|
194
|
+
$redis.sadd('clients', @settings.client.name).callback do
|
195
|
+
$redis.hset('events:' + @settings.client.name, 'test', {
|
205
196
|
:status => 2,
|
206
197
|
:output => 'CRITICAL',
|
207
198
|
:flapping => false,
|
208
199
|
:occurrences => 1
|
209
200
|
}.to_json).callback do
|
210
|
-
|
211
|
-
status 201
|
212
|
-
body nil
|
213
|
-
end
|
201
|
+
$redis.set('stash:test/test', '{"key": "value"}')
|
214
202
|
end
|
215
203
|
end
|
216
204
|
end
|
205
|
+
self.run!(:port => @settings.api.port)
|
217
206
|
end
|
218
207
|
end
|
219
208
|
end
|
data/lib/sensu/client.rb
CHANGED
@@ -13,34 +13,39 @@ module Sensu
|
|
13
13
|
|
14
14
|
%w[INT TERM].each do |signal|
|
15
15
|
Signal.trap(signal) do
|
16
|
-
|
17
|
-
EM.add_timer(1) do
|
18
|
-
EM.stop
|
19
|
-
end
|
16
|
+
client.stop(signal)
|
20
17
|
end
|
21
18
|
end
|
22
19
|
end
|
23
20
|
end
|
24
21
|
|
25
22
|
def initialize(options={})
|
26
|
-
config = Sensu::Config.new(
|
23
|
+
config = Sensu::Config.new(options)
|
27
24
|
@settings = config.settings
|
28
|
-
|
25
|
+
@logger = config.logger
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop(signal)
|
29
|
+
@logger.warn('[process] -- ' + signal + ' -- stopping sensu client')
|
30
|
+
EM.add_timer(1) do
|
31
|
+
EM.stop
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
def setup_amqp
|
32
|
-
|
36
|
+
@logger.debug('[amqp] -- connecting to rabbitmq')
|
33
37
|
connection = AMQP.connect(@settings.rabbitmq.to_hash.symbolize_keys)
|
34
38
|
@amq = MQ.new(connection)
|
35
39
|
end
|
36
40
|
|
37
41
|
def publish_keepalive
|
38
|
-
|
42
|
+
@logger.debug('[keepalive] -- publishing keepalive -- ' + @settings.client.timestamp.to_s)
|
39
43
|
@keepalive_queue ||= @amq.queue('keepalives')
|
40
44
|
@keepalive_queue.publish(@settings.client.to_json)
|
41
45
|
end
|
42
46
|
|
43
47
|
def setup_keepalives
|
48
|
+
@logger.debug('[keepalive] -- setup keepalives')
|
44
49
|
@settings.client.timestamp = Time.now.to_i
|
45
50
|
publish_keepalive
|
46
51
|
EM.add_periodic_timer(30) do
|
@@ -50,7 +55,7 @@ module Sensu
|
|
50
55
|
end
|
51
56
|
|
52
57
|
def publish_result(check)
|
53
|
-
|
58
|
+
@logger.info('[result] -- publishing check result -- ' + check.name)
|
54
59
|
@result_queue ||= @amq.queue('results')
|
55
60
|
@result_queue.publish({
|
56
61
|
:client => @settings.client.name,
|
@@ -59,6 +64,7 @@ module Sensu
|
|
59
64
|
end
|
60
65
|
|
61
66
|
def execute_check(check)
|
67
|
+
@logger.debug('[execute] -- executing check -- ' + check.name)
|
62
68
|
@checks_in_progress ||= Array.new
|
63
69
|
if @settings.checks.key?(check.name)
|
64
70
|
unless @checks_in_progress.include?(check.name)
|
@@ -82,7 +88,7 @@ module Sensu
|
|
82
88
|
end
|
83
89
|
EM.defer(execute, publish)
|
84
90
|
else
|
85
|
-
|
91
|
+
@logger.warn('[execute] -- missing client attributes -- ' + unmatched_tokens.join(', ') + ' -- ' + check.name)
|
86
92
|
check.status = 3
|
87
93
|
check.output = 'Missing client attributes: ' + unmatched_tokens.join(', ')
|
88
94
|
check.internal = true
|
@@ -91,7 +97,7 @@ module Sensu
|
|
91
97
|
end
|
92
98
|
end
|
93
99
|
else
|
94
|
-
|
100
|
+
@logger.warn('[execute] -- unkown check -- ' + check.name)
|
95
101
|
check.status = 3
|
96
102
|
check.output = 'Unknown check'
|
97
103
|
check.internal = true
|
@@ -101,22 +107,24 @@ module Sensu
|
|
101
107
|
end
|
102
108
|
|
103
109
|
def setup_subscriptions
|
110
|
+
@logger.debug('[subscribe] -- setup subscriptions')
|
104
111
|
@check_queue = @amq.queue(UUIDTools::UUID.random_create.to_s, :exclusive => true)
|
105
112
|
@settings.client.subscriptions.each do |exchange|
|
106
|
-
|
113
|
+
@logger.debug('[subscribe] -- queue binding to exchange -- ' + exchange)
|
107
114
|
@check_queue.bind(@amq.fanout(exchange))
|
108
115
|
end
|
109
116
|
@check_queue.subscribe do |check_json|
|
110
117
|
check = Hashie::Mash.new(JSON.parse(check_json))
|
111
|
-
|
118
|
+
@logger.info('[subscribe] -- received check -- ' + check.name)
|
112
119
|
execute_check(check)
|
113
120
|
end
|
114
121
|
end
|
115
122
|
|
116
123
|
def setup_queue_monitor
|
124
|
+
@logger.debug('[monitor] -- setup queue monitor')
|
117
125
|
EM.add_periodic_timer(5) do
|
118
126
|
unless @check_queue.subscribed?
|
119
|
-
|
127
|
+
@logger.warn('[monitor] -- reconnecting to rabbitmq')
|
120
128
|
@check_queue.delete
|
121
129
|
EM.add_timer(1) do
|
122
130
|
setup_subscriptions
|
@@ -126,8 +134,9 @@ module Sensu
|
|
126
134
|
end
|
127
135
|
|
128
136
|
def setup_socket
|
129
|
-
|
137
|
+
@logger.debug('[socket] -- starting up socket server')
|
130
138
|
EM.start_server('127.0.0.1', 3030, ClientSocket) do |socket|
|
139
|
+
socket.logger = @logger
|
131
140
|
socket.client_name = @settings.client.name
|
132
141
|
socket.result_queue = @amq.queue('results')
|
133
142
|
end
|
@@ -135,35 +144,32 @@ module Sensu
|
|
135
144
|
end
|
136
145
|
|
137
146
|
class ClientSocket < EM::Connection
|
138
|
-
attr_accessor :client_name, :result_queue
|
139
|
-
|
140
|
-
def post_init
|
141
|
-
EM.debug('[socket] -- client connected')
|
142
|
-
end
|
147
|
+
attr_accessor :logger, :client_name, :result_queue
|
143
148
|
|
144
149
|
def receive_data(data)
|
150
|
+
@logger.debug('[socket] -- client connected')
|
145
151
|
begin
|
146
152
|
check = Hashie::Mash.new(JSON.parse(data))
|
147
153
|
validates = %w[name status output].all? do |key|
|
148
154
|
check.key?(key)
|
149
155
|
end
|
150
156
|
if validates
|
151
|
-
|
157
|
+
@logger.info('[socket] -- publishing check result -- ' + check.name)
|
152
158
|
@result_queue.publish({
|
153
159
|
:client => @client_name,
|
154
160
|
:check => check.to_hash
|
155
161
|
}.to_json)
|
156
162
|
else
|
157
|
-
|
163
|
+
@logger.warn('[socket] -- a check name, exit status, and output are required -- e.g. {name: x, status: 0, output: "y"}')
|
158
164
|
end
|
159
165
|
rescue JSON::ParserError
|
160
|
-
|
166
|
+
@logger.warn('[socket] -- could not parse check result -- expecting JSON')
|
161
167
|
end
|
162
168
|
close_connection
|
163
169
|
end
|
164
170
|
|
165
171
|
def unbind
|
166
|
-
|
172
|
+
@logger.debug('[socket] -- client disconnected')
|
167
173
|
end
|
168
174
|
end
|
169
175
|
end
|
data/lib/sensu/config.rb
CHANGED
@@ -4,42 +4,83 @@ require 'json'
|
|
4
4
|
require 'hashie'
|
5
5
|
require 'uuidtools'
|
6
6
|
require 'amqp'
|
7
|
-
require '
|
7
|
+
require 'cabin'
|
8
|
+
require 'cabin/outputs/em-stdlib-logger'
|
8
9
|
require File.join(File.dirname(__FILE__), 'helpers')
|
9
10
|
|
10
11
|
module Sensu
|
11
12
|
class Config
|
12
|
-
attr_accessor :settings
|
13
|
+
attr_accessor :settings, :logger
|
13
14
|
|
14
15
|
def initialize(options={})
|
15
16
|
config_file = options[:config_file] || '/etc/sensu/config.json'
|
16
|
-
|
17
|
-
|
17
|
+
if File.readable?(config_file)
|
18
|
+
begin
|
19
|
+
@settings = Hashie::Mash.new(JSON.parse(File.open(config_file, 'r').read))
|
20
|
+
rescue JSON::ParserError => e
|
21
|
+
invalid_config('configuration file must be valid JSON: ' + e)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
invalid_config('configuration file does not exist or is not readable: ' + config_file)
|
25
|
+
end
|
26
|
+
@logger = Cabin::Channel.new
|
27
|
+
log_dir = File.writable?('/var/log') ? '/var/log' : '/tmp'
|
28
|
+
ruby_logger = Logger.new(File.join(log_dir, 'sensu.log'))
|
29
|
+
@logger.subscribe(Cabin::Outputs::EmStdlibLogger.new(ruby_logger))
|
30
|
+
@logger.level = options[:verbose] ? 'debug' : 'info'
|
31
|
+
validate_config(options['type'])
|
18
32
|
end
|
19
33
|
|
20
|
-
def validate_config
|
34
|
+
def validate_config(type)
|
35
|
+
@logger.debug('[config] -- validating configuration')
|
36
|
+
has_keys(%w[rabbitmq])
|
37
|
+
case type
|
38
|
+
when 'server'
|
39
|
+
has_keys(%w[redis handlers checks])
|
40
|
+
when 'api'
|
41
|
+
has_keys(%w[redis api])
|
42
|
+
when 'client'
|
43
|
+
has_keys(%w[client checks])
|
44
|
+
end
|
21
45
|
@settings.checks.each do |name, details|
|
22
46
|
unless details.interval.is_a?(Integer) && details.interval > 0
|
23
|
-
|
47
|
+
invalid_config('missing interval for check ' + name)
|
24
48
|
end
|
25
49
|
unless details.command.is_a?(String)
|
26
|
-
|
50
|
+
invalid_config('missing command for check ' + name)
|
27
51
|
end
|
28
52
|
unless details.subscribers.is_a?(Array) && details.subscribers.count > 0
|
29
|
-
|
53
|
+
invalid_config('missing subscribers for check ' + name)
|
30
54
|
end
|
31
55
|
end
|
32
56
|
unless @settings.client.name.is_a?(String)
|
33
|
-
|
57
|
+
invalid_config('client must have a name')
|
34
58
|
end
|
35
59
|
unless @settings.client.address.is_a?(String)
|
36
|
-
|
60
|
+
invalid_config('client must have an address (ip or hostname)')
|
37
61
|
end
|
38
62
|
unless @settings.client.subscriptions.is_a?(Array) && @settings.client.subscriptions.count > 0
|
39
|
-
|
63
|
+
invalid_config('client must have subscriptions')
|
64
|
+
end
|
65
|
+
if type
|
66
|
+
@logger.debug('[config] -- configuration valid -- running ' + type)
|
67
|
+
puts 'configuration valid -- running ' + type
|
40
68
|
end
|
41
69
|
end
|
42
70
|
|
71
|
+
def has_keys(keys)
|
72
|
+
keys.each do |key|
|
73
|
+
unless @settings.key?(key)
|
74
|
+
invalid_config('missing the following key: ' + key)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def invalid_config(message)
|
80
|
+
@logger.error('[config] -- configuration invalid -- ' + message)
|
81
|
+
raise 'configuration invalid, ' + message
|
82
|
+
end
|
83
|
+
|
43
84
|
def self.read_arguments(arguments)
|
44
85
|
options = Hash.new
|
45
86
|
optparse = OptionParser.new do |opts|
|
@@ -54,10 +95,12 @@ module Sensu
|
|
54
95
|
options[:worker] = true
|
55
96
|
end
|
56
97
|
end
|
57
|
-
options[:config_file] = nil
|
58
98
|
opts.on('-c', '--config FILE', 'Sensu JSON config FILE (default: /etc/sensu/config.json)') do |file|
|
59
99
|
options[:config_file] = file
|
60
100
|
end
|
101
|
+
opts.on('-v', '--verbose', 'Enable verbose logging') do
|
102
|
+
options[:verbose] = true
|
103
|
+
end
|
61
104
|
end
|
62
105
|
optparse.parse!(arguments)
|
63
106
|
options
|
data/lib/sensu/server.rb
CHANGED
@@ -21,38 +21,43 @@ module Sensu
|
|
21
21
|
|
22
22
|
%w[INT TERM].each do |signal|
|
23
23
|
Signal.trap(signal) do
|
24
|
-
|
25
|
-
EM.add_timer(1) do
|
26
|
-
EM.stop
|
27
|
-
end
|
24
|
+
server.stop(signal)
|
28
25
|
end
|
29
26
|
end
|
30
27
|
end
|
31
28
|
end
|
32
29
|
|
33
30
|
def initialize(options={})
|
34
|
-
config = Sensu::Config.new(
|
31
|
+
config = Sensu::Config.new(options)
|
35
32
|
@settings = config.settings
|
33
|
+
@logger = config.logger
|
36
34
|
@is_worker = options[:worker]
|
37
|
-
|
35
|
+
end
|
36
|
+
|
37
|
+
def stop(signal)
|
38
|
+
@logger.warn('[process] -- ' + signal + ' -- stopping sensu server')
|
39
|
+
EM.add_timer(1) do
|
40
|
+
EM.stop
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
def setup_redis
|
41
|
-
|
45
|
+
@logger.debug('[redis] -- connecting to redis')
|
42
46
|
@redis = EM::Hiredis.connect('redis://' + @settings.redis.host + ':' + @settings.redis.port.to_s)
|
43
47
|
end
|
44
48
|
|
45
49
|
def setup_amqp
|
46
|
-
|
50
|
+
@logger.debug('[amqp] -- connecting to rabbitmq')
|
47
51
|
connection = AMQP.connect(@settings.rabbitmq.to_hash.symbolize_keys)
|
48
52
|
@amq = MQ.new(connection)
|
49
53
|
end
|
50
54
|
|
51
55
|
def setup_keepalives
|
56
|
+
@logger.debug('[keepalive] -- setup keepalive')
|
52
57
|
@keepalive_queue = @amq.queue('keepalives')
|
53
58
|
@keepalive_queue.subscribe do |keepalive_json|
|
54
59
|
client = Hashie::Mash.new(JSON.parse(keepalive_json))
|
55
|
-
|
60
|
+
@logger.debug('[keepalive] -- received keepalive -- ' + client.name)
|
56
61
|
@redis.set('client:' + client.name, keepalive_json).callback do
|
57
62
|
@redis.sadd('clients', client.name)
|
58
63
|
end
|
@@ -71,18 +76,19 @@ module Sensu
|
|
71
76
|
end
|
72
77
|
report = proc do |output|
|
73
78
|
output.split(/\n+/).each do |line|
|
74
|
-
|
79
|
+
@logger.info('[handler] -- ' + line)
|
75
80
|
end
|
76
81
|
end
|
77
82
|
if @settings.handlers.key?(event.check.handler)
|
78
|
-
|
83
|
+
@logger.debug('[event] -- handling event -- ' + [event.check.handler, event.client.name, event.check.name].join(' -- '))
|
79
84
|
EM.defer(handler, report)
|
80
85
|
else
|
81
|
-
|
86
|
+
@logger.warn('[event] -- handler does not exist -- ' + event.check.handler)
|
82
87
|
end
|
83
88
|
end
|
84
89
|
|
85
90
|
def process_result(result)
|
91
|
+
@logger.debug('[result] -- processing result for ' + [result.client, result.check.name].join(' -- '))
|
86
92
|
@redis.get('client:' + result.client).callback do |client_json|
|
87
93
|
unless client_json.nil?
|
88
94
|
client = Hashie::Mash.new(JSON.parse(client_json))
|
@@ -136,7 +142,7 @@ module Sensu
|
|
136
142
|
end
|
137
143
|
end
|
138
144
|
else
|
139
|
-
|
145
|
+
@logger.debug('[result] -- check is flapping -- ' + client.name + ' -- ' + check.name)
|
140
146
|
@redis.hset('events:' + client.name, check.name, previous_event.merge({'flapping' => true}).to_json)
|
141
147
|
end
|
142
148
|
elsif check['status'] != 0
|
@@ -165,15 +171,17 @@ module Sensu
|
|
165
171
|
end
|
166
172
|
|
167
173
|
def setup_results
|
174
|
+
@logger.debug('[result] -- setup results')
|
168
175
|
@result_queue = @amq.queue('results')
|
169
176
|
@result_queue.subscribe do |result_json|
|
170
177
|
result = Hashie::Mash.new(JSON.parse(result_json))
|
171
|
-
|
178
|
+
@logger.info('[result] -- received result -- ' + result.client + ' -- ' + result.check.name)
|
172
179
|
process_result(result)
|
173
180
|
end
|
174
181
|
end
|
175
182
|
|
176
183
|
def setup_publisher(options={})
|
184
|
+
@logger.debug('[publisher] -- setup publisher')
|
177
185
|
exchanges = Hash.new
|
178
186
|
stagger = options[:test] ? 0 : 7
|
179
187
|
@settings.checks.each_with_index do |(name, details), index|
|
@@ -185,7 +193,7 @@ module Sensu
|
|
185
193
|
end
|
186
194
|
interval = options[:test] ? 0.5 : details.interval
|
187
195
|
EM.add_periodic_timer(interval) do
|
188
|
-
|
196
|
+
@logger.info('[publisher] -- publishing check -- ' + name + ' -- ' + exchange)
|
189
197
|
exchanges[exchange].publish({'name' => name, 'issued' => Time.now.to_i}.to_json)
|
190
198
|
end
|
191
199
|
end
|
@@ -195,8 +203,9 @@ module Sensu
|
|
195
203
|
end
|
196
204
|
|
197
205
|
def setup_keepalive_monitor
|
206
|
+
@logger.debug('[keepalive] -- setup keepalive monitor')
|
198
207
|
EM.add_periodic_timer(30) do
|
199
|
-
|
208
|
+
@logger.debug('[keepalive] -- checking for stale clients')
|
200
209
|
@redis.smembers('clients').callback do |clients|
|
201
210
|
clients.each do |client_id|
|
202
211
|
@redis.get('client:' + client_id).callback do |client_json|
|
@@ -234,13 +243,14 @@ module Sensu
|
|
234
243
|
end
|
235
244
|
|
236
245
|
def setup_queue_monitor
|
246
|
+
@logger.debug('[monitor] -- setup queue monitor')
|
237
247
|
EM.add_periodic_timer(5) do
|
238
248
|
unless @keepalive_queue.subscribed?
|
239
|
-
|
249
|
+
@logger.warn('[monitor] -- reconnecting to rabbitmq')
|
240
250
|
setup_keepalives
|
241
251
|
end
|
242
252
|
unless @result_queue.subscribed?
|
243
|
-
|
253
|
+
@logger.warn('[monitor] -- reconnecting to rabbitmq')
|
244
254
|
setup_results
|
245
255
|
end
|
246
256
|
end
|
data/sensu.gemspec
CHANGED
@@ -7,8 +7,8 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.authors = ["Sean Porter", "Justin Kolberg"]
|
8
8
|
s.email = ["sean.porter@sonian.net", "justin.kolberg@sonian.net"]
|
9
9
|
s.homepage = "https://github.com/sonian/sensu"
|
10
|
-
s.summary = %q{A
|
11
|
-
s.description = %q{A
|
10
|
+
s.summary = %q{A monitoring framework}
|
11
|
+
s.description = %q{A monitoring framework that aims to be simple, malleable, and scalable. Uses the publish/subscribe model.}
|
12
12
|
s.license = "MIT"
|
13
13
|
s.has_rdoc = false
|
14
14
|
|
@@ -16,16 +16,14 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.add_dependency("json")
|
17
17
|
s.add_dependency("hashie")
|
18
18
|
s.add_dependency("uuidtools")
|
19
|
-
s.add_dependency("
|
19
|
+
s.add_dependency("cabin", "0.1.7")
|
20
20
|
s.add_dependency("em-hiredis")
|
21
21
|
s.add_dependency("rack", "~> 1.3.4")
|
22
22
|
s.add_dependency("async_sinatra")
|
23
23
|
s.add_dependency("thin")
|
24
24
|
|
25
25
|
s.add_development_dependency("rake")
|
26
|
-
s.add_development_dependency("
|
27
|
-
s.add_development_dependency("em-ventually")
|
28
|
-
s.add_development_dependency("rest-client")
|
26
|
+
s.add_development_dependency("em-spec")
|
29
27
|
|
30
28
|
s.files = `git ls-files`.split("\n").reject {|f| f =~ /(dist|test|png)/}
|
31
29
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
data/sensu.windows
CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.authors = ["Sean Porter", "Justin Kolberg"]
|
7
7
|
s.email = ["sean.porter@sonian.net", "justin.kolberg@sonian.net"]
|
8
8
|
s.homepage = "https://github.com/sonian/sensu"
|
9
|
-
s.summary = %q{A
|
10
|
-
s.description = %q{A
|
9
|
+
s.summary = %q{A monitoring framework}
|
10
|
+
s.description = %q{A monitoring framework that aims to be simple, malleable, and scalable. Uses the publish/subscribe model.}
|
11
11
|
s.license = "MIT"
|
12
12
|
s.has_rdoc = false
|
13
13
|
|
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_dependency("json")
|
24
24
|
s.add_dependency("hashie")
|
25
25
|
s.add_dependency("uuidtools")
|
26
|
-
s.add_dependency("
|
26
|
+
s.add_dependency("cabin", "0.1.7")
|
27
27
|
|
28
28
|
s.files = `git ls-files`.split("\n").reject {|f| f =~ /(dist|test|server|api|png)/}
|
29
29
|
s.executables = "sensu-client"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sensu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 55
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 8
|
9
|
-
-
|
10
|
-
version: 0.8.
|
9
|
+
- 4
|
10
|
+
version: 0.8.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sean Porter
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2011-
|
19
|
+
date: 2011-11-09 00:00:00 -08:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -78,17 +78,19 @@ dependencies:
|
|
78
78
|
type: :runtime
|
79
79
|
version_requirements: *id004
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
|
-
name:
|
81
|
+
name: cabin
|
82
82
|
prerelease: false
|
83
83
|
requirement: &id005 !ruby/object:Gem::Requirement
|
84
84
|
none: false
|
85
85
|
requirements:
|
86
|
-
- - "
|
86
|
+
- - "="
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
hash:
|
88
|
+
hash: 21
|
89
89
|
segments:
|
90
90
|
- 0
|
91
|
-
|
91
|
+
- 1
|
92
|
+
- 7
|
93
|
+
version: 0.1.7
|
92
94
|
type: :runtime
|
93
95
|
version_requirements: *id005
|
94
96
|
- !ruby/object:Gem::Dependency
|
@@ -164,25 +166,9 @@ dependencies:
|
|
164
166
|
type: :development
|
165
167
|
version_requirements: *id010
|
166
168
|
- !ruby/object:Gem::Dependency
|
167
|
-
name:
|
169
|
+
name: em-spec
|
168
170
|
prerelease: false
|
169
171
|
requirement: &id011 !ruby/object:Gem::Requirement
|
170
|
-
none: false
|
171
|
-
requirements:
|
172
|
-
- - ~>
|
173
|
-
- !ruby/object:Gem::Version
|
174
|
-
hash: 19
|
175
|
-
segments:
|
176
|
-
- 2
|
177
|
-
- 7
|
178
|
-
- 0
|
179
|
-
version: 2.7.0
|
180
|
-
type: :development
|
181
|
-
version_requirements: *id011
|
182
|
-
- !ruby/object:Gem::Dependency
|
183
|
-
name: em-ventually
|
184
|
-
prerelease: false
|
185
|
-
requirement: &id012 !ruby/object:Gem::Requirement
|
186
172
|
none: false
|
187
173
|
requirements:
|
188
174
|
- - ">="
|
@@ -192,22 +178,8 @@ dependencies:
|
|
192
178
|
- 0
|
193
179
|
version: "0"
|
194
180
|
type: :development
|
195
|
-
version_requirements: *
|
196
|
-
|
197
|
-
name: rest-client
|
198
|
-
prerelease: false
|
199
|
-
requirement: &id013 !ruby/object:Gem::Requirement
|
200
|
-
none: false
|
201
|
-
requirements:
|
202
|
-
- - ">="
|
203
|
-
- !ruby/object:Gem::Version
|
204
|
-
hash: 3
|
205
|
-
segments:
|
206
|
-
- 0
|
207
|
-
version: "0"
|
208
|
-
type: :development
|
209
|
-
version_requirements: *id013
|
210
|
-
description: A server monitoring framework using the publish-subscribe model
|
181
|
+
version_requirements: *id011
|
182
|
+
description: A monitoring framework that aims to be simple, malleable, and scalable. Uses the publish/subscribe model.
|
211
183
|
email:
|
212
184
|
- sean.porter@sonian.net
|
213
185
|
- justin.kolberg@sonian.net
|
@@ -221,6 +193,7 @@ extra_rdoc_files: []
|
|
221
193
|
|
222
194
|
files:
|
223
195
|
- .gitignore
|
196
|
+
- .travis.yml
|
224
197
|
- Gemfile
|
225
198
|
- Gemfile.lock
|
226
199
|
- MIT-LICENSE.txt
|
@@ -270,6 +243,6 @@ rubyforge_project:
|
|
270
243
|
rubygems_version: 1.3.7
|
271
244
|
signing_key:
|
272
245
|
specification_version: 3
|
273
|
-
summary: A
|
246
|
+
summary: A monitoring framework
|
274
247
|
test_files: []
|
275
248
|
|