sensu 0.12.6 → 0.13.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +41 -0
- data/lib/sensu/api.rb +145 -205
- data/lib/sensu/cli.rb +2 -1
- data/lib/sensu/client.rb +51 -119
- data/lib/sensu/constants.rb +1 -7
- data/lib/sensu/daemon.rb +221 -0
- data/lib/sensu/server.rb +105 -202
- data/lib/sensu/socket.rb +4 -4
- data/lib/sensu/utilities.rb +6 -29
- data/sensu.gemspec +10 -6
- metadata +223 -228
- data/lib/sensu/base.rb +0 -75
- data/lib/sensu/extensions.rb +0 -162
- data/lib/sensu/extensions/handlers/debug.rb +0 -17
- data/lib/sensu/extensions/mutators/only_check_output.rb +0 -17
- data/lib/sensu/io.rb +0 -98
- data/lib/sensu/logstream.rb +0 -93
- data/lib/sensu/process.rb +0 -48
- data/lib/sensu/rabbitmq.rb +0 -106
- data/lib/sensu/settings.rb +0 -483
data/lib/sensu/cli.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'optparse'
|
2
|
+
require 'sensu/logger/constants'
|
2
3
|
|
3
4
|
module Sensu
|
4
5
|
class CLI
|
@@ -27,7 +28,7 @@ module Sensu
|
|
27
28
|
end
|
28
29
|
opts.on('-L', '--log_level LEVEL', 'Log severity LEVEL') do |level|
|
29
30
|
log_level = level.to_s.downcase.to_sym
|
30
|
-
unless
|
31
|
+
unless Logger::LEVELS.include?(log_level)
|
31
32
|
puts 'Unknown log level: ' + level.to_s
|
32
33
|
exit 1
|
33
34
|
end
|
data/lib/sensu/client.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'sensu/daemon'
|
2
|
+
require 'sensu/socket'
|
3
3
|
|
4
4
|
module Sensu
|
5
5
|
class Client
|
6
|
-
include
|
6
|
+
include Daemon
|
7
7
|
|
8
8
|
attr_accessor :safe_mode
|
9
9
|
|
@@ -11,63 +11,40 @@ module Sensu
|
|
11
11
|
client = self.new(options)
|
12
12
|
EM::run do
|
13
13
|
client.start
|
14
|
-
client.
|
14
|
+
client.setup_signal_traps
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def initialize(options={})
|
19
|
-
|
20
|
-
@logger = base.logger
|
21
|
-
@settings = base.settings
|
22
|
-
@extensions = base.extensions
|
23
|
-
base.setup_process
|
24
|
-
@extensions.load_settings(@settings.to_hash)
|
25
|
-
@timers = Array.new
|
26
|
-
@checks_in_progress = Array.new
|
19
|
+
super
|
27
20
|
@safe_mode = @settings[:client][:safe_mode] || false
|
28
|
-
|
29
|
-
|
30
|
-
def setup_rabbitmq
|
31
|
-
@logger.debug('connecting to rabbitmq', {
|
32
|
-
:settings => @settings[:rabbitmq]
|
33
|
-
})
|
34
|
-
@rabbitmq = RabbitMQ.connect(@settings[:rabbitmq])
|
35
|
-
@rabbitmq.on_error do |error|
|
36
|
-
@logger.fatal('rabbitmq connection error', {
|
37
|
-
:error => error.to_s
|
38
|
-
})
|
39
|
-
stop
|
40
|
-
end
|
41
|
-
@rabbitmq.before_reconnect do
|
42
|
-
@logger.warn('reconnecting to rabbitmq')
|
43
|
-
end
|
44
|
-
@rabbitmq.after_reconnect do
|
45
|
-
@logger.info('reconnected to rabbitmq')
|
46
|
-
end
|
47
|
-
@amq = @rabbitmq.channel
|
21
|
+
@checks_in_progress = Array.new
|
48
22
|
end
|
49
23
|
|
50
24
|
def publish_keepalive
|
51
|
-
keepalive = @settings[:client].merge(
|
25
|
+
keepalive = @settings[:client].merge({
|
26
|
+
:version => VERSION,
|
27
|
+
:timestamp => Time.now.to_i
|
28
|
+
})
|
52
29
|
payload = redact_sensitive(keepalive, @settings[:client][:redact])
|
53
30
|
@logger.debug('publishing keepalive', {
|
54
31
|
:payload => payload
|
55
32
|
})
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
33
|
+
@transport.publish(:direct, 'keepalives', MultiJson.dump(payload)) do |info|
|
34
|
+
if info[:error]
|
35
|
+
@logger.error('failed to publish keepalive', {
|
36
|
+
:payload => payload,
|
37
|
+
:error => info[:error].to_s
|
38
|
+
})
|
39
|
+
end
|
63
40
|
end
|
64
41
|
end
|
65
42
|
|
66
43
|
def setup_keepalives
|
67
44
|
@logger.debug('scheduling keepalives')
|
68
45
|
publish_keepalive
|
69
|
-
@timers << EM::PeriodicTimer.new(20) do
|
70
|
-
|
46
|
+
@timers[:run] << EM::PeriodicTimer.new(20) do
|
47
|
+
unless @state == :paused
|
71
48
|
publish_keepalive
|
72
49
|
end
|
73
50
|
end
|
@@ -81,19 +58,19 @@ module Sensu
|
|
81
58
|
@logger.info('publishing check result', {
|
82
59
|
:payload => payload
|
83
60
|
})
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
61
|
+
@transport.publish(:direct, 'results', MultiJson.dump(payload)) do |info|
|
62
|
+
if info[:error]
|
63
|
+
@logger.error('failed to publish check result', {
|
64
|
+
:payload => payload,
|
65
|
+
:error => info[:error].to_s
|
66
|
+
})
|
67
|
+
end
|
91
68
|
end
|
92
69
|
end
|
93
70
|
|
94
71
|
def substitute_command_tokens(check)
|
95
72
|
unmatched_tokens = Array.new
|
96
|
-
substituted = check[:command].gsub(/:::(
|
73
|
+
substituted = check[:command].gsub(/:::([^:]*?):::/) do
|
97
74
|
token, default = $1.to_s.split('|', -1)
|
98
75
|
matched = token.split('.').inject(@settings[:client]) do |client, attribute|
|
99
76
|
if client[attribute].nil?
|
@@ -117,27 +94,16 @@ module Sensu
|
|
117
94
|
unless @checks_in_progress.include?(check[:name])
|
118
95
|
@checks_in_progress << check[:name]
|
119
96
|
command, unmatched_tokens = substitute_command_tokens(check)
|
120
|
-
check[:executed] = Time.now.to_i
|
121
97
|
if unmatched_tokens.empty?
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
})
|
126
|
-
started = Time.now.to_f
|
127
|
-
begin
|
128
|
-
check[:output], check[:status] = IO.popen(command, 'r', check[:timeout])
|
129
|
-
rescue => error
|
130
|
-
check[:output] = 'Unexpected error: ' + error.to_s
|
131
|
-
check[:status] = 2
|
132
|
-
end
|
98
|
+
check[:executed] = Time.now.to_i
|
99
|
+
started = Time.now.to_f
|
100
|
+
Spawn.process(command, :timeout => check[:timeout]) do |output, status|
|
133
101
|
check[:duration] = ('%.3f' % (Time.now.to_f - started)).to_f
|
134
|
-
check
|
135
|
-
|
136
|
-
publish = Proc.new do |check|
|
102
|
+
check[:output] = output
|
103
|
+
check[:status] = status
|
137
104
|
publish_result(check)
|
138
105
|
@checks_in_progress.delete(check[:name])
|
139
106
|
end
|
140
|
-
EM::defer(execute, publish)
|
141
107
|
else
|
142
108
|
check[:output] = 'Unmatched command tokens: ' + unmatched_tokens.join(', ')
|
143
109
|
check[:status] = 3
|
@@ -195,16 +161,13 @@ module Sensu
|
|
195
161
|
|
196
162
|
def setup_subscriptions
|
197
163
|
@logger.debug('subscribing to client subscriptions')
|
198
|
-
@
|
199
|
-
@
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
end
|
206
|
-
queue.subscribe do |payload|
|
207
|
-
check = Oj.load(payload)
|
164
|
+
@settings[:client][:subscriptions].each do |subscription|
|
165
|
+
@logger.debug('subscribing to a subscription', {
|
166
|
+
:subscription => subscription
|
167
|
+
})
|
168
|
+
funnel = [@settings[:client][:name], VERSION, Time.now.to_i].join('-')
|
169
|
+
@transport.subscribe(:fanout, subscription, funnel) do |message_info, message|
|
170
|
+
check = MultiJson.load(message)
|
208
171
|
@logger.info('received check request', {
|
209
172
|
:check => check
|
210
173
|
})
|
@@ -219,12 +182,12 @@ module Sensu
|
|
219
182
|
checks.each do |check|
|
220
183
|
check_count += 1
|
221
184
|
scheduling_delay = stagger * check_count % 30
|
222
|
-
@timers << EM::Timer.new(scheduling_delay) do
|
185
|
+
@timers[:run] << EM::Timer.new(scheduling_delay) do
|
223
186
|
interval = testing? ? 0.5 : check[:interval]
|
224
|
-
@timers << EM::PeriodicTimer.new(interval) do
|
225
|
-
|
187
|
+
@timers[:run] << EM::PeriodicTimer.new(interval) do
|
188
|
+
unless @state == :paused
|
226
189
|
check[:issued] = Time.now.to_i
|
227
|
-
process_check(check)
|
190
|
+
process_check(check.dup)
|
228
191
|
end
|
229
192
|
end
|
230
193
|
end
|
@@ -252,27 +215,16 @@ module Sensu
|
|
252
215
|
EM::start_server(options[:bind], options[:port], Socket) do |socket|
|
253
216
|
socket.logger = @logger
|
254
217
|
socket.settings = @settings
|
255
|
-
socket.
|
218
|
+
socket.transport = @transport
|
256
219
|
end
|
257
220
|
EM::open_datagram_socket(options[:bind], options[:port], Socket) do |socket|
|
258
221
|
socket.logger = @logger
|
259
222
|
socket.settings = @settings
|
260
|
-
socket.
|
223
|
+
socket.transport = @transport
|
261
224
|
socket.reply = false
|
262
225
|
end
|
263
226
|
end
|
264
227
|
|
265
|
-
def unsubscribe
|
266
|
-
@logger.warn('unsubscribing from client subscriptions')
|
267
|
-
if @rabbitmq.connected?
|
268
|
-
@check_request_queue.unsubscribe
|
269
|
-
else
|
270
|
-
@check_request_queue.before_recovery do
|
271
|
-
@check_request_queue.unsubscribe
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
228
|
def complete_checks_in_progress(&block)
|
277
229
|
@logger.info('completing checks in progress', {
|
278
230
|
:checks_in_progress => @checks_in_progress
|
@@ -286,43 +238,23 @@ module Sensu
|
|
286
238
|
end
|
287
239
|
|
288
240
|
def start
|
289
|
-
|
241
|
+
setup_transport
|
290
242
|
setup_keepalives
|
291
243
|
setup_subscriptions
|
292
244
|
setup_standalone
|
293
245
|
setup_sockets
|
246
|
+
super
|
294
247
|
end
|
295
248
|
|
296
249
|
def stop
|
297
250
|
@logger.warn('stopping')
|
298
|
-
@timers.each do |timer|
|
251
|
+
@timers[:run].each do |timer|
|
299
252
|
timer.cancel
|
300
253
|
end
|
301
|
-
unsubscribe
|
254
|
+
@transport.unsubscribe
|
302
255
|
complete_checks_in_progress do
|
303
|
-
@
|
304
|
-
|
305
|
-
@logger.warn('stopping reactor')
|
306
|
-
EM::stop_event_loop
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
def trap_signals
|
312
|
-
@signals = Array.new
|
313
|
-
STOP_SIGNALS.each do |signal|
|
314
|
-
Signal.trap(signal) do
|
315
|
-
@signals << signal
|
316
|
-
end
|
317
|
-
end
|
318
|
-
EM::PeriodicTimer.new(1) do
|
319
|
-
signal = @signals.shift
|
320
|
-
if STOP_SIGNALS.include?(signal)
|
321
|
-
@logger.warn('received signal', {
|
322
|
-
:signal => signal
|
323
|
-
})
|
324
|
-
stop
|
325
|
-
end
|
256
|
+
@transport.close
|
257
|
+
super
|
326
258
|
end
|
327
259
|
end
|
328
260
|
end
|
data/lib/sensu/constants.rb
CHANGED
@@ -1,12 +1,6 @@
|
|
1
1
|
module Sensu
|
2
2
|
unless defined?(Sensu::VERSION)
|
3
|
-
VERSION = '0.
|
4
|
-
|
5
|
-
LOG_LEVELS = [:debug, :info, :warn, :error, :fatal]
|
6
|
-
|
7
|
-
SETTINGS_CATEGORIES = [:checks, :filters, :mutators, :handlers]
|
8
|
-
|
9
|
-
EXTENSION_CATEGORIES = [:generics, :checks, :mutators, :handlers]
|
3
|
+
VERSION = '0.13.0.alpha'
|
10
4
|
|
11
5
|
SEVERITIES = %w[ok warning critical unknown]
|
12
6
|
|
data/lib/sensu/daemon.rb
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
gem 'multi_json', '1.10.1'
|
4
|
+
|
5
|
+
gem 'sensu-em', '2.0.0'
|
6
|
+
gem 'sensu-logger', '0.0.1'
|
7
|
+
gem 'sensu-settings', '0.0.4'
|
8
|
+
gem 'sensu-extension', '0.0.3'
|
9
|
+
gem 'sensu-extensions', '0.0.4'
|
10
|
+
gem 'sensu-transport', '0.0.2'
|
11
|
+
gem 'sensu-spawn', '0.0.3'
|
12
|
+
|
13
|
+
require 'time'
|
14
|
+
require 'uri'
|
15
|
+
|
16
|
+
require 'sensu/logger'
|
17
|
+
require 'sensu/settings'
|
18
|
+
require 'sensu/extensions'
|
19
|
+
require 'sensu/transport'
|
20
|
+
require 'sensu/spawn'
|
21
|
+
|
22
|
+
require 'sensu/constants'
|
23
|
+
require 'sensu/utilities'
|
24
|
+
require 'sensu/cli'
|
25
|
+
require 'sensu/redis'
|
26
|
+
|
27
|
+
MultiJson.load_options = {:symbolize_keys => true}
|
28
|
+
|
29
|
+
module Sensu
|
30
|
+
module Daemon
|
31
|
+
include Utilities
|
32
|
+
|
33
|
+
attr_reader :state
|
34
|
+
|
35
|
+
def initialize(options={})
|
36
|
+
@state = :initializing
|
37
|
+
@timers = {
|
38
|
+
:run => Array.new
|
39
|
+
}
|
40
|
+
setup_logger(options)
|
41
|
+
load_settings(options)
|
42
|
+
load_extensions(options)
|
43
|
+
setup_process(options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def setup_logger(options={})
|
47
|
+
@logger = Logger.get(options)
|
48
|
+
@logger.setup_signal_traps
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_concerns(concerns=[], level=:warn)
|
52
|
+
concerns.each do |concern|
|
53
|
+
message = concern.delete(:message)
|
54
|
+
@logger.send(level, message, redact_sensitive(concern))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_settings(options={})
|
59
|
+
@settings = Settings.load(options)
|
60
|
+
log_concerns(@settings.warnings)
|
61
|
+
failures = @settings.validate
|
62
|
+
unless failures.empty?
|
63
|
+
@logger.fatal('invalid settings')
|
64
|
+
log_concerns(failures, :fatal)
|
65
|
+
@logger.fatal('SENSU NOT RUNNING!')
|
66
|
+
exit 2
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_extensions(options={})
|
71
|
+
@extensions = Extensions.load(options)
|
72
|
+
log_concerns(@extensions.warnings)
|
73
|
+
extension_settings = @settings.to_hash.dup
|
74
|
+
@extensions.all.each do |extension|
|
75
|
+
extension.logger = @logger
|
76
|
+
extension.settings = extension_settings
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def setup_process(options)
|
81
|
+
if options[:daemonize]
|
82
|
+
daemonize
|
83
|
+
end
|
84
|
+
if options[:pid_file]
|
85
|
+
write_pid(options[:pid_file])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def start
|
90
|
+
@state = :running
|
91
|
+
end
|
92
|
+
|
93
|
+
def pause
|
94
|
+
@state = :paused
|
95
|
+
end
|
96
|
+
|
97
|
+
def resume
|
98
|
+
@state = :running
|
99
|
+
end
|
100
|
+
|
101
|
+
def stop
|
102
|
+
@state = :stopped
|
103
|
+
@logger.warn('stopping reactor')
|
104
|
+
EM::stop_event_loop
|
105
|
+
end
|
106
|
+
|
107
|
+
def setup_signal_traps
|
108
|
+
@signals = Array.new
|
109
|
+
STOP_SIGNALS.each do |signal|
|
110
|
+
Signal.trap(signal) do
|
111
|
+
@signals << signal
|
112
|
+
end
|
113
|
+
end
|
114
|
+
EM::PeriodicTimer.new(1) do
|
115
|
+
signal = @signals.shift
|
116
|
+
if STOP_SIGNALS.include?(signal)
|
117
|
+
@logger.warn('received signal', {
|
118
|
+
:signal => signal
|
119
|
+
})
|
120
|
+
stop
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def setup_transport
|
126
|
+
if @settings[:transport].is_a?(Hash)
|
127
|
+
transport_name = @settings[:transport][:name]
|
128
|
+
end
|
129
|
+
transport_name ||= 'rabbitmq'
|
130
|
+
transport_settings = @settings[transport_name.to_sym]
|
131
|
+
@logger.debug('connecting to transport', {
|
132
|
+
:name => transport_name,
|
133
|
+
:settings => transport_settings
|
134
|
+
})
|
135
|
+
@transport = Transport.connect(transport_name, transport_settings)
|
136
|
+
@transport.logger = @logger
|
137
|
+
@transport.on_error do |error|
|
138
|
+
@logger.fatal('transport connection error', {
|
139
|
+
:error => error.to_s
|
140
|
+
})
|
141
|
+
stop
|
142
|
+
end
|
143
|
+
@transport.before_reconnect do
|
144
|
+
unless testing?
|
145
|
+
@logger.warn('reconnecting to transport')
|
146
|
+
pause
|
147
|
+
end
|
148
|
+
end
|
149
|
+
@transport.after_reconnect do
|
150
|
+
@logger.info('reconnected to transport')
|
151
|
+
resume
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def setup_redis
|
156
|
+
@logger.debug('connecting to redis', {
|
157
|
+
:settings => @settings[:redis]
|
158
|
+
})
|
159
|
+
@redis = Redis.connect(@settings[:redis])
|
160
|
+
@redis.on_error do |error|
|
161
|
+
@logger.fatal('redis connection error', {
|
162
|
+
:error => error.to_s
|
163
|
+
})
|
164
|
+
stop
|
165
|
+
end
|
166
|
+
@redis.before_reconnect do
|
167
|
+
unless testing?
|
168
|
+
@logger.warn('reconnecting to redis')
|
169
|
+
pause
|
170
|
+
end
|
171
|
+
end
|
172
|
+
@redis.after_reconnect do
|
173
|
+
@logger.info('reconnected to redis')
|
174
|
+
resume
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def write_pid(file)
|
181
|
+
begin
|
182
|
+
File.open(file, 'w') do |pid_file|
|
183
|
+
pid_file.puts(Process.pid)
|
184
|
+
end
|
185
|
+
rescue
|
186
|
+
@logger.fatal('could not write to pid file', {
|
187
|
+
:pid_file => file
|
188
|
+
})
|
189
|
+
@logger.fatal('SENSU NOT RUNNING!')
|
190
|
+
exit 2
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def daemonize
|
195
|
+
Kernel.srand
|
196
|
+
if Kernel.fork
|
197
|
+
exit
|
198
|
+
end
|
199
|
+
unless Process.setsid
|
200
|
+
@logger.fatal('cannot detach from controlling terminal')
|
201
|
+
@logger.fatal('SENSU NOT RUNNING!')
|
202
|
+
exit 2
|
203
|
+
end
|
204
|
+
Signal.trap('SIGHUP', 'IGNORE')
|
205
|
+
if Kernel.fork
|
206
|
+
exit
|
207
|
+
end
|
208
|
+
Dir.chdir('/')
|
209
|
+
ObjectSpace.each_object(IO) do |io|
|
210
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
211
|
+
begin
|
212
|
+
unless io.closed?
|
213
|
+
io.close
|
214
|
+
end
|
215
|
+
rescue
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|