You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
7.5 KiB
278 lines
7.5 KiB
package runtime
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/haproxytech/client-native/v6/errors"
|
|
"github.com/haproxytech/client-native/v6/models"
|
|
)
|
|
|
|
// SetTableEntry create or update a stick-table entry in the table.
|
|
func (s *SingleRuntime) SetTableEntry(table, key string, dataType models.StickTableEntry) error {
|
|
b, err := json.Marshal(dataType)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var marshalDataType map[string]interface{}
|
|
if err = json.Unmarshal(b, &marshalDataType); err != nil {
|
|
return err
|
|
}
|
|
|
|
const setTableEntryCommand = "set table %s key %s data.%s %v"
|
|
for k, v := range marshalDataType {
|
|
if k == "id" || k == "key" || k == "use" {
|
|
continue
|
|
}
|
|
command := fmt.Sprintf(setTableEntryCommand, table, key, k, v)
|
|
_, err := s.ExecuteWithResponse(command)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ShowTables returns Stick Tables descriptions from runtime
|
|
func (s *SingleRuntime) ShowTables() (models.StickTables, error) {
|
|
response, err := s.ExecuteWithResponse("show table")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.parseStickTables(response), nil
|
|
}
|
|
|
|
// ShowTables returns one Stick Table descriptions from runtime
|
|
func (s *SingleRuntime) ShowTable(name string) (*models.StickTable, error) {
|
|
response, err := s.ExecuteWithResponse("show table")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lines := strings.Split(response, "\n")
|
|
for _, line := range lines {
|
|
stkT := s.parseStickTable(line)
|
|
if stkT == nil || stkT.Name != name {
|
|
continue
|
|
}
|
|
return stkT, nil
|
|
}
|
|
return nil, fmt.Errorf("no data for table %s: %w", name, errors.ErrNotFound)
|
|
}
|
|
|
|
// GetTableEntries returns Stick Tables entries
|
|
func (s *SingleRuntime) GetTableEntries(name string, filter []string, key string) (models.StickTableEntries, error) {
|
|
cmd := fmt.Sprintf("show table %s", name)
|
|
|
|
// use only first filter here
|
|
if len(filter) > 0 {
|
|
cmd = fmt.Sprintf("%s data.%s", cmd, filter[0])
|
|
}
|
|
|
|
if key != "" {
|
|
cmd = fmt.Sprintf("%s key %s", cmd, key)
|
|
}
|
|
|
|
response, err := s.ExecuteWithResponse(cmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lines := strings.Split(response, "\n")
|
|
entries := models.StickTableEntries{}
|
|
for _, line := range lines {
|
|
if strings.TrimSpace(line) == "" || strings.HasPrefix(strings.TrimSpace(line), "#") {
|
|
continue
|
|
}
|
|
entry := parseStickTableEntry(line)
|
|
if entry != nil {
|
|
entries = append(entries, entry)
|
|
}
|
|
}
|
|
return entries, nil
|
|
}
|
|
|
|
func (s *SingleRuntime) parseStickTables(output string) models.StickTables {
|
|
lines := strings.Split(output, "\n")
|
|
|
|
stkTables := models.StickTables{}
|
|
for _, line := range lines {
|
|
if strings.TrimSpace(line) == "" || !strings.HasPrefix(strings.TrimSpace(line), "# table:") {
|
|
continue
|
|
}
|
|
stkTable := s.parseStickTable(line)
|
|
|
|
if stkTable == nil {
|
|
continue
|
|
}
|
|
|
|
stkTables = append(stkTables, stkTable)
|
|
}
|
|
return stkTables
|
|
}
|
|
|
|
func (s *SingleRuntime) parseStickTable(output string) *models.StickTable {
|
|
if !strings.HasPrefix(output, "# table:") {
|
|
return nil
|
|
}
|
|
proc := int64(s.process)
|
|
stkTable := &models.StickTable{Process: &proc}
|
|
|
|
stkStrings := strings.Split(output, ",")
|
|
|
|
for _, stkT := range stkStrings {
|
|
switch {
|
|
case strings.HasPrefix(stkT, "# table:"):
|
|
stkTable.Name = strings.TrimSpace(stkT[len("# table:"):])
|
|
case strings.HasPrefix(stkT, " type:"):
|
|
stkTable.Type = strings.TrimSpace(stkT[len(" type:"):])
|
|
case strings.HasPrefix(stkT, " size:"):
|
|
s, _ := strconv.ParseInt(strings.TrimSpace(stkT[len(" size:"):]), 10, 64)
|
|
stkTable.Size = &s
|
|
case strings.HasPrefix(stkT, " used:"):
|
|
u, _ := strconv.ParseInt(strings.TrimSpace(stkT[len(" used:"):]), 10, 64)
|
|
stkTable.Used = &u
|
|
}
|
|
}
|
|
|
|
return stkTable
|
|
}
|
|
|
|
func parseStickTableEntry(output string) *models.StickTableEntry { //nolint:gocognit,gocyclo,cyclop
|
|
idData := strings.SplitN(output, ":", 2)
|
|
if len(idData) != 2 {
|
|
return nil
|
|
}
|
|
entry := &models.StickTableEntry{ID: idData[0]}
|
|
data := parseStickTableEntryLine(strings.TrimSpace(idData[1]))
|
|
for k, v := range data {
|
|
switch key := k; {
|
|
case key == "server_id":
|
|
sID, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.ServerID = &sID
|
|
}
|
|
case key == "gpc0":
|
|
gpc0, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.Gpc0 = &gpc0
|
|
}
|
|
case strings.HasPrefix(key, "gpc0_rate("):
|
|
gpc0Rate, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.Gpc0Rate = &gpc0Rate
|
|
}
|
|
case key == "gpc1":
|
|
gpc1, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.Gpc1 = &gpc1
|
|
}
|
|
case strings.HasPrefix(key, "gpc1_rate("):
|
|
gpc1Rate, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.Gpc1Rate = &gpc1Rate
|
|
}
|
|
case key == "conn_cnt":
|
|
connCnt, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.ConnCnt = &connCnt
|
|
}
|
|
case key == "conn_cur":
|
|
connCur, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.ConnCur = &connCur
|
|
}
|
|
case strings.HasPrefix(key, "conn_rate("):
|
|
connRate, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.ConnRate = &connRate
|
|
}
|
|
case key == "sess_cnt":
|
|
sessCnt, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.SessCnt = &sessCnt
|
|
}
|
|
case strings.HasPrefix(key, "sess_rate("):
|
|
sessRate, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.SessRate = &sessRate
|
|
}
|
|
case key == "http_req_cnt":
|
|
httpReqCnt, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.HTTPReqCnt = &httpReqCnt
|
|
}
|
|
case strings.HasPrefix(key, "http_req_rate("):
|
|
httpReqRate, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.HTTPReqRate = &httpReqRate
|
|
}
|
|
case key == "http_err_cnt":
|
|
httpErrCnt, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.HTTPErrCnt = &httpErrCnt
|
|
}
|
|
case strings.HasPrefix(key, "http_err_rate("):
|
|
httpErrRate, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.HTTPErrRate = &httpErrRate
|
|
}
|
|
case key == "bytes_in_cnt":
|
|
bytesInCnt, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.BytesInCnt = &bytesInCnt
|
|
}
|
|
case strings.HasPrefix(key, "bytes_in_rate("):
|
|
bytesInRate, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.BytesInRate = &bytesInRate
|
|
}
|
|
case key == "bytes_out_cnt":
|
|
bytesOutCnt, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.BytesOutCnt = &bytesOutCnt
|
|
}
|
|
case strings.HasPrefix(key, "bytes_out_rate("):
|
|
bytesOutRate, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.BytesOutRate = &bytesOutRate
|
|
}
|
|
case key == "use":
|
|
entry.Use = strings.TrimSpace(v) == "1"
|
|
case key == "exp":
|
|
exp, err := strconv.ParseInt(strings.TrimSpace(v), 10, 64)
|
|
if err == nil {
|
|
entry.Exp = &exp
|
|
}
|
|
case key == "key":
|
|
entry.Key = strings.TrimSpace(v)
|
|
}
|
|
}
|
|
return entry
|
|
}
|
|
|
|
func parseStickTableEntryLine(data string) map[string]string {
|
|
words := strings.Split(data, " ")
|
|
retData := make(map[string]string)
|
|
|
|
currentKey := ""
|
|
for _, word := range words {
|
|
if currentKey != "" {
|
|
retData[currentKey] = fmt.Sprintf("%s %s", retData[currentKey], word)
|
|
if !strings.HasSuffix(word, "\\") {
|
|
currentKey = ""
|
|
}
|
|
} else {
|
|
kv := strings.Split(word, "=")
|
|
if len(kv) == 2 {
|
|
retData[kv[0]] = kv[1]
|
|
if strings.HasPrefix(kv[1], "\"") && strings.HasSuffix(kv[1], "\\") {
|
|
currentKey = kv[0]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return retData
|
|
}
|
|
|