sensu 0.9.5 → 0.9.6.beta

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/lib/sensu/base.rb ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+
3
+ gem 'eventmachine', '~> 1.0.0.beta.4'
4
+
5
+ require 'optparse'
6
+ require 'json'
7
+ require 'cabin'
8
+ require 'amqp'
9
+
10
+ require File.join(File.dirname(__FILE__), 'patches', 'ruby')
11
+ require File.join(File.dirname(__FILE__), 'patches', 'amqp')
12
+
13
+ require File.join(File.dirname(__FILE__), 'constants')
14
+ require File.join(File.dirname(__FILE__), 'cli')
15
+ require File.join(File.dirname(__FILE__), 'logger')
16
+ require File.join(File.dirname(__FILE__), 'settings')
17
+ require File.join(File.dirname(__FILE__), 'process')
18
+
19
+ module Sensu
20
+ class Base
21
+ attr_reader :options, :logger, :settings
22
+
23
+ def initialize(options={})
24
+ @options = DEFAULT_OPTIONS.merge(options)
25
+ @logger = Cabin::Channel.get
26
+ setup_logging
27
+ setup_settings
28
+ setup_process
29
+ end
30
+
31
+ def setup_logging
32
+ Sensu::Logger.new(@options)
33
+ end
34
+
35
+ def setup_settings
36
+ @settings = Sensu::Settings.new
37
+ @settings.load_env
38
+ @settings.load_file(@options[:config_file])
39
+ Dir[@options[:config_dir] + '/**/*.json'].each do |file|
40
+ @settings.load_file(file)
41
+ end
42
+ begin
43
+ @settings.validate
44
+ rescue => error
45
+ @logger.fatal('CONFIG INVALID', {
46
+ :error => error.to_s
47
+ })
48
+ @logger.fatal('SENSU NOT RUNNING!')
49
+ exit 2
50
+ end
51
+ end
52
+
53
+ def setup_process
54
+ Sensu::Process.new(@options)
55
+ end
56
+ end
57
+ end
data/lib/sensu/cli.rb ADDED
@@ -0,0 +1,37 @@
1
+ module Sensu
2
+ class CLI
3
+ def self.read(arguments=ARGV)
4
+ options = Hash.new
5
+ optparse = OptionParser.new do |opts|
6
+ opts.on('-h', '--help', 'Display this message') do
7
+ puts opts
8
+ exit
9
+ end
10
+ opts.on('-V', '--version', 'Display version') do
11
+ puts VERSION
12
+ exit
13
+ end
14
+ opts.on('-c', '--config FILE', 'Sensu JSON config FILE. Default is /etc/sensu/config.json') do |file|
15
+ options[:config_file] = file
16
+ end
17
+ opts.on('-d', '--config_dir DIR', 'DIR for supplemental Sensu JSON config files. Default is /etc/sensu/conf.d/') do |dir|
18
+ options[:config_dir] = dir
19
+ end
20
+ opts.on('-l', '--log FILE', 'Log to a given FILE. Default is to log to stdout') do |file|
21
+ options[:log_file] = file
22
+ end
23
+ opts.on('-v', '--verbose', 'Enable verbose logging') do
24
+ options[:verbose] = true
25
+ end
26
+ opts.on('-b', '--background', 'Fork into the background') do
27
+ options[:daemonize] = true
28
+ end
29
+ opts.on('-p', '--pid_file FILE', 'Write the PID to a given FILE') do |file|
30
+ options[:pid_file] = file
31
+ end
32
+ end
33
+ optparse.parse!(arguments)
34
+ DEFAULT_OPTIONS.merge(options)
35
+ end
36
+ end
37
+ end
data/lib/sensu/client.rb CHANGED
@@ -1,23 +1,17 @@
1
- require File.join(File.dirname(__FILE__), 'config')
1
+ require File.join(File.dirname(__FILE__), 'base')
2
+ require File.join(File.dirname(__FILE__), 'socket')
2
3
 
