micro/router/table.go

166 lines
3.5 KiB
Go

package router
import (
"errors"
"fmt"
"hash/fnv"
"strings"
"sync"
"github.com/olekukonko/tablewriter"
)
var (
// ErrRouteNotFound is returned when no route was found
ErrRouteNotFound = errors.New("route not found")
// ErrDuplicateRoute is return when route already exists
ErrDuplicateRoute = errors.New("duplicate route")
// ErrNotImplemented is returned when some functionality has not been implemented
ErrNotImplemented = errors.New("not implemented")
)
// Table is routing table
type Table interface {
// Add adds new route to the table
Add(Entry) error
// Remove removes route from the table
Remove(Entry) error
// Update updates route in the table
Update(...EntryOption) error
// Lookup looks up routes in the table
Lookup(Query) ([]Entry, error)
// Size returns the size of the table
Size() int
// String prints the routing table
String() string
}
// table is routing table
// It maps service name to routes
type table struct {
// m stores routing table map
m map[string]map[uint64]Entry
sync.RWMutex
}
// NewTable creates new routing table and returns it
func NewTable() Table {
return &table{
m: make(map[string]map[uint64]Entry),
}
}
// Add adds new routing entry
func (t *table) Add(e Entry) error {
t.Lock()
defer t.Unlock()
destAddr := e.Options().DestAddr
h := fnv.New64()
h.Write([]byte(e.Options().DestAddr + e.Options().Hop.Address()))
if _, ok := t.m[destAddr]; !ok {
// create new map for DestAddr routes
t.m[destAddr] = make(map[uint64]Entry)
t.m[destAddr][h.Sum64()] = e
return nil
}
if _, ok := t.m[destAddr][h.Sum64()]; ok && e.Options().Policy == OverrideIfExists {
t.m[destAddr][h.Sum64()] = e
return nil
}
return ErrDuplicateRoute
}
// Remove removes entry from the routing table
func (t *table) Remove(e Entry) error {
t.Lock()
defer t.Unlock()
destAddr := e.Options().DestAddr
h := fnv.New64()
h.Write([]byte(e.Options().DestAddr + e.Options().Hop.Address()))
if _, ok := t.m[destAddr]; !ok {
return ErrRouteNotFound
} else {
delete(t.m[destAddr], h.Sum64())
return nil
}
return nil
}
// Update updates routing entry
func (t *table) Update(opts ...EntryOption) error {
t.Lock()
defer t.Unlock()
e := NewEntry(opts...)
destAddr := e.Options().DestAddr
h := fnv.New64()
h.Write([]byte(e.Options().DestAddr + e.Options().Hop.Address()))
if _, ok := t.m[destAddr]; !ok {
return ErrRouteNotFound
}
if _, ok := t.m[destAddr][h.Sum64()]; ok {
t.m[destAddr][h.Sum64()] = e
return nil
}
return ErrRouteNotFound
}
// Lookup looks up entry in the routing table
func (t *table) Lookup(q Query) ([]Entry, error) {
return nil, ErrNotImplemented
}
// Size returns the size of the routing table
func (t *table) Size() int {
t.RLock()
defer t.RUnlock()
return len(t.m)
}
// String returns text representation of routing table
func (t *table) String() string {
t.RLock()
defer t.RUnlock()
// this will help us build routing table string
sb := &strings.Builder{}
// create nice table printing structure
table := tablewriter.NewWriter(sb)
table.SetHeader([]string{"Dest", "Hop", "Src", "Metric"})
var destAddr, prevAddr string
for _, entries := range t.m {
for _, entry := range entries {
destAddr = entry.Options().DestAddr
// we want to avoid printing the same dest address
if prevAddr == destAddr {
destAddr = ""
}
strEntry := []string{
destAddr,
entry.Options().Hop.Address(),
fmt.Sprintf("%d", entry.Options().SrcAddr),
fmt.Sprintf("%d", entry.Options().Metric),
}
table.Append(strEntry)
prevAddr = destAddr
}
}
return sb.String()
}