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,58 @@
|
|
1
|
+
module Deadpool
|
2
|
+
module Monitor
|
3
|
+
class Base
|
4
|
+
attr_accessor :logger
|
5
|
+
|
6
|
+
def initialize(config, monitor_config, logger)
|
7
|
+
@config, @monitor_config, @logger = config, monitor_config, logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def primary_ok?
|
11
|
+
raise NotImplementedError, "primary_ok? is not implemented"
|
12
|
+
end
|
13
|
+
|
14
|
+
def secondary_ok?
|
15
|
+
raise NotImplementedError, "secondary_ok? is not implemented"
|
16
|
+
end
|
17
|
+
|
18
|
+
def system_check
|
19
|
+
update_state
|
20
|
+
take_snapshot
|
21
|
+
end
|
22
|
+
|
23
|
+
def state
|
24
|
+
@state ||= Deadpool::State.new name, self.class
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def update_state
|
30
|
+
primary_okay, secondary_okay = primary_ok?, secondary_ok?
|
31
|
+
logger.debug "PrimaryOkay? #{primary_okay}, SecondaryOkay? #{secondary_okay}"
|
32
|
+
|
33
|
+
args = case [ primary_okay, secondary_okay ]
|
34
|
+
when [true, true]
|
35
|
+
[ OK, "Primary and Secondary are up." ]
|
36
|
+
when [false, true]
|
37
|
+
[ WARNING, "Primary is down. Secondary is up." ]
|
38
|
+
when [true, false]
|
39
|
+
[ WARNING, "Primary is up. Secondary is down." ]
|
40
|
+
when [false, false]
|
41
|
+
[ CRITICAL, "Primary and Secondary are down." ]
|
42
|
+
else
|
43
|
+
[ CRITICAL, "Implementation Error." ]
|
44
|
+
end
|
45
|
+
|
46
|
+
self.state.set_state(*args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def take_snapshot
|
50
|
+
StateSnapshot.new(self.state)
|
51
|
+
end
|
52
|
+
|
53
|
+
def name
|
54
|
+
@monitor_config[:name].nil? ? '' : @monitor_config[:name]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Assumes at a minimum the following config:
|
2
|
+
# --
|
3
|
+
# primary_host: '127.0.0.1'
|
4
|
+
# secondary_host: '127.0.0.1'
|
5
|
+
# monitor_config:
|
6
|
+
# nagios_plugin_path: '/usr/lib/nagios/plugins/check_something'
|
7
|
+
|
8
|
+
module Deadpool
|
9
|
+
|
10
|
+
module Monitor
|
11
|
+
|
12
|
+
class GenericNagios < Base
|
13
|
+
|
14
|
+
def primary_ok?
|
15
|
+
check_host(@config[:primary_host])
|
16
|
+
end
|
17
|
+
|
18
|
+
def secondary_ok?
|
19
|
+
check_host(@config[:secondary_host])
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def check_host(host)
|
25
|
+
check_command = "#{nagios_plugin_path} -H #{host} #{nagios_options}"
|
26
|
+
logger.debug check_command
|
27
|
+
status_message = `#{check_command}`
|
28
|
+
exit_status = $?
|
29
|
+
logger.debug "Generic Nagios Check Status Message: #{status_message}"
|
30
|
+
logger.debug "Generic Nagios Check Exit Status: #{exit_status}"
|
31
|
+
|
32
|
+
return exit_status == 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def nagios_plugin_path
|
36
|
+
@config[:monitor_config][:nagios_plugin_path]
|
37
|
+
end
|
38
|
+
|
39
|
+
def nagios_options
|
40
|
+
@config[:monitor_config][:nagios_options]
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Assumes at a minimum the following config:
|
2
|
+
# --
|
3
|
+
# primary_host: '127.0.0.1'
|
4
|
+
# secondary_host: '127.0.0.1'
|
5
|
+
# monitor_config:
|
6
|
+
# nagios_plugin_path: '/usr/lib/nagios/plugins/check_mysql'
|
7
|
+
|
8
|
+
module Deadpool
|
9
|
+
|
10
|
+
module Monitor
|
11
|
+
|
12
|
+
class Mysql < Base
|
13
|
+
|
14
|
+
def primary_ok?
|
15
|
+
return check_mysql(@config[:primary_host])
|
16
|
+
end
|
17
|
+
|
18
|
+
def secondary_ok?
|
19
|
+
return check_mysql(@config[:secondary_host])
|
20
|
+
# && check_mysql_slave(@config[:secondary_host])
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def check_mysql(host)
|
26
|
+
check_command = "#{nagios_plugin_path} -H #{host} -u '#{username}' -p '#{password}'"
|
27
|
+
logger.debug check_command
|
28
|
+
status_message = `#{check_command}`
|
29
|
+
exit_status = $?
|
30
|
+
logger.debug "MySQL Check Status Message: #{status_message}"
|
31
|
+
logger.debug "MySQL Check Exit Status: #{exit_status}"
|
32
|
+
|
33
|
+
return exit_status == 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_mysql_slave(host)
|
37
|
+
check_command = "#{nagios_plugin_path} -H #{host} -u '#{username}' -p '#{password}' --check-slave"
|
38
|
+
logger.debug check_command
|
39
|
+
status_message = `#{check_command}`
|
40
|
+
exit_status = $?
|
41
|
+
logger.debug "MySQL Check Status Message: #{status_message}"
|
42
|
+
logger.debug "MySQL Check Exit Status: #{exit_status}"
|
43
|
+
|
44
|
+
return exit_status == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def nagios_plugin_path
|
48
|
+
@config[:monitor_config][:nagios_plugin_path]
|
49
|
+
end
|
50
|
+
|
51
|
+
def username
|
52
|
+
@config[:monitor_config][:username]
|
53
|
+
end
|
54
|
+
|
55
|
+
def password
|
56
|
+
@config[:monitor_config][:password]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Assumes at a minimum the following config:
|
2
|
+
# --
|
3
|
+
# primary_host: '127.0.0.1'
|
4
|
+
# secondary_host: '127.0.0.1'
|
5
|
+
# monitor_config:
|
6
|
+
# redis_args: '[-h host] [-p port] [-a authpw] [-r repeat_times] [-n db_num]'
|
7
|
+
|
8
|
+
module Deadpool
|
9
|
+
module Monitor
|
10
|
+
class Redis < Base
|
11
|
+
|
12
|
+
def primary_ok?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def secondary_ok?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
require 'optparse'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'strscan'
|
5
|
+
|
6
|
+
|
7
|
+
module Deadpool
|
8
|
+
|
9
|
+
module Options
|
10
|
+
|
11
|
+
def parse_options(argv)
|
12
|
+
@options = self.parse_command_line(argv)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_command_line(argv)
|
16
|
+
options = {}
|
17
|
+
options[:config_path] = '/etc/deadpool'
|
18
|
+
options[:daemonize] = nil
|
19
|
+
|
20
|
+
@option_parser = OptionParser.new do |opts|
|
21
|
+
opts.banner = "Usage: deadpool_hosts {help|full_report|nagios_report} [options]"
|
22
|
+
|
23
|
+
opts.separator "Commands:"
|
24
|
+
opts.on("-h", "--help", "Print this help message.") do |help|
|
25
|
+
options[:help] = true
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-d", "--daemon", "Background the server.") do |daemon|
|
29
|
+
options[:daemonize] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.separator "Options:"
|
33
|
+
opts.on("--config_path=PATH", String,
|
34
|
+
"Path to configs and custom plugins. #{options[:config_path]} by default.") do |config_path|
|
35
|
+
options[:config_path] = config_path
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
remaining_arguments = @option_parser.parse! argv
|
40
|
+
|
41
|
+
unless remaining_arguments.empty?
|
42
|
+
help "[#{remaining_arguments.join(' ')}] is not understood."
|
43
|
+
end
|
44
|
+
|
45
|
+
return options
|
46
|
+
end
|
47
|
+
|
48
|
+
def help(message=nil)
|
49
|
+
unless message.nil?
|
50
|
+
puts message
|
51
|
+
end
|
52
|
+
puts @option_parser.help
|
53
|
+
exit 4
|
54
|
+
end
|
55
|
+
|
56
|
+
def config_path
|
57
|
+
return @options[:config_path]
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Deadpool
|
4
|
+
|
5
|
+
class Server
|
6
|
+
|
7
|
+
# include Deadpool::Options
|
8
|
+
include Deadpool::Daemonizer
|
9
|
+
|
10
|
+
attr_accessor :logger
|
11
|
+
attr_reader :config
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
@options = options
|
15
|
+
@state = Deadpool::State.new self.class.to_s
|
16
|
+
@config = Deadpool::Helper.configure(@options)
|
17
|
+
@logger = Deadpool::Helper.setup_logger(@config)
|
18
|
+
|
19
|
+
# if @options[:daemonize]
|
20
|
+
# options = @options[:pid_file].nil? ? {} : {:pid => @options[:pid_file]}
|
21
|
+
# self.daemonize options
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# load_handlers
|
25
|
+
# start_deadpool_handlers
|
26
|
+
# start_command_server
|
27
|
+
# schedule_system_check
|
28
|
+
end
|
29
|
+
|
30
|
+
def run(daemonize)
|
31
|
+
EventMachine::run {
|
32
|
+
if daemonize
|
33
|
+
options = @options[:pid_file].nil? ? {} : {:pid => @options[:pid_file]}
|
34
|
+
self.daemonize options
|
35
|
+
end
|
36
|
+
|
37
|
+
load_handlers
|
38
|
+
start_deadpool_handlers
|
39
|
+
start_command_server
|
40
|
+
schedule_system_check
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_handlers
|
45
|
+
@handlers = {}
|
46
|
+
@state.set_state(OK, 'Loading Handlers.')
|
47
|
+
|
48
|
+
Dir[@options[:config_path] + '/config/pools/*.yml'].each do |pool_yml|
|
49
|
+
pool_config = Deadpool::Helper.symbolize_keys YAML.load(File.read(pool_yml))
|
50
|
+
@handlers[pool_config[:pool_name]] = Deadpool::Handler.new(pool_config, logger)
|
51
|
+
end
|
52
|
+
|
53
|
+
@state.set_state(OK, 'Handlers loaded.')
|
54
|
+
end
|
55
|
+
|
56
|
+
def start_deadpool_handlers
|
57
|
+
@handlers.each_value do |handler|
|
58
|
+
timer = EventMachine::PeriodicTimer.new(handler.check_interval) do
|
59
|
+
handler.monitor_pool(timer)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def schedule_system_check
|
65
|
+
timer = EventMachine::PeriodicTimer.new(@config[:system_check_interval]) do
|
66
|
+
system_check(true)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def start_command_server
|
71
|
+
EventMachine::start_server @config[:admin_hostname], @config[:admin_port], Deadpool::AdminServer do |connection|
|
72
|
+
connection.deadpool_server = self
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def system_check(force=false)
|
77
|
+
if force || @cached_state_snapshot.nil?
|
78
|
+
@state.reset!
|
79
|
+
@cached_state_snapshot = Deadpool::StateSnapshot.new @state
|
80
|
+
|
81
|
+
@handlers.each_value do |handler|
|
82
|
+
@cached_state_snapshot.add_child handler.system_check
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
return @cached_state_snapshot
|
87
|
+
end
|
88
|
+
|
89
|
+
def promote_server(pool_name, server)
|
90
|
+
unless @handlers[pool_name].nil?
|
91
|
+
# logger.debug "Pool Name: #{pool_name}, Server: #{server}"
|
92
|
+
return @handlers[pool_name].promote_server server
|
93
|
+
else
|
94
|
+
logger.error "'#{pool_name}' pool not found."
|
95
|
+
return false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Deadpool
|
5
|
+
|
6
|
+
class State
|
7
|
+
|
8
|
+
attr_reader :name, :timestamp, :status_code, :error_messages, :all_messages
|
9
|
+
|
10
|
+
def initialize(name, klass='')
|
11
|
+
names = []
|
12
|
+
names << name unless (name.nil? and name.blank?)
|
13
|
+
names << klass.to_s unless (klass.nil? and klass.blank?)
|
14
|
+
@name = names.join " - "
|
15
|
+
@locked = false
|
16
|
+
reset!
|
17
|
+
end
|
18
|
+
|
19
|
+
def lock
|
20
|
+
@locked = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def unlock
|
24
|
+
@locked = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_state(code, message)
|
28
|
+
unless @locked
|
29
|
+
if code == OK
|
30
|
+
@timestamp = Time.now
|
31
|
+
@status_code = OK
|
32
|
+
@error_messages = []
|
33
|
+
@all_messages = [message]
|
34
|
+
else
|
35
|
+
@timestamp = Time.now
|
36
|
+
@status_code = code
|
37
|
+
@error_messages = [message]
|
38
|
+
@all_messages = []
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def reset!(message=nil)
|
44
|
+
unless @locked
|
45
|
+
@timestamp = Time.now
|
46
|
+
@status_code = OK
|
47
|
+
@error_messages = []
|
48
|
+
@all_messages = message.nil? ? [] : [message]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def escalate_status_code(code)
|
53
|
+
unless @locked
|
54
|
+
@timestamp = Time.now
|
55
|
+
|
56
|
+
if code >= @status_code
|
57
|
+
@status_code = code
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_message(message)
|
63
|
+
unless @locked
|
64
|
+
@timestamp = Time.now
|
65
|
+
@all_messages << message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_error_message(message)
|
70
|
+
unless @locked
|
71
|
+
@timestamp = Time.now
|
72
|
+
@error_messages << message
|
73
|
+
# @all_messages << message
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module Deadpool
|
5
|
+
|
6
|
+
class StateSnapshot
|
7
|
+
|
8
|
+
def initialize(state)
|
9
|
+
@name = state.name
|
10
|
+
@timestamp = state.timestamp
|
11
|
+
@status_code = state.status_code
|
12
|
+
@all_messages = state.all_messages
|
13
|
+
@error_messages = state.error_messages
|
14
|
+
@children = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_child(child)
|
18
|
+
@children << child
|
19
|
+
end
|
20
|
+
|
21
|
+
def overall_status
|
22
|
+
@children.map { |child| child.overall_status }.push(@status_code).max
|
23
|
+
end
|
24
|
+
|
25
|
+
def all_error_messages
|
26
|
+
@children.inject(@error_messages) do |arr, child|
|
27
|
+
arr + child.all_error_messages
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def nagios_report
|
32
|
+
message = ''
|
33
|
+
if overall_status != OK
|
34
|
+
message += all_error_messages.join(' | ')
|
35
|
+
end
|
36
|
+
|
37
|
+
message += " last checked #{(Time.now - @timestamp).round} seconds ago."
|
38
|
+
|
39
|
+
"#{status_code_to_s(overall_status)} - #{message}\n"
|
40
|
+
end
|
41
|
+
|
42
|
+
def full_report
|
43
|
+
output = "System Status: #{status_code_to_s(overall_status)}\n\n"
|
44
|
+
output += self.to_s
|
45
|
+
|
46
|
+
return output
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s(indent=0)
|
50
|
+
indent_space = ' ' * indent
|
51
|
+
|
52
|
+
output = "#{indent_space}#{@name}\n"
|
53
|
+
output += "#{indent_space}#{status_code_to_s(@status_code)} - checked #{(Time.now - @timestamp).round} seconds ago.\n"
|
54
|
+
unless @error_messages.empty?
|
55
|
+
output += "#{indent_space}!!! #{@error_messages.join("\n#{indent_space}!!! ")}\n"
|
56
|
+
end
|
57
|
+
unless @all_messages.empty?
|
58
|
+
output += "#{indent_space}#{@all_messages.join("\n#{indent_space}")}\n"
|
59
|
+
end
|
60
|
+
output += "\n"
|
61
|
+
|
62
|
+
@children.each do |child|
|
63
|
+
output += child.to_s(indent+1)
|
64
|
+
end
|
65
|
+
|
66
|
+
return output
|
67
|
+
end
|
68
|
+
|
69
|
+
def status_code_to_s(code)
|
70
|
+
case code
|
71
|
+
when OK then 'OK'
|
72
|
+
when WARNING then 'WARNING'
|
73
|
+
when CRITICAL then 'CRITICAL'
|
74
|
+
else
|
75
|
+
'UNKNOWN'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|