sensu 0.17.2 → 0.18.0.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a0fac0647fef9c4dad920c91d477e0aac3ccefe
4
- data.tar.gz: f0ba6869b8a2e76e9c9a673332a87af8f9b30262
3
+ metadata.gz: 3562490d63bf32fe403bb3395f673b9be6ffd4e4
4
+ data.tar.gz: ba043f59f239c29b9d8353d29ca892c6a3400384
5
5
  SHA512:
6
- metadata.gz: 10ccf9155bbda665fe9c8b590cb93bc413e1825324069d00187d58466dd9a86ad0a6d99608796aabeaa39b3c1daac6b49e3d6612c7b4bf875c1a88c5e61d5186
7
- data.tar.gz: 8ece96457154d1d64fab3516afc527c5690397e84ad2d3c412ae25c1c7b1c467eba9f374da2cde8c4daa9bef1625bdc17ac7ad6b99beb469d6504b7a6db31170
6
+ metadata.gz: 0aae29a7af64355686413c1a4d6f108a1c59276312f31755065a5901faf5a5c508a9514b04e98e9942a651877b1d375a8ab9e3a6ba75e461310dded889c35b50
7
+ data.tar.gz: d0cbbbd0000a734f411a4cdc5a396a40bb281cfcdbd77b009bc61365045de95eef0937a2ff169b25c76cea89b3371cb56a2779cfad31a817d04cccee0b2f57da
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## 0.18.0 - TBD
2
+
3
+ ### Features
4
+
5
+ Dynamic (or JIT) client creation (in the registry) for check results for a
6
+ nonexistent client or a check source. Sensu clients can now monitor an
7
+ external resource on its behalf, using a check `source` to create a JIT
8
+ client for the resource, used to store the execution history and provide
9
+ context within event data. JIT client data in the registry can be
10
+ managed/updated via the Sensu API, POST `/clients`.
11
+
12
+ Storing the latest check result for every client/check pair. This data is
13
+ currently exposed via the API at `/clients/:client/history` and will be
14
+ used by several upcoming features.
15
+
1
16
  ## 0.17.2 - 2015-04-08
2
17
 
3
18
  ### Other
@@ -170,7 +170,9 @@ module Sensu
170
170
  begin
171
171
  data = MultiJson.load(env["rack.input"].read)
172
172
  valid = rules.all? do |key, rule|
173
- data[key].is_a?(rule[:type]) || (rule[:nil_ok] && data[key].nil?)
173
+ value = data[key]
174
+ (value.is_a?(rule[:type]) || (rule[:nil_ok] && value.nil?)) &&
175
+ rule[:regex].nil? || (rule[:regex] && (value =~ rule[:regex]) == 0)
174
176
  end
175
177
  if valid
176
178
  callback.call(data)
@@ -302,6 +304,23 @@ module Sensu
302
304
  end
303
305
  end
304
306
 
307
+ apost "/clients/?" do
308
+ rules = {
309
+ :name => {:type => String, :nil_ok => false, :regex => /^[\w\.-]+$/},
310
+ :address => {:type => String, :nil_ok => false},
311
+ :subscriptions => {:type => Array, :nil_ok => false}
312
+ }
313
+ read_data(rules) do |data|
314
+ data[:keepalives] = false
315
+ data[:timestamp] = Time.now.to_i
316
+ settings.redis.set("client:#{data[:name]}", MultiJson.dump(data)) do
317
+ settings.redis.sadd("clients", data[:name]) do
318
+ created!(MultiJson.dump(:name => data[:name]))
319
+ end
320
+ end
321
+ end
322
+ end
323
+
305
324
  aget "/clients/?" do
306
325
  response = Array.new
307
326
  settings.redis.smembers("clients") do |clients|
@@ -333,22 +352,25 @@ module Sensu
333
352
 
334
353
  aget %r{/clients/([\w\.-]+)/history/?$} do |client_name|
335
354
  response = Array.new
336
- settings.redis.smembers("history:#{client_name}") do |checks|
355
+ settings.redis.smembers("result:#{client_name}") do |checks|
337
356
  unless checks.empty?
338
357
  checks.each_with_index do |check_name, index|
339
- history_key = "history:#{client_name}:#{check_name}"
358
+ result_key = "#{client_name}:#{check_name}"
359
+ history_key = "history:#{result_key}"
340
360
  settings.redis.lrange(history_key, -21, -1) do |history|
