haproxy 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/README.md +133 -0
- data/haproxy.gemspec +4 -4
- data/lib/haproxy/socket_reader.rb +2 -1
- data/lib/haproxy/version.rb +2 -2
- data/spec/haproxy_spec.rb +6 -0
- data/spec/socket_reader_spec.rb +59 -0
- data/spec/spec_helper.rb +118 -0
- metadata +9 -13
data/Gemfile
CHANGED
data/README.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# HAProxy RubyGem
|
2
|
+
|
3
|
+
This gem aims to provide an interface to query HAProxy for statistics,
|
4
|
+
and eventually management through the sockets API.
|
5
|
+
|
6
|
+
## What's HAProxy?
|
7
|
+
|
8
|
+
[HAProxy](http://haproxy.1wt.eu/) is the _Reliable, High Performance
|
9
|
+
TCP/HTTP Load Balancer_.
|
10
|
+
|
11
|
+
## Why this gem?
|
12
|
+
|
13
|
+
* I use HAProxy at my work.
|
14
|
+
* I wanted to know how to create gems.
|
15
|
+
* I wanted to contribute with OSS.
|
16
|
+
* Why not?
|
17
|
+
* All of the above.
|
18
|
+
|
19
|
+
## Example of use
|
20
|
+
|
21
|
+
#! /usr/bin/env ruby
|
22
|
+
require 'haproxy'
|
23
|
+
require 'pp'
|
24
|
+
|
25
|
+
haproxy = HAProxy.read_stats '/path/to/haproxy.stats.socket'
|
26
|
+
|
27
|
+
pp haproxy.info
|
28
|
+
|
29
|
+
# {:name=>"HAProxy",
|
30
|
+
# :version=>"1.3.22",
|
31
|
+
# :release_date=>"2009/10/14",
|
32
|
+
# :nbproc=>"1",
|
33
|
+
# :process_num=>"1",
|
34
|
+
# :pid=>"10222",
|
35
|
+
# :uptime=>"0d 0h33m12s",
|
36
|
+
# :uptime_sec=>"1992",
|
37
|
+
# :memmax_mb=>"0",
|
38
|
+
# :ulimit_n=>"4013",
|
39
|
+
# :maxsock=>"4013",
|
40
|
+
# :maxconn=>"2000",
|
41
|
+
# :maxpipes=>"0",
|
42
|
+
# :currconns=>"1",
|
43
|
+
# :pipesused=>"0",
|
44
|
+
# :pipesfree=>"0",
|
45
|
+
# :tasks=>"1",
|
46
|
+
# :run_queue=>"1",
|
47
|
+
# :node=>"roke",
|
48
|
+
# :"description:"=>nil}
|
49
|
+
|
50
|
+
pp haproxy.stats
|
51
|
+
|
52
|
+
# [{:pxname=>"app1",
|
53
|
+
# :svname=>"thin1",
|
54
|
+
# :qcur=>"0",
|
55
|
+
# :qmax=>"0",
|
56
|
+
# :scur=>"0",
|
57
|
+
# :smax=>"0",
|
58
|
+
# :slim=>"",
|
59
|
+
# :stot=>"0",
|
60
|
+
# :bin=>"0",
|
61
|
+
# :bout=>"0",
|
62
|
+
# :dreq=>"",
|
63
|
+
# :dresp=>"0",
|
64
|
+
# :ereq=>"",
|
65
|
+
# :econ=>"0",
|
66
|
+
# :eresp=>"0",
|
67
|
+
# :wretr=>"0",
|
68
|
+
# :wredis=>"0",
|
69
|
+
# :status=>"no check",
|
70
|
+
# :weight=>"1",
|
71
|
+
# :act=>"1",
|
72
|
+
# :bck=>"0",
|
73
|
+
# :chkfail=>"",
|
74
|
+
# :chkdown=>"",
|
75
|
+
# :lastchg=>"",
|
76
|
+
# :downtime=>"",
|
77
|
+
# :qlimit=>"",
|
78
|
+
# :pid=>"1",
|
79
|
+
# :iid=>"1",
|
80
|
+
# :sid=>"1",
|
81
|
+
# :throttle=>"",
|
82
|
+
# :lbtot=>"0",
|
83
|
+
# :tracked=>"",
|
84
|
+
# :type=>"2",
|
85
|
+
# :rate=>"0",
|
86
|
+
# :rate_lim=>"",
|
87
|
+
# :rate_max=>"0"},...]
|
88
|
+
|
89
|
+
#### HAProxy sample configuration
|
90
|
+
|
91
|
+
global
|
92
|
+
stats socket haproxy
|
93
|
+
|
94
|
+
defaults
|
95
|
+
mode http
|
96
|
+
option httplog
|
97
|
+
option httpclose
|
98
|
+
retries 3
|
99
|
+
option redispatch
|
100
|
+
maxconn 2000
|
101
|
+
contimeout 5000
|
102
|
+
clitimeout 50000
|
103
|
+
srvtimeout 50000
|
104
|
+
stats uri /haproxy
|
105
|
+
|
106
|
+
listen app1 0.0.0.0:10000
|
107
|
+
balance roundrobin
|
108
|
+
|
109
|
+
server thin1 127.0.0.1:10001
|
110
|
+
server thin1 127.0.0.1:10002
|
111
|
+
server thin1 127.0.0.1:10003
|
112
|
+
server thin1 127.0.0.1:10004
|
113
|
+
server thin1 127.0.0.1:10005
|
114
|
+
|
115
|
+
frontend app2
|
116
|
+
bind 0.0.0.0:10011
|
117
|
+
default_backend app2
|
118
|
+
|
119
|
+
backend app2
|
120
|
+
balance roundrobin
|
121
|
+
server thin1 127.0.0.1:10006
|
122
|
+
server thin1 127.0.0.1:10007
|
123
|
+
server thin1 127.0.0.1:10008
|
124
|
+
server thin1 127.0.0.1:10009
|
125
|
+
server thin1 127.0.0.1:10010
|
126
|
+
|
127
|
+
## Roadmap
|
128
|
+
|
129
|
+
* Improve documentation
|
130
|
+
* Improve CSV parsing
|
131
|
+
* Add tests
|
132
|
+
* Add examples
|
133
|
+
* Read stats from HTTP and CSV files
|
data/haproxy.gemspec
CHANGED
@@ -4,13 +4,13 @@ require "haproxy/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "haproxy"
|
7
|
-
s.version =
|
7
|
+
s.version = HAProxy::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Leandro López (inkel)"]
|
10
10
|
s.email = ["inkel.ar@gmail.com"]
|
11
|
-
s.homepage = "
|
12
|
-
s.summary =
|
13
|
-
s.description =
|
11
|
+
s.homepage = "https://github.com/inkel/haproxy-ruby"
|
12
|
+
s.summary = 'HAProxy interface for reading statistics or managing servers (requires HAProxy 1.4+)'
|
13
|
+
s.description = 'This gem is intended for use as a HAProxy interface when you need to read statistics or if you like to manage proxies thru Ruby'
|
14
14
|
|
15
15
|
s.rubyforge_project = "haproxy"
|
16
16
|
|
@@ -6,6 +6,7 @@ module HAProxy
|
|
6
6
|
class SocketReader < HAProxy::StatsReader
|
7
7
|
|
8
8
|
def initialize(path)
|
9
|
+
raise ArgumentError, "Socket #{path} doesn't exists or is not a UNIX socket" unless File.exists?(path) and File.socket?(path)
|
9
10
|
@path = path
|
10
11
|
end
|
11
12
|
|
@@ -48,7 +49,7 @@ module HAProxy
|
|
48
49
|
|
49
50
|
params[:proxy] = "-1" if params[:proxy].eql?(:all)
|
50
51
|
params[:server] = "-1" if params[:server].eql?(:all)
|
51
|
-
|
52
|
+
|
52
53
|
types = [types] unless types.is_a?(Array)
|
53
54
|
|
54
55
|
params[:type] = case
|
data/lib/haproxy/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = "0.0.
|
1
|
+
module HAProxy
|
2
|
+
VERSION = "0.0.2"
|
3
3
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.expand_path('../spec_helper.rb', __FILE__)
|
2
|
+
require File.expand_path('../../lib/haproxy', __FILE__)
|
3
|
+
|
4
|
+
describe HAProxy::SocketReader do
|
5
|
+
|
6
|
+
it "should accept an existing socket file" do
|
7
|
+
HAProxy::Test.with_socket do |path|
|
8
|
+
HAProxy::SocketReader.new(path).should_not be_nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should fail without an existing socket file" do
|
13
|
+
Tempfile.new('invalid-file') do |file|
|
14
|
+
file.path.should_not be_socket
|
15
|
+
HAProxy::SocketReader.new(file.path).should_raise ArgumentError
|
16
|
+
end.close!
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return a Hash with proxy information" do
|
20
|
+
HAProxy::Test.with_socket(:info) do |path|
|
21
|
+
reader = HAProxy::SocketReader.new(path)
|
22
|
+
reader.info.class.should == Hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return a String when asking for errors" do
|
27
|
+
HAProxy::Test.with_socket(:without_errors) do |path|
|
28
|
+
reader = HAProxy::SocketReader.new(path)
|
29
|
+
reader.errors.class.should == String
|
30
|
+
end
|
31
|
+
|
32
|
+
HAProxy::Test.with_socket(:with_errors) do |path|
|
33
|
+
reader = HAProxy::SocketReader.new(path)
|
34
|
+
reader.errors.class.should == String
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return a String when asking for sessions" do
|
39
|
+
HAProxy::Test.with_socket(:without_sessions) do |path|
|
40
|
+
reader = HAProxy::SocketReader.new(path)
|
41
|
+
reader.sessions.class.should == Array
|
42
|
+
end
|
43
|
+
|
44
|
+
HAProxy::Test.with_socket(:with_sessions) do |path|
|
45
|
+
reader = HAProxy::SocketReader.new(path)
|
46
|
+
reader.sessions.class.should == Array
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
[ :frontends, :backends, :servers ].each do |type|
|
51
|
+
it "should return an Array with #{type} information" do
|
52
|
+
HAProxy::Test.with_socket do |path|
|
53
|
+
reader = HAProxy::SocketReader.new(path)
|
54
|
+
reader.send(type).class.should == Array
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module HAProxy
|
4
|
+
|
5
|
+
module Test
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def temp_file_path
|
9
|
+
tmp = Tempfile.new('haproxy-ruby-mocksock')
|
10
|
+
path = tmp.path
|
11
|
+
tmp.close!
|
12
|
+
path
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_socket action = nil, &block
|
16
|
+
path = self.temp_file_path
|
17
|
+
|
18
|
+
UNIXServer.open(path) do |socket|
|
19
|
+
|
20
|
+
Thread.abort_on_exception = true
|
21
|
+
|
22
|
+
Process.fork do
|
23
|
+
# print "#{self} awaiting for connections in #{path}\n"
|
24
|
+
client = socket.accept
|
25
|
+
# print "#{client} connected\n"
|
26
|
+
cmd, data = client.recvfrom(1024)
|
27
|
+
cmd.chomp!
|
28
|
+
# print "#{self} received '#{cmd}' from #{client}/#{data}\n"
|
29
|
+
client.send self.send(action || :unknown), 0
|
30
|
+
client.close
|
31
|
+
# print "#{self} closing\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
block.call(path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def unknown
|
39
|
+
"Unknown command"
|
40
|
+
end
|
41
|
+
|
42
|
+
def info
|
43
|
+
"""Name: HAProxy
|
44
|
+
Version: 1.3.25
|
45
|
+
Release_date: 2010/06/16
|
46
|
+
Nbproc: 1
|
47
|
+
Process_num: 1
|
48
|
+
Pid: 20518
|
49
|
+
Uptime: 6d 7h07m46s
|
50
|
+
Uptime_sec: 544066
|
51
|
+
Memmax_MB: 0
|
52
|
+
Ulimit-n: 80110
|
53
|
+
Maxsock: 80110
|
54
|
+
Maxconn: 40000
|
55
|
+
Maxpipes: 0
|
56
|
+
CurrConns: 28
|
57
|
+
PipesUsed: 0
|
58
|
+
PipesFree: 0
|
59
|
+
Tasks: 125
|
60
|
+
Run_queue: 1
|
61
|
+
node: A1441
|
62
|
+
description
|
63
|
+
"""
|
64
|
+
end
|
65
|
+
|
66
|
+
def with_errors
|
67
|
+
'''
|
68
|
+
[09/Mar/2011:16:05:48.038] frontend http-in (#1): invalid request
|
69
|
+
src 193.173.111.185, session #25765333, backend <NONE> (#-1), server <NONE> (#-1)
|
70
|
+
request length 352 bytes, error at position 23:
|
71
|
+
|
72
|
+
00000 POS LWqEQR/710 HTTP/1.1\x00\x00Content-Type: application/x-fcs\x00\x00
|
73
|
+
00058+ User- HTTP/1.1\r\n
|
74
|
+
00074 kwave Flash\r\n
|
75
|
+
00087 Host: 77.74.49.24\x00\x00Content-Length: 1\x00\x00P\r\n
|
76
|
+
00128 xy-Connection: Keep\r\n
|
77
|
+
00149 live\x00\x00Pragm\r\n
|
78
|
+
00162 no-cache\x00\x00X-NovINet: v1.2\x00\x00\x00\n
|
79
|
+
00192 \r\n
|
80
|
+
00194 \n
|
81
|
+
00195 k\xC3\xD9\xFB\x02\x9F\x02\r\x08\xBC\xDB\x1E\xD3Z[\xD4]\xDD%O\x0FX\x01
|
82
|
+
00218+ \xDD\x02\xB4\x98\xB4r\x87\xDB\x88\xD4\xBD\xA6\xBD\x80@\x15\x9D\x1A\xA7
|
83
|
+
00237+ \xE8,\x8B\x01\x1Ft\x97\x80\xDB\xBF\x87\xAD\x15\r\xDE\x9E\x0CL\x13 FB
|
84
|
+
00259+ \x822\xAA\xE0G\x10\t\xC3\xBC\e\xD0!\xC2dYh/\x01\x8A~\x85]z1\xC00\xBF
|
85
|
+
00286+ \xC5\x14\xFA\x7F\x03\xF2\xC1\x06\x10\r\n
|
86
|
+
00297 X\xA4Cqj0\xFC\x81\x14\xF2U\n
|
87
|
+
00309 (0\xBF\xC12<\xA0:\xB2\x84\xC5\xA1\xFAa01\xD0-\xF2\x85\xC8\xB0\r\n
|
88
|
+
00333 \x96$\x16\xB6\xB2\r,\x1D\x8B\x93\xAED\xBA\x9BE\r\n
|
89
|
+
00350 \r\n
|
90
|
+
'''
|
91
|
+
end
|
92
|
+
|
93
|
+
def without_errors
|
94
|
+
""
|
95
|
+
end
|
96
|
+
|
97
|
+
def with_sessions
|
98
|
+
'''0xa14a288: proto=tcpv4 src=74.125.16.1:56772 fe=http-in be=gelderlander_textlink srv=mongrel-10306 as=0 ts=08 age=49s calls=5 rq[f=00f0a0h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=4s,wx=,ax=] s0=[7,0h,fd=13,ex=] s1=[7,0h,fd=16,ex=] exp=4s
|
99
|
+
0x9745898: proto=tcpv4 src=86.89.73.31:2778 fe=http-in be=gelderlander_textlink srv=mongrel-10315 as=0 ts=08 age=42s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=8s,wx=,ax=] s0=[7,0h,fd=19,ex=] s1=[7,8h,fd=20,ex=] exp=8s
|
100
|
+
0xa26cff0: proto=tcpv4 src=194.151.221.60:19546 fe=http-in be=limburger_textlink srv=mongrel-10708 as=0 ts=08 age=40s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=10s,wx=,ax=] s0=[7,0h,fd=25,ex=] s1=[7,8h,fd=26,ex=] exp=10s
|
101
|
+
0x9f2dff0: proto=tcpv4 src=86.86.45.76:59090 fe=http-in be=tctubantia_textlink srv=mongrel-10507 as=0 ts=08 age=38s calls=5 rq[f=00f0a0h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=11s,wx=,ax=] s0=[7,0h,fd=23,ex=] s1=[7,0h,fd=24,ex=] exp=11s
|
102
|
+
0xa2cb998: proto=tcpv4 src=82.75.108.32:1734 fe=http-in be=tctubantia_textlink srv=mongrel-10505 as=0 ts=08 age=37s calls=3 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=12s,wx=,ax=] s0=[7,0h,fd=28,ex=] s1=[7,8h,fd=29,ex=] exp=12s
|
103
|
+
0xa04b7e8: proto=tcpv4 src=80.254.148.59:1158 fe=http-in be=destentor_textlink srv=mongrel-10403 as=0 ts=08 age=34s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=16s,wx=,ax=] s0=[7,0h,fd=32,ex=] s1=[7,8h,fd=33,ex=] exp=16s
|
104
|
+
0x97d0b90: proto=tcpv4 src=82.75.133.111:50836 fe=http-in be=tctubantia_textlink srv=mongrel-10510 as=0 ts=08 age=21s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=29s,wx=,ax=] s0=[7,0h,fd=1,ex=] s1=[7,8h,fd=8,ex=] exp=29s
|
105
|
+
0x96b1220: proto=tcpv4 src=82.136.217.27:14445 fe=http-in be=gelderlander_textlink srv=mongrel-10310 as=0 ts=08 age=19s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=30s,wx=,ax=] s0=[7,0h,fd=21,ex=] s1=[7,8h,fd=22,ex=] exp=30s
|
106
|
+
0xa12e7a8: proto=tcpv4 src=190.98.34.84:49421 fe=http-in be=gelderlander_textlink srv=mongrel-10303 as=0 ts=08 age=16s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=33s,wx=,ax=] s0=[7,0h,fd=15,ex=] s1=[7,8h,fd=17,ex=] exp=33s
|
107
|
+
0x9b5f570: proto=tcpv4 src=82.95.242.177:50124 fe=http-in be=limburger_textlink srv=mongrel-10701 as=0 ts=08 age=14s calls=4 rq[f=009080h,l=0,an=00h,rx=,wx=,ax=] rp[f=001000h,l=0,an=10h,rx=35s,wx=,ax=] s0=[7,0h,fd=38,ex=] s1=[7,8h,fd=40,ex=] exp=35s
|
108
|
+
0x9fda668: proto=unix_stream as=2 ts=09 age=0s calls=2 rq[f=00e042h,l=10,an=20h,rx=10s,wx=,ax=] rp[f=048060h,l=2499,an=00h,rx=,wx=10s,ax=] s0=[7,0h,fd=2,ex=] s1=[0,0h,fd=-1,ex=] exp=10s
|
109
|
+
'''
|
110
|
+
end
|
111
|
+
|
112
|
+
def without_sessions
|
113
|
+
""
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: haproxy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
version: 0.0.1
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.2
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- "Leandro L\xC3\xB3pez (inkel)"
|
@@ -14,7 +10,7 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date:
|
13
|
+
date: 2011-03-09 00:00:00 -03:00
|
18
14
|
default_executable:
|
19
15
|
dependencies: []
|
20
16
|
|
@@ -30,6 +26,7 @@ extra_rdoc_files: []
|
|
30
26
|
files:
|
31
27
|
- .gitignore
|
32
28
|
- Gemfile
|
29
|
+
- README.md
|
33
30
|
- Rakefile
|
34
31
|
- haproxy.gemspec
|
35
32
|
- lib/haproxy.rb
|
@@ -37,8 +34,11 @@ files:
|
|
37
34
|
- lib/haproxy/socket_reader.rb
|
38
35
|
- lib/haproxy/stats_reader.rb
|
39
36
|
- lib/haproxy/version.rb
|
37
|
+
- spec/haproxy_spec.rb
|
38
|
+
- spec/socket_reader_spec.rb
|
39
|
+
- spec/spec_helper.rb
|
40
40
|
has_rdoc: true
|
41
|
-
homepage:
|
41
|
+
homepage: https://github.com/inkel/haproxy-ruby
|
42
42
|
licenses: []
|
43
43
|
|
44
44
|
post_install_message:
|
@@ -51,21 +51,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
segments:
|
55
|
-
- 0
|
56
54
|
version: "0"
|
57
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
56
|
none: false
|
59
57
|
requirements:
|
60
58
|
- - ">="
|
61
59
|
- !ruby/object:Gem::Version
|
62
|
-
segments:
|
63
|
-
- 0
|
64
60
|
version: "0"
|
65
61
|
requirements: []
|
66
62
|
|
67
63
|
rubyforge_project: haproxy
|
68
|
-
rubygems_version: 1.
|
64
|
+
rubygems_version: 1.6.1
|
69
65
|
signing_key:
|
70
66
|
specification_version: 3
|
71
67
|
summary: HAProxy interface for reading statistics or managing servers (requires HAProxy 1.4+)
|