Skip to content
Snippets Groups Projects
Commit 356a8f67 authored by André Sterba's avatar André Sterba
Browse files

Add representation of device routing tables

parent 806fd4d8
No related branches found
No related tags found
5 merge requests!376Add additional example application hostname-checker,!349Northbound refactoring to implement NIB concept for devices,!343Add basic application framework and example application to show interaction between events an NBI,!339Create basic venv-manager for use with arista,!324Provide prototype implementation for topology handling
Pipeline #107053 passed
package routingtables
import (
"github.com/google/uuid"
)
// Route is a routing table entry on a device
type Route struct {
ID uuid.UUID `bson:"_id"`
TargetIPRange string `bson:"target_ip_range"`
PortID uuid.UUID `bson:"port_id"`
Metric string `bson:"metric"`
}
package routingtables
import "github.com/google/uuid"
// RoutingTable is the routing table of a device
type RoutingTable struct {
ID uuid.UUID `bson:"_id"`
NodeID uuid.UUID `bson:"node_id"`
Routes Route `bson:"routes"`
}
// GetID returns the id of a routingtable
func (r RoutingTable) GetID() uuid.UUID {
return r.ID
}
package routingtables
import (
"code.fbi.h-da.de/danet/gosdn/controller/topology/nodes"
"code.fbi.h-da.de/danet/gosdn/controller/topology/ports"
"code.fbi.h-da.de/danet/gosdn/controller/topology/store"
"github.com/google/uuid"
)
// Service defines a interface for a RoutingTableService
type Service interface {
EnsureExists(RoutingTable) (RoutingTable, error)
Update(RoutingTable) error
Delete(RoutingTable) error
Get(store.Query) (RoutingTable, error)
GetAll() ([]RoutingTable, error)
}
// RoutingTableService is a RoutingTableService
type RoutingTableService struct {
store Store
nodeService nodes.Service
portService ports.Service
}
// NewRoutingTableService creates a RoutingTableService
func NewRoutingTableService(
store Store,
nodeService nodes.Service,
portService ports.Service,
) Service {
return &RoutingTableService{
store: store,
nodeService: nodeService,
portService: portService,
}
}
// EnsureExists either creates a new routingTable or returns an already existing routingTable
func (p *RoutingTableService) EnsureExists(routingTable RoutingTable) (RoutingTable, error) {
if routingTable.ID == uuid.Nil {
routingTable.ID = uuid.New()
err := p.store.Add(routingTable)
if err != nil {
return routingTable, err
}
return routingTable, nil
}
routingTable, err := p.Get(store.Query{ID: routingTable.ID})
if err != nil {
return routingTable, err
}
return routingTable, nil
}
// Update updates an existing routingTable
func (p *RoutingTableService) Update(routingTable RoutingTable) error {
err := p.store.Update(routingTable)
if err != nil {
return err
}
return nil
}
// Delete deletes a routingTable
func (p *RoutingTableService) Delete(routingTable RoutingTable) error {
err := p.store.Delete(routingTable)
if err != nil {
return err
}
return nil
}
// Get gets a routingTable
func (p *RoutingTableService) Get(query store.Query) (RoutingTable, error) {
routingTable, err := p.store.Get(query)
if err != nil {
return routingTable, err
}
return routingTable, nil
}
// GetAll gets all existing routingTables
func (p *RoutingTableService) GetAll() ([]RoutingTable, error) {
nodes, err := p.store.GetAll()
if err != nil {
return nodes, err
}
return nodes, nil
}
package routingtables
import (
"reflect"
"testing"
"code.fbi.h-da.de/danet/gosdn/controller/topology/nodes"
"code.fbi.h-da.de/danet/gosdn/controller/topology/ports"
"code.fbi.h-da.de/danet/gosdn/controller/topology/store"
"github.com/google/uuid"
)
func getTestNode() nodes.Node {
return nodes.Node{
ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"),
Name: "Test-Source-Node",
}
}
func getTestPort() ports.Port {
return ports.Port{
ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fc2"),
Name: "Test-Target-Port",
}
}
func getTestRoute() Route {
return Route{
ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05f12"),
TargetIPRange: "10.13.37.0/24",
PortID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05fc2"),
Metric: "1",
}
}
func getTestRoutingTable() RoutingTable {
return RoutingTable{
ID: uuid.MustParse("1fa479e7-d393-4d45-822d-485cc1f05f34"),
NodeID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"),
Routes: getTestRoute(),
}
}
func getTestStoreWithRoutingTables(t *testing.T, routingTables []RoutingTable) Store {
store := store.NewGenericStore[RoutingTable]()
for _, rt := range routingTables {
err := store.Add(rt)
if err != nil {
t.Fatalf("failed to prepare test store while adding routing table: %v", err)
}
}
return store
}
func getTestStoreWithNodes(t *testing.T, nodesToAdd []nodes.Node) nodes.Store {
store := store.NewGenericStore[nodes.Node]()
for _, node := range nodesToAdd {
err := store.Add(node)
if err != nil {
t.Fatalf("failed to prepare test store while adding node: %v", err)
}
}
return store
}
func getTestStoreWithPorts(t *testing.T, portsToAdd []ports.Port) ports.Store {
store := store.NewGenericStore[ports.Port]()
for _, port := range portsToAdd {
err := store.Add(port)
if err != nil {
t.Fatalf("failed to prepare test store while adding port: %v", err)
}
}
return store
}
func TestNewRoutingTableService(t *testing.T) {
type args struct {
store Store
nodeService nodes.Service
portService ports.Service
}
tests := []struct {
name string
args args
want Service
}{
{
name: "should create a new topology service",
args: args{
store: getTestStoreWithRoutingTables(t, []RoutingTable{}),
nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{})),
portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{})),
},
want: NewRoutingTableService(
getTestStoreWithRoutingTables(t, []RoutingTable{}),
nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{})),
ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{})),
),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewRoutingTableService(tt.args.store, tt.args.nodeService, tt.args.portService); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewNodeService() = %v, want %v", got, tt.want)
}
})
}
}
func TestTopologyService_EnsureExists(t *testing.T) {
type fields struct {
store Store
nodeService nodes.Service
portService ports.Service
}
type args struct {
routingTable RoutingTable
}
tests := []struct {
name string
fields fields
args args
want RoutingTable
wantErr bool
}{
{
name: "should if a routing table that is not in the store",
fields: fields{
store: getTestStoreWithRoutingTables(t, []RoutingTable{}),
nodeService: nodes.NewNodeService(getTestStoreWithNodes(
t,
[]nodes.Node{},
)),
portService: ports.NewPortService(getTestStoreWithPorts(
t,
[]ports.Port{},
)),
},
args: args{
routingTable: getTestRoutingTable(),
},
want: RoutingTable{},
wantErr: true,
},
{
name: "should return routing table that is in the store",
fields: fields{
store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}),
nodeService: nodes.NewNodeService(getTestStoreWithNodes(
t,
[]nodes.Node{getTestNode()},
)),
portService: ports.NewPortService(getTestStoreWithPorts(
t,
[]ports.Port{getTestPort()},
)),
},
args: args{
routingTable: getTestRoutingTable(),
},
want: getTestRoutingTable(),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &RoutingTableService{
store: tt.fields.store,
nodeService: tt.fields.nodeService,
portService: tt.fields.portService,
}
got, err := p.EnsureExists(tt.args.routingTable)
if (err != nil) != tt.wantErr {
t.Errorf("RoutingTableService.EnsureExists() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("RoutingTableService.EnsureExists() = %v, want %v", got, tt.want)
}
})
}
}
func TestRoutingTableService_Update(t *testing.T) {
type fields struct {
store Store
nodeService nodes.Service
portService ports.Service
}
type args struct {
routingTable RoutingTable
}
tests := []struct {
name string
fields fields
args args
want RoutingTable
wantErr bool
}{
{
name: "should update an existing routing table",
fields: fields{
store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}),
nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{})),
portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{})),
},
args: args{
routingTable: getTestRoutingTable(),
},
want: getTestRoutingTable(),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &RoutingTableService{
store: tt.fields.store,
nodeService: tt.fields.nodeService,
portService: tt.fields.portService,
}
if err := p.Update(tt.args.routingTable); (err != nil) != tt.wantErr {
t.Errorf("RoutingTableService.Update() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestRoutingTableService_Delete(t *testing.T) {
type fields struct {
store Store
nodeService nodes.Service
portService ports.Service
}
type args struct {
routingTable RoutingTable
}
tests := []struct {
name string
fields fields
args args
want RoutingTable
wantErr bool
}{
{
name: "should delete an existing routing table",
fields: fields{
store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}),
nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{})),
portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{})),
},
args: args{
routingTable: getTestRoutingTable(),
},
wantErr: false,
},
{
name: "should fail if a routing table does not exists",
fields: fields{
store: store.NewGenericStore[RoutingTable](),
nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{})),
portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{})),
},
args: args{
routingTable: getTestRoutingTable(),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &RoutingTableService{
store: tt.fields.store,
nodeService: tt.fields.nodeService,
portService: tt.fields.portService,
}
if err := p.Delete(tt.args.routingTable); (err != nil) != tt.wantErr {
t.Errorf("RoutingTableService.Delete() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestRoutingTableService_Get(t *testing.T) {
type fields struct {
store Store
nodeService nodes.Service
portService ports.Service
}
type args struct {
query store.Query
}
tests := []struct {
name string
fields fields
args args
want RoutingTable
wantErr bool
}{
{
name: "should error if routing table with uuid is not in store",
fields: fields{
store: getTestStoreWithRoutingTables(t, []RoutingTable{}),
nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{})),
portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{})),
},
args: args{
query: store.Query{
ID: getTestRoutingTable().ID,
},
},
want: RoutingTable{},
wantErr: true,
},
{
name: "should return routing table that is in the store",
fields: fields{
store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}),
},
args: args{
query: store.Query{
ID: getTestRoutingTable().ID,
},
},
want: getTestRoutingTable(),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &RoutingTableService{
store: tt.fields.store,
nodeService: tt.fields.nodeService,
portService: tt.fields.portService,
}
got, err := p.Get(tt.args.query)
if (err != nil) != tt.wantErr {
t.Errorf("RoutingTableService.Get() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("RoutingTableService.Get() = %v, want %v", got, tt.want)
}
})
}
}
func TestRoutingTableService_GetAll(t *testing.T) {
type fields struct {
store Store
nodeService nodes.Service
portService ports.Service
}
tests := []struct {
name string
fields fields
want []RoutingTable
wantErr bool
}{
{
name: "should get all stored routing tables",
fields: fields{
store: getTestStoreWithRoutingTables(t, []RoutingTable{getTestRoutingTable()}),
nodeService: nodes.NewNodeService(getTestStoreWithNodes(t, []nodes.Node{})),
portService: ports.NewPortService(getTestStoreWithPorts(t, []ports.Port{})),
},
want: []RoutingTable{getTestRoutingTable()},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &RoutingTableService{
store: tt.fields.store,
nodeService: tt.fields.nodeService,
portService: tt.fields.portService,
}
got, err := p.GetAll()
if (err != nil) != tt.wantErr {
t.Errorf("RoutingTableService.GetAll() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("RoutingTableService.GetAll() = %v, want %v", got, tt.want)
}
})
}
}
package routingtables
import (
"fmt"
"log"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/database"
"code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors"
"code.fbi.h-da.de/danet/gosdn/controller/topology/store"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
// Store defines a RoutingTable store interface
type Store interface {
Add(RoutingTable) error
Update(RoutingTable) error
Delete(RoutingTable) error
Get(store.Query) (RoutingTable, error)
GetAll() ([]RoutingTable, error)
}
// DatabaseRoutingTableStore is a database store for routingTables
type DatabaseRoutingTableStore struct {
storeName string
}
// NewDatabaseRoutingTableStore returns a RoutingTableStore
func NewDatabaseRoutingTableStore() Store {
return &DatabaseRoutingTableStore{
storeName: fmt.Sprintf("routing-table-store.json"),
}
}
// Get takes a routing-tables's UUID or name and returns the entries.
func (s *DatabaseRoutingTableStore) Get(query store.Query) (RoutingTable, error) {
var loadedRoutingTable RoutingTable
if query.ID.String() != "" {
loadedRoutingTable, err := s.getByID(query.ID)
if err != nil {
return loadedRoutingTable, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name}
}
return loadedRoutingTable, nil
}
loadedRoutingTable, err := s.getByName(query.Name)
if err != nil {
return loadedRoutingTable, errors.ErrCouldNotFind{ID: query.ID, Name: query.Name}
}
return loadedRoutingTable, nil
}
func (s *DatabaseRoutingTableStore) getByID(idOfRoutingTable uuid.UUID) (RoutingTable, error) {
var RoutingTable RoutingTable
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfRoutingTable.String()}})
if result == nil {
return RoutingTable, errors.ErrCouldNotFind{ID: idOfRoutingTable}
}
err := result.Decode(&RoutingTable)
if err != nil {
log.Printf("Failed marshalling %v", err)
return RoutingTable, errors.ErrCouldNotFind{ID: idOfRoutingTable}
}
return RoutingTable, nil
}
func (s *DatabaseRoutingTableStore) getByName(nameOfRoutingTable string) (RoutingTable, error) {
var loadedRoutingTable RoutingTable
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfRoutingTable}})
if result == nil {
return loadedRoutingTable, errors.ErrCouldNotFind{Name: nameOfRoutingTable}
}
err := result.Decode(&loadedRoutingTable)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedRoutingTable, errors.ErrCouldNotFind{Name: nameOfRoutingTable}
}
return loadedRoutingTable, nil
}
// GetAll returns all stored routingTables.
func (s *DatabaseRoutingTableStore) GetAll() ([]RoutingTable, error) {
var loadedRoutingTable []RoutingTable
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
cursor, err := collection.Find(ctx, bson.D{})
if err != nil {
return []RoutingTable{}, err
}
defer cursor.Close(ctx)
err = cursor.All(ctx, &loadedRoutingTable)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedRoutingTable, errors.ErrCouldNotMarshall{Type: loadedRoutingTable, Err: err}
}
return loadedRoutingTable, nil
}
// Add adds a RoutingTable to the store.
func (s *DatabaseRoutingTableStore) Add(routingTable RoutingTable) error {
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
_, err := client.Database(database.DatabaseName).
Collection(s.storeName).
InsertOne(ctx, routingTable)
if err != nil {
log.Printf("Could not add Node: %v", err)
return errors.ErrCouldNotCreate{Identifier: routingTable.ID, Type: routingTable, Err: err}
}
return nil
}
// Update updates a existing routingTable.
func (s *DatabaseRoutingTableStore) Update(routingTable RoutingTable) error {
var updatedLoadedRoutingTable RoutingTable
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
update := bson.D{primitive.E{Key: "$set", Value: routingTable}}
upsert := false
after := options.After
opt := options.FindOneAndUpdateOptions{
Upsert: &upsert,
ReturnDocument: &after,
}
err := client.Database(database.DatabaseName).
Collection(s.storeName).
FindOneAndUpdate(
ctx, bson.M{"_id": routingTable.ID.String()}, update, &opt).
Decode(&updatedLoadedRoutingTable)
if err != nil {
log.Printf("Could not update Node: %v", err)
return errors.ErrCouldNotUpdate{Identifier: routingTable.ID, Type: routingTable, Err: err}
}
return nil
}
// Delete deletes a node from the node store.
func (s *DatabaseRoutingTableStore) Delete(routingTable RoutingTable) error {
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
db := client.Database(database.DatabaseName)
collection := db.Collection(s.storeName)
_, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: routingTable.ID.String()}})
if err != nil {
return err
}
return nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment