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"
)
// Options allows to set router options
// Options are router options
type Options struct {
// ID is router ID
ID string
@ -82,7 +82,7 @@ func NetworkRegistry(r registry.Registry) Option {
}
// RouterIB allows to configure RIB
func RouterIB(r RIB) Option {
func RouterRIB(r RIB) Option {
return func(o *Options) {
o.RIB = r
}

View File

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

View File

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

View File

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

View File

@ -1,19 +1,26 @@
// Package router provides an interface for micro network 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
type Router interface {
// Init initializes the router with options
Init(...Option) error
// Options returns the router options
Options() Options
// Table returns routing table
// Table returns the router routing table
Table() Table
// Address returns router adddress
// Address returns the router adddress
Address() string
// Gossip returns router gossip address
// Gossip returns the router gossip address
Gossip() string
// Network returns router network address
// Network returns the router network address
Network() string
// Start starts the router
Start() error
@ -26,18 +33,6 @@ type Router interface {
// Option used by the router
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
func NewRouter(opts ...Option) Router {
return newRouter(opts...)

View File

@ -2,251 +2,41 @@ package router
import (
"errors"
"fmt"
"hash"
"hash/fnv"
"strings"
"sync"
"github.com/google/uuid"
"github.com/olekukonko/tablewriter"
)
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")
// ErrDuplicateRoute is return when route already exists
// ErrDuplicateRoute is returned when the 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
// Table defines routing 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
// Remove removes existing route from the table
// Remove removes existing route from the routing table
Remove(Route) error
// Update updates route in the table
// Update updates route in the routing table
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)
// 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)
// Size returns the size of the table
// Size returns the size of the routing table
Size() int
// String prints the routing table
String() string
}
// table is routing table
type table struct {
// 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
}
// TableOption used by the routing table
type TableOption func(*TableOptions)
// NewTable creates new routing table and returns it
func NewTable() Table {
h := fnv.New64()
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()
func NewTable(opts ...TableOption) Table {
return newTable(opts...)
}

View File

@ -9,7 +9,11 @@ var (
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 {
// Next is a blocking call that returns watch result
Next() (*Result, error)