Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add check-es-index-field-count.rb #112

Merged
merged 1 commit into from
Mar 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
This CHANGELOG follows the format listed [here](https://github.com/sensu-plugins/community/blob/master/HOW_WE_CHANGELOG.md)

## [Unreleased]
### Added
- check-es-indices-field-number.rb: check if the number of fields in index(es) is approaching limit (default to 1000 in ES)
### Changed
- Address a couple of rubocop style violations.

## [1.11.0] - 2018-02-23
### Added
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* /bin/check-es-cluster-status.rb
* /bin/check-es-file-descriptors.rb
* /bin/check-es-heap.rb
* /bin/check-es-indices-field-count.rb
* /bin/check-es-indexes.rb
* /bin/check-es-indicies-sizes.rb
* /bin/check-es-node-status.rb
Expand Down Expand Up @@ -42,13 +43,13 @@ Note: The test suite uses an elasticsearch instance in order to have passing tes

```bash
docker run -d --name sensu-elasticsearch-6 docker.elastic.co/elasticsearch/elasticsearch:6.2.2
```
```

Running the tests:

```bash
bundle install --path vendor/bundle
bundle exec kitchen test
```
```

You can find sample output for all tests running successfully in [this gist](https://gist.github.com/alexandrustaetu/d19feea1296d2ce7e367542265252d7a).
You can find sample output for all tests running successfully in [this gist](https://gist.github.com/alexandrustaetu/d19feea1296d2ce7e367542265252d7a).
194 changes: 194 additions & 0 deletions bin/check-es-indices-field-count.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#! /usr/bin/env ruby
#
# check-es-indices-field-count
#
# DESCRIPTION:
# This plugin checks if the number of fields in ES index(es) is approaching limit. ES by default
# puts this limit at 1000 fields per index.
#
# OUTPUT:
# plain text
#
# PLATFORMS:
# Linux
#
# DEPENDENCIES:
# gem: sensu-plugin
#
# USAGE:
# This example checks if the number of fields for an index is reaching its limit set in
# index.mapping.total_fields.limit. This check takes as paramater the index name or
# comma-separated list of indices, and optionally the type to check on. If type is not specified,
# all types in index are examined.
# You can also specify an optional value for the limit. When omitted, this will default to 1000.
#
# check-es-indices-field-count.rb -h <hostname or ip> -p 9200
# -i <index1>,<index2> --types <type_in_index> -w <pct> -c <pct>
#
# If any indices crossing the specified thresholds (warning/critical), beside the appropriate return code
# this check will also output a list of indices with the violated percentage for further troubleshooting.
# NOTES:
#
# LICENSE:
# CloudCruiser <[email protected]>
# Released under the same terms as Sensu (the MIT license); see LICENSE
# for details.
#

require 'sensu-plugin/check/cli'
require 'elasticsearch'
require 'sensu-plugins-elasticsearch'
require 'json'

#
# ES Indices Field Count
#
class ESIndicesFieldCount < Sensu::Plugin::Check::CLI
include ElasticsearchCommon

option :host,
description: 'Elasticsearch host',
short: '-h HOST',
long: '--host HOST',
default: 'localhost'

option :port,
description: 'Elasticsearch port',
short: '-p PORT',
long: '--port PORT',
proc: proc(&:to_i),
default: 9200

option :scheme,
description: 'Elasticsearch connection scheme, defaults to https for authenticated connections',
short: '-s SCHEME',
long: '--scheme SCHEME'

option :password,
description: 'Elasticsearch connection password',
short: '-P PASSWORD',
long: '--password PASSWORD'

option :user,
description: 'Elasticsearch connection user',
short: '-u USER',
long: '--user USER'

option :timeout,
description: 'Elasticsearch query timeout in seconds',
short: '-t TIMEOUT',
long: '--timeout TIMEOUT',
proc: proc(&:to_i),
default: 30

option :index,
description: 'Elasticsearch indices to check against.
Comma-separated list of index names to search.
Default to `_all` if omitted. Accepts wildcards',
short: '-i INDEX',
long: '--indices INDEX',
default: '_all'

option :types,
description: 'Elasticsearch types of index to check against.
Comma-separated list of types. When omitted, all types are checked against.',
short: '-T TYPES',
long: '--types TYPES'

option :limit,
description: 'Default number of fields limit to compare against.
Elasticsearch defaults this to 1000 if none is specied in index setting.',
short: '-l',
long: '--limit LIMIT',
proc: proc(&:to_i),
default: 1000

option :warn,
short: '-w PCT',
long: '--warn PCT',
description: 'WARNING threshold in percentage',
proc: proc(&:to_f),
default: 85.0

option :crit,
short: '-c N',
long: '--crit N',
description: 'CRITICAL threshold in percentage',
proc: proc(&:to_f),
default: 95.0

def indexfieldcount
index_field_count = {}
mappings = client.indices.get_mapping index: config[:index], type: config[:types]
mappings.each do |index, index_mapping|
unless index_mapping['mappings'].nil?
type_field_count = {}
index_mapping['mappings'].each do |type, type_mapping|
fieldcount = if type_mapping['properties'].nil?
0
else
type_mapping['properties'].length
end
type_field_count[type] = fieldcount
end

index_field_count[index] = type_field_count
end
end

index_field_count
end

def fieldlimitsetting
field_limit_setting = {}
settings = client.indices.get_settings index: config[:index]
settings.each do |index, index_setting|
index_field_limit = index_setting['settings']['index.mapping.total_fields.limit']
# when no index.mapping.total_fields.limit, use value of the limit parameter, which defaults to 1000.
index_field_limit = config[:limit] if index_field_limit.nil?
field_limit_setting[index] = { 'limit' => index_field_limit }
end

field_limit_setting
end

def run
fieldcounts = indexfieldcount
limits = fieldlimitsetting

warnings = {}
criticals = {}

if fieldcounts.empty?
unknown "Can't find any indices."
end

fieldcounts.each do |index, counts|
counts.each do |type, count|
pct = count.to_f / limits[index]['limit'] * 100

if config[:warn] <= pct && pct < config[:crit]
warnings[index] = {} if warnings[index].nil?
warnings[index][type] = pct.round(2)
end

if config[:crit] <= pct
criticals[index] = {} if criticals[index].nil?
criticals[index][type] = pct.round(2)
end
end
end

unless criticals.empty?
critical "Number of fields in indices is at critical level.
#{JSON.pretty_generate(criticals)}"
end

unless warnings.empty?
warning "Number of fields in indices is at warning level.
#{JSON.pretty_generate(warnings)}"
end

ok
end
end
7 changes: 4 additions & 3 deletions bin/check-es-indices-sizes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,12 @@ def build_indices_with_sizes
def run
node_fs_stats = client.nodes.stats metric: 'fs,indices'
nodes_being_used = node_fs_stats['nodes'].values.select { |node| node['indices']['store']['size_in_bytes'] > 0 }

# TODO: come back and cleanup all these rubocop disables with a little refactor
# rubocop:disable Style/SingleLineBlockParams
used_in_bytes = nodes_being_used.map { |node| node['fs']['data'].map { |data| data['total_in_bytes'] - data['available_in_bytes'] }.flatten }.flatten.inject { |sum, x| sum + x } # rubocop:disable LineLength
# rubocop:disable Style/SingleLineBlockParams,Metrics/LineLength
used_in_bytes = nodes_being_used.map { |node| node['fs']['data'].map { |data| data['total_in_bytes'] - data['available_in_bytes'] }.flatten }.flatten.inject { |sum, x| sum + x }
total_in_bytes = nodes_being_used.map { |node| node['fs']['data'].map { |data| data['total_in_bytes'] }.flatten }.flatten.inject { |sum, x| sum + x }
# rubocop:enable Style/SingleLineBlockParams LineLength
# rubocop:enable Style/SingleLineBlockParams,Metrics/LineLength

if config[:maximum_megabytes] > 0
target_bytes_used = config[:maximum_megabytes] * 1_000_000
Expand Down
10 changes: 5 additions & 5 deletions lib/sensu-plugins-elasticsearch/elasticsearch-query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,11 @@ def build_request_options
end

def es_date_math_string(end_time)
if config[:minutes_previous] == 0 && \
config[:hours_previous] == 0 && \
config[:days_previous] == 0 && \
config[:weeks_previous] == 0 && \
config[:months_previous] == 0
if config[:minutes_previous].zero? && \
config[:hours_previous].zero? && \
config[:days_previous].zero? && \
config[:weeks_previous].zero? && \
config[:months_previous].zero?
nil
else
es_math = "#{end_time.strftime '%Y-%m-%dT%H:%M:%S'}||"
Expand Down
1 change: 0 additions & 1 deletion sensu-plugins-elasticsearch.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ require 'date'
require_relative 'lib/sensu-plugins-elasticsearch'

# pvt_key = '~/.ssh/gem-private_key.pem'

Gem::Specification.new do |s|
s.authors = ['Sensu Plugins and contributors']
# s.cert_chain = ['certs/sensu-plugins.pem']
Expand Down
47 changes: 47 additions & 0 deletions test/fixtures/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,50 @@ RUBY_HOME=${MY_RUBY_HOME}
cd $DATA_DIR
SIGN_GEM=false gem build sensu-plugins-elasticsearch.gemspec
gem install sensu-plugins-elasticsearch-*.gem

# hostnamectl
if [ $(curl -sI -XGET sensu-elasticsearch-6:9200/field_count_index | grep -c '200 OK') -ne 0 ]; then
# clean up existing index
echo "Clean up existing index field_count_index"
curl -XDELETE sensu-elasticsearch-6:9200/field_count_index
echo
fi

echo
echo "Create index field_count_index"
echo
curl -XPUT sensu-elasticsearch-6:9200/field_count_index

echo
echo "Create mapping for index field_count_index"
echo
curl --header 'Content-Type: application/json' -XPUT sensu-elasticsearch-6:9200/field_count_index/_mapping/test -d @- <<'EOF'
{
"properties": {
"field1": {
"type": "boolean"
},
"field2": {
"type": "boolean"
},
"field3": {
"type": "boolean"
},
"field4": {
"type": "boolean"
},
"field5": {
"type": "boolean"
},
"field6": {
"type": "boolean"
},
"field7": {
"type": "boolean"
},
"field8": {
"type": "boolean"
}
}
}
EOF
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require 'spec_helper'
require 'shared_spec'

gem_path = '/usr/local/bin'
check_name = 'check-es-indices-field-count.rb'
check = "#{gem_path}/#{check_name}"
host = 'sensu-elasticsearch-6'

describe 'ruby environment' do
it_behaves_like 'ruby checks', check
end

describe command("#{check} --host #{host} -i field_count_index -l 10 -w 85 -c 95") do
its(:exit_status) { should eq 0 }
its(:stdout) { should match(/ESIndicesFieldCount OK/) }
end

describe command("#{check} --host #{host} -l 10 -w 85 -c 95") do
its(:exit_status) { should eq 0 }
its(:stdout) { should match(/ESIndicesFieldCount OK/) }
end

describe command("#{check} --host #{host} -i field_count_index -l 10 -w 70 -c 90") do
its(:exit_status) { should eq 1 }
end

describe command("#{check} --host #{host} -i field_count_index -l 10 -w 70 -c 80") do
its(:exit_status) { should eq 2 }
end