sensu 0.12.6 → 0.13.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sensu/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'optparse'
2
+ require 'sensu/logger/constants'
2
3
 
3
4
  module Sensu
4
5
  class CLI
@@ -27,7 +28,7 @@ module Sensu
27
28
  end
28
29
  opts.on('-L', '--log_level LEVEL', 'Log severity LEVEL') do |level|
29
30
  log_level = level.to_s.downcase.to_sym
30
- unless LOG_LEVELS.include?(log_level)
31
+ unless Logger::LEVELS.include?(log_level)
31
32
  puts 'Unknown log level: ' + level.to_s
32
33
  exit 1
33
34
  end
data/lib/sensu/client.rb CHANGED
@@ -1,9 +1,9 @@
1
- require File.join(File.dirname(__FILE__), 'base')
2
- require File.join(File.dirname(__FILE__), 'socket')
1
+ require 'sensu/daemon'
2
+ require 'sensu/socket'
3
3
 
4
4
  module Sensu
5
5
  class Client
6
- include Utilities
6
+ include Daemon
7
7
 
8
8
  attr_accessor :safe_mode
9
9
 
@@ -11,63 +11,40 @@ module Sensu
11
11
  client = self.new(options)
12
12
  EM::run do
13
13
  client.start
14
- client.trap_signals
14
+ client.setup_signal_traps
15
15
  end
16
16
  end
17
17
 
18
18
  def initialize(options={})
19
- base = Base.new(options)
20
- @logger = base.logger
21
- @settings = base.settings
22
- @extensions = base.extensions
23
- base.setup_process
24
- @extensions.load_settings(@settings.to_hash)
25
- @timers = Array.new
26
- @checks_in_progress = Array.new
19
+ super
27
20
  @safe_mode = @settings[:client][:safe_mode] || false
28
- end
29
-
30
- def setup_rabbitmq
31
- @logger.debug('connecting to rabbitmq', {
32
- :settings => @settings[:rabbitmq]
33
- })
34
- @rabbitmq = RabbitMQ.connect(@settings[:rabbitmq])
35
- @rabbitmq.on_error do |error|
36
- @logger.fatal('rabbitmq connection error', {
37
- :error => error.to_s
38
- })
39
- stop
40
- end
41
- @rabbitmq.before_reconnect do
42
- @logger.warn('reconnecting to rabbitmq')
43
- end
44
- @rabbitmq.after_reconnect do
45
- @logger.info('reconnected to rabbitmq')
46
- end
47
- @amq = @rabbitmq.channel
21
+ @checks_in_progress = Array.new
48
22
  end
49
23
 
50
24
  def publish_keepalive
51
- keepalive = @settings[:client].merge(:timestamp => Time.now.to_i)
25
+ keepalive = @settings[:client].merge({
26
+ :version => VERSION,
27
+ :timestamp => Time.now.to_i
28
+ })
52
29
  payload = redact_sensitive(keepalive, @settings[:client][:redact])
53
30
  @logger.debug('publishing keepalive', {
54
31
  :payload => payload
55
32
  })
56
- begin
57
- @amq.direct('keepalives').publish(Oj.dump(payload))
58
- rescue AMQ::Client::ConnectionClosedError => error
59
- @logger.error('failed to publish keepalive', {
60
- :payload => payload,
61
- :error => error.to_s
62
- })
33
+ @transport.publish(:direct, 'keepalives', MultiJson.dump(payload)) do |info|
34
+ if info[:error]
35
+ @logger.error('failed to publish keepalive', {
36
+ :payload => payload,
37
+ :error => info[:error].to_s
38
+ })
39
+ end
63
40
  end
64
41
  end
65
42
 
66
43
  def setup_keepalives
67
44
  @logger.debug('scheduling keepalives')
68
45
  publish_keepalive
69
- @timers << EM::PeriodicTimer.new(20) do
70
- if @rabbitmq.connected?
46
+ @timers[:run] << EM::PeriodicTimer.new(20) do
47
+ unless @state == :paused
71
48
  publish_keepalive
72
49
  end
73
50
  end
@@ -81,19 +58,19 @@ module Sensu
81
58
  @logger.info('publishing check result', {
82
59
  :payload => payload
83
60
  })
84
- begin
85
- @amq.direct('results').publish(Oj.dump(payload))
86
- rescue AMQ::Client::ConnectionClosedError => error
87
- @logger.error('failed to publish check result', {
88
- :payload => payload,
89
- :error => error.to_s
90
- })
61
+ @transport.publish(:direct, 'results', MultiJson.dump(payload)) do |info|
62
+ if info[:error]
63
+ @logger.error('failed to publish check result', {
64
+ :payload => payload,
65
+ :error => info[:error].to_s
66
+ })
67
+ end
91
68
  end
