haproxy 0.0.1 → 0.0.2
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/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+)
|