class MysqlManager::CommandLine

Attributes

utilities[RW]

Public Class Methods

new() click to toggle source
# File lib/mysql_manager/command_line.rb, line 36
def initialize
  @options = {}
  @options[:db] = {}
  @options[:log] = {}
  @options[:kill] = {}
  @options[:reload_my_cnf] = {}
  @options[:skip_replication_errors] = {}
  @options[:hotcopy] = {}

  begin
    @optparse = OptionParser.new do |opts|
      opts.banner = "Usage: #{$0} options"
      
    
      #
      # Killing options
      #
      @options[:kill][:execute] = false
      opts.on( '--kill', 'Kill queries based on specified criteria') do
        @options[:kill][:execute] = true
      end

      @options[:kill][:max_query_time] = -1
      opts.on( '--kill:max-query-time TIME', 'Kill queries that have been running for more than TIME') do |time|
        @options[:kill][:max_query_time] = time.to_i
      end

      @options[:kill][:user] = []
      opts.on( '--kill:user USER', 'Kill queries matching USER (repeatable)') do |user|
        @options[:kill][:user] << to_pattern(user) 
      end

      @options[:kill][:host] = []
      opts.on( '--kill:host HOST', 'Kill queries matching HOST (repeatable)') do |host|
        @options[:kill][:host] << to_pattern(host) 
      end

      @options[:kill][:query] = []
      opts.on( '--kill:query SQL', 'Kill queries matching SQL (repeatable)') do |sql|
        @options[:kill][:query] << to_pattern(sql) 
      end

      @options[:kill][:command] = []
      opts.on( '--kill:command COMMAND', 'Kill queries matching COMMAND (repeatable)') do |command|
        @options[:kill][:command] << to_pattern(command) 
      end

      @options[:kill][:state] = []
      opts.on( '--kill:state STATE', 'Kill queries matching STATE (repeatable)') do |state|
        @options[:kill][:state] << to_pattern(state) 
      end

      @options[:kill][:db] = []
      opts.on( '--kill:db DB', 'Kill queries matching DB (repeatable)') do |db|
        @options[:kill][:db] << to_pattern(db) 
      end

      #
      # Skip Replication Error options
      #
      @options[:skip_replication_errors][:execute] = false
      opts.on( '--skip-replication-errors', 'Skip replication errors based on specified criteria') do
        @options[:skip_replication_errors][:execute] = true
      end

      @options[:skip_replication_errors][:max_errors] = -1
      opts.on( '--skip-replication-errors:max-errors NUMBER', "Abort after encountering NUMBER of errors (default: #{@options[:skip_replication_errors][:max_errors]})") do |number|
        @options[:skip_replication_errors][:max_errors] = number.to_i
      end

      @options[:skip_replication_errors][:max_error_duration] = -1
      opts.on( '--skip-replication-errors:max-error-duration SECONDS', "Abort after attempting to recover after SECONDS elapsed (default: #{@options[:skip_replication_errors][:max_error_durration]})") do |seconds|
        @options[:skip_replication_errors][:max_error_duration] = seconds.to_i
      end

      @options[:skip_replication_errors][:min_healthy_duration] = -1
      opts.on( '--skip-replication-errors:min-healthy-duration SECONDS', "Abort after replication healthy for SECONDS elapsed (default: #{@options[:skip_replication_errors][:min_healthy_duration]})") do |seconds|
        @options[:skip_replication_errors][:min_healthy_duration] = seconds.to_i
      end

      @options[:skip_replication_errors][:log_frequency] = 10
      opts.on( '--skip-replication-errors:max-errors SECONDS', "Output replication status events every SECONDS elapsed (default: #{@options[:skip_replication_errors][:log_frequency]})") do |seconds|
        @options[:skip_replication_errors][:log_frequency] = seconds.to_i
      end
 
      #
      # Reload my.cnf options
      #
      @options[:reload_my_cnf][:execute] = false
      opts.on( '--reload-my-cnf', 'Reload my.cnf based on specified criteria') do
        @options[:reload_my_cnf][:execute] = true
      end


      @options[:reload_my_cnf][:config] = '/etc/my.cnf'
      opts.on( '--reload-my-cnf:config FILE', "Issue set 'SET GLOBAL' for each variable in FILE (default: #{@options[:reload_my_cnf][:config]})") do |file|
        @options[:reload_my_cnf][:config] = file
      end

      @options[:reload_my_cnf][:groups] = ['mysqld', 'mysqld_safe', 'mysql.server', 'mysql_server', 'server', 'mysql']
      opts.on( '--reload-my-cnf:groups GROUP', "Issue set 'SET GLOBAL' for each variable in FILE (default: #{@options[:reload_my_cnf][:groups].join(', ')})") do |group|
        @options[:reload_my_cnf][:groups] << group
      end


      #
      # Database connection options
      #
      @options[:db][:dsn] = 'DBI:Mysql:mysql:localhost'
      opts.on( '--db:dsn DSN', "DSN to connect to MySQL database (default: #{@options[:db][:dsn]})" ) do|dsn|
        @options[:db][:dsn] = dsn
      end

      @options[:db][:username] = 'root'
      opts.on( '--db:username USERNAME', "Username corresponding to DSN (default: #{@options[:db][:username]})" ) do|username|
        @options[:db][:username] = username
      end

      @options[:db][:password] = ""
      opts.on( '--db:password PASSWORD', "Password corresponding to DSN (default: #{@options[:db][:password]})" ) do|password|
        @options[:db][:password] = password
      end

      #
      # Logging options
      #
      @options[:log][:level] = Logger::INFO
      opts.on( '--log:level LEVEL', 'Logging level' ) do|level|
        @options[:log][:level] = Logger.const_get level.upcase
      end

      @options[:log][:file] = STDERR
      opts.on( '--log:file FILE', 'Write logs to FILE (default: STDERR)' ) do|file|
        @options[:log][:file] = File.open(file, File::WRONLY | File::APPEND | File::CREAT)
      end

      @options[:log][:age] = 7
      opts.on( '--log:age DAYS', "Rotate logs after DAYS pass (default: #{@options[:log][:age]})" ) do|days|
        @options[:log][:age] = days.to_i
      end

      @options[:log][:size] = 1024*1024*10
      opts.on( '--log:size SIZE', 'Rotate logs after the grow past SIZE bytes' ) do |size|
        @options[:log][:size] = size.to_i
      end

      #
      # Hotcopy
      #

      @options[:hotcopy][:execute] = false
      opts.on( '--hotcopy', 'Perform a hotcopy') do
        @options[:hotcopy][:execute] = true
      end

      @options[:hotcopy][:data_dir] = '/var/lib/mysql'
      opts.on( '--hotcopy:data-dir PATH', "Rsync mysql data dir from PATH (default: #{@options[:hotcopy][:data_dir]})") do |path|
        @options[:hotcopy][:data_dir] = path.to_s
      end

      @options[:hotcopy][:backup_dir] = '/tmp/mysql'
      opts.on( '--hotcopy:backup-dir PATH', "Rsync to PATH (default: #{@options[:hotcopy][:backup_dir]})") do |path|
        @options[:hotcopy][:backup_dir] = path.to_s
      end

      @options[:hotcopy][:rsync_args] = '-av'
      opts.on( '--hotcopy:rsync-args ARGS', "Arguments to pass to rsync (default: #{@options[:hotcopy][:rsync_args]})") do |args|
        @options[:hotcopy][:rsync_args] = Shellwords.split(args.to_s)
      end

      @options[:hotcopy][:rsync_bin] = 'rsync'
      opts.on( '--hotcopy:rsync-bin bin', "Rsync executable path (default: #{@options[:hotcopy][:rsync_bin]})") do |bin|
        @options[:hotcopy][:rsync_bin] = bin.to_s
      end

      @options[:hotcopy][:rsync_ttl] = 60
      opts.on( '--hotcopy:rsync-ttl ttl', "Acceptable rsync execution time before performing table locks (default: #{@options[:hotcopy][:rsync_ttl]})") do |ttl|
        @options[:hotcopy][:rsync_ttl] = ttl.to_i
      end

      #
      # General options
      #
      @options[:dry_run] = false
      opts.on( '--dry-run', 'Do not run statements which affect the state of the database when executed' ) do
        @options[:dry_run] = true
      end


      opts.on( '-V', '--version', 'Display version information' ) do
        puts "MySQL Manager #{MysqlManager::VERSION}"
        puts "Copyright (C) 2012 Erik Osterman <e@osterman.com>"
        puts "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>"
        puts "This is free software: you are free to change and redistribute it."
        puts "There is NO WARRANTY, to the extent permitted by law."
        exit
      end

      opts.on( '-h', '--help', 'Display this screen' ) do
        puts opts
        exit
      end
    end

    @optparse.parse!

    raise MissingArgumentException.new("No action specified") if @options.select { |k,v| v.instance_of?(Hash) && v.has_key?(:execute) && v[:execute] == true }.size == 0
    @log = Logger.new(@options[:log][:file], @options[:log][:age], @options[:log][:size])
    @log.level = @options[:log][:level]
    @utilities = Utilities.new(@options[:db])
    @utilities.log = @log
    @utilities.dry_run = @options[:dry_run]
  rescue DBI::DatabaseError => e
    @log.fatal(e.message)
    exit(1) 
  rescue MissingArgumentException => e
    puts e.message
    puts @optparse
    exit (1)
  end