3
4
  module Sensu
4
5
  class Client
5
6
  def self.run(options={})
6
7
  client = self.new(options)
7
- if options[:daemonize]
8
- Process.daemonize
9
- end
10
- if options[:pid_file]
11
- Process.write_pid(options[:pid_file])
12
- end
13
- EM::threadpool_size = 14
14
8
  EM::run do
15
9
  client.setup_rabbitmq
16
10
  client.setup_keepalives
17
11
  client.setup_subscriptions
18
12
  client.setup_rabbitmq_monitor
19
13
  client.setup_standalone
20
- client.setup_socket
14
+ client.setup_sockets
21
15
 
22
16
  %w[INT TERM].each do |signal|
23
17
  Signal.trap(signal) do
@@ -28,27 +22,31 @@ module Sensu
28
22
  end
29
23
 
30
24
  def initialize(options={})
31
- config = Sensu::Config.new(options)
32
- @logger = config.logger
33
- @settings = config.settings
25
+ base = Sensu::Base.new(options)
26
+ @logger = base.logger
27
+ @settings = base.settings
34
28
  @timers = Array.new
35
29
  @checks_in_progress = Array.new
36
30
  end
37
31
 
38
32
  def setup_rabbitmq
39
- @logger.debug('[rabbitmq] -- connecting to rabbitmq')
40
- @rabbitmq = AMQP.connect(@settings.rabbitmq.to_hash.symbolize_keys)
33
+ @logger.debug('connecting to rabbitmq', {
34
+ :settings => @settings[:rabbitmq]
35
+ })
36
+ @rabbitmq = AMQP.connect(@settings[:rabbitmq])
41
37
  @amq = AMQP::Channel.new(@rabbitmq)
42
38
  end
43
39
 
44
40
  def publish_keepalive
45
- @settings.client.timestamp = Time.now.to_i
46
- @logger.debug('[keepalive] -- publishing keepalive -- ' + @settings.client.timestamp.to_s)
47
- @amq.queue('keepalives').publish(@settings.client.to_json)
41
+ payload = @settings[:client].merge(:timestamp => Time.now.to_i)
42
+ @logger.debug('publishing keepalive', {
43
+ :payload => payload
44
+ })
45
+ @amq.queue('keepalives').publish(payload.to_json)
48
46
  end
49
47
 
50
48
  def setup_keepalives
51
- @logger.debug('[keepalive] -- setup keepalives')
49
+ @logger.debug('scheduling keepalives')
52
50
  publish_keepalive
53
51
  @timers << EM::PeriodicTimer.new(30) do
54
52
  unless @rabbitmq.reconnecting?
@@ -58,111 +56,133 @@ module Sensu
58
56
  end
59
57
 
60
58
  def publish_result(check)
61
- @logger.info('[result] -- publishing check result -- ' + [check.name, check.status, check.output.gsub(/\n/, '\n')].join(' -- '))
62
- @amq.queue('results').publish({
63
- :client => @settings.client.name,
64
- :check => check.to_hash
65
- }.to_json)
59
+ payload = {
60
+ :client => @settings[:client][:name],
61
+ :check => check
62
+ }
63
+ @logger.info('publishing check result', {
64
+ :payload => payload
65
+ })
66
+ @amq.queue('results').publish(payload.to_json)
66
67
  end
67
68
 
68
69
  def execute_check(check)
69
- @logger.debug('[execute] -- attempting to execute check -- ' + check.name)
70
- if @settings.checks.key?(check.name)
71
- unless @checks_in_progress.include?(check.name)
72
- @logger.debug('[execute] -- executing check -- ' + check.name)
73
- @checks_in_progress.push(check.name)
70
+ @logger.debug('attempting to execute check', {
71
+ :check => check
72
+ })
73
+ if @settings.check_exists?(check[:name])
74
+ unless @checks_in_progress.include?(check[:name])
75
+ @logger.debug('executing check', {
76
+ :check => check
77
+ })
78
+ @checks_in_progress.push(check[:name])
74
79
  unmatched_tokens = Array.new
