sensu 1.0.4 → 1.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -0
- data/README.md +4 -4
- data/lib/sensu/api/http_handler.rb +2 -20
- data/lib/sensu/api/routes/results.rb +3 -1
- data/lib/sensu/client/http_socket.rb +20 -13
- data/lib/sensu/client/process.rb +83 -8
- data/lib/sensu/constants.rb +1 -1
- data/lib/sensu/daemon.rb +1 -1
- data/lib/sensu/server/handle.rb +2 -1
- data/lib/sensu/server/process.rb +187 -63
- data/sensu.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed1314dc2d888635865717c37c4e71d77da17b28
|
4
|
+
data.tar.gz: 5f90de786a5691adf798879beca63a4d11732f38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ad6541d3bba0982e9b0f397349f29889fc597a294ffdaad6e7dba49aa8af8830745d30a45061adbb67385a21ec2248ca8dc32640b73700f8278671280edb021
|
7
|
+
data.tar.gz: 60f3e3a064bcdcc8255ba3f0a52d4798ddbbf2a67b9588048b43e1b648ed1ebfe4d0cd747947df4980ea9616003e006937b9c6d99647e27db1ae3970667722e7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,61 @@
|
|
1
|
+
## 1.1.0 - 2017-09-27
|
2
|
+
|
3
|
+
### Features
|
4
|
+
|
5
|
+
Check hooks, commands run by the Sensu client in response to the result of
|
6
|
+
the check command execution. The Sensu client will execute the appropriate
|
7
|
+
configured hook command, depending on the check execution status (e.g. 1).
|
8
|
+
Valid hook names include (in order of precedence): "1"-"255", "ok",
|
9
|
+
"warning", "critical", "unknown", and "non-zero". The check hook command
|
10
|
+
output, status, executed timestamp, and duration are captured and
|
11
|
+
published in the check result. Check hook commands can optionally receive
|
12
|
+
JSON serialized Sensu client and check definition data via STDIN.
|
13
|
+
|
14
|
+
Check STDIN. A boolean check definition attribute, `"stdin"`, when set to
|
15
|
+
`true` instructs the Sensu client to write JSON serialized Sensu client
|
16
|
+
and check definition data to the check command process STDIN. This
|
17
|
+
attribute cannot be used with existing Sensu check plugins, nor Nagios
|
18
|
+
plugins etc, as the Sensu client will wait indefinitely for the check
|
19
|
+
process to read and close STDIN.
|
20
|
+
|
21
|
+
Splayed proxy check request publishing. Users can now splay proxy check
|
22
|
+
requests (optional), evenly, over a window of time, determined by the
|
23
|
+
check interval and a configurable splay coverage percentage. For
|
24
|
+
example, if a check has an interval of 60s and a configured splay
|
25
|
+
coverage of 90%, its proxy check requests would be splayed evenly over a
|
26
|
+
time window of 60s * 90%, 54s, leaving 6s for the last proxy check
|
27
|
+
execution before the the next round of proxy check requests for the same
|
28
|
+
check. Proxy check request splayed publishing can be configured with two
|
29
|
+
new check definition attributes, within the `proxy_requests` scope,
|
30
|
+
`splay` (boolean) to enable it and `splay_coverage` (integer percentage,
|
31
|
+
defaults to `90`).
|
32
|
+
|
33
|
+
Configurable check output truncation (for storage in Redis). Check output
|
34
|
+
truncation can be manually enabled/disabled with the check definition
|
35
|
+
attribute "truncate_output", e.g.`"truncate_output": false`. The output
|
36
|
+
truncation length can be configured with the check definition attribute
|
37
|
+
"truncate_output_length", e.g. `"truncate_output_length": 1024`. Check
|
38
|
+
output truncation is still enabled by default for metric checks, with
|
39
|
+
`"type": "metric"`.
|
40
|
+
|
41
|
+
Sensu client HTTP socket basic authentication can how be applied to all
|
42
|
+
endpoints (not just `/settings`), via the client definition http_socket
|
43
|
+
attribute "protect_all_endpoints", e.g. `"protect_all_endpoints": true`.
|
44
|
+
|
45
|
+
### Other
|
46
|
+
|
47
|
+
Improved check TTL monitoring performance.
|
48
|
+
|
49
|
+
The Sensu extension run log event log level is now set to debug (instead
|
50
|
+
of info) when the run output is empty and the status is 0.
|
51
|
+
|
52
|
+
### Fixes
|
53
|
+
|
54
|
+
Added initial timestamp to proxy client definitions. The Uchiwa and Sensu
|
55
|
+
dashboards will no longer display "Invalid Date".
|
56
|
+
|
57
|
+
Deleting check history when deleting an associated check result.
|
58
|
+
|
1
59
|
## 1.0.3 - 2017-08-25
|
2
60
|
|
3
61
|
### Fixes
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|

