deadpool 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/deadpool_admin +8 -0
 - data/bin/deadpool_generator +8 -0
 - data/bin/deadpool_hosts +222 -0
 - data/config/default_environment.yml +7 -0
 - data/doc/init/deadpool.conf +17 -0
 - data/lib/deadpool.rb +45 -0
 - data/lib/deadpool/admin.rb +197 -0
 - data/lib/deadpool/admin_server.rb +65 -0
 - data/lib/deadpool/command_line_server.rb +169 -0
 - data/lib/deadpool/daemonizer.rb +82 -0
 - data/lib/deadpool/failover_protocol.rb +91 -0
 - data/lib/deadpool/failover_protocol/etc_hosts.rb +157 -0
 - data/lib/deadpool/failover_protocol/exec_remote_command.rb +106 -0
 - data/lib/deadpool/generator.rb +220 -0
 - data/lib/deadpool/handler.rb +100 -0
 - data/lib/deadpool/helper.rb +39 -0
 - data/lib/deadpool/monitor/base.rb +58 -0
 - data/lib/deadpool/monitor/generic_nagios.rb +47 -0
 - data/lib/deadpool/monitor/mysql.rb +62 -0
 - data/lib/deadpool/monitor/redis.rb +21 -0
 - data/lib/deadpool/options.rb +62 -0
 - data/lib/deadpool/server.rb +101 -0
 - data/lib/deadpool/state.rb +78 -0
 - data/lib/deadpool/state_snapshot.rb +81 -0
 - metadata +125 -0
 
| 
         @@ -0,0 +1,106 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'net/ssh'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Deadpool
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              module FailoverProtocol
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                class ExecRemoteCommand < Base
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def setup
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @test_command = @failover_config[:test_command]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @exec_command = @failover_config[:exec_command]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @client_hosts = @failover_config[:client_hosts]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @username     = @failover_config[:username]
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @password     = @failover_config[:password]
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @use_sudo     = @failover_config[:use_sudo]
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @sudo_path    = @failover_config[:sudo_path].nil? ? 'sudo' : @failover_config[:sudo_path]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  # Return true or false
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Don't update system state.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # return true or false success or failure
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def preflight_check
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @client_hosts.all? { |client_host| test_client(client_host) }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  def test_client(client_host)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    logger.debug "Testing Client #{client_host}"
         
     | 
| 
      
 28 
     | 
    
         
            +
                    return exec_remote_command(@test_command, client_host)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # Promote the host to primary.  This is used by initiate_failover_protocol!
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # and for manual promotion by an administrator.
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # new_primary is an IP address
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # TODO: change new_primary to be a config label.
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # return true or false success or failure
         
     | 
| 
      
 36 
     | 
    
         
            +
                  def promote_to_primary(new_primary)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    success = true
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    @client_hosts.each do |client_host|
         
     | 
| 
      
 40 
     | 
    
         
            +
                      if exec_remote_command(@exec_command, client_host)
         
     | 
| 
      
 41 
     | 
    
         
            +
                        exec_remote_command(@exec_command, client_host)
         
     | 
| 
      
 42 
     | 
    
         
            +
                        logger.info "Promotion exec command succeeded on #{client_host}"
         
     | 
| 
      
 43 
     | 
    
         
            +
                      else
         
     | 
| 
      
 44 
     | 
    
         
            +
                        logger.error "Promotion exec command failed on #{client_host}"
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    return success
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  # Perform checks against anything that could cause a failover protocol to fail
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # Perform checks on system state.
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # return New Deadpool::StateSnapshot
         
     | 
| 
      
 54 
     | 
    
         
            +
                  def system_check
         
     | 
| 
      
 55 
     | 
    
         
            +
                    failed    = []
         
     | 
| 
      
 56 
     | 
    
         
            +
                    succeeded = []
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    # Collect check data
         
     | 
| 
      
 59 
     | 
    
         
            +
                    @client_hosts.each do |client_host|
         
     | 
| 
      
 60 
     | 
    
         
            +
                      if test_client(client_host)
         
     | 
| 
      
 61 
     | 
    
         
            +
                        succeeded << client_host
         
     | 
| 
      
 62 
     | 
    
         
            +
                      else
         
     | 
