sensu 0.9.1 → 0.9.2.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sensu.rb +1 -1
- data/lib/sensu/api.rb +10 -5
- data/lib/sensu/client.rb +72 -50
- data/lib/sensu/config.rb +72 -80
- data/lib/sensu/patches/ruby.rb +7 -1
- data/lib/sensu/server.rb +32 -17
- metadata +12 -9
data/lib/sensu.rb
CHANGED
data/lib/sensu/api.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'config')
|
2
2
|
|
3
|
+
require 'thin'
|
3
4
|
require 'sinatra/async'
|
4
5
|
require 'redis'
|
5
6
|
|
@@ -12,7 +13,9 @@ module Sensu
|
|
12
13
|
def self.run(options={})
|
13
14
|
EM::run do
|
14
15
|
self.setup(options)
|
15
|
-
|
16
|
+
|
17
|
+
Thin::Logging.silent = true
|
18
|
+
Thin::Server.start(self, $settings.api.port)
|
16
19
|
|
17
20
|
%w[INT TERM].each do |signal|
|
18
21
|
Signal.trap(signal) do
|
@@ -24,8 +27,8 @@ module Sensu
|
|
24
27
|
|
25
28
|
def self.setup(options={})
|
26
29
|
config = Sensu::Config.new(options)
|
30
|
+
$logger = config.logger
|
27
31
|
$settings = config.settings
|
28
|
-
$logger = config.logger || config.open_log
|
29
32
|
if options[:daemonize]
|
30
33
|
Process.daemonize
|
31
34
|
end
|
@@ -246,7 +249,7 @@ module Sensu
|
|
246
249
|
end
|
247
250
|
end
|
248
251
|
|
249
|
-
def self.
|
252
|
+
def self.setup_test_scaffolding(options={})
|
250
253
|
self.setup(options)
|
251
254
|
$settings.client.timestamp = Time.now.to_i
|
252
255
|
$redis.set('client:' + $settings.client.name, $settings.client.to_json).callback do
|
@@ -258,7 +261,8 @@ module Sensu
|
|
258
261
|
:occurrences => 1
|
259
262
|
}.to_json).callback do
|
260
263
|
$redis.set('stash:test/test', '{"key": "value"}').callback do
|
261
|
-
|
264
|
+
Thin::Logging.silent = true
|
265
|
+
Thin::Server.start(self, $settings.api.port)
|
262
266
|
end
|
263
267
|
end
|
264
268
|
end
|
@@ -267,7 +271,8 @@ module Sensu
|
|
267
271
|
|
268
272
|
def self.stop(signal)
|
269
273
|
$logger.warn('[stop] -- stopping sensu api -- ' + signal)
|
270
|
-
|
274
|
+
$logger.warn('[stop] -- stopping reactor')
|
275
|
+
EM::PeriodicTimer.new(0.25) do
|
271
276
|
EM::stop_event_loop
|
272
277
|
end
|
273
278
|
end
|
data/lib/sensu/client.rb
CHANGED
@@ -15,6 +15,7 @@ module Sensu
|
|
15
15
|
client.setup_keepalives
|
16
16
|
client.setup_subscriptions
|
17
17
|
client.setup_queue_monitor
|
18
|
+
client.setup_standalone
|
18
19
|
client.setup_socket
|
19
20
|
|
20
21
|
%w[INT TERM].each do |signal|
|
@@ -27,8 +28,10 @@ module Sensu
|
|
27
28
|
|
28
29
|
def initialize(options={})
|
29
30
|
config = Sensu::Config.new(options)
|
31
|
+
@logger = config.logger
|
30
32
|
@settings = config.settings
|
31
|
-
@
|
33
|
+
@timers = Array.new
|
34
|
+
@checks_in_progress = Array.new
|
32
35
|
end
|
33
36
|
|
34
37
|
def setup_amqp
|
@@ -46,7 +49,7 @@ module Sensu
|
|
46
49
|
def setup_keepalives
|
47
50
|
@logger.debug('[keepalive] -- setup keepalives')
|
48
51
|
publish_keepalive
|
49
|
-
EM::PeriodicTimer.new(30) do
|
52
|
+
@timers << EM::PeriodicTimer.new(30) do
|
50
53
|
publish_keepalive
|
51
54
|
end
|
52
55
|
end
|
@@ -60,16 +63,24 @@ module Sensu
|
|
60
63
|
end
|
61
64
|
|
62
65
|
def execute_check(check)
|
63
|
-
@logger.debug('[execute] --
|
64
|
-
@checks_in_progress ||= Array.new
|
66
|
+
@logger.debug('[execute] -- attempting to execute check -- ' + check.name)
|
65
67
|
if @settings.checks.key?(check.name)
|
66
68
|
unless @checks_in_progress.include?(check.name)
|
69
|
+
@logger.debug('[execute] -- executing check -- ' + check.name)
|
67
70
|
@checks_in_progress.push(check.name)
|
68
71
|
unmatched_tokens = Array.new
|
69
72
|
command = @settings.checks[check.name].command.gsub(/:::(.*?):::/) do
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
+
token = $1.to_s
|
74
|
+
begin
|
75
|
+
value = @settings.client.instance_eval(token)
|
76
|
+
if value.nil?
|
77
|
+
unmatched_tokens.push(token)
|
78
|
+
end
|
79
|
+
rescue NoMethodError
|
80
|
+
value = nil
|
81
|
+
unmatched_tokens.push(token)
|
82
|
+
end
|
83
|
+
value
|
73
84
|
end
|
74
85
|
if unmatched_tokens.empty?
|
75
86
|
execute = proc do
|
@@ -97,6 +108,8 @@ module Sensu
|
|
97
108
|
publish_result(check)
|
98
109
|
@checks_in_progress.delete(check.name)
|
99
110
|
end
|
111
|
+
else
|
112
|
+
@logger.debug('[execute] -- previous check execution still in progress -- ' + check.name)
|
100
113
|
end
|
101
114
|
else
|
102
115
|
@logger.warn('[execute] -- unkown check -- ' + check.name)
|
@@ -110,60 +123,50 @@ module Sensu
|
|
110
123
|
|
111
124
|
def setup_subscriptions
|
112
125
|
@logger.debug('[subscribe] -- setup subscriptions')
|
113
|
-
@
|
114
|
-
@settings.client.subscriptions.
|
115
|
-
@settings.client.subscriptions.each do |exchange|
|
126
|
+
@check_request_queue = @amq.queue(String.unique, :exclusive => true)
|
127
|
+
@settings.client.subscriptions.uniq.each do |exchange|
|
116
128
|
@logger.debug('[subscribe] -- queue binding to exchange -- ' + exchange)
|
117
|
-
@
|
129
|
+
@check_request_queue.bind(@amq.fanout(exchange))
|
118
130
|
end
|
119
|
-
@
|
120
|
-
check = Hashie::Mash.new(JSON.parse(
|
121
|
-
@logger.info('[subscribe] -- received check -- ' + check.name)
|
122
|
-
|
123
|
-
@logger.info('[subscribe] -- check requires matching -- ' + check.name)
|
124
|
-
matches = check.matching.all? do |key, value|
|
125
|
-
desired = case key
|
126
|
-
when /^!/
|
127
|
-
key = key.slice(1..-1)
|
128
|
-
false
|
129
|
-
else
|
130
|
-
true
|
131
|
-
end
|
132
|
-
matched = case key
|
133
|
-
when 'subscribes'
|
134
|
-
value.all? do |subscription|
|
135
|
-
@settings.client.subscriptions.include?(subscription)
|
136
|
-
end
|
137
|
-
else
|
138
|
-
@settings.client[key] == value
|
139
|
-
end
|
140
|
-
desired == matched
|
141
|
-
end
|
142
|
-
if matches
|
143
|
-
@logger.info('[subscribe] -- client matches -- ' + check.name)
|
144
|
-
execute_check(check)
|
145
|
-
else
|
146
|
-
@logger.info('[subscribe] -- client does not match -- ' + check.name)
|
147
|
-
end
|
148
|
-
else
|
149
|
-
execute_check(check)
|
150
|
-
end
|
131
|
+
@check_request_queue.subscribe do |check_request_json|
|
132
|
+
check = Hashie::Mash.new(JSON.parse(check_request_json))
|
133
|
+
@logger.info('[subscribe] -- received check request -- ' + check.name)
|
134
|
+
execute_check(check)
|
151
135
|
end
|
152
136
|
end
|
153
137
|
|
154
138
|
def setup_queue_monitor
|
155
139
|
@logger.debug('[monitor] -- setup queue monitor')
|
156
|
-
EM::PeriodicTimer.new(5) do
|
157
|
-
unless @
|
140
|
+
@timers << EM::PeriodicTimer.new(5) do
|
141
|
+
unless @check_request_queue.subscribed?
|
158
142
|
@logger.warn('[monitor] -- re-subscribing to subscriptions')
|
159
|
-
@
|
160
|
-
EM::Timer.new(1) do
|
143
|
+
@check_request_queue.delete
|
144
|
+
@timers << EM::Timer.new(1) do
|
161
145
|
setup_subscriptions
|
162
146
|
end
|
163
147
|
end
|
164
148
|
end
|
165
149
|
end
|
166
150
|
|
151
|
+
def setup_standalone(options={})
|
152
|
+
@logger.debug('[standalone] -- setup standalone')
|
153
|
+
standalone_count = 0
|
154
|
+
@settings.checks.each do |name, details|
|
155
|
+
if details.standalone
|
156
|
+
standalone_count += 1
|
157
|
+
check = Hashie::Mash.new(details.merge({:name => name}))
|
158
|
+
stagger = options[:test] ? 0 : 7
|
159
|
+
@timers << EM::Timer.new(stagger*standalone_count) do
|
160
|
+
interval = options[:test] ? 0.5 : details.interval
|
161
|
+
@timers << EM::PeriodicTimer.new(interval) do
|
162
|
+
check.issued = Time.now.to_i
|
163
|
+
execute_check(check)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
167
170
|
def setup_socket
|
168
171
|
@logger.debug('[socket] -- starting up socket')
|
169
172
|
EM::start_server('127.0.0.1', 3030, ClientSocket) do |socket|
|
@@ -173,10 +176,29 @@ module Sensu
|
|
173
176
|
end
|
174
177
|
end
|
175
178
|
|
179
|
+
def stop_reactor
|
180
|
+
@logger.info('[stop] -- completing checks in progress')
|
181
|
+
complete_in_progress = EM::tick_loop do
|
182
|
+
if @checks_in_progress.empty?
|
183
|
+
:stop
|
184
|
+
end
|
185
|
+
end
|
186
|
+
complete_in_progress.on_stop do
|
187
|
+
@logger.warn('[stop] -- stopping reactor')
|
188
|
+
EM::PeriodicTimer.new(0.25) do
|
189
|
+
EM::stop_event_loop
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
176
194
|
def stop(signal)
|
177
195
|
@logger.warn('[stop] -- stopping sensu client -- ' + signal)
|
178
|
-
|
179
|
-
|
196
|
+
@timers.each do |timer|
|
197
|
+
timer.cancel
|
198
|
+
end
|
199
|
+
@logger.warn('[stop] -- unsubscribing from subscriptions')
|
200
|
+
@check_request_queue.unsubscribe do
|
201
|
+
stop_reactor
|
180
202
|
end
|
181
203
|
end
|
182
204
|
end
|
@@ -201,7 +223,7 @@ module Sensu
|
|
201
223
|
@logger.warn('[socket] -- a check name, exit status, and output are required -- e.g. {"name": "x", "status": 0, "output": "y"}')
|
202
224
|
end
|
203
225
|
rescue JSON::ParserError => error
|
204
|
-
@logger.warn('[socket] -- check result must be valid JSON: ' + error
|
226
|
+
@logger.warn('[socket] -- check result must be valid JSON: ' + error)
|
205
227
|
end
|
206
228
|
close_connection
|
207
229
|
end
|
data/lib/sensu/config.rb
CHANGED
@@ -15,85 +15,45 @@ require 'cabin/outputs/em-stdlib-logger'
|
|
15
15
|
|
16
16
|
module Sensu
|
17
17
|
class Config
|
18
|
-
attr_accessor :
|
18
|
+
attr_accessor :logger, :settings
|
19
19
|
|
20
20
|
DEFAULT_OPTIONS = {
|
21
|
-
:log_file => '/tmp/sensu.log',
|
22
21
|
:config_file => '/etc/sensu/config.json',
|
23
|
-
:config_dir => '/etc/sensu/conf.d'
|
24
|
-
:validate => true,
|
25
|
-
:daemonize => false,
|
26
|
-
:pid_file => '/tmp/' + File.basename($0) + '.pid',
|
27
|
-
:service => File.basename($0)
|
22
|
+
:config_dir => '/etc/sensu/conf.d'
|
28
23
|
}
|
29
24
|
|
30
25
|
def initialize(options={})
|
31
26
|
@options = DEFAULT_OPTIONS.merge(options)
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
read_config
|
36
|
-
if @options[:validate]
|
37
|
-
validate_config
|
38
|
-
end
|
27
|
+
setup_logging
|
28
|
+
setup_settings
|
39
29
|
end
|
40
30
|
|
41
31
|
def invalid_config(message)
|
42
32
|
raise 'configuration invalid, ' + message
|
43
33
|
end
|
44
34
|
|
45
|
-
def
|
46
|
-
@
|
47
|
-
|
48
|
-
ruby_logger = case @options[:service]
|
49
|
-
when 'rake'
|
50
|
-
Logger.new(@options[:log_file])
|
51
|
-
else
|
35
|
+
def setup_logging
|
36
|
+
if @options[:log_file]
|
37
|
+
if File.writable?(@options[:log_file]) || !File.exist?(@options[:log_file]) && File.writable?(File.dirname(@options[:log_file]))
|
52
38
|
STDOUT.reopen(@options[:log_file], 'a')
|
53
39
|
STDERR.reopen(STDOUT)
|
54
40
|
STDOUT.sync = true
|
55
|
-
|
41
|
+
else
|
42
|
+
invalid_config('log file is not writable: ' + @options[:log_file])
|
56
43
|
end
|
57
|
-
else
|
58
|
-
invalid_config('log file is not writable: ' + @options[:log_file])
|
59
44
|
end
|
60
|
-
@logger
|
45
|
+
@logger = Cabin::Channel.new
|
46
|
+
log_output = File.basename($0) == 'rake' ? '/tmp/sensu_test.log' : STDOUT
|
47
|
+
@logger.subscribe(Cabin::Outputs::EmStdlibLogger.new(Logger.new(log_output)))
|
61
48
|
@logger.level = @options[:verbose] ? :debug : :info
|
62
49
|
if Signal.list.include?('USR1')
|
63
50
|
Signal.trap('USR1') do
|
64
51
|
@logger.level = @logger.level == :info ? :debug : :info
|
65
52
|
end
|
66
53
|
end
|
67
|
-
@logger
|
68
|
-
end
|
69
|
-
|
70
|
-
def read_config
|
71
|
-
if File.readable?(@options[:config_file])
|
72
|
-
begin
|
73
|
-
@settings = Hashie::Mash.new(JSON.parse(File.open(@options[:config_file], 'r').read))
|
74
|
-
rescue JSON::ParserError => error
|
75
|
-
invalid_config('configuration file (' + @options[:config_file] + ') must be valid JSON: ' + error.to_s)
|
76
|
-
end
|
77
|
-
else
|
78
|
-
invalid_config('configuration file does not exist or is not readable: ' + @options[:config_file])
|
79
|
-
end
|
80
|
-
if File.exists?(@options[:config_dir])
|
81
|
-
Dir[@options[:config_dir] + '/**/*.json'].each do |snippet_file|
|
82
|
-
begin
|
83
|
-
snippet_hash = JSON.parse(File.open(snippet_file, 'r').read)
|
84
|
-
rescue JSON::ParserError => error
|
85
|
-
invalid_config('configuration snippet file (' + snippet_file + ') must be valid JSON: ' + error.to_s)
|
86
|
-
end
|
87
|
-
merged_settings = @settings.to_hash.deep_merge(snippet_hash)
|
88
|
-
if @logger
|
89
|
-
@logger.warn('[settings] configuration snippet (' + snippet_file + ') applied changes: ' + @settings.deep_diff(merged_settings).to_json)
|
90
|
-
end
|
91
|
-
@settings = Hashie::Mash.new(merged_settings)
|
92
|
-
end
|
93
|
-
end
|
94
54
|
end
|
95
55
|
|
96
|
-
def
|
56
|
+
def validate_common_settings
|
97
57
|
@settings.checks.each do |name, details|
|
98
58
|
unless details.interval.is_a?(Integer) && details.interval > 0
|
99
59
|
invalid_config('missing interval for check ' + name)
|
@@ -101,8 +61,10 @@ module Sensu
|
|
101
61
|
unless details.command.is_a?(String)
|
102
62
|
invalid_config('missing command for check ' + name)
|
103
63
|
end
|
104
|
-
unless details.
|
105
|
-
|
64
|
+
unless details.standalone
|
65
|
+
unless details.subscribers.is_a?(Array) && details.subscribers.count > 0
|
66
|
+
invalid_config('missing subscribers for check ' + name)
|
67
|
+
end
|
106
68
|
end
|
107
69
|
if details.key?('handler')
|
108
70
|
unless details.handler.is_a?(String)
|
@@ -117,7 +79,7 @@ module Sensu
|
|
117
79
|
end
|
118
80
|
end
|
119
81
|
|
120
|
-
def
|
82
|
+
def validate_server_settings
|
121
83
|
unless @settings.handlers.include?('default')
|
122
84
|
invalid_config('missing default handler')
|
123
85
|
end
|
@@ -125,28 +87,32 @@ module Sensu
|
|
125
87
|
unless details.is_a?(Hash)
|
126
88
|
invalid_config('hander details must be a hash ' + name)
|
127
89
|
end
|
128
|
-
unless details.
|
90
|
+
unless details['type'].is_a?(String)
|
129
91
|
invalid_config('missing type for handler ' + name)
|
130
92
|
end
|
131
93
|
case details['type']
|
132
94
|
when 'pipe'
|
133
|
-
unless details.
|
95
|
+
unless details.command.is_a?(String)
|
134
96
|
invalid_config('missing command for pipe handler ' + name)
|
135
97
|
end
|
136
98
|
when 'amqp'
|
137
|
-
unless details.
|
99
|
+
unless details.exchange.is_a?(Hash)
|
138
100
|
invalid_config('missing exchange details for amqp handler ' + name)
|
139
101
|
end
|
140
|
-
unless details.exchange.
|
102
|
+
unless details.exchange.name.is_a?(String)
|
141
103
|
invalid_config('missing exchange name for amqp handler ' + name)
|
142
104
|
end
|
105
|
+
when 'set'
|
106
|
+
unless details.handlers.is_a?(Array) && details.handlers.count > 0
|
107
|
+
invalid_config('missing handler set for handler ' + name)
|
108
|
+
end
|
143
109
|
else
|
144
110
|
invalid_config('unknown type for handler ' + name)
|
145
111
|
end
|
146
112
|
end
|
147
113
|
end
|
148
114
|
|
149
|
-
def
|
115
|
+
def validate_client_settings
|
150
116
|
unless @settings.client.name.is_a?(String)
|
151
117
|
invalid_config('client must have a name')
|
152
118
|
end
|
@@ -166,54 +132,80 @@ module Sensu
|
|
166
132
|
end
|
167
133
|
end
|
168
134
|
|
169
|
-
def
|
170
|
-
|
171
|
-
@logger.debug('[config] -- validating configuration')
|
172
|
-
end
|
135
|
+
def validate_settings
|
136
|
+
@logger.debug('[validate] -- validating configuration')
|
173
137
|
has_keys(%w[rabbitmq checks])
|
174
|
-
|
175
|
-
case
|
138
|
+
validate_common_settings
|
139
|
+
case File.basename($0)
|
176
140
|
when 'rake'
|
177
141
|
has_keys(%w[redis api handlers client])
|
178
|
-
|
179
|
-
|
142
|
+
validate_server_settings
|
143
|
+
validate_client_settings
|
180
144
|
when 'sensu-server'
|
181
145
|
has_keys(%w[redis handlers])
|
182
|
-
|
146
|
+
validate_server_settings
|
183
147
|
when 'sensu-api'
|
184
148
|
has_keys(%w[redis api])
|
185
149
|
when 'sensu-client'
|
186
150
|
has_keys(%w[client])
|
187
|
-
|
151
|
+
validate_client_settings
|
152
|
+
end
|
153
|
+
@logger.info('[validate] -- configuration valid -- running')
|
154
|
+
end
|
155
|
+
|
156
|
+
def setup_settings
|
157
|
+
if File.readable?(@options[:config_file])
|
158
|
+
begin
|
159
|
+
config_hash = JSON.parse(File.open(@options[:config_file], 'r').read)
|
160
|
+
rescue JSON::ParserError => error
|
161
|
+
invalid_config('configuration file (' + @options[:config_file] + ') must be valid JSON: ' + error)
|
162
|
+
end
|
163
|
+
@settings = Hashie::Mash.new(config_hash)
|
164
|
+
else
|
165
|
+
invalid_config('configuration file does not exist or is not readable: ' + @options[:config_file])
|
188
166
|
end
|
189
|
-
if @
|
190
|
-
@
|
167
|
+
if File.exists?(@options[:config_dir])
|
168
|
+
Dir[@options[:config_dir] + '/**/*.json'].each do |snippet_file|
|
169
|
+
if File.readable?(snippet_file)
|
170
|
+
begin
|
171
|
+
snippet_hash = JSON.parse(File.open(snippet_file, 'r').read)
|
172
|
+
rescue JSON::ParserError => error
|
173
|
+
invalid_config('configuration snippet file (' + snippet_file + ') must be valid JSON: ' + error)
|
174
|
+
end
|
175
|
+
merged_settings = @settings.to_hash.deep_merge(snippet_hash)
|
176
|
+
@logger.warn('[settings] configuration snippet (' + snippet_file + ') applied changes: ' + @settings.deep_diff(merged_settings).to_json)
|
177
|
+
@settings = Hashie::Mash.new(merged_settings)
|
178
|
+
else
|
179
|
+
invalid_config('configuration snippet file is not readable: ' + snippet_file)
|
180
|
+
end
|
181
|
+
end
|
191
182
|
end
|
183
|
+
validate_settings
|
192
184
|
end
|
193
185
|
|
194
186
|
def self.read_arguments(arguments)
|
195
187
|
options = Hash.new
|
196
188
|
optparse = OptionParser.new do |opts|
|
197
|
-
opts.on('-h', '--help', 'Display this
|
189
|
+
opts.on('-h', '--help', 'Display this message') do
|
198
190
|
puts opts
|
199
191
|
exit
|
200
192
|
end
|
201
|
-
opts.on('-c', '--config FILE', 'Sensu JSON config FILE
|
193
|
+
opts.on('-c', '--config FILE', 'Sensu JSON config FILE. Default is /etc/sensu/config.json') do |file|
|
202
194
|
options[:config_file] = file
|
203
195
|
end
|
204
|
-
opts.on('-d', '--config_dir DIR', '
|
196
|
+
opts.on('-d', '--config_dir DIR', 'DIR for supplemental Sensu JSON config files. Default is /etc/sensu/conf.d/') do |dir|
|
205
197
|
options[:config_dir] = dir
|
206
198
|
end
|
207
|
-
opts.on('-l', '--log FILE', '
|
199
|
+
opts.on('-l', '--log FILE', 'Log to a given FILE. Default is to log to stdout') do |file|
|
208
200
|
options[:log_file] = file
|
209
201
|
end
|
210
|
-
opts.on('-v', '--verbose', 'Enable verbose logging
|
202
|
+
opts.on('-v', '--verbose', 'Enable verbose logging') do
|
211
203
|
options[:verbose] = true
|
212
204
|
end
|
213
|
-
opts.on('-b', '--background', 'Fork into
|
205
|
+
opts.on('-b', '--background', 'Fork into the background') do
|
214
206
|
options[:daemonize] = true
|
215
207
|
end
|
216
|
-
opts.on('-p', '--pid_file FILE', '
|
208
|
+
opts.on('-p', '--pid_file FILE', 'Write the PID to a given FILE') do |file|
|
217
209
|
options[:pid_file] = file
|
218
210
|
end
|
219
211
|
end
|
data/lib/sensu/patches/ruby.rb
CHANGED
@@ -33,7 +33,13 @@ class Hash
|
|
33
33
|
|
34
34
|
def deep_merge(hash)
|
35
35
|
merger = proc do |key, value1, value2|
|
36
|
-
value1.is_a?(Hash) && value2.is_a?(Hash)
|
36
|
+
if value1.is_a?(Hash) && value2.is_a?(Hash)
|
37
|
+
value1.merge(value2, &merger)
|
38
|
+
elsif value1.is_a?(Array) && value2.is_a?(Array)
|
39
|
+
value1.concat(value2).uniq
|
40
|
+
else
|
41
|
+
value2
|
42
|
+
end
|
37
43
|
end
|
38
44
|
self.merge(hash, &merger)
|
39
45
|
end
|
data/lib/sensu/server.rb
CHANGED
@@ -35,9 +35,10 @@ module Sensu
|
|
35
35
|
|
36
36
|
def initialize(options={})
|
37
37
|
config = Sensu::Config.new(options)
|
38
|
+
@logger = config.logger
|
38
39
|
@settings = config.settings
|
39
|
-
@logger = config.logger || config.open_log
|
40
40
|
@timers = Array.new
|
41
|
+
@handlers_in_progress = 0
|
41
42
|
end
|
42
43
|
|
43
44
|
def setup_redis
|
@@ -72,14 +73,21 @@ module Sensu
|
|
72
73
|
else
|
73
74
|
['default']
|
74
75
|
end
|
76
|
+
handlers.map! do |handler|
|
77
|
+
@settings.handlers[handler]['type'] == 'set' ? @settings.handlers[handler].handlers : handler
|
78
|
+
end
|
79
|
+
handlers.flatten!
|
80
|
+
handlers.uniq!
|
75
81
|
report = proc do |output|
|
76
82
|
output.split(/\n+/).each do |line|
|
77
83
|
@logger.info('[handler] -- ' + line)
|
78
84
|
end
|
85
|
+
@handlers_in_progress -= 1
|
79
86
|
end
|
80
87
|
handlers.each do |handler|
|
81
88
|
if @settings.handlers.key?(handler)
|
82
89
|
@logger.debug('[event] -- handling event -- ' + [handler, event.client.name, event.check.name].join(' -- '))
|
90
|
+
@handlers_in_progress += 1
|
83
91
|
details = @settings.handlers[handler]
|
84
92
|
case details['type']
|
85
93
|
when 'pipe'
|
@@ -93,7 +101,7 @@ module Sensu
|
|
93
101
|
output = io.read
|
94
102
|
end
|
95
103
|
rescue Errno::EPIPE => error
|
96
|
-
output = handler + ' -- broken pipe: ' + error
|
104
|
+
output = handler + ' -- broken pipe: ' + error
|
97
105
|
end
|
98
106
|
end
|
99
107
|
output
|
@@ -106,6 +114,10 @@ module Sensu
|
|
106
114
|
@logger.debug('[event] -- publishing event to rabbitmq exchange -- ' + [exchange, event.client.name, event.check.name].join(' -- '))
|
107
115
|
payload = details.send_only_check_output ? event.check.output : event.to_json
|
108
116
|
@amq.method(exchange_type).call(exchange, exchange_options).publish(payload)
|
117
|
+
@handlers_in_progress -= 1
|
118
|
+
when 'set'
|
119
|
+
@logger.warn('[event] -- handler sets cannot be nested -- ' + handler)
|
120
|
+
@handlers_in_progress -= 1
|
109
121
|
end
|
110
122
|
else
|
111
123
|
@logger.warn('[event] -- unknown handler -- ' + handler)
|
@@ -214,20 +226,13 @@ module Sensu
|
|
214
226
|
stagger = options[:test] ? 0 : 7
|
215
227
|
@settings.checks.each_with_index do |(name, details), index|
|
216
228
|
check_request = Hashie::Mash.new({:name => name})
|
217
|
-
unless details.publish == false
|
229
|
+
unless details.publish == false || details.standalone
|
218
230
|
@timers << EM::Timer.new(stagger*index) do
|
219
|
-
details.subscribers.each do |
|
220
|
-
if target.is_a?(Hash)
|
221
|
-
@logger.debug('[publisher] -- check requires matching -- ' + target.to_hash.to_s + ' -- ' + name)
|
222
|
-
check_request.matching = target
|
223
|
-
exchange = 'uchiwa'
|
224
|
-
else
|
225
|
-
exchange = target
|
226
|
-
end
|
231
|
+
details.subscribers.each do |exchange|
|
227
232
|
interval = options[:test] ? 0.5 : details.interval
|
228
233
|
@timers << EM::PeriodicTimer.new(interval) do
|
229
|
-
check_request.issued = Time.now.to_i
|
230
234
|
@logger.info('[publisher] -- publishing check request -- ' + name + ' -- ' + exchange)
|
235
|
+
check_request.issued = Time.now.to_i
|
231
236
|
@amq.fanout(exchange).publish(check_request.to_json)
|
232
237
|
end
|
233
238
|
end
|
@@ -333,9 +338,19 @@ module Sensu
|
|
333
338
|
end
|
334
339
|
|
335
340
|
def stop_reactor
|
336
|
-
|
337
|
-
|
338
|
-
EM::
|
341
|
+
EM::Timer.new(1) do
|
342
|
+
@logger.info('[stop] -- completing handlers in progress')
|
343
|
+
complete_in_progress = EM::tick_loop do
|
344
|
+
if @handlers_in_progress == 0
|
345
|
+
:stop
|
346
|
+
end
|
347
|
+
end
|
348
|
+
complete_in_progress.on_stop do
|
349
|
+
@logger.warn('[stop] -- stopping reactor')
|
350
|
+
EM::PeriodicTimer.new(0.25) do
|
351
|
+
EM::stop_event_loop
|
352
|
+
end
|
353
|
+
end
|
339
354
|
end
|
340
355
|
end
|
341
356
|
|
@@ -344,10 +359,10 @@ module Sensu
|
|
344
359
|
@timers.each do |timer|
|
345
360
|
timer.cancel
|
346
361
|
end
|
362
|
+
@logger.warn('[stop] -- unsubscribing from keepalives')
|
347
363
|
@keepalive_queue.unsubscribe do
|
348
|
-
@logger.warn('[stop] --
|
364
|
+
@logger.warn('[stop] -- unsubscribing from results')
|
349
365
|
@result_queue.unsubscribe do
|
350
|
-
@logger.warn('[stop] -- unsubscribed from rabbitmq queue -- results')
|
351
366
|
if @is_master
|
352
367
|
@redis.del('lock:master').callback do
|
353
368
|
@logger.warn('[stop] -- resigned as master')
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sensu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 31098113
|
5
|
+
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
10
|
-
|
9
|
+
- 2
|
10
|
+
- beta
|
11
|
+
version: 0.9.2.beta
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- Sean Porter
|
@@ -16,7 +17,7 @@ autorequire:
|
|
16
17
|
bindir: bin
|
17
18
|
cert_chain: []
|
18
19
|
|
19
|
-
date:
|
20
|
+
date: 2012-01-04 00:00:00 -08:00
|
20
21
|
default_executable:
|
21
22
|
dependencies:
|
22
23
|
- !ruby/object:Gem::Dependency
|
@@ -258,12 +259,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
258
259
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
259
260
|
none: false
|
260
261
|
requirements:
|
261
|
-
- - "
|
262
|
+
- - ">"
|
262
263
|
- !ruby/object:Gem::Version
|
263
|
-
hash:
|
264
|
+
hash: 25
|
264
265
|
segments:
|
265
|
-
-
|
266
|
-
|
266
|
+
- 1
|
267
|
+
- 3
|
268
|
+
- 1
|
269
|
+
version: 1.3.1
|
267
270
|
requirements: []
|
268
271
|
|
269
272
|
rubyforge_project:
|