From db9aaf8d5b95a0734f911fcf81aed9eb4d8bad6e Mon Sep 17 00:00:00 2001 From: Annika Hannig Date: Mon, 5 Jul 2021 19:34:05 +0200 Subject: [PATCH] added bgplgd as separate source type for now --- pkg/backend/config.go | 22 +- pkg/sources/openbgpd/bgplgd_source.go | 324 ++++++++++++++++++++++++++ 2 files changed, 341 insertions(+), 5 deletions(-) create mode 100644 pkg/sources/openbgpd/bgplgd_source.go diff --git a/pkg/backend/config.go b/pkg/backend/config.go index bcc0822..cdc53d9 100644 --- a/pkg/backend/config.go +++ b/pkg/backend/config.go @@ -153,7 +153,7 @@ type SourceConfig struct { Backend string Birdwatcher birdwatcher.Config GoBGP gobgp.Config - OpenBGPd openbgpd.Config + OpenBGPD openbgpd.Config // Source instance instance sources.Source @@ -742,7 +742,19 @@ func getSources(config *ini.File) ([]*SourceConfig, error) { CacheTTL: cacheTTL, } backendConfig.MapTo(&c) - config.OpenBGPd = c + config.OpenBGPD = c + + case SourceBackendOpenBGPDBgplgd: + // Get cache TTL from the config + cacheTTL := time.Second * time.Duration(backendConfig.Key("cache_ttl").MustInt(0)) + + c := openbgpd.Config{ + ID: config.ID, + Name: config.Name, + CacheTTL: cacheTTL, + } + backendConfig.MapTo(&c) + config.OpenBGPD = c } // Add to list of sources @@ -817,8 +829,6 @@ func (cfg *SourceConfig) getInstance() sources.Source { return cfg.instance } - fmt.Println("GET INSTANCE: souirce type", cfg.Type) - var instance sources.Source switch cfg.Backend { case SourceBackendBirdwatcher: @@ -826,7 +836,9 @@ func (cfg *SourceConfig) getInstance() sources.Source { case SourceBackendGoBGP: instance = gobgp.NewGoBGP(cfg.GoBGP) case SourceBackendOpenBGPDStateServer: - instance = openbgpd.NewStateServerSource(&cfg.OpenBGPd) + instance = openbgpd.NewStateServerSource(&cfg.OpenBGPD) + case SourceBackendOpenBGPDBgplgd: + instance = openbgpd.NewBgplgdSource(&cfg.OpenBGPD) } cfg.instance = instance diff --git a/pkg/sources/openbgpd/bgplgd_source.go b/pkg/sources/openbgpd/bgplgd_source.go new file mode 100644 index 0000000..0a3eaef --- /dev/null +++ b/pkg/sources/openbgpd/bgplgd_source.go @@ -0,0 +1,324 @@ +package openbgpd + +import ( + "context" + "net/http" + "time" + + "github.com/alice-lg/alice-lg/pkg/api" + "github.com/alice-lg/alice-lg/pkg/caches" + "github.com/alice-lg/alice-lg/pkg/decoders" +) + +const ( + // BgplgdSourceVersion is currently fixed at 1.0 + BgplgdSourceVersion = "1.0" +) + +// BgplgdSource implements a source for Alice, consuming +// the openbgp bgplgd. +type BgplgdSource struct { + // cfg is the source configuration retrieved + // from the alice config file. + cfg *Config + + // Store the neighbor responses from the server here + neighborsCache *caches.NeighborsCache + + // Store the routes responses from the server + // here identified by neighborID + routesReceivedCache *caches.RoutesCache +} + +// NewBgplgdSource creates a new source instance with a configuration. +func NewBgplgdSource(cfg *Config) *BgplgdSource { + cacheDisabled := cfg.CacheTTL == 0 + + // Initialize caches + nc := caches.NewNeighborsCache(cacheDisabled) + rrc := caches.NewRoutesCache(cacheDisabled, 128) // configure this? + + return &BgplgdSource{ + cfg: cfg, + neighborsCache: nc, + routesReceivedCache: rrc, + } +} + +// ExpireCaches ... will flush the cache. +func (src *BgplgdSource) ExpireCaches() int { + totalExpired := src.routesReceivedCache.Expire() + return totalExpired +} + +// Requests +// ======== + +// ShowNeighborsRequest makes an all neighbors request +func (src *BgplgdSource) ShowNeighborsRequest(ctx context.Context) (*http.Request, error) { + url := src.cfg.APIURL("/neighbors") + return http.NewRequestWithContext(ctx, http.MethodGet, url, nil) +} + +// ShowNeighborsSummaryRequest builds an neighbors status request +func (src *BgplgdSource) ShowNeighborsSummaryRequest( + ctx context.Context, +) (*http.Request, error) { + url := src.cfg.APIURL("/summary") + return http.NewRequestWithContext(ctx, http.MethodGet, url, nil) +} + +// ShowNeighborRIBRequest retrives the routes accepted from the neighbor +// identified by bgp-id. +func (src *BgplgdSource) ShowNeighborRIBRequest( + ctx context.Context, + neighborID string, +) (*http.Request, error) { + url := src.cfg.APIURL("/rib?neighbor=%s", neighborID) + return http.NewRequestWithContext(ctx, http.MethodGet, url, nil) +} + +// ShowRIBRequest makes a request for retrieving all routes imported +// from all peers +func (src *BgplgdSource) ShowRIBRequest(ctx context.Context) (*http.Request, error) { + url := src.cfg.APIURL("/rib") + return http.NewRequestWithContext(ctx, http.MethodGet, url, nil) +} + +// Datasource +// ========== + +// makeCacheStatus will create a new api status with cache infos +func (src *BgplgdSource) makeCacheStatus() api.ApiStatus { + return api.ApiStatus{ + CacheStatus: api.CacheStatus{ + CachedAt: time.Now().UTC(), + }, + Version: BgplgdSourceVersion, + ResultFromCache: false, + Ttl: time.Now().UTC().Add(src.cfg.CacheTTL), + } +} + +// Status returns an API status response. In our case +// this is pretty much only that the service is available. +func (src *BgplgdSource) Status() (*api.StatusResponse, error) { + // Make API request and read response. We do not cache the result. + response := &api.StatusResponse{ + Api: src.makeCacheStatus(), + Status: api.Status{ + Version: "openbgpd", + Message: "openbgpd up and running", + }, + } + return response, nil +} + +// Neighbours retrievs a full list of all neighbors +func (src *BgplgdSource) Neighbours() (*api.NeighboursResponse, error) { + // Query cache and see if we have a hit + response := src.neighborsCache.Get() + if response != nil { + response.Api.ResultFromCache = true + return response, nil + } + + // Make API request and read response + req, err := src.ShowNeighborsRequest(context.Background()) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + body, err := decoders.ReadJSONResponse(res) + if err != nil { + return nil, err + } + + nb, err := decodeNeighbors(body) + if err != nil { + return nil, err + } + // Set route server id (sourceID) for all neighbors + for _, n := range nb { + n.RouteServerId = src.cfg.ID + } + response = &api.NeighboursResponse{ + Api: src.makeCacheStatus(), + Neighbours: nb, + } + src.neighborsCache.Set(response) + + return response, nil +} + +// NeighboursStatus retrives the status summary +// for all neightbors +func (src *BgplgdSource) NeighboursStatus() (*api.NeighboursStatusResponse, error) { + // Make API request and read response + req, err := src.ShowNeighborsSummaryRequest(context.Background()) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // Read and decode response + body, err := decoders.ReadJSONResponse(res) + if err != nil { + return nil, err + } + + nb, err := decodeNeighborsStatus(body) + if err != nil { + return nil, err + } + + response := &api.NeighboursStatusResponse{ + Api: src.makeCacheStatus(), + Neighbours: nb, + } + return response, nil +} + +// Routes retrieves the routes for a specific neighbor +// identified by ID. +func (src *BgplgdSource) Routes(neighborID string) (*api.RoutesResponse, error) { + response := src.routesReceivedCache.Get(neighborID) + if response != nil { + response.Api.ResultFromCache = true + return response, nil + } + + // Query RIB for routes received + req, err := src.ShowNeighborRIBRequest(context.Background(), neighborID) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // Read and decode response + body, err := decoders.ReadJSONResponse(res) + if err != nil { + return nil, err + } + + recv, err := decodeRoutes(body) + if err != nil { + return nil, err + } + + response = &api.RoutesResponse{ + Api: src.makeCacheStatus(), + Imported: recv, + NotExported: api.Routes{}, + Filtered: api.Routes{}, + } + src.routesReceivedCache.Set(neighborID, response) + + return response, nil +} + +// RoutesReceived returns the routes exported by the neighbor. +func (src *BgplgdSource) RoutesReceived(neighborID string) (*api.RoutesResponse, error) { + response := src.routesReceivedCache.Get(neighborID) + if response != nil { + response.Api.ResultFromCache = true + return response, nil + } + + // Query RIB for routes received + req, err := src.ShowNeighborRIBRequest(context.Background(), neighborID) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // Read and decode response + body, err := decoders.ReadJSONResponse(res) + if err != nil { + return nil, err + } + + recv, err := decodeRoutes(body) + if err != nil { + return nil, err + } + + response = &api.RoutesResponse{ + Api: src.makeCacheStatus(), + Imported: recv, + NotExported: api.Routes{}, + Filtered: api.Routes{}, + } + src.routesReceivedCache.Set(neighborID, response) + + return response, nil +} + +// RoutesFiltered retrieves the routes filtered / not valid +func (src *BgplgdSource) RoutesFiltered(neighborID string) (*api.RoutesResponse, error) { + response := &api.RoutesResponse{ + Api: src.makeCacheStatus(), + + Imported: api.Routes{}, + NotExported: api.Routes{}, + Filtered: api.Routes{}, + } + return response, nil +} + +// RoutesNotExported retrievs the routes not exported +// from the rs for a neighbor. +func (src *BgplgdSource) RoutesNotExported(neighborID string) (*api.RoutesResponse, error) { + response := &api.RoutesResponse{ + Api: src.makeCacheStatus(), + + Imported: api.Routes{}, + NotExported: api.Routes{}, + Filtered: api.Routes{}, + } + return response, nil +} + +// AllRoutes retrievs the entire RIB from the source. This is never +// cached as it is processed by the store. +func (src *BgplgdSource) AllRoutes() (*api.RoutesResponse, error) { + req, err := src.ShowRIBRequest(context.Background()) + if err != nil { + return nil, err + } + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // Read and decode response + body, err := decoders.ReadJSONResponse(res) + if err != nil { + return nil, err + } + + recv, err := decodeRoutes(body) + if err != nil { + return nil, err + } + + response := &api.RoutesResponse{ + Api: src.makeCacheStatus(), + Imported: recv, + NotExported: api.Routes{}, + Filtered: api.Routes{}, + } + return response, nil +}