75
- command = @settings.checks[check.name].command.gsub(/:::(.*?):::/) do
80
+ command = @settings[:checks][check[:name]][:command].gsub(/:::(.*?):::/) do
76
81
  token = $1.to_s
77
82
  begin
78
- value = @settings.client.instance_eval(token)
79
- if value.nil?
80
- unmatched_tokens.push(token)
81
- end
82
- rescue NoMethodError
83
- value = nil
83
+ substitute = @settings[:client].instance_eval(token)
84
+ rescue NameError, NoMethodError
85
+ substitute = nil
86
+ end
87
+ if substitute.nil?
84
88
  unmatched_tokens.push(token)
85
89
  end
86
- value
90
+ substitute
87
91
  end
88
92
  if unmatched_tokens.empty?
89
93
  execute = proc do
90
- Bundler.with_clean_env do
91
- started = Time.now.to_f
92
- begin
93
- IO.popen(command + ' 2>&1') do |io|
94
- check.output = io.read
95
- end
96
- check.status = $?.exitstatus
97
- rescue => error
98
- @logger.warn('[execute] -- unexpected error: ' + error.to_s)
99
- check.output = 'Unexpected error: ' + error.to_s
100
- check.status = 2
94
+ started = Time.now.to_f
95
+ begin
96
+ IO.popen(command + ' 2>&1') do |io|
97
+ check[:output] = io.read
101
98
  end
102
- check.duration = ('%.3f' % (Time.now.to_f - started)).to_f
99
+ check[:status] = $?.exitstatus
100
+ rescue => error
101
+ @logger.warn('unexpected error', {
102
+ :error => error.to_s
103
+ })
104
+ check[:output] = 'Unexpected error: ' + error.to_s
105
+ check[:status] = 2
103
106
  end
107
+ check[:duration] = ('%.3f' % (Time.now.to_f - started)).to_f
104
108
  end
105
109
  publish = proc do
106
- unless check.status.nil?
110
+ unless check[:status].nil?
107
111
  publish_result(check)
108
112
  else
109
- @logger.warn('[execute] -- nil exit status code -- ' + check.name)
113
+ @logger.warn('nil exit status code', {
114
+ :check => check
115
+ })
110
116
  end
111
- @checks_in_progress.delete(check.name)
117
+ @checks_in_progress.delete(check[:name])
112
118
  end
113
119
  EM::defer(execute, publish)
114
120
  else
115
- @logger.warn('[execute] -- missing client attributes -- ' + unmatched_tokens.join(', ') + ' -- ' + check.name)
116
- check.output = 'Missing client attributes: ' + unmatched_tokens.join(', ')
117
- check.status = 3
118
- check.handle = false
121
+ @logger.warn('missing client attributes', {
122
+ :check => check,
123
+ :unmatched_tokens => unmatched_tokens
124
+ })
125
+ check[:output] = 'Missing client attributes: ' + unmatched_tokens.join(', ')
126
+ check[:status] = 3
127
+ check[:handle] = false
119
128
  publish_result(check)
120
- @checks_in_progress.delete(check.name)
129
+ @checks_in_progress.delete(check[:name])
121
130
  end
122
131
  else
123
- @logger.warn('[execute] -- previous check execution still in progress -- ' + check.name)
132
+ @logger.warn('previous check execution in progress', {
133
+ :check => check
134
+ })
124
135
  end
125
136
  else
126
- @logger.warn('[execute] -- unkown check -- ' + check.name)
127
- check.output = 'Unknown check'
128
- check.status = 3
129
- check.handle = false
137
+ @logger.warn('unknown check', {
138
+ :check => check
139
+ })
140
+ check[:output] = 'Unknown check'
141
+ check[:status] = 3
142
+ check[:handle] = false
130
143
  publish_result(check)
131
- @checks_in_progress.delete(check.name)
144
+ @checks_in_progress.delete(check[:name])
132
145
  end
