Reorganised source. Renamed files. No Code change.

This commit is contained in:
Milos Gajdos 2019-06-13 12:09:49 +01:00
parent 95fc625e99
commit 6e669d4611
No known key found for this signature in database
GPG Key ID: 8B31058CC55DFD4F
9 changed files with 295 additions and 246 deletions

251
router/default_table.go Normal file
View File

@ -0,0 +1,251 @@
package router
import (
"fmt"
"hash"
"hash/fnv"
"strings"
"sync"
"github.com/google/uuid"
"github.com/olekukonko/tablewriter"
)
// TableOptions are routing table options
type TableOptions struct{}
// table is in memory routing table
type table struct {
// opts are table options
opts TableOptions
// m stores routing table map
m map[string]map[uint64]Route
// h hashes route entries
h hash.Hash64
// w is a list of table watchers
w map[string]*tableWatcher
sync.RWMutex
}
// newTable creates default routing table and returns it
func newTable(opts ...TableOption) Table {
// default options
var options TableOptions
// apply requested options
for _, o := range opts {
o(&options)
}
h := fnv.New64()
h.Reset()
return &table{
opts: options,
m: make(map[string]map[uint64]Route),
w: make(map[string]*tableWatcher),
h: h,
}
}
// Init initializes routing table with options
func (t *table) Init(opts ...TableOption) error {
for _, o := range opts {
o(&t.opts)
}
return nil
}
// Options returns routing table options
func (t *table) Options() TableOptions {
return t.opts
}
// Add adds a route to the routing table
func (t *table) Add(r Route) error {
t.Lock()
defer t.Unlock()
destAddr := r.Options().DestAddr
sum := t.hash(r)
if _, ok := t.m[destAddr]; !ok {
t.m[destAddr] = make(map[uint64]Route)
t.m[destAddr][sum] = r
go t.sendResult(&Result{Action: "add", Route: r})
return nil
}
if _, ok := t.m[destAddr][sum]; ok && r.Options().Policy == OverrideIfExists {
t.m[destAddr][sum] = r
go t.sendResult(&Result{Action: "update", Route: r})
return nil
}
if r.Options().Policy == IgnoreIfExists {
return nil
}
return ErrDuplicateRoute
}
// Remove removes the route from the routing table
func (t *table) Remove(r Route) error {
t.Lock()
defer t.Unlock()
destAddr := r.Options().DestAddr
sum := t.hash(r)
if _, ok := t.m[destAddr]; !ok {
return ErrRouteNotFound
}
delete(t.m[destAddr], sum)
go t.sendResult(&Result{Action: "remove", Route: r})
return nil
}
// Update updates routing table with new route
func (t *table) Update(r Route) error {
t.Lock()
defer t.Unlock()
destAddr := r.Options().DestAddr
sum := t.hash(r)
if _, ok := t.m[destAddr]; !ok {
return ErrRouteNotFound
}
if _, ok := t.m[destAddr][sum]; ok {
t.m[destAddr][sum] = r
go t.sendResult(&Result{Action: "update", Route: r})
return nil
}
return ErrRouteNotFound
}
// Lookup queries routing table and returns all routes that match it
func (t *table) Lookup(q Query) ([]Route, error) {
t.RLock()
defer t.RUnlock()
var results []Route
for destAddr, routes := range t.m {
if q.Options().DestAddr != "*" {
if q.Options().DestAddr != destAddr {
continue
}
for _, route := range routes {
if q.Options().Network == "*" || q.Options().Network == route.Options().Network {
results = append(results, route)
}
}
}
if q.Options().DestAddr == "*" {
for _, route := range routes {
if q.Options().Network == "*" || q.Options().Network == route.Options().Network {
results = append(results, route)
}
}
}
}
if len(results) == 0 && q.Options().Policy != DiscardNoRoute {
return nil, ErrRouteNotFound
}
return results, nil
}
// Watch returns routing table entry watcher
func (t *table) Watch(opts ...WatchOption) (Watcher, error) {
// by default watch everything
wopts := WatchOptions{
DestAddr: "*",
Network: "*",
}
for _, o := range opts {
o(&wopts)
}
watcher := &tableWatcher{
opts: wopts,
resChan: make(chan *Result, 10),
done: make(chan struct{}),
}
t.Lock()
t.w[uuid.New().String()] = watcher
t.Unlock()
return watcher, nil
}
// sendResult sends rules to all subscribe watchers
func (t *table) sendResult(r *Result) {
t.RLock()
defer t.RUnlock()
for _, w := range t.w {
select {
case w.resChan <- r:
case <-w.done:
}
}
}
// Size returns the size of the routing table
func (t *table) Size() int {
t.RLock()
defer t.RUnlock()
return len(t.m)
}
// String returns debug information
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{"Destination", "Gateway", "Network", "Metric"})
for _, destRoute := range t.m {
for _, route := range destRoute {
strRoute := []string{
route.Options().DestAddr,
route.Options().Gateway.Address(),
route.Options().Gateway.Network(),
fmt.Sprintf("%d", route.Options().Metric),
}
table.Append(strRoute)
}
}
// render table into sb
table.Render()
return sb.String()
}
// hash hashes the route using router gateway and network address
func (t *table) hash(r Route) uint64 {
gwAddr := r.Options().Gateway.Address()
netAddr := r.Options().Network
t.h.Reset()
t.h.Write([]byte(gwAddr + netAddr))
return t.h.Sum64()
}

