Implemented support to query the live neighbor status from the store
via uncached queries.
This commit is contained in:
parent
5175ccfe4c
commit
8f19408bad
@ -52,3 +52,30 @@ func (self *NeighboursResponse) CacheTtl() time.Duration {
|
||||
}
|
||||
|
||||
type NeighboursLookupResults map[string]Neighbours
|
||||
|
||||
|
||||
type NeighboursStatus []*NeighbourStatus
|
||||
|
||||
type NeighbourStatus struct {
|
||||
Id string `json:"id"`
|
||||
State string `json:"state"`
|
||||
Since time.Duration `json:"uptime"`
|
||||
}
|
||||
|
||||
// Implement sorting interface for status
|
||||
func (neighbours NeighboursStatus) Len() int {
|
||||
return len(neighbours)
|
||||
}
|
||||
|
||||
func (neighbours NeighboursStatus) Less(i, j int) bool {
|
||||
return neighbours[i].Id < neighbours[j].Id
|
||||
}
|
||||
|
||||
func (neighbours NeighboursStatus) Swap(i, j int) {
|
||||
neighbours[i], neighbours[j] = neighbours[j], neighbours[i]
|
||||
}
|
||||
|
||||
type NeighboursStatusResponse struct {
|
||||
Api ApiStatus `json:"api"`
|
||||
Neighbours NeighboursStatus `json:"neighbours"`
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ type ServerConfig struct {
|
||||
NeighboursStoreRefreshInterval int `ini:"neighbours_store_refresh_interval"`
|
||||
RoutesStoreRefreshInterval int `ini:"routes_store_refresh_interval"`
|
||||
Asn int `ini:"asn"`
|
||||
EnableNeighborsStatusRefresh bool `ini:"enable_neighbors_status_refresh"`
|
||||
}
|
||||
|
||||
type HousekeepingConfig struct {
|
||||
|
@ -15,10 +15,11 @@ var REGEX_MATCH_ASLOOKUP = regexp.MustCompile(`(?i)^AS(\d+)`)
|
||||
type NeighboursIndex map[string]*api.Neighbour
|
||||
|
||||
type NeighboursStore struct {
|
||||
neighboursMap map[string]NeighboursIndex
|
||||
configMap map[string]*SourceConfig
|
||||
statusMap map[string]StoreStatus
|
||||
refreshInterval time.Duration
|
||||
neighboursMap map[string]NeighboursIndex
|
||||
configMap map[string]*SourceConfig
|
||||
statusMap map[string]StoreStatus
|
||||
refreshInterval time.Duration
|
||||
refreshNeighborStatus bool
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
@ -48,11 +49,14 @@ func NewNeighboursStore(config *Config) *NeighboursStore {
|
||||
refreshInterval = time.Duration(5) * time.Minute
|
||||
}
|
||||
|
||||
refreshNeighborStatus := config.Server.EnableNeighborsStatusRefresh
|
||||
|
||||
store := &NeighboursStore{
|
||||
neighboursMap: neighboursMap,
|
||||
statusMap: statusMap,
|
||||
configMap: configMap,
|
||||
refreshInterval: refreshInterval,
|
||||
refreshNeighborStatus: refreshNeighborStatus,
|
||||
}
|
||||
return store
|
||||
}
|
||||
@ -165,9 +169,32 @@ func (self *NeighboursStore) GetNeighborsAt(sourceId string) api.Neighbours {
|
||||
neighborsIdx := self.neighboursMap[sourceId]
|
||||
self.RUnlock()
|
||||
|
||||
var neighborsStatus map[string]api.NeighbourStatus
|
||||
if self.refreshNeighborStatus {
|
||||
sourceConfig := self.configMap[sourceId]
|
||||
source := sourceConfig.getInstance()
|
||||
|
||||
neighborsStatusData, err := source.NeighboursStatus()
|
||||
if err == nil {
|
||||
neighborsStatus = make(map[string]api.NeighbourStatus, len(neighborsStatusData.Neighbours))
|
||||
|
||||
for _, neighbor := range neighborsStatusData.Neighbours {
|
||||
neighborsStatus[neighbor.Id] = *neighbor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
neighbors := make(api.Neighbours, 0, len(neighborsIdx))
|
||||
|
||||
for _, neighbor := range neighborsIdx {
|
||||
if self.refreshNeighborStatus {
|
||||
if _, ok := neighborsStatus[neighbor.Id]; ok {
|
||||
self.Lock()
|
||||
neighbor.State = neighborsStatus[neighbor.Id].State
|
||||
self.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
neighbors = append(neighbors, neighbor)
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ClientResponse map[string]interface{}
|
||||
@ -22,8 +23,8 @@ func NewClient(api string) *Client {
|
||||
}
|
||||
|
||||
// Make API request, parse response and return map or error
|
||||
func (self *Client) GetJson(endpoint string) (ClientResponse, error) {
|
||||
res, err := http.Get(self.Api + endpoint)
|
||||
func (self *Client) Get(client *http.Client, url string) (ClientResponse, error) {
|
||||
res, err := client.Get(url)
|
||||
if err != nil {
|
||||
return ClientResponse{}, err
|
||||
}
|
||||
@ -44,3 +45,19 @@ func (self *Client) GetJson(endpoint string) (ClientResponse, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Make API request, parse response and return map or error
|
||||
func (self *Client) GetJson(endpoint string) (ClientResponse, error) {
|
||||
client := &http.Client{}
|
||||
|
||||
return self.Get(client, self.Api + endpoint)
|
||||
}
|
||||
|
||||
// Make API request, parse response and return map or error
|
||||
func (self *Client) GetJsonTimeout(timeout time.Duration, endpoint string) (ClientResponse, error) {
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
return self.Get(client, self.Api + endpoint)
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ type Config struct {
|
||||
ServerTimeExt string `ini:"servertime_ext"`
|
||||
ShowLastReboot bool `ini:"show_last_reboot"`
|
||||
|
||||
Type string `ini:"type"`
|
||||
PeerTablePrefix string `ini:"peer_table_prefix"`
|
||||
PipeProtocolPrefix string `ini:"pipe_protocol_prefix"`
|
||||
Type string `ini:"type"`
|
||||
PeerTablePrefix string `ini:"peer_table_prefix"`
|
||||
PipeProtocolPrefix string `ini:"pipe_protocol_prefix"`
|
||||
NeighborsRefreshTimeout int `ini:"neighbors_refresh_timeout"`
|
||||
}
|
||||
|
@ -200,6 +200,31 @@ func parseNeighbours(bird ClientResponse, config Config) (api.Neighbours, error)
|
||||
return neighbours, nil
|
||||
}
|
||||
|
||||
// Parse neighbours response
|
||||
func parseNeighboursShort(bird ClientResponse, config Config) (api.NeighboursStatus, error) {
|
||||
neighbours := api.NeighboursStatus{}
|
||||
protocols := bird["protocols"].(map[string]interface{})
|
||||
|
||||
// Iterate over protocols map:
|
||||
for protocolId, proto := range protocols {
|
||||
protocol := proto.(map[string]interface{})
|
||||
|
||||
uptime := parseRelativeServerTime(protocol["since"], config)
|
||||
|
||||
neighbour := &api.NeighbourStatus{
|
||||
Id: protocolId,
|
||||
State: mustString(protocol["state"], "unknown"),
|
||||
Since: uptime,
|
||||
}
|
||||
|
||||
neighbours = append(neighbours, neighbour)
|
||||
}
|
||||
|
||||
sort.Sort(neighbours)
|
||||
|
||||
return neighbours, nil
|
||||
}
|
||||
|
||||
// Parse route bgp info
|
||||
func parseRouteBgpInfo(data interface{}) api.BgpInfo {
|
||||
bgpData, ok := data.(map[string]interface{})
|
||||
|
@ -5,7 +5,9 @@ import (
|
||||
"github.com/alice-lg/alice-lg/backend/caches"
|
||||
"github.com/alice-lg/alice-lg/backend/sources"
|
||||
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Birdwatcher interface {
|
||||
@ -145,6 +147,47 @@ func (self *GenericBirdwatcher) filterRoutesByDuplicates(routes api.Routes, filt
|
||||
return routes
|
||||
}
|
||||
|
||||
func (self *GenericBirdwatcher) filterRoutesByNeighborId(routes api.Routes, neighborId string) api.Routes {
|
||||
result_routes := make(api.Routes, 0, len(routes))
|
||||
|
||||
// Choose routes with next_hop == gateway of this neighbour
|
||||
for _, route := range routes {
|
||||
if route.Details["from_protocol"] == neighborId {
|
||||
result_routes = append(result_routes, route)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort routes for deterministic ordering
|
||||
sort.Sort(result_routes)
|
||||
routes = result_routes
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
func (self *GenericBirdwatcher) fetchProtocolsShort() (*api.ApiStatus, map[string]interface{}, error) {
|
||||
// Query birdwatcher
|
||||
timeout := 2 * time.Second
|
||||
if self.config.NeighborsRefreshTimeout > 0 {
|
||||
timeout = time.Duration(self.config.NeighborsRefreshTimeout) * time.Second
|
||||
}
|
||||
bird, err := self.client.GetJsonTimeout(timeout, "/protocols/short?uncached=true")
|
||||
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
|
||||
}
|
||||
|
||||
if _, ok := bird["protocols"]; !ok {
|
||||
return nil, nil, fmt.Errorf("Failed to fetch protocols")
|
||||
}
|
||||
|
||||
return &apiStatus, bird, nil
|
||||
}
|
||||
|
||||
func (self *GenericBirdwatcher) ExpireCaches() int {
|
||||
count := self.routesRequiredCache.Expire()
|
||||
count += self.routesNotExportedCache.Expire()
|
||||
@ -179,6 +222,28 @@ func (self *GenericBirdwatcher) Status() (*api.StatusResponse, error) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Get live neighbor status
|
||||
func (self *GenericBirdwatcher) NeighboursStatus() (*api.NeighboursStatusResponse, error) {
|
||||
// Query birdwatcher
|
||||
apiStatus, birdProtocols, err := self.fetchProtocolsShort()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the neighbors short
|
||||
neighbours, err := parseNeighboursShort(birdProtocols, self.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &api.NeighboursStatusResponse{
|
||||
Api: *apiStatus,
|
||||
Neighbours: neighbours,
|
||||
}
|
||||
|
||||
return response, nil // dereference for now
|
||||
}
|
||||
|
||||
// Make routes lookup
|
||||
func (self *GenericBirdwatcher) LookupPrefix(prefix string) (*api.RoutesLookupResponse, error) {
|
||||
// Get RS info
|
||||
|
@ -8,6 +8,7 @@ type Source interface {
|
||||
ExpireCaches() int
|
||||
Status() (*api.StatusResponse, error)
|
||||
Neighbours() (*api.NeighboursResponse, error)
|
||||
NeighboursStatus() (*api.NeighboursStatusResponse, error)
|
||||
Routes(neighbourId string) (*api.RoutesResponse, error)
|
||||
RoutesReceived(neighbourId string) (*api.RoutesResponse, error)
|
||||
RoutesFiltered(neighbourId string) (*api.RoutesResponse, error)
|
||||
|
@ -5,8 +5,10 @@
|
||||
[server]
|
||||
# configures the built-in webserver and provides global application settings
|
||||
listen_http = 127.0.0.1:7340
|
||||
enable_prefix_lookup = true
|
||||
# enable the prefix-lookup endpoint / the global search feature
|
||||
enable_prefix_lookup = true
|
||||
# Try to refresh the neighbor status on every request to /neighbors
|
||||
enable_neighbors_status_refresh = false
|
||||
asn = 9033
|
||||
# this ASN is used as a fallback value in the RPKI feature and for route
|
||||
# filtering evaluation with large BGP communities
|
||||
@ -150,6 +152,8 @@ api = http://rs1.example.com:29184/
|
||||
type = multi_table
|
||||
peer_table_prefix = T
|
||||
pipe_protocol_prefix = M
|
||||
# Timeout in seconds to wait for the status data (only required if enable_neighbors_status_refresh is true)
|
||||
neighbors_refresh_timeout = 2
|
||||
|
||||
# Optional:
|
||||
show_last_reboot = true
|
||||
@ -164,6 +168,8 @@ api = http://rs1.example.com:29186/
|
||||
type = multi_table
|
||||
peer_table_prefix = T
|
||||
pipe_protocol_prefix = M
|
||||
# Timeout in seconds to wait for the status data (only required if enable_neighbors_status_refresh is true)
|
||||
neighbors_refresh_timeout = 2
|
||||
|
||||
# Optional: Examples for time format
|
||||
# Please see https://golang.org/pkg/time/#pkg-constants for an
|
||||
|
Loading…
x
Reference in New Issue
Block a user