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,143 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Checks ElasticSearch shard allocation setting status
4
+ # ===
5
+ #
6
+ # DESCRIPTION:
7
+ # Checks the ElasticSearch shard allocation persistent and transient settings
8
+ # and will return status based on a difference in those settings.
9
+ #
10
+ # OUTPUT:
11
+ # plain-text
12
+ #
13
+ # PLATFORMS:
14
+ # Linux
15
+ #
16
+ # DEPENDENCIES:
17
+ # gem: sensu-plugin
18
+ # gem: rest-client
19
+ #
20
+ # USAGE:
21
+ #
22
+ # NOTES:
23
+ #
24
+ # LICENSE:
25
+ # Copyright 2014 Yieldbot, Inc <devops@yieldbot.com>
26
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
27
+ # for details.
28
+ #
29
+
30
+ require 'sensu-plugin/check/cli'
31
+ require 'rest-client'
32
+ require 'json'
33
+ require 'base64'
34
+
35
+ #
36
+ # == Elastic Search Shard Allocation Status
37
+ #
38
+ class ESShardAllocationStatus < Sensu::Plugin::Check::CLI
39
+ option :scheme,
40
+ description: 'URI scheme',
41
+ long: '--scheme SCHEME',
42
+ default: 'http'
43
+
44
+ option :server,
45
+ description: 'Elasticsearch server',
46
+ short: '-s SERVER',
47
+ long: '--server SERVER',
48
+ default: 'localhost'
49
+
50
+ option :port,
51
+ description: 'Port',
52
+ short: '-p PORT',
53
+ long: '--port PORT',
54
+ default: '9200'
55
+
56
+ option :allow_non_master,
57
+ description: 'Allow check to run on non-master nodes',
58
+ short: '-a',
59
+ long: '--allow-non-master',
60
+ default: false
61
+
62
+ option :timeout,
63
+ description: 'Sets the connection timeout for REST client',
64
+ short: '-t SECS',
65
+ long: '--timeout SECS',
66
+ proc: proc(&:to_i),
67
+ default: 45
68
+
69
+ option :user,
70
+ description: 'Elasticsearch User',
71
+ short: '-u USER',
72
+ long: '--user USER'
73
+
74
+ option :password,
75
+ description: 'Elasticsearch Password',
76
+ short: '-P PASS',
77
+ long: '--password PASS'
78
+
79
+ option :cert_file,
80
+ description: 'Cert file to use',
81
+ long: '--cert-file CERT'
82
+
83
+ def get_es_resource(resource)
84
+ headers = {}
85
+ if config[:user] && config[:password]
86
+ auth = 'Basic ' + Base64.strict_encode64("#{config[:user]}:#{config[:password]}").chomp
87
+ headers = { 'Authorization' => auth }
88
+ end
89
+
90
+ r = if config[:cert_file]
91
+ RestClient::Resource.new("#{config[:scheme]}://#{config[:server]}:#{config[:port]}#{resource}",
92
+ ssl_ca_file: config[:cert_file].to_s,
93
+ timeout: config[:timeout],
94
+ headers: headers)
95
+ else
96
+ RestClient::Resource.new("#{config[:scheme]}://#{config[:server]}:#{config[:port]}#{resource}",
97
+ timeout: config[:timeout],
98
+ headers: headers)
99
+ end
100
+ JSON.parse(r.get)
101
+ rescue Errno::ECONNREFUSED
102
+ warning 'Connection refused'
103
+ rescue RestClient::RequestTimeout
104
+ critical 'Connection timed out'
105
+ rescue RestClient::ServiceUnavailable
106
+ critical 'Service is unavailable'
107
+ rescue Errno::ECONNRESET
108
+ critical 'Connection reset by peer'
109
+ end
110
+
111
+ def master?
112
+ state = get_es_resource('/_cluster/state/master_node')
113
+ local = get_es_resource('/_nodes/_local')
114
+ local['nodes'].keys.first == state['master_node']
115
+ end
116
+
117
+ def get_status(type)
118
+ settings = get_es_resource('/_cluster/settings')
119
+ # Get the status for the given type, or default to 'all'
120
+ # which is the ES default
121
+ begin
122
+ settings[type]['cluster']['routing']['allocation']['enable'].downcase
123
+ rescue StandardError
124
+ 'all'
125
+ end
126
+ end
127
+
128
+ def run
129
+ if config[:allow_non_master] || master?
130
+ transient = get_status('transient')
131
+ persistent = get_status('persistent')
132
+
133
+ if transient == persistent
134
+ ok "Persistent and transient allocation match: #{persistent}"
135
+ else
136
+ critical "Persistent(#{persistent}) and transient(#{transient}) \
137
+ shard allocation do not match."
138
+ end
139
+ else
140
+ ok 'Not the master'
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,99 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # handler-es-delete-indices.rb
4
+ #
5
+ # DESCRIPTION:
6
+ # This handler deletes indices.
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
+ # Deletes the indices given to it from a check output and a configured
21
+ # regex, and then deletes the indices matched.
22
+ #
23
+ # NOTES:
24
+ #
25
+ # LICENSE:
26
+ # Brendan Leon Gibat <brendan.gibat@gmail.com>
27
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
28
+ # for details.
29
+ #
30
+
31
+ require 'sensu-handler'
32
+ require 'elasticsearch'
33
+ require 'aws_es_transport'
34
+ require 'sensu-plugins-elasticsearch'
35
+
36
+ class ESIndexCleanup < Sensu::Handler
37
+ include ElasticsearchCommon
38
+
39
+ option :transport,
40
+ long: '--transport TRANSPORT',
41
+ description: 'Transport to use to communicate with ES. Use "AWS" for signed AWS transports.'
42
+
43
+ option :region,
44
+ long: '--region REGION',
45
+ description: 'Region (necessary for AWS Transport)'
46
+
47
+ option :host,
48
+ description: 'Elasticsearch host',
49
+ short: '-h HOST',
50
+ long: '--host HOST',
51
+ default: 'localhost'
52
+
53
+ option :port,
54
+ description: 'Elasticsearch port',
55
+ short: '-p PORT',
56
+ long: '--port PORT',
57
+ proc: proc(&:to_i),
58
+ default: 9200
59
+
60
+ option :scheme,
61
+ description: 'Elasticsearch connection scheme, defaults to https for authenticated connections',
62
+ short: '-s SCHEME',
63
+ long: '--scheme SCHEME'
64
+
65
+ option :password,
66
+ description: 'Elasticsearch connection password',
67
+ short: '-P PASSWORD',
68
+ long: '--password PASSWORD'
69
+
70
+ option :user,
71
+ description: 'Elasticsearch connection user',
72
+ short: '-u USER',
73
+ long: '--user USER'
74
+
75
+ option :timeout,
76
+ description: 'Elasticsearch query timeout in seconds',
77
+ short: '-t TIMEOUT',
78
+ long: '--timeout TIMEOUT',
79
+ proc: proc(&:to_i),
80
+ default: 60
81
+
82
+ option :event_regex,
83
+ description: 'Elasticsearch connection user',
84
+ short: '-e EVENT_REGEX',
85
+ long: '--event-regex EVENT_REGEX',
86
+ default: 'INDEX\[([^\]]+)\]'
87
+
88
+ def handle
89
+ event_regex = Regexp.new(config[:event_regex])
90
+ indices_to_delete = @event['check']['output'].scan(event_regex).flatten
91
+ if !indices_to_delete.nil? && !indices_to_delete.empty?
92
+
93
+ puts("Deleting indices: [ #{indices_to_delete.sort.join(', ')} ]")
94
+ client.indices.delete index: indices_to_delete
95
+ else
96
+ puts('No indices matched pattern to delete.')
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,215 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # es-cluster-metrics
4
+ #
5
+ # DESCRIPTION:
6
+ # This plugin uses the ES API to collect metrics, producing a JSON
7
+ # document which is outputted to STDOUT. An exit status of 0 indicates
8
+ # the plugin has successfully collected and produced metrics.
9
+ #
10
+ # OUTPUT:
11
+ # metric data
12
+ #
13
+ # PLATFORMS:
14
+ # Linux
15
+ #
16
+ # DEPENDENCIES:
17
+ # gem: sensu-plugin
18
+ # gem: rest-client
19
+ #
20
+ # USAGE:
21
+ # #YELLOW
22
+ #
23
+ # NOTES:
24
+ #
25
+ # LICENSE:
26
+ # Copyright 2011 Sonian, Inc <chefs@sonian.net>
27
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
28
+ # for details.
29
+ #
30
+
31
+ require 'sensu-plugin/metric/cli'
32
+ require 'rest-client'
33
+ require 'json'
34
+ require 'base64'
35
+
36
+ #
37
+ # ES Cluster Metrics
38
+ #
39
+ class ESClusterMetrics < Sensu::Plugin::Metric::CLI::Graphite
40
+ option :scheme,
41
+ description: 'Metric naming scheme, text to prepend to metric',
42
+ short: '-s SCHEME',
43
+ long: '--scheme SCHEME',
44
+ default: "#{Socket.gethostname}.elasticsearch.cluster"
45
+
46
+ option :host,
47
+ description: 'Elasticsearch host',
48
+ short: '-h HOST',
49
+ long: '--host HOST',
50
+ default: 'localhost'
51
+
52
+ option :port,
53
+ description: 'Elasticsearch port',
54
+ short: '-p PORT',
55
+ long: '--port PORT',
56
+ proc: proc(&:to_i),
57
+ default: 9200
58
+
59
+ option :timeout,
60
+ description: 'Sets the connection timeout for REST client',
61
+ short: '-t SECS',
62
+ long: '--timeout SECS',
63
+ proc: proc(&:to_i),
64
+ default: 30
65
+
66
+ option :allow_non_master,
67
+ description: 'Allow check to run on non-master nodes',
68
+ short: '-a',
69
+ long: '--allow-non-master',
70
+ default: false
71
+
72
+ option :enable_percolate,
73
+ description: 'Enables percolator stats (ES 2 and older only)',
74
+ short: '-o',
75
+ long: '--enable-percolate',
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: '-P 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_FILE'
96
+
97
+ def acquire_es_version
98
+ info = get_es_resource('/')
99
+ info['version']['number']
100
+ end
101
+
102
+ def get_es_resource(resource)
103
+ headers = {}
104
+ if config[:user] && config[:password]
105
+ auth = 'Basic ' + Base64.strict_encode64("#{config[:user]}:#{config[:password]}").chomp
106
+ headers = { 'Authorization' => auth }
107
+ end
108
+
109
+ protocol = if config[:https]
110
+ 'https'
111
+ else
112
+ 'http'
113
+ end
114
+
115
+ r = if config[:cert_file]
116
+ RestClient::Resource.new("#{protocol}://#{config[:host]}:#{config[:port]}#{resource}",
117
+ ssl_ca_file: config[:cert_file].to_s,
118
+ timeout: config[:timeout],
119
+ headers: headers)
120
+ else
121
+ RestClient::Resource.new("#{protocol}://#{config[:host]}:#{config[:port]}#{resource}",
122
+ timeout: config[:timeout],
123
+ headers: headers)
124
+ end
125
+ ::JSON.parse(r.get)
126
+ rescue Errno::ECONNREFUSED
127
+ warning 'Connection refused'
128
+ rescue RestClient::RequestTimeout
129
+ warning 'Connection timed out'
130
+ end
131
+
132
+ def master?
133
+ state = if Gem::Version.new(acquire_es_version) >= Gem::Version.new('3.0.0')
134
+ get_es_resource('/_cluster/state/master_node')
135
+ else
136
+ get_es_resource('/_cluster/state?filter_routing_table=true&filter_metadata=true&filter_indices=true')
137
+ end
138
+ local = if Gem::Version.new(acquire_es_version) >= Gem::Version.new('1.0.0')
139
+ get_es_resource('/_nodes/_local')
140
+ else
141
+ get_es_resource('/_cluster/nodes/_local')
142
+ end
143
+ local['nodes'].keys.first == state['master_node']
144
+ end
145
+
146
+ def acquire_health
147
+ health = get_es_resource('/_cluster/health').reject { |k, _v| %w[cluster_name timed_out].include?(k) }
148
+ health['status'] = %w[red yellow green].index(health['status'])
149
+ health
150
+ end
151
+
152
+ def acquire_document_count
153
+ document_count = get_es_resource('/_stats/docs')
154
+ count = document_count['_all']['total']
155
+ if count.empty?
156
+ return 0
157
+ else
158
+ return count['docs']['count']
159
+ end
160
+ end
161
+
162
+ def acquire_cluster_metrics
163
+ cluster_stats = get_es_resource('/_cluster/stats')
164
+ cluster_metrics = Hash.new { |h, k| h[k] = {} }
165
+ cluster_metrics['fs']['total_in_bytes'] = cluster_stats['nodes']['fs']['total_in_bytes']
166
+ cluster_metrics['fs']['free_in_bytes'] = cluster_stats['nodes']['fs']['free_in_bytes']
167
+ cluster_metrics['fs']['store_in_bytes'] = cluster_stats['indices']['store']['size_in_bytes']
168
+ cluster_metrics['fs']['disk_reads'] = cluster_stats['nodes']['fs']['disk_reads']
169
+ cluster_metrics['fs']['disk_writes'] = cluster_stats['nodes']['fs']['disk_writes']
170
+ cluster_metrics['fs']['disk_read_size_in_bytes'] = cluster_stats['nodes']['fs']['disk_read_size_in_bytes']
171
+ cluster_metrics['fs']['disk_write_size_in_bytes'] = cluster_stats['nodes']['fs']['disk_write_size_in_bytes']
172
+ cluster_metrics['fielddata']['memory_size_in_bytes'] = cluster_stats['indices']['fielddata']['memory_size_in_bytes']
173
+ cluster_metrics['fielddata']['evictions'] = cluster_stats['indices']['fielddata']['evictions']
174
+
175
+ # Elasticsearch changed the name filter_cache to query_cache in 2.0+
176
+ cache_name = Gem::Version.new(acquire_es_version) < Gem::Version.new('2.0.0') ? 'filter_cache' : 'query_cache'
177
+
178
+ cluster_metrics[cache_name]['memory_size_in_bytes'] = cluster_stats['indices'][cache_name]['memory_size_in_bytes']
179
+ cluster_metrics[cache_name]['evictions'] = cluster_stats['indices'][cache_name]['evictions']
180
+ cluster_metrics['mem'] = cluster_stats['nodes']['jvm']['mem']
181
+
182
+ if config[:enable_percolate] && Gem::Version.new(acquire_es_version) < Gem::Version.new('5.0.0')
183
+ cluster_metrics['percolate']['total'] = cluster_stats['indices']['percolate']['total']
184
+ cluster_metrics['percolate']['time_in_millis'] = cluster_stats['indices']['percolate']['time_in_millis']
185
+ cluster_metrics['percolate']['queries'] = cluster_stats['indices']['percolate']['queries']
186
+ end
187
+ cluster_metrics
188
+ end
189
+
190
+ def acquire_allocation_status
191
+ cluster_config = get_es_resource('/_cluster/settings')
192
+ transient_settings = cluster_config['transient']
193
+ if transient_settings.key?('cluster')
194
+ return %w[none new_primaries primaries all].index(transient_settings['cluster']['routing']['allocation']['enable'])
195
+ else
196
+ return nil
197
+ end
198
+ end
199
+
200
+ def run
201
+ if config[:allow_non_master] || master?
202
+ acquire_health.each do |k, v|
203
+ output(config[:scheme] + '.' + k, v)
204
+ end
205
+ acquire_cluster_metrics.each do |cluster_metric|
206
+ cluster_metric[1].each do |k, v|
207
+ output(config[:scheme] + '.' + cluster_metric[0] + '.' + k, v || 0)
208
+ end
209
+ end
210
+ output(config[:scheme] + '.document_count', acquire_document_count)
211
+ output(config[:scheme] + '.allocation_status', acquire_allocation_status) unless acquire_allocation_status.nil?
212
+ end
213
+ ok
214
+ end
215
+ end