133
146
  end
134
147
 
135
148
  def setup_subscriptions
136
- @logger.debug('[subscribe] -- setup subscriptions')
137
- @uniq_queue_name ||= rand(36**32).to_s(36)
149
+ @logger.debug('subscribing to client subscriptions')
150
+ @uniq_queue_name ||= rand(36 ** 32).to_s(36)
138
151
  @check_request_queue = @amq.queue(@uniq_queue_name, :auto_delete => true)
139
- @settings.client.subscriptions.uniq.each do |exchange|
140
- @logger.debug('[subscribe] -- queue binding to exchange -- ' + exchange)
141
- @check_request_queue.bind(@amq.fanout(exchange))
152
+ @settings[:client][:subscriptions].uniq.each do |exchange_name|
153
+ @logger.debug('binding queue to exchange', {
154
+ :queue => @uniq_queue_name,
155
+ :exchange => {
156
+ :name => exchange_name,
157
+ :type => 'fanout'
158
+ }
159
+ })
160
+ @check_request_queue.bind(@amq.fanout(exchange_name))
142
161
  end
143
- @check_request_queue.subscribe do |check_request_json|
162
+ @check_request_queue.subscribe do |payload|
144
163
  begin
145
- check = Hashie::Mash.new(JSON.parse(check_request_json))
146
- if check.name.is_a?(String) && check.issued.is_a?(Integer)
147
- @logger.info('[subscribe] -- received check request -- ' + check.name)
148
- execute_check(check)
149
- else
150
- @logger.warn('[subscribe] -- invalid check request: ' + check_request_json)
151
- end
164
+ check = JSON.parse(payload, :symbolize_names => true)
165
+ @logger.info('received check request', {
166
+ :check => check
167
+ })
168
+ execute_check(check)
152
169
  rescue JSON::ParserError => error
153
- @logger.warn('[subscribe] -- check request must be valid json: ' + error.to_s)
170
+ @logger.warn('check request payload must be valid json', {
171
+ :payload => payload,
172
+ :error => error.to_s
173
+ })
154
174
  end
155
175
  end
156
176
  end
157
177
 
158
178
  def setup_rabbitmq_monitor
159
- @logger.debug('[monitor] -- setup rabbitmq monitor')
179
+ @logger.debug('monitoring rabbitmq connection')
160
180
  @timers << EM::PeriodicTimer.new(5) do
161
181
  if @rabbitmq.reconnecting?
162
- @logger.warn('[monitor] -- reconnecting to rabbitmq')
182
+ @logger.warn('reconnecting to rabbitmq')
163
183
  else
164
184
  unless @check_request_queue.subscribed?
165
- @logger.warn('[monitor] -- re-subscribing to client subscriptions')
185
+ @logger.warn('re-subscribing to client subscriptions')
166
186
  setup_subscriptions
167
187
  end
168
188
  end
@@ -170,18 +190,17 @@ module Sensu
170
190
  end
171
191
 
172
192
  def setup_standalone(options={})
173
- @logger.debug('[standalone] -- setup standalone checks')
193
+ @logger.debug('scheduling standalone checks')
174
194
  standalone_check_count = 0
175
- @settings.checks.each do |name, details|
176
- if details.standalone
195
+ stagger = options[:test] ? 0 : 7
196
+ @settings.checks.each do |check|
197
+ if check[:standalone]
177
198
  standalone_check_count += 1
178
- check = Hashie::Mash.new(details.merge(:name => name))
179
- stagger = options[:test] ? 0 : 7
180
- @timers << EM::Timer.new(stagger*standalone_check_count) do
181
- interval = options[:test] ? 0.5 : details.interval
199
+ @timers << EM::Timer.new(stagger * standalone_check_count) do
200
+ interval = options[:test] ? 0.5 : check[:interval]
182
201
  @timers << EM::PeriodicTimer.new(interval) do
183
202
  unless @rabbitmq.reconnecting?
