1
0
mirror of https://github.com/alice-lg/alice-lg.git synced 2024-05-11 05:55:03 +00:00
alice-lg-alice-lg/pkg/backend/routes_store.go

398 lines
8.1 KiB
Go
Raw Normal View History

2021-03-22 16:25:47 +01:00
package backend
2017-06-23 11:12:24 +02:00
import (
"log"
2017-06-23 16:11:47 +02:00
"strings"
2017-07-14 14:18:47 +02:00
"sync"
2017-06-23 11:12:24 +02:00
"time"
2017-07-14 14:18:47 +02:00
2021-03-22 17:35:20 +01:00
"github.com/alice-lg/alice-lg/pkg/api"
2017-06-23 11:12:24 +02:00
)
2021-03-22 17:35:20 +01:00
// The RoutesStore holds a mapping of routes,
// status and configs and will be queried instead
// of a backend by the API
2017-06-23 11:12:24 +02:00
type RoutesStore struct {
2018-12-09 17:31:02 +01:00
routesMap map[string]*api.RoutesResponse
statusMap map[string]StoreStatus
configMap map[string]*SourceConfig
2018-09-25 21:26:11 +02:00
2018-07-07 11:51:13 +02:00
refreshInterval time.Duration
2018-09-25 21:26:11 +02:00
lastRefresh time.Time
2017-07-14 14:18:47 +02:00
2018-06-26 10:51:11 +02:00
sync.RWMutex
2017-06-23 11:12:24 +02:00
}
2021-03-22 17:35:20 +01:00
// NewRoutesStore makes a new store instance
// with a config.
2017-06-23 11:12:24 +02:00
func NewRoutesStore(config *Config) *RoutesStore {
// Build mapping based on source instances
2018-12-09 17:31:02 +01:00
routesMap := make(map[string]*api.RoutesResponse)
statusMap := make(map[string]StoreStatus)
configMap := make(map[string]*SourceConfig)
2017-06-23 14:25:40 +02:00
2017-06-23 11:12:24 +02:00
for _, source := range config.Sources {
2017-06-23 18:01:49 +02:00
id := source.Id
configMap[id] = source
2018-07-11 18:25:42 +02:00
routesMap[id] = &api.RoutesResponse{}
2017-06-23 18:01:49 +02:00
statusMap[id] = StoreStatus{
2017-06-23 12:36:32 +02:00
State: STATE_INIT,
}
2017-06-23 11:12:24 +02:00
}
2018-07-07 11:51:13 +02:00
// Set refresh interval as duration, fall back to
// five minutes if no interval is set.
refreshInterval := time.Duration(
config.Server.RoutesStoreRefreshInterval) * time.Minute
if refreshInterval == 0 {
refreshInterval = time.Duration(5) * time.Minute
}
2017-06-23 11:12:24 +02:00
store := &RoutesStore{
routesMap: routesMap,
statusMap: statusMap,
configMap: configMap,
2018-07-07 11:51:13 +02:00
refreshInterval: refreshInterval,
2017-06-23 11:12:24 +02:00
}
return store
}
2021-03-22 17:35:20 +01:00
// Start starts the routes store
func (rs *RoutesStore) Start() {
2017-06-23 11:12:24 +02:00
log.Println("Starting local routes store")
2021-03-22 17:35:20 +01:00
log.Println("Routes Store refresh interval set to:", rs.refreshInterval)
if err := rs.init(); err != nil {
log.Fatal(err)
}
2017-06-23 14:25:40 +02:00
}
2017-06-23 11:12:24 +02:00
2017-06-23 14:25:40 +02:00
// Service initialization
2021-03-22 17:35:20 +01:00
func (rs *RoutesStore) init() error {
2017-06-23 12:36:32 +02:00
// Initial refresh
2021-03-22 17:35:20 +01:00
rs.update()
2017-06-23 14:25:40 +02:00
// Initial stats
2021-03-22 17:35:20 +01:00
rs.Stats().Log()
2017-06-23 16:25:21 +02:00
// Periodically update store
for {
2021-03-22 17:35:20 +01:00
time.Sleep(rs.refreshInterval)
rs.update()
2017-06-23 16:25:21 +02:00
}
2017-06-23 11:12:24 +02:00
}
// Update all routes
2021-03-22 17:35:20 +01:00
func (rs *RoutesStore) update() {
2018-10-01 12:13:40 +02:00
successCount := 0
errorCount := 0
2018-10-01 12:02:14 +02:00
t0 := time.Now()
2021-03-22 17:35:20 +01:00
for sourceID := range rs.routesMap {
sourceConfig := rs.configMap[sourceID]
2018-10-01 12:02:14 +02:00
source := sourceConfig.getInstance()
2017-06-23 18:01:49 +02:00
2017-06-23 12:36:32 +02:00
// Get current update state
2021-03-22 17:35:20 +01:00
if rs.statusMap[sourceID].State == STATE_UPDATING {
2017-06-23 12:36:32 +02:00
continue // nothing to do here
}
2018-07-13 11:38:20 +02:00
// Set update state
2021-03-22 17:35:20 +01:00
rs.Lock()
rs.statusMap[sourceID] = StoreStatus{
2017-06-23 12:36:32 +02:00
State: STATE_UPDATING,
}
2021-03-22 17:35:20 +01:00
rs.Unlock()
2017-06-23 12:36:32 +02:00
2017-06-23 11:12:24 +02:00
routes, err := source.AllRoutes()
if err != nil {
2018-10-01 12:02:14 +02:00
log.Println(
"Refreshing the routes store failed for:", sourceConfig.Name,
"(", sourceConfig.Id, ")",
"with:", err,
"- NEXT STATE: ERROR",
)
2021-03-22 17:35:20 +01:00
rs.Lock()
rs.statusMap[sourceID] = StoreStatus{
2017-06-23 12:36:32 +02:00
State: STATE_ERROR,
LastError: err,
LastRefresh: time.Now(),
}
2021-03-22 17:35:20 +01:00
rs.Unlock()
2018-07-13 11:41:00 +02:00
2018-10-01 12:13:40 +02:00
errorCount++
2017-06-23 11:12:24 +02:00
continue
}
2017-06-23 12:36:32 +02:00
2021-03-22 17:35:20 +01:00
rs.Lock()
2017-06-23 12:36:32 +02:00
// Update data
2021-03-22 17:35:20 +01:00
rs.routesMap[sourceID] = routes
2017-06-23 12:36:32 +02:00
// Update state
2021-03-22 17:35:20 +01:00
rs.statusMap[sourceID] = StoreStatus{
2017-06-23 12:36:32 +02:00
LastRefresh: time.Now(),
State: STATE_READY,
}
2021-03-22 17:35:20 +01:00
rs.lastRefresh = time.Now().UTC()
rs.Unlock()
2018-10-01 12:13:40 +02:00
successCount++
2017-06-23 11:12:24 +02:00
}
2018-10-01 12:02:14 +02:00
refreshDuration := time.Since(t0)
log.Println(
2018-10-01 12:13:40 +02:00
"Refreshed routes store for", successCount, "of", successCount+errorCount,
"sources with", errorCount, "error(s) in", refreshDuration,
2018-10-01 12:02:14 +02:00
)
2017-06-23 11:12:24 +02:00
}
2021-03-22 17:35:20 +01:00
// Stats calculates some store insights
func (rs *RoutesStore) Stats() RoutesStoreStats {
2017-06-23 12:55:08 +02:00
totalImported := 0
totalFiltered := 0
2017-06-23 17:40:19 +02:00
rsStats := []RouteServerRoutesStats{}
2017-06-23 12:55:08 +02:00
2021-03-22 17:35:20 +01:00
rs.RLock()
for sourceID, routes := range rs.routesMap {
status := rs.statusMap[sourceID]
2017-06-23 12:55:08 +02:00
totalImported += len(routes.Imported)
totalFiltered += len(routes.Filtered)
2017-06-23 17:40:19 +02:00
serverStats := RouteServerRoutesStats{
2021-03-22 17:35:20 +01:00
Name: rs.configMap[sourceID].Name,
2017-06-23 14:25:40 +02:00
2017-06-23 12:55:08 +02:00
Routes: RoutesStats{
Filtered: len(routes.Filtered),
Imported: len(routes.Imported),
},
State: stateToString(status.State),
UpdatedAt: status.LastRefresh,
}
rsStats = append(rsStats, serverStats)
}
2021-03-22 17:35:20 +01:00
rs.RUnlock()
2017-06-23 12:55:08 +02:00
// Make stats
2017-06-23 16:25:21 +02:00
storeStats := RoutesStoreStats{
2017-06-23 12:55:08 +02:00
TotalRoutes: RoutesStats{
Imported: totalImported,
Filtered: totalFiltered,
},
RouteServers: rsStats,
}
return storeStats
}
2021-03-22 17:35:20 +01:00
// CachedAt provides a cache status
func (rs *RoutesStore) CachedAt() time.Time {
return rs.lastRefresh
2018-09-25 21:26:11 +02:00
}
2021-03-22 17:35:20 +01:00
// CacheTTL returns the TTL time
func (rs *RoutesStore) CacheTTL() time.Time {
return rs.lastRefresh.Add(rs.refreshInterval)
2018-09-25 21:26:11 +02:00
}
2017-06-30 14:15:43 +02:00
// Lookup routes transform
2018-07-11 14:07:26 +02:00
func routeToLookupRoute(
source *SourceConfig,
state string,
route *api.Route,
) *api.LookupRoute {
2017-06-30 14:15:43 +02:00
// Get neighbour
neighbour := AliceNeighboursStore.GetNeighbourAt(source.Id, route.NeighbourId)
// Make route
2018-07-06 17:04:09 +02:00
lookup := &api.LookupRoute{
2017-06-30 14:15:43 +02:00
Id: route.Id,
NeighbourId: route.NeighbourId,
Neighbour: neighbour,
Routeserver: api.Routeserver{
Id: source.Id,
Name: source.Name,
},
State: state,
Network: route.Network,
Interface: route.Interface,
Gateway: route.Gateway,
Metric: route.Metric,
Bgp: route.Bgp,
Age: route.Age,
Type: route.Type,
2018-08-02 14:30:40 +02:00
Primary: route.Primary,
2017-06-30 14:15:43 +02:00
}
return lookup
}
2017-06-23 16:11:47 +02:00
// Routes filter
2017-06-30 14:15:43 +02:00
func filterRoutesByPrefix(
2018-07-11 14:07:26 +02:00
source *SourceConfig,
2018-07-07 11:35:34 +02:00
routes api.Routes,
2017-06-23 16:11:47 +02:00
prefix string,
state string,
2018-07-07 11:35:34 +02:00
) api.LookupRoutes {
results := api.LookupRoutes{}
2017-06-23 16:11:47 +02:00
for _, route := range routes {
// Naiive filtering:
if strings.HasPrefix(strings.ToLower(route.Network), prefix) {
2017-06-30 14:15:43 +02:00
lookup := routeToLookupRoute(source, state, route)
2017-06-23 16:11:47 +02:00
results = append(results, lookup)
}
}
return results
}
2017-06-30 14:15:43 +02:00
func filterRoutesByNeighbourIds(
2018-07-11 14:07:26 +02:00
source *SourceConfig,
2018-07-07 11:35:34 +02:00
routes api.Routes,
2017-06-30 14:15:43 +02:00
neighbourIds []string,
state string,
2018-07-07 11:35:34 +02:00
) api.LookupRoutes {
2017-06-30 14:15:43 +02:00
2018-07-07 11:35:34 +02:00
results := api.LookupRoutes{}
2017-06-30 14:15:43 +02:00
for _, route := range routes {
// Filtering:
if MemberOf(neighbourIds, route.NeighbourId) == true {
lookup := routeToLookupRoute(source, state, route)
results = append(results, lookup)
}
}
return results
2017-06-23 17:40:19 +02:00
}
2021-03-22 17:35:20 +01:00
// LookupNeighboursPrefixesAt performs a single route server
// routes lookup by neighbor id
func (rs *RoutesStore) LookupNeighboursPrefixesAt(
sourceID string,
2017-06-30 14:15:43 +02:00
neighbourIds []string,
2018-07-07 11:35:34 +02:00
) chan api.LookupRoutes {
response := make(chan api.LookupRoutes)
2017-06-30 11:12:15 +02:00
2017-06-30 14:15:43 +02:00
go func() {
2021-03-22 17:35:20 +01:00
rs.RLock()
source := rs.configMap[sourceID]
routes := rs.routesMap[sourceID]
rs.RUnlock()
2017-06-30 14:15:43 +02:00
filtered := filterRoutesByNeighbourIds(
source,
routes.Filtered,
neighbourIds,
"filtered")
imported := filterRoutesByNeighbourIds(
source,
routes.Imported,
neighbourIds,
"imported")
2018-07-07 11:45:34 +02:00
var result api.LookupRoutes
2017-06-30 14:15:43 +02:00
result = append(filtered, imported...)
response <- result
}()
return response
2017-06-30 11:12:15 +02:00
}
2021-03-22 17:35:20 +01:00
// LookupPrefixAt performs a single RS lookup
func (rs *RoutesStore) LookupPrefixAt(
sourceID string,
2017-06-23 16:11:47 +02:00
prefix string,
2018-07-07 11:35:34 +02:00
) chan api.LookupRoutes {
2017-06-23 16:11:47 +02:00
2018-07-07 11:35:34 +02:00
response := make(chan api.LookupRoutes)
2017-06-23 16:11:47 +02:00
go func() {
2021-03-22 17:35:20 +01:00
rs.RLock()
config := rs.configMap[sourceID]
routes := rs.routesMap[sourceID]
rs.RUnlock()
2017-06-23 17:40:19 +02:00
2017-06-30 14:15:43 +02:00
filtered := filterRoutesByPrefix(
2017-06-23 16:11:47 +02:00
config,
routes.Filtered,
prefix,
"filtered")
2017-06-30 14:15:43 +02:00
imported := filterRoutesByPrefix(
2017-06-23 16:11:47 +02:00
config,
routes.Imported,
prefix,
"imported")
2018-07-07 11:45:34 +02:00
var result api.LookupRoutes
2017-06-30 14:15:43 +02:00
result = append(filtered, imported...)
2017-06-23 16:11:47 +02:00
response <- result
}()
return response
}
2021-03-22 17:35:20 +01:00
// LookupPrefix performs a lookup over all route servers
func (rs *RoutesStore) LookupPrefix(prefix string) api.LookupRoutes {
2018-07-07 11:35:34 +02:00
result := api.LookupRoutes{}
responses := []chan api.LookupRoutes{}
2017-06-23 16:11:47 +02:00
// Normalize prefix to lower case
prefix = strings.ToLower(prefix)
2017-06-23 16:11:47 +02:00
// Dispatch
2021-03-22 17:35:20 +01:00
rs.RLock()
for sourceID := range rs.routesMap {
res := rs.LookupPrefixAt(sourceID, prefix)
2017-06-23 16:11:47 +02:00
responses = append(responses, res)
}
2021-03-22 17:35:20 +01:00
rs.RUnlock()
2017-06-23 16:11:47 +02:00
// Collect
for _, response := range responses {
routes := <-response
result = append(result, routes...)
2017-06-30 14:15:43 +02:00
close(response)
}
return result
}
2021-03-22 17:35:20 +01:00
// LookupPrefixForNeighbours returns all routes for
// a set of neighbors.
func (rs *RoutesStore) LookupPrefixForNeighbours(
2017-06-30 14:15:43 +02:00
neighbours api.NeighboursLookupResults,
2018-07-07 11:35:34 +02:00
) api.LookupRoutes {
2017-06-30 14:15:43 +02:00
2018-07-07 11:35:34 +02:00
result := api.LookupRoutes{}
responses := []chan api.LookupRoutes{}
2017-06-30 14:15:43 +02:00
// Dispatch
2021-03-22 17:35:20 +01:00
for sourceID, locals := range neighbours {
2017-06-30 14:15:43 +02:00
lookupNeighbourIds := []string{}
for _, n := range locals {
lookupNeighbourIds = append(lookupNeighbourIds, n.Id)
}
2021-03-22 17:35:20 +01:00
res := rs.LookupNeighboursPrefixesAt(sourceID, lookupNeighbourIds)
2017-06-30 14:15:43 +02:00
responses = append(responses, res)
}
// Collect
for _, response := range responses {
routes := <-response
result = append(result, routes...)
close(response)
2017-06-23 16:11:47 +02:00
}
2017-06-23 11:12:24 +02:00
return result
}