package bird import ( "fmt" "log" "os" "reflect" "testing" "github.com/kr/pretty" ) func openFile(filename string) (*os.File, error) { sample := "../test/" + filename return os.Open(sample) } func TestParseStatus(t *testing.T) { tests := []struct { file string expected Parsed }{ // Test for bird1.x status { "status1.sample", Parsed{ "current_server": "2021-03-30 02:28:45", "last_reboot": "2021-03-30 02:28:19", "last_reconfig": "2021-03-30 02:28:19", "message": "Daemon is up and running", "router_id": "172.25.3.2", "version": "1.6.6", }, }, // Test for bird2.x status { "status2.sample", Parsed{ "current_server": "2021-03-30 02:23:32.330", "last_reboot": "2021-03-30 01:58:07.850", "last_reconfig": "2021-03-30 01:58:07.850", "message": "Daemon is up and running", "router_id": "172.25.3.2", "version": "2.0.7", }, }, // Test with advanced version { "status3.sample", Parsed{ "current_server": "2022-06-07 08:28:16", "last_reboot": "2022-06-03 12:35:43", "last_reconfig": "2022-06-03 12:35:43", "message": "Daemon is up and running", "router_id": "1.2.3.4", "version": "v2.0.9-11-g207ac485", }, }, } for _, test := range tests { f, err := openFile(test.file) if err != nil { t.Error(err) } s := parseStatus(f) f.Close() status := s["status"].(Parsed) if !reflect.DeepEqual(status, test.expected) { t.Error("Parse status: ", status, "expected: ", test.expected) } } } func TestParseBgpRoutes(t *testing.T) { inputs := []string{ "1 imported, 0 filtered, 2 exported, 1 preferred", "0 imported, 2846 exported", // Bird 1.4.x } expected := []Parsed{ Parsed{ "imported": int64(1), "filtered": int64(0), "exported": int64(2), "preferred": int64(1), }, Parsed{ "imported": int64(0), "exported": int64(2846), }, } for i, in := range inputs { routes := parseProtocolRoutes(in) if !reflect.DeepEqual(routes, expected[i]) { t.Error("Parse bgpRoutes:", routes, "expected:", expected[i]) } } } func TestParseProtocolBgp(t *testing.T) { f, err := openFile("protocols_bgp_pipe.sample") if err != nil { t.Error(err) } defer f.Close() p := parseProtocols(f) log.Printf("%# v", pretty.Formatter(p)) protocols := p["protocols"].(Parsed) if len(protocols) != 3 { //log.Printf("%# v", pretty.Formatter(protocols)) t.Fatalf("Expected 3 protocols, found: %v", len(protocols)) } fmt.Println(protocols) } func TestParseProtocolShort(t *testing.T) { f, err := openFile("protocols_short.sample") if err != nil { t.Error(err) } defer f.Close() p := parseProtocolsShort(f) log.Printf("%# v", pretty.Formatter(p)) protocols := p["protocols"].(Parsed) if len(protocols) != 27 { //log.Printf("%# v", pretty.Formatter(protocols)) t.Fatalf("Expected 27 protocols, found: %v", len(protocols)) } fmt.Println(protocols) } func TestParseRoutesAllIpv4Bird1(t *testing.T) { runTestForIpv4WithFile("routes_bird1_ipv4.sample", t) } func TestParseRoutesAllIpv4Bird2(t *testing.T) { runTestForIpv4WithFile("routes_bird2_ipv4.sample", t) } func runTestForIpv4WithFile(file string, t *testing.T) { f, err := openFile(file) if err != nil { t.Error(err) } defer f.Close() result := parseRoutes(f) routes, ok := result["routes"].([]Parsed) if !ok { t.Fatal("Error getting routes") } if len(routes) != 4 { t.Fatal("Expected 4 routes but got ", len(routes)) } assertRouteIsEqual(expectedRoute{ network: "16.0.0.0/24", gateway: "1.2.3.16", asPath: []string{"1340"}, community: [][]int64{ {0, 5464}, {0, 8339}, {0, 8741}, {0, 8823}, {0, 12387}, {0, 13101}, {0, 16097}, {0, 16316}, {0, 20546}, {0, 20686}, {0, 20723}, {0, 21083}, {0, 21385}, {0, 24940}, {0, 25504}, {0, 28876}, {0, 29545}, {0, 30058}, {0, 31103}, {0, 31400}, {0, 39090}, {0, 39392}, {0, 39912}, {0, 42473}, {0, 43957}, {0, 44453}, {0, 47297}, {0, 47692}, {0, 48200}, {0, 50629}, {0, 51191}, {0, 51839}, {0, 51852}, {0, 54113}, {0, 56719}, {0, 57957}, {0, 60517}, {0, 60574}, {0, 61303}, {0, 62297}, {0, 62336}, {0, 62359}, {33891, 33892}, {33891, 50673}, {48793, 48793}, {50673, 500}, {65101, 11077}, {65102, 11000}, {65103, 724}, {65104, 150}, }, largeCommunities: [][]int64{ {9033, 65666, 12}, {9033, 65666, 9}, }, extendedCommunities: []interface{}{ []interface{}{"rt", "42", "1234"}, []interface{}{"generic", "0x43000000", "0x1"}, }, metric: 100, localPref: "100", protocol: "ID8503_AS1340", primary: true, iface: "eno7", }, routes[0], "Route 1", t) assertRouteIsEqual(expectedRoute{ network: "200.0.0.0/24", gateway: "1.2.3.15", asPath: []string{"1339"}, community: [][]int64{ {65011, 40}, {9033, 3251}, }, largeCommunities: [][]int64{ {9033, 65666, 12}, {9033, 65666, 9}, }, extendedCommunities: []interface{}{ []interface{}{"ro", "21414", "52001"}, []interface{}{"ro", "21414", "52004"}, []interface{}{"ro", "21414", "64515"}, }, metric: 100, localPref: "100", protocol: "ID8497_AS1339", primary: true, iface: "eno7", }, routes[1], "Route 2", t) assertRouteIsEqual(expectedRoute{ network: "200.0.0.0/24", gateway: "1.2.3.16", asPath: []string{"1340"}, community: [][]int64{ {65011, 3}, {9033, 3251}, }, largeCommunities: [][]int64{ {9033, 65666, 12}, {9033, 65666, 9}, }, extendedCommunities: []interface{}{ []interface{}{"ro", "21414", "52001"}, []interface{}{"ro", "21414", "52004"}, []interface{}{"ro", "21414", "64515"}, }, metric: 100, localPref: "100", protocol: "ID8503_AS1340", primary: false, iface: "eno8", }, routes[2], "Route 3", t) assertRouteIsEqual(expectedRoute{ network: "16.0.0.0/24", gateway: "1.2.3.16", asPath: []string{"1340"}, community: [][]int64{ {65011, 3}, {9033, 3251}, }, largeCommunities: [][]int64{ {9033, 65666, 12}, {9033, 65666, 9}, }, extendedCommunities: []interface{}{ []interface{}{"rt", "42", "1234"}, []interface{}{"generic", "0x43000000", "0x1"}, }, metric: 100, localPref: "100", protocol: "ID8503_AS1340", primary: true, iface: "eno7", }, routes[3], "Route 4", t) } func TestParseRoutesAllIpv6Bird1(t *testing.T) { runTestForIpv6WithFile("routes_bird1_ipv6.sample", t) } func TestParseRoutesAllIpv6Bird2(t *testing.T) { runTestForIpv6WithFile("routes_bird2_ipv6.sample", t) } func runTestForIpv6WithFile(file string, t *testing.T) { f, err := openFile(file) if err != nil { t.Error(err) } defer f.Close() result := parseRoutes(f) routes, ok := result["routes"].([]Parsed) if !ok { t.Fatal("Error getting routes") } if len(routes) != 3 { t.Fatal("Expected 3 routes but got ", len(routes)) } assertRouteIsEqual(expectedRoute{ network: "2001:4860::/32", gateway: "fe80:ffff:ffff::1", asPath: []string{"15169"}, community: [][]int64{ {0, 5464}, {0, 8339}, {0, 8741}, {0, 8823}, {0, 12387}, {0, 13101}, {0, 16097}, {0, 16316}, {0, 20546}, {0, 20686}, {0, 20723}, {0, 21083}, {0, 21385}, {0, 24940}, {0, 25504}, {0, 28876}, {0, 29545}, {0, 30058}, {0, 31103}, {0, 31400}, {0, 39090}, {0, 39392}, {0, 39912}, {0, 42473}, {0, 43957}, {0, 44453}, {0, 47297}, {0, 47692}, {0, 48200}, {0, 50629}, {0, 51191}, {0, 51839}, {0, 51852}, {0, 54113}, {0, 56719}, {0, 57957}, {0, 60517}, {0, 60574}, {0, 61303}, {0, 62297}, {0, 62336}, {0, 62359}, {33891, 33892}, {33891, 50673}, {48793, 48793}, {50673, 500}, {65101, 11077}, {65102, 11000}, {65103, 724}, {65104, 150}, }, largeCommunities: [][]int64{ {48821, 0, 2000}, {48821, 0, 2100}, }, extendedCommunities: []interface{}{ []interface{}{"ro", "21414", "52001"}, []interface{}{"ro", "21414", "52004"}, []interface{}{"ro", "21414", "64515"}, }, metric: 100, localPref: "500", primary: true, protocol: "upstream1", iface: "eth2", }, routes[0], "Route 1", t) assertRouteIsEqual(expectedRoute{ network: "2001:4860::/32", gateway: "fe80:ffff:ffff::2", asPath: []string{"50629", "15169"}, community: [][]int64{ {50629, 200}, {50629, 201}, }, largeCommunities: [][]int64{ {48821, 0, 3000}, {48821, 0, 3100}, }, extendedCommunities: []interface{}{ []interface{}{"ro", "21414", "52001"}, []interface{}{"ro", "21414", "52004"}, []interface{}{"ro", "21414", "64515"}, }, localPref: "100", metric: 100, primary: false, protocol: "upstream2", iface: "eth3", }, routes[1], "Route 2", t) assertRouteIsEqual(expectedRoute{ network: "2001:678:1e0::/48", gateway: "fe80:ffff:ffff::2", asPath: []string{"202739"}, community: [][]int64{ {48821, 2000}, {48821, 2100}, }, largeCommunities: [][]int64{ {48821, 0, 2000}, {48821, 0, 2100}, }, extendedCommunities: []interface{}{ []interface{}{"unknown 0x4300", "0", "1"}, }, metric: 100, localPref: "5000", primary: true, protocol: "upstream2", iface: "eth2", }, routes[2], "Route 3", t) } func assertRouteIsEqual(expected expectedRoute, actual Parsed, name string, t *testing.T) { if prefix := value(actual, "network", name, t).(string); prefix != expected.network { t.Fatal(name, ": Expected network to be:", expected.network, "not", prefix) } if nextHop := value(actual, "gateway", name, t).(string); nextHop != expected.gateway { t.Fatal(name, ": Expected gateway to be:", expected.gateway, "not", nextHop) } if metric := value(actual, "metric", name, t).(int64); metric != expected.metric { t.Fatal(name, ": Expected metric to be:", expected.metric, "not", metric) } if protocol := value(actual, "from_protocol", name, t).(string); protocol != expected.protocol { t.Fatal(name, ": Expected protocol to be:", expected.protocol, "not", protocol) } if iface := value(actual, "interface", name, t).(string); iface != expected.iface { t.Fatal(name, ": Expected interface to be:", expected.iface, "not", iface) } bgp := actual["bgp"].(Parsed) if localPref := value(bgp, "local_pref", name, t).(string); localPref != expected.localPref { t.Fatal(name, ": Expected local_pref to be:", expected.localPref, "not", localPref) } if asPath := value(bgp, "as_path", name, t).([]string); !reflect.DeepEqual(asPath, expected.asPath) { t.Fatal(name, ": Expected as_path to be:", expected.asPath, "not", asPath) } if community := value(bgp, "communities", name, t).([][]int64); !reflect.DeepEqual(community, expected.community) { t.Fatal(name, ": Expected community to be:", expected.community, "not", community) } if largeCommunity := value(bgp, "large_communities", name, t).([][]int64); !reflect.DeepEqual(largeCommunity, expected.largeCommunities) { t.Fatal(name, ": Expected large_community to be:", expected.largeCommunities, "not", largeCommunity) } if extendedCommunity, ok := bgp["ext_communities"]; ok { if !reflect.DeepEqual(extendedCommunity.([]interface{}), expected.extendedCommunities) { t.Fatal(name, ": Expected ext_community to be:", expected.extendedCommunities, "not", extendedCommunity) } } } func value(parsed Parsed, key, name string, t *testing.T) interface{} { v, ok := parsed[key] if !ok { t.Fatal(name, ": Key not found", key) } return v } type expectedRoute struct { network string gateway string asPath []string community [][]int64 largeCommunities [][]int64 extendedCommunities []interface{} metric int64 protocol string primary bool localPref string iface string }