sensu 0.8.18 → 0.8.19
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/README.org +1 -0
- data/lib/sensu.rb +1 -1
- data/lib/sensu/api.rb +16 -1
- data/lib/sensu/client.rb +16 -5
- data/lib/sensu/config.rb +36 -23
- data/lib/sensu/server.rb +42 -29
- metadata +4 -4
data/README.org
CHANGED
data/lib/sensu.rb
CHANGED
data/lib/sensu/api.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Sensu
|
|
|
25
25
|
def self.setup(options={})
|
|
26
26
|
config = Sensu::Config.new(options)
|
|
27
27
|
$settings = config.settings
|
|
28
|
-
$logger = config.
|
|
28
|
+
$logger = config.open_log
|
|
29
29
|
$logger.debug('[setup] -- connecting to redis')
|
|
30
30
|
$redis = EM.connect($settings.redis.host, $settings.redis.port, Redis::Reconnect)
|
|
31
31
|
$logger.debug('[setup] -- connecting to rabbitmq')
|
|
@@ -102,6 +102,21 @@ module Sensu
|
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
+
aget '/checks' do
|
|
106
|
+
$logger.debug('[checks] -- ' + request.ip + ' -- GET -- request for check list')
|
|
107
|
+
body $settings.checks.to_json
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
aget '/check/:name' do |check|
|
|
111
|
+
$logger.debug('[check] -- ' + request.ip + ' -- GET -- request for check -- ' + check)
|
|
112
|
+
if $settings.checks.key?(check)
|
|
113
|
+
body $settings.checks[check].to_json
|
|
114
|
+
else
|
|
115
|
+
status 404
|
|
116
|
+
body nil
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
105
120
|
aget '/events' do
|
|
106
121
|
$logger.debug('[events] -- ' + request.ip + ' -- GET -- request for event list')
|
|
107
122
|
response = Hash.new
|
data/lib/sensu/client.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Sensu
|
|
|
22
22
|
def initialize(options={})
|
|
23
23
|
config = Sensu::Config.new(options)
|
|
24
24
|
@settings = config.settings
|
|
25
|
-
@logger = config.
|
|
25
|
+
@logger = config.open_log
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def stop(signal)
|
|
@@ -97,7 +97,7 @@ module Sensu
|
|
|
97
97
|
@logger.warn('[execute] -- missing client attributes -- ' + unmatched_tokens.join(', ') + ' -- ' + check.name)
|
|
98
98
|
check.status = 3
|
|
99
99
|
check.output = 'Missing client attributes: ' + unmatched_tokens.join(', ')
|
|
100
|
-
check.
|
|
100
|
+
check.handle = false
|
|
101
101
|
publish_result(check)
|
|
102
102
|
@checks_in_progress.delete(check.name)
|
|
103
103
|
end
|
|
@@ -106,7 +106,7 @@ module Sensu
|
|
|
106
106
|
@logger.warn('[execute] -- unkown check -- ' + check.name)
|
|
107
107
|
check.status = 3
|
|
108
108
|
check.output = 'Unknown check'
|
|
109
|
-
check.
|
|
109
|
+
check.handle = false
|
|
110
110
|
publish_result(check)
|
|
111
111
|
@checks_in_progress.delete(check.name)
|
|
112
112
|
end
|
|
@@ -126,11 +126,22 @@ module Sensu
|
|
|
126
126
|
if check.key?('matching')
|
|
127
127
|
@logger.info('[subscribe] -- check requires matching -- ' + check.name)
|
|
128
128
|
matches = check.matching.all? do |key, value|
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
desired = case key
|
|
130
|
+
when /^!/
|
|
131
|
+
key = key.slice(1..-1)
|
|
132
|
+
false
|
|
133
|
+
else
|
|
134
|
+
true
|
|
135
|
+
end
|
|
136
|
+
matched = case key
|
|
137
|
+
when 'subscribes'
|
|
138
|
+
value.all? do |subscription|
|
|
139
|
+
@settings.client.subscriptions.include?(subscription)
|
|
140
|
+
end
|
|
131
141
|
else
|
|
132
142
|
@settings.client[key] == value
|
|
133
143
|
end
|
|
144
|
+
desired == matched
|
|
134
145
|
end
|
|
135
146
|
if matches
|
|
136
147
|
@logger.info('[subscribe] -- client matches -- ' + check.name)
|
data/lib/sensu/config.rb
CHANGED
|
@@ -17,49 +17,62 @@ module Sensu
|
|
|
17
17
|
class Config
|
|
18
18
|
attr_accessor :settings, :logger
|
|
19
19
|
|
|
20
|
+
DEFAULT_OPTIONS = {
|
|
21
|
+
:log_file => '/tmp/sensu.log',
|
|
22
|
+
:config_file => '/etc/sensu/config.json',
|
|
23
|
+
:config_dir => '/etc/sensu/conf.d',
|
|
24
|
+
:validate => true,
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
def initialize(options={})
|
|
28
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
|
29
|
+
read_config
|
|
30
|
+
validate_config if @options[:validate]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def open_log
|
|
21
34
|
@logger = Cabin::Channel.new
|
|
22
|
-
log_file
|
|
23
|
-
|
|
24
|
-
ruby_logger = Logger.new(log_file)
|
|
35
|
+
if File.writable?(@options[:log_file]) || !File.exist?(@options[:log_file]) && File.writable?(File.dirname(@options[:log_file]))
|
|
36
|
+
ruby_logger = Logger.new(@options[:log_file])
|
|
25
37
|
else
|
|
26
|
-
invalid_config('log file is not writable: ' + log_file)
|
|
38
|
+
invalid_config('log file is not writable: ' + @options[:log_file])
|
|
27
39
|
end
|
|
28
40
|
@logger.subscribe(Cabin::Outputs::EmStdlibLogger.new(ruby_logger))
|
|
29
|
-
@logger.level = options[:verbose] ? :debug : :info
|
|
41
|
+
@logger.level = @options[:verbose] ? :debug : :info
|
|
30
42
|
Signal.trap('USR1') do
|
|
31
43
|
@logger.level = @logger.level == :info ? :debug : :info
|
|
32
44
|
end
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
@logger
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def read_config
|
|
49
|
+
if File.readable?(@options[:config_file])
|
|
35
50
|
begin
|
|
36
|
-
@settings = Hashie::Mash.new(JSON.parse(File.open(config_file, 'r').read))
|
|
51
|
+
@settings = Hashie::Mash.new(JSON.parse(File.open(@options[:config_file], 'r').read))
|
|
37
52
|
rescue JSON::ParserError => error
|
|
38
|
-
invalid_config('configuration file (' + config_file + ') must be valid JSON: ' + error)
|
|
53
|
+
invalid_config('configuration file (' + @options[:config_file] + ') must be valid JSON: ' + error.to_s)
|
|
39
54
|
end
|
|
40
55
|
else
|
|
41
|
-
invalid_config('configuration file does not exist or is not readable: ' + config_file)
|
|
56
|
+
invalid_config('configuration file does not exist or is not readable: ' + @options[:config_file])
|
|
42
57
|
end
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Dir[config_dir + '/**/*.json'].each do |snippet_file|
|
|
58
|
+
if File.exists?(@options[:config_dir])
|
|
59
|
+
Dir[@options[:config_dir] + '/**/*.json'].each do |snippet_file|
|
|
46
60
|
begin
|
|
47
61
|
snippet_hash = JSON.parse(File.open(snippet_file, 'r').read)
|
|
48
62
|
rescue JSON::ParserError => error
|
|
49
|
-
invalid_config('configuration snippet file (' + snippet_file + ') must be valid JSON: ' + error)
|
|
63
|
+
invalid_config('configuration snippet file (' + snippet_file + ') must be valid JSON: ' + error.to_s)
|
|
50
64
|
end
|
|
51
65
|
merged_settings = @settings.to_hash.deep_merge(snippet_hash)
|
|
52
|
-
@logger.warn('[settings] configuration snippet (' + snippet_file + ') applied changes: ' + @settings.deep_diff(merged_settings).to_json)
|
|
66
|
+
@logger.warn('[settings] configuration snippet (' + snippet_file + ') applied changes: ' + @settings.deep_diff(merged_settings).to_json) if @logger
|
|
53
67
|
@settings = Hashie::Mash.new(merged_settings)
|
|
54
68
|
end
|
|
55
69
|
end
|
|
56
|
-
validate_config(options['type'])
|
|
57
70
|
end
|
|
58
71
|
|
|
59
|
-
def validate_config
|
|
60
|
-
@logger.debug('[config] -- validating configuration')
|
|
72
|
+
def validate_config
|
|
73
|
+
@logger.debug('[config] -- validating configuration') if @logger
|
|
61
74
|
has_keys(%w[rabbitmq])
|
|
62
|
-
case type
|
|
75
|
+
case @options['type']
|
|
63
76
|
when 'server'
|
|
64
77
|
has_keys(%w[redis handlers checks])
|
|
65
78
|
unless @settings.handlers.include?('default')
|
|
@@ -100,9 +113,9 @@ module Sensu
|
|
|
100
113
|
end
|
|
101
114
|
end
|
|
102
115
|
end
|
|
103
|
-
if type
|
|
104
|
-
@logger.debug('[config] -- configuration valid -- running ' + type)
|
|
105
|
-
puts 'configuration valid -- running ' + type
|
|
116
|
+
if @options['type']
|
|
117
|
+
@logger.debug('[config] -- configuration valid -- running ' + @options['type']) if @logger
|
|
118
|
+
puts 'configuration valid -- running ' + @options['type']
|
|
106
119
|
end
|
|
107
120
|
end
|
|
108
121
|
|
|
@@ -115,7 +128,7 @@ module Sensu
|
|
|
115
128
|
end
|
|
116
129
|
|
|
117
130
|
def invalid_config(message)
|
|
118
|
-
@logger.error('[config] -- configuration invalid -- ' + message)
|
|
131
|
+
@logger.error('[config] -- configuration invalid -- ' + message) if @logger
|
|
119
132
|
raise 'configuration invalid, ' + message
|
|
120
133
|
end
|
|
121
134
|
|
data/lib/sensu/server.rb
CHANGED
|
@@ -6,7 +6,7 @@ require File.join(File.dirname(__FILE__), 'helpers', 'redis')
|
|
|
6
6
|
|
|
7
7
|
module Sensu
|
|
8
8
|
class Server
|
|
9
|
-
attr_accessor :redis, :is_worker
|
|
9
|
+
attr_accessor :redis, :amq, :is_worker
|
|
10
10
|
|
|
11
11
|
def self.run(options={})
|
|
12
12
|
EM.threadpool_size = 16
|
|
@@ -33,7 +33,7 @@ module Sensu
|
|
|
33
33
|
def initialize(options={})
|
|
34
34
|
config = Sensu::Config.new(options)
|
|
35
35
|
@settings = config.settings
|
|
36
|
-
@logger = config.
|
|
36
|
+
@logger = config.open_log
|
|
37
37
|
@is_worker = options[:worker]
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -84,22 +84,35 @@ module Sensu
|
|
|
84
84
|
handlers.each do |handler|
|
|
85
85
|
if @settings.handlers.key?(handler)
|
|
86
86
|
@logger.debug('[event] -- handling event -- ' + [handler, event.client.name, event.check.name].join(' -- '))
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
if @settings.handlers[handler].is_a?(Hash)
|
|
88
|
+
details = @settings.handlers[handler]
|
|
89
|
+
case details.type
|
|
90
|
+
when "amqp"
|
|
91
|
+
exchange = details.exchange || 'events'
|
|
92
|
+
@logger.debug('[event] -- publishing event to amqp exchange -- ' + [exchange, event.client.name, event.check.name].join(' -- '))
|
|
93
|
+
message = details.send_only_check_output ? event.check.output : event.to_json
|
|
94
|
+
@amq.direct(exchange).publish(message)
|
|
95
|
+
else
|
|
96
|
+
@logger.warn('[event] -- unknown handler type -- ' + details.type)
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
handle = proc do
|
|
100
|
+
output = ''
|
|
101
|
+
Bundler.with_clean_env do
|
|
102
|
+
begin
|
|
103
|
+
IO.popen(@settings.handlers[handler] + ' 2>&1', 'r+') do |io|
|
|
104
|
+
io.write(event.to_json)
|
|
105
|
+
io.close_write
|
|
106
|
+
output = io.read
|
|
107
|
+
end
|
|
108
|
+
rescue Errno::EPIPE => error
|
|
109
|
+
output = handler + ' -- broken pipe: ' + error
|
|
95
110
|
end
|
|
96
|
-
rescue Errno::EPIPE => error
|
|
97
|
-
output = handler + ' -- broken pipe: ' + error
|
|
98
111
|
end
|
|
112
|
+
output
|
|
99
113
|
end
|
|
100
|
-
|
|
114
|
+
EM.defer(handle, report)
|
|
101
115
|
end
|
|
102
|
-
EM.defer(handle, report)
|
|
103
116
|
else
|
|
104
117
|
@logger.warn('[event] -- handler does not exist -- ' + handler)
|
|
105
118
|
end
|
|
@@ -123,6 +136,7 @@ module Sensu
|
|
|
123
136
|
history_key = 'history:' + client.name + ':' + check.name
|
|
124
137
|
@redis.rpush(history_key, check.status).callback do
|
|
125
138
|
@redis.lrange(history_key, -21, -1).callback do |history|
|
|
139
|
+
event.check.history = history
|
|
126
140
|
total_state_change = 0
|
|
127
141
|
unless history.count < 21
|
|
128
142
|
state_changes = 0
|
|
@@ -156,7 +170,7 @@ module Sensu
|
|
|
156
170
|
unless is_flapping
|
|
157
171
|
unless check.auto_resolve == false && !check.force_resolve
|
|
158
172
|
@redis.hdel('events:' + client.name, check.name).callback do
|
|
159
|
-
unless check.
|
|
173
|
+
unless check.handle == false
|
|
160
174
|
event.action = 'resolve'
|
|
161
175
|
handle_event(event)
|
|
162
176
|
end
|
|
@@ -166,7 +180,7 @@ module Sensu
|
|
|
166
180
|
@logger.debug('[result] -- check is flapping -- ' + client.name + ' -- ' + check.name)
|
|
167
181
|
@redis.hset('events:' + client.name, check.name, previous_occurrence.merge({'flapping' => true}).to_json)
|
|
168
182
|
end
|
|
169
|
-
elsif check
|
|
183
|
+
elsif check.status != 0
|
|
170
184
|
if previous_occurrence && check.status == previous_occurrence.status
|
|
171
185
|
event.occurrences = previous_occurrence.occurrences += 1
|
|
172
186
|
end
|
|
@@ -176,7 +190,7 @@ module Sensu
|
|
|
176
190
|
:flapping => is_flapping,
|
|
177
191
|
:occurrences => event.occurrences
|
|
178
192
|
}.to_json).callback do
|
|
179
|
-
unless check.
|
|
193
|
+
unless check.handle == false
|
|
180
194
|
event.check.flapping = is_flapping
|
|
181
195
|
event.action = 'create'
|
|
182
196
|
handle_event(event)
|
|
@@ -206,24 +220,23 @@ module Sensu
|
|
|
206
220
|
exchanges = Hash.new
|
|
207
221
|
stagger = options[:test] ? 0 : 7
|
|
208
222
|
@settings.checks.each_with_index do |(name, details), index|
|
|
209
|
-
|
|
210
|
-
check.name = name
|
|
223
|
+
check_request = Hashie::Mash.new({:name => name})
|
|
211
224
|
unless details.publish == false
|
|
212
225
|
EM.add_timer(stagger*index) do
|
|
213
|
-
details.subscribers.each do |
|
|
214
|
-
if
|
|
215
|
-
@logger.debug('[publisher] -- check requires matching -- ' +
|
|
216
|
-
|
|
226
|
+
details.subscribers.each do |target|
|
|
227
|
+
if target.is_a?(Hash)
|
|
228
|
+
@logger.debug('[publisher] -- check requires matching -- ' + target.to_hash.to_s + ' -- ' + name)
|
|
229
|
+
check_request.matching = target
|
|
217
230
|
exchange = 'uchiwa'
|
|
218
231
|
else
|
|
219
|
-
exchange =
|
|
232
|
+
exchange = target
|
|
220
233
|
end
|
|
221
234
|
exchanges[exchange] ||= @amq.fanout(exchange)
|
|
222
235
|
interval = options[:test] ? 0.5 : details.interval
|
|
223
236
|
EM.add_periodic_timer(interval) do
|
|
224
|
-
|
|
237
|
+
check_request.issued = Time.now.to_i
|
|
225
238
|
@logger.info('[publisher] -- publishing check -- ' + name + ' -- ' + exchange)
|
|
226
|
-
exchanges[exchange].publish(
|
|
239
|
+
exchanges[exchange].publish(check_request.to_json)
|
|
227
240
|
end
|
|
228
241
|
end
|
|
229
242
|
end
|
|
@@ -239,7 +252,7 @@ module Sensu
|
|
|
239
252
|
clients.each do |client_id|
|
|
240
253
|
@redis.get('client:' + client_id).callback do |client_json|
|
|
241
254
|
client = Hashie::Mash.new(JSON.parse(client_json))
|
|
242
|
-
|
|
255
|
+
time_since_last_keepalive = Time.now.to_i - client.timestamp
|
|
243
256
|
result = Hashie::Mash.new({
|
|
244
257
|
:client => client.name,
|
|
245
258
|
:check => {
|
|
@@ -248,11 +261,11 @@ module Sensu
|
|
|
248
261
|
}
|
|
249
262
|
})
|
|
250
263
|
case
|
|
251
|
-
when
|
|
264
|
+
when time_since_last_keepalive >= 180
|
|
252
265
|
result.check.status = 2
|
|
253
266
|
result.check.output = 'No keep-alive sent from host in over 180 seconds'
|
|
254
267
|
@result_queue.publish(result.to_json)
|
|
255
|
-
when
|
|
268
|
+
when time_since_last_keepalive >= 120
|
|
256
269
|
result.check.status = 1
|
|
257
270
|
result.check.output = 'No keep-alive sent from host in over 120 seconds'
|
|
258
271
|
@result_queue.publish(result.to_json)
|
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: 25
|
|
5
5
|
prerelease: false
|
|
6
6
|
segments:
|
|
7
7
|
- 0
|
|
8
8
|
- 8
|
|
9
|
-
-
|
|
10
|
-
version: 0.8.
|
|
9
|
+
- 19
|
|
10
|
+
version: 0.8.19
|
|
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-12-
|
|
19
|
+
date: 2011-12-16 00:00:00 -08:00
|
|
20
20
|
default_executable:
|
|
21
21
|
dependencies:
|
|
22
22
|
- !ruby/object:Gem::Dependency
|