| 
      
 63 
     | 
    
         
            +
                        failed << client_host
         
     | 
| 
      
 64 
     | 
    
         
            +
                      end
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    # Compile write check data.
         
     | 
| 
      
 68 
     | 
    
         
            +
                    if !succeeded.empty? && failed.empty?
         
     | 
| 
      
 69 
     | 
    
         
            +
                      @state.set_state OK, "Exec test passed all servers: #{succeeded.join(', ')}"
         
     | 
| 
      
 70 
     | 
    
         
            +
                    elsif !succeeded.empty? && !failed.empty?
         
     | 
| 
      
 71 
     | 
    
         
            +
                      @state.set_state WARNING, "Exec test passed on: #{succeeded.join(', ')}"
         
     | 
| 
      
 72 
     | 
    
         
            +
                      @state.add_error_message "Exec test failed on #{failed.join(', ')}"
         
     | 
| 
      
 73 
     | 
    
         
            +
                    elsif succeeded.empty?
         
     | 
| 
      
 74 
     | 
    
         
            +
                      @state.set_state WARNING, "Exec test failed all servers: #{failed.join(', ')}"
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    return Deadpool::StateSnapshot.new @state
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def exec_remote_command(command, host)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    options = @password.nil? ? {} : {:password => @password}
         
     | 
| 
      
 84 
     | 
    
         
            +
                    command = "#{command} ; echo 'ExecRemoteCommand.success: '$?"
         
     | 
| 
      
 85 
     | 
    
         
            +
                    command = "#{@sudo_path} #{command}" if @use_sudo
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    logger.debug "executing #{command} on #{host}"
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 90 
     | 
    
         
            +
                      output = 0
         
     | 
| 
      
 91 
     | 
    
         
            +
                      Net::SSH.start(host, @username, options) do |ssh|
         
     | 
| 
      
 92 
     | 
    
         
            +
                        output = ssh.exec!(command)
         
     | 
| 
      
 93 
     | 
    
         
            +
                      end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                      return (output =~ /ExecRemoteCommand.success: 0/)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 97 
     | 
    
         
            +
                      logger.error "Couldn't execute #{command} on #{host} with Username: #{@username} and options: #{options.inspect}"
         
     | 
| 
      
 98 
     | 
    
         
            +
                      return false
         
     | 
| 
      
 99 
     | 
    
         
            +
                    end
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
              end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,220 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'ostruct'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'strscan'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Deadpool
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Generator
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                # include FileUtils
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(argv)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @argv     = argv
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def run
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @options = self.parse_command_line
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @config  = Deadpool::Helper.configure @options
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  self.execute_command(@options)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def parse_command_line
         
     | 
| 
      
 23 
     | 
    
         
            +
                  options                       = Hash.new
         
     | 
| 
      
 24 
     | 
    
         
            +
                  options[:command_count]       = 0
         
     | 
| 
      
 25 
     | 
    
         
            +
                  options[:config_path]         = '/etc/deadpool'
         
     | 
| 
      
 26 
     | 
    
         
            +
                  options[:upstart_config_path] = '/etc/init/deadpool.conf'
         
     | 
| 
      
 27 
     | 
    
         
            +
                  options[:upstart_init_path]   = '/etc/init.d/deadpool'
         
     | 
| 
      
 28 
     | 
    
         
            +
                  options[:upstart_script_path] = '/lib/init/upstart-job'
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  @option_parser = OptionParser.new do |opts|
         
     | 
| 
      
 31 
     | 
    
         
            +
                    opts.banner = "Usage: deadpool_generator command [options]"
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    opts.separator "Commands:"
         
     | 
| 
      
 34 
     | 
    
         
            +
                    opts.on("-h", "--help", "Print this help message.") do |help|
         
     | 
| 
      
 35 
     | 
    
         
            +
                      options[:command_count] += 1
         
     | 
| 
      
 36 
     | 
    
         
            +
                      options[:command]        = :help
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    opts.on("--upstart_init", "Generate and upstart config.") do |upstart|
         
     | 
| 
      
 39 
     | 
    
         
            +
                      options[:command_count] += 1
         
     | 
| 
      
 40 
     | 
    
         
            +
                      options[:command]        = :upstart_init
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                    opts.on("--configuration", "Generate a config directory structure and example files.") do |configuration|
         
     | 
