diff --git a/bird/bird.go b/bird/bird.go index f1dc80b..b114b73 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -1,7 +1,9 @@ package bird import ( + "bytes" "fmt" + "io" "reflect" "strings" "sync" @@ -53,10 +55,16 @@ func toCache(key string, val Parsed) { Cache.Unlock() } -func Run(args string) ([]byte, error) { +func Run(args string) (io.Reader, error) { args = "show " + args argsList := strings.Split(args, " ") - return exec.Command(ClientConf.BirdCmd, argsList...).Output() + + out, err := exec.Command(ClientConf.BirdCmd, argsList...).Output() + if err != nil { + return nil, err + } + + return bytes.NewReader(out), nil } func InstallRateLimitReset() { @@ -93,7 +101,7 @@ func checkRateLimit() bool { return true } -func RunAndParse(cmd string, parser func([]byte) Parsed) (Parsed, bool) { +func RunAndParse(cmd string, parser func(io.Reader) Parsed) (Parsed, bool) { if val, ok := fromCache(cmd); ok { return val, true } diff --git a/bird/parser.go b/bird/parser.go index 012f839..ef6e9bd 100644 --- a/bird/parser.go +++ b/bird/parser.go @@ -2,6 +2,7 @@ package bird import ( "bufio" + "io" "regexp" "strconv" "strings" @@ -93,44 +94,32 @@ func emptyLine(line string) bool { return len(strings.TrimSpace(line)) == 0 } -func getLinesUnfiltered(input string) []string { - lines := make([]string, 0) +func getLines(reader io.Reader, excludeFilter func(string) bool) []string { + lines := []string{} - reader := strings.NewReader(input) scanner := bufio.NewScanner(reader) - for scanner.Scan() { + if excludeFilter != nil && excludeFilter(scanner.Text()) { + continue + } + lines = append(lines, scanner.Text()) } return lines } -func getLinesFromString(input string) []string { - lines := getLinesUnfiltered(input) - - var filtered []string - - for _, line := range lines { - if !emptyLine(line) { - filtered = append(filtered, line) - } - } - - return filtered -} - -func getLines(input []byte) []string { - return getLinesFromString(string(input)) +func getNonEmptyLines(reader io.Reader) []string { + return getLines(reader, emptyLine) } func specialLine(line string) bool { return (strings.HasPrefix(line, "BIRD") || strings.HasPrefix(line, "Access restricted")) } -func parseStatus(input []byte) Parsed { +func parseStatus(reader io.Reader) Parsed { res := Parsed{} - lines := getLines(input) + lines := getNonEmptyLines(reader) for _, line := range lines { if regex.status.startLine.MatchString(line) { @@ -157,10 +146,10 @@ func parseStatus(input []byte) Parsed { return Parsed{"status": res} } -func parseProtocols(input []byte) Parsed { +func parseProtocols(reader io.Reader) Parsed { res := Parsed{} protocols := []string{} - lines := getLinesUnfiltered(string(input)) + lines := getLines(reader, nil) proto := "" for _, line := range lines { @@ -178,9 +167,9 @@ func parseProtocols(input []byte) Parsed { return res } -func parseSymbols(input []byte) Parsed { +func parseSymbols(reader io.Reader) Parsed { res := Parsed{} - lines := getLines(input) + lines := getNonEmptyLines(reader) for _, line := range lines { if specialLine(line) { @@ -196,9 +185,9 @@ func parseSymbols(input []byte) Parsed { return Parsed{"symbols": res} } -func parseRoutes(input []byte) Parsed { +func parseRoutes(reader io.Reader) Parsed { res := Parsed{} - lines := getLines(input) + lines := getNonEmptyLines(reader) routes := []Parsed{} route := Parsed{} @@ -325,9 +314,9 @@ func parseRoutesLargeCommunities(groups []string, res Parsed) { res["large_communities"] = communities } -func parseRoutesCount(input []byte) Parsed { +func parseRoutesCount(reader io.Reader) Parsed { res := Parsed{} - lines := getLines(input) + lines := getNonEmptyLines(reader) for _, line := range lines { if specialLine(line) { @@ -351,7 +340,7 @@ func isCorrectChannel(currentIPVersion string) bool { return currentIPVersion == IPVersion } -func parseBgp(input string) Parsed { +func parseBgp(lines string) Parsed { res := Parsed{} routeChanges := Parsed{} @@ -366,9 +355,13 @@ func parseBgp(input string) Parsed { func(l string) bool { return parseBgpStringValuesRx(l, res) }, } - lines := getLinesFromString(input) ipVersion := "" - for _, line := range lines { + + reader := strings.NewReader(lines) + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + line := scanner.Text() + if m := regex.bgp.channel.FindStringSubmatch(line); len(m) > 0 { ipVersion = m[1] } diff --git a/bird/parser_test.go b/bird/parser_test.go index b1b4835..f394c46 100644 --- a/bird/parser_test.go +++ b/bird/parser_test.go @@ -1,15 +1,14 @@ package bird import ( - "fmt" - "io/ioutil" + "os" "reflect" "testing" ) -func readSampleData(filename string) ([]byte, error) { +func openFile(filename string) (*os.File, error) { sample := "../test/" + filename - return ioutil.ReadFile(sample) + return os.Open(sample) } func TestParseBgpRoutes(t *testing.T) { @@ -42,12 +41,13 @@ func TestParseBgpRoutes(t *testing.T) { } func TestParseRoutesAll(t *testing.T) { - sample, err := readSampleData("routes_bird1_ipv4.sample") + f, err := openFile("routes_bird1_ipv4.sample") if err != nil { t.Error(err) } + defer f.Close() - result := parseRoutes(sample) + result := parseRoutes(f) routes, ok := result["routes"].([]Parsed) if !ok { t.Error("Error getting routes") @@ -135,13 +135,14 @@ func TestParseRoutesAllBird2(t *testing.T) { runTestForIpv6WithTemplate("routes_bird2_ipv6.sample", t) } -func runTestForIpv6WithTemplate(template string, t *testing.T) { - sample, err := readSampleData(template) +func runTestForIpv6WithTemplate(file string, t *testing.T) { + f, err := openFile(file) if err != nil { t.Error(err) } + defer f.Close() - result := parseRoutes(sample) + result := parseRoutes(f) routes, ok := result["routes"].([]Parsed) if !ok { t.Fatal("Error getting routes") @@ -151,8 +152,6 @@ func runTestForIpv6WithTemplate(template string, t *testing.T) { t.Fatal("Expected 3 routes but got ", len(routes)) } - fmt.Println(routes[1]) - assertRouteIsEqual(expectedRoute{ network: "2001:4860::/32", gateway: "fe80:ffff:ffff::1",