341
361
  history.map! do |status|
342
362
  status.to_i
343
363
  end
344
- execution_key = "execution:#{client_name}:#{check_name}"
345
- settings.redis.get(execution_key) do |last_execution|
364
+ settings.redis.get("result:#{result_key}") do |result_json|
365
+ result = MultiJson.load(result_json)
366
+ last_execution = result[:executed]
346
367
  unless history.empty? || last_execution.nil?
347
368
  item = {
348
369
  :check => check_name,
349
370
  :history => history,
350
371
  :last_execution => last_execution.to_i,
351
- :last_status => history.last
372
+ :last_status => history.last,
373
+ :last_result => result
352
374
  }
353
375
  response << item
354
376
  end
@@ -373,18 +395,17 @@ module Sensu
373
395
  end
374
396
  EM::Timer.new(5) do
375
397
  client = MultiJson.load(client_json)
376
- settings.logger.info("deleting client", {
377
- :client => client
378
- })
398
+ settings.logger.info("deleting client", :client => client)
379
399
  settings.redis.srem("clients", client_name) do
380
400
  settings.redis.del("client:#{client_name}")
381
401
  settings.redis.del("events:#{client_name}")
382
- settings.redis.smembers("history:#{client_name}") do |checks|
402
+ settings.redis.smembers("result:#{client_name}") do |checks|
383
403
  checks.each do |check_name|
384
- settings.redis.del("history:#{client_name}:#{check_name}")
385
- settings.redis.del("execution:#{client_name}:#{check_name}")
404
+ result_key = "#{client_name}:#{check_name}"
405
+ settings.redis.del("result:#{result_key}")
406
+ settings.redis.del("history:#{result_key}")
386
407
  end
387
- settings.redis.del("history:#{client_name}")
408
+ settings.redis.del("result:#{client_name}")
388
409
  end
389
410
  end
390
411
  end
@@ -428,7 +449,7 @@ module Sensu
428
449
  :subscribers => subscribers
429
450
  })
430
451
  subscribers.uniq.each do |exchange_name|
431
- settings.transport.publish(:fanout, exchange_name, MultiJson.dump(payload)) do |info|
452
+ settings.transport.publish(:fanout, exchange_name.to_s, MultiJson.dump(payload)) do |info|
432
453
  if info[:error]