92
69
  end
93
70
 
94
71
  def substitute_command_tokens(check)
95
72
  unmatched_tokens = Array.new
96
- substituted = check[:command].gsub(/:::(.*?):::/) do
73
+ substituted = check[:command].gsub(/:::([^:]*?):::/) do
97
74
  token, default = $1.to_s.split('|', -1)
98
75
  matched = token.split('.').inject(@settings[:client]) do |client, attribute|
99
76
  if client[attribute].nil?
@@ -117,27 +94,16 @@ module Sensu
117
94
  unless @checks_in_progress.include?(check[:name])
118
95
  @checks_in_progress << check[:name]
119
96
  command, unmatched_tokens = substitute_command_tokens(check)
120
- check[:executed] = Time.now.to_i
121
97
  if unmatched_tokens.empty?
122
- execute = Proc.new do
123
- @logger.debug('executing check command', {
124
- :check => check
125
- })
126
- started = Time.now.to_f
127
- begin
128
- check[:output], check[:status] = IO.popen(command, 'r', check[:timeout])
129
- rescue => error
130
- check[:output] = 'Unexpected error: ' + error.to_s
131
- check[:status] = 2
132
- end
98
+ check[:executed] = Time.now.to_i
99
+ started = Time.now.to_f
100
+ Spawn.process(command, :timeout => check[:timeout]) do |output, status|
133
101
  check[:duration] = ('%.3f' % (Time.now.to_f - started)).to_f
134
- check
135
- end
136
- publish = Proc.new do |check|
102
+ check[:output] = output
103
+ check[:status] = status
137
104
  publish_result(check)
138
105
  @checks_in_progress.delete(check[:name])
139
106
  end
140
- EM::defer(execute, publish)
141
107
  else
142
108
  check[:output] = 'Unmatched command tokens: ' + unmatched_tokens.join(', ')
143
109
  check[:status] = 3
@@ -195,16 +161,13 @@ module Sensu
195
161
 
196
162
  def setup_subscriptions
197
163
  @logger.debug('subscribing to client subscriptions')
198
- @check_request_queue = @amq.queue('', :auto_delete => true) do |queue|
199
- @settings[:client][:subscriptions].each do |exchange_name|
200
- @logger.debug('binding queue to exchange', {
201
- :queue_name => queue.name,
202
- :exchange_name => exchange_name
203
- })
204
- queue.bind(@amq.fanout(exchange_name))
205
- end
206
- queue.subscribe do |payload|
207
- check = Oj.load(payload)
164
+ @settings[:client][:subscriptions].each do |subscription|
165
+ @logger.debug('subscribing to a subscription', {
166
+ :subscription => subscription
167
+ })
168
+ funnel = [@settings[:client][:name], VERSION, Time.now.to_i].join('-')
169
+ @transport.subscribe(:fanout, subscription, funnel) do |message_info, message|
170
+ check = MultiJson.load(message)
208
171
  @logger.info('received check request', {
209
172
  :check => check
210
173
  })
@@ -219,12 +182,12 @@ module Sensu
219
182
  checks.each do |check|
220
183
  check_count += 1
221
184
  scheduling_delay = stagger * check_count % 30
222
- @timers << EM::Timer.new(scheduling_delay) do
185
+ @timers[:run] << EM::Timer.new(scheduling_delay) do
223
186
  interval = testing? ? 0.5 : check[:interval]
224
- @timers << EM::PeriodicTimer.new(interval) do
225
- if @rabbitmq.connected?
187
+ @timers[:run] << EM::PeriodicTimer.new(interval) do
188
+ unless @state == :paused
226
189
  check[:issued] = Time.now.to_i
227
- process_check(check)
190
+ process_check(check.dup)
228
191
  end
229
192
  end
230
193
  end
@@ -252,27 +215,16 @@ module Sensu
252
215
  EM::start_server(options[:bind], options[:port], Socket) do |socket|
253
216
  socket.logger = @logger
254
217
  socket.settings = @settings
255
- socket.amq = @amq
218
+ socket.transport = @transport
256
219
  end
257
220
  EM::open_datagram_socket(options[:bind], options[:port], Socket) do |socket|
258
221
  socket.logger = @logger
259
222
  socket.settings = @settings
260
- socket.amq = @amq
223
+ socket.transport = @transport
261
224
  socket.reply = false
262
225
  end
263
226
  end
