2016-11-10 14:16:44 +01:00
|
|
|
package bird
|
|
|
|
|
2016-11-10 14:51:24 +01:00
|
|
|
import (
|
2016-11-10 15:11:32 +01:00
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
2016-11-10 14:51:24 +01:00
|
|
|
)
|
2016-11-10 14:16:44 +01:00
|
|
|
|
2016-11-10 14:51:24 +01:00
|
|
|
type Parsed map[string]interface{}
|
|
|
|
|
2016-11-10 16:06:38 +01:00
|
|
|
func emptyLine(line string) bool {
|
|
|
|
return len(strings.TrimSpace(line)) == 0
|
|
|
|
}
|
|
|
|
|
2016-11-11 13:28:40 +01:00
|
|
|
func getLinesUnfiltered(input string) []string {
|
2016-11-10 15:11:32 +01:00
|
|
|
line_sep := regexp.MustCompile(`((\r?\n)|(\r\n?))`)
|
2016-11-11 13:28:40 +01:00
|
|
|
return line_sep.Split(input, -1)
|
2016-11-10 16:06:38 +01:00
|
|
|
}
|
|
|
|
|
2016-11-11 13:28:40 +01:00
|
|
|
func getLinesFromString(input string) []string {
|
2016-11-10 16:06:38 +01:00
|
|
|
lines := getLinesUnfiltered(input)
|
2016-11-10 15:11:32 +01:00
|
|
|
|
|
|
|
var filtered []string
|
|
|
|
|
|
|
|
for _, line := range lines {
|
2016-11-10 16:06:38 +01:00
|
|
|
if !emptyLine(line) {
|
2016-11-10 15:11:32 +01:00
|
|
|
filtered = append(filtered, line)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filtered
|
|
|
|
}
|
|
|
|
|
2016-11-11 13:28:40 +01:00
|
|
|
func getLines(input []byte) []string {
|
2016-11-11 13:32:27 +01:00
|
|
|
return getLinesFromString(string(input))
|
2016-11-11 13:28:40 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 15:11:32 +01:00
|
|
|
func specialLine(line string) bool {
|
|
|
|
return (strings.HasPrefix(line, "BIRD") ||
|
|
|
|
strings.HasPrefix(line, "Access restricted"))
|
|
|
|
}
|
|
|
|
|
2016-11-10 14:51:24 +01:00
|
|
|
func parseStatus(input []byte) Parsed {
|
2016-11-10 15:11:32 +01:00
|
|
|
res := Parsed{}
|
|
|
|
lines := getLines(input)
|
|
|
|
|
|
|
|
start_line_rx := regexp.MustCompile(`^BIRD\s([0-9\.]+)\s*$`)
|
|
|
|
router_id_rx := regexp.MustCompile(`^Router\sID\sis\s([0-9\.]+)\s*$`)
|
2017-02-16 15:54:04 +01:00
|
|
|
current_server_rx := regexp.MustCompile(`^Current\sserver\stime\sis\s([0-9\-]+\s[0-9\:]+)\s*$`)
|
|
|
|
last_reboot_rx := regexp.MustCompile(`^Last\sreboot\son\s([0-9\-]+\s[0-9\:]+)\s*$`)
|
|
|
|
last_reconfig_rx := regexp.MustCompile(`^Last\sreconfiguration\son\s([0-9\-]+\s[0-9\:]+)\s*$`)
|
2016-11-10 16:06:38 +01:00
|
|
|
|
2016-11-10 15:11:32 +01:00
|
|
|
for _, line := range lines {
|
|
|
|
if start_line_rx.MatchString(line) {
|
|
|
|
res["version"] = start_line_rx.FindStringSubmatch(line)[1]
|
|
|
|
} else if router_id_rx.MatchString(line) {
|
|
|
|
res["router_id"] = router_id_rx.FindStringSubmatch(line)[1]
|
|
|
|
} else if current_server_rx.MatchString(line) {
|
|
|
|
res["current_server"] = current_server_rx.FindStringSubmatch(line)[1]
|
|
|
|
} else if last_reboot_rx.MatchString(line) {
|
|
|
|
res["last_reboot"] = last_reboot_rx.FindStringSubmatch(line)[1]
|
|
|
|
} else if last_reconfig_rx.MatchString(line) {
|
|
|
|
res["last_reconfig"] = last_reconfig_rx.FindStringSubmatch(line)[1]
|
|
|
|
} else {
|
|
|
|
res["message"] = line
|
|
|
|
}
|
|
|
|
}
|
2016-11-11 14:34:06 +01:00
|
|
|
return Parsed{"status": res}
|
2016-11-10 14:16:44 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 14:51:24 +01:00
|
|
|
func parseProtocols(input []byte) Parsed {
|
2016-11-10 16:06:38 +01:00
|
|
|
res := Parsed{}
|
|
|
|
protocols := []string{}
|
2016-11-11 13:28:40 +01:00
|
|
|
lines := getLinesUnfiltered(string(input))
|
2016-11-10 16:06:38 +01:00
|
|
|
|
|
|
|
proto := ""
|
|
|
|
for _, line := range lines {
|
|
|
|
if emptyLine(line) {
|
|
|
|
if !emptyLine(proto) {
|
|
|
|
protocols = append(protocols, proto)
|
|
|
|
}
|
|
|
|
proto = ""
|
|
|
|
} else {
|
|
|
|
proto += (line + "\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res["protocols"] = protocols
|
|
|
|
return res
|
2016-11-10 14:16:44 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 14:51:24 +01:00
|
|
|
func parseSymbols(input []byte) Parsed {
|
2016-11-10 15:11:32 +01:00
|
|
|
res := Parsed{}
|
|
|
|
lines := getLines(input)
|
|
|
|
|
|
|
|
key_rx := regexp.MustCompile(`^([^\s]+)\s+(.+)\s*$`)
|
|
|
|
for _, line := range lines {
|
|
|
|
if specialLine(line) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if key_rx.MatchString(line) {
|
|
|
|
groups := key_rx.FindStringSubmatch(line)
|
|
|
|
res[groups[2]] = groups[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-11 14:34:06 +01:00
|
|
|
return Parsed{"symbols": res}
|
2016-11-10 14:16:44 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 17:32:16 +01:00
|
|
|
func mainRouteDetail(groups []string, route Parsed) Parsed {
|
|
|
|
route["network"] = groups[1]
|
|
|
|
route["gateway"] = groups[2]
|
|
|
|
route["interface"] = groups[3]
|
|
|
|
route["from_protocol"] = groups[4]
|
|
|
|
route["age"] = groups[5]
|
|
|
|
route["learnt_from"] = groups[6]
|
|
|
|
route["primary"] = groups[7] == "*"
|
2016-11-11 16:00:36 +01:00
|
|
|
route["metric"] = parseInt(groups[8])
|
2016-11-10 17:32:16 +01:00
|
|
|
return route
|
|
|
|
}
|
|
|
|
|
2016-11-10 14:51:24 +01:00
|
|
|
func parseRoutes(input []byte) Parsed {
|
2016-11-10 17:32:16 +01:00
|
|
|
res := Parsed{}
|
|
|
|
lines := getLines(input)
|
|
|
|
|
|
|
|
routes := []Parsed{}
|
|
|
|
|
|
|
|
route := Parsed{}
|
2016-12-05 15:26:19 +01:00
|
|
|
start_def_rx := regexp.MustCompile(`^([0-9a-f\.\:\/]+)\s+via\s+([0-9a-f\.\:]+)\s+on\s+(\w+)\s+\[([\w\.:]+)\s+([0-9\-\:\s]+)(?:\s+from\s+([0-9a-f\.\:\/]+)){0,1}\]\s+(?:(\*)\s+){0,1}\((\d+)(?:\/\d+){0,1}\).*`)
|
2017-02-15 12:20:55 +01:00
|
|
|
second_rx := regexp.MustCompile(`^\s+via\s+([0-9a-f\.\:]+)\s+on\s+([\w+)\s+\[([\w\.]+)\s+([0-9\-\:\s]+)(?:\s+from\s+([0-9a-f\.\:\/]+)){0,1}\]\s+(?:(\*)\s+){0,1}\((\d+)(?:\/\d+){0,1}\).*$`)
|
2016-11-10 17:32:16 +01:00
|
|
|
type_rx := regexp.MustCompile(`^\s+Type:\s+(.*)\s*$`)
|
2016-12-05 16:36:59 +01:00
|
|
|
bgp_rx := regexp.MustCompile(`^\s+BGP.(\w+):\s+(.+)\s*$`)
|
2017-02-22 16:09:54 +01:00
|
|
|
community_rx := regexp.MustCompile(`^\((\d+),\s*(\d+)\)`)
|
|
|
|
large_community_rx := regexp.MustCompile(`^\((\d+),\s*(\d+),\s*(\d+)\)`)
|
|
|
|
paren_rx := regexp.MustCompile(`\([^\(]*\)\s*`)
|
2016-11-10 17:32:16 +01:00
|
|
|
for _, line := range lines {
|
|
|
|
if specialLine(line) || (len(route) == 0 && emptyLine(line)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if start_def_rx.MatchString(line) {
|
|
|
|
if len(route) > 0 {
|
|
|
|
routes = append(routes, route)
|
|
|
|
route = Parsed{}
|
|
|
|
}
|
|
|
|
route = mainRouteDetail(start_def_rx.FindStringSubmatch(line), route)
|
|
|
|
} else if second_rx.MatchString(line) {
|
|
|
|
routes = append(routes, route)
|
|
|
|
var network string
|
|
|
|
if tmp, ok := route["network"]; ok {
|
|
|
|
if val, ok := tmp.(string); ok {
|
|
|
|
network = val
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
route = Parsed{}
|
|
|
|
|
|
|
|
groups := second_rx.FindStringSubmatch(line)
|
|
|
|
first, groups := groups[0], groups[1:]
|
|
|
|
groups = append([]string{network}, groups...)
|
|
|
|
groups = append([]string{first}, groups...)
|
|
|
|
route = mainRouteDetail(groups, route)
|
|
|
|
} else if type_rx.MatchString(line) {
|
2016-11-11 14:26:07 +01:00
|
|
|
submatch := type_rx.FindStringSubmatch(line)[1]
|
|
|
|
route["type"] = strings.Split(submatch, " ")
|
2016-11-10 17:32:16 +01:00
|
|
|
} else if bgp_rx.MatchString(line) {
|
|
|
|
groups := bgp_rx.FindStringSubmatch(line)
|
|
|
|
bgp := Parsed{}
|
|
|
|
|
|
|
|
if tmp, ok := route["bgp"]; ok {
|
|
|
|
if val, ok := tmp.(Parsed); ok {
|
|
|
|
bgp = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if groups[1] == "community" {
|
|
|
|
communities := [][]int64{}
|
2017-02-22 16:09:54 +01:00
|
|
|
for _, community := range paren_rx.FindAllString(groups[2], -1) {
|
2016-11-10 17:32:16 +01:00
|
|
|
if community_rx.MatchString(community) {
|
|
|
|
com_groups := community_rx.FindStringSubmatch(community)
|
2016-11-11 15:00:15 +01:00
|
|
|
maj := parseInt(com_groups[1])
|
|
|
|
min := parseInt(com_groups[2])
|
2016-11-10 17:32:16 +01:00
|
|
|
communities = append(communities, []int64{maj, min})
|
|
|
|
}
|
|
|
|
}
|
2016-11-11 17:57:45 +01:00
|
|
|
bgp["communities"] = communities
|
2017-02-16 13:52:47 +01:00
|
|
|
} else if groups[1] == "large_community" {
|
|
|
|
communities := [][]int64{}
|
2017-02-22 16:09:54 +01:00
|
|
|
for _, community := range paren_rx.FindAllString(groups[2], -1) {
|
2017-02-16 13:52:47 +01:00
|
|
|
if large_community_rx.MatchString(community) {
|
|
|
|
com_groups := large_community_rx.FindStringSubmatch(community)
|
|
|
|
maj := parseInt(com_groups[1])
|
|
|
|
min := parseInt(com_groups[2])
|
|
|
|
pat := parseInt(com_groups[3])
|
|
|
|
communities = append(communities, []int64{maj, min, pat})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bgp["large_communities"] = communities
|
2016-11-25 14:22:22 +01:00
|
|
|
} else if groups[1] == "as_path" {
|
|
|
|
bgp["as_path"] = strings.Split(groups[2], " ")
|
2016-11-10 17:32:16 +01:00
|
|
|
} else {
|
|
|
|
bgp[groups[1]] = groups[2]
|
|
|
|
}
|
|
|
|
|
|
|
|
route["bgp"] = bgp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(route) > 0 {
|
|
|
|
routes = append(routes, route)
|
|
|
|
}
|
|
|
|
|
|
|
|
res["routes"] = routes
|
|
|
|
return res
|
2016-11-10 14:16:44 +01:00
|
|
|
}
|
|
|
|
|
2016-11-10 14:51:24 +01:00
|
|
|
func parseRoutesCount(input []byte) Parsed {
|
2016-11-10 15:11:32 +01:00
|
|
|
res := Parsed{}
|
|
|
|
lines := getLines(input)
|
|
|
|
|
|
|
|
count_rx := regexp.MustCompile(`^(\d+)\s+of\s+(\d+)\s+routes.*$`)
|
|
|
|
for _, line := range lines {
|
|
|
|
if specialLine(line) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if count_rx.MatchString(line) {
|
2016-11-11 14:26:07 +01:00
|
|
|
count := count_rx.FindStringSubmatch(line)[1]
|
2016-11-11 15:00:15 +01:00
|
|
|
res["routes"] = parseInt(count)
|
2016-11-10 15:11:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
2016-11-10 14:16:44 +01:00
|
|
|
}
|
2016-11-10 18:43:29 +01:00
|
|
|
|
2016-11-11 13:28:40 +01:00
|
|
|
// Will snake_case a value like that:
|
|
|
|
// I am a Weird stRiNg -> i_am_a_weird_string
|
|
|
|
func treatKey(key string) string {
|
2016-11-11 13:32:27 +01:00
|
|
|
spaces := regexp.MustCompile(`\s+`)
|
|
|
|
key = spaces.ReplaceAllString(key, "_")
|
|
|
|
return strings.ToLower(key)
|
2016-11-11 13:28:40 +01:00
|
|
|
}
|
|
|
|
|
2016-11-11 14:26:07 +01:00
|
|
|
func parseInt(from string) int64 {
|
|
|
|
val, err := strconv.ParseInt(from, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
|
2016-11-10 18:43:29 +01:00
|
|
|
func parseBgp(input string) Parsed {
|
2016-11-11 13:32:27 +01:00
|
|
|
res := Parsed{}
|
|
|
|
lines := getLinesFromString(input)
|
|
|
|
route_changes := Parsed{}
|
|
|
|
|
2016-12-05 14:06:17 +01:00
|
|
|
bgp_rx := regexp.MustCompile(`^([\w\.:]+)\s+BGP\s+(\w+)\s+(\w+)\s+([0-9]{4}-[0-9]{2}-[0-9]{2}\s+[0-9]{2}:[0-9]{2}:[0-9]{2})\s*(\w+)?.*$`)
|
2016-11-11 13:32:27 +01:00
|
|
|
num_val_rx := regexp.MustCompile(`^\s+([^:]+):\s+([\d]+)\s*$`)
|
|
|
|
str_val_rx := regexp.MustCompile(`^\s+([^:]+):\s+(.+)\s*$`)
|
|
|
|
routes_rx := regexp.MustCompile(`^\s+Routes:\s+(\d+)\s+imported,\s+(\d+)\s+filtered,\s+(\d+)\s+exported,\s+(\d+)\s+preferred\s*$`)
|
|
|
|
imp_updates_rx := regexp.MustCompile(`^\s+Import updates:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*$`)
|
|
|
|
imp_withdraws_rx := regexp.MustCompile(`^\s+Import withdraws:\s+(\d+)\s+(\d+)\s+\-\-\-\s+(\d+)\s+(\d+)\s*$`)
|
|
|
|
exp_updates_rx := regexp.MustCompile(`^\s+Export updates:\s+(\d+)\s+(\d+)\s+(\d+)\s+\-\-\-\s+(\d+)\s*$`)
|
2016-11-11 14:26:07 +01:00
|
|
|
exp_withdraws_rx := regexp.MustCompile(`^\s+Export withdraws:\s+(\d+)(\s+\-\-\-){2}\s+(\d+)\s*$`)
|
2016-11-11 13:32:27 +01:00
|
|
|
for _, line := range lines {
|
|
|
|
if bgp_rx.MatchString(line) {
|
|
|
|
groups := bgp_rx.FindStringSubmatch(line)
|
|
|
|
|
|
|
|
res["protocol"] = groups[1]
|
|
|
|
res["bird_protocol"] = "BGP"
|
|
|
|
res["table"] = groups[2]
|
|
|
|
res["state"] = groups[3]
|
|
|
|
res["state_changed"] = groups[4]
|
|
|
|
res["connection"] = groups[5]
|
|
|
|
} else if routes_rx.MatchString(line) {
|
|
|
|
routes := Parsed{}
|
|
|
|
groups := routes_rx.FindStringSubmatch(line)
|
|
|
|
|
2016-11-11 14:26:07 +01:00
|
|
|
routes["imported"] = parseInt(groups[1])
|
|
|
|
routes["filtered"] = parseInt(groups[2])
|
|
|
|
routes["exported"] = parseInt(groups[3])
|
|
|
|
routes["preferred"] = parseInt(groups[4])
|
2016-11-11 13:32:27 +01:00
|
|
|
|
|
|
|
res["routes"] = routes
|
|
|
|
} else if imp_updates_rx.MatchString(line) {
|
|
|
|
updates := Parsed{}
|
|
|
|
groups := imp_updates_rx.FindStringSubmatch(line)
|
|
|
|
|
2016-11-11 14:26:07 +01:00
|
|
|
updates["received"] = parseInt(groups[1])
|
|
|
|
updates["rejected"] = parseInt(groups[2])
|
|
|
|
updates["filtered"] = parseInt(groups[3])
|
|
|
|
updates["ignored"] = parseInt(groups[4])
|
|
|
|
updates["accepted"] = parseInt(groups[5])
|
2016-11-11 13:32:27 +01:00
|
|
|
|
|
|
|
route_changes["import_updates"] = updates
|
|
|
|
} else if imp_withdraws_rx.MatchString(line) {
|
|
|
|
updates := Parsed{}
|
|
|
|
groups := imp_withdraws_rx.FindStringSubmatch(line)
|
|
|
|
|
2016-11-11 14:26:07 +01:00
|
|
|
updates["received"] = parseInt(groups[1])
|
|
|
|
updates["rejected"] = parseInt(groups[2])
|
|
|
|
updates["filtered"] = parseInt(groups[3])
|
|
|
|
updates["accepted"] = parseInt(groups[4])
|
2016-11-11 13:32:27 +01:00
|
|
|
|
|
|
|
route_changes["import_withdraws"] = updates
|
|
|
|
} else if exp_updates_rx.MatchString(line) {
|
|
|
|
updates := Parsed{}
|
|
|
|
groups := exp_updates_rx.FindStringSubmatch(line)
|
|
|
|
|
2016-11-11 14:26:07 +01:00
|
|
|
updates["received"] = parseInt(groups[1])
|
|
|
|
updates["rejected"] = parseInt(groups[2])
|
|
|
|
updates["ignored"] = parseInt(groups[3])
|
|
|
|
updates["accepted"] = parseInt(groups[4])
|
2016-11-11 13:32:27 +01:00
|
|
|
|
|
|
|
route_changes["export_updates"] = updates
|
|
|
|
} else if exp_withdraws_rx.MatchString(line) {
|
|
|
|
updates := Parsed{}
|
|
|
|
groups := exp_withdraws_rx.FindStringSubmatch(line)
|
|
|
|
|
2016-11-11 14:26:07 +01:00
|
|
|
updates["received"] = parseInt(groups[1])
|
|
|
|
updates["accepted"] = parseInt(groups[3])
|
2016-11-11 13:32:27 +01:00
|
|
|
|
|
|
|
route_changes["export_withdraws"] = updates
|
|
|
|
} else if num_val_rx.MatchString(line) {
|
|
|
|
groups := num_val_rx.FindStringSubmatch(line)
|
|
|
|
|
|
|
|
key := treatKey(groups[1])
|
|
|
|
|
2016-11-11 14:26:07 +01:00
|
|
|
res[key] = parseInt(groups[2])
|
2016-11-11 13:32:27 +01:00
|
|
|
} else if str_val_rx.MatchString(line) {
|
|
|
|
groups := str_val_rx.FindStringSubmatch(line)
|
|
|
|
|
|
|
|
key := treatKey(groups[1])
|
|
|
|
|
|
|
|
res[key] = groups[2]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res["route_changes"] = route_changes
|
|
|
|
|
|
|
|
if _, ok := res["routes"]; !ok {
|
|
|
|
routes := Parsed{}
|
|
|
|
|
|
|
|
routes["accepted"] = 0
|
|
|
|
routes["filtered"] = 0
|
|
|
|
routes["exported"] = 0
|
|
|
|
routes["preferred"] = 0
|
|
|
|
|
|
|
|
res["routes"] = routes
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
2016-11-10 18:43:29 +01:00
|
|
|
}
|