View File

@ -12,7 +12,7 @@ var (
DefaultNetworkAddress = ":9094" DefaultNetworkAddress = ":9094"
) )
// Options allows to set router options // Options are router options
type Options struct { type Options struct {
// ID is router ID // ID is router ID
ID string ID string
@ -82,7 +82,7 @@ func NetworkRegistry(r registry.Registry) Option {
} }
// RouterIB allows to configure RIB // RouterIB allows to configure RIB
func RouterIB(r RIB) Option { func RouterRIB(r RIB) Option {
return func(o *Options) { return func(o *Options) {
o.RIB = r o.RIB = r
} }

View File

@ -10,6 +10,9 @@ const (
ClosestMatch ClosestMatch
) )
// QueryOption is used to define query options
type QueryOption func(*QueryOptions)
// QueryOptions allow to define routing table query options // QueryOptions allow to define routing table query options
type QueryOptions struct { type QueryOptions struct {
// DestAddr defines destination address // DestAddr defines destination address

View File

@ -12,6 +12,9 @@ type RIB interface {
String() string String() string
} }
// RIBOptopn is used to configure RIB
type RIBOption func(*RIBOptions)
// RIBOptions allow to set RIB sources. // RIBOptions allow to set RIB sources.
type RIBOptions struct { type RIBOptions struct {
// Source defines RIB source URL // Source defines RIB source URL

View File

@ -14,6 +14,9 @@ const (
ErrIfExists ErrIfExists
) )
// RouteOption is used to define routing table entry options
type RouteOption func(*RouteOptions)
// RouteOptions defines micro network routing table route options // RouteOptions defines micro network routing table route options
type RouteOptions struct { type RouteOptions struct {
// DestAddr is destination address // DestAddr is destination address

View File

@ -1,19 +1,26 @@
// Package router provides an interface for micro network router // Package router provides an interface for micro network router
package router package router
import "errors"
var (
// ErrNotImplemented is returned when some functionality has not been implemented
ErrNotImplemented = errors.New("not implemented")
)
// Router is micro network router // Router is micro network router
type Router interface { type Router interface {
// Init initializes the router with options // Init initializes the router with options
Init(...Option) error Init(...Option) error
// Options returns the router options // Options returns the router options
Options() Options Options() Options
// Table returns routing table // Table returns the router routing table
Table() Table Table() Table
// Address returns router adddress // Address returns the router adddress
Address() string Address() string
// Gossip returns router gossip address // Gossip returns the router gossip address
Gossip() string Gossip() string
// Network returns router network address // Network returns the router network address
Network() string Network() string
// Start starts the router // Start starts the router
Start() error Start() error
@ -26,18 +33,6 @@ type Router interface {
// Option used by the router // Option used by the router
type Option func(*Options) type Option func(*Options)
// RIBOptopn is used to configure RIB
type RIBOption func(*RIBOptions)
// RouteOption is used to define routing table entry options
type RouteOption func(*RouteOptions)
// QueryOption is used to define query options
type QueryOption func(*QueryOptions)
// WatchOption is used to define what routes to watch in the table
type WatchOption func(*WatchOptions)
// NewRouter creates new Router and returns it // NewRouter creates new Router and returns it
func NewRouter(opts ...Option) Router { func NewRouter(opts ...Option) Router {
return newRouter(opts...) return newRouter(opts...)

View File

@ -2,251 +2,41 @@ package router
import ( import (
"errors" "errors"
"fmt"
"hash"
"hash/fnv"
"strings"
"sync"
"github.com/google/uuid"
"github.com/olekukonko/tablewriter"
) )
var ( var (
// ErrRouteNotFound is returned when no route was found // ErrRouteNotFound is returned when no route was found in the routing table
ErrRouteNotFound = errors.New("route not found") ErrRouteNotFound = errors.New("route not found")
// ErrDuplicateRoute is return when route already exists // ErrDuplicateRoute is returned when the route already exists
ErrDuplicateRoute = errors.New("duplicate route") ErrDuplicateRoute = errors.New("duplicate route")
// ErrNotImplemented is returned when some functionality has not been implemented
ErrNotImplemented = errors.New("not implemented")
) )
// Table is routing table // Table defines routing table interface
type Table interface { type Table interface {
// Add adds new route to the table // Init initializes the router with options
Init(...TableOption) error
// Options returns the router options
Options() TableOptions
// Add adds new route to the routing table
Add(Route) error Add(Route) error
// Remove removes existing route from the table // Remove removes existing route from the routing table
Remove(Route) error Remove(Route) error
// Update updates route in the table // Update updates route in the routing table
Update(Route) error Update(Route) error
// Lookup looks up routes in the table // Lookup looks up routes in the routing table and returns them
Lookup(Query) ([]Route, error) Lookup(Query) ([]Route, error)
// Watch returns a watcher which allows you to track updates to the table // Watch returns a watcher which allows to track updates to the routing table
Watch(opts ...WatchOption) (Watcher, error) Watch(opts ...WatchOption) (Watcher, error)
// Size returns the size of the table // Size returns the size of the routing table
Size() int Size() int
// String prints the routing table // String prints the routing table
String() string String() string
} }
// table is routing table // TableOption used by the routing table
type table struct { type TableOption func(*TableOptions)
// m stores routing table map
m map[string]map[uint64]Route
// h hashes route entries
h hash.Hash64
// w is a list of table watchers
w map[string]*tableWatcher
sync.RWMutex
}
// NewTable creates new routing table and returns it // NewTable creates new routing table and returns it
func NewTable() Table { func NewTable(opts ...TableOption) Table {
h := fnv.New64() return newTable(opts...)
h.Reset()
return &table{
m: make(map[string]map[uint64]Route),
w: make(map[string]*tableWatcher),
h: h,
}
}
// Add adds a route to the routing table
func (t *table) Add(r Route) error {
t.Lock()
defer t.Unlock()
destAddr := r.Options().DestAddr
sum := t.hash(r)
if _, ok := t.m[destAddr]; !ok {
t.m[destAddr] = make(map[uint64]Route)
t.m[destAddr][sum] = r
go t.sendResult(&Result{Action: "add", Route: r})
return nil
}
if _, ok := t.m[destAddr][sum]; ok && r.Options().Policy == OverrideIfExists {
t.m[destAddr][sum] = r
go t.sendResult(&Result{Action: "update", Route: r})
return nil
}
if r.Options().Policy == IgnoreIfExists {
return nil
}
return ErrDuplicateRoute
}
// Remove removes the route from the routing table
func (t *table) Remove(r Route) error {
t.Lock()
defer t.Unlock()
destAddr := r.Options().DestAddr
sum := t.hash(r)
if _, ok := t.m[destAddr]; !ok {
return ErrRouteNotFound
}
delete(t.m[destAddr], sum)
go t.sendResult(&Result{Action: "remove", Route: r})
return nil
}
// Update updates routing table with new route
func (t *table) Update(r Route) error {
t.Lock()
defer t.Unlock()
destAddr := r.Options().DestAddr
sum := t.hash(r)
if _, ok := t.m[destAddr]; !ok {
return ErrRouteNotFound
}
if _, ok := t.m[destAddr][sum]; ok {
t.m[destAddr][sum] = r
go t.sendResult(&Result{Action: "update", Route: r})
return nil
}
return ErrRouteNotFound
}
// Lookup queries routing table and returns all routes that match it
func (t *table) Lookup(q Query) ([]Route, error) {
t.RLock()
defer t.RUnlock()
var results []Route
for destAddr, routes := range t.m {
if q.Options().DestAddr != "*" {
if q.Options().DestAddr != destAddr {
continue
}
for _, route := range routes {
if q.Options().Network == "*" || q.Options().Network == route.Options().Network {
results = append(results, route)
}
}
}
if q.Options().DestAddr == "*" {
for _, route := range routes {
if q.Options().Network == "*" || q.Options().Network == route.Options().Network {
results = append(results, route)
}
}
}
}
if len(results) == 0 && q.Options().Policy != DiscardNoRoute {
return nil, ErrRouteNotFound
}
return results, nil
}
// Watch returns routing table entry watcher
func (t *table) Watch(opts ...WatchOption) (Watcher, error) {
// by default watch everything
wopts := WatchOptions{
DestAddr: "*",
Network: "*",
}
for _, o := range opts {
o(&wopts)
}
watcher := &tableWatcher{
opts: wopts,
resChan: make(chan *Result, 10),
done: make(chan struct{}),
}
t.Lock()
t.w[uuid.New().String()] = watcher
t.Unlock()
return watcher, nil
}
// sendResult sends rules to all subscribe watchers
func (t *table) sendResult(r *Result) {
t.RLock()
defer t.RUnlock()
for _, w := range t.w {
select {
case w.resChan <- r:
case <-w.done:
}
}
}
// Size returns the size of the routing table
func (t *table) Size() int {
t.RLock()
defer t.RUnlock()
return len(t.m)
}
// String returns debug information
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{"Destination", "Gateway", "Network", "Metric"})
for _, destRoute := range t.m {
for _, route := range destRoute {
strRoute := []string{
route.Options().DestAddr,
route.Options().Gateway.Address(),
route.Options().Gateway.Network(),
fmt.Sprintf("%d", route.Options().Metric),
}
table.Append(strRoute)
}
}
// render table into sb
table.Render()
return sb.String()
}
// hash hashes the route using router gateway and network address
func (t *table) hash(r Route) uint64 {
gwAddr := r.Options().Gateway.Address()
netAddr := r.Options().Network
t.h.Reset()
t.h.Write([]byte(gwAddr + netAddr))
return t.h.Sum64()
} }

View File

@ -9,7 +9,11 @@ var (
ErrWatcherStopped = errors.New("routing table watcher stopped") ErrWatcherStopped = errors.New("routing table watcher stopped")
) )
// Watcher is an interface that returns updates to the routing table // WatchOption is used to define what routes to watch in the table
type WatchOption func(*WatchOptions)
// Watcher defines routing table watcher interface
// Watcher returns updates to the routing table
type Watcher interface { type Watcher interface {
// Next is a blocking call that returns watch result // Next is a blocking call that returns watch result
Next() (*Result, error) Next() (*Result, error)