sensu-plugins/sensu-plugins-mysql

View on GitHub
bin/metrics-mysql-processes.rb

Summary

Maintainability
A
35 mins
Test Coverage
#!/usr/bin/env ruby
# frozen_string_literal: false

#
# metrics-mysql-processes
#
# DESCRIPTION:
#   Gets metrics out of of MySQL's "SHOW PROCESSLIST" query.
#
#   Output number of connections per-users, number of connections
#   per-databases, number of the different commands running.
#
# OUTPUT:
#   metric-data
#
# PLATFORMS:
#   Linux, Windows, BSD, Solaris, etc
#
# DEPENDENCIES:
#   gem: sensu-plugin
#   gem: mysql
#
# USAGE:
#   This was implemented to load mysql credentials without parsing the username/password.
#   The ini file should be readable by the sensu user/group.
#   Ref: http://eric.lubow.org/2009/ruby/parsing-ini-files-with-ruby/
#
#   EXAMPLE
#     mysql-alive.rb -h db01 --ini '/etc/sensu/my.cnf'
#     mysql-alive.rb -h db01 --ini '/etc/sensu/my.cnf' --ini-section customsection
#
#   MY.CNF INI FORMAT
#   [client]
#   user=sensu
#   password="abcd1234"
#
#   [customsection]
#   user=user
#   password="password"
#
# NOTES:
#
# LICENSE:
#   Jonathan Ballet <jballet@edgelab.ch>
#   Released under the same terms as Sensu (the MIT license); see LICENSE
#   for details.

require 'sensu-plugin/metric/cli'
require 'mysql'
require 'socket'
require 'inifile'

class MetricsMySQLProcesses < Sensu::Plugin::Metric::CLI::Graphite
  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',
         default: ''

  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 :scheme,
         description: 'Metric naming scheme, text to prepend to metric',
         short: '-s SCHEME',
         long: '--scheme SCHEME',
         default: "#{Socket.gethostname}.mysql"

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

  def set_default_metrics
    {
      'user' => {},
      'database' => {},
      'command' => {}
    }.each_value { |value| value.default = 0 }
  end

  def db_connection_creds
    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_user, db_pass]
  end

  def run
    config[:host].split(' ').each do |mysql_host|
      mysql_shorthostname = mysql_host.split('.')[0]
      db_user, db_pass = db_connection_creds
      begin
        mysql = Mysql.new(mysql_host, db_user, db_pass, nil, config[:port], config[:socket])

        results = mysql.query('SHOW PROCESSLIST')
      rescue StandardError => e
        unknown "Unable to query MySQL: #{e.message}"
      end

      metrics = set_default_metrics

      results.each_hash do |row|
        metrics['user'][row['User']] += 1
        if row['db'] # If no database has been selected by the process, it is set to nil.
          metrics['database'][row['db']] += 1
        end
        metrics['command'][row['Command']] += 1
      end

      metrics.each do |key, value|
        value.each do |instance, count|
          output "#{config[:scheme]}.#{mysql_shorthostname}.#{key}.#{instance}", count
        end
      end
    end

    ok
  end
end