|
2
2
|
|
3
|
-
[](https://travis-ci.org/sensu/sensu)
|
4
4
|
[](https://github.com/sensu/sensu/blob/master/CHANGELOG.md)
|
5
|
-

|
5
|
+
[](https://raw.githubusercontent.com/sensu/sensu/master/MIT-LICENSE.txt)
|
6
6
|
|
7
7
|
# Sensu
|
8
8
|
|
@@ -27,7 +27,7 @@ on configuring and operating Sensu.
|
|
27
27
|
## Getting Help
|
28
28
|
|
29
29
|
If you have questions not covered by the documentation, the Sensu community
|
30
|
-
is here to help. Please check out our [chat][4] on
|
30
|
+
is here to help. Please check out our [chat][4] on Slack, or the
|
31
31
|
[sensu-users][5] discussion list.
|
32
32
|
|
33
33
|
Commercial support is also available. See the [support section of our website][6] for more detail.
|
@@ -45,7 +45,7 @@ Sensu Core is released under the [MIT license][8].
|
|
45
45
|
[1]: https://sensuapp.org/enterprise
|
46
46
|
[2]: https://sensuapp.org/docs/latest/platforms/
|
47
47
|
[3]: http://sensuapp.org/docs/latest/overview
|
48
|
-
[4]:
|
48
|
+
[4]: https://slack.sensu.io/
|
49
49
|
[5]: http://groups.google.com/group/sensu-users
|
50
50
|
[6]: https://sensuapp.org/support
|
51
51
|
[7]: https://github.com/sensu/sensu/blob/master/CONTRIBUTING.md
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require "sensu/utilities"
|
2
1
|
require "sensu/api/routes"
|
3
2
|
require "sensu/api/utilities/filter_response_content"
|
4
3
|
|
@@ -22,11 +21,9 @@ module Sensu
|
|
22
21
|
# @result [Hash]
|
23
22
|
def request_details
|
24
23
|
return @request_details if @request_details
|
25
|
-
@request_id = @http.fetch(:x_request_id, random_uuid)
|
26
24
|
@request_start_time = Time.now.to_f
|
27
25
|
_, remote_address = Socket.unpack_sockaddr_in(get_peername)
|
28
26
|
@request_details = {
|
29
|
-
:request_id => @request_id,
|
30
27
|
:remote_address => remote_address,
|
31
28
|
:user_agent => @http[:user_agent],
|
32
29
|
:method => @http_request_method,
|
@@ -47,9 +44,7 @@ module Sensu
|
|
47
44
|
end
|
48
45
|
|
49
46
|
# Log the HTTP response. This method calculates the
|
50
|
-
# request/response time.
|
51
|
-
# response body log event, as it is generally very verbose and
|
52
|
-
# unnecessary in most cases.
|
47
|
+
# request/response time.
|
53
48
|
def log_response
|
54
49
|
@logger.info("api response", {
|
55
50
|
:request => request_details,
|
@@ -57,12 +52,6 @@ module Sensu
|
|
57
52
|
:content_length => @response.content.to_s.bytesize,
|
58
53
|
:time => (Time.now.to_f - @request_start_time).round(3)
|
59
54
|
})
|
60
|
-
@logger.debug("api response body", {
|
61
|
-
:request => {
|
62
|
-
:request_id => @request_id
|
63
|
-
},
|
64
|
-
:content => @response.content
|
65
|
-
})
|
66
55
|
end
|
67
56
|
|
68
57
|
# Parse the HTTP request URI using a regular expression,
|
@@ -159,13 +148,6 @@ module Sensu
|
|
159
148
|
end
|
160
149
|
end
|
161
150
|
|
162
|
-
# Set the HTTP response headers, including the request ID and
|
163
|
-
# cors headers (via `set_cores_headers()`).
|
164
|
-
def set_headers
|
165
|
-
@response.headers["X-Request-ID"] = @request_id
|
166
|
-
set_cors_headers
|
167
|
-
end
|
168
|
-
|
169
151
|
# Paginate the provided items. This method uses two HTTP query
|
170
152
|
# parameters to determine how to paginate the items, `limit` and
|
171
153
|
# `offset`. The parameter `limit` specifies how many items are
|
@@ -410,7 +392,7 @@ module Sensu
|
|
410
392
|
log_request
|
411
393
|
parse_parameters
|
412
394
|
create_response
|
413
|
-
|
395
|
+
set_cors_headers
|
414
396
|
if authorized?
|
415
397
|
if connected?
|
416
398
|
route_request
|
@@ -87,6 +87,14 @@ module Sensu
|
|
87
87
|
false
|
88
88
|
end
|
89
89
|
|
90
|
+
def unauthorized_response
|
91
|
+
@logger.warn("http socket refusing to serve unauthorized request")
|
92
|
+
@response.headers["WWW-Authenticate"] = 'Basic realm="Sensu Client Restricted Area"'
|
93
|
+
send_response(401, "Unauthorized", {
|
94
|
+
:response => "You must be authenticated using your http_options user and password settings"
|
95
|
+
})
|
96
|
+
end
|
97
|
+
|
90
98
|
def send_response(status, status_string, content)
|
91
99
|
@logger.debug("http socket sending response", {
|
92
100
|
:status => status,
|
@@ -100,6 +108,9 @@ module Sensu
|
|
100
108
|
end
|
101
109
|
|
102
110
|
def process_request_info
|
111
|
+
if @settings[:client][:http_socket][:protect_all_endpoints]
|
112
|
+
return unauthorized_response unless authorized?
|
113
|
+
end
|
103
114
|
transport_info do |info|
|
104
115
|
send_response(200, "OK", {
|
105
116
|
:sensu => {
|
@@ -111,6 +122,9 @@ module Sensu
|
|
111
122
|
end
|
112
123
|
|
113
124
|
def process_request_results
|
125
|
+
if @settings[:client][:http_socket][:protect_all_endpoints]
|
126
|
+
return unauthorized_response unless authorized?
|
127
|
+
end
|
114
128
|
if @http[:content_type] and @http[:content_type] == "application/json" and @http_content
|
115
129
|
begin
|
116
130
|
check = Sensu::JSON::load(@http_content)
|
@@ -125,19 +139,12 @@ module Sensu
|
|
125
139
|
end
|
126
140
|
|
127
141
|
def process_request_settings
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
else
|
133
|
-
send_response(200, "OK", redact_sensitive(@settings.to_hash))
|
134
|
-
end
|
142
|
+
return unauthorized_response unless authorized?
|
143
|
+
@logger.info("http socket responding to request for configuration settings")
|
144
|
+
if @http_query_string and @http_query_string.downcase.include?("redacted=false")
|
145
|
+
send_response(200, "OK", @settings.to_hash)
|
135
146
|
else
|
136
|
-
|
137
|
-
@response.headers["WWW-Authenticate"] = 'Basic realm="Sensu Client Restricted Area"'
|
138
|
-
send_response(401, "Unauthorized", {
|
139
|
-
:response => "You must be authenticated using your http_options user and password settings"
|
140
|
-
})
|
147
|
+
send_response(200, "OK", redact_sensitive(@settings.to_hash))
|
141
148
|
end
|
142
149
|
end
|
143
150
|
|
@@ -181,7 +188,7 @@ module Sensu
|
|
181
188
|
:http_request_uri => @http_request_uri
|
182
189
|
})
|
183
190
|
send_response(405, "Method Not Allowed", {
|
184
|
-
:response => "Valid methods for this endpoint: #{
|
191
|
+
:response => "Valid methods for this endpoint: #{endpoint['methods'].keys}"
|
185
192
|
})
|
186
193
|
end
|
187
194
|
else
|
data/lib/sensu/client/process.rb
CHANGED
@@ -110,13 +110,66 @@ module Sensu
|
|
110
110
|
[check[:source], check[:name]].compact.join(":")
|
111
111
|
end
|
112
112
|
|
113
|
+
# Execute a check hook, capturing its output (STDOUT/ERR),
|
114
|
+
# exit status code, executed timestamp, and duration. This
|
115
|
+
# method determines which hook command to run by inspecting the
|
116
|
+
# check execution status. Check hook command tokens are
|
117
|
+
# substituted with the associated client attribute values, via
|
118
|
+
# `substitute_tokens()`. If there are unmatched check attribute
|
119
|
+
# value tokens, the check hook will not be executed, instead the
|
120
|
+
# hook command output will be set to report the unmatched
|
121
|
+
# tokens. Hook commands may expect/read and utilize JSON
|
122
|
+
# serialized Sensu client and check data via STDIN, if the hook
|
123
|
+
# definition includes `"stdin": true` (default is `false`). A
|
124
|
+
# hook may have a configured execution timeout, e.g. `"timeout":
|
125
|
+
# 30`, if one is not specified, the timeout defaults to 60
|
126
|
+
# seconds.
|
127
|
+
#
|
128
|
+
# @param check [Hash]
|
129
|
+
# @yield [check] callback/block called after executing the check
|
130
|
+
# hook (if any).
|
131
|
+
def execute_check_hook(check)
|
132
|
+
@logger.debug("attempting to execute check hook", :check => check)
|
133
|
+
severity = SEVERITIES[check[:status]] || "unknown"
|
134
|
+
hook = check[:hooks][check[:status].to_s] || check[:hooks][severity.to_sym]
|
135
|
+
if hook.nil? && check[:status] != 0
|
136
|
+
hook = check[:hooks]["non-zero"]
|
137
|
+
end
|
138
|
+
if hook
|
139
|
+
command, unmatched_tokens = substitute_tokens(hook[:command].dup, @settings[:client])
|
140
|
+
started = Time.now.to_f
|
141
|
+
hook[:executed] = started.to_i
|
142
|
+
if unmatched_tokens.empty?
|
143
|
+
options = {:timeout => hook.fetch(:timeout, 60)}
|
144
|
+
if hook[:stdin]
|
145
|
+
options[:data] = Sensu::JSON.dump({
|
146
|
+
:client => @settings[:client],
|
147
|
+
:check => check
|
148
|
+
})
|
149
|
+
end
|
150
|
+
Spawn.process(command, options) do |output, status|
|
151
|
+
hook[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
152
|
+
hook[:output] = output
|
153
|
+
hook[:status] = status
|
154
|
+
yield(check)
|
155
|
+
end
|
156
|
+
else
|
157
|
+
hook[:output] = "Unmatched client token(s): " + unmatched_tokens.join(", ")
|
158
|
+
hook[:status] = 3
|
159
|
+
yield(check)
|
160
|
+
end
|
161
|
+
else
|
162
|
+
yield(check)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
113
166
|
# Execute a check command, capturing its output (STDOUT/ERR),
|
114
167
|
# exit status code, execution duration, timestamp, and publish
|
115
168
|
# the result. This method guards against multiple executions for
|
116
169
|
# the same check. Check attribute value tokens are substituted
|
117
170
|
# with the associated client attribute values, via
|
118
|
-
# `object_substitute_tokens()`. The original check command
|
119
|
-
# always published, to guard against publishing
|
171
|
+
# `object_substitute_tokens()`. The original check command and
|
172
|
+
# hooks are always published, to guard against publishing
|
120
173
|
# sensitive/redacted client attribute values. If there are
|
121
174
|
# unmatched check attribute value tokens, the check will not be
|
122
175
|
# executed, instead a check result will be published reporting
|
@@ -129,16 +182,31 @@ module Sensu
|
|
129
182
|
unless @checks_in_progress.include?(in_progress_key)
|
130
183
|
@checks_in_progress << in_progress_key
|
131
184
|
substituted, unmatched_tokens = object_substitute_tokens(check.dup, @settings[:client])
|
132
|
-
check = substituted.merge(:command => check[:command])
|
185
|
+
check = substituted.merge(:command => check[:command], :hooks => check[:hooks])
|
186
|
+
check.delete(:hooks) if check[:hooks].nil?
|
133
187
|
started = Time.now.to_f
|
134
188
|
check[:executed] = started.to_i
|
135
189
|
if unmatched_tokens.empty?
|
136
|
-
|
190
|
+
options = {:timeout => check[:timeout]}
|
191
|
+
if check[:stdin]
|
192
|
+
options[:data] = Sensu::JSON.dump({
|
193
|
+
:client => @settings[:client],
|
194
|
+
:check => check
|
195
|
+
})
|
196
|
+
end
|
197
|
+
Spawn.process(substituted[:command], options) do |output, status|
|
137
198
|
check[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
138
199
|
check[:output] = output
|
139
200
|
check[:status] = status
|
140
|
-
|
141
|
-
|
201
|
+
if check[:hooks] && !check[:hooks].empty?
|
202
|
+
execute_check_hook(check) do |check|
|
203
|
+
publish_check_result(check)
|
204
|
+
@checks_in_progress.delete(in_progress_key)
|
205
|
+
end
|
206
|
+
else
|
207
|
+
publish_check_result(check)
|
208
|
+
@checks_in_progress.delete(in_progress_key)
|
209
|
+
end
|
142
210
|
end
|
143
211
|
else
|
144
212
|
check[:output] = "Unmatched client token(s): " + unmatched_tokens.join(", ")
|
@@ -178,8 +246,15 @@ module Sensu
|
|
178
246
|
check[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
179
247
|
check[:output] = output
|
180
248
|
check[:status] = status
|
181
|
-
|
182
|
-
|
249
|
+
if check[:hooks] && !check[:hooks].empty?
|
250
|
+
execute_check_hook(check) do |check|
|
251
|
+
publish_check_result(check)
|
252
|
+
@checks_in_progress.delete(in_progress_key)
|
253
|
+
end
|
254
|
+
else
|
255
|
+
publish_check_result(check)
|
256
|
+
@checks_in_progress.delete(in_progress_key)
|
257
|
+
end
|
183
258
|
end
|
184
259
|
else
|
185
260
|
@logger.warn("previous check extension execution in progress", :check => check)
|
data/lib/sensu/constants.rb
CHANGED
data/lib/sensu/daemon.rb
CHANGED
data/lib/sensu/server/handle.rb
CHANGED
@@ -126,7 +126,8 @@ module Sensu
|
|
126
126
|
# @param event_id [String] event UUID
|
127
127
|
def handler_extension(handler, event_data, event_id)
|
128
128
|
handler.safe_run(event_data) do |output, status|
|
129
|
-
|
129
|
+
log_level = (output.empty? && status.zero?) ? :debug : :info
|
130
|
+
@logger.send(log_level, "handler extension output", {
|
130
131
|
:extension => handler.definition,
|
131
132
|
:event => { :id => event_id },
|
132
133
|
:output => output,
|
data/lib/sensu/server/process.rb
CHANGED
@@ -320,16 +320,17 @@ module Sensu
|
|
320
320
|
end
|
321
321
|
end
|
322
322
|
|
323
|
-
# Truncate check output.
|
324
|
-
# "
|
325
|
-
#
|
326
|
-
#
|
323
|
+
# Truncate check output. Metric checks (`"type": "metric"`), or
|
324
|
+
# checks with `"truncate_output": true`, have their output
|
325
|
+
# truncated to a single line and a maximum character length of
|
326
|
+
# 255 by default. The maximum character length can be change by
|
327
|
+
# the `"truncate_output_length"` check definition attribute.
|
327
328
|
#
|
328
329
|
# @param check [Hash]
|
329
330
|
# @return [Hash] check with truncated output.
|
330
331
|
def truncate_check_output(check)
|
331
|
-
|
332
|
-
|
332
|
+
if check[:truncate_output] ||
|
333
|
+
(check[:type] == METRIC_CHECK_TYPE && check[:truncate_output] != false)
|
333
334
|
begin
|
334
335
|
output_lines = check[:output].split("\n")
|
335
336
|
rescue ArgumentError
|
@@ -341,8 +342,9 @@ module Sensu
|
|
341
342
|
output_lines = utf8_output.split("\n")
|
342
343
|
end
|
343
344
|
output = output_lines.first || check[:output]
|
344
|
-
|
345
|
-
|
345
|
+
truncate_output_length = check.fetch(:truncate_output_length, 255)
|
346
|
+
if output_lines.length > 1 || output.length > truncate_output_length
|
347
|
+
output = output[0..truncate_output_length] + "\n..."
|
346
348
|
end
|
347
349
|
check.merge(:output => output)
|
348
350
|
else
|
@@ -603,7 +605,8 @@ module Sensu
|
|
603
605
|
:address => "unknown",
|
604
606
|
:subscriptions => ["client:#{name}"],
|
605
607
|
:keepalives => false,
|
606
|
-
:version => VERSION
|
608
|
+
:version => VERSION,
|
609
|
+
:timestamp => Time.now.to_i
|
607
610
|
}
|
608
611
|
end
|
609
612
|
|
@@ -792,45 +795,129 @@ module Sensu
|
|
792
795
|
end
|
793
796
|
end
|
794
797
|
|
798
|
+
# Determine and return clients from the registry that match a
|
799
|
+
# set of attributes.
|
800
|
+
#
|
801
|
+
# @param clients [Array] of client names.
|
802
|
+
# @param attributes [Hash]
|
803
|
+
# @yield [Array] callback/block called after determining the
|
804
|
+
# matching clients, returning them as a block parameter.
|
805
|
+
def determine_matching_clients(clients, attributes)
|
806
|
+
client_keys = clients.map { |client_name| "client:#{client_name}" }
|
807
|
+
@redis.mget(*client_keys) do |client_json_objects|
|
808
|
+
matching_clients = []
|
809
|
+
client_json_objects.each do |client_json|
|
810
|
+
unless client_json.nil?
|
811
|
+
client = Sensu::JSON.load(client_json)
|
812
|
+
if attributes_match?(client, attributes)
|
813
|
+
matching_clients << client
|
814
|
+
end
|
815
|
+
end
|
816
|
+
end
|
817
|
+
yield(matching_clients)
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
# Publish a proxy check request for a client. This method
|
822
|
+
# substitutes client tokens in the check definition prior to
|
823
|
+
# publish the check request. If there are unmatched client
|
824
|
+
# tokens, a warning is logged, and a check request is not
|
825
|
+
# published.
|
826
|
+
#
|
827
|
+
# @param client [Hash] definition.
|
828
|
+
# @param check [Hash] definition.
|
829
|
+
def publish_proxy_check_request(client, check)
|
830
|
+
@logger.debug("creating a proxy check request", {
|
831
|
+
:client => client,
|
832
|
+
:check => check
|
833
|
+
})
|
834
|
+
proxy_check, unmatched_tokens = object_substitute_tokens(check.dup, client)
|
835
|
+
if unmatched_tokens.empty?
|
836
|
+
proxy_check[:source] ||= client[:name]
|
837
|
+
publish_check_request(proxy_check)
|
838
|
+
else
|
839
|
+
@logger.warn("failed to publish a proxy check request", {
|
840
|
+
:reason => "unmatched client tokens",
|
841
|
+
:unmatched_tokens => unmatched_tokens,
|
842
|
+
:client => client,
|
843
|
+
:check => check
|
844
|
+
})
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
# Publish proxy check requests for one or more clients. This
|
849
|
+
# method can optionally splay proxy check requests, evenly, over
|
850
|
+
# a period of time, determined by the check interval and a
|
851
|
+
# configurable splay coverage percentage. For example, splay
|
852
|
+
# proxy check requests over 60s * 90%, 54s, leaving 6s for the
|
853
|
+
# last proxy check execution before the the next round of proxy
|
854
|
+
# check requests for the same check. The
|
855
|
+
# `publish_proxy_check_request() method is used to publish the
|
856
|
+
# proxy check requests.
|
857
|
+
#
|
858
|
+
# @param clients [Array] of client definitions.
|
859
|
+
# @param check [Hash] definition.
|
860
|
+
def publish_proxy_check_requests(clients, check)
|
861
|
+
client_count = clients.length
|
862
|
+
splay = 0
|
863
|
+
if check[:proxy_requests][:splay]
|
864
|
+
interval = check[:interval]
|
865
|
+
if check[:cron]
|
866
|
+
interval = determine_check_cron_time(check)
|
867
|
+
end
|
868
|
+
unless interval.nil?
|
869
|
+
splay_coverage = check[:proxy_requests].fetch(:splay_coverage, 90)
|
870
|
+
splay = interval * (splay_coverage / 100.0) / client_count
|
871
|
+
end
|
872
|
+
end
|
873
|
+
splay_timer = 0
|
874
|
+
clients.each do |client|
|
875
|
+
unless splay == 0
|
876
|
+
EM::Timer.new(splay_timer) do
|
877
|
+
publish_proxy_check_request(client, check)
|
878
|
+
end
|
879
|
+
splay_timer += splay
|
880
|
+
else
|
881
|
+
publish_proxy_check_request(client, check)
|
882
|
+
end
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
795
886
|
# Create and publish one or more proxy check requests. This
|
796
887
|
# method iterates through the Sensu client registry for clients
|
797
888
|
# that matched provided proxy request client attributes. A proxy
|
798
889
|
# check request is created for each client in the registry that
|
799
890
|
# matches the proxy request client attributes. Proxy check
|
800
891
|
# requests have their client tokens subsituted by the associated
|
801
|
-
# client attributes values. The
|
802
|
-
#
|
892
|
+
# client attributes values. The `determine_matching_clients()`
|
893
|
+
# method is used to fetch and inspect each slide of clients from
|
894
|
+
# the registry, returning those that match the configured proxy
|
895
|
+
# request client attributes. A relatively small clients slice
|
896
|
+
# size (20) is used to reduce the number of clients inspected
|
897
|
+
# within a single tick of the EM reactor. The
|
898
|
+
# `publish_proxy_check_requests()` method is used to iterate
|
899
|
+
# through the matching Sensu clients, creating their own unique
|
900
|
+
# proxy check request, substituting client tokens, and then
|
901
|
+
# publishing them to the targetted subscriptions.
|
803
902
|
#
|
804
903
|
# @param check [Hash] definition.
|
805
|
-
def
|
904
|
+
def create_proxy_check_requests(check)
|
806
905
|
client_attributes = check[:proxy_requests][:client_attributes]
|
807
906
|
unless client_attributes.empty?
|
808
907
|
@redis.smembers("clients") do |clients|
|
809
|
-
clients.
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
:check => check
|
817
|
-
})
|
818
|
-
proxy_check, unmatched_tokens = object_substitute_tokens(check.dup, client)
|
819
|
-
if unmatched_tokens.empty?
|
820
|
-
proxy_check[:source] ||= client[:name]
|
821
|
-
publish_check_request(proxy_check)
|
822
|
-
else
|
823
|
-
@logger.warn("failed to publish a proxy check request", {
|
824
|
-
:reason => "unmatched client tokens",
|
825
|
-
:unmatched_tokens => unmatched_tokens,
|
826
|
-
:client => client,
|
827
|
-
:check => check
|
828
|
-
})
|
829
|
-
end
|
830
|
-
end
|
908
|
+
client_count = clients.length
|
909
|
+
proxy_check_requests = Proc.new do |matching_clients, slice_start, slice_size|
|
910
|
+
unless slice_start > client_count - 1
|
911
|
+
clients_slice = clients.slice(slice_start..slice_size)
|
912
|
+
determine_matching_clients(clients_slice, client_attributes) do |additional_clients|
|
913
|
+
matching_clients += additional_clients
|
914
|
+
proxy_check_requests.call(matching_clients, slice_start + 20, slice_size + 20)
|
831
915
|
end
|
916
|
+
else
|
917
|
+
publish_proxy_check_requests(matching_clients, check)
|
832
918
|
end
|
833
919
|
end
|
920
|
+
proxy_check_requests.call([], 0, 19)
|
834
921
|
end
|
835
922
|
end
|
836
923
|
end
|
@@ -846,7 +933,7 @@ module Sensu
|
|
846
933
|
Proc.new do
|
847
934
|
unless check_subdued?(check)
|
848
935
|
if check[:proxy_requests]
|
849
|
-
|
936
|
+
create_proxy_check_requests(check)
|
850
937
|
else
|
851
938
|
publish_check_request(check)
|
852
939
|
end
|
@@ -1064,46 +1151,83 @@ module Sensu
|
|
1064
1151
|
end
|
1065
1152
|
end
|
1066
1153
|
|
1154
|
+
# Create check TTL results. This method will retrieve check
|
1155
|
+
# results from the registry and determine the time since their
|
1156
|
+
# last check execution (in seconds). If the time since last
|
1157
|
+
# execution is equal to or greater than the defined check TTL, a
|
1158
|
+
# warning check result is published with the appropriate check
|
1159
|
+
# output.
|
1160
|
+
#
|
1161
|
+
# @param ttl_keys [Array] of TTL keys.
|
1162
|
+
# @param interval [Integer] to use for the check TTL result
|
1163
|
+
# interval.
|
1164
|
+
# @yield [] callback/block called after the check TTL results
|
1165
|
+
# have been created.
|
1166
|
+
def create_check_ttl_results(ttl_keys, interval=30)
|
1167
|
+
result_keys = ttl_keys.map { |ttl_key| "result:#{ttl_key}" }
|
1168
|
+
@redis.mget(*result_keys) do |result_json_objects|
|
1169
|
+
result_json_objects.each_with_index do |result_json, index|
|
1170
|
+
unless result_json.nil?
|
1171
|
+
check = Sensu::JSON.load(result_json)
|
1172
|
+
next unless check[:ttl] && check[:executed] && !check[:force_resolve]
|
1173
|
+
time_since_last_execution = Time.now.to_i - check[:executed]
|
1174
|
+
if time_since_last_execution >= check[:ttl]
|
1175
|
+
client_name = ttl_keys[index].split(":").first
|
1176
|
+
keepalive_event_exists?(client_name) do |event_exists|
|
1177
|
+
unless event_exists
|
1178
|
+
check[:output] = "Last check execution was "
|
1179
|
+
check[:output] << "#{time_since_last_execution} seconds ago"
|
1180
|
+
check[:status] = check[:ttl_status] || 1
|
1181
|
+
check[:interval] = interval
|
1182
|
+
publish_check_result(client_name, check)
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
else
|
1187
|
+
@redis.srem("ttl", result_key)
|
1188
|
+
end
|
1189
|
+
end
|
1190
|
+
yield
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
|
1067
1194
|
# Determine stale check results, those that have not executed in
|
1068
1195
|
# a specified amount of time (check TTL). This method iterates
|
1069
1196
|
# through stored check results that have a defined TTL value (in
|
1070
|
-
# seconds). The
|
1071
|
-
#
|
1072
|
-
# execution
|
1073
|
-
#
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
check[:interval] = interval
|
1091
|
-
publish_check_result(client_name, check)
|
1092
|
-
end
|
1093
|
-
end
|
1094
|
-
end
|
1095
|
-
else
|
1096
|
-
@redis.srem("ttl", result_key)
|
1197
|
+
# seconds). The `create_check_ttl_results()` method is used to
|
1198
|
+
# inspect each check result, calculating their time since last
|
1199
|
+
# check execution (in seconds). If the time since last execution
|
1200
|
+
# is equal to or greater than the check TTL, a warning check
|
1201
|
+
# result is published with the appropriate check output. A
|
1202
|
+
# relatively small check results slice size (20) is used to
|
1203
|
+
# reduce the number of check results inspected within a single
|
1204
|
+
# tick of the EM reactor.
|
1205
|
+
#
|
1206
|
+
# @param interval [Integer] to use for the check TTL result
|
1207
|
+
# interval.
|
1208
|
+
def determine_stale_check_results(interval=30)
|
1209
|
+
@logger.info("determining stale check results (ttl)")
|
1210
|
+
@redis.smembers("ttl") do |ttl_keys|
|
1211
|
+
ttl_key_count = ttl_keys.length
|
1212
|
+
ttl_check_results = Proc.new do |slice_start, slice_size|
|
1213
|
+
unless slice_start > ttl_key_count - 1
|
1214
|
+
ttl_keys_slice = ttl_keys.slice(slice_start..slice_size)
|
1215
|
+
create_check_ttl_results(ttl_keys_slice, interval) do
|
1216
|
+
ttl_check_results.call(slice_start + 20, slice_size + 20)
|
1097
1217
|
end
|
1098
1218
|
end
|
1099
1219
|
end
|
1220
|
+
ttl_check_results.call(0, 19)
|
1100
1221
|
end
|
1101
1222
|
end
|
1102
1223
|
|
1103
1224
|
# Set up the check result monitor, a periodic timer to run
|
1104
1225
|
# `determine_stale_check_results()` every 30 seconds. The timer
|
1105
1226
|
# is stored in the timers hash under `:tasks`.
|
1106
|
-
|
1227
|
+
#
|
1228
|
+
# @param interval [Integer] to use for the check TTL result
|
1229
|
+
# interval.
|
1230
|
+
def setup_check_result_monitor(interval=30)
|
1107
1231
|
@logger.debug("monitoring check results")
|
1108
1232
|
@timers[:tasks][:check_result_monitor] << EM::PeriodicTimer.new(interval) do
|
1109
1233
|
determine_stale_check_results(interval)
|
data/sensu.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.add_dependency "eventmachine", "1.2.5"
|
16
16
|
s.add_dependency "sensu-json", "2.1.0"
|
17
17
|
s.add_dependency "sensu-logger", "1.2.1"
|
18
|
-
s.add_dependency "sensu-settings", "10.
|
18
|
+
s.add_dependency "sensu-settings", "10.9.0"
|
19
19
|
s.add_dependency "sensu-extension", "1.5.1"
|
20
20
|
s.add_dependency "sensu-extensions", "1.9.0"
|
21
21
|
s.add_dependency "sensu-transport", "7.0.2"
|
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: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Porter
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-09-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -59,14 +59,14 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - '='
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: 10.
|
62
|
+
version: 10.9.0
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - '='
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 10.
|
69
|
+
version: 10.9.0
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: sensu-extension
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|