sensu 0.9.9.beta.2 → 0.9.9.beta.3
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/CHANGELOG.md +2 -0
- data/lib/sensu/api.rb +51 -82
- data/lib/sensu/base.rb +30 -24
- data/lib/sensu/cli.rb +9 -6
- data/lib/sensu/client.rb +25 -42
- data/lib/sensu/constants.rb +7 -6
- data/lib/sensu/extensions.rb +101 -0
- data/lib/sensu/extensions/handlers/debug.rb +17 -0
- data/lib/sensu/extensions/mutators/only_check_output.rb +17 -0
- data/lib/sensu/io.rb +2 -0
- data/lib/sensu/{logger.rb → logstream.rb} +19 -15
- data/lib/sensu/process.rb +1 -1
- data/lib/sensu/rabbitmq.rb +74 -0
- data/lib/sensu/redis.rb +10 -114
- data/lib/sensu/server.rb +201 -196
- data/lib/sensu/settings.rb +29 -67
- data/lib/sensu/utilities.rb +75 -0
- data/sensu.gemspec +3 -3
- metadata +19 -14
data/lib/sensu/constants.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
module Sensu
|
2
2
|
unless defined?(Sensu::VERSION)
|
3
|
-
VERSION = '0.9.9.beta.
|
4
|
-
end
|
3
|
+
VERSION = '0.9.9.beta.3'
|
5
4
|
|
6
|
-
unless defined?(Sensu::DEFAULT_OPTIONS)
|
7
5
|
DEFAULT_OPTIONS = {
|
8
6
|
:config_file => '/etc/sensu/config.json',
|
9
|
-
:config_dir => '/etc/sensu/conf.d'
|
7
|
+
:config_dir => '/etc/sensu/conf.d',
|
8
|
+
:log_level => :info
|
10
9
|
}
|
11
|
-
end
|
12
10
|
|
13
|
-
|
11
|
+
SETTINGS_CATEGORIES = [:checks, :filters, :mutators, :handlers]
|
12
|
+
|
13
|
+
EXTENSION_CATEGORIES = [:mutators, :handlers]
|
14
|
+
|
14
15
|
SEVERITIES = %w[ok warning critical unknown]
|
15
16
|
end
|
16
17
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Sensu
|
2
|
+
class Extensions
|
3
|
+
def initialize
|
4
|
+
@logger = Logger.get
|
5
|
+
@extensions = Hash.new
|
6
|
+
EXTENSION_CATEGORIES.each do |category|
|
7
|
+
@extensions[category] = Hash.new
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
@extensions[key]
|
13
|
+
end
|
14
|
+
|
15
|
+
EXTENSION_CATEGORIES.each do |category|
|
16
|
+
define_method(category.to_s.chop + '_exists?') do |extension_name|
|
17
|
+
@extensions[category].has_key?(extension_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def require_directory(directory)
|
22
|
+
Dir.glob(File.join(directory, '**/*.rb')).each do |file|
|
23
|
+
begin
|
24
|
+
require file
|
25
|
+
rescue ScriptError => error
|
26
|
+
@logger.error('failed to require extension', {
|
27
|
+
:extension_file => file,
|
28
|
+
:error => error
|
29
|
+
})
|
30
|
+
@logger.warn('ignoring extension', {
|
31
|
+
:extension_file => file
|
32
|
+
})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_all
|
38
|
+
require_directory(File.join(File.dirname(__FILE__), 'extensions'))
|
39
|
+
EXTENSION_CATEGORIES.each do |category|
|
40
|
+
extension_type = category.to_s.chop
|
41
|
+
Extension.const_get(extension_type.capitalize).descendants.each do |klass|
|
42
|
+
extension = klass.new
|
43
|
+
@extensions[category][extension.name] = extension
|
44
|
+
loaded(extension_type, extension.name, extension.description)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def loaded(type, name, description)
|
52
|
+
@logger.info('loaded extension', {
|
53
|
+
:type => type,
|
54
|
+
:name => name,
|
55
|
+
:description => description
|
56
|
+
})
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module Extension
|
61
|
+
class Base
|
62
|
+
def name
|
63
|
+
'base'
|
64
|
+
end
|
65
|
+
|
66
|
+
def description
|
67
|
+
'extension description (change me)'
|
68
|
+
end
|
69
|
+
|
70
|
+
def definition
|
71
|
+
{
|
72
|
+
:type => 'extension',
|
73
|
+
:name => name
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def [](key)
|
78
|
+
definition[key.to_sym]
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_key?(key)
|
82
|
+
definition.has_key?(key.to_sym)
|
83
|
+
end
|
84
|
+
|
85
|
+
def run(event=nil, &block)
|
86
|
+
block.call('noop', 0)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.descendants
|
90
|
+
ObjectSpace.each_object(Class).select do |klass|
|
91
|
+
klass < self
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
EXTENSION_CATEGORIES.each do |category|
|
97
|
+
extension_type = category.to_s.chop
|
98
|
+
Object.const_set(extension_type.capitalize, Class.new(Base))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sensu
|
2
|
+
module Extension
|
3
|
+
class OnlyCheckOutput < Mutator
|
4
|
+
def name
|
5
|
+
'only_check_output'
|
6
|
+
end
|
7
|
+
|
8
|
+
def description
|
9
|
+
'returns check output'
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(event, &block)
|
13
|
+
block.call(event[:check][:output], 0)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/sensu/io.rb
CHANGED
@@ -3,7 +3,9 @@ gem 'cabin', '0.4.4'
|
|
3
3
|
require 'cabin'
|
4
4
|
|
5
5
|
module Sensu
|
6
|
-
class
|
6
|
+
class LogStream
|
7
|
+
attr_reader :logger
|
8
|
+
|
7
9
|
def initialize
|
8
10
|
@logger = Cabin::Channel.get
|
9
11
|
STDOUT.sync = true
|
@@ -15,19 +17,16 @@ module Sensu
|
|
15
17
|
@logger.level = log_level
|
16
18
|
end
|
17
19
|
|
18
|
-
def reopen(file
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
:log_file => file
|
29
|
-
})
|
30
|
-
end
|
20
|
+
def reopen(file)
|
21
|
+
@log_file = file
|
22
|
+
if File.writable?(file) || !File.exist?(file) && File.writable?(File.dirname(file))
|
23
|
+
STDOUT.reopen(file, 'a')
|
24
|
+
STDOUT.sync = true
|
25
|
+
STDERR.reopen(STDOUT)
|
26
|
+
else
|
27
|
+
@logger.error('log file is not writable', {
|
28
|
+
:log_file => file
|
29
|
+
})
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
@@ -39,11 +38,15 @@ module Sensu
|
|
39
38
|
end
|
40
39
|
if Signal.list.include?('USR2')
|
41
40
|
Signal.trap('USR2') do
|
42
|
-
|
41
|
+
if @log_file
|
42
|
+
reopen(@log_file)
|
43
|
+
end
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
47
|
+
end
|
46
48
|
|
49
|
+
class Logger
|
47
50
|
def self.get
|
48
51
|
Cabin::Channel.get
|
49
52
|
end
|
@@ -52,6 +55,7 @@ module Sensu
|
|
52
55
|
class NullLogger
|
53
56
|
[:debug, :info, :warn, :error, :fatal].each do |method|
|
54
57
|
define_method(method) do |*arguments|
|
58
|
+
true
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
data/lib/sensu/process.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
gem 'amqp', '0.9.8'
|
2
|
+
|
3
|
+
require 'amqp'
|
4
|
+
|
5
|
+
module Sensu
|
6
|
+
class RabbitMQError < StandardError; end
|
7
|
+
|
8
|
+
class RabbitMQ
|
9
|
+
attr_reader :channel
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@on_error = Proc.new {}
|
13
|
+
@before_reconnect = Proc.new {}
|
14
|
+
@after_reconnect = Proc.new {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_error(&block)
|
18
|
+
@on_error = block
|
19
|
+
end
|
20
|
+
|
21
|
+
def before_reconnect(&block)
|
22
|
+
@before_reconnect = block
|
23
|
+
end
|
24
|
+
|
25
|
+
def after_reconnect(&block)
|
26
|
+
@after_reconnect = block
|
27
|
+
end
|
28
|
+
|
29
|
+
def connect(options={})
|
30
|
+
options.reject! do |key, value|
|
31
|
+
key == :heartbeat
|
32
|
+
end
|
33
|
+
on_failure = Proc.new do
|
34
|
+
error = RabbitMQError.new('cannot connect to rabbitmq')
|
35
|
+
@on_error.call(error)
|
36
|
+
end
|
37
|
+
@connection = AMQP.connect(options, {
|
38
|
+
:on_tcp_connection_failure => on_failure,
|
39
|
+
:on_possible_authentication_failure => on_failure
|
40
|
+
})
|
41
|
+
@connection.logger = NullLogger.get
|
42
|
+
@connection.on_tcp_connection_loss do |connection, settings|
|
43
|
+
unless connection.reconnecting?
|
44
|
+
@before_reconnect.call
|
45
|
+
connection.periodically_reconnect(5)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@channel = AMQP::Channel.new(@connection)
|
49
|
+
@channel.auto_recovery = true
|
50
|
+
@channel.on_error do |channel, channel_close|
|
51
|
+
error = RabbitMQError.new('rabbitmq channel closed')
|
52
|
+
@on_error.call(error)
|
53
|
+
end
|
54
|
+
@channel.on_recovery do
|
55
|
+
@after_reconnect.call
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def connected?
|
60
|
+
@connection.connected?
|
61
|
+
end
|
62
|
+
|
63
|
+
def close
|
64
|
+
@connection.close
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.connect(options={})
|
68
|
+
options ||= Hash.new
|
69
|
+
rabbitmq = self.new
|
70
|
+
rabbitmq.connect(options)
|
71
|
+
rabbitmq
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/sensu/redis.rb
CHANGED
@@ -1,122 +1,18 @@
|
|
1
|
-
gem '
|
1
|
+
gem 'em-redis-unified', '0.4.1'
|
2
2
|
|
3
|
-
require 'redis'
|
3
|
+
require 'em-redis'
|
4
4
|
|
5
5
|
module Sensu
|
6
|
-
class Redis
|
7
|
-
|
8
|
-
|
9
|
-
alias :em_reconnect :reconnect
|
10
|
-
|
11
|
-
def initialize(*arguments)
|
12
|
-
super
|
13
|
-
@logger = Sensu::Logger.get
|
14
|
-
@settings = Hash.new
|
15
|
-
@connection_established = false
|
16
|
-
@connected = false
|
17
|
-
@reconnecting = false
|
18
|
-
@closing_connection = false
|
19
|
-
end
|
20
|
-
|
21
|
-
def setup_heartbeat
|
22
|
-
@heartbeat ||= EM::PeriodicTimer.new(10) do
|
23
|
-
if connected?
|
24
|
-
ping
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def connection_completed
|
30
|
-
@connection_established = true
|
31
|
-
@connected = true
|
32
|
-
@reconnecting = false
|
33
|
-
if @settings[:password]
|
34
|
-
auth(@settings[:password]).callback do |reply|
|
35
|
-
unless reply == 'OK'
|
36
|
-
@logger.fatal('redis authentication failed')
|
37
|
-
close_connection
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
info.callback do |reply|
|
42
|
-
redis_version = reply.split(/\n/).select { |v| v =~ /^redis_version/ }.first.split(/:/).last.chomp
|
43
|
-
if redis_version < '1.3.14'
|
44
|
-
@logger.fatal('redis version must be >= 2.0 RC 1')
|
45
|
-
close_connection
|
46
|
-
end
|
47
|
-
end
|
48
|
-
setup_heartbeat
|
49
|
-
end
|
50
|
-
|
51
|
-
def reconnect(immediate=false, wait=10)
|
52
|
-
if @reconnecting && !immediate
|
53
|
-
EM::Timer.new(wait) do
|
54
|
-
em_reconnect(@settings[:host], @settings[:port])
|
55
|
-
end
|
56
|
-
else
|
57
|
-
@reconnecting = true
|
58
|
-
em_reconnect(@settings[:host], @settings[:port])
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def close
|
63
|
-
@closing_connection = true
|
64
|
-
close_connection
|
65
|
-
end
|
66
|
-
|
67
|
-
def on_tcp_connection_loss(&block)
|
68
|
-
if block.respond_to?(:call)
|
69
|
-
@on_tcp_connection_loss = block
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def unbind
|
74
|
-
@connected = false
|
75
|
-
super
|
76
|
-
unless @closing_connection
|
77
|
-
if @connection_established
|
78
|
-
if @on_tcp_connection_loss
|
79
|
-
@on_tcp_connection_loss.call(self, @settings)
|
80
|
-
end
|
81
|
-
else
|
82
|
-
if @on_tcp_connection_failure
|
83
|
-
@on_tcp_connection_failure.call(self, @settings)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def connected?
|
90
|
-
@connected
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.connect(options, additional={})
|
6
|
+
class Redis
|
7
|
+
def self.connect(options={})
|
94
8
|
options ||= Hash.new
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
rescue
|
102
|
-
@logger.fatal('invalid redis url')
|
103
|
-
@logger.fatal('SENSU NOT RUNNING!')
|
104
|
-
exit 2
|
9
|
+
connection = EM::Protocols::Redis.connect(options)
|
10
|
+
connection.info do |info|
|
11
|
+
if info[:redis_version] < '1.3.14'
|
12
|
+
klass = EM::Protocols::Redis::RedisError
|
13
|
+
message = 'redis version must be >= 2.0 RC 1'
|
14
|
+
connection.error(klass, message)
|
105
15
|
end
|
106
|
-
else
|
107
|
-
host = options[:host] || 'localhost'
|
108
|
-
port = options[:port] || 6379
|
109
|
-
password = options[:password]
|
110
|
-
end
|
111
|
-
connection = EM::connect(host, port, self) do |redis|
|
112
|
-
redis.settings = {
|
113
|
-
:host => host,
|
114
|
-
:port => port,
|
115
|
-
:password => password
|
116
|
-
}
|
117
|
-
end
|
118
|
-
if additional[:on_tcp_connection_failure].respond_to?(:call)
|
119
|
-
connection.on_tcp_connection_failure = additional[:on_tcp_connection_failure]
|
120
16
|
end
|
121
17
|
connection
|
122
18
|
end
|