refactored decoders for reusability

This commit is contained in:
Annika Hannig 2021-03-25 21:47:31 +01:00
parent 9d7611916b
commit 18a4b67748
No known key found for this signature in database
GPG Key ID: 62E226E47DDCE58D
6 changed files with 134 additions and 91 deletions

16
pkg/decoders/maps.go Normal file
View File

@ -0,0 +1,16 @@
package decoders
// MapGet retrievs a key from an expected map
// it falls back if the input is not a map
// or the key was not found.
func MapGet(m interface{}, key string, fallback interface{}) interface{} {
smap, ok := m.(map[string]interface{})
if !ok {
return fallback
}
val, ok := smap[key]
if !ok {
return fallback
}
return val
}

85
pkg/decoders/types.go Normal file
View File

@ -0,0 +1,85 @@
package decoders
// Decode interfaces into expected types
// with a fallback.
import (
"strconv"
"time"
)
// String asserts a string, provided a default
func String(value interface{}, fallback string) string {
sval, ok := value.(string)
if !ok {
return fallback
}
return sval
}
// StringList decodes a list of strings
func StringList(data interface{}) []string {
list := []string{}
ldata, ok := data.([]interface{})
if !ok {
return []string{}
}
for _, e := range ldata {
s, ok := e.(string)
if ok {
list = append(list, s)
}
}
return list
}
// IntList decodes a list of integers
func IntList(data interface{}) []int {
list := []int{}
sdata := StringList(data)
for _, e := range sdata {
val, _ := strconv.Atoi(e)
list = append(list, val)
}
return list
}
// Int decodes an integer value
func Int(value interface{}, fallback int) int {
fval, ok := value.(float64)
if !ok {
return fallback
}
return int(fval)
}
// Bool decodes a boolean value
func Bool(value interface{}, fallback bool) bool {
val, ok := value.(bool)
if !ok {
return fallback
}
return val
}
// Duration decodes a time.Duration
func Duration(value interface{}, fallback time.Duration) time.Duration {
val, ok := value.(time.Duration)
if !ok {
return fallback
}
return val
}
// TimeUTC returns the time expecting an UTC timestamp
func TimeUTC(value interface{}, fallback time.Time) time.Time {
sval := String(value, "")
if sval == "" {
return fallback
}
t, err := time.Parse(time.RFC3339Nano, sval)
if err != nil {
return fallback
}
return t
}

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/alice-lg/alice-lg/pkg/decoders"
)
// Convert server time string to time
@ -136,9 +137,9 @@ func parseBirdwatcherStatus(bird ClientResponse, config Config) (api.Status, err
LastReboot: lastReboot,
LastReconfig: lastReconfig,
Backend: "bird",
Version: mustString(birdStatus["version"], "unknown"),
Message: mustString(birdStatus["message"], "unknown"),
RouterId: mustString(birdStatus["router_id"], "unknown"),
Version: decoders.String(birdStatus["version"], "unknown"),
Message: decoders.String(birdStatus["message"], "unknown"),
RouterId: decoders.String(birdStatus["router_id"], "unknown"),
}
return status, nil
@ -162,7 +163,7 @@ func parseNeighbours(bird ClientResponse, config Config) (api.Neighbours, error)
routes := protocol["routes"].(map[string]interface{})
uptime := parseRelativeServerTime(protocol["state_changed"], config)
lastError := mustString(protocol["last_error"], "")
lastError := decoders.String(protocol["last_error"], "")
routesReceived := float64(0)
if routes != nil {
@ -177,17 +178,17 @@ func parseNeighbours(bird ClientResponse, config Config) (api.Neighbours, error)
neighbour := &api.Neighbour{
Id: protocolId,
Address: mustString(protocol["neighbor_address"], "error"),
Asn: mustInt(protocol["neighbor_as"], 0),
Address: decoders.String(protocol["neighbor_address"], "error"),
Asn: decoders.Int(protocol["neighbor_as"], 0),
State: strings.ToLower(
mustString(protocol["state"], "unknown")),
Description: mustString(protocol["description"], "no description"),
decoders.String(protocol["state"], "unknown")),
Description: decoders.String(protocol["description"], "no description"),
RoutesReceived: mustInt(routesReceived, 0),
RoutesAccepted: mustInt(routes["imported"], 0),
RoutesFiltered: mustInt(routes["filtered"], 0),
RoutesExported: mustInt(routes["exported"], 0), //TODO protocol_exported?
RoutesPreferred: mustInt(routes["preferred"], 0),
RoutesReceived: decoders.Int(routesReceived, 0),
RoutesAccepted: decoders.Int(routes["imported"], 0),
RoutesFiltered: decoders.Int(routes["filtered"], 0),
RoutesExported: decoders.Int(routes["exported"], 0), //TODO protocol_exported?
RoutesPreferred: decoders.Int(routes["preferred"], 0),
Uptime: uptime,
LastError: lastError,
@ -218,7 +219,7 @@ func parseNeighboursShort(bird ClientResponse, config Config) (api.NeighboursSta
neighbour := &api.NeighbourStatus{
Id: protocolId,
State: mustString(protocol["state"], "unknown"),
State: decoders.String(protocol["state"], "unknown"),
Since: uptime,
}
@ -238,18 +239,18 @@ func parseRouteBgpInfo(data interface{}) api.BgpInfo {
return api.BgpInfo{}
}
asPath := mustIntList(bgpData["as_path"])
asPath := decoders.IntList(bgpData["as_path"])
communities := parseBgpCommunities(bgpData["communities"])
largeCommunities := parseBgpCommunities(bgpData["large_communities"])
extCommunities := parseExtBgpCommunities(bgpData["ext_communities"])
localPref, _ := strconv.Atoi(mustString(bgpData["local_pref"], "0"))
med, _ := strconv.Atoi(mustString(bgpData["med"], "0"))
localPref, _ := strconv.Atoi(decoders.String(bgpData["local_pref"], "0"))
med, _ := strconv.Atoi(decoders.String(bgpData["med"], "0"))
bgp := api.BgpInfo{
Origin: mustString(bgpData["origin"], "unknown"),
Origin: decoders.String(bgpData["origin"], "unknown"),
AsPath: asPath,
NextHop: mustString(bgpData["next_hop"], "unknown"),
NextHop: decoders.String(bgpData["next_hop"], "unknown"),
LocalPref: localPref,
Med: med,
Communities: communities,
@ -312,18 +313,18 @@ func parseRoutesData(birdRoutes []interface{}, config Config) api.Routes {
rdata := data.(map[string]interface{})
age := parseRelativeServerTime(rdata["age"], config)
rtype := mustStringList(rdata["type"])
rtype := decoders.StringList(rdata["type"])
bgpInfo := parseRouteBgpInfo(rdata["bgp"])
route := &api.Route{
Id: mustString(rdata["network"], "unknown"),
NeighbourId: mustString(rdata["from_protocol"], "unknown neighbour"),
Id: decoders.String(rdata["network"], "unknown"),
NeighbourId: decoders.String(rdata["from_protocol"], "unknown neighbour"),
Network: mustString(rdata["network"], "unknown net"),
Interface: mustString(rdata["interface"], "unknown interface"),
Gateway: mustString(rdata["gateway"], "unknown gateway"),
Metric: mustInt(rdata["metric"], -1),
Primary: mustBool(rdata["primary"], false),
Network: decoders.String(rdata["network"], "unknown net"),
Interface: decoders.String(rdata["interface"], "unknown interface"),
Gateway: decoders.String(rdata["gateway"], "unknown gateway"),
Metric: decoders.Int(rdata["metric"], -1),
Primary: decoders.Bool(rdata["primary"], false),
Age: age,
Type: rtype,
Bgp: bgpInfo,

View File

@ -7,6 +7,7 @@ import (
"strings"
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/alice-lg/alice-lg/pkg/decoders"
)
type MultiTableBirdwatcher struct {
@ -238,7 +239,7 @@ func (self *MultiTableBirdwatcher) fetchRequiredRoutes(neighborId string) (*api.
importedRoutes := api.Routes{}
if len(receivedRoutes) > 0 {
peer := receivedRoutes[0].Gateway
learntFrom := mustString(receivedRoutes[0].Details["learnt_from"], peer)
learntFrom := decoders.String(receivedRoutes[0].Details["learnt_from"], peer)
filteredRoutes = self.filterRoutesByPeerOrLearntFrom(filteredRoutes, peer, learntFrom)
importedRoutes = self.filterRoutesByDuplicates(receivedRoutes, filteredRoutes)
@ -506,7 +507,7 @@ func (self *MultiTableBirdwatcher) AllRoutes() (*api.RoutesResponse, error) {
protocolsBgp := self.filterProtocolsBgp(birdProtocols)
for protocolId, protocolsData := range protocolsBgp["protocols"].(map[string]interface{}) {
peer := protocolsData.(map[string]interface{})["neighbor_address"].(string)
learntFrom := mustString(protocolsData.(map[string]interface{})["learnt_from"], peer)
learntFrom := decoders.String(protocolsData.(map[string]interface{})["learnt_from"], peer)
// Fetch filtered routes
_, filtered, err := self.fetchFilteredRoutes(protocolId)

View File

@ -5,6 +5,7 @@ import (
"sort"
"github.com/alice-lg/alice-lg/pkg/api"
"github.com/alice-lg/alice-lg/pkg/decoders"
)
type SingleTableBirdwatcher struct {
@ -119,7 +120,7 @@ func (self *SingleTableBirdwatcher) fetchRequiredRoutes(neighborId string) (*api
importedRoutes := api.Routes{}
if len(receivedRoutes) > 0 {
peer := receivedRoutes[0].Gateway
learntFrom := mustString(receivedRoutes[0].Details["learnt_from"], peer)
learntFrom := decoders.String(receivedRoutes[0].Details["learnt_from"], peer)
filteredRoutes = self.filterRoutesByPeerOrLearntFrom(filteredRoutes, peer, learntFrom)
importedRoutes = self.filterRoutesByDuplicates(receivedRoutes, filteredRoutes)

View File

@ -1,61 +0,0 @@
package birdwatcher
import (
"strconv"
)
/*
* Types helper for parser
*/
// Assert string, provide default
func mustString(value interface{}, fallback string) string {
sval, ok := value.(string)
if !ok {
return fallback
}
return sval
}
// Assert list of strings
func mustStringList(data interface{}) []string {
list := []string{}
ldata, ok := data.([]interface{})
if !ok {
return []string{}
}
for _, e := range ldata {
s, ok := e.(string)
if ok {
list = append(list, s)
}
}
return list
}
// Convert list of strings to int
func mustIntList(data interface{}) []int {
list := []int{}
sdata := mustStringList(data)
for _, e := range sdata {
val, _ := strconv.Atoi(e)
list = append(list, val)
}
return list
}
func mustInt(value interface{}, fallback int) int {
fval, ok := value.(float64)
if !ok {
return fallback
}
return int(fval)
}
func mustBool(value interface{}, fallback bool) bool {
val, ok := value.(bool)
if !ok {
return fallback
}
return val
}