1
0
Fork 0
mirror of https://github.com/alice-lg/birdwatcher.git synced 2025-03-09 00:00:05 +01:00
birdwatcher/bird/parser.go

358 lines
10 KiB
Go
Raw Permalink Normal View History

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*$`)
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
}
}
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]
}
}
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
} 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) {
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
}