sensu 0.24.1-java → 0.25.0.beta-java
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/lib/sensu/api/http_handler.rb +340 -0
- data/lib/sensu/api/process.rb +59 -947
- data/lib/sensu/api/routes.rb +72 -0
- data/lib/sensu/api/routes/aggregates.rb +191 -0
- data/lib/sensu/api/routes/checks.rb +25 -0
- data/lib/sensu/api/routes/clients.rb +152 -0
- data/lib/sensu/api/routes/events.rb +76 -0
- data/lib/sensu/api/routes/health.rb +34 -0
- data/lib/sensu/api/routes/info.rb +28 -0
- data/lib/sensu/api/routes/request.rb +33 -0
- data/lib/sensu/api/routes/resolve.rb +31 -0
- data/lib/sensu/api/routes/results.rb +116 -0
- data/lib/sensu/api/routes/stashes.rb +102 -0
- data/lib/sensu/api/utilities/publish_check_request.rb +57 -0
- data/lib/sensu/api/utilities/publish_check_result.rb +36 -0
- data/lib/sensu/api/utilities/resolve_event.rb +29 -0
- data/lib/sensu/api/utilities/transport_info.rb +37 -0
- data/lib/sensu/client/process.rb +28 -1
- data/lib/sensu/client/socket.rb +1 -1
- data/lib/sensu/constants.rb +1 -1
- data/lib/sensu/daemon.rb +18 -8
- data/lib/sensu/server/process.rb +13 -12
- data/sensu.gemspec +1 -3
- metadata +62 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 048445f25b420a9b822e62b06dfba2997bc88877
|
4
|
+
data.tar.gz: 2648250dabf344c8f21310fc42a68f9080305218
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf83b9bc65ce7f35b98e51e2be32c8fd7e54af7f1a973f4531a5eeb6ae9b3408afe0a901279639ac841ded21a237569a1c5d8610c9f22bc678265b5066e169b9
|
7
|
+
data.tar.gz: 9c883f93e1ee1f3f7433a8e940c986b7aaf7d75b1825ea9bed5638045640686b59e7dc54eb7b4a79dcc198101da7a7d707a116fc673cc8eb5fcbfd91e6f330fe
|
@@ -0,0 +1,340 @@
|
|
1
|
+
require "sensu/api/validators"
|
2
|
+
require "sensu/api/routes"
|
3
|
+
|
4
|
+
gem "em-http-server", "0.1.8"
|
5
|
+
|
6
|
+
require "em-http-server"
|
7
|
+
require "base64"
|
8
|
+
|
9
|
+
module Sensu
|
10
|
+
module API
|
11
|
+
class HTTPHandler < EM::HttpServer::Server
|
12
|
+
include Routes
|
13
|
+
|
14
|
+
attr_accessor :logger, :settings, :redis, :transport
|
15
|
+
|
16
|
+
# Log the HTTP request. This method determines the remote
|
17
|
+
# address for the HTTP client, `@remote_address`. The debug log
|
18
|
+
# level is used for requests as response logging includes the
|
19
|
+
# same information.
|
20
|
+
def log_request
|
21
|
+
_, @remote_address = Socket.unpack_sockaddr_in(get_peername)
|
22
|
+
@logger.debug("api request", {
|
23
|
+
:remote_address => @remote_address,
|
24
|
+
:user_agent => @http[:user_agent],
|
25
|
+
:request_method => @http_request_method,
|
26
|
+
:request_uri => @http_request_uri,
|
27
|
+
:request_query_string => @http_query_string,
|
28
|
+
:request_body => @http_content
|
29
|
+
})
|
30
|
+
end
|
31
|
+
|
32
|
+
# Log the HTTP response.
|
33
|
+
def log_response
|
34
|
+
@logger.info("api response", {
|
35
|
+
:remote_address => @remote_address,
|
36
|
+
:user_agent => @http[:user_agent],
|
37
|
+
:request_method => @http_request_method,
|
38
|
+
:request_uri => @http_request_uri,
|
39
|
+
:request_query_string => @http_query_string,
|
40
|
+
:request_body => @http_content,
|
41
|
+
:response_status => @response.status,
|
42
|
+
:response_body => @response.content
|
43
|
+
})
|
44
|
+
end
|
45
|
+
|
46
|
+
# Parse the HTTP query string for parameters. This method
|
47
|
+
# creates `@params`, a hash of parsed query parameters, used by
|
48
|
+
# the API routes.
|
49
|
+
def parse_parameters
|
50
|
+
@params = {}
|
51
|
+
if @http_query_string
|
52
|
+
@http_query_string.split("&").each do |pair|
|
53
|
+
key, value = pair.split("=")
|
54
|
+
@params[key.to_sym] = value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Determine if a parameter has an integer value and if so return
|
60
|
+
# it as one. This method will return `nil` if the parameter
|
61
|
+
# value is not an integer.
|
62
|
+
#
|
63
|
+
# @param value [String]
|
64
|
+
# @return [Integer, nil]
|
65
|
+
def integer_parameter(value)
|
66
|
+
value =~ /\A[0-9]+\z/ ? value.to_i : nil
|
67
|
+
end
|
68
|
+
|
69
|
+
# Read JSON data from the HTTP request content and validate it
|
70
|
+
# with the provided rules. If the HTTP request content does not
|
71
|
+
# contain valid JSON or it does not pass validation, this method
|
72
|
+
# returns a `400` (Bad Request) HTTP response.
|
73
|
+
#
|
74
|
+
# @param rules [Hash] containing the validation rules.
|
75
|
+
# @yield [Object] the callback/block called with the data after successfully
|
76
|
+
# parsing and validating the the HTTP request content.
|
77
|
+
def read_data(rules={})
|
78
|
+
begin
|
79
|
+
data = Sensu::JSON.load(@http_content)
|
80
|
+
valid = data.is_a?(Hash) && rules.all? do |key, rule|
|
81
|
+
value = data[key]
|
82
|
+
(value.is_a?(rule[:type]) || (rule[:nil_ok] && value.nil?)) &&
|
83
|
+
(value.nil? || rule[:regex].nil?) ||
|
84
|
+
(rule[:regex] && (value =~ rule[:regex]) == 0)
|
85
|
+
end
|
86
|
+
if valid
|
87
|
+
yield(data)
|
88
|
+
else
|
89
|
+
bad_request!
|
90
|
+
end
|
91
|
+
rescue Sensu::JSON::ParseError
|
92
|
+
bad_request!
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Create an EM HTTP Server HTTP response object, `@response`.
|
97
|
+
# The response object is use to build up the response status,
|
98
|
+
# status string, content type, and content. The response object
|
99
|
+
# is responsible for sending the HTTP response to the HTTP
|
100
|
+
# client and closing the connection afterwards.
|
101
|
+
#
|
102
|
+
# @return [Object]
|
103
|
+
def create_response
|
104
|
+
@response = EM::DelegatedHttpResponse.new(self)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Set the cors (Cross-origin resource sharing) HTTP headers.
|
108
|
+
def set_cors_headers
|
109
|
+
api = @settings[:api]
|
110
|
+
api[:cors] ||= {
|
111
|
+
"Origin" => "*",
|
112
|
+
"Methods" => "GET, POST, PUT, DELETE, OPTIONS",
|
113
|
+
"Credentials" => "true",
|
114
|
+
"Headers" => "Origin, X-Requested-With, Content-Type, Accept, Authorization"
|
115
|
+
}
|
116
|
+
if api[:cors].is_a?(Hash)
|
117
|
+
api[:cors].each do |header, value|
|
118
|
+
@response.headers["Access-Control-Allow-#{header}"] = value
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Paginate the provided items. This method uses two HTTP query
|
124
|
+
# parameters to determine how to paginate the items, `limit` and
|
125
|
+
# `offset`. The parameter `limit` specifies how many items are
|
126
|
+
# to be returned in the response. The parameter `offset`
|
127
|
+
# specifies the items array index, skipping a number of items.
|
128
|
+
# This method sets the "X-Pagination" HTTP response header to a
|
129
|
+
# JSON object containing the `limit`, `offset` and `total`
|
130
|
+
# number of items that are being paginated.
|
131
|
+
#
|
132
|
+
# @param items [Array]
|
133
|
+
# @return [Array] paginated items.
|
134
|
+
def pagination(items)
|
135
|
+
limit = integer_parameter(@params[:limit])
|
136
|
+
offset = integer_parameter(@params[:offset]) || 0
|
137
|
+
unless limit.nil?
|
138
|
+
@response.headers["X-Pagination"] = Sensu::JSON.dump(
|
139
|
+
:limit => limit,
|
140
|
+
:offset => offset,
|
141
|
+
:total => items.length
|
142
|
+
)
|
143
|
+
paginated = items.slice(offset, limit)
|
144
|
+
Array(paginated)
|
145
|
+
else
|
146
|
+
items
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Respond to an HTTP request. The routes set `@response_status`,
|
151
|
+
# `@response_status_string`, and `@response_content`
|
152
|
+
# appropriately. The HTTP response status defaults to `200` with
|
153
|
+
# the status string `OK`. The Sensu API only returns JSON
|
154
|
+
# response content, `@response_content` is assumed to be a Ruby
|
155
|
+
# object that can be serialized as JSON.
|
156
|
+
def respond
|
157
|
+
@response.status = @response_status || 200
|
158
|
+
@response.status_string = @response_status_string || "OK"
|
159
|
+
if @response_content
|
160
|
+
@response.content_type "application/json"
|
161
|
+
@response.content = Sensu::JSON.dump(@response_content)
|
162
|
+
end
|
163
|
+
log_response
|
164
|
+
@response.send_response
|
165
|
+
end
|
166
|
+
|
167
|
+
# Determine if an HTTP request is authorized. This method
|
168
|
+
# compares the configured API user and password (if any) with
|
169
|
+
# the HTTP request basic authentication credentials. No
|
170
|
+
# authentication is done if the API user and password are not
|
171
|
+
# configured. OPTIONS HTTP requests bypass authentication.
|
172
|
+
#
|
173
|
+
# @return [TrueClass, FalseClass]
|
174
|
+
def authorized?
|
175
|
+
api = @settings[:api]
|
176
|
+
if api && api[:user] && api[:password]
|
177
|
+
if @http_request_method == OPTIONS_METHOD
|
178
|
+
true
|
179
|
+
elsif @http[:authorization]
|
180
|
+
scheme, base64 = @http[:authorization].split("\s")
|
181
|
+
if scheme == "Basic"
|
182
|
+
user, password = Base64.decode64(base64).split(":")
|
183
|
+
user == api[:user] && password == api[:password]
|
184
|
+
else
|
185
|
+
false
|
186
|
+
end
|
187
|
+
else
|
188
|
+
false
|
189
|
+
end
|
190
|
+
else
|
191
|
+
true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Determine if the API is connected to Redis and the Transport.
|
196
|
+
# This method sets the `@response_content` if the API is not
|
197
|
+
# connected or it has not yet initialized the connection
|
198
|
+
# objects. The `/info` and `/health` routes are excluded from
|
199
|
+
# the connectivity checks.
|
200
|
+
def connected?
|
201
|
+
connected = true
|
202
|
+
if @redis && @transport
|
203
|
+
unless @http_request_uri =~ INFO_URI || @http_request_uri =~ HEALTH_URI
|
204
|
+
unless @redis.connected?
|
205
|
+
@response_content = {:error => "not connected to redis"}
|
206
|
+
connected = false
|
207
|
+
end
|
208
|
+
unless @transport.connected?
|
209
|
+
@response_content = {:error => "not connected to transport"}
|
210
|
+
connected = false
|
211
|
+
end
|
212
|
+
end
|
213
|
+
else
|
214
|
+
@response_content = {:error => "redis and transport connections not initialized"}
|
215
|
+
connected = false
|
216
|
+
end
|
217
|
+
connected
|
218
|
+
end
|
219
|
+
|
220
|
+
# Respond to the HTTP request with a `201` (Created) response.
|
221
|
+
def created!
|
222
|
+
@response_status = 201
|
223
|
+
@response_status_string = "Created"
|
224
|
+
respond
|
225
|
+
end
|
226
|
+
|
227
|
+
# Respond to the HTTP request with a `202` (Accepted) response.
|
228
|
+
def accepted!
|
229
|
+
@response_status = 202
|
230
|
+
@response_status_string = "Accepted"
|
231
|
+
respond
|
232
|
+
end
|
233
|
+
|
234
|
+
# Respond to the HTTP request with a `204` (No Response)
|
235
|
+
# response.
|
236
|
+
def no_content!
|
237
|
+
@response_status = 204
|
238
|
+
@response_status_string = "No Response"
|
239
|
+
respond
|
240
|
+
end
|
241
|
+
|
242
|
+
# Respond to the HTTP request with a `400` (Bad Request)
|
243
|
+
# response.
|
244
|
+
def bad_request!
|
245
|
+
@response_status = 400
|
246
|
+
@response_status_string = "Bad Request"
|
247
|
+
respond
|
248
|
+
end
|
249
|
+
|
250
|
+
# Respond to the HTTP request with a `401` (Unauthroized)
|
251
|
+
# response. This method sets the "WWW-Autenticate" HTTP response
|
252
|
+
# header.
|
253
|
+
def unauthorized!
|
254
|
+
@response.headers["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
|
255
|
+
@response_status = 401
|
256
|
+
@response_status_string = "Unauthorized"
|
257
|
+
respond
|
258
|
+
end
|
259
|
+
|
260
|
+
# Respond to the HTTP request with a `404` (Not Found) response.
|
261
|
+
def not_found!
|
262
|
+
@response_status = 404
|
263
|
+
@response_status_string = "Not Found"
|
264
|
+
respond
|
265
|
+
end
|
266
|
+
|
267
|
+
# Respond to the HTTP request with a `412` (Precondition Failed)
|
268
|
+
# response.
|
269
|
+
def precondition_failed!
|
270
|
+
@response_status = 412
|
271
|
+
@response_status_string = "Precondition Failed"
|
272
|
+
respond
|
273
|
+
end
|
274
|
+
|
275
|
+
# Respond to the HTTP request with a `500` (Internal Server
|
276
|
+
# Error) response.
|
277
|
+
def error!
|
278
|
+
@response_status = 500
|
279
|
+
@response_status_string = "Internal Server Error"
|
280
|
+
respond
|
281
|
+
end
|
282
|
+
|
283
|
+
# Route the HTTP request. OPTIONS HTTP requests will always
|
284
|
+
# return a `200` with no response content. The route regular
|
285
|
+
# expressions and associated route method calls are provided by
|
286
|
+
# `ROUTES`. If a route match is not found, this method responds
|
287
|
+
# with a `404` (Not Found) HTTP response.
|
288
|
+
def route_request
|
289
|
+
if @http_request_method == OPTIONS_METHOD
|
290
|
+
respond
|
291
|
+
else
|
292
|
+
route = ROUTES[@http_request_method].detect do |route|
|
293
|
+
@http_request_uri =~ route[0]
|
294
|
+
end
|
295
|
+
unless route.nil?
|
296
|
+
send(route[1])
|
297
|
+
else
|
298
|
+
not_found!
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# Process a HTTP request. Log the request, parse the HTTP query
|
304
|
+
# parameters, create the HTTP response object, set the cors HTTP
|
305
|
+
# response headers, determine if the request is authorized,
|
306
|
+
# determine if the API is connected to Redis and the Transport,
|
307
|
+
# and then route the HTTP request (responding to the request).
|
308
|
+
# This method is called by EM HTTP Server when handling a new
|
309
|
+
# connection.
|
310
|
+
def process_http_request
|
311
|
+
log_request
|
312
|
+
parse_parameters
|
313
|
+
create_response
|
314
|
+
set_cors_headers
|
315
|
+
if authorized?
|
316
|
+
if connected?
|
317
|
+
route_request
|
318
|
+
else
|
319
|
+
error!
|
320
|
+
end
|
321
|
+
else
|
322
|
+
unauthorized!
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Catch uncaught/unexpected errors, log them, and attempt to
|
327
|
+
# respond with a `500` (Internal Server Error) HTTP response.
|
328
|
+
# This method is called by EM HTTP Server.
|
329
|
+
#
|
330
|
+
# @param error [Object]
|
331
|
+
def http_request_errback(error)
|
332
|
+
@logger.error("unexpected api error", {
|
333
|
+
:error => error.to_s,
|
334
|
+
:backtrace => error.backtrace.join("\n")
|
335
|
+
})
|
336
|
+
error! rescue nil
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
data/lib/sensu/api/process.rb
CHANGED
@@ -1,958 +1,70 @@
|
|
1
1
|
require "sensu/daemon"
|
2
|
-
require "sensu/api/
|
3
|
-
|
4
|
-
gem "sinatra", "1.4.6"
|
5
|
-
gem "async_sinatra", "1.2.0"
|
6
|
-
|
7
|
-
unless RUBY_PLATFORM =~ /java/
|
8
|
-
gem "thin", "1.6.4"
|
9
|
-
require "thin"
|
10
|
-
end
|
11
|
-
|
12
|
-
require "sinatra/async"
|
2
|
+
require "sensu/api/http_handler"
|
13
3
|
|
14
4
|
module Sensu
|
15
5
|
module API
|
16
|
-
class Process
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
true
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def start
|
80
|
-
start_server
|
81
|
-
super
|
82
|
-
end
|
83
|
-
|
84
|
-
def stop
|
85
|
-
@logger.warn("stopping")
|
86
|
-
stop_server do
|
87
|
-
settings.redis.close if settings.respond_to?(:redis)
|
88
|
-
settings.transport.close if settings.respond_to?(:transport)
|
89
|
-
super
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def test(options={})
|
94
|
-
bootstrap(options)
|
95
|
-
setup_connections do
|
96
|
-
start
|
6
|
+
class Process
|
7
|
+
include Daemon
|
8
|
+
|
9
|
+
# Create an instance of the Sensu API process, setup the Redis
|
10
|
+
# and Transport connections, start the API HTTP server, set up
|
11
|
+
# API process signal traps (for stopping), within the
|
12
|
+
# EventMachine event loop.
|
13
|
+
#
|
14
|
+
# @param options [Hash]
|
15
|
+
def self.run(options={})
|
16
|
+
api = self.new(options)
|
17
|
+
EM::run do
|
18
|
+
api.setup_redis
|
19
|
+
api.setup_transport
|
20
|
+
api.start
|
21
|
+
api.setup_signal_traps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Start the Sensu API HTTP server. This method sets the service
|
26
|
+
# state to `:running`.
|
27
|
+
def start
|
28
|
+
api = @settings[:api] || {}
|
29
|
+
bind = api[:bind] || "0.0.0.0"
|
30
|
+
port = api[:port] || 4567
|
31
|
+
@logger.info("api listening", {
|
32
|
+
:bind => bind,
|
33
|
+
:port => port
|
34
|
+
})
|
35
|
+
@http_server = EM::start_server(bind, port, HTTPHandler) do |handler|
|
36
|
+
handler.logger = @logger
|
37
|
+
handler.settings = @settings
|
38
|
+
handler.redis = @redis
|
39
|
+
handler.transport = @transport
|
40
|
+
end
|
41
|
+
super
|
42
|
+
end
|
43
|
+
|
44
|
+
# Stop the Sensu API process. This method stops the HTTP server,
|
45
|
+
# closes the Redis and transport connections, sets the service
|
46
|
+
# state to `:stopped`, and stops the EventMachine event loop.
|
47
|
+
def stop
|
48
|
+
@logger.warn("stopping")
|
49
|
+
EM::stop_server(@http_server)
|
50
|
+
@redis.close if @redis
|
51
|
+
@transport.close if @transport
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
# Create an instance of the Sensu API with initialized
|
56
|
+
# connections for running test specs.
|
57
|
+
#
|
58
|
+
# @param options [Hash]
|
59
|
+
def self.test(options={})
|
60
|
+
api = self.new(options)
|
61
|
+
api.setup_redis do
|
62
|
+
api.setup_transport do
|
63
|
+
api.start
|
97
64
|
yield
|
98
65
|
end
|
99
66
|
end
|
100
67
|
end
|
101
|
-
|
102
|
-
configure do
|
103
|
-
disable :protection
|
104
|
-
disable :show_exceptions
|
105
|
-
end
|
106
|
-
|
107
|
-
not_found do
|
108
|
-
""
|
109
|
-
end
|
110
|
-
|
111
|
-
error do
|
112
|
-
""
|
113
|
-
end
|
114
|
-
|
115
|
-
helpers do
|
116
|
-
def request_log_line
|
117
|
-
settings.logger.info([env["REQUEST_METHOD"], env["REQUEST_PATH"]].join(" "), {
|
118
|
-
:remote_address => env["REMOTE_ADDR"],
|
119
|
-
:user_agent => env["HTTP_USER_AGENT"],
|
120
|
-
:request_method => env["REQUEST_METHOD"],
|
121
|
-
:request_uri => env["REQUEST_URI"],
|
122
|
-
:request_body => env["rack.input"].read
|
123
|
-
})
|
124
|
-
env["rack.input"].rewind
|
125
|
-
end
|
126
|
-
|
127
|
-
def connected?
|
128
|
-
if settings.respond_to?(:redis) && settings.respond_to?(:transport)
|
129
|
-
unless ["/info", "/health"].include?(env["REQUEST_PATH"])
|
130
|
-
unless settings.redis.connected?
|
131
|
-
not_connected!("not connected to redis")
|
132
|
-
end
|
133
|
-
unless settings.transport.connected?
|
134
|
-
not_connected!("not connected to transport")
|
135
|
-
end
|
136
|
-
end
|
137
|
-
else
|
138
|
-
not_connected!("redis and transport connections not initialized")
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def authorized?
|
143
|
-
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
144
|
-
@auth.provided? &&
|
145
|
-
@auth.basic? &&
|
146
|
-
@auth.credentials &&
|
147
|
-
@auth.credentials == [settings.api[:user], settings.api[:password]]
|
148
|
-
end
|
149
|
-
|
150
|
-
def protected!
|
151
|
-
if settings.api[:user] && settings.api[:password]
|
152
|
-
return if authorized?
|
153
|
-
headers["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
|
154
|
-
unauthorized!
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def error!(body="")
|
159
|
-
throw(:halt, [500, body])
|
160
|
-
end
|
161
|
-
|
162
|
-
def not_connected!(message)
|
163
|
-
error!(Sensu::JSON.dump(:error => message))
|
164
|
-
end
|
165
|
-
|
166
|
-
def bad_request!
|
167
|
-
ahalt 400
|
168
|
-
end
|
169
|
-
|
170
|
-
def unauthorized!
|
171
|
-
throw(:halt, [401, ""])
|
172
|
-
end
|
173
|
-
|
174
|
-
def not_found!
|
175
|
-
ahalt 404
|
176
|
-
end
|
177
|
-
|
178
|
-
def precondition_failed!
|
179
|
-
ahalt 412
|
180
|
-
end
|
181
|
-
|
182
|
-
def created!(response)
|
183
|
-
status 201
|
184
|
-
body response
|
185
|
-
end
|
186
|
-
|
187
|
-
def accepted!(response)
|
188
|
-
status 202
|
189
|
-
body response
|
190
|
-
end
|
191
|
-
|
192
|
-
def issued!
|
193
|
-
accepted!(Sensu::JSON.dump(:issued => Time.now.to_i))
|
194
|
-
end
|
195
|
-
|
196
|
-
def no_content!
|
197
|
-
status 204
|
198
|
-
body ""
|
199
|
-
end
|
200
|
-
|
201
|
-
def read_data(rules={})
|
202
|
-
begin
|
203
|
-
data = Sensu::JSON.load(env["rack.input"].read)
|
204
|
-
valid = rules.all? do |key, rule|
|
205
|
-
value = data[key]
|
206
|
-
(value.is_a?(rule[:type]) || (rule[:nil_ok] && value.nil?)) &&
|
207
|
-
(value.nil? || rule[:regex].nil?) ||
|
208
|
-
(rule[:regex] && (value =~ rule[:regex]) == 0)
|
209
|
-
end
|
210
|
-
if valid
|
211
|
-
yield(data)
|
212
|
-
else
|
213
|
-
bad_request!
|
214
|
-
end
|
215
|
-
rescue Sensu::JSON::ParseError
|
216
|
-
bad_request!
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def integer_parameter(parameter)
|
221
|
-
parameter =~ /\A[0-9]+\z/ ? parameter.to_i : nil
|
222
|
-
end
|
223
|
-
|
224
|
-
def pagination(items)
|
225
|
-
limit = integer_parameter(params[:limit])
|
226
|
-
offset = integer_parameter(params[:offset]) || 0
|
227
|
-
unless limit.nil?
|
228
|
-
headers["X-Pagination"] = Sensu::JSON.dump(
|
229
|
-
:limit => limit,
|
230
|
-
:offset => offset,
|
231
|
-
:total => items.length
|
232
|
-
)
|
233
|
-
paginated = items.slice(offset, limit)
|
234
|
-
Array(paginated)
|
235
|
-
else
|
236
|
-
items
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def transport_info
|
241
|
-
info = {
|
242
|
-
:keepalives => {
|
243
|
-
:messages => nil,
|
244
|
-
:consumers => nil
|
245
|
-
},
|
246
|
-
:results => {
|
247
|
-
:messages => nil,
|
248
|
-
:consumers => nil
|
249
|
-
},
|
250
|
-
:connected => settings.transport.connected?
|
251
|
-
}
|
252
|
-
if settings.transport.connected?
|
253
|
-
settings.transport.stats("keepalives") do |stats|
|
254
|
-
info[:keepalives] = stats
|
255
|
-
settings.transport.stats("results") do |stats|
|
256
|
-
info[:results] = stats
|
257
|
-
yield(info)
|
258
|
-
end
|
259
|
-
end
|
260
|
-
else
|
261
|
-
yield(info)
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
def publish_check_result(client_name, check)
|
266
|
-
check[:issued] = Time.now.to_i
|
267
|
-
check[:executed] = Time.now.to_i
|
268
|
-
check[:status] ||= 0
|
269
|
-
payload = {
|
270
|
-
:client => client_name,
|
271
|
-
:check => check
|
272
|
-
}
|
273
|
-
settings.logger.info("publishing check result", :payload => payload)
|
274
|
-
settings.transport.publish(:direct, "results", Sensu::JSON.dump(payload)) do |info|
|
275
|
-
if info[:error]
|
276
|
-
settings.logger.error("failed to publish check result", {
|
277
|
-
:payload => payload,
|
278
|
-
:error => info[:error].to_s
|
279
|
-
})
|
280
|
-
end
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
def resolve_event(event_json)
|
285
|
-
event = Sensu::JSON.load(event_json)
|
286
|
-
check = event[:check].merge(
|
287
|
-
:output => "Resolving on request of the API",
|
288
|
-
:status => 0,
|
289
|
-
:force_resolve => true
|
290
|
-
)
|
291
|
-
check.delete(:history)
|
292
|
-
publish_check_result(event[:client][:name], check)
|
293
|
-
end
|
294
|
-
|
295
|
-
def transport_publish_options(subscription, message)
|
296
|
-
_, raw_type = subscription.split(":", 2).reverse
|
297
|
-
case raw_type
|
298
|
-
when "direct", "roundrobin"
|
299
|
-
[:direct, subscription, message]
|
300
|
-
else
|
301
|
-
[:fanout, subscription, message]
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
def publish_check_request(check)
|
306
|
-
payload = check.merge(:issued => Time.now.to_i)
|
307
|
-
settings.logger.info("publishing check request", {
|
308
|
-
:payload => payload,
|
309
|
-
:subscribers => check[:subscribers]
|
310
|
-
})
|
311
|
-
check[:subscribers].each do |subscription|
|
312
|
-
options = transport_publish_options(subscription.to_s, Sensu::JSON.dump(payload))
|
313
|
-
settings.transport.publish(*options) do |info|
|
314
|
-
if info[:error]
|
315
|
-
settings.logger.error("failed to publish check request", {
|
316
|
-
:subscription => subscription,
|
317
|
-
:payload => payload,
|
318
|
-
:error => info[:error].to_s
|
319
|
-
})
|
320
|
-
end
|
321
|
-
end
|
322
|
-
end
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
before do
|
327
|
-
request_log_line
|
328
|
-
content_type "application/json"
|
329
|
-
settings.cors.each do |header, value|
|
330
|
-
headers["Access-Control-Allow-#{header}"] = value
|
331
|
-
end
|
332
|
-
connected?
|
333
|
-
protected! unless env["REQUEST_METHOD"] == "OPTIONS"
|
334
|
-
end
|
335
|
-
|
336
|
-
aoptions "/*" do
|
337
|
-
body ""
|
338
|
-
end
|
339
|
-
|
340
|
-
aget "/info/?" do
|
341
|
-
transport_info do |info|
|
342
|
-
response = {
|
343
|
-
:sensu => {
|
344
|
-
:version => VERSION
|
345
|
-
},
|
346
|
-
:transport => info,
|
347
|
-
:redis => {
|
348
|
-
:connected => settings.redis.connected?
|
349
|
-
}
|
350
|
-
}
|
351
|
-
body Sensu::JSON.dump(response)
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
aget "/health/?" do
|
356
|
-
if settings.redis.connected? && settings.transport.connected?
|
357
|
-
healthy = Array.new
|
358
|
-
min_consumers = integer_parameter(params[:consumers])
|
359
|
-
max_messages = integer_parameter(params[:messages])
|
360
|
-
transport_info do |info|
|
361
|
-
if min_consumers
|
362
|
-
healthy << (info[:keepalives][:consumers] >= min_consumers)
|
363
|
-
healthy << (info[:results][:consumers] >= min_consumers)
|
364
|
-
end
|
365
|
-
if max_messages
|
366
|
-
healthy << (info[:keepalives][:messages] <= max_messages)
|
367
|
-
healthy << (info[:results][:messages] <= max_messages)
|
368
|
-
end
|
369
|
-
healthy.all? ? no_content! : precondition_failed!
|
370
|
-
end
|
371
|
-
else
|
372
|
-
precondition_failed!
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
apost "/clients/?" do
|
377
|
-
read_data do |data|
|
378
|
-
data[:keepalives] = data.fetch(:keepalives, false)
|
379
|
-
data[:version] = VERSION
|
380
|
-
data[:timestamp] = Time.now.to_i
|
381
|
-
validator = Validators::Client.new
|
382
|
-
if validator.valid?(data)
|
383
|
-
settings.redis.set("client:#{data[:name]}", Sensu::JSON.dump(data)) do
|
384
|
-
settings.redis.sadd("clients", data[:name]) do
|
385
|
-
created!(Sensu::JSON.dump(:name => data[:name]))
|
386
|
-
end
|
387
|
-
end
|
388
|
-
else
|
389
|
-
bad_request!
|
390
|
-
end
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
aget "/clients/?" do
|
395
|
-
response = Array.new
|
396
|
-
settings.redis.smembers("clients") do |clients|
|
397
|
-
clients = pagination(clients)
|
398
|
-
unless clients.empty?
|
399
|
-
clients.each_with_index do |client_name, index|
|
400
|
-
settings.redis.get("client:#{client_name}") do |client_json|
|
401
|
-
unless client_json.nil?
|
402
|
-
response << Sensu::JSON.load(client_json)
|
403
|
-
else
|
404
|
-
settings.logger.error("client data missing from registry", :client_name => client_name)
|
405
|
-
settings.redis.srem("clients", client_name)
|
406
|
-
end
|
407
|
-
if index == clients.length - 1
|
408
|
-
body Sensu::JSON.dump(response)
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|
412
|
-
else
|
413
|
-
body Sensu::JSON.dump(response)
|
414
|
-
end
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
aget %r{^/clients?/([\w\.-]+)/?$} do |client_name|
|
419
|
-
settings.redis.get("client:#{client_name}") do |client_json|
|
420
|
-
unless client_json.nil?
|
421
|
-
body client_json
|
422
|
-
else
|
423
|
-
not_found!
|
424
|
-
end
|
425
|
-
end
|
426
|
-
end
|
427
|
-
|
428
|
-
aget %r{^/clients/([\w\.-]+)/history/?$} do |client_name|
|
429
|
-
response = Array.new
|
430
|
-
settings.redis.smembers("result:#{client_name}") do |checks|
|
431
|
-
unless checks.empty?
|
432
|
-
checks.each_with_index do |check_name, index|
|
433
|
-
result_key = "#{client_name}:#{check_name}"
|
434
|
-
history_key = "history:#{result_key}"
|
435
|
-
settings.redis.lrange(history_key, -21, -1) do |history|
|
436
|
-
history.map! do |status|
|
437
|
-
status.to_i
|
438
|
-
end
|
439
|
-
settings.redis.get("result:#{result_key}") do |result_json|
|
440
|
-
unless result_json.nil?
|
441
|
-
result = Sensu::JSON.load(result_json)
|
442
|
-
last_execution = result[:executed]
|
443
|
-
unless history.empty? || last_execution.nil?
|
444
|
-
item = {
|
445
|
-
:check => check_name,
|
446
|
-
:history => history,
|
447
|
-
:last_execution => last_execution.to_i,
|
448
|
-
:last_status => history.last,
|
449
|
-
:last_result => result
|
450
|
-
}
|
451
|
-
response << item
|
452
|
-
end
|
453
|
-
end
|
454
|
-
if index == checks.length - 1
|
455
|
-
body Sensu::JSON.dump(response)
|
456
|
-
end
|
457
|
-
end
|
458
|
-
end
|
459
|
-
end
|
460
|
-
else
|
461
|
-
body Sensu::JSON.dump(response)
|
462
|
-
end
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
|
-
adelete %r{^/clients?/([\w\.-]+)/?$} do |client_name|
|
467
|
-
settings.redis.get("client:#{client_name}") do |client_json|
|
468
|
-
unless client_json.nil?
|
469
|
-
settings.redis.hgetall("events:#{client_name}") do |events|
|
470
|
-
events.each do |check_name, event_json|
|
471
|
-
resolve_event(event_json)
|
472
|
-
end
|
473
|
-
delete_client = Proc.new do |attempts|
|
474
|
-
attempts += 1
|
475
|
-
settings.redis.hgetall("events:#{client_name}") do |events|
|
476
|
-
if events.empty? || attempts == 5
|
477
|
-
settings.logger.info("deleting client from registry", :client_name => client_name)
|
478
|
-
settings.redis.srem("clients", client_name) do
|
479
|
-
settings.redis.del("client:#{client_name}")
|
480
|
-
settings.redis.del("client:#{client_name}:signature")
|
481
|
-
settings.redis.del("events:#{client_name}")
|
482
|
-
settings.redis.smembers("result:#{client_name}") do |checks|
|
483
|
-
checks.each do |check_name|
|
484
|
-
result_key = "#{client_name}:#{check_name}"
|
485
|
-
settings.redis.del("result:#{result_key}")
|
486
|
-
settings.redis.del("history:#{result_key}")
|
487
|
-
end
|
488
|
-
settings.redis.del("result:#{client_name}")
|
489
|
-
end
|
490
|
-
end
|
491
|
-
else
|
492
|
-
EM::Timer.new(1) do
|
493
|
-
delete_client.call(attempts)
|
494
|
-
end
|
495
|
-
end
|
496
|
-
end
|
497
|
-
end
|
498
|
-
delete_client.call(0)
|
499
|
-
issued!
|
500
|
-
end
|
501
|
-
else
|
502
|
-
not_found!
|
503
|
-
end
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
507
|
-
aget "/checks/?" do
|
508
|
-
body Sensu::JSON.dump(settings.all_checks)
|
509
|
-
end
|
510
|
-
|
511
|
-
aget %r{^/checks?/([\w\.-]+)/?$} do |check_name|
|
512
|
-
if settings.checks[check_name]
|
513
|
-
response = settings.checks[check_name].merge(:name => check_name)
|
514
|
-
body Sensu::JSON.dump(response)
|
515
|
-
else
|
516
|
-
not_found!
|
517
|
-
end
|
518
|
-
end
|
519
|
-
|
520
|
-
apost "/request/?" do
|
521
|
-
rules = {
|
522
|
-
:check => {:type => String, :nil_ok => false},
|
523
|
-
:subscribers => {:type => Array, :nil_ok => true}
|
524
|
-
}
|
525
|
-
read_data(rules) do |data|
|
526
|
-
if settings.checks[data[:check]]
|
527
|
-
check = settings.checks[data[:check]].dup
|
528
|
-
check[:name] = data[:check]
|
529
|
-
check[:subscribers] ||= Array.new
|
530
|
-
check[:subscribers] = data[:subscribers] if data[:subscribers]
|
531
|
-
publish_check_request(check)
|
532
|
-
issued!
|
533
|
-
else
|
534
|
-
not_found!
|
535
|
-
end
|
536
|
-
end
|
537
|
-
end
|
538
|
-
|
539
|
-
aget "/events/?" do
|
540
|
-
response = Array.new
|
541
|
-
settings.redis.smembers("clients") do |clients|
|
542
|
-
unless clients.empty?
|
543
|
-
clients.each_with_index do |client_name, index|
|
544
|
-
settings.redis.hgetall("events:#{client_name}") do |events|
|
545
|
-
events.each do |check_name, event_json|
|
546
|
-
response << Sensu::JSON.load(event_json)
|
547
|
-
end
|
548
|
-
if index == clients.length - 1
|
549
|
-
body Sensu::JSON.dump(response)
|
550
|
-
end
|
551
|
-
end
|
552
|
-
end
|
553
|
-
else
|
554
|
-
body Sensu::JSON.dump(response)
|
555
|
-
end
|
556
|
-
end
|
557
|
-
end
|
558
|
-
|
559
|
-
aget %r{^/events/([\w\.-]+)/?$} do |client_name|
|
560
|
-
response = Array.new
|
561
|
-
settings.redis.hgetall("events:#{client_name}") do |events|
|
562
|
-
events.each do |check_name, event_json|
|
563
|
-
response << Sensu::JSON.load(event_json)
|
564
|
-
end
|
565
|
-
body Sensu::JSON.dump(response)
|
566
|
-
end
|
567
|
-
end
|
568
|
-
|
569
|
-
aget %r{^/events?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
|
570
|
-
settings.redis.hgetall("events:#{client_name}") do |events|
|
571
|
-
event_json = events[check_name]
|
572
|
-
unless event_json.nil?
|
573
|
-
body event_json
|
574
|
-
else
|
575
|
-
not_found!
|
576
|
-
end
|
577
|
-
end
|
578
|
-
end
|
579
|
-
|
580
|
-
adelete %r{^/events?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
|
581
|
-
settings.redis.hgetall("events:#{client_name}") do |events|
|
582
|
-
if events.include?(check_name)
|
583
|
-
resolve_event(events[check_name])
|
584
|
-
issued!
|
585
|
-
else
|
586
|
-
not_found!
|
587
|
-
end
|
588
|
-
end
|
589
|
-
end
|
590
|
-
|
591
|
-
apost "/resolve/?" do
|
592
|
-
rules = {
|
593
|
-
:client => {:type => String, :nil_ok => false},
|
594
|
-
:check => {:type => String, :nil_ok => false}
|
595
|
-
}
|
596
|
-
read_data(rules) do |data|
|
597
|
-
settings.redis.hgetall("events:#{data[:client]}") do |events|
|
598
|
-
if events.include?(data[:check])
|
599
|
-
resolve_event(events[data[:check]])
|
600
|
-
issued!
|
601
|
-
else
|
602
|
-
not_found!
|
603
|
-
end
|
604
|
-
end
|
605
|
-
end
|
606
|
-
end
|
607
|
-
|
608
|
-
aget "/aggregates/?" do
|
609
|
-
settings.redis.smembers("aggregates") do |aggregates|
|
610
|
-
aggregates.map! do |aggregate|
|
611
|
-
{:name => aggregate}
|
612
|
-
end
|
613
|
-
body Sensu::JSON.dump(aggregates)
|
614
|
-
end
|
615
|
-
end
|
616
|
-
|
617
|
-
aget %r{^/aggregates/([\w\.-]+)/?$} do |aggregate|
|
618
|
-
response = {
|
619
|
-
:clients => 0,
|
620
|
-
:checks => 0,
|
621
|
-
:results => {
|
622
|
-
:ok => 0,
|
623
|
-
:warning => 0,
|
624
|
-
:critical => 0,
|
625
|
-
:unknown => 0,
|
626
|
-
:total => 0,
|
627
|
-
:stale => 0
|
628
|
-
}
|
629
|
-
}
|
630
|
-
settings.redis.smembers("aggregates:#{aggregate}") do |aggregate_members|
|
631
|
-
unless aggregate_members.empty?
|
632
|
-
clients = []
|
633
|
-
checks = []
|
634
|
-
results = []
|
635
|
-
aggregate_members.each_with_index do |member, index|
|
636
|
-
client_name, check_name = member.split(":")
|
637
|
-
clients << client_name
|
638
|
-
checks << check_name
|
639
|
-
result_key = "result:#{client_name}:#{check_name}"
|
640
|
-
settings.redis.get(result_key) do |result_json|
|
641
|
-
unless result_json.nil?
|
642
|
-
results << Sensu::JSON.load(result_json)
|
643
|
-
else
|
644
|
-
settings.redis.srem("aggregates:#{aggregate}", member)
|
645
|
-
end
|
646
|
-
if index == aggregate_members.length - 1
|
647
|
-
response[:clients] = clients.uniq.length
|
648
|
-
response[:checks] = checks.uniq.length
|
649
|
-
max_age = integer_parameter(params[:max_age])
|
650
|
-
if max_age
|
651
|
-
result_count = results.length
|
652
|
-
timestamp = Time.now.to_i - max_age
|
653
|
-
results.reject! do |result|
|
654
|
-
result[:executed] < timestamp
|
655
|
-
end
|
656
|
-
response[:results][:stale] = result_count - results.length
|
657
|
-
end
|
658
|
-
response[:results][:total] = results.length
|
659
|
-
results.each do |result|
|
660
|
-
severity = (SEVERITIES[result[:status]] || "unknown")
|
661
|
-
response[:results][severity.to_sym] += 1
|
662
|
-
end
|
663
|
-
body Sensu::JSON.dump(response)
|
664
|
-
end
|
665
|
-
end
|
666
|
-
end
|
667
|
-
else
|
668
|
-
not_found!
|
669
|
-
end
|
670
|
-
end
|
671
|
-
end
|
672
|
-
|
673
|
-
adelete %r{^/aggregates/([\w\.-]+)/?$} do |aggregate|
|
674
|
-
settings.redis.smembers("aggregates") do |aggregates|
|
675
|
-
if aggregates.include?(aggregate)
|
676
|
-
settings.redis.srem("aggregates", aggregate) do
|
677
|
-
settings.redis.del("aggregates:#{aggregate}") do
|
678
|
-
no_content!
|
679
|
-
end
|
680
|
-
end
|
681
|
-
else
|
682
|
-
not_found!
|
683
|
-
end
|
684
|
-
end
|
685
|
-
end
|
686
|
-
|
687
|
-
aget %r{^/aggregates/([\w\.-]+)/clients/?$} do |aggregate|
|
688
|
-
response = Array.new
|
689
|
-
settings.redis.smembers("aggregates:#{aggregate}") do |aggregate_members|
|
690
|
-
unless aggregate_members.empty?
|
691
|
-
clients = Hash.new
|
692
|
-
aggregate_members.each do |member|
|
693
|
-
client_name, check_name = member.split(":")
|
694
|
-
clients[client_name] ||= []
|
695
|
-
clients[client_name] << check_name
|
696
|
-
end
|
697
|
-
clients.each do |client_name, checks|
|
698
|
-
response << {
|
699
|
-
:name => client_name,
|
700
|
-
:checks => checks
|
701
|
-
}
|
702
|
-
end
|
703
|
-
body Sensu::JSON.dump(response)
|
704
|
-
else
|
705
|
-
not_found!
|
706
|
-
end
|
707
|
-
end
|
708
|
-
end
|
709
|
-
|
710
|
-
aget %r{^/aggregates/([\w\.-]+)/checks/?$} do |aggregate|
|
711
|
-
response = Array.new
|
712
|
-
settings.redis.smembers("aggregates:#{aggregate}") do |aggregate_members|
|
713
|
-
unless aggregate_members.empty?
|
714
|
-
checks = Hash.new
|
715
|
-
aggregate_members.each do |member|
|
716
|
-
client_name, check_name = member.split(":")
|
717
|
-
checks[check_name] ||= []
|
718
|
-
checks[check_name] << client_name
|
719
|
-
end
|
720
|
-
checks.each do |check_name, clients|
|
721
|
-
response << {
|
722
|
-
:name => check_name,
|
723
|
-
:clients => clients
|
724
|
-
}
|
725
|
-
end
|
726
|
-
body Sensu::JSON.dump(response)
|
727
|
-
else
|
728
|
-
not_found!
|
729
|
-
end
|
730
|
-
end
|
731
|
-
end
|
732
|
-
|
733
|
-
aget %r{^/aggregates/([\w\.-]+)/results/([\w\.-]+)/?$} do |aggregate, severity|
|
734
|
-
response = Array.new
|
735
|
-
if SEVERITIES.include?(severity)
|
736
|
-
settings.redis.smembers("aggregates:#{aggregate}") do |aggregate_members|
|
737
|
-
unless aggregate_members.empty?
|
738
|
-
summaries = Hash.new
|
739
|
-
max_age = integer_parameter(params[:max_age])
|
740
|
-
current_timestamp = Time.now.to_i
|
741
|
-
aggregate_members.each_with_index do |member, index|
|
742
|
-
client_name, check_name = member.split(":")
|
743
|
-
result_key = "result:#{client_name}:#{check_name}"
|
744
|
-
settings.redis.get(result_key) do |result_json|
|
745
|
-
unless result_json.nil?
|
746
|
-
result = Sensu::JSON.load(result_json)
|
747
|
-
if SEVERITIES[result[:status]] == severity &&
|
748
|
-
(max_age.nil? || result[:executed] >= (current_timestamp - max_age))
|
749
|
-
summaries[check_name] ||= {}
|
750
|
-
summaries[check_name][result[:output]] ||= {:total => 0, :clients => []}
|
751
|
-
summaries[check_name][result[:output]][:total] += 1
|
752
|
-
summaries[check_name][result[:output]][:clients] << client_name
|
753
|
-
end
|
754
|
-
end
|
755
|
-
if index == aggregate_members.length - 1
|
756
|
-
summaries.each do |check_name, outputs|
|
757
|
-
summary = outputs.map do |output, output_summary|
|
758
|
-
{:output => output}.merge(output_summary)
|
759
|
-
end
|
760
|
-
response << {
|
761
|
-
:check => check_name,
|
762
|
-
:summary => summary
|
763
|
-
}
|
764
|
-
end
|
765
|
-
body Sensu::JSON.dump(response)
|
766
|
-
end
|
767
|
-
end
|
768
|
-
end
|
769
|
-
else
|
770
|
-
not_found!
|
771
|
-
end
|
772
|
-
end
|
773
|
-
else
|
774
|
-
bad_request!
|
775
|
-
end
|
776
|
-
end
|
777
|
-
|
778
|
-
apost %r{^/stash(?:es)?/(.*)/?} do |path|
|
779
|
-
read_data do |data|
|
780
|
-
settings.redis.set("stash:#{path}", Sensu::JSON.dump(data)) do
|
781
|
-
settings.redis.sadd("stashes", path) do
|
782
|
-
created!(Sensu::JSON.dump(:path => path))
|
783
|
-
end
|
784
|
-
end
|
785
|
-
end
|
786
|
-
end
|
787
|
-
|
788
|
-
aget %r{^/stash(?:es)?/(.*)/?} do |path|
|
789
|
-
settings.redis.get("stash:#{path}") do |stash_json|
|
790
|
-
unless stash_json.nil?
|
791
|
-
body stash_json
|
792
|
-
else
|
793
|
-
not_found!
|
794
|
-
end
|
795
|
-
end
|
796
|
-
end
|
797
|
-
|
798
|
-
adelete %r{^/stash(?:es)?/(.*)/?} do |path|
|
799
|
-
settings.redis.exists("stash:#{path}") do |stash_exists|
|
800
|
-
if stash_exists
|
801
|
-
settings.redis.srem("stashes", path) do
|
802
|
-
settings.redis.del("stash:#{path}") do
|
803
|
-
no_content!
|
804
|
-
end
|
805
|
-
end
|
806
|
-
else
|
807
|
-
not_found!
|
808
|
-
end
|
809
|
-
end
|
810
|
-
end
|
811
|
-
|
812
|
-
aget "/stashes/?" do
|
813
|
-
response = Array.new
|
814
|
-
settings.redis.smembers("stashes") do |stashes|
|
815
|
-
unless stashes.empty?
|
816
|
-
stashes.each_with_index do |path, index|
|
817
|
-
settings.redis.get("stash:#{path}") do |stash_json|
|
818
|
-
settings.redis.ttl("stash:#{path}") do |ttl|
|
819
|
-
unless stash_json.nil?
|
820
|
-
item = {
|
821
|
-
:path => path,
|
822
|
-
:content => Sensu::JSON.load(stash_json),
|
823
|
-
:expire => ttl
|
824
|
-
}
|
825
|
-
response << item
|
826
|
-
else
|
827
|
-
settings.redis.srem("stashes", path)
|
828
|
-
end
|
829
|
-
if index == stashes.length - 1
|
830
|
-
body Sensu::JSON.dump(pagination(response))
|
831
|
-
end
|
832
|
-
end
|
833
|
-
end
|
834
|
-
end
|
835
|
-
else
|
836
|
-
body Sensu::JSON.dump(response)
|
837
|
-
end
|
838
|
-
end
|
839
|
-
end
|
840
|
-
|
841
|
-
apost "/stashes/?" do
|
842
|
-
rules = {
|
843
|
-
:path => {:type => String, :nil_ok => false},
|
844
|
-
:content => {:type => Hash, :nil_ok => false},
|
845
|
-
:expire => {:type => Integer, :nil_ok => true}
|
846
|
-
}
|
847
|
-
read_data(rules) do |data|
|
848
|
-
stash_key = "stash:#{data[:path]}"
|
849
|
-
settings.redis.set(stash_key, Sensu::JSON.dump(data[:content])) do
|
850
|
-
settings.redis.sadd("stashes", data[:path]) do
|
851
|
-
response = Sensu::JSON.dump(:path => data[:path])
|
852
|
-
if data[:expire]
|
853
|
-
settings.redis.expire(stash_key, data[:expire]) do
|
854
|
-
created!(response)
|
855
|
-
end
|
856
|
-
else
|
857
|
-
created!(response)
|
858
|
-
end
|
859
|
-
end
|
860
|
-
end
|
861
|
-
end
|
862
|
-
end
|
863
|
-
|
864
|
-
apost "/results/?" do
|
865
|
-
rules = {
|
866
|
-
:name => {:type => String, :nil_ok => false, :regex => /\A[\w\.-]+\z/},
|
867
|
-
:output => {:type => String, :nil_ok => false},
|
868
|
-
:status => {:type => Integer, :nil_ok => true},
|
869
|
-
:source => {:type => String, :nil_ok => true, :regex => /\A[\w\.-]+\z/}
|
870
|
-
}
|
871
|
-
read_data(rules) do |data|
|
872
|
-
publish_check_result("sensu-api", data)
|
873
|
-
issued!
|
874
|
-
end
|
875
|
-
end
|
876
|
-
|
877
|
-
aget "/results/?" do
|
878
|
-
response = Array.new
|
879
|
-
settings.redis.smembers("clients") do |clients|
|
880
|
-
unless clients.empty?
|
881
|
-
clients.each_with_index do |client_name, client_index|
|
882
|
-
settings.redis.smembers("result:#{client_name}") do |checks|
|
883
|
-
if !checks.empty?
|
884
|
-
checks.each_with_index do |check_name, check_index|
|
885
|
-
result_key = "result:#{client_name}:#{check_name}"
|
886
|
-
settings.redis.get(result_key) do |result_json|
|
887
|
-
unless result_json.nil?
|
888
|
-
check = Sensu::JSON.load(result_json)
|
889
|
-
response << {:client => client_name, :check => check}
|
890
|
-
end
|
891
|
-
if client_index == clients.length - 1 && check_index == checks.length - 1
|
892
|
-
body Sensu::JSON.dump(response)
|
893
|
-
end
|
894
|
-
end
|
895
|
-
end
|
896
|
-
elsif client_index == clients.length - 1
|
897
|
-
body Sensu::JSON.dump(response)
|
898
|
-
end
|
899
|
-
end
|
900
|
-
end
|
901
|
-
else
|
902
|
-
body Sensu::JSON.dump(response)
|
903
|
-
end
|
904
|
-
end
|
905
|
-
end
|
906
|
-
|
907
|
-
aget %r{^/results?/([\w\.-]+)/?$} do |client_name|
|
908
|
-
response = Array.new
|
909
|
-
settings.redis.smembers("result:#{client_name}") do |checks|
|
910
|
-
unless checks.empty?
|
911
|
-
checks.each_with_index do |check_name, check_index|
|
912
|
-
result_key = "result:#{client_name}:#{check_name}"
|
913
|
-
settings.redis.get(result_key) do |result_json|
|
914
|
-
unless result_json.nil?
|
915
|
-
check = Sensu::JSON.load(result_json)
|
916
|
-
response << {:client => client_name, :check => check}
|
917
|
-
end
|
918
|
-
if check_index == checks.length - 1
|
919
|
-
body Sensu::JSON.dump(response)
|
920
|
-
end
|
921
|
-
end
|
922
|
-
end
|
923
|
-
else
|
924
|
-
not_found!
|
925
|
-
end
|
926
|
-
end
|
927
|
-
end
|
928
|
-
|
929
|
-
aget %r{^/results?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
|
930
|
-
result_key = "result:#{client_name}:#{check_name}"
|
931
|
-
settings.redis.get(result_key) do |result_json|
|
932
|
-
unless result_json.nil?
|
933
|
-
check = Sensu::JSON.load(result_json)
|
934
|
-
response = {:client => client_name, :check => check}
|
935
|
-
body Sensu::JSON.dump(response)
|
936
|
-
else
|
937
|
-
not_found!
|
938
|
-
end
|
939
|
-
end
|
940
|
-
end
|
941
|
-
|
942
|
-
adelete %r{^/results?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
|
943
|
-
result_key = "result:#{client_name}:#{check_name}"
|
944
|
-
settings.redis.exists(result_key) do |result_exists|
|
945
|
-
if result_exists
|
946
|
-
settings.redis.srem("result:#{client_name}", check_name) do
|
947
|
-
settings.redis.del(result_key) do |result_json|
|
948
|
-
no_content!
|
949
|
-
end
|
950
|
-
end
|
951
|
-
else
|
952
|
-
not_found!
|
953
|
-
end
|
954
|
-
end
|
955
|
-
end
|
956
68
|
end
|
957
69
|
end
|
958
70
|
end
|