1
0
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

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
}