package pkg import ( "fmt" "regexp" "strconv" "strings" ) type ( Pool struct { AllowNAT string `json:"allow_nat"` AllowSNAT string `json:"allow_snat"` AppService string `json:"app_service"` AutoScaleGroupID string `json:"autoscale_group_id"` Description string `json:"description"` GatewayFailsafeDevice string `json:"gateway_failsafe_device"` IgnorePersistedWeight string `json:"ignore_persisted_weight"` IPTOSToClient string `json:"ip_tos_to_client"` IPTOSToServer string `json:"ip_tos_to_server"` LinkQOSToClient string `json:"link_qos_to_client"` LinkQOSToServer string `json:"link_qos_to_server"` LoadBalancingMode string `json:"load_balancing_mode"` Members map[string]*Member `json:"members"` Metadata string `json:"metadata"` MinActiveMembers int `json:"min_active_members"` MinUpMembers int `json:"min_up_members"` MinUpMembersAction string `json:"min_up_members_action"` MinUpMembersChecking string `json:"min_up_members_checking"` Monitor string `json:"monitor"` Partition string `json:"partition"` Profiles string `json:"profiles"` QueueDepthLimit int `json:"queue_depth_limit"` QueueOnConnectionLimit string `json:"queue_on_connection_limit"` QueueTimeLimit int `json:"queue_time_limit"` ReselectTries int `json:"reselect_tries"` ServiceDownAction string `json:"service_down_action"` SlowRampTime int `json:"slow_ramp_time"` } Member struct { Address string `json:"address"` AppService string `json:"app_service"` ConnectionLimit int `json:"connection_limit"` Description string `json:"description"` DynamicRatio int `json:"dynamic_ratio"` Ephemeral bool `json:"ephemeral"` InheritProfile string `json:"inherit_profile"` Logging string `json:"logging"` Monitor string `json:"monitor"` PriorityGroup int `json:"priority_group"` RateLimit string `json:"rate_limit"` Ratio int `json:"ratio"` Session string `json:"session"` State string `json:"state"` FQDN struct { AutoPopulate string `json:"autopopulate"` Name string `json:"name"` } `json:"fqdn"` Metadata string `json:"metadata"` // Todo: Parse this, it's a map[string]???? Profiles string `json:"profiles"` // Todo: Need to parse this, it's a []string } ) func ParseMembers(raw string) (map[string]*Member, error) { members := make(map[string]*Member) if raw == "none" { return members, nil } re, err := regexp.Compile(`(\S+) { address (\S+) app-service (.*?) connection-limit (.*?) description (.*?) dynamic-ratio (.*?) ephemeral (.*?) inherit-profile (.*?) logging (.*?) monitor (.*?) priority-group (.*?) rate-limit (.*?) ratio (\d+) session (.*?) state (.*?) fqdn (.*?) metadata (.*?) profiles (.*?) }`) if err != nil { return nil, fmt.Errorf("error compiling regex for pool members: %s", err.Error()) } matches := re.FindAllStringSubmatch(raw, -1) for _, match := range matches { pri, err := strconv.Atoi(match[11]) if err != nil { return nil, fmt.Errorf("error converting priority group to integer: %s", err.Error()) } ratio, err := strconv.Atoi(match[13]) if err != nil { return nil, fmt.Errorf("error converting ratio to integer: %s", err.Error()) } connLimit, err := strconv.Atoi(match[4]) if err != nil { return nil, fmt.Errorf("error converting connection limit to integer: %s", err.Error()) } dynRatio, err := strconv.Atoi(match[6]) if err != nil { return nil, fmt.Errorf("error converting dynamic ratio to integer: %s", err.Error()) } var autoPopulate, name string if match[16] != "none" { re := regexp.MustCompile(`{ autopopulate (.*?) name (.*?) }`) subMatch := re.FindStringSubmatch(match[16]) if len(subMatch) != 3 { return nil, fmt.Errorf("error applying regex to fqdn") } autoPopulate = subMatch[1] name = subMatch[2] } members[match[1]] = &Member{ Address: match[2], AppService: match[3], ConnectionLimit: connLimit, Description: match[5], DynamicRatio: dynRatio, Ephemeral: parseBool(match[7]), InheritProfile: match[8], Logging: match[9], Monitor: match[10], PriorityGroup: pri, RateLimit: match[12], Ratio: ratio, Session: match[14], State: match[15], FQDN: struct { AutoPopulate string `json:"autopopulate"` Name string `json:"name"` }{ AutoPopulate: autoPopulate, Name: name, }, Metadata: match[17], Profiles: match[18], } } return members, nil } func ParsePool(raw string) (*Pool, error) { re, err := regexp.Compile(`ltm pool (?:.*?) { allow-nat (.*?) allow-snat (.*?) app-service (.*?) autoscale-group-id (\S+) description (.*?) gateway-failsafe-device (.*?) ignore-persisted-weight (.*?) ip-tos-to-client (.*?) ip-tos-to-server (.*?) link-qos-to-client (.*?) link-qos-to-server (.*) load-balancing-mode (.*) members (.*) metadata (.*?) min-active-members (.*?) min-up-members (.*?) min-up-members-action (.*?) min-up-members-checking (.*?) monitor (.*?) partition (.*?) profiles (.*?) queue-depth-limit (.*?) queue-on-connection-limit (.*?) queue-time-limit (.*?) reselect-tries (.*?) service-down-action (.*?) slow-ramp-time (.*?) }`) if err != nil { return nil, fmt.Errorf("error compiling regex for pool line: %s", err.Error()) } match := re.FindStringSubmatch(raw) //for i, v := range match { // fmt.Printf("i: %v\tv: %v\n", i, v) //} if len(match) != 28 { fmt.Println("Unmatched:") fmt.Println(raw) return nil, fmt.Errorf("received incorrect # of fields (%v, wanted 28) from regex", len(match)) } members, err := ParseMembers(match[13]) if err != nil { return nil, fmt.Errorf("error parsing members: %s", err.Error()) } minActive, err := strconv.Atoi(match[15]) if err != nil { return nil, fmt.Errorf("error converting min active members to integer: %s", err.Error()) } minUp, err := strconv.Atoi(match[16]) if err != nil { return nil, fmt.Errorf("error converting min up members to integer: %s", err.Error()) } queueDepth, err := strconv.Atoi(match[22]) if err != nil { return nil, fmt.Errorf("error converting queue depth limit to integer: %s", err.Error()) } queueTime, err := strconv.Atoi(match[24]) if err != nil { return nil, fmt.Errorf("error converting queue time limit to integer: %s", err.Error()) } reselect, err := strconv.Atoi(match[25]) if err != nil { return nil, fmt.Errorf("error converting reselect tries to integer: %s", err.Error()) } slowRamp, err := strconv.Atoi(match[27]) if err != nil { return nil, fmt.Errorf("error converting slow ramp time to integer: %s", err.Error()) } return &Pool{ AllowNAT: match[1], AllowSNAT: match[2], AppService: match[3], AutoScaleGroupID: match[4], Description: match[5], GatewayFailsafeDevice: match[6], IgnorePersistedWeight: match[7], IPTOSToClient: match[8], IPTOSToServer: match[9], LinkQOSToClient: match[10], LinkQOSToServer: match[11], LoadBalancingMode: match[12], Members: members, Metadata: match[14], MinActiveMembers: minActive, MinUpMembers: minUp, MinUpMembersAction: match[17], MinUpMembersChecking: match[18], Monitor: match[19], Partition: match[20], Profiles: match[21], QueueDepthLimit: queueDepth, QueueOnConnectionLimit: match[23], QueueTimeLimit: queueTime, ReselectTries: reselect, ServiceDownAction: match[26], SlowRampTime: slowRamp, }, nil } func ParsePools(raw string) (map[string]*Pool, error) { p := make(map[string]*Pool) split := strings.Split(raw, "\n") for n, line := range split { if n == 0 || n == len(split)-1 { continue // skip first line, which is echo of command, or last line } pool, err := ParsePool(line) if err != nil { return nil, fmt.Errorf("error parsing pool: %s", err.Error()) } f := strings.Fields(line) if pool != nil { p[f[2]] = pool } } return p, nil }