sensu 0.7.0-x86-mswin32 → 0.7.1-x86-mswin32
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/Gemfile.lock +3 -1
- data/lib/sensu.rb +1 -1
- data/lib/sensu/client.rb +37 -28
- data/lib/sensu/config.rb +15 -11
- data/lib/sensu/helpers.rb +17 -15
- data/sensu.gemspec +2 -1
- data/sensu.windows +3 -2
- metadata +19 -12
- data/bin/sensu-api +0 -9
- data/bin/sensu-server +0 -9
- data/lib/sensu/api.rb +0 -173
- data/lib/sensu/server.rb +0 -226
data/Gemfile.lock
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sensu (0.
|
4
|
+
sensu (0.7.0)
|
5
5
|
amqp (= 0.7.4)
|
6
6
|
async_sinatra
|
7
7
|
em-hiredis
|
8
8
|
em-syslog
|
9
|
+
hashie
|
9
10
|
json
|
10
11
|
thin
|
11
12
|
uuidtools
|
@@ -29,6 +30,7 @@ GEM
|
|
29
30
|
callsite (~> 0.0.5)
|
30
31
|
eventmachine
|
31
32
|
eventmachine (0.12.10)
|
33
|
+
hashie (1.1.0)
|
32
34
|
hiredis (0.3.2)
|
33
35
|
json (1.6.1)
|
34
36
|
mime-types (1.16)
|
data/lib/sensu.rb
CHANGED
data/lib/sensu/client.rb
CHANGED
@@ -23,69 +23,78 @@ module Sensu
|
|
23
23
|
def initialize(options={})
|
24
24
|
config = Sensu::Config.new(:config_file => options[:config_file])
|
25
25
|
@settings = config.settings
|
26
|
-
@checks_in_progress = Array.new
|
27
26
|
end
|
28
27
|
|
29
28
|
def setup_amqp
|
30
|
-
connection = AMQP.connect(
|
29
|
+
connection = AMQP.connect(@settings.rabbitmq.to_hash.symbolize_keys)
|
31
30
|
@amq = MQ.new(connection)
|
32
|
-
@keepalive_queue = @amq.queue('keepalives')
|
33
|
-
@result_queue = @amq.queue('results')
|
34
31
|
end
|
35
32
|
|
36
33
|
def setup_keepalives
|
37
|
-
|
34
|
+
keepalive_queue = @amq.queue('keepalives')
|
35
|
+
@settings.client.timestamp = Time.now.to_i
|
36
|
+
keepalive_queue.publish(@settings.client.to_json)
|
38
37
|
EM.add_periodic_timer(30) do
|
39
|
-
@
|
38
|
+
@settings.client.timestamp = Time.now.to_i
|
39
|
+
keepalive_queue.publish(@settings.client.to_json)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
def publish_result(check)
|
44
|
+
@result_queue ||= @amq.queue('results')
|
45
|
+
@result_queue.publish({
|
46
|
+
:client => @settings.client.name,
|
47
|
+
:check => check.to_hash
|
48
|
+
}.to_json)
|
49
|
+
end
|
50
|
+
|
43
51
|
def execute_check(check)
|
44
|
-
|
45
|
-
if @settings
|
46
|
-
unless @checks_in_progress.include?(check
|
47
|
-
@checks_in_progress.push(check
|
52
|
+
@checks_in_progress ||= Array.new
|
53
|
+
if @settings.checks.key?(check.name)
|
54
|
+
unless @checks_in_progress.include?(check.name)
|
55
|
+
@checks_in_progress.push(check.name)
|
48
56
|
unmatched_tokens = Array.new
|
49
|
-
command = @settings
|
57
|
+
command = @settings.checks[check.name].command.gsub(/:::(.*?):::/) do
|
50
58
|
key = $1.to_s
|
51
|
-
unmatched_tokens.push(key) unless @settings
|
52
|
-
@settings
|
59
|
+
unmatched_tokens.push(key) unless @settings.client.key?(key)
|
60
|
+
@settings.client[key].to_s
|
53
61
|
end
|
54
62
|
if unmatched_tokens.empty?
|
55
63
|
execute = proc do
|
56
64
|
IO.popen(command + ' 2>&1') do |io|
|
57
|
-
|
65
|
+
check.output = io.read
|
58
66
|
end
|
59
|
-
|
60
|
-
result
|
67
|
+
check.status = $?.exitstatus
|
61
68
|
end
|
62
69
|
publish = proc do |result|
|
63
|
-
|
64
|
-
@checks_in_progress.delete(
|
70
|
+
publish_result(check)
|
71
|
+
@checks_in_progress.delete(check.name)
|
65
72
|
end
|
66
73
|
EM.defer(execute, publish)
|
67
74
|
else
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
75
|
+
check.status = 3
|
76
|
+
check.output = 'Missing client attributes: ' + unmatched_tokens.join(', ')
|
77
|
+
check.internal = true
|
78
|
+
publish_result(check)
|
79
|
+
@checks_in_progress.delete(check.name)
|
72
80
|
end
|
73
81
|
end
|
74
82
|
else
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
83
|
+
check.status = 3
|
84
|
+
check.output = 'Unknown check'
|
85
|
+
check.internal = true
|
86
|
+
publish_result(check)
|
87
|
+
@checks_in_progress.delete(check.name)
|
79
88
|
end
|
80
89
|
end
|
81
90
|
|
82
91
|
def setup_subscriptions
|
83
92
|
@check_queue = @amq.queue(UUIDTools::UUID.random_create.to_s, :exclusive => true)
|
84
|
-
@settings
|
93
|
+
@settings.client.subscriptions.each do |exchange|
|
85
94
|
@check_queue.bind(@amq.fanout(exchange))
|
86
95
|
end
|
87
96
|
@check_queue.subscribe do |check_json|
|
88
|
-
check = JSON.parse(check_json)
|
97
|
+
check = Hashie::Mash.new(JSON.parse(check_json))
|
89
98
|
execute_check(check)
|
90
99
|
end
|
91
100
|
end
|
data/lib/sensu/config.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems' if RUBY_VERSION < '1.9.0'
|
2
2
|
require 'optparse'
|
3
3
|
require 'json'
|
4
|
+
require 'hashie'
|
4
5
|
require 'uuidtools'
|
5
6
|
require 'amqp'
|
6
7
|
require 'em/syslog'
|
@@ -12,29 +13,29 @@ module Sensu
|
|
12
13
|
|
13
14
|
def initialize(options={})
|
14
15
|
config_file = options[:config_file] || '/etc/sensu/config.json'
|
15
|
-
@settings = JSON.parse(File.open(config_file, 'r').read)
|
16
|
+
@settings = Hashie::Mash.new(JSON.parse(File.open(config_file, 'r').read))
|
16
17
|
validate_config
|
17
18
|
end
|
18
19
|
|
19
20
|
def validate_config
|
20
|
-
@settings
|
21
|
-
unless
|
21
|
+
@settings.checks.each do |name, details|
|
22
|
+
unless details.interval.is_a?(Integer) && details.interval > 0
|
22
23
|
raise 'configuration invalid, missing interval for check ' + name
|
23
24
|
end
|
24
|
-
unless
|
25
|
+
unless details.command.is_a?(String)
|
25
26
|
raise 'configuration invalid, missing command for check ' + name
|
26
27
|
end
|
27
|
-
unless
|
28
|
+
unless details.subscribers.is_a?(Array) && details.subscribers.count > 0
|
28
29
|
raise 'configuration invalid, missing subscribers for check ' + name
|
29
30
|
end
|
30
31
|
end
|
31
|
-
unless @settings
|
32
|
+
unless @settings.client.name.is_a?(String)
|
32
33
|
raise 'configuration invalid, client must have a name'
|
33
34
|
end
|
34
|
-
unless @settings
|
35
|
+
unless @settings.client.address.is_a?(String)
|
35
36
|
raise 'configuration invalid, client must have an address (ip or hostname)'
|
36
37
|
end
|
37
|
-
unless @settings
|
38
|
+
unless @settings.client.subscriptions.is_a?(Array) && @settings.client.subscriptions.count > 0
|
38
39
|
raise 'configuration invalid, client must have subscriptions'
|
39
40
|
end
|
40
41
|
end
|
@@ -46,9 +47,12 @@ module Sensu
|
|
46
47
|
puts opts
|
47
48
|
exit
|
48
49
|
end
|
49
|
-
|
50
|
-
|
51
|
-
options[:worker] =
|
50
|
+
current_process = $0.split('/').last
|
51
|
+
if current_process == 'sensu-server' || current_process == 'rake_test_loader.rb'
|
52
|
+
options[:worker] = false
|
53
|
+
opts.on('-w', '--worker', 'Only consume jobs, no check publishing (default: false)') do
|
54
|
+
options[:worker] = true
|
55
|
+
end
|
52
56
|
end
|
53
57
|
options[:config_file] = nil
|
54
58
|
opts.on('-c', '--config FILE', 'Sensu JSON config FILE (default: /etc/sensu/config.json)') do |file|
|
data/lib/sensu/helpers.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
class Hash
|
2
|
+
def symbolize_keys(item = self)
|
3
|
+
case item
|
4
|
+
when Array
|
5
|
+
item.map do |i|
|
6
|
+
symbolize_keys(i)
|
7
|
+
end
|
8
|
+
when Hash
|
9
|
+
Hash[
|
10
|
+
item.map do |key, value|
|
11
|
+
new_key = key.is_a?(String) ? key.to_sym : key
|
12
|
+
new_value = symbolize_keys(value)
|
13
|
+
[new_key, new_value]
|
14
|
+
end
|
15
|
+
]
|
16
|
+
else
|
17
|
+
item
|
6
18
|
end
|
7
|
-
when Hash
|
8
|
-
Hash[
|
9
|
-
item.map do |key, value|
|
10
|
-
k = key.is_a?(String) ? key.to_sym : key
|
11
|
-
v = symbolize_keys(value)
|
12
|
-
[k,v]
|
13
|
-
end
|
14
|
-
]
|
15
|
-
else
|
16
|
-
item
|
17
19
|
end
|
18
20
|
end
|
data/sensu.gemspec
CHANGED
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.add_dependency("amqp", "0.7.4")
|
16
16
|
s.add_dependency("json")
|
17
|
+
s.add_dependency("hashie")
|
17
18
|
s.add_dependency("uuidtools")
|
18
19
|
s.add_dependency("em-syslog")
|
19
20
|
s.add_dependency("em-hiredis")
|
@@ -25,7 +26,7 @@ Gem::Specification.new do |s|
|
|
25
26
|
s.add_development_dependency('em-ventually')
|
26
27
|
s.add_development_dependency('rest-client')
|
27
28
|
|
28
|
-
s.files = `git ls-files`.split("\n").reject {|f| f =~ /(dist|test)/}
|
29
|
+
s.files = `git ls-files`.split("\n").reject {|f| f =~ /(dist|test|png)/}
|
29
30
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
30
31
|
s.require_paths = ["lib"]
|
31
32
|
end
|
data/sensu.windows
CHANGED
@@ -21,10 +21,11 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_dependency("eventmachine", "1.0.0.beta.4.1")
|
22
22
|
s.add_dependency("amqp", "0.7.4")
|
23
23
|
s.add_dependency("json")
|
24
|
+
s.add_dependency("hashie")
|
24
25
|
s.add_dependency("uuidtools")
|
25
26
|
s.add_dependency("em-syslog")
|
26
27
|
|
27
|
-
s.files = `git ls-files`.split("\n").reject {|f| f =~ /(dist|test)/}
|
28
|
-
s.executables =
|
28
|
+
s.files = `git ls-files`.split("\n").reject {|f| f =~ /(dist|test|server|api|png)/}
|
29
|
+
s.executables = "sensu-client"
|
29
30
|
s.require_paths = ["lib"]
|
30
31
|
end
|
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: 1
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 0.7.
|
9
|
+
- 1
|
10
|
+
version: 0.7.1
|
11
11
|
platform: x86-mswin32
|
12
12
|
authors:
|
13
13
|
- Sean Porter
|
@@ -69,7 +69,7 @@ dependencies:
|
|
69
69
|
type: :runtime
|
70
70
|
version_requirements: *id003
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
|
-
name:
|
72
|
+
name: hashie
|
73
73
|
prerelease: false
|
74
74
|
requirement: &id004 !ruby/object:Gem::Requirement
|
75
75
|
none: false
|
@@ -83,7 +83,7 @@ dependencies:
|
|
83
83
|
type: :runtime
|
84
84
|
version_requirements: *id004
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
|
-
name:
|
86
|
+
name: uuidtools
|
87
87
|
prerelease: false
|
88
88
|
requirement: &id005 !ruby/object:Gem::Requirement
|
89
89
|
none: false
|
@@ -96,14 +96,26 @@ dependencies:
|
|
96
96
|
version: "0"
|
97
97
|
type: :runtime
|
98
98
|
version_requirements: *id005
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: em-syslog
|
101
|
+
prerelease: false
|
102
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
hash: 3
|
108
|
+
segments:
|
109
|
+
- 0
|
110
|
+
version: "0"
|
111
|
+
type: :runtime
|
112
|
+
version_requirements: *id006
|
99
113
|
description: A server monitoring framework using the publish-subscribe model
|
100
114
|
email:
|
101
115
|
- sean.porter@sonian.net
|
102
116
|
- justin.kolberg@sonian.net
|
103
117
|
executables:
|
104
|
-
- sensu-api
|
105
118
|
- sensu-client
|
106
|
-
- sensu-server
|
107
119
|
extensions: []
|
108
120
|
|
109
121
|
extra_rdoc_files: []
|
@@ -115,16 +127,11 @@ files:
|
|
115
127
|
- MIT-LICENSE.txt
|
116
128
|
- README.org
|
117
129
|
- Rakefile
|
118
|
-
- bin/sensu-api
|
119
130
|
- bin/sensu-client
|
120
|
-
- bin/sensu-server
|
121
131
|
- lib/sensu.rb
|
122
|
-
- lib/sensu/api.rb
|
123
132
|
- lib/sensu/client.rb
|
124
133
|
- lib/sensu/config.rb
|
125
134
|
- lib/sensu/helpers.rb
|
126
|
-
- lib/sensu/server.rb
|
127
|
-
- sensu-logo.png
|
128
135
|
- sensu.gemspec
|
129
136
|
- sensu.windows
|
130
137
|
has_rdoc: true
|
data/bin/sensu-api
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
raise "The Sensu API does not run on Windows" if RUBY_PLATFORM.downcase =~ /mswin|mingw32|windows/
|
4
|
-
|
5
|
-
$: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
|
6
|
-
require 'sensu/api'
|
7
|
-
|
8
|
-
options = Sensu::Config.read_arguments(ARGV)
|
9
|
-
Sensu::API.run(options)
|
data/bin/sensu-server
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
raise "The Sensu Server does not run on Windows" if RUBY_PLATFORM.downcase =~ /mswin|mingw32|windows/
|
4
|
-
|
5
|
-
$: << File.dirname(__FILE__) + '/../lib' unless $:.include?(File.dirname(__FILE__) + '/../lib/')
|
6
|
-
require 'sensu/server'
|
7
|
-
|
8
|
-
options = Sensu::Config.read_arguments(ARGV)
|
9
|
-
Sensu::Server.run(options)
|
data/lib/sensu/api.rb
DELETED
@@ -1,173 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'config')
|
2
|
-
require 'sinatra/async'
|
3
|
-
require 'em-hiredis'
|
4
|
-
|
5
|
-
module Sensu
|
6
|
-
class API < Sinatra::Base
|
7
|
-
register Sinatra::Async
|
8
|
-
|
9
|
-
def self.run(options={})
|
10
|
-
EM.run do
|
11
|
-
self.setup(options)
|
12
|
-
self.run!(:port => @settings['api']['port'])
|
13
|
-
|
14
|
-
Signal.trap('INT') do
|
15
|
-
EM.stop
|
16
|
-
end
|
17
|
-
|
18
|
-
Signal.trap('TERM') do
|
19
|
-
EM.stop
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.setup(options={})
|
25
|
-
config = Sensu::Config.new(options)
|
26
|
-
@settings = config.settings
|
27
|
-
set :redis, EM::Hiredis.connect('redis://' + @settings['redis']['host'] + ':' + @settings['redis']['port'].to_s)
|
28
|
-
connection = AMQP.connect(symbolize_keys(@settings['rabbitmq']))
|
29
|
-
set :amq, MQ.new(connection)
|
30
|
-
end
|
31
|
-
|
32
|
-
helpers do
|
33
|
-
include Rack::Utils
|
34
|
-
alias_method :conn, :settings
|
35
|
-
end
|
36
|
-
|
37
|
-
before do
|
38
|
-
content_type 'application/json'
|
39
|
-
end
|
40
|
-
|
41
|
-
aget '/clients' do
|
42
|
-
current_clients = Array.new
|
43
|
-
conn.redis.smembers('clients').callback do |clients|
|
44
|
-
unless clients.empty?
|
45
|
-
clients.each_with_index do |client, index|
|
46
|
-
conn.redis.get('client:' + client).callback do |client_json|
|
47
|
-
current_clients.push(JSON.parse(client_json))
|
48
|
-
body current_clients.to_json if index == clients.size-1
|
49
|
-
end
|
50
|
-
end
|
51
|
-
else
|
52
|
-
body current_clients.to_json
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
aget '/client/:name' do |client|
|
58
|
-
conn.redis.get('client:' + client).callback do |client_json|
|
59
|
-
status 404 if client_json.nil?
|
60
|
-
body client_json
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
adelete '/client/:name' do |client|
|
65
|
-
conn.redis.sismember('clients', client).callback do |client_exists|
|
66
|
-
unless client_exists == 0
|
67
|
-
conn.redis.exists('events:' + client).callback do |events_exist|
|
68
|
-
unless events_exist == 0
|
69
|
-
conn.redis.hgetall('events:' + client).callback do |events|
|
70
|
-
Hash[*events].keys.each do |check_name|
|
71
|
-
check = {'name' => check_name, 'issued' => Time.now.to_i, 'status' => 0, 'output' => 'Client is being removed'}
|
72
|
-
conn.amq.queue('results').publish({'client' => client, 'check' => check}.to_json)
|
73
|
-
end
|
74
|
-
EM.add_timer(5) do
|
75
|
-
conn.redis.srem('clients', client)
|
76
|
-
conn.redis.del('events:' + client)
|
77
|
-
conn.redis.del('client:' + client)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
else
|
81
|
-
conn.redis.srem('clients', client)
|
82
|
-
conn.redis.del('events:' + client)
|
83
|
-
conn.redis.del('client:' + client)
|
84
|
-
end
|
85
|
-
status 204
|
86
|
-
body nil
|
87
|
-
end
|
88
|
-
else
|
89
|
-
status 404
|
90
|
-
body nil
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
aget '/event/:client/:check' do |client, check|
|
96
|
-
conn.redis.hgetall('events:' + client).callback do |events|
|
97
|
-
client_events = Hash[*events]
|
98
|
-
event = client_events[check]
|
99
|
-
status 404 if event.nil?
|
100
|
-
body event
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
aget '/events' do
|
105
|
-
current_events = Hash.new
|
106
|
-
conn.redis.smembers('clients').callback do |clients|
|
107
|
-
unless clients.empty?
|
108
|
-
clients.each_with_index do |client, index|
|
109
|
-
conn.redis.hgetall('events:' + client).callback do |events|
|
110
|
-
client_events = Hash[*events]
|
111
|
-
client_events.each do |key, value|
|
112
|
-
client_events[key] = JSON.parse(value)
|
113
|
-
end
|
114
|
-
current_events[client] = client_events unless client_events.empty?
|
115
|
-
body current_events.to_json if index == clients.size-1
|
116
|
-
end
|
117
|
-
end
|
118
|
-
else
|
119
|
-
body current_events.to_json
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
apost '/stash/*' do |path|
|
125
|
-
conn.redis.set('stash:' + path, request.body.read).callback do
|
126
|
-
status 201
|
127
|
-
body nil
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
aget '/stash/*' do |path|
|
132
|
-
conn.redis.get('stash:' + path).callback do |stash|
|
133
|
-
status 404 if stash.nil?
|
134
|
-
body stash
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
adelete '/stash/*' do |path|
|
139
|
-
conn.redis.exists('stash:' + path).callback do |stash_exist|
|
140
|
-
unless stash_exist == 0
|
141
|
-
conn.redis.del('stash:' + path).callback do
|
142
|
-
status 204
|
143
|
-
body nil
|
144
|
-
end
|
145
|
-
else
|
146
|
-
status 404
|
147
|
-
body nil
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
apost '/test' do
|
153
|
-
client = '{
|
154
|
-
"name": "test",
|
155
|
-
"address": "localhost",
|
156
|
-
"subscriptions": [
|
157
|
-
"foo",
|
158
|
-
"bar"
|
159
|
-
]
|
160
|
-
}'
|
161
|
-
conn.redis.set('client:test', client).callback do
|
162
|
-
conn.redis.sadd('clients', 'test').callback do
|
163
|
-
conn.redis.hset('events:test', 'test', {'status' => 2, 'output' => 'CRITICAL', 'occurrences' => 1}.to_json).callback do
|
164
|
-
conn.redis.set('stash:test/test', '{"key": "value"}').callback do
|
165
|
-
status 201
|
166
|
-
body nil
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
data/lib/sensu/server.rb
DELETED
@@ -1,226 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'config')
|
2
|
-
require 'em-hiredis'
|
3
|
-
|
4
|
-
module Sensu
|
5
|
-
class Server
|
6
|
-
attr_accessor :redis, :is_worker
|
7
|
-
alias :redis_connection :redis
|
8
|
-
|
9
|
-
def self.run(options={})
|
10
|
-
EM.threadpool_size = 15
|
11
|
-
EM.run do
|
12
|
-
server = self.new(options)
|
13
|
-
server.setup_logging
|
14
|
-
server.setup_redis
|
15
|
-
server.setup_amqp
|
16
|
-
server.setup_keepalives
|
17
|
-
server.setup_results
|
18
|
-
unless server.is_worker
|
19
|
-
server.setup_publisher
|
20
|
-
server.setup_keepalive_monitor
|
21
|
-
end
|
22
|
-
server.setup_queue_monitor
|
23
|
-
|
24
|
-
Signal.trap('INT') do
|
25
|
-
EM.stop
|
26
|
-
end
|
27
|
-
|
28
|
-
Signal.trap('TERM') do
|
29
|
-
EM.stop
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def initialize(options={})
|
35
|
-
config = Sensu::Config.new(:config_file => options[:config_file])
|
36
|
-
@settings = config.settings
|
37
|
-
@is_worker = options[:worker]
|
38
|
-
end
|
39
|
-
|
40
|
-
def setup_logging
|
41
|
-
EM.syslog_setup(@settings['syslog']['host'], @settings['syslog']['port'])
|
42
|
-
end
|
43
|
-
|
44
|
-
def setup_redis
|
45
|
-
@redis = EM::Hiredis.connect('redis://' + @settings['redis']['host'] + ':' + @settings['redis']['port'].to_s)
|
46
|
-
end
|
47
|
-
|
48
|
-
def setup_amqp
|
49
|
-
connection = AMQP.connect(symbolize_keys(@settings['rabbitmq']))
|
50
|
-
@amq = MQ.new(connection)
|
51
|
-
end
|
52
|
-
|
53
|
-
def setup_keepalives
|
54
|
-
@keepalive_queue = @amq.queue('keepalives')
|
55
|
-
@keepalive_queue.subscribe do |keepalive_json|
|
56
|
-
client = JSON.parse(keepalive_json)['name']
|
57
|
-
@redis.set('client:' + client, keepalive_json).callback do
|
58
|
-
@redis.sadd('clients', client)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def handle_event(event)
|
64
|
-
handler = proc do
|
65
|
-
output = ''
|
66
|
-
IO.popen(@settings['handlers'][event['check']['handler']] + ' 2>&1', 'r+') do |io|
|
67
|
-
io.write(event.to_json)
|
68
|
-
io.close_write
|
69
|
-
output = io.read
|
70
|
-
end
|
71
|
-
output
|
72
|
-
end
|
73
|
-
report = proc do |output|
|
74
|
-
output.split(/\n+/).each do |line|
|
75
|
-
EM.debug(line)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
EM.defer(handler, report)
|
79
|
-
end
|
80
|
-
|
81
|
-
def process_result(result)
|
82
|
-
@redis.get('client:' + result['client']).callback do |client_json|
|
83
|
-
unless client_json.nil?
|
84
|
-
client = JSON.parse(client_json)
|
85
|
-
check = result['check']
|
86
|
-
if @settings['checks'][check['name']]
|
87
|
-
check.merge!(@settings['checks'][check['name']])
|
88
|
-
end
|
89
|
-
check['handler'] ||= 'default'
|
90
|
-
event = {'client' => client, 'check' => check, 'occurrences' => 1}
|
91
|
-
if check['type'] == 'metric'
|
92
|
-
handle_event(event)
|
93
|
-
else
|
94
|
-
history_key = 'history:' + client['name'] + ':' + check['name']
|
95
|
-
@redis.rpush(history_key, check['status']).callback do
|
96
|
-
@redis.lrange(history_key, -21, -1).callback do |history|
|
97
|
-
total_state_change = 0
|
98
|
-
unless history.count < 21
|
99
|
-
state_changes = 0
|
100
|
-
change_weight = 0.8
|
101
|
-
history.each do |status|
|
102
|
-
previous_status ||= status
|
103
|
-
unless status == previous_status
|
104
|
-
state_changes += change_weight
|
105
|
-
end
|
106
|
-
change_weight += 0.02
|
107
|
-
previous_status = status
|
108
|
-
end
|
109
|
-
total_state_change = (state_changes.fdiv(20) * 100).to_i
|
110
|
-
@redis.lpop(history_key)
|
111
|
-
end
|
112
|
-
high_flap_threshold = check['high_flap_threshold'] || 50
|
113
|
-
low_flap_threshold = check['low_flap_threshold'] || 40
|
114
|
-
@redis.hget('events:' + client['name'], check['name']).callback do |event_json|
|
115
|
-
previous_event = event_json ? JSON.parse(event_json) : false
|
116
|
-
flapping = previous_event ? previous_event['flapping'] : false
|
117
|
-
check['flapping'] = case
|
118
|
-
when total_state_change >= high_flap_threshold
|
119
|
-
true
|
120
|
-
when flapping && total_state_change <= low_flap_threshold
|
121
|
-
false
|
122
|
-
else
|
123
|
-
flapping
|
124
|
-
end
|
125
|
-
if previous_event && check['status'] == 0
|
126
|
-
unless check['flapping']
|
127
|
-
@redis.hdel('events:' + client['name'], check['name']).callback do
|
128
|
-
event['action'] = 'resolve'
|
129
|
-
handle_event(event)
|
130
|
-
end
|
131
|
-
else
|
132
|
-
@redis.hset('events:' + client['name'], check['name'], previous_event.merge({'flapping' => true}).to_json)
|
133
|
-
end
|
134
|
-
elsif check['status'] != 0
|
135
|
-
if previous_event && check['status'] == previous_event['status']
|
136
|
-
event['occurrences'] = previous_event['occurrences'] += 1
|
137
|
-
end
|
138
|
-
@redis.hset('events:' + client['name'], check['name'], {
|
139
|
-
'status' => check['status'],
|
140
|
-
'output' => check['output'],
|
141
|
-
'flapping' => check['flapping'],
|
142
|
-
'occurrences' => event['occurrences']
|
143
|
-
}.to_json).callback do
|
144
|
-
event['action'] = 'create'
|
145
|
-
handle_event(event)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def setup_results
|
157
|
-
@result_queue = @amq.queue('results')
|
158
|
-
@result_queue.subscribe do |result_json|
|
159
|
-
result = JSON.parse(result_json)
|
160
|
-
process_result(result)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def setup_publisher(options={})
|
165
|
-
exchanges = Hash.new
|
166
|
-
stagger = options[:test] ? 0 : 7
|
167
|
-
@settings['checks'].each_with_index do |(name, details), index|
|
168
|
-
EM.add_timer(stagger*index) do
|
169
|
-
details['subscribers'].each do |exchange|
|
170
|
-
if exchanges[exchange].nil?
|
171
|
-
exchanges[exchange] = @amq.fanout(exchange)
|
172
|
-
end
|
173
|
-
interval = options[:test] ? 0.5 : details['interval']
|
174
|
-
EM.add_periodic_timer(interval) do
|
175
|
-
exchanges[exchange].publish({'name' => name, 'issued' => Time.now.to_i}.to_json)
|
176
|
-
EM.debug('name="Published Check" event_id=server action="Published check ' + name + ' to the ' + exchange + ' exchange"')
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def setup_keepalive_monitor
|
184
|
-
EM.add_periodic_timer(30) do
|
185
|
-
@redis.smembers('clients').callback do |clients|
|
186
|
-
clients.each do |client_id|
|
187
|
-
@redis.get('client:' + client_id).callback do |client_json|
|
188
|
-
client = JSON.parse(client_json)
|
189
|
-
time_since_last_check = Time.now.to_i - client['timestamp']
|
190
|
-
result = {'client' => client['name'], 'check' => {'name' => 'keepalive', 'issued' => Time.now.to_i}}
|
191
|
-
case
|
192
|
-
when time_since_last_check >= 180
|
193
|
-
result['check']['status'] = 2
|
194
|
-
result['check']['output'] = 'No keep-alive sent from host in over 180 seconds'
|
195
|
-
process_result(result)
|
196
|
-
when time_since_last_check >= 120
|
197
|
-
result['check']['status'] = 1
|
198
|
-
result['check']['output'] = 'No keep-alive sent from host in over 120 seconds'
|
199
|
-
process_result(result)
|
200
|
-
else
|
201
|
-
@redis.hexists('events:' + client_id, 'keepalive').callback do |exists|
|
202
|
-
if exists == 1
|
203
|
-
result['check']['status'] = 0
|
204
|
-
result['check']['output'] = 'Keep-alive sent from host'
|
205
|
-
process_result(result)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def setup_queue_monitor
|
216
|
-
EM.add_periodic_timer(5) do
|
217
|
-
unless @keepalive_queue.subscribed?
|
218
|
-
setup_keepalives
|
219
|
-
end
|
220
|
-
unless @result_queue.subscribed?
|
221
|
-
setup_results
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|