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

Mysql select value check #84

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ 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-mysql-select-value.rb script (@DrMurx)

## [2.5.1] - 2018-06-21
### Fixed
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* bin/check-mysql-threads.rb
* bin/check-mysql-query-result-count.rb
* bin/check-mysql-select-count.rb
* bin/check-mysql-select-value.rb
* bin/check-mysql-msr-replication-status.rb
* bin/metrics-mysql-graphite.rb
* bin/metrics-mysql-processes.rb
Expand Down Expand Up @@ -93,6 +94,11 @@ $ /opt/sensu/embedded/bin/check-mysql-replication-status.rb --host=<SLAVE> --ini
/opt/sensu/embedded/bin$ /opt/sensu/embedded/bin/ruby check-mysql-select-count.rb --host=localhost --port=3306 --user=collectd --pass=tflypass --socket=/data/mysql.sock --warning 30000 --critical 50000 --query 'SELECT count(*) FROM table t'
```

**check-mysql-select-value** example
```bash
/opt/sensu/embedded/bin$ /opt/sensu/embedded/bin/ruby check-mysql-select-value.rb --host=localhost --port=3306 --user=collectd --pass=tflypass --socket=/data/mysql.sock --warning 1 --critical 2 --query 'SELECT MyStoredFunction()'
```

**metrics-mysql-query-result-count** example
```bash
/opt/sensu/embedded/bin$ /opt/sensu/embedded/bin/ruby metrics-mysql-query-result-count.rb --host=localhost --port=3306 --user=collectd --pass=tflypass --socket=/data/mysql.sock --query 'SELECT DISTINCT(t.id) FROM table t where t.failed = true'
Expand All @@ -110,6 +116,7 @@ In keeping with the principle of least privilege you should create a new user wi
| check-mysql-innodb-lock.rb | `PROCESS` |
| check-mysql-query-result-count.rb | depends on query |
| check-mysql-select-count.rb | `SELECT` |
| check-mysql-select-value.rb | `SELECT` |
| check-mysql-replication-status.rb | `SUPER` OR `REPLICATION_CLIENT` (the latter is preferable)|
| check-mysql-msr-replication-status.rb | `SELECT` |
| check-mysql-status.rb | `SELECT` |
Expand Down
Empty file modified bin/check-mysql-select-count.rb
100644 → 100755
Empty file.
131 changes: 131 additions & 0 deletions bin/check-mysql-select-value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/env ruby
#
# MySQL Select Count Check
#
# Checks the length of a result set from a MySQL query.
#
# Copyright 2017 Andrew Thal <[email protected]> to check-mysql-query-result-count.rb
# Modified by Mutsutoshi Yoshimoto <[email protected]> 2018 to select count(*) version
# Modified by Jan Kunzmann <[email protected]> 2018 to select value version
#
# Released under the same terms as Sensu (the MIT license); see LICENSE
# for details.

require 'sensu-plugin/check/cli'
require 'mysql'
require 'inifile'

class MysqlSelectCountCheck < Sensu::Plugin::Check::CLI
option :host,
short: '-h HOST',
long: '--host HOST',
description: 'MySQL Host to connect to',
required: true

option :port,
short: '-P PORT',
long: '--port PORT',
description: 'MySQL Port to connect to',
proc: proc(&:to_i),
default: 3306

option :username,
short: '-u USERNAME',
long: '--user USERNAME',
description: 'MySQL Username'

option :password,
short: '-p PASSWORD',
long: '--pass PASSWORD',
description: 'MySQL password'

option :database,
short: '-d DATABASE',
long: '--database DATABASE',
description: 'MySQL database',
required: true

option :ini,
short: '-i',
long: '--ini VALUE',
description: 'My.cnf ini file'

option :ini_section,
description: 'Section in my.cnf ini file',
long: '--ini-section VALUE',
default: 'client'

option :socket,
short: '-S SOCKET',
long: '--socket SOCKET',
description: 'MySQL Unix socket to connect to'

option :warn,
short: '-w VALUE',
long: '--warning VALUE',
description: 'Warning when query value exceeds threshold',
proc: proc(&:to_f),
required: true

option :crit,
short: '-c VALUE',
long: '--critical VALUE',
description: 'Critical when query value exceeds threshold',
proc: proc(&:to_f),
DrMurx marked this conversation as resolved.
Show resolved Hide resolved
required: true

option :query,
short: '-q SELECT_VALUE_QUERY',
long: '--query SELECT_VALUE_QUERY',
description: 'Query to execute',
required: true


def run
if config[:ini]
ini = IniFile.load(config[:ini])
section = ini[config[:ini_section]]
db_user = section['user']
db_pass = section['password']
else
db_user = config[:username]
db_pass = config[:password]
end

db = Mysql.real_connect(config[:host], db_user, db_pass, config[:database], config[:port], config[:socket])

rs = db.query(config[:query])
fields = rs.fetch_fields
col_name = fields[0].name.capitalize

value = rs.fetch_row[0].to_f

if config[:crit] > config[:warn]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I think this is a clever way to solve the problem I generally find it's not a good idea to do this kind of auto detection as you can't validate that its been configured correctly. Perhaps passing in a --threshold-type (or similar) with an option list would be better. If someone wants to check if something is greater than and the critical threshold is lower than the warning threshold we should return an unknown as we can't determine if this is intentional or a misconfiguration. Having people be explicit what they want removes the ambiguity and makes it easier to stop bad configuration from alerting people. Here is an example of passing in a list of acceptable arguments: https://github.com/sensu-plugins/sensu-plugins-redis/blob/3.0.1/lib/redis_client_options.rb#L75. Also if you set the warning threshold to be the same as the critical this will default to lower which I am not sure is the right default.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Will change.

beyond = "above"
beneath = "below"
factor = 1.0
else
beyond = "below"
beneath = "above"
factor = -1.0
end

if value * factor >= config[:crit] * factor
critical "#{col_name} #{value} is #{beyond} the CRITICAL limit of #{config[:crit]}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having the tripped threshold in the message seems redundant as it will start with the severity. Also not sure we really need to change "beyond" conditionally I think a more generic word such as exceeded would cover it, if you want to say do a hash lookup based on the type of threshold passed in I am less opposed I just find that these ways of trying to auto detect things often contain subtle bugs and are hard to detect misconfiguration.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that most people would understand "value A exceeds value B" as "value A is above value B". Looking at a monitoring dashboard or a notification from a check which I might not even have configured myself, the description should tell me unambiguous what's going on. I'll redo the code with hashes, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough though we are sacrificing a bit of code readability for the sake of being grammatically correct.

elsif value * factor >= config[:warn] * factor
warning "#{col_name} #{value} is #{beyond} the WARNING limit of #{config[:warn]}"
else
ok "#{col_name} #{value} is #{beneath} thresholds"
end

rescue Mysql::Error => e
errstr = "Error code: #{e.errno} Error message: #{e.error}"
critical "#{errstr} SQLSTATE: #{e.sqlstate}" if e.respond_to?('sqlstate')

rescue StandardError => e
critical "Unhandled exception: #{e}"

ensure
db.close if db
end
end