mirror of
https://github.com/alice-lg/alice-lg.git
synced 2024-05-11 05:55:03 +00:00
282 lines
5.5 KiB
Go
282 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"github.com/ecix/alice-lg/backend/api"
|
|
"github.com/ecix/alice-lg/backend/sources"
|
|
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
STATE_INIT = iota
|
|
STATE_READY
|
|
STATE_UPDATING
|
|
STATE_ERROR
|
|
)
|
|
|
|
type RoutesStats struct {
|
|
Filtered int `json:"filtered"`
|
|
Imported int `json:"imported"`
|
|
}
|
|
|
|
type RouteServerStats struct {
|
|
Name string `json:"name"`
|
|
Routes RoutesStats `json:"routes"`
|
|
|
|
State string
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
type StoreStats struct {
|
|
TotalRoutes RoutesStats `json:"total_routes"`
|
|
RouteServers []RouteServerStats `json:"route_servers"`
|
|
}
|
|
|
|
// Write stats to the log
|
|
func (stats StoreStats) Log() {
|
|
log.Println("Routes store:")
|
|
|
|
log.Println(" Routes Imported:",
|
|
stats.TotalRoutes.Imported,
|
|
"Filtered:",
|
|
stats.TotalRoutes.Filtered)
|
|
log.Println(" Routeservers:")
|
|
|
|
for _, rs := range stats.RouteServers {
|
|
log.Println(" -", rs.Name)
|
|
log.Println(" State:", rs.State)
|
|
log.Println(" UpdatedAt:", rs.UpdatedAt)
|
|
log.Println(" Routes Imported:",
|
|
rs.Routes.Imported,
|
|
"Filtered:",
|
|
rs.Routes.Filtered)
|
|
}
|
|
}
|
|
|
|
type StoreStatus struct {
|
|
LastRefresh time.Time
|
|
LastError error
|
|
State int
|
|
}
|
|
|
|
type RoutesStore struct {
|
|
routesMap map[sources.Source]api.RoutesResponse
|
|
statusMap map[sources.Source]StoreStatus
|
|
configMap map[sources.Source]SourceConfig
|
|
}
|
|
|
|
func NewRoutesStore(config *Config) *RoutesStore {
|
|
|
|
// Build mapping based on source instances
|
|
routesMap := make(map[sources.Source]api.RoutesResponse)
|
|
statusMap := make(map[sources.Source]StoreStatus)
|
|
configMap := make(map[sources.Source]SourceConfig)
|
|
|
|
for _, source := range config.Sources {
|
|
instance := source.getInstance()
|
|
configMap[instance] = source
|
|
routesMap[instance] = api.RoutesResponse{}
|
|
statusMap[instance] = StoreStatus{
|
|
State: STATE_INIT,
|
|
}
|
|
}
|
|
|
|
store := &RoutesStore{
|
|
routesMap: routesMap,
|
|
statusMap: statusMap,
|
|
configMap: configMap,
|
|
}
|
|
return store
|
|
}
|
|
|
|
func (self *RoutesStore) Start() {
|
|
log.Println("Starting local routes store")
|
|
go self.init()
|
|
}
|
|
|
|
// Service initialization
|
|
func (self *RoutesStore) init() {
|
|
// Initial refresh
|
|
self.update()
|
|
|
|
// Initial stats
|
|
self.Stats().Log()
|
|
}
|
|
|
|
// Update all routes
|
|
func (self *RoutesStore) update() {
|
|
for source, _ := range self.routesMap {
|
|
// Get current update state
|
|
if self.statusMap[source].State == STATE_UPDATING {
|
|
continue // nothing to do here
|
|
}
|
|
|
|
// Set update state
|
|
self.statusMap[source] = StoreStatus{
|
|
State: STATE_UPDATING,
|
|
}
|
|
|
|
routes, err := source.AllRoutes()
|
|
if err != nil {
|
|
self.statusMap[source] = StoreStatus{
|
|
State: STATE_ERROR,
|
|
LastError: err,
|
|
LastRefresh: time.Now(),
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// Update data
|
|
self.routesMap[source] = routes
|
|
// Update state
|
|
self.statusMap[source] = StoreStatus{
|
|
LastRefresh: time.Now(),
|
|
State: STATE_READY,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper: stateToString
|
|
func stateToString(state int) string {
|
|
switch state {
|
|
case STATE_INIT:
|
|
return "INIT"
|
|
case STATE_READY:
|
|
return "READY"
|
|
case STATE_UPDATING:
|
|
return "UPDATING"
|
|
case STATE_ERROR:
|
|
return "ERROR"
|
|
}
|
|
return "INVALID"
|
|
}
|
|
|
|
// Calculate store insights
|
|
func (self *RoutesStore) Stats() StoreStats {
|
|
totalImported := 0
|
|
totalFiltered := 0
|
|
|
|
rsStats := []RouteServerStats{}
|
|
|
|
for source, routes := range self.routesMap {
|
|
status := self.statusMap[source]
|
|
|
|
totalImported += len(routes.Imported)
|
|
totalFiltered += len(routes.Filtered)
|
|
|
|
serverStats := RouteServerStats{
|
|
Name: self.configMap[source].Name,
|
|
|
|
Routes: RoutesStats{
|
|
Filtered: len(routes.Filtered),
|
|
Imported: len(routes.Imported),
|
|
},
|
|
|
|
State: stateToString(status.State),
|
|
UpdatedAt: status.LastRefresh,
|
|
}
|
|
|
|
rsStats = append(rsStats, serverStats)
|
|
}
|
|
|
|
// Make stats
|
|
storeStats := StoreStats{
|
|
TotalRoutes: RoutesStats{
|
|
Imported: totalImported,
|
|
Filtered: totalFiltered,
|
|
},
|
|
RouteServers: rsStats,
|
|
}
|
|
return storeStats
|
|
}
|
|
|
|
// Routes filter
|
|
func filterRoutes(
|
|
config SourceConfig,
|
|
routes []api.Route,
|
|
prefix string,
|
|
state string,
|
|
) []api.LookupRoute {
|
|
|
|
results := []api.LookupRoute{}
|
|
|
|
for _, route := range routes {
|
|
// Naiive filtering:
|
|
if strings.HasPrefix(route.Network, prefix) {
|
|
lookup := api.LookupRoute{
|
|
Id: route.Id,
|
|
NeighbourId: route.NeighbourId,
|
|
|
|
Routeserver: api.Routeserver{
|
|
Id: config.Id,
|
|
Name: config.Name,
|
|
},
|
|
|
|
State: state,
|
|
|
|
Network: route.Network,
|
|
Interface: route.Interface,
|
|
Gateway: route.Gateway,
|
|
Metric: route.Metric,
|
|
Bgp: route.Bgp,
|
|
Age: route.Age,
|
|
Type: route.Type,
|
|
}
|
|
results = append(results, lookup)
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
|
|
// Single RS lookup
|
|
func (self *RoutesStore) lookupRs(
|
|
source sources.Source,
|
|
prefix string,
|
|
) chan []api.LookupRoute {
|
|
|
|
response := make(chan []api.LookupRoute)
|
|
config := self.configMap[source]
|
|
routes := self.routesMap[source]
|
|
|
|
go func() {
|
|
filtered := filterRoutes(
|
|
config,
|
|
routes.Filtered,
|
|
prefix,
|
|
"filtered")
|
|
imported := filterRoutes(
|
|
config,
|
|
routes.Imported,
|
|
prefix,
|
|
"imported")
|
|
|
|
result := append(filtered, imported...)
|
|
|
|
response <- result
|
|
}()
|
|
|
|
return response
|
|
}
|
|
|
|
func (self *RoutesStore) Lookup(prefix string) []api.LookupRoute {
|
|
result := []api.LookupRoute{}
|
|
responses := []chan []api.LookupRoute{}
|
|
|
|
// Dispatch
|
|
for source, _ := range self.routesMap {
|
|
res := self.lookupRs(source, prefix)
|
|
responses = append(responses, res)
|
|
}
|
|
|
|
// Collect
|
|
for _, response := range responses {
|
|
routes := <-response
|
|
result = append(result, routes...)
|
|
}
|
|
|
|
return result
|
|
}
|