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