264
227
 
265
- def unsubscribe
266
- @logger.warn('unsubscribing from client subscriptions')
267
- if @rabbitmq.connected?
268
- @check_request_queue.unsubscribe
269
- else
270
- @check_request_queue.before_recovery do
271
- @check_request_queue.unsubscribe
272
- end
273
- end
274
- end
275
-
276
228
  def complete_checks_in_progress(&block)
277
229
  @logger.info('completing checks in progress', {
278
230
  :checks_in_progress => @checks_in_progress
@@ -286,43 +238,23 @@ module Sensu
286
238
  end
287
239
 
288
240
  def start
289
- setup_rabbitmq
241
+ setup_transport
290
242
  setup_keepalives
291
243
  setup_subscriptions
292
244
  setup_standalone
293
245
  setup_sockets
246
+ super
294
247
  end
295
248
 
296
249
  def stop
297
250
  @logger.warn('stopping')
298
- @timers.each do |timer|
251
+ @timers[:run].each do |timer|
299
252
  timer.cancel
300
253
  end
301
- unsubscribe
254
+ @transport.unsubscribe
302
255
  complete_checks_in_progress do
303
- @extensions.stop_all do
304
- @rabbitmq.close
305
- @logger.warn('stopping reactor')
306
- EM::stop_event_loop
307
- end
308
- end
309
- end
310
-
311
- def trap_signals
312
- @signals = Array.new
313
- STOP_SIGNALS.each do |signal|
314
- Signal.trap(signal) do
315
- @signals << signal
316
- end
317
- end
318
- EM::PeriodicTimer.new(1) do
319
- signal = @signals.shift
320
- if STOP_SIGNALS.include?(signal)
321
- @logger.warn('received signal', {
322
- :signal => signal
323
- })
324
- stop
325
- end
256
+ @transport.close
257
+ super
326
258
  end
327
259
  end
328
260
  end
@@ -1,12 +1,6 @@
1
1
  module Sensu
2
2
  unless defined?(Sensu::VERSION)
3
- VERSION = '0.12.6'
4
-
5
- LOG_LEVELS = [:debug, :info, :warn, :error, :fatal]
6
-
7
- SETTINGS_CATEGORIES = [:checks, :filters, :mutators, :handlers]
8
-
9
- EXTENSION_CATEGORIES = [:generics, :checks, :mutators, :handlers]
3
+ VERSION = '0.13.0.alpha'
10
4
 
11
5
  SEVERITIES = %w[ok warning critical unknown]
12
6
 
@@ -0,0 +1,221 @@
1
+ require 'rubygems'
2
+
3
+ gem 'multi_json', '1.10.1'
4
+
5
+ gem 'sensu-em', '2.0.0'
6
+ gem 'sensu-logger', '0.0.1'
7
+ gem 'sensu-settings', '0.0.4'
8
+ gem 'sensu-extension', '0.0.3'
9
+ gem 'sensu-extensions', '0.0.4'
10
+ gem 'sensu-transport', '0.0.2'
11
+ gem 'sensu-spawn', '0.0.3'
12
+
13
+ require 'time'
14
+ require 'uri'
15
+
16
+ require 'sensu/logger'
17
+ require 'sensu/settings'
18
+ require 'sensu/extensions'
19
+ require 'sensu/transport'
20
+ require 'sensu/spawn'
21
+
22
+ require 'sensu/constants'
23
+ require 'sensu/utilities'
24
+ require 'sensu/cli'
25
+ require 'sensu/redis'
26
+
27
+ MultiJson.load_options = {:symbolize_keys => true}
28
+
29
+ module Sensu
30
+ module Daemon
31
+ include Utilities
32
+
33
+ attr_reader :state
34
+
35
+ def initialize(options={})
36
+ @state = :initializing
37
+ @timers = {
38
+ :run => Array.new
39
+ }
40
+ setup_logger(options)
41
+ load_settings(options)
42
+ load_extensions(options)
43
+ setup_process(options)
44
+ end
45
+
46
+ def setup_logger(options={})
47
+ @logger = Logger.get(options)
48
+ @logger.setup_signal_traps
49
+ end
50
+
51
+ def log_concerns(concerns=[], level=:warn)
52
+ concerns.each do |concern|
53
+ message = concern.delete(:message)
54
+ @logger.send(level, message, redact_sensitive(concern))
55
+ end
56
+ end
57
+
58
+ def load_settings(options={})
59
+ @settings = Settings.load(options)
60
+ log_concerns(@settings.warnings)
61
+ failures = @settings.validate
62
+ unless failures.empty?
63
+ @logger.fatal('invalid settings')
64
+ log_concerns(failures, :fatal)
65
+ @logger.fatal('SENSU NOT RUNNING!')
66
+ exit 2
67
+ end
68
+ end
69
+
70
+ def load_extensions(options={})
71
+ @extensions = Extensions.load(options)
72
+ log_concerns(@extensions.warnings)
73
+ extension_settings = @settings.to_hash.dup
74
+ @extensions.all.each do |extension|
75
+ extension.logger = @logger
76
+ extension.settings = extension_settings
77
+ end
78
+ end
79
+
80
+ def setup_process(options)
81
+ if options[:daemonize]
82
+ daemonize
83
+ end
84
+ if options[:pid_file]
85
+ write_pid(options[:pid_file])
86
+ end
87
+ end
88
+
89
+ def start
90
+ @state = :running
91
+ end
92
+
93
+ def pause
94
+ @state = :paused
95
+ end
96
+
97
+ def resume
98
+ @state = :running
99
+ end
100
+
101
+ def stop
102
+ @state = :stopped
103
+ @logger.warn('stopping reactor')
104
+ EM::stop_event_loop
105
+ end
106
+
107
+ def setup_signal_traps
108
+ @signals = Array.new
109
+ STOP_SIGNALS.each do |signal|
110
+ Signal.trap(signal) do
111
+ @signals << signal
112
+ end
113
+ end
114
+ EM::PeriodicTimer.new(1) do
115
+ signal = @signals.shift
116
+ if STOP_SIGNALS.include?(signal)
117
+ @logger.warn('received signal', {
118
+ :signal => signal
119
+ })
120
+ stop
121
+ end
122
+ end
123
+ end
124
+
125
+ def setup_transport
126
+ if @settings[:transport].is_a?(Hash)
127
+ transport_name = @settings[:transport][:name]
128
+ end
129
+ transport_name ||= 'rabbitmq'
130
+ transport_settings = @settings[transport_name.to_sym]
131
+ @logger.debug('connecting to transport', {
132
+ :name => transport_name,
133
+ :settings => transport_settings
134
+ })
135
+ @transport = Transport.connect(transport_name, transport_settings)
136
+ @transport.logger = @logger
137
+ @transport.on_error do |error|
138
+ @logger.fatal('transport connection error', {
139
+ :error => error.to_s
140
+ })
141
+ stop
142
+ end
143
+ @transport.before_reconnect do
144
+ unless testing?
145
+ @logger.warn('reconnecting to transport')
146
+ pause
147
+ end
148
+ end
149
+ @transport.after_reconnect do
150
+ @logger.info('reconnected to transport')
151
+ resume
152
+ end
153
+ end
154
+
155
+ def setup_redis
156
+ @logger.debug('connecting to redis', {
157
+ :settings => @settings[:redis]
158
+ })
159
+ @redis = Redis.connect(@settings[:redis])
160
+ @redis.on_error do |error|
161
+ @logger.fatal('redis connection error', {
162
+ :error => error.to_s
163
+ })
164
+ stop
165
+ end
166
+ @redis.before_reconnect do
167
+ unless testing?
168
+ @logger.warn('reconnecting to redis')
169
+ pause
170
+ end
171
+ end
172
+ @redis.after_reconnect do
173
+ @logger.info('reconnected to redis')
174
+ resume
175
+ end
176
+ end
177
+
178
+ private
179
+
180
+ def write_pid(file)
181
+ begin
182
+ File.open(file, 'w') do |pid_file|
183
+ pid_file.puts(Process.pid)
184
+ end
185
+ rescue
186
+ @logger.fatal('could not write to pid file', {
187
+ :pid_file => file
188
+ })
189
+ @logger.fatal('SENSU NOT RUNNING!')
190
+ exit 2
191
+ end
192
+ end
193
+
194
+ def daemonize
195
+ Kernel.srand
196
+ if Kernel.fork
197
+ exit
198
+ end
199
+ unless Process.setsid
200
+ @logger.fatal('cannot detach from controlling terminal')
201
+ @logger.fatal('SENSU NOT RUNNING!')
202
+ exit 2
203
+ end
204
+ Signal.trap('SIGHUP', 'IGNORE')
205
+ if Kernel.fork
206
+ exit
207
+ end
208
+ Dir.chdir('/')
209
+ ObjectSpace.each_object(IO) do |io|
210
+ unless [STDIN, STDOUT, STDERR].include?(io)
211
+ begin
212
+ unless io.closed?
213
+ io.close
214
+ end
215
+ rescue
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end