sensu 0.9.6.beta.3 → 0.9.6.beta.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sensu/api.rb +1 -1
- data/lib/sensu/base.rb +15 -4
- data/lib/sensu/cli.rb +2 -2
- data/lib/sensu/constants.rb +9 -5
- data/lib/sensu/logger.rb +10 -10
- data/lib/sensu/patches/redis.rb +17 -4
- data/lib/sensu/process.rb +7 -12
- data/lib/sensu/server.rb +114 -72
- data/lib/sensu/settings.rb +42 -0
- metadata +4 -4
data/lib/sensu/api.rb
CHANGED
data/lib/sensu/base.rb
CHANGED
@@ -4,6 +4,8 @@ gem 'eventmachine', '~> 1.0.0.beta.4'
|
|
4
4
|
|
5
5
|
require 'optparse'
|
6
6
|
require 'json'
|
7
|
+
require 'time'
|
8
|
+
require 'uri'
|
7
9
|
require 'cabin'
|
8
10
|
require 'amqp'
|
9
11
|
|
@@ -21,7 +23,7 @@ module Sensu
|
|
21
23
|
attr_reader :options, :logger, :settings
|
22
24
|
|
23
25
|
def initialize(options={})
|
24
|
-
@options = DEFAULT_OPTIONS.merge(options)
|
26
|
+
@options = Sensu::DEFAULT_OPTIONS.merge(options)
|
25
27
|
@logger = Cabin::Channel.get
|
26
28
|
setup_logging
|
27
29
|
setup_settings
|
@@ -29,7 +31,9 @@ module Sensu
|
|
29
31
|
end
|
30
32
|
|
31
33
|
def setup_logging
|
32
|
-
Sensu::Logger.new(@options)
|
34
|
+
logger = Sensu::Logger.new(@options)
|
35
|
+
logger.reopen
|
36
|
+
logger.setup_traps
|
33
37
|
end
|
34
38
|
|
35
39
|
def setup_settings
|
@@ -42,7 +46,7 @@ module Sensu
|
|
42
46
|
begin
|
43
47
|
@settings.validate
|
44
48
|
rescue => error
|
45
|
-
@logger.fatal('
|
49
|
+
@logger.fatal('config invalid', {
|
46
50
|
:error => error.to_s
|
47
51
|
})
|
48
52
|
@logger.fatal('SENSU NOT RUNNING!')
|
@@ -52,7 +56,14 @@ module Sensu
|
|
52
56
|
end
|
53
57
|
|
54
58
|
def setup_process
|
55
|
-
Sensu::Process.new
|
59
|
+
process = Sensu::Process.new
|
60
|
+
if @options[:daemonize]
|
61
|
+
process.daemonize
|
62
|
+
end
|
63
|
+
if @options[:pid_file]
|
64
|
+
process.write_pid(@options[:pid_file])
|
65
|
+
end
|
66
|
+
process.setup_eventmachine
|
56
67
|
end
|
57
68
|
end
|
58
69
|
end
|
data/lib/sensu/cli.rb
CHANGED
@@ -8,7 +8,7 @@ module Sensu
|
|
8
8
|
exit
|
9
9
|
end
|
10
10
|
opts.on('-V', '--version', 'Display version') do
|
11
|
-
puts VERSION
|
11
|
+
puts Sensu::VERSION
|
12
12
|
exit
|
13
13
|
end
|
14
14
|
opts.on('-c', '--config FILE', 'Sensu JSON config FILE. Default is /etc/sensu/config.json') do |file|
|
@@ -31,7 +31,7 @@ module Sensu
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
optparse.parse!(arguments)
|
34
|
-
DEFAULT_OPTIONS.merge(options)
|
34
|
+
Sensu::DEFAULT_OPTIONS.merge(options)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/sensu/constants.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
module Sensu
|
2
|
-
VERSION
|
2
|
+
unless defined?(Sensu::VERSION)
|
3
|
+
VERSION = '0.9.6.beta.4'
|
4
|
+
end
|
3
5
|
|
4
|
-
DEFAULT_OPTIONS
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
unless defined?(Sensu::DEFAULT_OPTIONS)
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
:config_file => '/etc/sensu/config.json',
|
9
|
+
:config_dir => '/etc/sensu/conf.d'
|
10
|
+
}
|
11
|
+
end
|
8
12
|
end
|
data/lib/sensu/logger.rb
CHANGED
@@ -4,26 +4,26 @@ module Sensu
|
|
4
4
|
@logger = Cabin::Channel.get
|
5
5
|
@logger.subscribe(STDOUT)
|
6
6
|
@logger.level = options[:verbose] ? :debug : options[:log_level] || :info
|
7
|
-
|
8
|
-
setup_traps(options)
|
7
|
+
@log_file = options[:log_file]
|
9
8
|
end
|
10
9
|
|
11
|
-
def reopen(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
def reopen(file=nil)
|
11
|
+
file ||= @log_file
|
12
|
+
unless file.nil?
|
13
|
+
@log_file = file
|
14
|
+
if File.writable?(file) || !File.exist?(file) && File.writable?(File.dirname(file))
|
15
|
+
STDOUT.reopen(file, 'a')
|
16
16
|
STDERR.reopen(STDOUT)
|
17
17
|
STDOUT.sync = true
|
18
18
|
else
|
19
19
|
@logger.error('log file is not writable', {
|
20
|
-
:log_file =>
|
20
|
+
:log_file => file
|
21
21
|
})
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def setup_traps
|
26
|
+
def setup_traps
|
27
27
|
if Signal.list.include?('USR1')
|
28
28
|
Signal.trap('USR1') do
|
29
29
|
@logger.level = @logger.level == :info ? :debug : :info
|
@@ -31,7 +31,7 @@ module Sensu
|
|
31
31
|
end
|
32
32
|
if Signal.list.include?('USR2')
|
33
33
|
Signal.trap('USR2') do
|
34
|
-
reopen(
|
34
|
+
reopen(@log_file)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/sensu/patches/redis.rb
CHANGED
@@ -46,13 +46,26 @@ module Redis
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def self.connect(options
|
50
|
-
|
51
|
-
|
49
|
+
def self.connect(options)
|
50
|
+
options ||= Hash.new
|
51
|
+
if options.is_a?(String)
|
52
|
+
begin
|
53
|
+
uri = URI.parse(options)
|
54
|
+
host = uri.host
|
55
|
+
port = uri.port || 6379
|
56
|
+
password = uri.password
|
57
|
+
rescue
|
58
|
+
raise('invalid redis url')
|
59
|
+
end
|
60
|
+
else
|
61
|
+
host = options[:host] || 'localhost'
|
62
|
+
port = options[:port] || 6379
|
63
|
+
password = options[:password]
|
64
|
+
end
|
52
65
|
EM::connect(host, port, Redis::Client) do |client|
|
53
66
|
client.redis_host = host
|
54
67
|
client.redis_port = port
|
55
|
-
client.redis_password =
|
68
|
+
client.redis_password = password
|
56
69
|
end
|
57
70
|
end
|
58
71
|
end
|
data/lib/sensu/process.rb
CHANGED
@@ -1,25 +1,19 @@
|
|
1
1
|
module Sensu
|
2
2
|
class Process
|
3
|
-
def initialize
|
3
|
+
def initialize
|
4
4
|
@logger = Cabin::Channel.get
|
5
|
-
if options[:daemonize]
|
6
|
-
daemonize
|
7
|
-
end
|
8
|
-
if options[:pid_file]
|
9
|
-
write_pid(options[:pid_file])
|
10
|
-
end
|
11
|
-
setup_eventmachine
|
12
5
|
end
|
13
6
|
|
14
|
-
def write_pid(
|
7
|
+
def write_pid(file)
|
15
8
|
begin
|
16
|
-
File.open(
|
17
|
-
|
9
|
+
File.open(file, 'w') do |pid_file|
|
10
|
+
pid_file.puts(::Process.pid)
|
18
11
|
end
|
19
12
|
rescue
|
20
13
|
@logger.fatal('could not write to pid file', {
|
21
|
-
:pid_file =>
|
14
|
+
:pid_file => file
|
22
15
|
})
|
16
|
+
@logger.fatal('SENSU NOT RUNNING!')
|
23
17
|
exit 2
|
24
18
|
end
|
25
19
|
end
|
@@ -31,6 +25,7 @@ module Sensu
|
|
31
25
|
end
|
32
26
|
unless ::Process.setsid
|
33
27
|
@logger.fatal('cannot detach from controlling terminal')
|
28
|
+
@logger.fatal('SENSU NOT RUNNING!')
|
34
29
|
exit 2
|
35
30
|
end
|
36
31
|
Signal.trap('SIGHUP', 'IGNORE')
|
data/lib/sensu/server.rb
CHANGED
@@ -63,6 +63,43 @@ module Sensu
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
def check_subdued?(check, subdue_type)
|
67
|
+
subdue = false
|
68
|
+
if check[:subdue].is_a?(Hash)
|
69
|
+
if check[:subdue].has_key?(:start) && check[:subdue].has_key?(:end)
|
70
|
+
start = Time.parse(check[:subdue][:start])
|
71
|
+
stop = Time.parse(check[:subdue][:end])
|
72
|
+
if stop < start
|
73
|
+
if Time.now < stop
|
74
|
+
start = Time.parse('12:00:00 AM')
|
75
|
+
else
|
76
|
+
stop = Time.parse('11:59:59 PM')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if Time.now >= start && Time.now <= stop
|
80
|
+
subdue = true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
if check[:subdue].has_key?(:days)
|
84
|
+
days = check[:subdue][:days].map(&:downcase)
|
85
|
+
if days.include?(Time.now.strftime('%A').downcase)
|
86
|
+
subdue = true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
if subdue && check[:subdue].has_key?(:exceptions)
|
90
|
+
subdue = check[:subdue][:exceptions].none? do |exception|
|
91
|
+
Time.now >= Time.parse(exception[:start]) && Time.now <= Time.parse(exception[:end])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
if subdue
|
96
|
+
(!check[:subdue].has_key?(:at) && subdue_type == :handler) ||
|
97
|
+
(check[:subdue].has_key?(:at) && check[:subdue][:at].to_sym == subdue_type)
|
98
|
+
else
|
99
|
+
false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
66
103
|
def check_handlers(check)
|
67
104
|
handler_list = case
|
68
105
|
when check.has_key?(:handler)
|
@@ -93,73 +130,75 @@ module Sensu
|
|
93
130
|
end
|
94
131
|
|
95
132
|
def handle_event(event)
|
96
|
-
|
97
|
-
|
98
|
-
output.
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
end
|
104
|
-
@handlers_in_progress -= 1
|
105
|
-
end
|
106
|
-
handlers = check_handlers(event[:check])
|
107
|
-
handlers.each do |handler|
|
108
|
-
@logger.debug('handling event', {
|
109
|
-
:event => event,
|
110
|
-
:handler => handler
|
111
|
-
})
|
112
|
-
@handlers_in_progress += 1
|
113
|
-
case handler[:type]
|
114
|
-
when 'pipe'
|
115
|
-
execute = proc do
|
116
|
-
begin
|
117
|
-
IO.popen(handler[:command] + ' 2>&1', 'r+') do |io|
|
118
|
-
io.write(event.to_json)
|
119
|
-
io.close_write
|
120
|
-
io.read
|
121
|
-
end
|
122
|
-
rescue Errno::ENOENT => error
|
123
|
-
@logger.error('handler does not exist', {
|
124
|
-
:event => event,
|
125
|
-
:handler => handler,
|
126
|
-
:error => error.to_s
|
127
|
-
})
|
128
|
-
rescue Errno::EPIPE => error
|
129
|
-
@logger.error('broken pipe', {
|
130
|
-
:event => event,
|
131
|
-
:handler => handler,
|
132
|
-
:error => error.to_s
|
133
|
-
})
|
134
|
-
rescue => error
|
135
|
-
@logger.error('unexpected error', {
|
136
|
-
:event => event,
|
137
|
-
:handler => handler,
|
138
|
-
:error => error.to_s
|
133
|
+
unless check_subdued?(event[:check], :handler)
|
134
|
+
report = proc do |output|
|
135
|
+
if output.is_a?(String)
|
136
|
+
output.split(/\n+/).each do |line|
|
137
|
+
@logger.info('handler output', {
|
138
|
+
:output => line
|
139
139
|
})
|
140
140
|
end
|
141
141
|
end
|
142
|
-
EM::defer(execute, report)
|
143
|
-
when 'amqp'
|
144
|
-
exchange_name = handler[:exchange][:name]
|
145
|
-
exchange_type = handler[:exchange].has_key?(:type) ? handler[:exchange][:type].to_sym : :direct
|
146
|
-
exchange_options = handler[:exchange].reject do |key, value|
|
147
|
-
[:name, :type].include?(key)
|
148
|
-
end
|
149
|
-
@logger.debug('publishing event to an amqp exchange', {
|
150
|
-
:event => event,
|
151
|
-
:exchange => handler[:exchange]
|
152
|
-
})
|
153
|
-
payload = handler[:send_only_check_output] ? event[:check][:output] : event.to_json
|
154
|
-
unless payload.empty?
|
155
|
-
@amq.method(exchange_type).call(exchange_name, exchange_options).publish(payload)
|
156
|
-
end
|
157
142
|
@handlers_in_progress -= 1
|
158
|
-
|
159
|
-
|
143
|
+
end
|
144
|
+
handlers = check_handlers(event[:check])
|
145
|
+
handlers.each do |handler|
|
146
|
+
@logger.debug('handling event', {
|
147
|
+
:event => event,
|
160
148
|
:handler => handler
|
161
149
|
})
|
162
|
-
@handlers_in_progress
|
150
|
+
@handlers_in_progress += 1
|
151
|
+
case handler[:type]
|
152
|
+
when 'pipe'
|
153
|
+
execute = proc do
|
154
|
+
begin
|
155
|
+
IO.popen(handler[:command] + ' 2>&1', 'r+') do |io|
|
156
|
+
io.write(event.to_json)
|
157
|
+
io.close_write
|
158
|
+
io.read
|
159
|
+
end
|
160
|
+
rescue Errno::ENOENT => error
|
161
|
+
@logger.error('handler does not exist', {
|
162
|
+
:event => event,
|
163
|
+
:handler => handler,
|
164
|
+
:error => error.to_s
|
165
|
+
})
|
166
|
+
rescue Errno::EPIPE => error
|
167
|
+
@logger.error('broken pipe', {
|
168
|
+
:event => event,
|
169
|
+
:handler => handler,
|
170
|
+
:error => error.to_s
|
171
|
+
})
|
172
|
+
rescue => error
|
173
|
+
@logger.error('unexpected error', {
|
174
|
+
:event => event,
|
175
|
+
:handler => handler,
|
176
|
+
:error => error.to_s
|
177
|
+
})
|
178
|
+
end
|
179
|
+
end
|
180
|
+
EM::defer(execute, report)
|
181
|
+
when 'amqp'
|
182
|
+
exchange_name = handler[:exchange][:name]
|
183
|
+
exchange_type = handler[:exchange].has_key?(:type) ? handler[:exchange][:type].to_sym : :direct
|
184
|
+
exchange_options = handler[:exchange].reject do |key, value|
|
185
|
+
[:name, :type].include?(key)
|
186
|
+
end
|
187
|
+
@logger.debug('publishing event to an amqp exchange', {
|
188
|
+
:event => event,
|
189
|
+
:exchange => handler[:exchange]
|
190
|
+
})
|
191
|
+
payload = handler[:send_only_check_output] ? event[:check][:output] : event.to_json
|
192
|
+
unless payload.empty?
|
193
|
+
@amq.method(exchange_type).call(exchange_name, exchange_options).publish(payload)
|
194
|
+
end
|
195
|
+
@handlers_in_progress -= 1
|
196
|
+
when 'set'
|
197
|
+
@logger.error('handler sets cannot be nested', {
|
198
|
+
:handler => handler
|
199
|
+
})
|
200
|
+
@handlers_in_progress -= 1
|
201
|
+
end
|
163
202
|
end
|
164
203
|
end
|
165
204
|
end
|
@@ -242,6 +281,7 @@ module Sensu
|
|
242
281
|
unless check[:auto_resolve] == false && !check[:force_resolve]
|
243
282
|
@redis.hdel('events:' + client[:name], check[:name]).callback do
|
244
283
|
unless check[:handle] == false
|
284
|
+
event[:occurrences] = previous_occurrence[:occurrences]
|
245
285
|
event[:action] = 'resolve'
|
246
286
|
handle_event(event)
|
247
287
|
else
|
@@ -294,17 +334,19 @@ module Sensu
|
|
294
334
|
@timers << EM::Timer.new(stagger * check_count) do
|
295
335
|
interval = options[:test] ? 0.5 : check[:interval]
|
296
336
|
@timers << EM::PeriodicTimer.new(interval) do
|
297
|
-
unless
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
337
|
+
unless check_subdued?(check, :publisher)
|
338
|
+
unless @rabbitmq.reconnecting?
|
339
|
+
payload = {
|
340
|
+
:name => check[:name],
|
341
|
+
:issued => Time.now.to_i
|
342
|
+
}
|
343
|
+
@logger.info('publishing check request', {
|
344
|
+
:payload => payload,
|
345
|
+
:subscribers => check[:subscribers]
|
346
|
+
})
|
347
|
+
check[:subscribers].uniq.each do |exchange_name|
|
348
|
+
@amq.fanout(exchange_name).publish(payload.to_json)
|
349
|
+
end
|
308
350
|
end
|
309
351
|
end
|
310
352
|
end
|
data/lib/sensu/settings.rb
CHANGED
@@ -203,6 +203,48 @@ module Sensu
|
|
203
203
|
raise('handlers must be an array for check: ' + check[:name])
|
204
204
|
end
|
205
205
|
end
|
206
|
+
if check.has_key?(:subdue)
|
207
|
+
unless check[:subdue].is_a?(Hash)
|
208
|
+
raise('subdue must be a hash for check: ' + check[:name])
|
209
|
+
end
|
210
|
+
if check[:subdue].has_key?(:start) || check[:subdue].has_key?(:end)
|
211
|
+
begin
|
212
|
+
Time.parse(check[:subdue][:start])
|
213
|
+
Time.parse(check[:subdue][:end])
|
214
|
+
rescue
|
215
|
+
raise('subdue start & end times must be valid for check: ' + check[:name])
|
216
|
+
end
|
217
|
+
end
|
218
|
+
if check[:subdue].has_key?(:days)
|
219
|
+
unless check[:subdue][:days].is_a?(Array)
|
220
|
+
raise('subdue days must be an array for check: ' + check[:name])
|
221
|
+
end
|
222
|
+
check[:subdue][:days].each do |day|
|
223
|
+
days = %w[sunday monday tuesday wednesday thursday friday saturday]
|
224
|
+
unless day.is_a?(String) && days.include?(day.downcase)
|
225
|
+
raise('subdue days must be valid days of the week for check: ' + check[:name])
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
if check[:subdue].has_key?(:exceptions)
|
230
|
+
unless check[:subdue][:exceptions].is_a?(Array)
|
231
|
+
raise('subdue exceptions must be an array for check: ' + check[:name])
|
232
|
+
end
|
233
|
+
check[:subdue][:exceptions].each do |exception|
|
234
|
+
unless exception.is_a?(Hash)
|
235
|
+
raise('subdue exception items must be a hash for check: ' + check[:name])
|
236
|
+
end
|
237
|
+
if exception.has_key?(:start) || exception.has_key?(:end)
|
238
|
+
begin
|
239
|
+
Time.parse(exception[:start])
|
240
|
+
Time.parse(exception[:end])
|
241
|
+
rescue
|
242
|
+
raise('subdue exception start & end times must be valid for check: ' + check[:name])
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
206
248
|
end
|
207
249
|
end
|
208
250
|
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sensu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 62196235
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
9
|
- 6
|
10
10
|
- beta
|
11
|
-
-
|
12
|
-
version: 0.9.6.beta.
|
11
|
+
- 4
|
12
|
+
version: 0.9.6.beta.4
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Sean Porter
|
@@ -18,7 +18,7 @@ autorequire:
|
|
18
18
|
bindir: bin
|
19
19
|
cert_chain: []
|
20
20
|
|
21
|
-
date: 2012-05-
|
21
|
+
date: 2012-05-19 00:00:00 -07:00
|
22
22
|
default_executable:
|
23
23
|
dependencies:
|
24
24
|
- !ruby/object:Gem::Dependency
|