pools with generics
This commit is contained in:
parent
d6fa635377
commit
85c5d19d4f
@ -4,6 +4,7 @@ import (
|
||||
"math"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/alice-lg/alice-lg/pkg/api"
|
||||
)
|
||||
@ -12,14 +13,14 @@ import (
|
||||
// This works with large and standard communities. For extended
|
||||
// communities, use the ExtCommunityPool.
|
||||
type CommunitiesPool struct {
|
||||
root *Node
|
||||
root *Node[int, api.Community]
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// NewCommunitiesPool creates a new pool for a single BGP community
|
||||
func NewCommunitiesPool() *CommunitiesPool {
|
||||
return &CommunitiesPool{
|
||||
root: NewNode(api.Community{}),
|
||||
root: NewNode[int, api.Community](api.Community{}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,9 +29,9 @@ func (p *CommunitiesPool) Acquire(c api.Community) api.Community {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
if len(c) == 0 {
|
||||
return p.root.ptr.(api.Community) // root
|
||||
return p.root.value
|
||||
}
|
||||
return p.root.traverse(c, c).(api.Community)
|
||||
return p.root.traverse(c, c)
|
||||
}
|
||||
|
||||
// Read a single bgp community
|
||||
@ -38,20 +39,20 @@ func (p *CommunitiesPool) Read(c api.Community) api.Community {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
if len(c) == 0 {
|
||||
return p.root.ptr.(api.Community) // root
|
||||
return p.root.value // root
|
||||
}
|
||||
v := p.root.read(c, c)
|
||||
v := p.root.read(c)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return v.(api.Community)
|
||||
return v
|
||||
}
|
||||
|
||||
// CommunitiesSetPool is for deduplicating a list of BGP communities
|
||||
// (Large and default. The ext communities representation right now
|
||||
// makes problems and need to be fixed. TODO.)
|
||||
type CommunitiesSetPool struct {
|
||||
root *Node
|
||||
root *Node[unsafe.Pointer, []api.Community]
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
@ -59,7 +60,7 @@ type CommunitiesSetPool struct {
|
||||
// of BGP communities.
|
||||
func NewCommunitiesSetPool() *CommunitiesSetPool {
|
||||
return &CommunitiesSetPool{
|
||||
root: NewNode([]api.Community{}),
|
||||
root: NewNode[unsafe.Pointer, []api.Community]([]api.Community{}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,24 +70,30 @@ func (p *CommunitiesSetPool) Acquire(communities []api.Community) []api.Communit
|
||||
defer p.Unlock()
|
||||
// Make identification list by using the pointer address
|
||||
// of the deduplicated community as ID
|
||||
ids := make([]int, len(communities))
|
||||
ids := make([]unsafe.Pointer, len(communities))
|
||||
set := make([]api.Community, len(communities))
|
||||
for i, comm := range communities {
|
||||
commPtr := Communities.Acquire(comm)
|
||||
addr := reflect.ValueOf(commPtr).UnsafePointer()
|
||||
ids[i] = int(uintptr(addr))
|
||||
ids[i] = reflect.ValueOf(commPtr).UnsafePointer()
|
||||
set[i] = commPtr
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return p.root.ptr.([]api.Community)
|
||||
return p.root.value
|
||||
}
|
||||
return p.root.traverse(set, ids).([]api.Community)
|
||||
return p.root.traverse(set, ids)
|
||||
}
|
||||
|
||||
// NewExtCommunitiesSetPool creates a new pool for extended communities
|
||||
func NewExtCommunitiesSetPool() *CommunitiesSetPool {
|
||||
return &CommunitiesSetPool{
|
||||
root: NewNode([]api.ExtCommunity{}),
|
||||
// ExtCommunitiesSetPool is for deduplicating a list of ext. BGP communities
|
||||
type ExtCommunitiesSetPool struct {
|
||||
root *Node[unsafe.Pointer, []api.ExtCommunity]
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewExtCommunitiesSetPool creates a new pool for lists
|
||||
// of BGP communities.
|
||||
func NewExtCommunitiesSetPool() *ExtCommunitiesSetPool {
|
||||
return &ExtCommunitiesSetPool{
|
||||
root: NewNode[unsafe.Pointer, []api.ExtCommunity]([]api.ExtCommunity{}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,23 +106,22 @@ func extPrefixToInt(s string) int {
|
||||
}
|
||||
|
||||
// AcquireExt a list of ext bgp communities
|
||||
func (p *CommunitiesSetPool) AcquireExt(communities []api.ExtCommunity) []api.ExtCommunity {
|
||||
func (p *ExtCommunitiesSetPool) Acquire(communities []api.ExtCommunity) []api.ExtCommunity {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
// Make identification list
|
||||
ids := make([]int, len(communities))
|
||||
ids := make([]unsafe.Pointer, len(communities))
|
||||
for i, comm := range communities {
|
||||
r := extPrefixToInt(comm[0].(string))
|
||||
icomm := []int{r, comm[1].(int), comm[2].(int)}
|
||||
|
||||
// get community identifier
|
||||
commPtr := ExtCommunities.Acquire(icomm)
|
||||
addr := reflect.ValueOf(commPtr).UnsafePointer()
|
||||
ids[i] = int(uintptr(addr))
|
||||
ids[i] = reflect.ValueOf(commPtr).UnsafePointer()
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return p.root.ptr.([]api.ExtCommunity)
|
||||
return p.root.value
|
||||
}
|
||||
return p.root.traverse(communities, ids).([]api.ExtCommunity)
|
||||
return p.root.traverse(communities, ids)
|
||||
}
|
||||
|
@ -125,14 +125,14 @@ func TestAcquireExtCommunitiesSets(t *testing.T) {
|
||||
c3 := []api.ExtCommunity{
|
||||
{"ro", 6, 1},
|
||||
{"rt", 6, 2},
|
||||
{"rt", 1, 1},
|
||||
{"xyz", 1, 1},
|
||||
}
|
||||
|
||||
p := NewExtCommunitiesSetPool()
|
||||
|
||||
pc1 := p.AcquireExt(c1)
|
||||
pc2 := p.AcquireExt(c2)
|
||||
pc3 := p.AcquireExt(c3)
|
||||
pc1 := p.Acquire(c1)
|
||||
pc2 := p.Acquire(c2)
|
||||
pc3 := p.Acquire(c3)
|
||||
|
||||
if fmt.Sprintf("%p", c1) == fmt.Sprintf("%p", c2) {
|
||||
t.Error("expected c1 !== c2")
|
||||
|
@ -4,77 +4,19 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Node is a node in the tree.
|
||||
type Node struct {
|
||||
children map[int]*Node
|
||||
counter int
|
||||
ptr interface{}
|
||||
}
|
||||
|
||||
// NewNode creates a new tree node
|
||||
func NewNode(ptr interface{}) *Node {
|
||||
return &Node{
|
||||
children: map[int]*Node{},
|
||||
ptr: ptr,
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse read only; returns nil if not found
|
||||
func (n *Node) read(list interface{}, tail []int) interface{} {
|
||||
value := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for value in children
|
||||
child, ok := n.children[value]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set list ptr if required
|
||||
if len(tail) == 0 {
|
||||
return child.ptr
|
||||
}
|
||||
|
||||
return child.read(list, tail)
|
||||
}
|
||||
|
||||
// Internally acquire list by traversing the tree and
|
||||
// creating nodes if required.
|
||||
func (n *Node) traverse(list interface{}, tail []int) interface{} {
|
||||
value := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for value in children
|
||||
child, ok := n.children[value]
|
||||
if !ok {
|
||||
child = NewNode(nil)
|
||||
n.children[value] = child
|
||||
}
|
||||
|
||||
// Set list ptr if required
|
||||
if len(tail) == 0 {
|
||||
if child.ptr == nil {
|
||||
child.ptr = list
|
||||
}
|
||||
return child.ptr
|
||||
}
|
||||
|
||||
return child.traverse(list, tail)
|
||||
}
|
||||
|
||||
// A IntListPool can be used to deduplicate
|
||||
// lists of integers. Like an AS path or BGP communities.
|
||||
//
|
||||
// A Tree datastructure is used.
|
||||
type IntListPool struct {
|
||||
root *Node
|
||||
root *Node[int, []int]
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewIntListPool creates a new int list pool
|
||||
func NewIntListPool() *IntListPool {
|
||||
return &IntListPool{
|
||||
root: NewNode([]int{}),
|
||||
root: NewNode[int, []int]([]int{}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,16 +26,16 @@ func (p *IntListPool) Acquire(list []int) []int {
|
||||
defer p.Unlock()
|
||||
|
||||
if len(list) == 0 {
|
||||
return p.root.ptr.([]int) // root
|
||||
return p.root.value // root
|
||||
}
|
||||
return p.root.traverse(list, list).([]int)
|
||||
return p.root.traverse(list, list)
|
||||
}
|
||||
|
||||
// A StringListPool can be used for deduplicating lists
|
||||
// of strings. (This is a variant of an int list, as string
|
||||
// values are converted to int.
|
||||
type StringListPool struct {
|
||||
root *Node
|
||||
root *Node[int, []string]
|
||||
values map[string]int
|
||||
head int
|
||||
sync.Mutex
|
||||
@ -104,14 +46,14 @@ func NewStringListPool() *StringListPool {
|
||||
return &StringListPool{
|
||||
head: 1,
|
||||
values: map[string]int{},
|
||||
root: NewNode([]string{}),
|
||||
root: NewNode[int, []string]([]string{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire the string list pointer from the pool
|
||||
func (p *StringListPool) Acquire(list []string) []string {
|
||||
if len(list) == 0 {
|
||||
return p.root.ptr.([]string) // root
|
||||
return p.root.value
|
||||
}
|
||||
|
||||
// Make idenfier list
|
||||
@ -127,5 +69,5 @@ func (p *StringListPool) Acquire(list []string) []string {
|
||||
id[i] = v
|
||||
}
|
||||
|
||||
return p.root.traverse(list, id).([]string)
|
||||
return p.root.traverse(list, id)
|
||||
}
|
||||
|
@ -2,9 +2,7 @@ package pools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestAcquireIntList(t *testing.T) {
|
||||
@ -27,23 +25,6 @@ func TestAcquireIntList(t *testing.T) {
|
||||
t.Log(fmt.Sprintf("Ptr: %p %p => %p %p", a, b, r1, r2))
|
||||
}
|
||||
|
||||
func TestPtrIntList(t *testing.T) {
|
||||
a := []int{23, 42, 1337, 65535, 1}
|
||||
b := []int{23, 42, 1337, 65535, 1}
|
||||
|
||||
pa := unsafe.Pointer(&a[0])
|
||||
pb := unsafe.Pointer(&b[0])
|
||||
|
||||
t.Log(fmt.Sprintf("Ptr: %p %p", a, b))
|
||||
t.Log(fmt.Sprintf("P: %p %p", pa, pb))
|
||||
|
||||
ra := reflect.ValueOf(a).UnsafePointer()
|
||||
rb := reflect.ValueOf(b).UnsafePointer()
|
||||
t.Log(fmt.Sprintf("P: %x %x %v", ra, rb, ra == rb))
|
||||
|
||||
t.Log(fmt.Sprintf("P: %x %x %v", int(uintptr(ra)), rb, ra == rb))
|
||||
}
|
||||
|
||||
func TestAcquireStringList(t *testing.T) {
|
||||
q := []string{"foo", "bar", "bgp"}
|
||||
w := []string{"foo", "bar", "bgp"}
|
||||
|
311
pkg/pools/node.go
Normal file
311
pkg/pools/node.go
Normal file
@ -0,0 +1,311 @@
|
||||
package pools
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/alice-lg/alice-lg/pkg/api"
|
||||
)
|
||||
|
||||
// NOTE: Yes, generics could be used here.
|
||||
// This also looks like a pretty good use case for them.
|
||||
// However, the performance penalty is too high.
|
||||
//
|
||||
// With: Refreshed routes of rs1.foo (BIRD2 IPv4) in 26.905169348s
|
||||
// Without: Refreshed routes of rs1.foo (BIRD2 IPv4) in 46.760695927s
|
||||
//
|
||||
// So yeah. Copy and paste time!
|
||||
|
||||
type Node[T comparable, V any] struct {
|
||||
children map[T]*Node[T, V] // map of children
|
||||
value V
|
||||
final bool
|
||||
}
|
||||
|
||||
// NewNode creates a new tree node
|
||||
func NewNode[T comparable, V any](value V) *Node[T, V] {
|
||||
return &Node[T, V]{
|
||||
children: map[T]*Node[T, V]{},
|
||||
value: value,
|
||||
final: false,
|
||||
}
|
||||
}
|
||||
|
||||
// traverse inserts a new node into the three if required
|
||||
// or returns the object if it already exists.
|
||||
func (n *Node[T, V]) traverse(value V, tail []T) V {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
var zero V
|
||||
child = NewNode[T, V](zero)
|
||||
n.children[id] = child
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
if !child.final {
|
||||
child.value = value
|
||||
child.final = true
|
||||
}
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.traverse(value, tail)
|
||||
}
|
||||
|
||||
// read returns the object if it exists or nil if not.
|
||||
func (n *Node[T, V]) read(tail []T) V {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
var zero V
|
||||
return zero
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.read(tail)
|
||||
}
|
||||
|
||||
// IntListNode is a node with an integer as key and
|
||||
// a list of integers as value.
|
||||
type IntListNode struct {
|
||||
children map[int]*IntListNode
|
||||
value []int
|
||||
}
|
||||
|
||||
// NewIntListNode creates a new int tree node
|
||||
func NewIntListNode(value []int) *IntListNode {
|
||||
return &IntListNode{
|
||||
children: map[int]*IntListNode{},
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// IntList traverse inserts a new node into the three if required
|
||||
// or returns the object if it already exists.
|
||||
func (n *IntListNode) traverse(value []int, tail []int) []int {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
child = NewIntListNode(nil)
|
||||
n.children[id] = child
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
if child.value == nil {
|
||||
child.value = value
|
||||
}
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.traverse(value, tail)
|
||||
}
|
||||
|
||||
// read returns the object if it exists or nil if not.
|
||||
func (n *IntListNode) read(tail []int) []int {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.read(tail)
|
||||
}
|
||||
|
||||
// StringListNode is a node with an integer as key and
|
||||
// a list of integers as value.
|
||||
type StringListNode struct {
|
||||
children map[int]*StringListNode
|
||||
value []string
|
||||
}
|
||||
|
||||
// NewStringListNode creates a new int tree node
|
||||
func NewStringListNode(value []string) *StringListNode {
|
||||
return &StringListNode{
|
||||
children: map[int]*StringListNode{},
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// StringList traverse inserts a new node into the three if required
|
||||
// or returns the object if it already exists.
|
||||
func (n *StringListNode) traverse(value []string, tail []int) []string {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
child = NewStringListNode(nil)
|
||||
n.children[id] = child
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
if child.value == nil {
|
||||
child.value = value
|
||||
}
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.traverse(value, tail)
|
||||
}
|
||||
|
||||
// read returns the object if it exists or nil if not.
|
||||
func (n *StringListNode) read(tail []int) []string {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.read(tail)
|
||||
}
|
||||
|
||||
// CommunityNode is a node with an integer as key
|
||||
type CommunityNode struct {
|
||||
children map[int]*CommunityNode
|
||||
value api.Community
|
||||
}
|
||||
|
||||
// NewCommunityNode creates a new int tree node
|
||||
func NewCommunityNode(value []int) *CommunityNode {
|
||||
return &CommunityNode{
|
||||
children: map[int]*CommunityNode{},
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// CommunityNode: traverse inserts a new node into the three if required
|
||||
// or returns the object if it already exists.
|
||||
func (n *CommunityNode) traverse(value api.Community, tail []int) api.Community {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
child = NewCommunityNode(nil)
|
||||
n.children[id] = child
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
if child.value == nil {
|
||||
child.value = value
|
||||
}
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.traverse(value, tail)
|
||||
}
|
||||
|
||||
// read returns the object if it exists or nil if not.
|
||||
func (n *CommunityNode) read(tail []int) api.Community {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.read(tail)
|
||||
}
|
||||
|
||||
// CommunityListNode is a node with an integer as key and
|
||||
// a list of integers as value.
|
||||
type CommunityListNode struct {
|
||||
children map[unsafe.Pointer]*CommunityListNode
|
||||
value []api.Community
|
||||
}
|
||||
|
||||
// NewCommunityListNode creates a new int tree node
|
||||
func NewCommunityListNode(value []api.Community) *CommunityListNode {
|
||||
return &CommunityListNode{
|
||||
children: map[unsafe.Pointer]*CommunityListNode{},
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// CommunityList traverse inserts a new node into the three if required
|
||||
// or returns the object if it already exists.
|
||||
func (n *CommunityListNode) traverse(
|
||||
value []api.Community,
|
||||
tail []unsafe.Pointer,
|
||||
) []api.Community {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
child = NewCommunityListNode(nil)
|
||||
n.children[id] = child
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
if child.value == nil {
|
||||
child.value = value
|
||||
}
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.traverse(value, tail)
|
||||
}
|
||||
|
||||
// read returns the object if it exists or nil if not.
|
||||
func (n *CommunityListNode) read(tail []unsafe.Pointer) []api.Community {
|
||||
id := tail[0]
|
||||
tail = tail[1:]
|
||||
|
||||
// Seek for identifier in children
|
||||
child, ok := n.children[id]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set obj if required
|
||||
if len(tail) == 0 {
|
||||
return child.value
|
||||
}
|
||||
|
||||
return child.read(tail)
|
||||
}
|
@ -47,7 +47,7 @@ var ExtCommunities *CommunitiesPool
|
||||
var CommunitiesSets *CommunitiesSetPool
|
||||
|
||||
// ExtCommunitiesSets stores a list of extended communities
|
||||
var ExtCommunitiesSets *CommunitiesSetPool
|
||||
var ExtCommunitiesSets *ExtCommunitiesSetPool
|
||||
|
||||
// LargeCommunitiesSets store a list of large BGP communities
|
||||
var LargeCommunitiesSets *CommunitiesSetPool
|
||||
|
@ -259,7 +259,7 @@ func parseRouteBgpInfo(data interface{}) *api.BGPInfo {
|
||||
LocalPref: localPref,
|
||||
Med: med,
|
||||
Communities: pools.CommunitiesSets.Acquire(communities),
|
||||
ExtCommunities: pools.ExtCommunitiesSets.AcquireExt(extCommunities),
|
||||
ExtCommunities: pools.ExtCommunitiesSets.Acquire(extCommunities),
|
||||
LargeCommunities: pools.LargeCommunitiesSets.Acquire(largeCommunities),
|
||||
}
|
||||
return bgp
|
||||
|
@ -159,7 +159,7 @@ func (gobgp *GoBGP) parsePathIntoRoute(
|
||||
|
||||
route.BGP.AsPath = pools.ASPaths.Acquire(route.BGP.AsPath)
|
||||
route.BGP.Communities = pools.CommunitiesSets.Acquire(route.BGP.Communities)
|
||||
route.BGP.ExtCommunities = pools.ExtCommunitiesSets.AcquireExt(route.BGP.ExtCommunities)
|
||||
route.BGP.ExtCommunities = pools.ExtCommunitiesSets.Acquire(route.BGP.ExtCommunities)
|
||||
route.BGP.LargeCommunities = pools.LargeCommunitiesSets.Acquire(route.BGP.LargeCommunities)
|
||||
|
||||
route.Metric = (route.BGP.LocalPref + route.BGP.Med)
|
||||
|
@ -184,7 +184,7 @@ func decodeRoute(details map[string]interface{}) (*api.Route, error) {
|
||||
AsPath: pools.ASPaths.Acquire(asPath),
|
||||
NextHop: pools.Gateways4.Acquire(trueNextHop),
|
||||
Communities: pools.CommunitiesSets.Acquire(communities),
|
||||
ExtCommunities: pools.ExtCommunitiesSets.AcquireExt(extendedCommunities),
|
||||
ExtCommunities: pools.ExtCommunitiesSets.Acquire(extendedCommunities),
|
||||
LargeCommunities: pools.LargeCommunitiesSets.Acquire(largeCommunities),
|
||||
LocalPref: localPref,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user