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

Merge branch 'develop' into master for 1.11.4

Changes since last version:
* Fix race condition between main() and InstallRateLimitReset()
* Fix endpoint /routes/count/table now returns integer instead of routes
* Fix endpoint /routes/count/protocol now returns integer instead of routes
* Fix endpoint /routes/prefixed ignores URL paremeter
* Fix endpoint /symbols
* Fix TestParseProtocolBgp()
* Fix some filtered routes missing in /routes/dump
* Fix parser return correct type on error
* New endpoint for total number of best-paths '/routes/count/primary'
* Parser: support extended communities and test-cases
* Introduce new data structure for extended communities replacing the Parsed type
* Cache: avoid duplicate cache writes in Status()
This commit is contained in:
Benedikt Rudolph 2019-02-22 14:11:35 +01:00
commit 5279fe862f
11 changed files with 147 additions and 52 deletions

View file

@ -28,7 +28,7 @@ var Cache = struct {
var NilParse Parsed = (Parsed)(nil)
var BirdError Parsed = Parsed{"error": "bird unreachable"}
func isSpecial(ret Parsed) bool {
func IsSpecial(ret Parsed) bool {
return reflect.DeepEqual(ret, NilParse) || reflect.DeepEqual(ret, BirdError)
}
@ -132,10 +132,15 @@ func RunAndParse(cmd string, parser func(io.Reader) Parsed) (Parsed, bool) {
}
func Status() (Parsed, bool) {
birdStatus, ok := RunAndParse("status", parseStatus)
if isSpecial(birdStatus) {
return birdStatus, ok
birdStatus, from_cache := RunAndParse("status", parseStatus)
if IsSpecial(birdStatus) {
return birdStatus, from_cache
}
if from_cache {
return birdStatus, from_cache
}
status := birdStatus["status"].(Parsed)
// Last Reconfig Timestamp source:
@ -164,7 +169,7 @@ func Status() (Parsed, bool) {
birdStatus["status"] = status
return birdStatus, ok
return birdStatus, from_cache
}
func Protocols() (Parsed, bool) {
@ -173,7 +178,7 @@ func Protocols() (Parsed, bool) {
func ProtocolsBgp() (Parsed, bool) {
protocols, from_cache := Protocols()
if isSpecial(protocols) {
if IsSpecial(protocols) {
return protocols, from_cache
}
@ -195,7 +200,7 @@ func Symbols() (Parsed, bool) {
}
func RoutesPrefixed(prefix string) (Parsed, bool) {
cmd := routeQueryForChannel("route all")
cmd := routeQueryForChannel("route " + prefix + " all")
return RunAndParse(cmd, parseRoutes)
}
@ -206,7 +211,12 @@ func RoutesProto(protocol string) (Parsed, bool) {
func RoutesProtoCount(protocol string) (Parsed, bool) {
cmd := routeQueryForChannel("route protocol "+protocol) + " count"
return RunAndParse(cmd, parseRoutes)
return RunAndParse(cmd, parseRoutesCount)
}
func RoutesProtoPrimaryCount(protocol string) (Parsed, bool) {
cmd := routeQueryForChannel("route primary protocol "+protocol) + " count"
return RunAndParse(cmd, parseRoutesCount)
}
func RoutesFiltered(protocol string) (Parsed, bool) {
@ -294,10 +304,8 @@ func RoutesDumpPerPeerTable() (Parsed, bool) {
protocols := protocolsRes["protocols"].(Parsed)
for protocol, details := range protocols {
details, ok := details.(Parsed)
if !ok {
continue
}
details := details.(Parsed)
counters, ok := details["routes"].(Parsed)
if !ok {
continue
@ -307,7 +315,10 @@ func RoutesDumpPerPeerTable() (Parsed, bool) {
continue // nothing to do here.
}
// Lookup filtered routes
pfilteredRes, _ := RoutesFiltered(protocol)
pfilteredRes, from_cache := RoutesFiltered(protocol)
if reflect.DeepEqual(pfilteredRes, BirdError) {
return pfilteredRes, from_cache
}
pfiltered, ok := pfilteredRes["routes"].([]Parsed)
if !ok {

View file

@ -37,15 +37,16 @@ var (
countRx *regexp.Regexp
}
routes struct {
startDefinition *regexp.Regexp
second *regexp.Regexp
routeType *regexp.Regexp
bgp *regexp.Regexp
community *regexp.Regexp
largeCommunity *regexp.Regexp
origin *regexp.Regexp
prefixBird2 *regexp.Regexp
gatewayBird2 *regexp.Regexp
startDefinition *regexp.Regexp
second *regexp.Regexp
routeType *regexp.Regexp
bgp *regexp.Regexp
community *regexp.Regexp
largeCommunity *regexp.Regexp
extendedCommunity *regexp.Regexp
origin *regexp.Regexp
prefixBird2 *regexp.Regexp
gatewayBird2 *regexp.Regexp
}
}
)
@ -76,6 +77,7 @@ func init() {
regex.routes.bgp = regexp.MustCompile(`^\s+BGP.(\w+):\s+(.+)\s*$`)
regex.routes.community = regexp.MustCompile(`^\((\d+),\s*(\d+)\)`)
regex.routes.largeCommunity = regexp.MustCompile(`^\((\d+),\s*(\d+),\s*(\d+)\)`)
regex.routes.extendedCommunity = regexp.MustCompile(`^\(([^,]+),\s*(\d+),\s*(\d+)\)`)
regex.routes.origin = regexp.MustCompile(`\([^\(]*\)\s*`)
regex.routes.prefixBird2 = regexp.MustCompile(`^([0-9a-f\.\:\/]+)?\s+unicast\s+\[([\w\.:]+)\s+([0-9\-\:\s]+)(?:\s+from\s+([0-9a-f\.\:\/]+))?\]\s+(?:(\*)\s+)?\((\d+)(?:\/\d+)?(?:\/[^\)]*)?\).*$`)
regex.routes.gatewayBird2 = regexp.MustCompile(`^\s+via\s+([0-9a-f\.\:]+)\s+on\s+([\w\.]+)\s*$`)
@ -167,7 +169,12 @@ func parseSymbols(reader io.Reader) Parsed {
if regex.symbols.keyRx.MatchString(line) {
groups := regex.symbols.keyRx.FindStringSubmatch(line)
res[groups[2]] = groups[1]
if _, ok := res[groups[2]]; !ok {
res[groups[2]] = []string{}
}
res[groups[2]] = append(res[groups[2]].([]string), groups[1])
}
}
@ -395,6 +402,8 @@ func parseRoutesBgp(line string, bgp Parsed) {
parseRoutesCommunities(groups, bgp)
} else if groups[1] == "large_community" {
parseRoutesLargeCommunities(groups, bgp)
} else if groups[1] == "ext_community" {
parseRoutesExtendedCommunities(groups, bgp)
} else if groups[1] == "as_path" {
bgp["as_path"] = strings.Split(groups[2], " ")
} else {
@ -431,6 +440,19 @@ func parseRoutesLargeCommunities(groups []string, res Parsed) {
res["large_communities"] = communities
}
func parseRoutesExtendedCommunities(groups []string, res Parsed) {
communities := []interface{}{}
for _, community := range regex.routes.origin.FindAllString(groups[2], -1) {
if regex.routes.extendedCommunity.MatchString(community) {
communityGroups := regex.routes.extendedCommunity.FindStringSubmatch(community)
communities = append(communities, []interface{}{communityGroups[1], parseInt(communityGroups[2]), parseInt(communityGroups[3])})
}
}
res["ext_communities"] = communities
}
func parseRoutesCount(reader io.Reader) Parsed {
res := Parsed{}
@ -491,10 +513,10 @@ func parseProtocol(lines string) Parsed {
if _, ok := res["routes"]; !ok {
routes := Parsed{}
routes["accepted"] = 0
routes["filtered"] = 0
routes["exported"] = 0
routes["preferred"] = 0
routes["accepted"] = int64(0)
routes["filtered"] = int64(0)
routes["exported"] = int64(0)
routes["preferred"] = int64(0)
res["routes"] = routes
}
@ -597,7 +619,7 @@ func treatKey(key string) string {
func parseInt(from string) int64 {
val, err := strconv.ParseInt(from, 10, 64)
if err != nil {
return 0
return int64(0)
}
return val

View file

@ -53,14 +53,7 @@ func TestParseProtocolBgp(t *testing.T) {
p := parseProtocols(f)
log.Printf("%# v", pretty.Formatter(p))
lines := p["protocols"].([]string)
protocols := []Parsed{}
for _, v := range lines {
p2 := parseProtocol(v)
protocols = append(protocols, p2)
}
protocols := p["protocols"].(Parsed)
if len(protocols) != 3 {
//log.Printf("%# v", pretty.Formatter(protocols))
@ -68,7 +61,6 @@ func TestParseProtocolBgp(t *testing.T) {
}
fmt.Println(protocols)
}
func TestParseRoutesAllIpv4Bird1(t *testing.T) {
@ -108,6 +100,9 @@ func runTestForIpv4WithFile(file string, t *testing.T) {
{9033, 65666, 12},
{9033, 65666, 9},
},
extendedCommunities: []interface{}{
[]interface{}{"rt", int64(48858), int64(50)},
},
metric: 100,
localPref: "100",
protocol: "ID8503_AS1340",
@ -126,6 +121,11 @@ func runTestForIpv4WithFile(file string, t *testing.T) {
{9033, 65666, 12},
{9033, 65666, 9},
},
extendedCommunities: []interface{}{
[]interface{}{"ro", int64(21414), int64(52001)},
[]interface{}{"ro", int64(21414), int64(52004)},
[]interface{}{"ro", int64(21414), int64(64515)},
},
metric: 100,
localPref: "100",
protocol: "ID8497_AS1339",
@ -144,6 +144,11 @@ func runTestForIpv4WithFile(file string, t *testing.T) {
{9033, 65666, 12},
{9033, 65666, 9},
},
extendedCommunities: []interface{}{
[]interface{}{"ro", int64(21414), int64(52001)},
[]interface{}{"ro", int64(21414), int64(52004)},
[]interface{}{"ro", int64(21414), int64(64515)},
},
metric: 100,
localPref: "100",
protocol: "ID8503_AS1340",
@ -162,6 +167,9 @@ func runTestForIpv4WithFile(file string, t *testing.T) {
{9033, 65666, 12},
{9033, 65666, 9},
},
extendedCommunities: []interface{}{
[]interface{}{"rt", int64(48858), int64(50)},
},
metric: 100,
localPref: "100",
protocol: "ID8503_AS1340",
@ -207,6 +215,11 @@ func runTestForIpv6WithFile(file string, t *testing.T) {
{48821, 0, 2000},
{48821, 0, 2100},
},
extendedCommunities: []interface{}{
[]interface{}{"ro", int64(21414), int64(52001)},
[]interface{}{"ro", int64(21414), int64(52004)},
[]interface{}{"ro", int64(21414), int64(64515)},
},
metric: 100,
localPref: "500",
primary: true,
@ -225,6 +238,11 @@ func runTestForIpv6WithFile(file string, t *testing.T) {
{48821, 0, 3000},
{48821, 0, 3100},
},
extendedCommunities: []interface{}{
[]interface{}{"ro", int64(21414), int64(52001)},
[]interface{}{"ro", int64(21414), int64(52004)},
[]interface{}{"ro", int64(21414), int64(64515)},
},
localPref: "100",
metric: 100,
primary: false,
@ -243,6 +261,9 @@ func runTestForIpv6WithFile(file string, t *testing.T) {
{48821, 0, 2000},
{48821, 0, 2100},
},
extendedCommunities: []interface{}{
[]interface{}{"unknown 0x4300", int64(0), int64(1)},
},
metric: 100,
localPref: "5000",
primary: true,
@ -288,6 +309,12 @@ func assertRouteIsEqual(expected expectedRoute, actual Parsed, name string, t *t
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{} {
@ -300,14 +327,15 @@ func value(parsed Parsed, key, name string, t *testing.T) interface{} {
}
type expectedRoute struct {
network string
gateway string
asPath []string
community [][]int64
largeCommunities [][]int64
metric int64
protocol string
primary bool
localPref string
iface string
network string
gateway string
asPath []string
community [][]int64
largeCommunities [][]int64
extendedCommunities []interface{}
metric int64
protocol string
primary bool
localPref string
iface string
}

View file

@ -65,6 +65,9 @@ func makeRouter(config endpoints.ServerConfig) *httprouter.Router {
if isModuleEnabled("routes_count_table", whitelist) {
r.GET("/routes/count/table/:table", endpoints.Endpoint(endpoints.TableCount))
}
if isModuleEnabled("routes_count_primary", whitelist) {
r.GET("/routes/count/primary/:protocol", endpoints.Endpoint(endpoints.ProtoPrimaryCount))
}
if isModuleEnabled("routes_filtered", whitelist) {
r.GET("/routes/filtered/:protocol", endpoints.Endpoint(endpoints.RoutesFiltered))
}
@ -156,7 +159,9 @@ func main() {
// Configuration
bird.ClientConf = birdConf
bird.StatusConf = conf.Status
bird.RateLimitConf.Lock()
bird.RateLimitConf.Conf = conf.Ratelimit
bird.RateLimitConf.Unlock()
bird.ParserConf = conf.Parser
endpoints.Conf = conf.Server

View file

@ -58,8 +58,16 @@ func ProtoCount(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) {
return bird.RoutesProtoCount(protocol)
}
func ProtoPrimaryCount(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) {
protocol, err := ValidateProtocolParam(ps.ByName("protocol"))
if err != nil {
return bird.Parsed{"error": fmt.Sprintf("%s", err)}, false
}
return bird.RoutesProtoPrimaryCount(protocol)
}
func TableCount(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) {
return bird.RoutesTable(ps.ByName("table"))
return bird.RoutesTableCount(ps.ByName("table"))
}
func RouteNet(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) {

View file

@ -13,10 +13,16 @@ func Symbols(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) {
func SymbolTables(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) {
val, from_cache := bird.Symbols()
return bird.Parsed{"symbols": val["routing table"]}, from_cache
if bird.IsSpecial(val) {
return val, from_cache
}
return bird.Parsed{"symbols": val["symbols"].(bird.Parsed)["routing table"]}, from_cache
}
func SymbolProtocols(r *http.Request, ps httprouter.Params) (bird.Parsed, bool) {
val, from_cache := bird.Symbols()
return bird.Parsed{"symbols": val["protocols"]}, from_cache
if bird.IsSpecial(val) {
return val, from_cache
}
return bird.Parsed{"symbols": val["symbols"].(bird.Parsed)["protocol"]}, from_cache
}

View file

@ -18,6 +18,7 @@ allow_from = []
# routes_table
# routes_count_protocol
# routes_count_table
# routes_count_primary
# routes_filtered
# routes_prefixed
# routes_noexport

View file

@ -7,6 +7,7 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (65011,3) (9033,3251)
BGP.large_community: (9033, 65666, 12) (9033, 65666, 9)
BGP.ext_community: (rt, 48858, 50)
200.0.0.0/24 via 1.2.3.15 on eno7 [ID8497_AS1339 2017-06-21 08:17:31] * (100) [AS1339i]
Type: BGP unicast univ
BGP.origin: IGP
@ -15,6 +16,7 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (65011,40) (9033,3251)
BGP.large_community: (9033, 65666, 12) (9033, 65666, 9)
BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515)
via 1.2.3.16 on eno8 [ID8503_AS1340 2017-06-21 08:17:33] (100) [AS1340i]
Type: BGP unicast univ
BGP.origin: IGP
@ -23,6 +25,7 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (65011,3) (9033,3251)
BGP.large_community: (9033, 65666, 12) (9033, 65666, 9)
BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515)
16.0.0.0/24 via 1.2.3.16 on eno7 [ID8503_AS1340 2017-06-21 08:17:33] * (100) [AS1340i]
Type: BGP unicast univ
BGP.origin: IGP
@ -31,3 +34,4 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (65011,3) (9033,3251)
BGP.large_community: (9033, 65666, 12) (9033, 65666, 9)
BGP.ext_community: (rt, 48858, 50)

View file

@ -8,6 +8,7 @@ BIRD 1.6.3 ready.
BGP.local_pref: 500
BGP.community: (9033,3001) (65000,680)
BGP.large_community: (48821, 0, 2000) (48821, 0, 2100)
BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515)
via fe80:ffff:ffff::2 on eth3 [upstream2 2018-01-14 14:33:52] (100) [AS15169i]
Type: BGP unicast univ
BGP.origin: IGP
@ -17,6 +18,7 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (50629,200) (50629,201)
BGP.large_community: (48821, 0, 3000) (48821, 0, 3100)
BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515)
2001:678:1e0::/48 via fe80:ffff:ffff::2 on eth2 [upstream2 2018-01-14 15:04:17 from 2001:678:1e0::2] * (100) [AS202739i]
Type: BGP unicast univ
BGP.origin: IGP
@ -24,4 +26,5 @@ BIRD 1.6.3 ready.
BGP.next_hop: 2001:678:1e0::2
BGP.local_pref: 5000
BGP.community: (48821,2000) (48821,2100)
BGP.large_community: (48821, 0, 2000) (48821, 0, 2100)
BGP.large_community: (48821, 0, 2000) (48821, 0, 2100)
BGP.ext_community: (unknown 0x4300, 0, 1)

View file

@ -8,6 +8,7 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (65011,3) (9033,3251)
BGP.large_community: (9033, 65666, 12) (9033, 65666, 9)
BGP.ext_community: (rt, 48858, 50)
200.0.0.0/24 unicast [ID8497_AS1339 2017-06-21 08:17:31] * (100) [AS1339i]
via 1.2.3.15 on eno7
Type: BGP univ
@ -17,6 +18,7 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (65011,40) (9033,3251)
BGP.large_community: (9033, 65666, 12) (9033, 65666, 9)
BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515)
unicast [ID8503_AS1340 2017-06-21 08:17:33] (100/?) [AS1340i]
via 1.2.3.16 on eno8
Type: BGP univ
@ -26,6 +28,7 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (65011,3) (9033,3251)
BGP.large_community: (9033, 65666, 12) (9033, 65666, 9)
BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515)
16.0.0.0/24 unicast [ID8503_AS1340 2017-06-21 08:17:33] * (100) [AS1340i]
via 1.2.3.16 on eno7
Type: BGP univ
@ -35,3 +38,4 @@ BIRD 1.6.3 ready.
BGP.local_pref: 100
BGP.community: (65011,3) (9033,3251)
BGP.large_community: (9033, 65666, 12) (9033, 65666, 9)
BGP.ext_community: (rt, 48858, 50)

View file

@ -9,6 +9,7 @@ BIRD 2.0.0 ready.
BGP.local_pref: 500
BGP.community: (9033,3001) (65000,680)
BGP.large_community: (48821, 0, 2000) (48821, 0, 2100)
BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515)
unicast [upstream2 2018-01-14 13:07:26 from fe80:ffff:ffff::2] (100/?) [AS15169i]
via fe80:ffff:ffff::2 on eth3
Type: BGP univ
@ -19,6 +20,7 @@ BIRD 2.0.0 ready.
BGP.local_pref: 100
BGP.community: (50629,200) (50629,201)
BGP.large_community: (48821, 0, 3000) (48821, 0, 3100)
BGP.ext_community: (ro, 21414, 52001) (ro, 21414, 52004) (ro, 21414, 64515)
2001:678:1e0::/48 unicast [upstream2 2018-01-15 20:31:39 from fe80:ffff:ffff::2] * (100/?) [i]
via fe80:ffff:ffff::2 on eth2
Type: BGP univ
@ -27,4 +29,5 @@ BIRD 2.0.0 ready.
BGP.next_hop: 2001:678:1e0::2
BGP.local_pref: 5000
BGP.community: (48821,2000) (48821,2100)
BGP.large_community: (48821, 0, 2000) (48821, 0, 2100)
BGP.large_community: (48821, 0, 2000) (48821, 0, 2100)
BGP.ext_community: (unknown 0x4300, 0, 1)