184
- check.issued = Time.now.to_i
203
+ check[:issued] = Time.now.to_i
185
204
  execute_check(check)
186
205
  end
187
206
  end
@@ -190,24 +209,34 @@ module Sensu
190
209
  end
191
210
  end
192
211
 
193
- def setup_socket
194
- @logger.debug('[socket] -- starting up client tcp socket')
195
- EM::start_server('127.0.0.1', 3030, ClientSocket) do |socket|
212
+ def setup_sockets
213
+ @logger.debug('binding client tcp socket')
214
+ EM::start_server('127.0.0.1', 3030, Sensu::Socket) do |socket|
215
+ socket.protocol = :tcp
216
+ socket.logger = @logger
196
217
  socket.settings = @settings
218
+ socket.amq = @amq
219
+ end
220
+ @logger.debug('binding client udp socket')
221
+ EM::open_datagram_socket('127.0.0.1', 3030, Sensu::Socket) do |socket|
222
+ socket.protocol = :udp
197
223
  socket.logger = @logger
224
+ socket.settings = @settings
198
225
  socket.amq = @amq
199
226
  end
200
227
  end
201
228
 
202
229
  def stop_reactor
203
- @logger.info('[stop] -- completing checks in progress')
230
+ @logger.info('completing checks in progress', {
231
+ :checks_in_progress => @checks_in_progress
232
+ })
204
233
  complete_in_progress = EM::tick_loop do
205
234
  if @checks_in_progress.empty?
206
235
  :stop
207
236
  end
208
237
  end
209
238
  complete_in_progress.on_stop do
210
- @logger.warn('[stop] -- stopping reactor')
239
+ @logger.warn('stopping reactor')
211
240
  EM::PeriodicTimer.new(0.25) do
212
241
  EM::stop_event_loop
213
242
  end
@@ -215,12 +244,15 @@ module Sensu
215
244
  end
216
245
 
217
246
  def stop(signal)
218
- @logger.warn('[stop] -- stopping sensu client -- ' + signal)
247
+ @logger.warn('received signal', {
248
+ :signal => signal
249
+ })
250
+ @logger.warn('stopping')
219
251
  @timers.each do |timer|
220
252
  timer.cancel
221
253
  end
222
254
  unless @rabbitmq.reconnecting?
223
- @logger.warn('[stop] -- unsubscribing from client subscriptions')
255
+ @logger.warn('unsubscribing from client subscriptions')
224
256
  @check_request_queue.unsubscribe do
225
257
  stop_reactor
226
258
  end
@@ -229,39 +261,4 @@ module Sensu
229
261
  end
230
262
  end
231
263
  end
232
-
233
- class ClientSocket < EM::Connection
234
- attr_accessor :settings, :logger, :amq
235
-
236
- def receive_data(data)
237
- if data == 'ping'
238
- @logger.debug('[socket] -- received ping')
239
- send_data('pong')
240
- else
241
- @logger.debug('[socket] -- received data -- ' + data)
242
- begin
243
- check = Hashie::Mash.new(JSON.parse(data))
244
- validates = %w[name output].all? do |key|
245
- check[key].is_a?(String)
246
- end
247
- check.issued = Time.now.to_i
248
- check.status ||= 0
249
- if validates && check.status.is_a?(Integer)
250
- @logger.info('[socket] -- publishing check result -- ' + [check.name, check.status, check.output.gsub(/\n/, '\n')].join(' -- '))
251
- @amq.queue('results').publish({
252
- :client => @settings.client.name,
253
- :check => check.to_hash
254
- }.to_json)
255
- send_data('ok')
256
- else
257
- @logger.warn('[socket] -- check name and output must be strings, status defaults to 0 -- e.g. {"name": "x", "output": "y"}')
258
- send_data('invalid')
259
- end
260
- rescue JSON::ParserError => error
261
- @logger.warn('[socket] -- check result must be valid json: ' + error.to_s)
262
- send_data('invalid')
263
- end
264
- end
265
- end
266
- end
267
264
  end