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

Add topology related structures and services

parent 2bd33971
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
This commit is part of merge request !343. Comments created here will be created in the context of that merge request.
package links
import (
"code.fbi.h-da.de/danet/gosdn/controller/topology/nodes"
"code.fbi.h-da.de/danet/gosdn/controller/topology/ports"
"github.com/google/uuid"
)
// Link is a representation of a physical or virtual link between two nodes and their ports
type Link struct {
ID uuid.UUID `bson:"_id"`
Name string `bson:"name,omitempty"`
SourceNode nodes.Node `bson:"source_node,omitempty"`
TargetNode nodes.Node `bson:"target_node,omitempty"`
SourcePort ports.Port `bson:"source_port,omitempty"`
TargetPort ports.Port `bson:"target_port,omitempty"`
}
package nodes
import "github.com/google/uuid"
// Node is a representation of a network element
type Node struct {
ID uuid.UUID `bson:"_id"`
Name string `bson:"name"`
}
package nodes
import (
"code.fbi.h-da.de/danet/gosdn/controller/store"
"github.com/google/uuid"
)
// Service defines a interface for a NodeService
type Service interface {
EnsureExists(Node) (Node, error)
Update(Node) error
Delete(Node) error
Get(store.Query) (Node, error)
GetAll() ([]Node, error)
}
// NodeService is a NodeService
type NodeService struct {
store Store
}
// NewNodeService creates a NodeService
func NewNodeService(store Store) Service {
return &NodeService{
store: store,
}
}
// EnsureExists either creates a new node or returns an already existing node
func (p *NodeService) EnsureExists(node Node) (Node, error) {
if node.ID == uuid.Nil {
node.ID = uuid.New()
err := p.store.Add(node)
if err != nil {
return node, err
}
return node, nil
}
node, err := p.Get(store.Query{ID: node.ID})
if err != nil {
return node, err
}
return node, nil
}
// Update updates an existing node
func (p *NodeService) Update(node Node) error {
err := p.store.Update(node)
if err != nil {
return err
}
return nil
}
// Delete deletes a node
func (p *NodeService) Delete(node Node) error {
err := p.store.Delete(node)
if err != nil {
return err
}
return nil
}
// Get gets a node
func (p *NodeService) Get(query store.Query) (Node, error) {
node, err := p.store.Get(query)
if err != nil {
return node, err
}
return node, nil
}
// GetAll gets all existing nodes
func (p *NodeService) GetAll() ([]Node, error) {
nodes, err := p.store.GetAll()
if err != nil {
return nodes, err
}
return nodes, nil
}
package nodes
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/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 NodeStore interface
type Store interface {
Add(Node) error
Update(Node) error
Delete(Node) error
Get(store.Query) (Node, error)
GetAll() ([]Node, error)
}
// DatabaseNodeStore is a database store for nodes
type DatabaseNodeStore struct {
storeName string
}
// NewDatabaseNodeStore returns a NodeStore
func NewDatabaseNodeStore() Store {
return &DatabaseNodeStore{
storeName: fmt.Sprintf("node-store.json"),
}
}
// Get takes a nodes's UUID or name and returns the nodes.
func (s *DatabaseNodeStore) Get(query store.Query) (Node, error) {
var loadedNode Node
if query.ID.String() != "" {
loadedNode, err := s.getByID(query.ID)
if err != nil {
return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedNode, nil
}
loadedNode, err := s.getByName(query.Name)
if err != nil {
return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedNode, nil
}
func (s *DatabaseNodeStore) getByID(idOfLink uuid.UUID) (Node, error) {
var loadedNode Node
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: idOfLink.String()}})
if result == nil {
return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName}
}
err := result.Decode(&loadedNode)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedNode, nil
}
func (s *DatabaseNodeStore) getByName(nameOfDevice string) (Node, error) {
var loadedNode Node
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: nameOfDevice}})
if result == nil {
return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName}
}
err := result.Decode(&loadedNode)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedNode, nil
}
// GetAll returns all stored nodes.
func (s *DatabaseNodeStore) GetAll() ([]Node, error) {
var loadedNode []Node
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 []Node{}, err
}
defer cursor.Close(ctx)
err = cursor.All(ctx, &loadedNode)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedNode, errors.ErrCouldNotMarshall{StoreName: s.storeName}
}
return loadedNode, nil
}
// Add adds a node to the node store.
func (s *DatabaseNodeStore) Add(node Node) error {
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
_, err := client.Database(database.DatabaseName).
Collection(s.storeName).
InsertOne(ctx, node)
if err != nil {
log.Printf("Could not add Node: %v", err)
return errors.ErrCouldNotCreate{StoreName: s.storeName}
}
return nil
}
// Update updates a existing node.
func (s *DatabaseNodeStore) Update(node Node) error {
var updatedLoadedNodes Node
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
update := bson.D{primitive.E{Key: "$set", Value: node}}
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": node.ID.String()}, update, &opt).
Decode(&updatedLoadedNodes)
if err != nil {
log.Printf("Could not update Node: %v", err)
return errors.ErrCouldNotUpdate{StoreName: s.storeName}
}
return nil
}
// Delete deletes a node from the node store.
func (s *DatabaseNodeStore) Delete(node Node) 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: node.ID.String()}})
if err != nil {
return err
}
return nil
}
package ports
import "github.com/google/uuid"
// Port is a representation of physical port on a network element
type Port struct {
ID uuid.UUID `bson:"_id"`
Name string `bson:"name,omitempty"`
}
package ports
import (
"code.fbi.h-da.de/danet/gosdn/controller/store"
"github.com/google/uuid"
)
// Service defines an interface for a PortService
type Service interface {
EnsureExists(Port) (Port, error)
Update(Port) error
Delete(Port) error
Get(store.Query) (Port, error)
GetAll() ([]Port, error)
}
// PortService is a service for ports
type PortService struct {
store Store
}
// NewPortService creates a new PortService
func NewPortService(store Store) Service {
return &PortService{
store: store,
}
}
// EnsureExists either creates a new port or returns an already existing port
func (p *PortService) EnsureExists(port Port) (Port, error) {
if port.ID == uuid.Nil {
port.ID = uuid.New()
err := p.store.Add(port)
if err != nil {
return port, err
}
return port, nil
}
port, err := p.Get(store.Query{ID: port.ID})
if err != nil {
return port, err
}
return port, nil
}
// Update updates an existing port
func (p *PortService) Update(port Port) error {
err := p.store.Update(port)
if err != nil {
return err
}
return nil
}
// Delete deletes a port
func (p *PortService) Delete(port Port) error {
err := p.store.Delete(port)
if err != nil {
return err
}
return nil
}
// Get gets a port
func (p *PortService) Get(query store.Query) (Port, error) {
port, err := p.store.Get(query)
if err != nil {
return port, err
}
return port, nil
}
// GetAll gets all existing ports
func (p *PortService) GetAll() ([]Port, error) {
nodes, err := p.store.GetAll()
if err != nil {
return nodes, err
}
return nodes, nil
}
package ports
import (
"fmt"
"log"
"code.fbi.h-da.de/danet/gosdn/controller/interfaces/device"
"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/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 PortStore interface
type Store interface {
Add(Port) error
Update(Port) error
Delete(Port) error
Get(store.Query) (Port, error)
GetAll() ([]Port, error)
}
// DatabasePortStore is a database store for ports
type DatabasePortStore struct {
storeName string
}
// NewDatabasePortStore returns a PortStore
func NewDatabasePortStore() Store {
return &DatabasePortStore{
storeName: fmt.Sprintf("port-store.json"),
}
}
// Get takes a Ports's UUID or name and returns the port.
func (s *DatabasePortStore) Get(query store.Query) (Port, error) {
var loadedPort Port
if query.ID.String() != "" {
loadedPort, err := s.getByID(query.ID)
if err != nil {
return loadedPort, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedPort, nil
}
loadedPort, err := s.getByName(query.Name)
if err != nil {
return loadedPort, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedPort, nil
}
func (s *DatabasePortStore) getByID(idOfLink uuid.UUID) (Port, error) {
var loadedPort Port
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: idOfLink.String()}})
if result == nil {
return loadedPort, errors.ErrCouldNotFind{StoreName: s.storeName}
}
err := result.Decode(&loadedPort)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedPort, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedPort, nil
}
func (s *DatabasePortStore) getByName(nameOfDevice string) (Port, error) {
var loadedDevice Port
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: nameOfDevice}})
if result == nil {
return loadedDevice, errors.ErrCouldNotFind{StoreName: s.storeName}
}
err := result.Decode(&loadedDevice)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedDevice, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedDevice, nil
}
// GetAll returns all stored ports.
func (s *DatabasePortStore) GetAll() ([]Port, error) {
var loadedPort []Port
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 []Port{}, err
}
defer cursor.Close(ctx)
err = cursor.All(ctx, &loadedPort)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedPort, errors.ErrCouldNotMarshall{StoreName: s.storeName}
}
return loadedPort, nil
}
// Add adds a port to the port store.
func (s *DatabasePortStore) Add(port Port) error {
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
_, err := client.Database(database.DatabaseName).
Collection(s.storeName).
InsertOne(ctx, port)
if err != nil {
log.Printf("Could not add Port: %v", err)
return errors.ErrCouldNotCreate{StoreName: s.storeName}
}
return nil
}
// Update updates a existing port.
func (s *DatabasePortStore) Update(port Port) error {
var updatedLoadedDevice device.LoadedDevice
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
update := bson.D{primitive.E{Key: "$set", Value: port}}
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": port.ID.String()}, update, &opt).
Decode(&updatedLoadedDevice)
if err != nil {
log.Printf("Could not update Port: %v", err)
return errors.ErrCouldNotUpdate{StoreName: s.storeName}
}
return nil
}
// Delete deletes a port from the port store.
func (s *DatabasePortStore) Delete(port Port) 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: port.ID.String()}})
if err != nil {
return err
}
return nil
}
package topology
package topology
import "code.fbi.h-da.de/danet/gosdn/controller/topology/links"
// TService defines an interface for a TopologyService
type TService interface {
AddLink(links.Link) error
UpdateLink(links.Link) error
DeleteLink(links.Link) error
GetAll() ([]links.Link, error)
}
// Service is a service for ports
type Service struct {
store Store
}
// NewTopologyService creates a new TopologyService
func NewTopologyService(store Store) TService {
return &Service{
store: store,
}
}
// AddLink adds a new link to the topology
func (t *Service) AddLink(link links.Link) error {
err := t.store.AddLink(link)
if err != nil {
return err
}
return nil
}
// UpdateLink updates an existing link
func (t *Service) UpdateLink(link links.Link) error {
err := t.store.UpdateLink(link)
if err != nil {
return err
}
return nil
}
// DeleteLink deletes a link
func (t *Service) DeleteLink(link links.Link) error {
err := t.store.DeleteLink(link)
if err != nil {
return err
}
return nil
}
// GetAll returns the current topology
func (t *Service) GetAll() ([]links.Link, error) {
topo, err := t.store.GetAll()
if err != nil {
return topo, err
}
return topo, nil
}
package topology
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/store"
"code.fbi.h-da.de/danet/gosdn/controller/topology/links"
"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 Topology store interface
type Store interface {
AddLink(links.Link) error
UpdateLink(links.Link) error
DeleteLink(links.Link) error
Get(store.Query) (links.Link, error)
GetAll() ([]links.Link, error)
}
// DatabaseTopologyStore is a database store for the topology
type DatabaseTopologyStore struct {
storeName string
}
// NewDatabaseTopologyStore returns a TopologyStore
func NewDatabaseTopologyStore() Store {
return &DatabaseTopologyStore{
storeName: fmt.Sprintf("topology-store.json"),
}
}
// Get takes a link's UUID or name and returns the link.
func (s *DatabaseTopologyStore) Get(query store.Query) (links.Link, error) {
var loadedTopology links.Link
if query.ID.String() != "" {
loadedDevice, err := s.getByID(query.ID)
if err != nil {
return loadedDevice, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedDevice, nil
}
loadedTopology, err := s.getByName(query.Name)
if err != nil {
return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedTopology, nil
}
func (s *DatabaseTopologyStore) getByID(idOfLink uuid.UUID) (links.Link, error) {
var loadedTopology links.Link
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: idOfLink.String()}})
if result == nil {
return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName}
}
err := result.Decode(&loadedTopology)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedTopology, nil
}
func (s *DatabaseTopologyStore) getByName(nameOfDevice string) (links.Link, error) {
var loadedTopology links.Link
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: nameOfDevice}})
if result == nil {
return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName}
}
err := result.Decode(&loadedTopology)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName}
}
return loadedTopology, nil
}
// GetAll returns all stored links.
func (s *DatabaseTopologyStore) GetAll() ([]links.Link, error) {
var loadedTopology []links.Link
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 loadedTopology, err
}
defer cursor.Close(ctx)
err = cursor.All(ctx, &loadedTopology)
if err != nil {
log.Printf("Failed marshalling %v", err)
return loadedTopology, errors.ErrCouldNotMarshall{StoreName: s.storeName}
}
return loadedTopology, nil
}
// AddLink adds a link to the link store.
func (s *DatabaseTopologyStore) AddLink(link links.Link) error {
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
_, err := client.Database(database.DatabaseName).
Collection(s.storeName).
InsertOne(ctx, link)
if err != nil {
log.Printf("Could not add Link: %v", err)
return errors.ErrCouldNotCreate{StoreName: s.storeName}
}
return nil
}
// UpdateLink updates a existing link.
func (s *DatabaseTopologyStore) UpdateLink(linkToUpdate links.Link) error {
var updatedLink links.Link
client, ctx, cancel := database.GetMongoConnection()
defer cancel()
defer client.Disconnect(ctx)
update := bson.D{primitive.E{Key: "$set", Value: linkToUpdate}}
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": linkToUpdate.ID.String()}, update, &opt).
Decode(&updatedLink)
if err != nil {
log.Printf("Could not update link: %v", err)
return errors.ErrCouldNotUpdate{StoreName: s.storeName}
}
return nil
}
// DeleteLink deletes a link from the link store.
func (s *DatabaseTopologyStore) DeleteLink(linkToDelete links.Link) 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: linkToDelete.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 register or to comment