| 
      
 43 
     | 
    
         
            +
                      options[:command_count] += 1
         
     | 
| 
      
 44 
     | 
    
         
            +
                      options[:command]        = :configuration
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  
         
     | 
| 
      
 47 
     | 
    
         
            +
                    opts.separator "Configuration Options:"
         
     | 
| 
      
 48 
     | 
    
         
            +
                    opts.on("--config_path=PATH", String, "path to create the config dir at (#{options[:config_path]})") do |config_path|
         
     | 
| 
      
 49 
     | 
    
         
            +
                      options[:config_path] = config_path
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    opts.separator "Upstart Options:"
         
     | 
| 
      
 53 
     | 
    
         
            +
                    opts.on("--upstart_config_path=PATH", String, "path to create the config dir at (#{options[:upstart_config_path]})") do |upstart_config_path|
         
     | 
| 
      
 54 
     | 
    
         
            +
                      options[:upstart_config_path] = upstart_config_path
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
                    opts.on("--upstart_init_path=PATH", String, "path to create the config dir at (#{options[:upstart_init_path]})") do |upstart_init_path|
         
     | 
| 
      
 57 
     | 
    
         
            +
                      options[:upstart_init_path] = upstart_init_path
         
     | 
| 
      
 58 
     | 
    
         
            +
                    end
         
     | 
| 
      
 59 
     | 
    
         
            +
                    opts.on("--upstart_script_path=PATH", String, "path to create the config dir at (#{options[:upstart_script_path]})") do |upstart_script_path|
         
     | 
| 
      
 60 
     | 
    
         
            +
                      options[:upstart_script_path] = upstart_script_path
         
     | 
| 
      
 61 
     | 
    
         
            +
                      unless File.exists? upstart_script_path
         
     | 
| 
      
 62 
     | 
    
         
            +
                        help "The upstart script is not at #{upstart_script_path}."
         
     | 
| 
      
 63 
     | 
    
         
            +
                      end
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    remaining_arguments = @option_parser.parse! @argv
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    unless remaining_arguments.empty?
         
     | 
| 
      
 70 
     | 
    
         
            +
                      help "[#{remaining_arguments.join(' ')}] is not understood."
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                    if options[:command_count] == 0
         
     | 
| 
      
 74 
     | 
    
         
            +
                      help "You must specify a command."
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    return options
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  def execute_command(options)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    case options[:command]
         
     | 
| 
      
 82 
     | 
    
         
            +
                    when :upstart_init
         
     | 
| 
      
 83 
     | 
    
         
            +
                      generate_upstart_init options
         
     | 
| 
      
 84 
     | 
    
         
            +
                    when :configuration
         
     | 
| 
      
 85 
     | 
    
         
            +
                      generate_configuration options
         
     | 
| 
      
 86 
     | 
    
         
            +
                    else
         
     | 
| 
      
 87 
     | 
    
         
            +
                      help
         
     | 
| 
      
 88 
     | 
    
         
            +
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                def help(message=nil)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  unless message.nil?
         
     | 
| 
      
 93 
     | 
    
         
            +
                    puts message
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  puts @option_parser.help
         
     | 
| 
      
 96 
     | 
    
         
            +
                  exit 4
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                def generate_upstart_init(options)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  config_path         = options[:config_path]
         
     | 
| 
      
 101 
     | 
    
         
            +
                  upstart_config_path = options[:upstart_config_path]
         
     | 
| 
      
 102 
     | 
    
         
            +
                  upstart_init_path   = options[:upstart_init_path]
         
     | 
| 
      
 103 
     | 
    
         
            +
                  upstart_script_path = options[:upstart_script_path]
         
     | 
| 
      
 104 
     | 
    
         
            +
                  config_params       = config_path.nil? ? '' : "--config_path=#{config_path}"
         
     | 
| 
      
 105 
     | 
    
         
            +
                  ruby_path           = `which ruby`.strip
         
     | 
| 
      
 106 
     | 
    
         
            +
                  deadpool_admin_path = `which deadpool_admin`.strip
         
     | 
| 
      
 107 
     | 
    
         
            +
                  
         
     | 
