├── .gitignore ├── LICENSE ├── README.md ├── client.go ├── client_test.go ├── go.mod ├── go.sum ├── info.go ├── kvcodec ├── types.go └── unmarshal.go └── stat.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 bradley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/bcicen/go-haproxy?status.svg)](https://godoc.org/github.com/bcicen/go-haproxy) 2 | [![codebeat badge](https://codebeat.co/badges/f947c19e-0d7b-47d0-87b4-4e2e555ba806)](https://codebeat.co/projects/github-com-bcicen-go-haproxy) 3 | 4 | # go-haproxy 5 | Go library for interacting with HAProxys stats socket. 6 | 7 | ## Usage 8 | 9 | Initialize a client object. Supported address schemas are `tcp://` and `unix:///` 10 | ```go 11 | client := &haproxy.HAProxyClient{ 12 | Addr: "tcp://localhost:9999", 13 | } 14 | ``` 15 | 16 | Fetch results for a built in command(currently supports `show stats` and `show info`): 17 | ```go 18 | stats, err := client.Stats() 19 | for _, i := range stats { 20 | fmt.Printf("%s: %s\n", i.SvName, i.Status) 21 | } 22 | ``` 23 | 24 | Or retrieve the result body from an arbitrary command string: 25 | ```go 26 | result, err := h.RunCommand("show info") 27 | fmt.Println(result.String()) 28 | ``` 29 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | // Package haproxy provides a minimal client for communicating with, and issuing commands to, HAproxy over a network or file socket. 2 | package haproxy 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "net" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | const ( 14 | socketSchema = "unix://" 15 | tcpSchema = "tcp://" 16 | ) 17 | 18 | // HAProxyClient is the main structure of the library. 19 | type HAProxyClient struct { 20 | Addr string 21 | Timeout int 22 | conn net.Conn 23 | } 24 | 25 | // RunCommand is the entrypoint to the client. Sends an arbitray command string to HAProxy. 26 | func (h *HAProxyClient) RunCommand(cmd string) (*bytes.Buffer, error) { 27 | err := h.dial() 28 | if err != nil { 29 | return nil, err 30 | } 31 | defer h.conn.Close() 32 | 33 | result := bytes.NewBuffer(nil) 34 | 35 | _, err = h.conn.Write([]byte(cmd + "\n")) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | _, err = io.Copy(result, h.conn) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | if strings.HasPrefix(result.String(), "Unknown command") { 46 | return nil, fmt.Errorf("Unknown command: %s", cmd) 47 | } 48 | 49 | return result, nil 50 | } 51 | 52 | func (h *HAProxyClient) dial() (err error) { 53 | if h.Timeout == 0 { 54 | h.Timeout = 30 55 | } 56 | 57 | timeout := time.Duration(h.Timeout) * time.Second 58 | 59 | switch h.schema() { 60 | case "socket": 61 | h.conn, err = net.DialTimeout("unix", strings.Replace(h.Addr, socketSchema, "", 1), timeout) 62 | case "tcp": 63 | h.conn, err = net.DialTimeout("tcp", strings.Replace(h.Addr, tcpSchema, "", 1), timeout) 64 | default: 65 | return fmt.Errorf("unknown schema") 66 | } 67 | return err 68 | } 69 | 70 | func (h *HAProxyClient) schema() string { 71 | if strings.HasPrefix(h.Addr, socketSchema) { 72 | return "socket" 73 | } 74 | if strings.HasPrefix(h.Addr, tcpSchema) { 75 | return "tcp" 76 | } 77 | return "" 78 | } 79 | -------------------------------------------------------------------------------- /client_test.go: -------------------------------------------------------------------------------- 1 | package haproxy_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bcicen/go-haproxy" 7 | ) 8 | 9 | func ExampleHAProxyClient_Stats() { 10 | client := &haproxy.HAProxyClient{ 11 | Addr: "unix:///var/run/haproxy.sock", 12 | } 13 | stats, err := client.Stats() 14 | if err != nil { 15 | fmt.Println(err) 16 | return 17 | } 18 | for _, s := range stats { 19 | fmt.Printf("%s: %s\n", s.SvName, s.Status) 20 | } 21 | // Output: 22 | //static: DOWN 23 | //app1: UP 24 | //app2: UP 25 | //... 26 | } 27 | 28 | func ExampleHAProxyClient_Info() { 29 | client := &haproxy.HAProxyClient{ 30 | Addr: "unix:///var/run/haproxy.sock", 31 | } 32 | info, err := client.Info() 33 | if err != nil { 34 | fmt.Println(err) 35 | return 36 | } 37 | fmt.Printf("%s version %s\n", info.Name, info.Version) 38 | // Output: 39 | //HAProxy version 1.6.3 40 | } 41 | 42 | func ExampleHAProxyClient_RunCommand() { 43 | client := &haproxy.HAProxyClient{ 44 | Addr: "unix:///var/run/haproxy.sock", 45 | } 46 | result, err := client.RunCommand("show info") 47 | if err != nil { 48 | fmt.Println(err) 49 | return 50 | } 51 | fmt.Println(result.String()) 52 | } 53 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bcicen/go-haproxy 2 | 3 | go 1.16 4 | 5 | require github.com/gocarina/gocsv v0.0.0-20210516172204-ca9e8a8ddea8 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gocarina/gocsv v0.0.0-20210516172204-ca9e8a8ddea8 h1:hp1oqdzmv37vPLYFGjuM/RmUgUMfD9vQfMszc54l55Y= 2 | github.com/gocarina/gocsv v0.0.0-20210516172204-ca9e8a8ddea8/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= 3 | -------------------------------------------------------------------------------- /info.go: -------------------------------------------------------------------------------- 1 | package haproxy 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bcicen/go-haproxy/kvcodec" 7 | ) 8 | 9 | // Response from HAProxy "show info" command. 10 | type Info struct { 11 | Name string `kv:"Name"` 12 | Version string `kv:"Version"` 13 | ReleaseDate string `kv:"Release_date"` 14 | Nbthread uint64 `kv:"Nbthread"` 15 | Nbproc uint64 `kv:"Nbproc"` 16 | ProcessNum uint64 `kv:"Process_num"` 17 | Pid uint64 `kv:"Pid"` 18 | Uptime string `kv:"Uptime"` 19 | UptimeSec uint64 `kv:"Uptime_sec"` 20 | MemMaxMB uint64 `kv:"Memmax_MB"` 21 | PoolAllocMB uint64 `kv:"PoolAlloc_MB"` 22 | PoolUsedMB uint64 `kv:"PoolUsed_MB"` 23 | PoolFailed uint64 `kv:"PoolFailed"` 24 | UlimitN uint64 `kv:"Ulimit-n"` 25 | Maxsock uint64 `kv:"Maxsock"` 26 | Maxconn uint64 `kv:"Maxconn"` 27 | HardMaxconn uint64 `kv:"Hard_maxconn"` 28 | CurrConns uint64 `kv:"CurrConns"` 29 | CumConns uint64 `kv:"CumConns"` 30 | CumReq uint64 `kv:"CumReq"` 31 | MaxSslConns uint64 `kv:"MaxSslConns"` 32 | CurrSslConns uint64 `kv:"CurrSslConns"` 33 | CumSslConns uint64 `kv:"CumSslConns"` 34 | Maxpipes uint64 `kv:"Maxpipes"` 35 | PipesUsed uint64 `kv:"PipesUsed"` 36 | PipesFree uint64 `kv:"PipesFree"` 37 | ConnRate uint64 `kv:"ConnRate"` 38 | ConnRateLimit uint64 `kv:"ConnRateLimit"` 39 | MaxConnRate uint64 `kv:"MaxConnRate"` 40 | SessRate uint64 `kv:"SessRate"` 41 | SessRateLimit uint64 `kv:"SessRateLimit"` 42 | MaxSessRate uint64 `kv:"MaxSessRate"` 43 | SslRate uint64 `kv:"SslRate"` 44 | SslRateLimit uint64 `kv:"SslRateLimit"` 45 | MaxSslRate uint64 `kv:"MaxSslRate"` 46 | SslFrontendKeyRate uint64 `kv:"SslFrontendKeyRate"` 47 | SslFrontendMaxKeyRate uint64 `kv:"SslFrontendMaxKeyRate"` 48 | SslFrontendSessionReusePct uint64 `kv:"SslFrontendSessionReuse_pct"` 49 | SslBackendKeyRate uint64 `kv:"SslBackendKeyRate"` 50 | SslBackendMaxKeyRate uint64 `kv:"SslBackendMaxKeyRate"` 51 | SslCacheLookups uint64 `kv:"SslCacheLookups"` 52 | SslCacheMisses uint64 `kv:"SslCacheMisses"` 53 | CompressBpsIn uint64 `kv:"CompressBpsIn"` 54 | CompressBpsOut uint64 `kv:"CompressBpsOut"` 55 | CompressBpsRateLim uint64 `kv:"CompressBpsRateLim"` 56 | //ZlibMemUsage uint64 `kv:"ZlibMemUsage"` 57 | //MaxZlibMemUsage uint64 `kv:"MaxZlibMemUsage"` 58 | Tasks uint64 `kv:"Tasks"` 59 | RunQueue uint64 `kv:"Run_queue"` 60 | IdlePct uint64 `kv:"Idle_pct"` 61 | Node string `kv:"node"` 62 | Stopping uint64 `kv:"Stopping"` 63 | Jobs uint64 `kv:"Jobs"` 64 | UnstoppableJobs uint64 `kv:"Unstoppable Jobs"` 65 | Listeners uint64 `kv:"Listeners"` 66 | ActivePeers uint64 `kv:"ActivePeers"` 67 | ConnectedPeers uint64 `kv:"ConnectedPeers"` 68 | DroppedLogs uint64 `kv:"DroppedLogs"` 69 | BusyPolling uint64 `kv:"BusyPolling"` 70 | FailedResolutions uint64 `kv:"FailedResolutions"` 71 | TotalBytesOut uint64 `kv:"TotalBytesOut"` 72 | BytesOutRate uint64 `kv:"BytesOutRate"` 73 | DebugCommandsIssued uint64 `kv:"DebugCommandsIssued"` 74 | } 75 | 76 | // Equivalent to HAProxy "show info" command. 77 | func (h *HAProxyClient) Info() (*Info, error) { 78 | res, err := h.RunCommand("show info") 79 | if err != nil { 80 | return nil, err 81 | } 82 | info := &Info{} 83 | err = kvcodec.Unmarshal(res, info) 84 | if err != nil { 85 | return nil, fmt.Errorf("error decoding response: %s", err) 86 | } 87 | return info, nil 88 | } 89 | -------------------------------------------------------------------------------- /kvcodec/types.go: -------------------------------------------------------------------------------- 1 | package kvcodec 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | func toString(in interface{}) (string, error) { 11 | inValue := reflect.ValueOf(in) 12 | 13 | switch inValue.Kind() { 14 | case reflect.String: 15 | return inValue.String(), nil 16 | case reflect.Bool: 17 | b := inValue.Bool() 18 | if b { 19 | return "true", nil 20 | } 21 | return "false", nil 22 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 23 | return fmt.Sprintf("%v", inValue.Int()), nil 24 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 25 | return fmt.Sprintf("%v", inValue.Uint()), nil 26 | case reflect.Float32: 27 | return strconv.FormatFloat(inValue.Float(), byte('f'), -1, 32), nil 28 | case reflect.Float64: 29 | return strconv.FormatFloat(inValue.Float(), byte('f'), -1, 64), nil 30 | } 31 | return "", fmt.Errorf("unable to cast " + inValue.Type().String() + " to string") 32 | } 33 | 34 | func toBool(in interface{}) (bool, error) { 35 | inValue := reflect.ValueOf(in) 36 | 37 | switch inValue.Kind() { 38 | case reflect.String: 39 | s := inValue.String() 40 | switch s { 41 | case "yes": 42 | return true, nil 43 | case "no", "": 44 | return false, nil 45 | default: 46 | return strconv.ParseBool(s) 47 | } 48 | case reflect.Bool: 49 | return inValue.Bool(), nil 50 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 51 | i := inValue.Int() 52 | if i != 0 { 53 | return true, nil 54 | } 55 | return false, nil 56 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 57 | i := inValue.Uint() 58 | if i != 0 { 59 | return true, nil 60 | } 61 | return false, nil 62 | case reflect.Float32, reflect.Float64: 63 | f := inValue.Float() 64 | if f != 0 { 65 | return true, nil 66 | } 67 | return false, nil 68 | } 69 | return false, fmt.Errorf("unable to cast " + inValue.Type().String() + " to bool") 70 | } 71 | 72 | func toInt(in interface{}) (int64, error) { 73 | inValue := reflect.ValueOf(in) 74 | 75 | switch inValue.Kind() { 76 | case reflect.String: 77 | s := strings.TrimSpace(inValue.String()) 78 | if s == "" { 79 | return 0, nil 80 | } 81 | return strconv.ParseInt(s, 0, 64) 82 | case reflect.Bool: 83 | if inValue.Bool() { 84 | return 1, nil 85 | } 86 | return 0, nil 87 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 88 | return inValue.Int(), nil 89 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 90 | return int64(inValue.Uint()), nil 91 | case reflect.Float32, reflect.Float64: 92 | return int64(inValue.Float()), nil 93 | } 94 | return 0, fmt.Errorf("unable to cast " + inValue.Type().String() + " to int") 95 | } 96 | 97 | func toUint(in interface{}) (uint64, error) { 98 | inValue := reflect.ValueOf(in) 99 | 100 | switch inValue.Kind() { 101 | case reflect.String: 102 | s := strings.TrimSpace(inValue.String()) 103 | if s == "" { 104 | return 0, nil 105 | } 106 | 107 | // float input 108 | if strings.Contains(s, ".") { 109 | f, err := strconv.ParseFloat(s, 64) 110 | if err != nil { 111 | return 0, err 112 | } 113 | return uint64(f), nil 114 | } 115 | return strconv.ParseUint(s, 0, 64) 116 | case reflect.Bool: 117 | if inValue.Bool() { 118 | return 1, nil 119 | } 120 | return 0, nil 121 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 122 | return uint64(inValue.Int()), nil 123 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 124 | return inValue.Uint(), nil 125 | case reflect.Float32, reflect.Float64: 126 | return uint64(inValue.Float()), nil 127 | } 128 | return 0, fmt.Errorf("unable to cast " + inValue.Type().String() + " to uint") 129 | } 130 | 131 | func toFloat(in interface{}) (float64, error) { 132 | inValue := reflect.ValueOf(in) 133 | 134 | switch inValue.Kind() { 135 | case reflect.String: 136 | s := strings.TrimSpace(inValue.String()) 137 | if s == "" { 138 | return 0, nil 139 | } 140 | return strconv.ParseFloat(s, 64) 141 | case reflect.Bool: 142 | if inValue.Bool() { 143 | return 1, nil 144 | } 145 | return 0, nil 146 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 147 | return float64(inValue.Int()), nil 148 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 149 | return float64(inValue.Uint()), nil 150 | case reflect.Float32, reflect.Float64: 151 | return inValue.Float(), nil 152 | } 153 | return 0, fmt.Errorf("unable to cast " + inValue.Type().String() + " to float") 154 | } 155 | 156 | func setField(field reflect.Value, value string) error { 157 | switch field.Kind() { 158 | case reflect.String: 159 | s, err := toString(value) 160 | if err != nil { 161 | return err 162 | } 163 | field.SetString(s) 164 | case reflect.Bool: 165 | b, err := toBool(value) 166 | if err != nil { 167 | return err 168 | } 169 | field.SetBool(b) 170 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 171 | i, err := toInt(value) 172 | if err != nil { 173 | return err 174 | } 175 | field.SetInt(i) 176 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 177 | ui, err := toUint(value) 178 | if err != nil { 179 | return err 180 | } 181 | field.SetUint(ui) 182 | case reflect.Float32, reflect.Float64: 183 | f, err := toFloat(value) 184 | if err != nil { 185 | return err 186 | } 187 | field.SetFloat(f) 188 | default: 189 | err := fmt.Errorf("unable to set field of type %s", field.Kind()) 190 | return err 191 | } 192 | return nil 193 | } 194 | -------------------------------------------------------------------------------- /kvcodec/unmarshal.go: -------------------------------------------------------------------------------- 1 | package kvcodec 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "reflect" 8 | "regexp" 9 | "strings" 10 | "sync" 11 | ) 12 | 13 | const ( 14 | tagLabel = "kv" 15 | kvDelim = ":" 16 | ) 17 | 18 | type structFields map[string]fieldMeta 19 | 20 | type fieldMeta struct { 21 | Key string 22 | Name string 23 | OmitAlways bool 24 | OmitEmpty bool 25 | } 26 | 27 | func newfieldMeta(field reflect.StructField) (meta fieldMeta) { 28 | meta = fieldMeta{ 29 | Name: field.Name, 30 | } 31 | fieldTags := strings.Split(field.Tag.Get(tagLabel), ",") 32 | for _, tag := range fieldTags { 33 | if tag == "-" { 34 | meta.OmitAlways = true 35 | return meta 36 | } 37 | if tag == "omitempty" { 38 | meta.OmitEmpty = true 39 | } else if tag != "" { 40 | meta.Key = tag 41 | } else { 42 | meta.Key = field.Name 43 | } 44 | } 45 | return meta 46 | } 47 | 48 | var err error 49 | var structMap = make(map[reflect.Type]structFields) 50 | var structMapMutex sync.RWMutex 51 | 52 | func getStructFields(rType reflect.Type) (structFields, error) { 53 | structMapMutex.RLock() 54 | stInfo, ok := structMap[rType] 55 | if !ok { 56 | stInfo, err = newStructFields(rType) 57 | if err != nil { 58 | return nil, err 59 | } 60 | structMap[rType] = stInfo 61 | } 62 | structMapMutex.RUnlock() 63 | return stInfo, nil 64 | } 65 | 66 | func newStructFields(rType reflect.Type) (structFields, error) { 67 | fieldsCount := rType.NumField() 68 | fieldMap := make(structFields) 69 | 70 | for i := 0; i < fieldsCount; i++ { 71 | field := rType.Field(i) 72 | meta := newfieldMeta(field) 73 | 74 | if field.PkgPath != "" { 75 | continue 76 | } 77 | 78 | if field.Anonymous && field.Type.Kind() == reflect.Struct { 79 | return nil, fmt.Errorf("embedded structs not supported") 80 | } 81 | 82 | if !meta.OmitAlways { 83 | fieldMap[meta.Key] = meta 84 | } 85 | } 86 | 87 | return fieldMap, nil 88 | } 89 | 90 | func trim(s string) string { 91 | re := regexp.MustCompile("(\\S+|\\S+)") 92 | return strings.Join(re.FindAllString(s, -1), " ") 93 | } 94 | 95 | func Unmarshal(in io.Reader, out interface{}) error { 96 | outValue := reflect.ValueOf(out) 97 | if outValue.Kind() == reflect.Ptr { 98 | outValue = outValue.Elem() 99 | } 100 | 101 | fields, err := getStructFields(outValue.Type()) 102 | if fields == nil { 103 | panic(err) 104 | } 105 | if err != nil { 106 | return err 107 | } 108 | 109 | scanner := bufio.NewScanner(in) 110 | for scanner.Scan() { 111 | if strings.Contains(scanner.Text(), kvDelim) { 112 | s := strings.Split(scanner.Text(), kvDelim) 113 | k, v := trim(s[0]), trim(s[1]) 114 | if meta, ok := fields[k]; ok { 115 | field := outValue.FieldByName(meta.Name) 116 | err = setField(field, v) 117 | if err != nil { 118 | return err 119 | } 120 | } 121 | } 122 | } 123 | return nil 124 | } 125 | -------------------------------------------------------------------------------- /stat.go: -------------------------------------------------------------------------------- 1 | package haproxy 2 | 3 | import ( 4 | "encoding/csv" 5 | "fmt" 6 | 7 | "github.com/gocarina/gocsv" 8 | ) 9 | 10 | // Response from HAProxy "show stat" command. 11 | type Stat struct { 12 | PxName string `csv:"# pxname"` 13 | SvName string `csv:"svname"` 14 | Qcur uint64 `csv:"qcur"` 15 | Qmax uint64 `csv:"qmax"` 16 | Scur uint64 `csv:"scur"` 17 | Smax uint64 `csv:"smax"` 18 | Slim uint64 `csv:"slim"` 19 | Stot uint64 `csv:"stot"` 20 | Bin uint64 `csv:"bin"` 21 | Bout uint64 `csv:"bout"` 22 | Dreq uint64 `csv:"dreq"` 23 | Dresp uint64 `csv:"dresp"` 24 | Ereq uint64 `csv:"ereq"` 25 | Econ uint64 `csv:"econ"` 26 | Eresp uint64 `csv:"eresp"` 27 | Wretr uint64 `csv:"wretr"` 28 | Wredis uint64 `csv:"wredis"` 29 | Status string `csv:"status"` 30 | Weight uint64 `csv:"weight"` 31 | Act uint64 `csv:"act"` 32 | Bck uint64 `csv:"bck"` 33 | ChkFail uint64 `csv:"chkfail"` 34 | ChkDown uint64 `csv:"chkdown"` 35 | Lastchg uint64 `csv:"lastchg"` 36 | Downtime uint64 `csv:"downtime"` 37 | Qlimit uint64 `csv:"qlimit"` 38 | Pid uint64 `csv:"pid"` 39 | Iid uint64 `csv:"iid"` 40 | Sid uint64 `csv:"sid"` 41 | Throttle uint64 `csv:"throttle"` 42 | Lbtot uint64 `csv:"lbtot"` 43 | Tracked uint64 `csv:"tracked"` 44 | Type uint64 `csv:"type"` 45 | Rate uint64 `csv:"rate"` 46 | RateLim uint64 `csv:"rate_lim"` 47 | RateMax uint64 `csv:"rate_max"` 48 | CheckStatus string `csv:"check_status"` 49 | CheckCode uint64 `csv:"check_code"` 50 | CheckDuration uint64 `csv:"check_duration"` 51 | Hrsp1xx uint64 `csv:"hrsp_1xx"` 52 | Hrsp2xx uint64 `csv:"hrsp_2xx"` 53 | Hrsp3xx uint64 `csv:"hrsp_3xx"` 54 | Hrsp4xx uint64 `csv:"hrsp_4xx"` 55 | Hrsp5xx uint64 `csv:"hrsp_5xx"` 56 | HrspOther uint64 `csv:"hrsp_other"` 57 | Hanafail uint64 `csv:"hanafail"` 58 | ReqRate uint64 `csv:"req_rate"` 59 | ReqRateMax uint64 `csv:"req_rate_max"` 60 | ReqTot uint64 `csv:"req_tot"` 61 | CliAbrt uint64 `csv:"cli_abrt"` 62 | SrvAbrt uint64 `csv:"srv_abrt"` 63 | CompIn uint64 `csv:"comp_in"` 64 | CompOut uint64 `csv:"comp_out"` 65 | CompByp uint64 `csv:"comp_byp"` 66 | CompRsp uint64 `csv:"comp_rsp"` 67 | LastSess int64 `csv:"lastsess"` 68 | LastChk string `csv:"last_chk"` 69 | LastAgt uint64 `csv:"last_agt"` 70 | Qtime uint64 `csv:"qtime"` 71 | Ctime uint64 `csv:"ctime"` 72 | Rtime uint64 `csv:"rtime"` 73 | Ttime uint64 `csv:"ttime"` 74 | AgentStatus uint64 `csv:"agent_status"` 75 | AgentCode uint64 `csv:"agent_code"` 76 | AgentDuration uint64 `csv:"agent_duration"` 77 | CheckDesc string `csv:"check_desc"` 78 | AgentDesc string `csv:"agent_desc"` 79 | CheckRise uint64 `csv:"check_rise"` 80 | CheckFall uint64 `csv:"check_fall"` 81 | CheckHealth uint64 `csv:"check_health"` 82 | AgentRise uint64 `csv:"agent_rise"` 83 | AgentFall uint64 `csv:"agent_fall"` 84 | AgentHealth uint64 `csv:"agent_health"` 85 | Addr string `csv:"addr"` 86 | Cookie uint64 `csv:"cookie"` 87 | Mode string `csv:"mode"` 88 | Algo string `csv:"algo"` 89 | ConnRate uint64 `csv:"conn_rate"` 90 | ConnRateMax uint64 `csv:"conn_rate_max"` 91 | ConnTot uint64 `csv:"conn_tot"` 92 | Intercepted uint64 `csv:"intercepted"` 93 | Dcon uint64 `csv:"dcon"` 94 | Dses uint64 `csv:"dses"` 95 | Wrew uint64 `csv:"wrew"` 96 | Connect uint64 `csv:"connect"` 97 | Reuse uint64 `csv:"reuse"` 98 | CacheLookups uint64 `csv:"cache_lookups"` 99 | CacheHits uint64 `csv:"cache_hits"` 100 | SrvIcur uint64 `csv:"srv_icur"` 101 | SrcIlim uint64 `csv:"src_ilim"` 102 | QtimeMax uint64 `csv:"qtime_max"` 103 | CtimeMax uint64 `csv:"ctime_max"` 104 | RtimeMax uint64 `csv:"rtime_max"` 105 | TtimeMax uint64 `csv:"ttime_max"` 106 | Eint uint64 `csv:"eint"` 107 | IdleConnCur uint64 `csv:"idle_conn_cur"` 108 | SafeConnCur uint64 `csv:"safe_conn_cur"` 109 | UsedConnCur uint64 `csv:"used_conn_cur"` 110 | NeedConnEst uint64 `csv:"need_conn_est"` 111 | } 112 | 113 | // Equivalent to HAProxy "show stat" command. 114 | func (h *HAProxyClient) Stats() (stats []*Stat, err error) { 115 | res, err := h.RunCommand("show stat") 116 | if err != nil { 117 | return nil, err 118 | } 119 | 120 | reader := csv.NewReader(res) 121 | reader.TrailingComma = true 122 | err = gocsv.UnmarshalCSV(reader, &stats) 123 | if err != nil { 124 | return nil, fmt.Errorf("error reading csv: %s", err) 125 | } 126 | 127 | // for _, s := range allStats { 128 | // switch s.SvName { 129 | // case "FRONTEND": 130 | // services.Frontends = append(services.Frontends, s) 131 | // case "BACKEND": 132 | // services.Backends = append(services.Backends, s) 133 | // default: 134 | // services.Listeners = append(services.Listeners, s) 135 | // } 136 | // } 137 | 138 | return stats, nil 139 | } 140 | --------------------------------------------------------------------------------