end

Public Instance Methods

execute() click to toggle source
# File lib/mysql_manager/command_line.rb, line 258
def execute
  @options.each do |type,options|
    next if options.instance_of?(Hash) && options.has_key?(:execute) && options[:execute] == false
    begin
      case type
      when :kill
        @log.debug("about to call kill_queries")
        @utilities.kill_queries(options)
      when :skip_replication_errors
        @log.debug("about to call skip_replication_errors")
        @utilities.skip_replication_errors(options)
      when :reload_my_cnf
        @log.debug("about to call reload_my_cnf")
        @utilities.reload_my_cnf(options)
      when :hotcopy
        @log.debug("about to call hotcopy")
        @utilities.hotcopy(options)
      end
    rescue DBI::DatabaseError => e
      @log.fatal(e.message)
      exit(1)
    rescue FileNotFoundException => e
      @log.fatal(e.message)
      exit(1)
    rescue Interrupt
      @log.info("Exiting")
      exit
    rescue Exception => e
      #@log.fatal(e.message + "\n" + e.backtrace.join("\n"))
      @log.fatal(e.message)
      exit(1)
    end
  end
end
to_pattern(str) click to toggle source
# File lib/mysql_manager/command_line.rb, line 28
def to_pattern(str)
  if str =~ /^\/(.*?)\/$/
    Regexp.new($1.to_s)
  else
    Regexp.new("^#{Regexp.escape(str)}$")
  end
end