| 
      
 108 
     | 
    
         
            +
                  upstart_conf =<<-EOF
         
     | 
| 
      
 109 
     | 
    
         
            +
            description     "Deadpool Service"
         
     | 
| 
      
 110 
     | 
    
         
            +
            author          "Kirt Fitzpatrick <kirt.fitzpatrick@akqa.com>"
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
            umask 007
         
     | 
| 
      
 113 
     | 
    
         
            +
            start on (net-device-up and local-filesystems)
         
     | 
| 
      
 114 
     | 
    
         
            +
            stop on runlevel [016]
         
     | 
| 
      
 115 
     | 
    
         
            +
            respawn
         
     | 
| 
      
 116 
     | 
    
         
            +
            exec #{ruby_path} #{deadpool_admin_path} --foreground #{config_params}
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  EOF
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  if upstart_config_path
         
     | 
| 
      
 121 
     | 
    
         
            +
                    File.open File.join(upstart_config_path), 'w' do |file|
         
     | 
| 
      
 122 
     | 
    
         
            +
                      file.write upstart_conf
         
     | 
| 
      
 123 
     | 
    
         
            +
                    end
         
     | 
| 
      
 124 
     | 
    
         
            +
                    
         
     | 
| 
      
 125 
     | 
    
         
            +
                    if upstart_init_path 
         
     | 
| 
      
 126 
     | 
    
         
            +
                      if File.exists? upstart_init_path
         
     | 
| 
      
 127 
     | 
    
         
            +
                        puts "#{upstart_config_path} has been (over)written."
         
     | 
| 
      
 128 
     | 
    
         
            +
                        puts "#{upstart_init_path} already exists.  It should be a symbolic link that points to #{upstart_script_path}"
         
     | 
| 
      
 129 
     | 
    
         
            +
                        ls_command = "ls -l #{upstart_init_path}"
         
     | 
| 
      
 130 
     | 
    
         
            +
                        # puts ls_command
         
     | 
| 
      
 131 
     | 
    
         
            +
                        # puts `#{ls_command}`
         
     | 
| 
      
 132 
     | 
    
         
            +
                        # puts "ln -s #{upstart_script_path} #{upstart_init_path}"
         
     | 
| 
      
 133 
     | 
    
         
            +
                      else
         
     | 
| 
      
 134 
     | 
    
         
            +
                        `ln -s #{upstart_script_path} #{upstart_init_path}`
         
     | 
| 
      
 135 
     | 
    
         
            +
                      end
         
     | 
| 
      
 136 
     | 
    
         
            +
                    end
         
     | 
| 
      
 137 
     | 
    
         
            +
                  else
         
     | 
| 
      
 138 
     | 
    
         
            +
                    puts upstart_conf
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                # mkdir path/config/pools
         
     | 
| 
      
 143 
     | 
    
         
            +
                #       path/config/environment.yml
         
     | 
| 
      
 144 
     | 
    
         
            +
                #       path/config/pools/example.yml
         
     | 
| 
      
 145 
     | 
    
         
            +
                # mkdir path/lib/deadpool/monitor
         
     | 
| 
      
 146 
     | 
    
         
            +
                #       path/lib/deadpool/monitor
         
     | 
| 
      
 147 
     | 
    
         
            +
                # mkdir path/lib/deadpool/failover_protocol
         
     | 
| 
      
 148 
     | 
    
         
            +
                #       path/lib/deadpool/failover_protocol
         
     | 
| 
      
 149 
     | 
    
         
            +
                def generate_configuration(options)
         
     | 
| 
      
 150 
     | 
    
         
            +
                  path = config_path
         
     | 
| 
      
 151 
     | 
    
         
            +
                  FileUtils.mkdir_p(File.join(path, 'config/pools'))
         
     | 
| 
      
 152 
     | 
    
         
            +
                  FileUtils.mkdir_p(File.join(path, 'lib/deadpool/monitor'))
         
     | 
| 
      
 153 
     | 
    
         
            +
                  FileUtils.mkdir_p(File.join(path, 'lib/deadpool/failover_protocol'))
         
     | 
| 
      
 154 
     | 
    
         
            +
                  File.open File.join(path, 'config/pools/example.yml'), 'w' do |file|
         
     | 
