forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
haproxy.go
306 lines (265 loc) · 9.21 KB
/
haproxy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
package haproxy
import (
"bytes"
"encoding/csv"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"strings"
"github.com/elastic/beats/metricbeat/mb/parse"
"github.com/gocarina/gocsv"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
// HostParser is used for parsing the configured HAProxy hosts.
var HostParser = parse.URLHostParserBuilder{DefaultScheme: "tcp"}.Build()
// Stat is an instance of the HAProxy stat information
type Stat struct {
PxName string `csv:"# pxname"`
SvName string `csv:"svname"`
Qcur string `csv:"qcur"`
Qmax string `csv:"qmax"`
Scur string `csv:"scur"`
Smax string `csv:"smax"`
Slim string `csv:"slim"`
Stot string `csv:"stot"`
Bin string `csv:"bin"`
Bout string `csv:"bout"`
Dreq string `csv:"dreq"`
Dresp string `csv:"dresp"`
Ereq string `csv:"ereq"`
Econ string `csv:"econ"`
Eresp string `csv:"eresp"`
Wretr string `csv:"wretr"`
Wredis string `csv:"wredis"`
Status string `csv:"status"`
Weight string `csv:"weight"`
Act string `csv:"act"`
Bck string `csv:"bck"`
ChkFail string `csv:"chkfail"`
ChkDown string `csv:"chkdown"`
Lastchg string `csv:"lastchg"`
Downtime string `csv:"downtime"`
Qlimit string `csv:"qlimit"`
Pid string `csv:"pid"`
Iid string `csv:"iid"`
Sid string `csv:"sid"`
Throttle string `csv:"throttle"`
Lbtot string `csv:"lbtot"`
Tracked string `csv:"tracked"`
Type string `csv:"type"`
Rate string `csv:"rate"`
RateLim string `csv:"rate_lim"`
RateMax string `csv:"rate_max"`
CheckStatus string `csv:"check_status"`
CheckCode string `csv:"check_code"`
CheckDuration string `csv:"check_duration"`
Hrsp1xx string `csv:"hrsp_1xx"`
Hrsp2xx string `csv:"hrsp_2xx"`
Hrsp3xx string `csv:"hrsp_3xx"`
Hrsp4xx string `csv:"hrsp_4xx"`
Hrsp5xx string `csv:"hrsp_5xx"`
HrspOther string `csv:"hrsp_other"`
Hanafail string `csv:"hanafail"`
ReqRate string `csv:"req_rate"`
ReqRateMax string `csv:"req_rate_max"`
ReqTot string `csv:"req_tot"`
CliAbrt string `csv:"cli_abrt"`
SrvAbrt string `csv:"srv_abrt"`
CompIn string `csv:"comp_in"`
CompOut string `csv:"comp_out"`
CompByp string `csv:"comp_byp"`
CompRsp string `csv:"comp_rsp"`
LastSess string `csv:"lastsess"`
LastChk string `csv:"last_chk"`
LastAgt string `csv:"last_agt"`
Qtime string `csv:"qtime"`
Ctime string `csv:"ctime"`
Rtime string `csv:"rtime"`
Ttime string `csv:"ttime"`
}
type Info struct {
Name string `mapstructure:"Name"`
Version string `mapstructure:"Version"`
ReleaseDate string `mapstructure:"Release_date"`
Nbproc string `mapstructure:"Nbproc"`
ProcessNum string `mapstructure:"Process_num"`
Pid string `mapstructure:"Pid"`
Uptime string `mapstructure:"Uptime"`
UptimeSec string `mapstructure:"Uptime_sec"`
MemMax string `mapstructure:"Memmax_MB"`
UlimitN string `mapstructure:"Ulimit-n"`
Maxsock string `mapstructure:"Maxsock"`
Maxconn string `mapstructure:"Maxconn"`
HardMaxconn string `mapstructure:"Hard_maxconn"`
CurrConns string `mapstructure:"CurrConns"`
CumConns string `mapstructure:"CumConns"`
CumReq string `mapstructure:"CumReq"`
MaxSslConns string `mapstructure:"MaxSslConns"`
CurrSslConns string `mapstructure:"CurrSslConns"`
CumSslConns string `mapstructure:"CumSslConns"`
Maxpipes string `mapstructure:"Maxpipes"`
PipesUsed string `mapstructure:"PipesUsed"`
PipesFree string `mapstructure:"PipesFree"`
ConnRate string `mapstructure:"ConnRate"`
ConnRateLimit string `mapstructure:"ConnRateLimit"`
MaxConnRate string `mapstructure:"MaxConnRate"`
SessRate string `mapstructure:"SessRate"`
SessRateLimit string `mapstructure:"SessRateLimit"`
MaxSessRate string `mapstructure:"MaxSessRate"`
SslRate string `mapstructure:"SslRate"`
SslRateLimit string `mapstructure:"SslRateLimit"`
MaxSslRate string `mapstructure:"MaxSslRate"`
SslFrontendKeyRate string `mapstructure:"SslFrontendKeyRate"`
SslFrontendMaxKeyRate string `mapstructure:"SslFrontendMaxKeyRate"`
SslFrontendSessionReusePct string `mapstructure:"SslFrontendSessionReuse_pct"`
SslBackendKeyRate string `mapstructure:"SslBackendKeyRate"`
SslBackendMaxKeyRate string `mapstructure:"SslBackendMaxKeyRate"`
SslCacheLookups string `mapstructure:"SslCacheLookups"`
SslCacheMisses string `mapstructure:"SslCacheMisses"`
CompressBpsIn string `mapstructure:"CompressBpsIn"`
CompressBpsOut string `mapstructure:"CompressBpsOut"`
CompressBpsRateLim string `mapstructure:"CompressBpsRateLim"`
ZlibMemUsage string `mapstructure:"ZlibMemUsage"`
MaxZlibMemUsage string `mapstructure:"MaxZlibMemUsage"`
Tasks string `mapstructure:"Tasks"`
RunQueue string `mapstructure:"Run_queue"`
IdlePct string `mapstructure:"Idle_pct"`
Node string `mapstructure:"Node"`
Description string `mapstructure:"Description"`
}
// Client is an instance of the HAProxy client
type clientProto interface {
Stat() (*bytes.Buffer, error)
Info() (*bytes.Buffer, error)
}
type Client struct {
proto clientProto
}
// NewHaproxyClient returns a new instance of HaproxyClient
func NewHaproxyClient(address string) (*Client, error) {
u, err := url.Parse(address)
if err != nil {
return nil, errors.Wrap(err, "invalid url")
}
switch u.Scheme {
case "tcp":
return &Client{&unixProto{Network: u.Scheme, Address: u.Host}}, nil
case "unix":
return &Client{&unixProto{Network: u.Scheme, Address: u.Path}}, nil
case "http", "https":
return &Client{&httpProto{URL: u}}, nil
default:
return nil, errors.Errorf("invalid protocol scheme: %s", u.Scheme)
}
}
// GetStat returns the result from the 'show stat' command
func (c *Client) GetStat() ([]*Stat, error) {
runResult, err := c.proto.Stat()
if err != nil {
return nil, err
}
var statRes []*Stat
csvReader := csv.NewReader(runResult)
csvReader.TrailingComma = true
err = gocsv.UnmarshalCSV(csvReader, &statRes)
if err != nil {
return nil, errors.Errorf("error parsing CSV: %s", err)
}
return statRes, nil
}
// GetInfo returns the result from the 'show stat' command
func (c *Client) GetInfo() (*Info, error) {
res, err := c.proto.Info()
if err != nil {
return nil, err
}
if b, err := ioutil.ReadAll(res); err == nil {
resultMap := map[string]interface{}{}
for _, ln := range strings.Split(string(b), "\n") {
ln := strings.TrimSpace(ln)
if ln == "" {
continue
}
parts := strings.Split(ln, ":")
if len(parts) != 2 {
continue
}
resultMap[parts[0]] = strings.TrimSpace(parts[1])
}
var result *Info
if err := mapstructure.Decode(resultMap, &result); err != nil {
return nil, err
}
return result, nil
}
return nil, err
}
type unixProto struct {
Network string
Address string
}
// Run sends a designated command to the haproxy stats socket
func (p *unixProto) run(cmd string) (*bytes.Buffer, error) {
var conn net.Conn
response := bytes.NewBuffer(nil)
conn, err := net.Dial(p.Network, p.Address)
if err != nil {
return response, err
}
defer conn.Close()
_, err = conn.Write([]byte(cmd + "\n"))
if err != nil {
return response, err
}
_, err = io.Copy(response, conn)
if err != nil {
return response, err
}
if strings.HasPrefix(response.String(), "Unknown command") {
return response, errors.Errorf("unknown command: %s", cmd)
}
return response, nil
}
func (p *unixProto) Stat() (*bytes.Buffer, error) {
return p.run("show stat")
}
func (p *unixProto) Info() (*bytes.Buffer, error) {
return p.run("show info")
}
type httpProto struct {
URL *url.URL
}
func (p *httpProto) Stat() (*bytes.Buffer, error) {
url := p.URL.String()
// Force csv format
if !strings.HasSuffix(url, ";csv") {
url += ";csv"
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
if p.URL.User != nil {
password, _ := p.URL.User.Password()
req.SetBasicAuth(p.URL.User.Username(), password)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, errors.Errorf("couldn't connect: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.Errorf("invalid response: %s", resp.Status)
}
d, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Errorf("couldn't read response body: %v", err)
}
return bytes.NewBuffer(d), nil
}
func (p *httpProto) Info() (*bytes.Buffer, error) {
return nil, errors.New("not supported")
}