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 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"`
|
NeighboursStoreRefreshInterval int `ini:"neighbours_store_refresh_interval"`
|
||||||
RoutesStoreRefreshInterval int `ini:"routes_store_refresh_interval"`
|
RoutesStoreRefreshInterval int `ini:"routes_store_refresh_interval"`
|
||||||
Asn int `ini:"asn"`
|
Asn int `ini:"asn"`
|
||||||
|
EnableNeighborsStatusRefresh bool `ini:"enable_neighbors_status_refresh"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type HousekeepingConfig struct {
|
type HousekeepingConfig struct {
|
||||||
|
@ -15,10 +15,11 @@ var REGEX_MATCH_ASLOOKUP = regexp.MustCompile(`(?i)^AS(\d+)`)
|
|||||||
type NeighboursIndex map[string]*api.Neighbour
|
type NeighboursIndex map[string]*api.Neighbour
|
||||||
|
|
||||||
type NeighboursStore struct {
|
type NeighboursStore struct {
|
||||||
neighboursMap map[string]NeighboursIndex
|
neighboursMap map[string]NeighboursIndex
|
||||||
configMap map[string]*SourceConfig
|
configMap map[string]*SourceConfig
|
||||||
statusMap map[string]StoreStatus
|
statusMap map[string]StoreStatus
|
||||||
refreshInterval time.Duration
|
refreshInterval time.Duration
|
||||||
|
refreshNeighborStatus bool
|
||||||
|
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
@ -48,11 +49,14 @@ func NewNeighboursStore(config *Config) *NeighboursStore {
|
|||||||
refreshInterval = time.Duration(5) * time.Minute
|
refreshInterval = time.Duration(5) * time.Minute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshNeighborStatus := config.Server.EnableNeighborsStatusRefresh
|
||||||
|
|
||||||
store := &NeighboursStore{
|
store := &NeighboursStore{
|
||||||
neighboursMap: neighboursMap,
|
neighboursMap: neighboursMap,
|
||||||
statusMap: statusMap,
|
statusMap: statusMap,
|
||||||
configMap: configMap,
|
configMap: configMap,
|
||||||
refreshInterval: refreshInterval,
|
refreshInterval: refreshInterval,
|
||||||
|
refreshNeighborStatus: refreshNeighborStatus,
|
||||||
}
|
}
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
@ -165,9 +169,32 @@ func (self *NeighboursStore) GetNeighborsAt(sourceId string) api.Neighbours {
|
|||||||
neighborsIdx := self.neighboursMap[sourceId]
|
neighborsIdx := self.neighboursMap[sourceId]
|
||||||
self.RUnlock()
|
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))
|
neighbors := make(api.Neighbours, 0, len(neighborsIdx))
|
||||||
|
|
||||||
for _, neighbor := range 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)
|
neighbors = append(neighbors, neighbor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientResponse map[string]interface{}
|
type ClientResponse map[string]interface{}
|
||||||
@ -22,8 +23,8 @@ func NewClient(api string) *Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make API request, parse response and return map or error
|
// Make API request, parse response and return map or error
|
||||||
func (self *Client) GetJson(endpoint string) (ClientResponse, error) {
|
func (self *Client) Get(client *http.Client, url string) (ClientResponse, error) {
|
||||||
res, err := http.Get(self.Api + endpoint)
|
res, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ClientResponse{}, err
|
return ClientResponse{}, err
|
||||||
}
|
}
|
||||||
@ -44,3 +45,19 @@ func (self *Client) GetJson(endpoint string) (ClientResponse, error) {
|
|||||||
|
|
||||||
return result, nil
|
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"`
|
ServerTimeExt string `ini:"servertime_ext"`
|
||||||
ShowLastReboot bool `ini:"show_last_reboot"`
|
ShowLastReboot bool `ini:"show_last_reboot"`
|
||||||
|
|
||||||
Type string `ini:"type"`
|
Type string `ini:"type"`
|
||||||
PeerTablePrefix string `ini:"peer_table_prefix"`
|
PeerTablePrefix string `ini:"peer_table_prefix"`
|
||||||
PipeProtocolPrefix string `ini:"pipe_protocol_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
|
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
|
// Parse route bgp info
|
||||||
func parseRouteBgpInfo(data interface{}) api.BgpInfo {
|
func parseRouteBgpInfo(data interface{}) api.BgpInfo {
|
||||||
bgpData, ok := data.(map[string]interface{})
|
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/caches"
|
||||||
"github.com/alice-lg/alice-lg/backend/sources"
|
"github.com/alice-lg/alice-lg/backend/sources"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Birdwatcher interface {
|
type Birdwatcher interface {
|
||||||
@ -145,6 +147,47 @@ func (self *GenericBirdwatcher) filterRoutesByDuplicates(routes api.Routes, filt
|
|||||||
return routes
|
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 {
|
func (self *GenericBirdwatcher) ExpireCaches() int {
|
||||||
count := self.routesRequiredCache.Expire()
|
count := self.routesRequiredCache.Expire()
|
||||||
count += self.routesNotExportedCache.Expire()
|
count += self.routesNotExportedCache.Expire()
|
||||||
@ -179,6 +222,28 @@ func (self *GenericBirdwatcher) Status() (*api.StatusResponse, error) {
|
|||||||
return response, nil
|
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
|
// Make routes lookup
|
||||||
func (self *GenericBirdwatcher) LookupPrefix(prefix string) (*api.RoutesLookupResponse, error) {
|
func (self *GenericBirdwatcher) LookupPrefix(prefix string) (*api.RoutesLookupResponse, error) {
|
||||||
// Get RS info
|
// Get RS info
|
||||||
|
@ -8,6 +8,7 @@ type Source interface {
|
|||||||
ExpireCaches() int
|
ExpireCaches() int
|
||||||
Status() (*api.StatusResponse, error)
|
Status() (*api.StatusResponse, error)
|
||||||
Neighbours() (*api.NeighboursResponse, error)
|
Neighbours() (*api.NeighboursResponse, error)
|
||||||
|
NeighboursStatus() (*api.NeighboursStatusResponse, error)
|
||||||
Routes(neighbourId string) (*api.RoutesResponse, error)
|
Routes(neighbourId string) (*api.RoutesResponse, error)
|
||||||
RoutesReceived(neighbourId string) (*api.RoutesResponse, error)
|
RoutesReceived(neighbourId string) (*api.RoutesResponse, error)
|
||||||
RoutesFiltered(neighbourId string) (*api.RoutesResponse, error)
|
RoutesFiltered(neighbourId string) (*api.RoutesResponse, error)
|
||||||
|
@ -5,8 +5,10 @@
|
|||||||
[server]
|
[server]
|
||||||
# configures the built-in webserver and provides global application settings
|
# configures the built-in webserver and provides global application settings
|
||||||
listen_http = 127.0.0.1:7340
|
listen_http = 127.0.0.1:7340
|
||||||
enable_prefix_lookup = true
|
|
||||||
# enable the prefix-lookup endpoint / the global search feature
|
# 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
|
asn = 9033
|
||||||
# this ASN is used as a fallback value in the RPKI feature and for route
|
# this ASN is used as a fallback value in the RPKI feature and for route
|
||||||
# filtering evaluation with large BGP communities
|
# filtering evaluation with large BGP communities
|
||||||
@ -150,6 +152,8 @@ api = http://rs1.example.com:29184/
|
|||||||
type = multi_table
|
type = multi_table
|
||||||
peer_table_prefix = T
|
peer_table_prefix = T
|
||||||
pipe_protocol_prefix = M
|
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:
|
# Optional:
|
||||||
show_last_reboot = true
|
show_last_reboot = true
|
||||||
@ -164,6 +168,8 @@ api = http://rs1.example.com:29186/
|
|||||||
type = multi_table
|
type = multi_table
|
||||||
peer_table_prefix = T
|
peer_table_prefix = T
|
||||||
pipe_protocol_prefix = M
|
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
|
# Optional: Examples for time format
|
||||||
# Please see https://golang.org/pkg/time/#pkg-constants for an
|
# Please see https://golang.org/pkg/time/#pkg-constants for an
|
||||||
|
Loading…
x
Reference in New Issue
Block a user