| 
      
 155 
     | 
    
         
            +
                    file.write <<-EOF
         
     | 
| 
      
 156 
     | 
    
         
            +
            pool_name:         'example_database'
         
     | 
| 
      
 157 
     | 
    
         
            +
            primary_host:      '10.1.2.3'
         
     | 
| 
      
 158 
     | 
    
         
            +
            secondary_host:    '10.2.3.4'
         
     | 
| 
      
 159 
     | 
    
         
            +
            check_interval:    1
         
     | 
| 
      
 160 
     | 
    
         
            +
            max_failed_checks: 10
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
            # There can be only one monitor per pool at this time.  The deadpool system
         
     | 
| 
      
 163 
     | 
    
         
            +
            # defines no rules for the monitor configuration except that it is called
         
     | 
| 
      
 164 
     | 
    
         
            +
            # monitor_config: and has monitor_class: defined at the base level.  
         
     | 
| 
      
 165 
     | 
    
         
            +
            # All other configuration variables are plugin specific.
         
     | 
| 
      
 166 
     | 
    
         
            +
            monitor_config:
         
     | 
| 
      
 167 
     | 
    
         
            +
              monitor_class: Mysql
         
     | 
| 
      
 168 
     | 
    
         
            +
              nagios_plugin_path: '/usr/lib/nagios/plugins'
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
            # There can be as many Failover Protocols as you want and you can use 
         
     | 
| 
      
 171 
     | 
    
         
            +
            # the same plugin multiple times.  The deadpool defines no riles for the 
         
     | 
| 
      
 172 
     | 
    
         
            +
            # failover protocol config except that it be an array element of 
         
     | 
| 
      
 173 
     | 
    
         
            +
            # failover_protocol_configs and defines protocol_class at it's base.  The rest
         
     | 
| 
      
 174 
     | 
    
         
            +
            # of the configuration is specific to the failover protocol.
         
     | 
| 
      
 175 
     | 
    
         
            +
            failover_protocol_configs:
         
     | 
| 
      
 176 
     | 
    
         
            +
              - protocol_class: EtcHosts
         
     | 
| 
      
 177 
     | 
    
         
            +
                script_path: '/usr/local/bin/deadpool_line_modifier'
         
     | 
| 
      
 178 
     | 
    
         
            +
                service_host_name: 'master.mysql.example.project.client'
         
     | 
| 
      
 179 
     | 
    
         
            +
                username: 'deadpool'
         
     | 
| 
      
 180 
     | 
    
         
            +
                password: 'p4ssw0rd'
         
     | 
| 
      
 181 
     | 
    
         
            +
                use_sudo: 1
         
     | 
| 
      
 182 
     | 
    
         
            +
                client_hosts:
         
     | 
| 
      
 183 
     | 
    
         
            +
                  - '10.3.4.5'   # app server 1 (web server)
         
     | 
| 
      
 184 
     | 
    
         
            +
                  - '10.4.5.6'   # app server 2 (web server)
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
              - protocol_class: ExecRemoteCommand
         
     | 
| 
      
 187 
     | 
    
         
            +
                test_command: '/etc/init.d/nginx status'
         
     | 
| 
      
 188 
     | 
    
         
            +
                exec_command: '/etc/init.d/nginx restart'
         
     | 
| 
      
 189 
     | 
    
         
            +
                username: 'deadpool'
         
     | 
| 
      
 190 
     | 
    
         
            +
                password: 'p4ssw0rd'
         
     | 
| 
      
 191 
     | 
    
         
            +
                use_sudo: 1
         
     | 
| 
      
 192 
     | 
    
         
            +
                client_hosts:
         
     | 
| 
      
 193 
     | 
    
         
            +
                  - '10.3.4.5'   # app server 1 (web server)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  - '10.4.5.6'   # app server 2 (web server)
         
     | 
| 
      
 195 
     | 
    
         
            +
                    EOF
         
     | 
| 
      
 196 
     | 
    
         
            +
                  end
         
     | 
| 
      
 197 
     | 
    
         
            +
                  
         
     | 
| 
      
 198 
     | 
    
         
            +
                  environment_config_path = File.join(path, 'config/environment.yml')
         
     | 
| 
      
 199 
     | 
    
         
            +
                  environment_conf = <<-EOF
         
     | 
