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,173 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-es-file-descriptors
4
+ #
5
+ # DESCRIPTION:
6
+ # This plugin checks the ElasticSearch file descriptor usage, using its API.
7
+ #
8
+ # OUTPUT:
9
+ # plain text
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: sensu-plugin
16
+ # gem: rest-client
17
+ #
18
+ # USAGE:
19
+ # #YELLOW
20
+ #
21
+ # NOTES:
22
+ #
23
+ # LICENSE:
24
+ # Author: S. Zachariah Sprackett <zac@sprackett.com>
25
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
26
+ # for details.
27
+ #
28
+
29
+ require 'sensu-plugin/check/cli'
30
+ require 'rest-client'
31
+ require 'json'
32
+ require 'base64'
33
+
34
+ #
35
+ # ES File Descriptiors
36
+ #
37
+ class ESFileDescriptors < Sensu::Plugin::Check::CLI
38
+ option :host,
39
+ description: 'Elasticsearch host',
40
+ short: '-h HOST',
41
+ long: '--host HOST',
42
+ default: 'localhost'
43
+
44
+ option :port,
45
+ description: 'Elasticsearch port',
46
+ short: '-p PORT',
47
+ long: '--port PORT',
48
+ proc: proc(&:to_i),
49
+ default: 9200
50
+
51
+ option :timeout,
52
+ description: 'Sets the connection timeout for REST client',
53
+ short: '-t SECS',
54
+ long: '--timeout SECS',
55
+ proc: proc(&:to_i),
56
+ default: 30
57
+
58
+ option :critical,
59
+ description: 'Critical percentage of FD usage',
60
+ short: '-c PERCENTAGE',
61
+ proc: proc(&:to_i),
62
+ default: 90
63
+
64
+ option :warning,
65
+ description: 'Warning percentage of FD usage',
66
+ short: '-w PERCENTAGE',
67
+ proc: proc(&:to_i),
68
+ default: 80
69
+
70
+ option :user,
71
+ description: 'Elasticsearch User',
72
+ short: '-u USER',
73
+ long: '--user USER'
74
+
75
+ option :password,
76
+ description: 'Elasticsearch Password',
77
+ short: '-P PASS',
78
+ long: '--password PASS'
79
+
80
+ option :https,
81
+ description: 'Enables HTTPS',
82
+ short: '-e',
83
+ long: '--https'
84
+
85
+ option :cert_file,
86
+ description: 'Cert file to use',
87
+ long: '--cert-file CERT'
88
+
89
+ def get_es_resource(resource)
90
+ headers = {}
91
+ if config[:user] && config[:password]
92
+ auth = 'Basic ' + Base64.strict_encode64("#{config[:user]}:#{config[:password]}").chomp
93
+ headers = { 'Authorization' => auth }
94
+ end
95
+
96
+ protocol = if config[:https]
97
+ 'https'
98
+ else
99
+ 'http'
100
+ end
101
+
102
+ r = if config[:cert_file]
103
+ RestClient::Resource.new("#{protocol}://#{config[:host]}:#{config[:port]}#{resource}",
104
+ ssl_ca_file: config[:cert_file].to_s,
105
+ timeout: config[:timeout],
106
+ headers: headers)
107
+ else
108
+ RestClient::Resource.new("#{protocol}://#{config[:host]}:#{config[:port]}#{resource}",
109
+ timeout: config[:timeout],
110
+ headers: headers)
111
+ end
112
+ JSON.parse(r.get)
113
+ rescue Errno::ECONNREFUSED
114
+ warning 'Connection refused'
115
+ rescue RestClient::RequestTimeout
116
+ warning 'Connection timed out'
117
+ rescue RestClient::ServiceUnavailable
118
+ warning 'Service is unavailable'
119
+ end
120
+
121
+ def acquire_es_version
122
+ info = get_es_resource('/')
123
+ info['version']['number']
124
+ end
125
+
126
+ def es_version
127
+ @es_version ||= Gem::Version.new(acquire_es_version)
128
+ end
129
+
130
+ def acquire_open_fds
131
+ stats = if es_version < Gem::Version.new('5.0.0')
132
+ get_es_resource('/_nodes/_local/stats?process=true')
133
+ else
134
+ get_es_resource('/_nodes/_local/stats/process')
135
+ end
136
+ begin
137
+ keys = stats['nodes'].keys
138
+ stats['nodes'][keys[0]]['process']['open_file_descriptors'].to_i
139
+ rescue NoMethodError
140
+ warning 'Failed to retrieve open_file_descriptors'
141
+ end
142
+ end
143
+
144
+ def acquire_max_fds
145
+ info = if es_version < Gem::Version.new('2.0.0')
146
+ get_es_resource('/_nodes/_local?process=true')
147
+ elsif es_version < Gem::Version.new('5.0.0')
148
+ get_es_resource('/_nodes/_local/stats?process=true')
149
+ else
150
+ get_es_resource('/_nodes/_local/stats/process')
151
+ end
152
+ begin
153
+ keys = info['nodes'].keys
154
+ info['nodes'][keys[0]]['process']['max_file_descriptors'].to_i
155
+ rescue NoMethodError
156
+ warning 'Failed to retrieve max_file_descriptors'
157
+ end
158
+ end
159
+
160
+ def run
161
+ open = acquire_open_fds
162
+ max = acquire_max_fds
163
+ used_percent = ((open.to_f / max.to_f) * 100).to_i
164
+
165
+ if used_percent >= config[:critical]
166
+ critical "fd usage #{used_percent}% exceeds #{config[:critical]}% (#{open}/#{max})"
167
+ elsif used_percent >= config[:warning]
168
+ warning "fd usage #{used_percent}% exceeds #{config[:warning]}% (#{open}/#{max})"
169
+ else
170
+ ok "fd usage at #{used_percent}% (#{open}/#{max})"
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,205 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # check-es-heap
4
+ #
5
+ # DESCRIPTION:
6
+ # This plugin checks ElasticSearch's Java heap usage using its API.
7
+ #
8
+ # OUTPUT:
9
+ # plain text
10
+ #
11
+ # PLATFORMS:
12
+ # Linux
13
+ #
14
+ # DEPENDENCIES:
15
+ # gem: sensu-plugin
16
+ # gem: rest-client
17
+ #
18
+ # USAGE:
19
+ # example commands
20
+ #
21
+ # NOTES:
22
+ #
23
+ # LICENSE:
24
+ # Copyright 2012 Sonian, Inc <chefs@sonian.net>
25
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
26
+ # for details.
27
+ #
28
+
29
+ require 'sensu-plugin/check/cli'
30
+ require 'rest-client'
31
+ require 'json'
32
+ require 'base64'
33
+
34
+ #
35
+ # ES Heap
36
+ #
37
+ class ESHeap < Sensu::Plugin::Check::CLI
38
+ option :host,
39
+ description: 'Elasticsearch host',
40
+ short: '-h HOST',
41
+ long: '--host HOST',
42
+ default: 'localhost'
43
+
44
+ option :port,
45
+ description: 'Elasticsearch port',
46
+ short: '-p PORT',
47
+ long: '--port PORT',
48
+ proc: proc(&:to_i),
49
+ default: 9200
50
+
51
+ option :warn,
52
+ short: '-w N',
53
+ long: '--warn N',
54
+ description: 'Heap used in bytes WARNING threshold',
55
+ proc: proc(&:to_i),
56
+ default: 0
57
+
58
+ option :timeout,
59
+ description: 'Sets the connection timeout for REST client',
60
+ short: '-t SECS',
61
+ long: '--timeout SECS',
62
+ proc: proc(&:to_i),
63
+ default: 30
64
+
65
+ option :crit,
66
+ short: '-c N',
67
+ long: '--crit N',
68
+ description: 'Heap used in bytes CRITICAL threshold',
69
+ proc: proc(&:to_i),
70
+ default: 0
71
+
72
+ option :percentage,
73
+ short: '-P',
74
+ long: '--percentage',
75
+ description: 'Use the WARNING and CRITICAL threshold numbers as percentage indicators of the total heap available',
76
+ default: false
77
+
78
+ option :user,
79
+ description: 'Elasticsearch User',
80
+ short: '-u USER',
81
+ long: '--user USER'
82
+
83
+ option :password,
84
+ description: 'Elasticsearch Password',
85
+ short: '-W PASS',
86
+ long: '--password PASS'
87
+
88
+ option :https,
89
+ description: 'Enables HTTPS',
90
+ short: '-e',
91
+ long: '--https'
92
+
93
+ option :cert_file,
94
+ description: 'Cert file to use',
95
+ long: '--cert-file CERT'
96
+
97
+ option :all,
98
+ description: 'Check all nodes in the ES cluster',
99
+ short: '-a',
100
+ long: '--all',
101
+ default: false
102
+
103
+ def acquire_es_version
104
+ info = acquire_es_resource('/')
105
+ info['version']['number']
106
+ end
107
+
108
+ def acquire_es_resource(resource)
109
+ headers = {}
110
+ if config[:user] && config[:password]
111
+ auth = 'Basic ' + Base64.strict_encode64("#{config[:user]}:#{config[:password]}").chomp
112
+ headers = { 'Authorization' => auth }
113
+ end
114
+
115
+ protocol = if config[:https]
116
+ 'https'
117
+ else
118
+ 'http'
119
+ end
120
+
121
+ r = if config[:cert_file]
122
+ RestClient::Resource.new("#{protocol}://#{config[:host]}:#{config[:port]}#{resource}",
123
+ ssl_ca_file: config[:cert_file].to_s,
124
+ timeout: config[:timeout],
125
+ headers: headers)
126
+ else
127
+ RestClient::Resource.new("#{protocol}://#{config[:host]}:#{config[:port]}#{resource}",
128
+ timeout: config[:timeout],
129
+ headers: headers)
130
+ end
131
+ JSON.parse(r.get)
132
+ rescue Errno::ECONNREFUSED
133
+ warning 'Connection refused'
134
+ rescue RestClient::RequestTimeout
135
+ warning 'Connection timed out'
136
+ rescue RestClient::ServiceUnavailable
137
+ warning 'Service is unavailable'
138
+ rescue JSON::ParserError
139
+ warning 'Elasticsearch API returned invalid JSON'
140
+ end
141
+
142
+ def acquire_stats
143
+ if Gem::Version.new(acquire_es_version) >= Gem::Version.new('1.0.0')
144
+ if config[:all]
145
+ acquire_es_resource('/_nodes/stats')
146
+ else
147
+ acquire_es_resource('/_nodes/_local/stats')
148
+ end
149
+ elsif config[:all]
150
+ acquire_es_resource('/_cluster/nodes/stats')
151
+ else
152
+ acquire_es_resource('/_cluster/nodes/_local/stats')
153
+ end
154
+ end
155
+
156
+ def acquire_heap_data(node)
157
+ return node['jvm']['mem']['heap_used_in_bytes'], node['jvm']['mem']['heap_max_in_bytes']
158
+ rescue StandardError
159
+ warning 'Failed to obtain heap used in bytes'
160
+ end
161
+
162
+ def acquire_heap_usage(heap_used, heap_max, node_name)
163
+ if config[:percentage]
164
+ heap_usage = ((100 * heap_used) / heap_max).to_i
165
+ output = if config[:all]
166
+ "Node #{node_name}: Heap used in bytes #{heap_used} (#{heap_usage}% full)\n"
167
+ else
168
+ "Heap used in bytes #{heap_used} (#{heap_usage}% full)"
169
+ end
170
+ else
171
+ heap_usage = heap_used
172
+ output = config[:all] ? "Node #{node_name}: Heap used in bytes #{heap_used}\n" : "Heap used in bytes #{heap_used}"
173
+ end
174
+ [heap_usage, output]
175
+ end
176
+
177
+ def run
178
+ stats = acquire_stats
179
+ status = { crit: '', warn: '', ok: '' }
180
+
181
+ # Check all the nodes in the cluster, alert if any of the nodes have heap usage above thresholds
182
+ stats['nodes'].each_value do |node|
183
+ heap_used, heap_max = acquire_heap_data(node)
184
+ heap_usage, output = acquire_heap_usage(heap_used, heap_max, node['name'])
185
+ if heap_usage >= config[:crit]
186
+ status[:crit] += output
187
+ elsif heap_usage >= config[:warn]
188
+ status[:warn] += output
189
+ elsif !config[:all]
190
+ status[:ok] += output
191
+ end
192
+ end
193
+
194
+ if !status[:crit].empty?
195
+ message status[:crit]
196
+ critical
197
+ elsif !status[:warn].empty?
198
+ message status[:warn]
199
+ warning
200
+ else
201
+ message status[:ok]
202
+ ok
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,89 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Check Elastic Search Indexes
4
+ # ===
5
+ #
6
+ # DESCRIPTION:
7
+ # This plugin will check a a node for dupe indexes
8
+ #
9
+ # OUTPUT:
10
+ # plain-text
11
+ #
12
+ # PLATFORMS:
13
+ # Linux
14
+ #
15
+ # DEPENDENCIES:
16
+ # gem: sensu-plugin
17
+ #
18
+ # needs usage
19
+ # USAGE:
20
+ #
21
+ # NOTES:
22
+ #
23
+ # LICENSE:
24
+ # Copyright 2014 Yieldbot, Inc <devops@yieldbot.com>
25
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
26
+ # for details.
27
+ #
28
+
29
+ require 'sensu-plugin/check/cli'
30
+
31
+ #
32
+ # == Check Elastic Search Cluster Index
33
+ #
34
+ class CheckESClusterIndex < Sensu::Plugin::Check::CLI
35
+ option :cluster,
36
+ description: 'Array of clusters to check',
37
+ short: '-C CLUSTER[,CLUSTER]',
38
+ long: '--cluster CLUSTER[,CLUSTER]',
39
+ proc: proc { |a| a.split(',') }
40
+
41
+ option :ignore,
42
+ description: 'Comma separated list of indexes to ignore',
43
+ short: '-i INDEX[,INDEX]',
44
+ long: '--ignore INDEX[,INDEX]',
45
+ proc: proc { |a| a.split(',') }
46
+
47
+ option :debug,
48
+ description: 'Debug',
49
+ short: '-d',
50
+ long: '--debug'
51
+
52
+ def run
53
+ # If only one cluster is given, no need to check the indexes
54
+ ok 'All indexes are unique' if config[:cluster].length == 1
55
+
56
+ port = ':9200'
57
+ cmd = '/_cat/indices?v | tail -n +2'
58
+
59
+ valid_index = {}
60
+ dupe_index = {}
61
+ config[:cluster].each do |u|
62
+ index_arr = `curl -s #{ u }#{ port }#{ cmd }`.split("\n")
63
+ index_arr.each do |t|
64
+ t = t.split[1]
65
+
66
+ # If the index is in the ignore list, go to the next one
67
+ next if config[:ignore].include? t
68
+
69
+ if valid_index.key?(t)
70
+ dupe_index[t] = [] unless dupe_index[t].is_a?(Array)
71
+ dupe_index[t] << u
72
+ dupe_index[t] << valid_index[t] unless dupe_index[t]
73
+ .include?(valid_index[t])
74
+ else
75
+ valid_index[t] = [] unless valid_index[t].is_a?(Array)
76
+ valid_index[t] << u
77
+ end
78
+ end
79
+ end
80
+
81
+ if dupe_index.count > 0
82
+ dupe_index.each do |k, v|
83
+ critical "#{k} is on #{v}"
84
+ end
85
+ else
86
+ ok 'All indexes are unique'
87
+ end
88
+ end
89
+ end