haproxyctl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/haproxyctl ADDED
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # HAProxy control script to start, stop, restart, configcheck, etc, as
4
+ # well as communicate to the stats socket.
5
+ #
6
+ # See https://github.com/flores/haproxyctl/README
7
+ #
8
+ # This line here is just for Redhat users who like "service haproxyctl blah"
9
+ # chkconfig: 2345 80 30
10
+ # description: HAProxy is a fast and reliable load balancer for UNIX systems
11
+ # HAProxyctl is an easy way to do init shit and talk to its stats socket
12
+ #
13
+
14
+ require 'pathname'
15
+ lib = File.join(File.dirname(Pathname.new(__FILE__).realpath), '../lib')
16
+ $:.unshift lib unless $:.include?(lib)
17
+
18
+ require 'haproxyctl'
19
+ include HAProxyCTL
20
+
21
+ argument = ARGV.join(' ')
22
+
23
+ unless has_exec?
24
+ puts usage if argument =~ /help/ || ARGV.length < 1
25
+
26
+ raise "Cannot find haproxy executable. Please ensure it is in your $PATH, or set $HAPROXY_BIN environment variable."
27
+ end
28
+
29
+ display_usage! if argument =~ /help/ || ARGV.length < 1
30
+
31
+ begin
32
+ case argument
33
+ when "start"
34
+ if pidof
35
+ raise("haproxy is already running on pid #{pidof}!")
36
+ else
37
+ start
38
+ end
39
+ when "stop"
40
+ stop(check_running)
41
+ when "restart"
42
+ if pidof
43
+ stop(pidof)
44
+ stillpidof = check_running()
45
+ while stillpidof == pidof
46
+ puts "still haven't killed old pid. waiting 3s for existing connections to die... (ctrl+c to stop)"
47
+ sleep 3
48
+ stillpidof = check_running() || 0
49
+ end
50
+ start()
51
+ else
52
+ puts "haproxy was not running. starting..."
53
+ start()
54
+ end
55
+ when "reload"
56
+ if ( pidof )
57
+ reload(pidof)
58
+ else
59
+ puts "haproxy not running. starting..."
60
+ start()
61
+ end
62
+ when "status"
63
+ if ( pidof )
64
+ puts "haproxy is running on pid #{pidof}.\nthese ports are used and guys are connected:"
65
+ system("lsof -ln -i |awk \'$2 ~ /#{pidof}/ {print $8\" \"$9}\'")
66
+ else
67
+ puts "haproxy is not running"
68
+ end
69
+ when "configcheck"
70
+ puts `#{exec} -c -f #{config_path}`
71
+ when "nagios"
72
+ if ( pidof )
73
+ puts "OK"
74
+ exit
75
+ else
76
+ puts "CRITICAL: HAProxy is not running!"
77
+ exit(2)
78
+ end
79
+ when "cloudkick"
80
+ if ( pidof )
81
+ puts "status ok haproxy is running"
82
+ conn = `lsof -ln -i |grep -c #{pidof}`.chomp.to_i
83
+ # removes the listener
84
+ conn = conn - 1
85
+ puts "metric connections int #{conn}"
86
+ status=unixsock("show stat")
87
+ status.each do |line|
88
+ line = line.split(',')
89
+ if (line[0] !~ /^#/)
90
+ host = "#{line[0]}_#{line[1]}"
91
+ puts "metric #{host}_request_rate int #{line[47]}" if line[47].to_i > 0
92
+ puts "metric #{host}_total_requests gauge #{line[49]}" if line[49].to_i > 0
93
+ puts "metric #{host}_health_check_duration int #{line[35]}" if line[35].to_i > 0
94
+ puts "metric ${host}_current_queue int #{line[3]}" if line[3].to_i > 0
95
+ end
96
+ end
97
+ else
98
+ puts "status err haproxy is not running!"
99
+ end
100
+ when "show health"
101
+ status=unixsock("show stat")
102
+ status.each do |line|
103
+ data = line.split(',')
104
+ printf "%-30s %-30s %-7s %3s\n", data[0], data[1], data[17], data[18]
105
+ end
106
+ when /show backend(s?)/
107
+ status=unixsock("show stat").grep(/BACKEND/)
108
+ status.each do |line|
109
+ data = line.split(',')
110
+ printf "%-30s %-30s %-7s %3s\n", data[0], data[1], data[17], data[18]
111
+ end
112
+ when /disable all EXCEPT (.+)/
113
+ servername=$1
114
+ status=unixsock("show stat")
115
+ backend = status.grep(/#{servername}/)
116
+ backend.each do |line|
117
+ backend_group = line.split(',')
118
+ status.each do |pool|
119
+ data = pool.split(',')
120
+ if ( (data[0] == backend_group[0]) && ( data[1] !~ /#{servername}|BACKEND|FRONTEND/ ) && ( data[17] = 'UP' ) )
121
+ unixsock("disable server #{data[0]}/#{data[1]}")
122
+ end
123
+ end
124
+ end
125
+ when /disable all (.+)/
126
+ servername=$1
127
+ status=unixsock("show stat")
128
+ status.each do |line|
129
+ data = line.split(',')
130
+ if ( ( data[1] = servername ) && ( data[17] = 'UP' ) )
131
+ unixsock("disable server #{data[0]}/#{servername}")
132
+ end
133
+ end
134
+ when /enable all EXCEPT (.+)/
135
+ servername=$1
136
+ status=unixsock("show stat")
137
+ backend = status.grep(/#{servername}/)
138
+ backend.each do |line|
139
+ backend_group = line.split(',')
140
+ status.each do |pool|
141
+ data = pool.split(',')
142
+ if ( (data[0] == backend_group[0]) && ( data[1] !~ /#{servername}|BACKEND|FRONTEND/ ) && ( data[17] =~ /Down|MAINT/i ) )
143
+ unixsock("enable server #{data[0]}/#{data[1]}")
144
+ end
145
+ end
146
+ end
147
+ when /enable all (.+)/
148
+ servername=$1
149
+ status=unixsock("show stat")
150
+ status.each do |line|
151
+ data = line.split(',')
152
+ if ( ( data[1] = servername ) && ( data[17] =~ /Down|MAINT/i ) )
153
+ unixsock("enable server #{data[0]}/#{servername}")
154
+ end
155
+ end
156
+ when 'version'
157
+ version
158
+ else
159
+ puts unixsock(argument)
160
+ end
161
+ rescue Errno::ENOENT => e
162
+ STDERR.puts e
163
+ exit 1
164
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/haproxyctl/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Carlo Flores"]
6
+ gem.email = ["github@petalphile.com"]
7
+ gem.description = %q{This is a simple wrapper to make life with HAProxy a little more convenient. Acts as an init script for start, stop, reload, restart, etc. Leverages 'socket' to enable and disable servers on the fly. Formats server weight and backends in a readable way. Provides Nagios and Cloudkick health checks. Compatible with RHEL chkconfig/service.}
8
+ gem.summary = %q{Wrapper to talk to the HAProxy socket, as well as regular init (start|stop|status|etc)}
9
+ gem.homepage = "https://github.com/flores/haproxyctl"
10
+ gem.rubyforge_project = "haproxyctl"
11
+ gem.license = "MIT"
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "haproxyctl"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = HAProxyCTL::VERSION
18
+ end
@@ -0,0 +1,82 @@
1
+ #!/bin/bash -ex
2
+ #
3
+ # This installs latest HAProxy from source along with HAProxyCTL
4
+ #
5
+ # It will clobber files and stuff and is only meant as a very
6
+ # quick and dirty (but sometimes handy) installer.
7
+ #
8
+
9
+ STARTINGDIR=$PWD
10
+
11
+ # make sure we have make, pcre and junk
12
+ if [ -e /etc/redhat-release ]; then
13
+ OS=redhat;
14
+ elif [ -e /etc/debian_version ]; then
15
+ OS=debian;
16
+ fi
17
+
18
+ if [ $OS ]; then
19
+ if [ $OS = 'redhat' ]; then
20
+ yum install -y pcre-devel make gcc libgcc git;
21
+ elif [ $OS = 'debian' ]; then
22
+ apt-get update;
23
+ apt-get install -y libpcre3 libpcre3-dev build-essential libgcc1 git;
24
+ fi
25
+ else
26
+ echo -e "I only understand Debian/RedHat/CentOS and this box does not appear to be any.\nExiting.\n- love, $0.";
27
+ exit 2;
28
+ fi
29
+
30
+ # grab last stable. HAProxy's site versions nicely - these will still be here after the next update
31
+ mkdir /usr/local/src || echo "Oops, /usr/local/src exists!"
32
+ cd /usr/local/src || exit 2
33
+ wget http://haproxy.1wt.eu/download/1.4/src/haproxy-1.4.20.tar.gz
34
+
35
+ # get rid of an existing haproxy
36
+ if [ -e /usr/local/haproxy ]; then
37
+ rm -fr /usr/local/haproxy
38
+ fi
39
+
40
+ # check the checksum
41
+ MD5CHECK=`md5sum /usr/local/src/haproxy-1.4.20.tar.gz |awk '{print $1}'`
42
+ if [ "$MD5CHECK" != "0cd3b91812ff31ae09ec4ace6355e29e" ] ; then
43
+ echo -e "MD5s do not match!\nBailing.";
44
+ exit 2;
45
+ fi
46
+
47
+ tar xvfz haproxy-1.4.20.tar.gz
48
+ cd haproxy-1.4.20
49
+
50
+ if uname -a | grep x86_64 ; then
51
+ make TARGET=linux26 CPU=x86_64 USE_PCRE=1
52
+ else
53
+ make TARGET=linux26 CPU=686 USE_PCRE=1
54
+ fi
55
+
56
+ make install
57
+
58
+ if [ -e /usr/sbin/haproxy ]; then
59
+ rm -f /usr/sbin/haproxy
60
+ fi
61
+
62
+ ln -s /usr/local/sbin/haproxy /usr/sbin/haproxy
63
+
64
+ # grab carlo's haproxyctl script/init
65
+ cd /usr/local
66
+ if [ -e /usr/local/haproxy ]; then
67
+ cd haproxy;
68
+ git pull;
69
+ else
70
+ git clone https://github.com/flores/haproxyctl.git
71
+ ln -s /usr/local/haproxyctl/haproxyctl /etc/init.d/haproxyctl
72
+ fi
73
+
74
+ # remove make and gcc
75
+ if [ $OS = 'redhat' ]; then
76
+ chkconfig --add haproxyctl;
77
+ yum remove -y gcc make
78
+ elif [ $OS = 'debian' ]; then
79
+ apt-get purge -y build-essential
80
+ fi
81
+
82
+ cd $STARTINGDIR
data/lib/haproxyctl.rb ADDED
@@ -0,0 +1,122 @@
1
+ require 'haproxyctl/version'
2
+ require 'haproxyctl/environment'
3
+ require 'socket'
4
+
5
+ module HAProxyCTL
6
+ include Environment
7
+
8
+ def start
9
+ puts "starting haproxy..."
10
+ system("#{exec} -f #{config_path} -D -p #{pidfile}")
11
+ newpid = check_running()
12
+ if ( newpid =~ /^\d+$/ )
13
+ puts "haproxy is running on pid #{newpid}"
14
+ return true
15
+ else
16
+ puts "error. haproxy did not start!"
17
+ return nil
18
+ end
19
+ end
20
+
21
+ def stop(pid)
22
+ if pid
23
+ puts "stopping haproxy on pid #{pid}..."
24
+ system("kill #{pid}") || system("kill -9 #{pid}")
25
+ puts "... stopped"
26
+ else
27
+ puts "haproxy is not running!"
28
+ end
29
+ end
30
+
31
+ def reload(pid)
32
+ if ( pid )
33
+ puts "gracefully stopping connections on pid #{pid}..."
34
+ system("#{exec} -f #{config_path} -sf #{pid}")
35
+ puts "checking if connections still alive on #{pid}..."
36
+ nowpid = check_running()
37
+ while ( pid == nowpid )
38
+ puts "still haven't killed old pid.
39
+ waiting 2s for existing connections to die...
40
+ (ctrl+c to stop this check)"
41
+ sleep 2
42
+ nowpid = check_running() || 0
43
+ end
44
+ puts "reloaded haproxy on pid #{nowpid}"
45
+ else
46
+ puts "haproxy is not running!"
47
+ end
48
+ end
49
+
50
+ def unixsock(command)
51
+
52
+ output=[]
53
+ runs = 0
54
+
55
+ begin
56
+ ctl=UNIXSocket.open(socket)
57
+ if (ctl)
58
+ ctl.write "#{command}\r\n"
59
+ else
60
+ puts "cannot talk to #{socket}"
61
+ end
62
+ rescue Errno::EPIPE
63
+ ctl.close
64
+ sleep 0.5
65
+ runs += 1
66
+ if ( runs < 4 )
67
+ retry
68
+ else
69
+ puts "the unix socket at #{socket} closed before we could complete this request"
70
+ exit
71
+ end
72
+ end
73
+ while (line = ctl.gets) do
74
+ unless ( line =~ /Unknown command/ )
75
+ output << line
76
+ end
77
+ end
78
+ ctl.close
79
+
80
+ return output
81
+ end
82
+
83
+ def display_usage!
84
+ puts usage
85
+ exit
86
+ end
87
+
88
+ def usage
89
+ <<-USAGE
90
+ usage: #{$0} <argument>
91
+ where <argument> can be:
92
+ start : start haproxy unless it is already running
93
+ stop : stop an existing haproxy
94
+ restart : immediately shutdown and restart
95
+ reload : gracefully terminate existing connections, reload #{config_path}
96
+ status : is haproxy running? on what ports per lsof?
97
+ configcheck : check #{config_path}
98
+ nagios : nagios-friendly status for running process and listener
99
+ cloudkick : cloudkick.com-friendly status and metric for connected users
100
+ show health : show status of all frontends and backend servers
101
+ show backends : show status of backend pools of servers
102
+ enable all <server> : re-enable a server previously in maint mode on multiple backends
103
+ disable all <server> : disable a server from every backend it exists
104
+ enable all EXCEPT <server> : like 'enable all', but re-enables every backend except for <server>
105
+ disable all EXCEPT <server> : like 'disable all', but disables every backend except for <server>
106
+ clear counters : clear max statistics counters (add 'all' for all counters)
107
+ help : this message
108
+ prompt : toggle interactive mode with prompt
109
+ quit : disconnect
110
+ show info : report information about the running process
111
+ show stat : report counters for each proxy and server
112
+ show errors : report last request and response errors for each proxy
113
+ show sess [id] : report the list of current sessions or dump this session
114
+ get weight : report a server's current weight
115
+ set weight : change a server's weight
116
+ set timeout : change a timeout setting
117
+ disable server : set a server in maintenance mode
118
+ enable server : re-enable a server that was previously in maintenance mode
119
+ version : version of this script
120
+ USAGE
121
+ end
122
+ end
@@ -0,0 +1,71 @@
1
+ module HAProxyCTL
2
+ module Environment
3
+ attr_accessor :pidof, :config_path, :config, :exec
4
+
5
+ def version
6
+ puts "HAProxyCTL #{HAProxyCTL::VERSION}"
7
+ end
8
+
9
+ def config_path
10
+ @config_path ||= ENV['HAPROXY_CONFIG'] || '/etc/haproxy/haproxy.cfg'
11
+ end
12
+
13
+ def config
14
+ @config ||= File.read(config_path)
15
+ end
16
+
17
+ def has_exec?
18
+ !exec.nil?
19
+ end
20
+
21
+ def exec
22
+ return(@exec) if @exec
23
+
24
+ @exec = ENV['HAPROXY_BIN']
25
+ @exec ||= `which haproxy`.chomp
26
+
27
+ if @exec.empty?
28
+ begin
29
+ `haproxy -v 2>/dev/null`
30
+ @exec = 'haproxy'
31
+ rescue Errno::ENOENT => e
32
+ @exec = nil
33
+ end
34
+ end
35
+
36
+ return(@exec)
37
+ end
38
+
39
+ def socket
40
+ @socket ||= begin
41
+ config.match /stats socket \s*([^\s]*)/
42
+ $1 || raise("Expecting 'stats socket <UNIX_socket_path>' in #{config_path}")
43
+ end
44
+ end
45
+
46
+ def pidfile
47
+ if config.match(/pidfile \s*([^\s]*)/)
48
+ @pidfile = $1
49
+ else
50
+ std_pid = "/var/run/haproxy.pid"
51
+ if File.exists?(std_pid)
52
+ @pidfile = std_pid
53
+ else
54
+ raise("Expecting 'pidfile <pid_file_path>' in #{config_path} or a pid file in #{std_pid}")
55
+ end
56
+ end
57
+ end
58
+
59
+ # @return [String, nil] Returns the PID of HAProxy as a string, if running. Nil otherwise.
60
+ def check_running
61
+ pid = File.read(pidfile)
62
+ pid.strip!
63
+
64
+ # verify this pid exists and is haproxy
65
+ if pid =~ /^\d+$/ and `ps -p #{pid} -o cmd=` =~ /#{exec}/
66
+ return pid
67
+ end
68
+ end
69
+ alias :pidof :check_running
70
+ end
71
+ end