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.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ require File.join(File.dirname(File.dirname(__FILE__)), 'lib', 'deadpool')
6
+
7
+ Deadpool::Admin.new(ARGV).run
8
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ require File.join(File.dirname(File.dirname(__FILE__)), 'lib', 'deadpool')
6
+
7
+ Deadpool::Generator.new(ARGV).run
8
+
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'strscan'
6
+
7
+ class DeadpoolHosts
8
+
9
+ def run
10
+ @options = self.parse_command_line
11
+ self.execute_command(@options)
12
+ end
13
+
14
+ def parse_command_line
15
+ options = OpenStruct.new
16
+ options.command_count = 0
17
+
18
+ @option_parser = OptionParser.new do |opts|
19
+ opts.banner = "Usage: deadpool_hosts {help|test|verify|switch} [options]"
20
+
21
+ opts.separator "Commands:"
22
+ opts.on("-h", "--help", "Print this help message.") do |h|
23
+ options.help = h
24
+ options.command_count += 1
25
+ options.command = :help
26
+ end
27
+ opts.on("--test", "Test that /etc/hosts can be touched.") do |t|
28
+ options.test = t
29
+ options.command_count += 1
30
+ options.command = :test
31
+ end
32
+ opts.on("--verify", "Verify that /etc/hosts hast the IP address that you think it should.") do |v|
33
+ options.verify = v
34
+ options.command_count += 1
35
+ options.command = :verify
36
+ end
37
+ opts.on("--switch", "Change the IP address in /etc/hosts") do |s|
38
+ options.switch = s
39
+ options.command_count += 1
40
+ options.command = :switch
41
+ end
42
+
43
+ opts.separator "Options:"
44
+ opts.on("--host_name=HOSTNAME", String, "Hostname to regex on in /etc/hosts") do |h|
45
+ options.host_name = h
46
+ end
47
+ opts.on("--ip_address=IP_ADDRESS", String, "IP Address") do |ip|
48
+ options.ip_address = ip
49
+ end
50
+ opts.on("--hosts_file=HOST_FILE_PATH", String, "Alternate Path for the Hosts file") do |hosts|
51
+ options.hosts_file = hosts
52
+ end
53
+ opts.on("--verbose", "Verbose Messages.") do |v|
54
+ options.verbose = v
55
+ end
56
+ opts.on("--debug", "Creates a dummy hosts file at /tmp/hosts and operates on that. Overrides the alternate hosts file option.") do |d|
57
+ options.debug = d
58
+ end
59
+ end
60
+
61
+ # Parse and verify
62
+ remaining_arguments = @option_parser.parse!
63
+
64
+ unless remaining_arguments.empty?
65
+ help "[#{remaining_arguments.join(' ')}] is not understood."
66
+ end
67
+
68
+ if options.command_count == 0
69
+ help "You must specify a command."
70
+ end
71
+
72
+ option_sum = (options.help ? 1 : 0) + (options.test ? 1 : 0) + (options.verify ? 1 : 0) + (options.switch ? 1 : 0)
73
+ if option_sum > 1
74
+ help "Only one command can be specified."
75
+ end
76
+
77
+ if options.verify
78
+ options_valid = true
79
+
80
+ if options.ip_address.nil?
81
+ puts "The verify command requires an ip_address argument."
82
+ options_valid = false
83
+ elsif ! options.ip_address.match(/\d+\.\d+\.\d+\.\d+/)
84
+ puts "ip_address doesn't look valid."
85
+ options_valid = false
86
+ end
87
+
88
+ if options.host_name.nil?
89
+ puts "The verify command requires a host_name argument."
90
+ options_valid = false
91
+ elsif options.host_name.size < 4
92
+ puts "The host_name parameter must be longer than 4 characters."
93
+ options_valid = false
94
+ end
95
+
96
+ unless options_valid
97
+ help
98
+ end
99
+ end
100
+
101
+
102
+ return options
103
+ end
104
+
105
+ def execute_command(options)
106
+ case options.command
107
+ when :help
108
+ help
109
+ when :test
110
+ test options
111
+ when :verify
112
+ verify options
113
+ when :switch
114
+ switch options
115
+ else
116
+ help
117
+ end
118
+ end
119
+
120
+ def help(message=nil)
121
+ unless message.nil?
122
+ puts message
123
+ end
124
+ puts @option_parser.help
125
+ exit 4
126
+ end
127
+
128
+ def test(options)
129
+ path = host_file_path(options)
130
+ out = `touch #{path} 2> /dev/null`
131
+
132
+ if $? == 0
133
+ puts "OK - #{path} is writable"
134
+ exit 0
135
+ else
136
+ puts "ERROR - #{path} is NOT writable"
137
+ exit 1
138
+ end
139
+ end
140
+
141
+ def verify(options)
142
+ scanner = StringScanner.new File.read(host_file_path(options))
143
+ verified = scanner.exist?(/#{options.ip_address}\s+#{options.host_name}\s+/)
144
+
145
+ if verified
146
+ puts "OK - #{options.host_name} was verified to point at #{options.ip_address}"
147
+ exit 0
148
+ else
149
+ puts "ERROR - #{options.host_name} does not point at #{options.ip_address}"
150
+ exit 1
151
+ end
152
+ end
153
+
154
+ def switch(options)
155
+ path = host_file_path(options)
156
+ scanner = StringScanner.new File.read(path)
157
+ host_exists = scanner.exist?(/\s+#{options.host_name}\s+/)
158
+
159
+ if host_exists
160
+ scanner.reset
161
+ scanner.scan_until(/\n.+#{options.host_name}\s*\n/)
162
+ pre_match = scanner.pre_match
163
+ matched = scanner.matched
164
+ post_match = scanner.post_match
165
+ verbose_message "\nPreceding lines: \n#{pre_match}"
166
+ verbose_message "\nMatched host definition: \n#{matched}"
167
+ verbose_message "\nFollowing lines: \n#{post_match}"
168
+ replacement_file = pre_match
169
+ replacement_file += "\n#{options.ip_address} #{options.host_name}\n"
170
+ replacement_file += post_match
171
+ verbose_message "Replacement hosts file:\n#{replacement_file}"
172
+ verbose_message "Writing hosts file..."
173
+ hosts_file = File.open path, 'w'
174
+ hosts_file.write replacement_file
175
+ hosts_file.close
176
+ puts "OK - Host definition replaced successfully."
177
+ exit 0
178
+ else
179
+ replacement_file = File.read(path)
180
+ replacement_file += "\n#{options.ip_address} #{options.host_name}\n"
181
+ verbose_message "Replacement hosts file:\n#{replacement_file}"
182
+ verbose_message "Writing hosts file..."
183
+ hosts_file = File.open path, 'w'
184
+ hosts_file.write replacement_file
185
+ hosts_file.close
186
+ puts "OK - Host definition added successfully."
187
+ exit 0
188
+ end
189
+ end
190
+
191
+ def host_file_path(options)
192
+ if options.debug
193
+ file = File.open '/tmp/hosts', 'w'
194
+ file.write DATA.read
195
+ file.close
196
+ return '/tmp/hosts'
197
+ elsif options.hosts_file
198
+ return options.hosts_file
199
+ else
200
+ return '/etc/hosts'
201
+ end
202
+ end
203
+
204
+ def verbose_message(message)
205
+ if @options.verbose
206
+ puts message
207
+ end
208
+ end
209
+
210
+ end
211
+
212
+ EtcHostsSwitch.new.run
213
+
214
+ __END__
215
+ 127.0.0.1 localhost localhost.localdomain
216
+ 184.106.205.117 web-01
217
+
218
+ 123.456.78.9 test_host_name
219
+ 123.456.78.9 other_service_that_shouldnt_be.touched
220
+ 123.456.78.9 test_host_name.similar
221
+
222
+
@@ -0,0 +1,7 @@
1
+
2
+ pid_file: '/var/run/deadpool.pid'
3
+ log_file: '/var/log/deadpool.log'
4
+ log_level: INFO
5
+ admin_hostname: 'localhost'
6
+ admin_port: 5507
7
+ system_check_interval: 30
@@ -0,0 +1,17 @@
1
+ # Deadpool Service
2
+
3
+ description "Deadpool Server"
4
+ author "Kirt Fitzpatrick <kirt.fitzpatrick@akqa.com>"
5
+
6
+ start on (net-device-up
7
+ and local-filesystems)
8
+
9
+ stop on runlevel [016]
10
+
11
+ respawn
12
+
13
+ # env HOME=/usr/local/bin/deadpool
14
+ env RUBY=/usr/local/bin/ruby
15
+ umask 007
16
+
17
+ exec $RUBY $HOME/bin/deadpool_admin --start
data/lib/deadpool.rb ADDED
@@ -0,0 +1,45 @@
1
+
2
+ require 'rubygems'
3
+ require 'eventmachine'
4
+ require 'logger'
5
+ require 'yaml'
6
+
7
+ $:.unshift File.dirname(__FILE__)
8
+
9
+
10
+ module Deadpool
11
+ OK = 0
12
+ WARNING = 1
13
+ CRITICAL = 2
14
+ UNKNOWN = 3
15
+
16
+ autoload :Admin, 'deadpool/admin'
17
+ autoload :AdminServer, 'deadpool/admin_server'
18
+ autoload :CommandLineServer, 'deadpool/command_line_server'
19
+ autoload :Daemonizer, 'deadpool/daemonizer'
20
+ autoload :Generator, 'deadpool/generator'
21
+ autoload :Handler, 'deadpool/handler'
22
+ autoload :Helper, 'deadpool/helper'
23
+ autoload :Options, 'deadpool/options'
24
+ autoload :State, 'deadpool/state'
25
+ autoload :StateSnapshot, 'deadpool/state_snapshot'
26
+ autoload :Server, 'deadpool/server'
27
+
28
+
29
+ module FailoverProtocol
30
+ autoload :Base, 'deadpool/failover_protocol'
31
+ end
32
+
33
+ module Monitor
34
+ autoload :Base, 'deadpool/monitor/base'
35
+ autoload :Mysql, 'deadpool/monitor/mysql'
36
+ autoload :GenericNagios, 'deadpool/monitor/generic_nagios'
37
+ end
38
+
39
+ module FailoverProtocol
40
+ autoload :EtcHosts, 'deadpool/failover_protocol/etc_hosts'
41
+ autoload :ExecRemoteCommand, 'deadpool/failover_protocol/exec_remote_command'
42
+ end
43
+
44
+ class DeadpoolError < StandardError; end
45
+ end
@@ -0,0 +1,197 @@
1
+
2
+ require 'optparse'
3
+ require 'ostruct'
4
+ require 'strscan'
5
+ require 'socket'
6
+ require 'json'
7
+
8
+
9
+ module Deadpool
10
+
11
+ class Admin
12
+
13
+ def initialize(argv)
14
+ @argv = argv
15
+ end
16
+
17
+ def run
18
+ @options = self.parse_command_line
19
+ @config = Deadpool::Helper.configure @options
20
+
21
+ self.execute_command(@options)
22
+ end
23
+
24
+ def parse_command_line
25
+ options = Hash.new
26
+ options[:command_count] = 0
27
+ options[:config_path] = '/etc/deadpool'
28
+
29
+ @option_parser = OptionParser.new do |opts|
30
+ opts.banner = "Usage: deadpool_hosts --command [options]"
31
+
32
+ opts.separator "Commands:"
33
+ opts.on("-h", "--help", "Print this help message.") do |help|
34
+ options[:command_count] += 1
35
+ options[:command] = :help
36
+ end
37
+ opts.on("--full_report", "Give the full system report.") do |full_report|
38
+ options[:command_count] += 1
39
+ options[:command] = :full_report
40
+ end
41
+ opts.on("--nagios_report", "Report system state in Nagios plugin format.") do |nagios_report|
42
+ options[:command_count] += 1
43
+ options[:command] = :nagios_report
44
+ end
45
+ opts.on("--promote_server", "Promote specified server to the master.") do |nagios_report|
46
+ options[:command_count] += 1
47
+ options[:command] = :promote_server
48
+ end
49
+ opts.on("--stop", "Stop the server.") do |stop|
50
+ options[:command_count] += 1
51
+ options[:command] = :stop
52
+ end
53
+ opts.on("--start", "Start the server in the background.") do |stop|
54
+ options[:command_count] += 1
55
+ options[:command] = :start
56
+ end
57
+ opts.on("--foreground", "Start the server in the foreground.") do |stop|
58
+ options[:command_count] += 1
59
+ options[:command] = :foreground
60
+ end
61
+
62
+ opts.separator "Options:"
63
+ opts.on("--server=SERVER_LABEL", String, "primary_host or secondary_host.") do |server|
64
+ options[:server] = server
65
+ end
66
+ opts.on("--pool=POOL_NAME", String, "Deadpool name to operate on.") do |pool|
67
+ options[:pool] = pool
68
+ end
69
+ opts.on("--config_path=PATH", String,
70
+ "Path to configs and custom plugins. #{options[:config_path]} by default.") do |config_path|
71
+ options[:config_path] = config_path
72
+ end
73
+ end
74
+
75
+ remaining_arguments = @option_parser.parse! @argv
76
+
77
+ unless remaining_arguments.empty?
78
+ help "[#{remaining_arguments.join(' ')}] is not understood."
79
+ end
80
+
81
+ if options[:command_count] == 0
82
+ help "You must specify a command."
83
+ end
84
+
85
+ return options
86
+ end
87
+
88
+ def execute_command(options)
89
+ case options[:command]
90
+ when :help
91
+ help
92
+ when :full_report
93
+ full_report options
94
+ when :nagios_report
95
+ nagios_report options
96
+ when :promote_server
97
+ promote_server options
98
+ when :stop
99
+ stop options
100
+ when :start
101
+ start options
102
+ when :foreground
103
+ foreground options
104
+ else
105
+ help
106
+ end
107
+ end
108
+
109
+ def help(message=nil)
110
+ unless message.nil?
111
+ puts message
112
+ end
113
+ puts @option_parser.help
114
+ exit 4
115
+ end
116
+
117
+ def full_report(options)
118
+ puts send_command_to_deadpool_server :command => 'full_report'
119
+ end
120
+
121
+ def nagios_report(options)
122
+ response = send_command_to_deadpool_server :command => 'nagios_report'
123
+
124
+ if (response.to_s =~ /^OK/) != nil
125
+ puts response.to_s
126
+ exit OK
127
+ elsif (response.to_s =~ /^WARNING/) != nil
128
+ puts response.to_s
129
+ exit WARNING
130
+ elsif (response.to_s =~ /^CRITICAL/) != nil
131
+ puts response.to_s
132
+ exit CRITICAL
133
+ elsif (response.to_s =~ /^UNKNOWN/) != nil
134
+ puts response.to_s
135
+ exit UNKNOWN
136
+ else
137
+ puts "UNKNOWN - #{response.to_s}"
138
+ exit UNKNOWN
139
+ end
140
+ end
141
+
142
+ def promote_server(options)
143
+ error_messages = []
144
+
145
+ if options[:pool].nil?
146
+ error_messages << "Promoting server requires --pool argument."
147
+ end
148
+
149
+ if options[:server].nil?
150
+ error_messages << "Promoting server requires --server argument."
151
+ end
152
+
153
+ unless error_messages.empty?
154
+ help error_messages.join "\n"
155
+ end
156
+
157
+ puts send_command_to_deadpool_server :command => 'promote_server', :pool => options[:pool], :server => options[:server]
158
+ end
159
+
160
+ def stop(options)
161
+ puts send_command_to_deadpool_server :command => 'stop'
162
+ end
163
+
164
+ def start(options)
165
+ puts Deadpool::Server.new(options).run(true)
166
+ end
167
+
168
+ def foreground(options)
169
+ puts Deadpool::Server.new(options).run(false)
170
+ end
171
+
172
+ def send_command_to_deadpool_server(options)
173
+ output = ''
174
+
175
+ begin
176
+ socket = TCPSocket.open(@config[:admin_hostname], @config[:admin_port])
177
+ rescue
178
+ return "Couldn't connect to deadpool server. Is it running?"
179
+ end
180
+
181
+ if socket
182
+ socket.puts JSON.dump(options)
183
+ while line = socket.gets
184
+ output += line
185
+ end
186
+ socket.close
187
+ else
188
+ return "Couldn't connect to deadpool server."
189
+ end
190
+
191
+ return output
192
+ end
193
+
194
+ end
195
+
196
+ end
197
+