diff --git a/controller/northbound/server/nbi.go b/controller/northbound/server/nbi.go index 040407e326693301a4e8050cf004185f70b60748..0cfae4ed240f193a95127d6643041c48a2b3b2d4 100644 --- a/controller/northbound/server/nbi.go +++ b/controller/northbound/server/nbi.go @@ -4,6 +4,9 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" "code.fbi.h-da.de/danet/gosdn/controller/metrics" + "code.fbi.h-da.de/danet/gosdn/controller/topology" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" "github.com/prometheus/client_golang/prometheus" log "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" @@ -13,32 +16,48 @@ import ( var pndc networkdomain.PndStore var userService rbac.UserService var roleService rbac.RoleService +var topologyService topology.Service +var nodeService nodes.Service +var portService ports.Service // NorthboundInterface is the representation of the // gRPC services used provided. type NorthboundInterface struct { - Pnd *pndServer - Core *core - Csbi *csbi - Sbi *sbiServer - Auth *Auth - User *User - Role *Role + Pnd *pndServer + Core *core + Csbi *csbi + Sbi *sbiServer + Auth *Auth + User *User + Role *Role + Topology *Topology } // NewNBI receives a PndStore and returns a new gRPC *NorthboundInterface -func NewNBI(pnds networkdomain.PndStore, users rbac.UserService, roles rbac.RoleService) *NorthboundInterface { +func NewNBI( + pnds networkdomain.PndStore, + users rbac.UserService, + roles rbac.RoleService, + topology topology.Service, + node nodes.Service, + port ports.Service, +) *NorthboundInterface { pndc = pnds userService = users roleService = roles + topologyService = topology + nodeService = node + portService = port + return &NorthboundInterface{ - Pnd: &pndServer{}, - Core: &core{}, - Csbi: &csbi{}, - Sbi: &sbiServer{}, - Auth: &Auth{}, - User: &User{}, - Role: &Role{}, + Pnd: &pndServer{}, + Core: &core{}, + Csbi: &csbi{}, + Sbi: &sbiServer{}, + Auth: &Auth{}, + User: &User{}, + Role: &Role{}, + Topology: &Topology{}, } } diff --git a/controller/northbound/server/topology.go b/controller/northbound/server/topology.go new file mode 100644 index 0000000000000000000000000000000000000000..0f4c47e5d5e96fd2b1f6fa5fe91c9aef1c7b4632 --- /dev/null +++ b/controller/northbound/server/topology.go @@ -0,0 +1,127 @@ +package server + +import ( + "context" + "time" + + apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology" + "code.fbi.h-da.de/danet/gosdn/controller/topology" + "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Topology holds a topologyService and represents a TopologyServiceServer. +type Topology struct { + apb.UnimplementedTopologyServiceServer + topologyService topology.Service +} + +// NewTopologyrServer receives a topologyService and returns a new TopologyServer. +func NewTopologyrServer(service topology.Service) *Topology { + return &Topology{ + topologyService: service, + } +} + +// AddLink adds a new link to the topology +func (t Topology) AddLink(ctx context.Context, request *apb.AddLinkRequest) (*apb.AddLinkResponse, error) { + sourceNode, sourcePort, err := t.ensureNodeAndPortExists(request.Link.SourceNode, request.Link.SourcePort) + if err != nil { + return nil, status.Errorf(codes.Aborted, "%v", err) + } + + targetNode, targetPort, err := t.ensureNodeAndPortExists(request.Link.TargetNode, request.Link.TargetPort) + if err != nil { + return nil, status.Errorf(codes.Aborted, "%v", err) + } + + link := links.Link{ + ID: uuid.New(), + Name: request.Link.Name, + SourceNode: sourceNode, + SourcePort: sourcePort, + TargetNode: targetNode, + TargetPort: targetPort, + } + err = topologyService.AddLink(link) + if err != nil { + return nil, status.Errorf(codes.Aborted, "%v", err) + } + + return &apb.AddLinkResponse{ + Timestamp: time.Now().UnixNano(), + Status: apb.Status_STATUS_OK, + }, nil +} + +// GetTopology returns the current topology in the form of all links +func (t Topology) GetTopology(ctx context.Context, request *apb.GetTopologyRequest) (*apb.GetTopologyResponse, error) { + topo, err := topologyService.GetAll() + if err != nil { + return nil, status.Errorf(codes.Aborted, "%v", err) + } + + topology := &apb.Topology{} + + for _, link := range topo { + topology.Links = append(topology.Links, &apb.Link{ + Id: link.ID.String(), + Name: link.Name, + SourceNode: &apb.Node{ + Id: link.SourceNode.ID.String(), + Name: link.SourceNode.Name, + }, + SourcePort: &apb.Port{ + Id: link.SourcePort.ID.String(), + }, + TargetNode: &apb.Node{ + Id: link.TargetNode.ID.String(), + Name: link.TargetNode.Name, + }, + TargetPort: &apb.Port{ + Id: link.TargetPort.ID.String(), + }, + }) + } + + return &apb.GetTopologyResponse{ + Timestamp: time.Now().UnixNano(), + Status: apb.Status_STATUS_OK, + Toplogy: topology, + }, nil +} + +func (t Topology) ensureNodeAndPortExists(incomingNode *apb.Node, incomingPort *apb.Port) (nodes.Node, ports.Port, error) { + node, err := nodeService.EnsureExists( + nodes.Node{ + ID: getExistingOrCreateNewUUIDFromString(incomingNode.Id), + }, + ) + if err != nil { + return node, ports.Port{}, status.Errorf(codes.Aborted, "%v", err) + } + + port, err := portService.EnsureExists( + ports.Port{ + ID: getExistingOrCreateNewUUIDFromString(incomingPort.Id), + }, + ) + if err != nil { + return nodes.Node{}, port, status.Errorf(codes.Aborted, "%v", err) + } + + return node, port, nil +} + +func getExistingOrCreateNewUUIDFromString(id string) uuid.UUID { + parsedID, err := uuid.Parse(id) + if err != nil { + return uuid.Nil + } + + return parsedID +}