| 
      
 200 
     | 
    
         
            +
            # log_path: '/var/log/deadpool.log'
         
     | 
| 
      
 201 
     | 
    
         
            +
            # log_level: INFO
         
     | 
| 
      
 202 
     | 
    
         
            +
            # system_check_interval: 30
         
     | 
| 
      
 203 
     | 
    
         
            +
            # admin_hostname: 'localhost'
         
     | 
| 
      
 204 
     | 
    
         
            +
            # admin_port: 5507
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                  EOF
         
     | 
| 
      
 207 
     | 
    
         
            +
                  if File.exists? enironment_config_path
         
     | 
| 
      
 208 
     | 
    
         
            +
                    puts "#{environment_config_path} already exists.  Here's what we would have copied there."
         
     | 
| 
      
 209 
     | 
    
         
            +
                    puts environment_conf
         
     | 
| 
      
 210 
     | 
    
         
            +
                  else
         
     | 
| 
      
 211 
     | 
    
         
            +
                    File.open environment_config_path, 'w' do |file|
         
     | 
| 
      
 212 
     | 
    
         
            +
                      file.write environment_conf
         
     | 
| 
      
 213 
     | 
    
         
            +
                    end
         
     | 
| 
      
 214 
     | 
    
         
            +
                  end
         
     | 
| 
      
 215 
     | 
    
         
            +
                  
         
     | 
| 
      
 216 
     | 
    
         
            +
                end
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
              end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,100 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Deadpool
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              class Handler
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                attr_accessor :logger
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :state,
         
     | 
| 
      
 9 
     | 
    
         
            +
                    :failure_count, 
         
     | 
| 
      
 10 
     | 
    
         
            +
                    :check_interval, 
         
     | 
| 
      
 11 
     | 
    
         
            +
                    :max_failed_checks, 
         
     | 
| 
      
 12 
     | 
    
         
            +
                    :pool_name, 
         
     | 
| 
      
 13 
     | 
    
         
            +
                    :primary_host, 
         
     | 
| 
      
 14 
     | 
    
         
            +
                    :secondary_host
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def initialize(config, logger)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @state             = Deadpool::State.new config[:pool_name], self.class
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @state             = Deadpool::State.new "Deadpool::Handler - #{config[:pool_name]}"
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @config            = config
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @logger            = logger
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @pool_name         = config[:pool_name]
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @check_interval    = config[:check_interval]
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @max_failed_checks = config[:max_failed_checks]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @primary_host      = config[:primary_host]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @secondary_host    = config[:secondary_host]
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @failure_count     = 0
         
     | 
| 
      
 27 
     | 
    
         
            +
                  instantiate_monitor
         
     | 
| 
      
 28 
     | 
    
         
            +
                  instantiate_failover_protocols
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @state.set_state(OK, "Handler initialized.")
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def monitor_pool(timer)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  if @monitor.primary_ok?
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @failure_count = 0
         
     | 
| 
      
 35 
     | 
    
         
            +
                    @state.set_state(OK, "Primary Check OK.")
         
     | 
| 
      
 36 
     | 
    
         
            +
                    logger.info "#{@pool_name} Primary Check Okay.  Failure Count set to 0."
         
     | 
| 
      
 37 
     | 
    
         
            +
                  else
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @failure_count += 1
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @state.set_state(WARNING, "Primary Check failed #{@failure_count} times")
         
     | 
| 
      
 40 
     | 
    
         
            +
                    logger.warn "#{@pool_name} Primary Check Failed.  Failure Count at #{@failure_count}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  if @failure_count >= @max_failed_checks
         
     | 
| 
      
 44 
     | 
    
         
            +
                    timer.cancel
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @state.set_state(WARNING, "Failure threshold exceeded.  Failover Protocol Initiated.")
         
     | 
| 
      
 46 
     | 
    
         
            +
                    logger.error "#{@pool_name} primary is dead.  Initiating Failover Protocol."
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    success = true
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @failover_protocols.each do |failover_protocol|
         
     | 
| 
      
 50 
     | 
    
         
            +
                      success = success && failover_protocol.initiate_failover_protocol!
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    if success
         
     | 
| 
      
 54 
     | 
    
         
            +
                      logger.warn "Failover Protocol Finished."
         
     | 
