mirror of
https://github.com/alice-lg/alice-lg.git
synced 2024-05-11 05:55:03 +00:00
316 lines
7.9 KiB
Go
316 lines
7.9 KiB
Go
package birdwatcher
|
|
|
|
import (
|
|
"github.com/alice-lg/alice-lg/backend/api"
|
|
|
|
"log"
|
|
"sort"
|
|
)
|
|
|
|
|
|
type SingleTableBirdwatcher struct {
|
|
GenericBirdwatcher
|
|
}
|
|
|
|
|
|
func (self *SingleTableBirdwatcher) fetchReceivedRoutes(neighborId string) (*api.ApiStatus, api.Routes, error) {
|
|
// Query birdwatcher
|
|
bird, err := self.client.GetJson("/routes/protocol/" + neighborId)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Use api status from first request
|
|
apiStatus, err := parseApiStatus(bird, self.config)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Parse the routes
|
|
received, err := parseRoutes(bird, self.config)
|
|
if err != nil {
|
|
log.Println("WARNING Could not retrieve received routes:", err)
|
|
log.Println("Is the 'routes_protocol' module active in birdwatcher?")
|
|
return &apiStatus, nil, err
|
|
}
|
|
|
|
return &apiStatus, received, nil
|
|
}
|
|
|
|
func (self *SingleTableBirdwatcher) fetchFilteredRoutes(neighborId string) (*api.ApiStatus, api.Routes, error) {
|
|
// Query birdwatcher
|
|
bird, err := self.client.GetJson("/routes/filtered/" + neighborId)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Use api status from first request
|
|
apiStatus, err := parseApiStatus(bird, self.config)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Parse the routes
|
|
filtered, err := parseRoutes(bird, self.config)
|
|
if err != nil {
|
|
log.Println("WARNING Could not retrieve filtered routes:", err)
|
|
log.Println("Is the 'routes_filtered' module active in birdwatcher?")
|
|
return &apiStatus, nil, err
|
|
}
|
|
|
|
return &apiStatus, filtered, nil
|
|
}
|
|
|
|
func (self *SingleTableBirdwatcher) fetchNotExportedRoutes(neighborId string) (*api.ApiStatus, api.Routes, error) {
|
|
// Query birdwatcher
|
|
bird, err := self.client.GetJson("/routes/noexport/" + neighborId)
|
|
|
|
// Use api status from first request
|
|
apiStatus, err := parseApiStatus(bird, self.config)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
// Parse the routes
|
|
notExported, err := parseRoutes(bird, self.config)
|
|
if err != nil {
|
|
log.Println("WARNING Could not retrieve routes not exported:", err)
|
|
log.Println("Is the 'routes_noexport' module active in birdwatcher?")
|
|
}
|
|
|
|
return &apiStatus, notExported, nil
|
|
}
|
|
|
|
/*
|
|
RoutesRequired is a specialized request to fetch:
|
|
|
|
- RoutesExported and
|
|
- RoutesFiltered
|
|
|
|
from Birdwatcher. As the not exported routes can be very many
|
|
these are optional and can be loaded on demand using the
|
|
RoutesNotExported() API.
|
|
|
|
A route deduplication is applied.
|
|
*/
|
|
func (self *SingleTableBirdwatcher) fetchRequiredRoutes(neighborId string) (*api.RoutesResponse, error) {
|
|
// Allow only one concurrent request for this neighbor
|
|
// to our backend server.
|
|
self.routesFetchMutex.Lock(neighborId)
|
|
defer self.routesFetchMutex.Unlock(neighborId)
|
|
|
|
// Check if we have a cache hit
|
|
response := self.routesRequiredCache.Get(neighborId)
|
|
if response != nil {
|
|
return response, nil
|
|
}
|
|
|
|
// First: get routes received
|
|
apiStatus, receivedRoutes, err := self.fetchReceivedRoutes(neighborId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Second: get routes filtered
|
|
_, filteredRoutes, err := self.fetchFilteredRoutes(neighborId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Perform route deduplication
|
|
importedRoutes := api.Routes{}
|
|
if len(receivedRoutes) > 0 {
|
|
peer := receivedRoutes[0].Gateway
|
|
learntFrom := mustString(receivedRoutes[0].Details["learnt_from"], peer)
|
|
|
|
filteredRoutes = self.filterRoutesByPeerOrLearntFrom(filteredRoutes, peer, learntFrom)
|
|
importedRoutes = self.filterRoutesByDuplicates(receivedRoutes, filteredRoutes)
|
|
}
|
|
|
|
response = &api.RoutesResponse{
|
|
Api: *apiStatus,
|
|
Imported: importedRoutes,
|
|
Filtered: filteredRoutes,
|
|
}
|
|
|
|
// Cache result
|
|
self.routesRequiredCache.Set(neighborId, response)
|
|
|
|
return response, nil
|
|
}
|
|
|
|
|
|
// Get neighbors from protocols
|
|
func (self *SingleTableBirdwatcher) Neighbours() (*api.NeighboursResponse, error) {
|
|
// Check if we hit the cache
|
|
response := self.neighborsCache.Get()
|
|
if response != nil {
|
|
return response, nil
|
|
}
|
|
|
|
// Query birdwatcher
|
|
bird, err := self.client.GetJson("/protocols/bgp")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Use api status from first request
|
|
apiStatus, err := parseApiStatus(bird, self.config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse the neighbors
|
|
neighbours, err := parseNeighbours(bird, self.config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response = &api.NeighboursResponse{
|
|
Api: apiStatus,
|
|
Neighbours: neighbours,
|
|
}
|
|
|
|
// Cache result
|
|
self.neighborsCache.Set(response)
|
|
|
|
return response, nil // dereference for now
|
|
}
|
|
|
|
// Get filtered and exported routes
|
|
func (self *SingleTableBirdwatcher) Routes(neighbourId string) (*api.RoutesResponse, error) {
|
|
response := &api.RoutesResponse{}
|
|
|
|
// Fetch required routes first (received and filtered)
|
|
required, err := self.fetchRequiredRoutes(neighbourId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Optional: NoExport
|
|
_, notExported, err := self.fetchNotExportedRoutes(neighbourId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response.Api = required.Api
|
|
response.Imported = required.Imported
|
|
response.Filtered = required.Filtered
|
|
response.NotExported = notExported
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// Get all received routes
|
|
func (self *SingleTableBirdwatcher) RoutesReceived(neighborId string) (*api.RoutesResponse, error) {
|
|
response := &api.RoutesResponse{}
|
|
|
|
// Check if we hit the cache
|
|
cachedRoutes := self.routesRequiredCache.Get(neighborId)
|
|
if cachedRoutes != nil {
|
|
response.Api = cachedRoutes.Api
|
|
response.Imported = cachedRoutes.Imported
|
|
return response, nil
|
|
}
|
|
|
|
// Fetch required routes first (received and filtered)
|
|
// However: Store in separate cache for faster access
|
|
routes, err := self.fetchRequiredRoutes(neighborId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response.Api = routes.Api
|
|
response.Imported = routes.Imported
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// Get all filtered routes
|
|
func (self *SingleTableBirdwatcher) RoutesFiltered(neighborId string) (*api.RoutesResponse, error) {
|
|
response := &api.RoutesResponse{}
|
|
|
|
// Check if we hit the cache
|
|
cachedRoutes := self.routesRequiredCache.Get(neighborId)
|
|
if cachedRoutes != nil {
|
|
response.Api = cachedRoutes.Api
|
|
response.Filtered = cachedRoutes.Filtered
|
|
return response, nil
|
|
}
|
|
|
|
// Fetch required routes first (received and filtered)
|
|
// However: Store in separate cache for faster access
|
|
routes, err := self.fetchRequiredRoutes(neighborId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response.Api = routes.Api
|
|
response.Filtered = routes.Filtered
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// Get all not exported routes
|
|
func (self *SingleTableBirdwatcher) RoutesNotExported(neighborId string) (*api.RoutesResponse, error) {
|
|
// Check if we hit the cache
|
|
response := self.routesNotExportedCache.Get(neighborId)
|
|
if response != nil {
|
|
return response, nil
|
|
}
|
|
|
|
// Fetch not exported routes
|
|
apiStatus, routes, err := self.fetchNotExportedRoutes(neighborId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response = &api.RoutesResponse{
|
|
Api: *apiStatus,
|
|
NotExported: routes,
|
|
}
|
|
|
|
// Cache result
|
|
self.routesNotExportedCache.Set(neighborId, response)
|
|
|
|
return response, nil
|
|
}
|
|
|
|
func (self *SingleTableBirdwatcher) AllRoutes() (*api.RoutesResponse, error) {
|
|
// First fetch all routes from the master table
|
|
birdImported, err := self.client.GetJson("/routes/table/master")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Then fetch all filtered routes from the master table
|
|
birdFiltered, err := self.client.GetJson("/routes/table/master/filtered")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Use api status from second request
|
|
apiStatus, err := parseApiStatus(birdFiltered, self.config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response := &api.RoutesResponse{
|
|
Api: apiStatus,
|
|
}
|
|
|
|
// Parse the routes
|
|
imported := parseRoutesData(birdImported["routes"].([]interface{}), self.config)
|
|
// Sort routes for deterministic ordering
|
|
sort.Sort(imported)
|
|
response.Imported = imported
|
|
|
|
// Parse the routes
|
|
filtered := parseRoutesData(birdFiltered["routes"].([]interface{}), self.config)
|
|
// Sort routes for deterministic ordering
|
|
sort.Sort(filtered)
|
|
response.Filtered = filtered
|
|
|
|
return response, nil
|
|
}
|