sensu-plugins-elasticsearch-boutetnico 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,287 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-es-query
4
+ #
5
+ # DESCRIPTION:
6
+ # This plugin checks an ElasticSearch query.
7
+ #
8
+ # OUTPUT:
9
+ # plain text
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: sensu-plugin
16
+ # gem: elasticsearch
17
+ # gem: aws_es_transport
18
+ #
19
+ # USAGE:
20
+ # This example checks that the average of the field "serve_time" in logs matching a query of
21
+ # anything (*) at the host elasticsearch.service.consul for the past 90 minutes
22
+ # will warn if above 120ms and go critical if the result average is above 250ms
23
+ # check-es-query-average.rb -h elasticsearch.service.consul -q "*"
24
+ # --types special_type -d 'logging-%Y.%m.%d' --minutes-previous 90 -p 9200 -c 0.250 -w 0.120 -a serve_time
25
+ #
26
+ # NOTES:
27
+ #
28
+ # LICENSE:
29
+ # Itamar Lavender <itamar.lavender@gmail.com>
30
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
31
+ # for details.
32
+ #
33
+
34
+ require 'sensu-plugin/check/cli'
35
+ require 'elasticsearch'
36
+ require 'time'
37
+ require 'uri'
38
+ require 'aws_es_transport'
39
+ require 'sensu-plugins-elasticsearch'
40
+
41
+ #
42
+ # ES Query Count
43
+ #
44
+ class ESQueryAverage < Sensu::Plugin::Check::CLI
45
+ include ElasticsearchCommon
46
+
47
+ option :index,
48
+ description: 'Elasticsearch indices to query.
49
+ Comma-separated list of index names to search.
50
+ Use `_all` or empty string to perform the operation on all indices.
51
+ Accepts wildcards',
52
+ short: '-i INDEX',
53
+ long: '--indices INDEX'
54
+
55
+ option :transport,
56
+ long: '--transport TRANSPORT',
57
+ description: 'Transport to use to communicate with ES. Use "AWS" for signed AWS transports.'
58
+
59
+ option :region,
60
+ long: '--region REGION',
61
+ description: 'Region (necessary for AWS Transport)'
62
+
63
+ option :types,
64
+ description: 'Elasticsearch types to limit searches to, comma separated list.',
65
+ long: '--types TYPES'
66
+
67
+ option :timestamp_field,
68
+ description: 'Field to use instead of @timestamp for query.',
69
+ long: '--timestamp-field FIELD_NAME',
70
+ default: '@timestamp'
71
+
72
+ option :offset,
73
+ description: 'Seconds before offset to end @timestamp against query.',
74
+ long: '--offset OFFSET',
75
+ proc: proc(&:to_i),
76
+ default: 0
77
+
78
+ option :ignore_unavailable,
79
+ description: 'Ignore unavailable indices.',
80
+ long: '--ignore-unavailable',
81
+ boolean: true,
82
+ default: true
83
+
84
+ option :minutes_previous,
85
+ description: 'Minutes before offset to check @timestamp against query.',
86
+ long: '--minutes-previous MINUTES_PREVIOUS',
87
+ proc: proc(&:to_i),
88
+ default: 0
89
+
90
+ option :hours_previous,
91
+ description: 'Hours before offset to check @timestamp against query.',
92
+ long: '--hours-previous HOURS_PREVIOUS',
93
+ proc: proc(&:to_i),
94
+ default: 0
95
+
96
+ option :days_previous,
97
+ description: 'Days before offset to check @timestamp against query.',
98
+ long: '--days-previous DAYS_PREVIOUS',
99
+ proc: proc(&:to_i),
100
+ default: 0
101
+
102
+ option :weeks_previous,
103
+ description: 'Weeks before offset to check @timestamp against query.',
104
+ long: '--weeks-previous WEEKS_PREVIOUS',
105
+ proc: proc(&:to_i),
106
+ default: 0
107
+
108
+ option :months_previous,
109
+ description: 'Months before offset to check @timestamp against query.',
110
+ long: '--months-previous MONTHS_PREVIOUS',
111
+ proc: proc(&:to_i),
112
+ default: 0
113
+
114
+ option :date_index,
115
+ description: 'Elasticsearch time based index.
116
+ Accepts format from http://ruby-doc.org/core-2.2.0/Time.html#method-i-strftime',
117
+ short: '-d DATE_INDEX',
118
+ long: '--date-index DATE_INDEX'
119
+
120
+ option :date_repeat_daily,
121
+ description: 'Elasticsearch date based index repeats daily.',
122
+ long: '--repeat-daily',
123
+ boolean: true,
124
+ default: true
125
+
126
+ option :date_repeat_hourly,
127
+ description: 'Elasticsearch date based index repeats hourly.',
128
+ long: '--repeat-hourly',
129
+ boolean: true,
130
+ default: false
131
+
132
+ option :search_field,
133
+ description: 'The Elasticsearch document field to search for your query string.',
134
+ short: '-f FIELD',
135
+ long: '--field FIELD',
136
+ required: false,
137
+ default: 'message'
138
+
139
+ option :query,
140
+ description: 'Elasticsearch query',
141
+ short: '-q QUERY',
142
+ long: '--query QUERY',
143
+ required: false
144
+
145
+ option :aggr,
146
+ description: 'Elasticsearch query aggr',
147
+ long: '--aggr',
148
+ boolean: true,
149
+ required: false,
150
+ default: true
151
+
152
+ option :aggr_field,
153
+ description: 'Elasticsearch query field to aggregate and average from',
154
+ short: '-a FIELD',
155
+ long: '--aggr-field FIELD',
156
+ required: true
157
+
158
+ option :host,
159
+ description: 'Elasticsearch host',
160
+ short: '-h HOST',
161
+ long: '--host HOST',
162
+ default: 'localhost'
163
+
164
+ option :port,
165
+ description: 'Elasticsearch port',
166
+ short: '-p PORT',
167
+ long: '--port PORT',
168
+ proc: proc(&:to_i),
169
+ default: 9200
170
+
171
+ option :scheme,
172
+ description: 'Elasticsearch connection scheme, defaults to https for authenticated connections',
173
+ short: '-s SCHEME',
174
+ long: '--scheme SCHEME'
175
+
176
+ option :password,
177
+ description: 'Elasticsearch connection password',
178
+ short: '-P PASSWORD',
179
+ long: '--password PASSWORD'
180
+
181
+ option :user,
182
+ description: 'Elasticsearch connection user',
183
+ short: '-u USER',
184
+ long: '--user USER'
185
+
186
+ option :headers,
187
+ description: 'A comma separated list of headers to pass to elasticsearch http client',
188
+ short: '-H headers',
189
+ long: '--headers headers',
190
+ default: 'Content-Type: application/json'
191
+
192
+ option :timeout,
193
+ description: 'Elasticsearch query timeout in seconds',
194
+ short: '-t TIMEOUT',
195
+ long: '--timeout TIMEOUT',
196
+ proc: proc(&:to_i),
197
+ default: 30
198
+
199
+ option :warn,
200
+ short: '-w N',
201
+ long: '--warn N',
202
+ description: 'Result average WARNING threshold',
203
+ proc: proc(&:to_f),
204
+ default: 0
205
+
206
+ option :crit,
207
+ short: '-c N',
208
+ long: '--crit N',
209
+ description: 'Result average CRITICAL threshold',
210
+ proc: proc(&:to_f),
211
+ default: 0
212
+
213
+ option :invert,
214
+ long: '--invert',
215
+ description: 'Invert thresholds',
216
+ boolean: true
217
+
218
+ option :kibana_url,
219
+ long: '--kibana-url KIBANA_URL',
220
+ description: 'Kibana URL query prefix that will be in critical / warning response output.'
221
+
222
+ def kibana_info
223
+ kibana_date_format = '%Y-%m-%dT%H:%M:%S.%LZ'
224
+ unless config[:kibana_url].nil?
225
+ index = config[:index]
226
+ unless config[:date_index].nil?
227
+ date_index_partition = config[:date_index].split('%')
228
+ index = "[#{date_index_partition.first}]" \
229
+ "#{date_index_partition[1..-1].join.sub('Y', 'YYYY').sub('y', 'YY').sub('m', 'MM').sub('d', 'DD').sub('j', 'DDDD').sub('H', 'hh')}"
230
+ end
231
+ end_time = Time.now.utc.to_i
232
+ start_time = end_time
233
+ if config[:minutes_previous] != 0
234
+ start_time -= (config[:minutes_previous] * 60)
235
+ end
236
+ if config[:hours_previous] != 0
237
+ start_time -= (config[:hours_previous] * 60 * 60)
238
+ end
239
+ if config[:days_previous] != 0
240
+ start_time -= (config[:days_previous] * 60 * 60 * 24)
241
+ end
242
+ if config[:weeks_previous] != 0
243
+ start_time -= (config[:weeks_previous] * 60 * 60 * 24 * 7)
244
+ end
245
+ if config[:months_previous] != 0
246
+ start_time -= (config[:months_previous] * 60 * 60 * 24 * 31)
247
+ end
248
+ "Kibana logs: #{config[:kibana_url]}/#/discover?_g=" \
249
+ "(refreshInterval:(display:Off,section:0,value:0),time:(from:'" \
250
+ "#{URI.escape(Time.at(start_time).utc.strftime kibana_date_format)}',mode:absolute,to:'" \
251
+ "#{URI.escape(Time.at(end_time).utc.strftime kibana_date_format)}'))&_a=(columns:!(_source),index:" \
252
+ "#{URI.escape(index)},interval:auto,query:(query_string:(analyze_wildcard:!t,query:'" \
253
+ "#{URI.escape(config[:query])}')),sort:!('@timestamp',desc))&dummy"
254
+ end
255
+ end
256
+
257
+ def run
258
+ response = client.search(build_request_options)
259
+ if config[:invert]
260
+ if response['aggregations']['average']['value'] < config[:crit]
261
+ critical "Query average (#{response['aggregations']['average']['value']}) was below critical threshold. #{kibana_info}"
262
+ elsif response['aggregations']['average']['value'] < config[:warn]
263
+ warning "Query average (#{response['aggregations']['average']['value']}) was below warning threshold. #{kibana_info}"
264
+ else
265
+ ok "Query average (#{response['aggregations']['average']['value']}) was ok"
266
+ end
267
+ elsif response['aggregations']['average']['value'] > config[:crit]
268
+ critical "Query average (#{response['aggregations']['average']['value']}) was above critical threshold. #{kibana_info}"
269
+ elsif response['aggregations']['average']['value'] > config[:warn]
270
+ warning "Query average (#{response['aggregations']['average']['value']}) was above warning threshold. #{kibana_info}"
271
+ else
272
+ ok "Query average (#{response['aggregations']['average']['value']}) was ok"
273
+ end
274
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
275
+ if config[:invert]
276
+ if response['aggregations']['average']['value'] < config[:crit]
277
+ critical "Query average (#{response['aggregations']['average']['value']}) was below critical threshold. #{kibana_info}"
278
+ elsif response['aggregations']['average']['value'] < config[:warn]
279
+ warning "Query average (#{response['aggregations']['average']['value']}) was below warning threshold. #{kibana_info}"
280
+ else
281
+ ok "Query average (#{response['aggregations']['average']['value']}) was ok"
282
+ end
283
+ else
284
+ ok 'No results found, average was below thresholds'
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,275 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-es-query
4
+ #
5
+ # DESCRIPTION:
6
+ # This plugin checks an ElasticSearch query.
7
+ #
8
+ # OUTPUT:
9
+ # plain text
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: sensu-plugin
16
+ # gem: elasticsearch
17
+ # gem: aws_es_transport
18
+ #
19
+ # USAGE:
20
+ # This example checks that the count of special_type logs matching a query of
21
+ # anything (*) at the host elasticsearch.service.consul for the past 90 minutes
22
+ # will warn if there are under 100 and go critical if the result count is below 1
23
+ # (The invert flag warns if counts are _below_ the critical and warning values)
24
+ # check-es-query-count.rb -h elasticsearch.service.consul -q "*" --invert
25
+ # --types special_type -d 'logging-%Y.%m.%d' --minutes-previous 90 -p 9200 -c 1 -w 100
26
+ #
27
+ # NOTES:
28
+ #
29
+ # LICENSE:
30
+ # Brendan Gibat <brendan.gibat@gmail.com>
31
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
32
+ # for details.
33
+ #
34
+
35
+ require 'sensu-plugin/check/cli'
36
+ require 'elasticsearch'
37
+ require 'time'
38
+ require 'uri'
39
+ require 'aws_es_transport'
40
+ require 'sensu-plugins-elasticsearch'
41
+
42
+ #
43
+ # ES Query Count
44
+ #
45
+ class ESQueryCount < Sensu::Plugin::Check::CLI
46
+ include ElasticsearchCommon
47
+
48
+ option :index,
49
+ description: 'Elasticsearch indices to query.
50
+ Comma-separated list of index names to search.
51
+ Use `_all` or empty string to perform the operation on all indices.
52
+ Accepts wildcards',
53
+ short: '-i INDEX',
54
+ long: '--indices INDEX'
55
+
56
+ option :transport,
57
+ long: '--transport TRANSPORT',
58
+ description: 'Transport to use to communicate with ES. Use "AWS" for signed AWS transports.'
59
+
60
+ option :region,
61
+ long: '--region REGION',
62
+ description: 'Region (necessary for AWS Transport)'
63
+
64
+ option :types,
65
+ description: 'Elasticsearch types to limit searches to, comma separated list.',
66
+ long: '--types TYPES'
67
+
68
+ option :timestamp_field,
69
+ description: 'Field to use instead of @timestamp for query.',
70
+ long: '--timestamp-field FIELD_NAME',
71
+ default: '@timestamp'
72
+
73
+ option :offset,
74
+ description: 'Seconds before offset to end @timestamp against query.',
75
+ long: '--offset OFFSET',
76
+ proc: proc(&:to_i),
77
+ default: 0
78
+
79
+ option :ignore_unavailable,
80
+ description: 'Ignore unavailable indices.',
81
+ long: '--ignore-unavailable',
82
+ boolean: true,
83
+ default: true
84
+
85
+ option :minutes_previous,
86
+ description: 'Minutes before offset to check @timestamp against query.',
87
+ long: '--minutes-previous MINUTES_PREVIOUS',
88
+ proc: proc(&:to_i),
89
+ default: 0
90
+
91
+ option :hours_previous,
92
+ description: 'Hours before offset to check @timestamp against query.',
93
+ long: '--hours-previous HOURS_PREVIOUS',
94
+ proc: proc(&:to_i),
95
+ default: 0
96
+
97
+ option :days_previous,
98
+ description: 'Days before offset to check @timestamp against query.',
99
+ long: '--days-previous DAYS_PREVIOUS',
100
+ proc: proc(&:to_i),
101
+ default: 0
102
+
103
+ option :weeks_previous,
104
+ description: 'Weeks before offset to check @timestamp against query.',
105
+ long: '--weeks-previous WEEKS_PREVIOUS',
106
+ proc: proc(&:to_i),
107
+ default: 0
108
+
109
+ option :months_previous,
110
+ description: 'Months before offset to check @timestamp against query.',
111
+ long: '--months-previous MONTHS_PREVIOUS',
112
+ proc: proc(&:to_i),
113
+ default: 0
114
+
115
+ option :date_index,
116
+ description: 'Elasticsearch time based index.
117
+ Accepts format from http://ruby-doc.org/core-2.2.0/Time.html#method-i-strftime',
118
+ short: '-d DATE_INDEX',
119
+ long: '--date-index DATE_INDEX'
120
+
121
+ option :date_repeat_daily,
122
+ description: 'Elasticsearch date based index repeats daily.',
123
+ long: '--repeat-daily',
124
+ boolean: true,
125
+ default: true
126
+
127
+ option :date_repeat_hourly,
128
+ description: 'Elasticsearch date based index repeats hourly.',
129
+ long: '--repeat-hourly',
130
+ boolean: true,
131
+ default: false
132
+
133
+ option :search_field,
134
+ description: 'The Elasticsearch document field to search for your query string.',
135
+ short: '-f FIELD',
136
+ long: '--field FIELD',
137
+ required: false,
138
+ default: 'message'
139
+
140
+ option :query,
141
+ description: 'Elasticsearch query',
142
+ short: '-q QUERY',
143
+ long: '--query QUERY',
144
+ required: true
145
+
146
+ option :host,
147
+ description: 'Elasticsearch host',
148
+ short: '-h HOST',
149
+ long: '--host HOST',
150
+ default: 'localhost'
151
+
152
+ option :port,
153
+ description: 'Elasticsearch port',
154
+ short: '-p PORT',
155
+ long: '--port PORT',
156
+ proc: proc(&:to_i),
157
+ default: 9200
158
+
159
+ option :scheme,
160
+ description: 'Elasticsearch connection scheme, defaults to https for authenticated connections',
161
+ short: '-s SCHEME',
162
+ long: '--scheme SCHEME'
163
+
164
+ option :password,
165
+ description: 'Elasticsearch connection password',
166
+ short: '-P PASSWORD',
167
+ long: '--password PASSWORD'
168
+
169
+ option :user,
170
+ description: 'Elasticsearch connection user',
171
+ short: '-u USER',
172
+ long: '--user USER'
173
+
174
+ option :headers,
175
+ description: 'A comma separated list of headers to pass to elasticsearch http client',
176
+ short: '-H headers',
177
+ long: '--headers headers',
178
+ default: 'Content-Type: application/json'
179
+
180
+ option :timeout,
181
+ description: 'Elasticsearch query timeout in seconds',
182
+ short: '-t TIMEOUT',
183
+ long: '--timeout TIMEOUT',
184
+ proc: proc(&:to_i),
185
+ default: 30
186
+
187
+ option :warn,
188
+ short: '-w N',
189
+ long: '--warn N',
190
+ description: 'Result count WARNING threshold',
191
+ proc: proc(&:to_i),
192
+ default: 0
193
+
194
+ option :crit,
195
+ short: '-c N',
196
+ long: '--crit N',
197
+ description: 'Result count CRITICAL threshold',
198
+ proc: proc(&:to_i),
199
+ default: 0
200
+
201
+ option :invert,
202
+ long: '--invert',
203
+ description: 'Invert thresholds',
204
+ boolean: true
205
+
206
+ option :kibana_url,
207
+ long: '--kibana-url KIBANA_URL',
208
+ description: 'Kibana URL query prefix that will be in critical / warning response output.'
209
+
210
+ def kibana_info
211
+ kibana_date_format = '%Y-%m-%dT%H:%M:%S.%LZ'
212
+ unless config[:kibana_url].nil?
213
+ index = config[:index]
214
+ unless config[:date_index].nil?
215
+ date_index_partition = config[:date_index].split('%')
216
+ index = "[#{date_index_partition.first}]" \
217
+ "#{date_index_partition[1..-1].join.sub('Y', 'YYYY').sub('y', 'YY').sub('m', 'MM').sub('d', 'DD').sub('j', 'DDDD').sub('H', 'hh')}"
218
+ end
219
+ end_time = Time.now.utc.to_i
220
+ start_time = end_time
221
+ if config[:minutes_previous] != 0
222
+ start_time -= (config[:minutes_previous] * 60)
223
+ end
224
+ if config[:hours_previous] != 0
225
+ start_time -= (config[:hours_previous] * 60 * 60)
226
+ end
227
+ if config[:days_previous] != 0
228
+ start_time -= (config[:days_previous] * 60 * 60 * 24)
229
+ end
230
+ if config[:weeks_previous] != 0
231
+ start_time -= (config[:weeks_previous] * 60 * 60 * 24 * 7)
232
+ end
233
+ if config[:months_previous] != 0
234
+ start_time -= (config[:months_previous] * 60 * 60 * 24 * 31)
235
+ end
236
+ "Kibana logs: #{config[:kibana_url]}/#/discover?_g=" \
237
+ "(refreshInterval:(display:Off,section:0,value:0),time:(from:'" \
238
+ "#{URI.escape(Time.at(start_time).utc.strftime kibana_date_format)}',mode:absolute,to:'" \
239
+ "#{URI.escape(Time.at(end_time).utc.strftime kibana_date_format)}'))&_a=(columns:!(_source),index:" \
240
+ "#{URI.escape(index)},interval:auto,query:(query_string:(analyze_wildcard:!t,query:'" \
241
+ "#{URI.escape(config[:query])}')),sort:!('@timestamp',desc))&dummy"
242
+ end
243
+ end
244
+
245
+ def run
246
+ response = client.count(build_request_options)
247
+ if config[:invert]
248
+ if response['count'] < config[:crit]
249
+ critical "Query count (#{response['count']}) was below critical threshold. #{kibana_info}"
250
+ elsif response['count'] < config[:warn]
251
+ warning "Query count (#{response['count']}) was below warning threshold. #{kibana_info}"
252
+ else
253
+ ok "Query count (#{response['count']}) was ok"
254
+ end
255
+ elsif response['count'] > config[:crit]
256
+ critical "Query count (#{response['count']}) was above critical threshold. #{kibana_info}"
257
+ elsif response['count'] > config[:warn]
258
+ warning "Query count (#{response['count']}) was above warning threshold. #{kibana_info}"
259
+ else
260
+ ok "Query count (#{response['count']}) was ok"
261
+ end
262
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
263
+ if config[:invert]
264
+ if response['count'] < config[:crit]
265
+ critical "Query count (#{response['count']}) was below critical threshold. #{kibana_info}"
266
+ elsif response['count'] < config[:warn]
267
+ warning "Query count (#{response['count']}) was below warning threshold. #{kibana_info}"
268
+ else
269
+ ok "Query count (#{response['count']}) was ok"
270
+ end
271
+ else
272
+ ok 'No results found, count was below thresholds'
273
+ end
274
+ end
275
+ end