youtubebeat/vendor/github.com/elastic/beats/metricbeat/module/haproxy/haproxy.go

324 lines
10 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
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")
}