| 
      
 55 
     | 
    
         
            +
                      @state.set_state(WARNING, "Failover Protocol in place.")
         
     | 
| 
      
 56 
     | 
    
         
            +
                      @state.lock
         
     | 
| 
      
 57 
     | 
    
         
            +
                    else
         
     | 
| 
      
 58 
     | 
    
         
            +
                      logger.error "Failover Protocol Failed!"
         
     | 
| 
      
 59 
     | 
    
         
            +
                      @state.set_state(CRITICAL, "Failover Protocol Failed!")
         
     | 
| 
      
 60 
     | 
    
         
            +
                      @state.lock
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def system_check
         
     | 
| 
      
 66 
     | 
    
         
            +
                  snapshot = Deadpool::StateSnapshot.new @state
         
     | 
| 
      
 67 
     | 
    
         
            +
                  snapshot.add_child @monitor.system_check
         
     | 
| 
      
 68 
     | 
    
         
            +
                  @failover_protocols.each do |failover_protocol|
         
     | 
| 
      
 69 
     | 
    
         
            +
                    # logger.debug failover_protocol.inspect
         
     | 
| 
      
 70 
     | 
    
         
            +
                    snapshot.add_child failover_protocol.system_check
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  return snapshot
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def promote_server(server)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  # This will stop at the first failure
         
     | 
| 
      
 78 
     | 
    
         
            +
                  @config[server] && @failover_protocols.all? do |failover_protocol|
         
     | 
| 
      
 79 
     | 
    
         
            +
                    failover_protocol.promote_to_primary @config[server]
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                protected
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                def instantiate_monitor
         
     | 
| 
      
 86 
     | 
    
         
            +
                  monitor_class = Deadpool::Monitor.const_get(@config[:monitor_config][:monitor_class])
         
     | 
| 
      
 87 
     | 
    
         
            +
                  @monitor      = monitor_class.new(@config, @config[:monitor_config], logger)
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def instantiate_failover_protocols
         
     | 
| 
      
 91 
     | 
    
         
            +
                  @failover_protocols = []
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @config[:failover_protocol_configs].each do |failover_config|
         
     | 
| 
      
 93 
     | 
    
         
            +
                    failover_protocol_class = Deadpool::FailoverProtocol.const_get(failover_config[:protocol_class])
         
     | 
| 
      
 94 
     | 
    
         
            +
                    @failover_protocols << failover_protocol_class.new(@config, failover_config, logger)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            module Deadpool
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              class Helper
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def self.symbolize_keys(arg)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  case arg
         
     | 
| 
      
 8 
     | 
    
         
            +
                  when Array
         
     | 
| 
      
 9 
     | 
    
         
            +
                    arg.map { |elem| symbolize_keys elem }
         
     | 
| 
      
 10 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 11 
     | 
    
         
            +
                    Hash[
         
     | 
| 
      
 12 
     | 
    
         
            +
                      arg.map { |key, value|  
         
     | 
| 
      
 13 
     | 
    
         
            +
                        k = key.is_a?(String) ? key.to_sym : key
         
     | 
| 
      
 14 
     | 
    
         
            +
                        v = symbolize_keys value
         
     | 
| 
      
 15 
     | 
    
         
            +
                        [k,v]
         
     | 
| 
      
 16 
     | 
    
         
            +
                      }]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  else
         
     | 
| 
      
 18 
     | 
    
         
            +
                    arg
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def self.configure(options)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  default_config = YAML.load(File.read(File.join(File.dirname(__FILE__), '../../config/default_environment.yml')))
         
     | 
| 
      
 24 
     | 
    
         
            +
                  user_config    = YAML.load(File.read(File.join(options[:config_path], 'config/environment.yml')))
         
     | 
| 
      
 25 
     | 
    
         
            +
                  config         = Deadpool::Helper.symbolize_keys default_config.merge(user_config).merge(options)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  return config
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def self.setup_logger(config)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  logger       = Logger.new(config[:log_path])
         
     | 
| 
      
 32 
     | 
    
         
            +
                  logger.level = Logger.const_get(config[:log_level].upcase)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  
         
     | 
| 
      
 34 
     | 
    
         
            +
                  return logger
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     |