433
454
  settings.logger.error("failed to publish check request", {
434
455
  :exchange_name => exchange_name,
@@ -1,7 +1,7 @@
1
1
  module Sensu
2
2
  unless defined?(Sensu::VERSION)
3
3
  # Sensu release version.
4
- VERSION = "0.17.2"
4
+ VERSION = "0.18.0.beta"
5
5
 
6
6
  # Sensu check severities.
7
7
  SEVERITIES = %w[ok warning critical unknown]
data/lib/sensu/daemon.rb CHANGED
@@ -4,7 +4,7 @@ gem "multi_json", "1.11.0"
4
4
 
5
5
  gem "sensu-em", "2.4.1"
6
6
  gem "sensu-logger", "1.0.0"
7
- gem "sensu-settings", "1.3.0"
7
+ gem "sensu-settings", "1.4.0"
8
8
  gem "sensu-extension", "1.1.2"
9
9
  gem "sensu-extensions", "1.2.0"
10
10
  gem "sensu-transport", "2.4.0"
@@ -49,7 +49,7 @@ module Sensu
49
49
  @logger.debug("updating client registry", :client => client)
50
50
  @redis.set("client:#{client[:name]}", MultiJson.dump(client)) do
51
51
  @redis.sadd("clients", client[:name]) do
52
- callback.call
52
+ callback.call(client)
53
53
  end
54
54
  end
55
55
  end
@@ -178,13 +178,16 @@ module Sensu
178
178
  # check execution across a number of Sensu clients. JSON
179
179
  # serialization is used for storing check result data.
180
180
  #
181
- # @param result [Hash]
182
- def aggregate_check_result(result)
183
- @logger.debug("adding check result to aggregate", :result => result)
184
- check = result[:check]
181
+ # @param client [Hash]
182
+ # @param check [Hash]
183
+ def aggregate_check_result(client, check)
184
+ @logger.debug("adding check result to aggregate", {
185
+ :client => client,
186
+ :check => check
187
+ })
185
188
  result_set = "#{check[:name]}:#{check[:issued]}"
186
189
  result_data = MultiJson.dump(:output => check[:output], :status => check[:status])
187
- @redis.hset("aggregation:#{result_set}", result[:client], result_data) do
190
+ @redis.hset("aggregation:#{result_set}", client[:name], result_data) do
188
191
  SEVERITIES.each do |severity|
189
192
  @redis.hsetnx("aggregate:#{result_set}", severity, 0)
190
193
  end
@@ -199,31 +202,34 @@ module Sensu
199
202
  end
200
203
  end
201
204
 
202
- # Store check result data. This method stores the 21 most recent
203
- # check result statuses for a client/check pair, this history
204
- # is used for event context and flap detection. The check
205
- # execution timestamp is also stored, to provide an indication
206
- # of how recent the data is.
205
+ # Store check result data. This method stores check result data
206
+ # and the 21 most recent check result statuses for a client/check
207
+ # pair, this history is used for event context and flap detection.
208
+ # The check execution timestamp is also stored, to provide an
209
+ # indication of how recent the data is.
207
210
  #
208
211
  # @param client [Hash]
209
212
  # @param check [Hash]
210
213
  # @param callback [Proc] to call when the check result data has
211
214
  # been stored (history, etc).
212
215
  def store_check_result(client, check, &callback)
213
- @redis.sadd("history:#{client[:name]}", check[:name])
216
+ @logger.debug("storing check result", :check => check)
217
+ @redis.sadd("result:#{client[:name]}", check[:name])
214
218
  result_key = "#{client[:name]}:#{check[:name]}"
215
- history_key = "history:#{result_key}"
216
- @redis.rpush(history_key, check[:status]) do
217
- @redis.set("execution:#{result_key}", check[:executed])
218
- @redis.ltrim(history_key, -21, -1)
219
- callback.call
219
+ check_truncated = check.merge(:output => check[:output][0..256])
220
+ @redis.set("result:#{result_key}", MultiJson.dump(check_truncated)) do
221
+ history_key = "history:#{result_key}"
222
+ @redis.rpush(history_key, check[:status]) do
223
+ @redis.ltrim(history_key, -21, -1)
224
+ callback.call
225
+ end
220
226
  end
221
227
  end
222
228
 
223
229
  # Fetch the execution history for a client/check pair, the 21
224
230
  # most recent check result statuses. This method also calculates
225
231
  # the total state change percentage for the history, this value
226
- # is use for check state flat detection, using a similar
232
+ # is use for check state flap detection, using a similar
227
233
  # algorithm to Nagios:
228
234
  # http://nagios.sourceforge.net/docs/3_0/flapping.html
229
235
  #
@@ -332,39 +338,73 @@ module Sensu
332
338
  end
333
339
  end
334
340
 
341
+ # Create a blank client (data) and add it to the client
342
+ # registry. Only the client name is known, the other client
343
+ # attributes must be updated via the API (POST /clients:client).
344
+ # Dynamically created clients and those updated via the API will
345
+ # have client keepalives disabled, `:keepalives` is set to
346
+ # `false`.
347
+ #
348
+ # @param name [Hash] to use for the client.
349
+ # @param callback [Proc] to be called with the dynamically
350
+ # created client data.
351
+ def create_client(name, &callback)
352
+ client = {
353
+ :name => name,
354
+ :address => "unknown",
355
+ :subscriptions => [],
356
+ :keepalives => false
357
+ }
358
+ update_client_registry(client, &callback)
359
+ end
360
+
361
+ # Retrieve a client (data) from Redis if it exists. If a client
362
+ # does not already exist, create one (a blank) using the
363
+ # `client_key` as the client name. Dynamically create client
364
+ # data can be updated using the API (POST /clients/:client).
365
+ #
366
+ # @param result [Hash] data.
367
+ # @param callback [Proc] to be called with client data, either
368
+ # retrieved from Redis, or dynamically created.
369
+ def retrieve_client(result, &callback)
370
+ client_key = result[:check][:source] || result[:client]
371
+ @redis.get("client:#{client_key}") do |client_json|
372
+ unless client_json.nil?
373
+ client = MultiJson.load(client_json)
374
+ callback.call(client)
375
+ else
376
+ create_client(client_key, &callback)
377
+ end
378
+ end
379
+ end
380
+
335
381
  # Process a check result, storing its data, inspecting its
336
382
  # contents, and taking the appropriate actions (eg. update the
337
383
  # event registry). A check result must have a valid client name,
338
- # associated with a client in the registry. Results without a
339
- # valid client are discarded, to keep the system "correct". If a
340
- # local check definition exists for the check name, and the
341
- # check result is not from a standalone check execution, it's
342
- # merged with the check result for more context.
384
+ # associated with a client in the registry or one will be
385
+ # created. If a local check definition exists for the check
386
+ # name, and the check result is not from a standalone check
387
+ # execution, it's merged with the check result for more context.
343
388
  #
344
389
  # @param result [Hash] data.
345
390
  def process_check_result(result)
346
391
  @logger.debug("processing result", :result => result)
347
- @redis.get("client:#{result[:client]}") do |client_json|
348
- unless client_json.nil?
349
- client = MultiJson.load(client_json)
350
- check = case
351
- when @settings.check_exists?(result[:check][:name]) && !result[:check][:standalone]
352
- @settings[:checks][result[:check][:name]].merge(result[:check])
353
- else
354
- result[:check]
355
- end
356
- aggregate_check_result(result) if check[:aggregate]
357
- store_check_result(client, check) do
358
- check_history(client, check) do |history, total_state_change|
359
- check[:history] = history
360
- check[:total_state_change] = total_state_change
361
- update_event_registry(client, check) do |event|
362
- process_event(event)
363
- end
392
+ retrieve_client(result) do |client|
393
+ check = case
394
+ when @settings.check_exists?(result[:check][:name]) && !result[:check][:standalone]
395
+ @settings[:checks][result[:check][:name]].merge(result[:check])
396
+ else
397
+ result[:check]
398
+ end
399
+ aggregate_check_result(client, check) if check[:aggregate]
400
+ store_check_result(client, check) do
401
+ check_history(client, check) do |history, total_state_change|
402
+ check[:history] = history
403
+ check[:total_state_change] = total_state_change
404
+ update_event_registry(client, check) do |event|
405
+ process_event(event)
364
406
  end
365
407
  end
366
- else
367
- @logger.warn("client not in registry", :client => result[:client])
368
408
  end
369
409
  end
370
410
  end
@@ -549,6 +589,7 @@ module Sensu
549
589
  @redis.get("client:#{client_name}") do |client_json|
550
590
  unless client_json.nil?
551
591
  client = MultiJson.load(client_json)
592
+ next if client[:keepalives] == false
552
593
  check = create_keepalive_check(client)
553
594
  time_since_last_keepalive = Time.now.to_i - client[:timestamp]
554
595
  check[:output] = "No keepalive sent from client for "
data/sensu.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency "eventmachine", "1.0.3"
20
20
  s.add_dependency "sensu-em", "2.4.1"
21
21
  s.add_dependency "sensu-logger", "1.0.0"
22
- s.add_dependency "sensu-settings", "1.3.0"
22
+ s.add_dependency "sensu-settings", "1.4.0"
23
23
  s.add_dependency "sensu-extension", "1.1.2"
24
24
  s.add_dependency "sensu-extensions", "1.2.0"
25
25
  s.add_dependency "sensu-transport", "2.4.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sensu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.2
4
+ version: 0.18.0.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Porter
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-04-08 00:00:00.000000000 Z
12
+ date: 2015-04-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -87,14 +87,14 @@ dependencies:
87
87
  requirements:
88
88
  - - '='
89
89
  - !ruby/object:Gem::Version
90
- version: 1.3.0
90
+ version: 1.4.0
91
91
  type: :runtime
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - '='
96
96
  - !ruby/object:Gem::Version
97
- version: 1.3.0
97
+ version: 1.4.0
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: sensu-extension
100
100
  requirement: !ruby/object:Gem::Requirement
@@ -297,9 +297,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
297
297
  version: '0'
298
298
  required_rubygems_version: !ruby/object:Gem::Requirement
299
299
  requirements:
300
- - - ">="
300
+ - - ">"
301
301
  - !ruby/object:Gem::Version
302
- version: '0'
302
+ version: 1.3.1
303
303
  requirements: []
304
304
  rubyforge_project:
305
305
  rubygems_version: 2.2.2