Skip to content
Snippets Groups Projects
topology.go 5.29 KiB
Newer Older
  • Learn to ignore specific revisions
  • package server
    
    import (
    	"context"
    	"time"
    
    	topopb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/topology"
    	query "code.fbi.h-da.de/danet/gosdn/controller/store"
    	"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"
    	"code.fbi.h-da.de/danet/gosdn/controller/topology/ports/configuration"
    
    	"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 {
    	topopb.UnimplementedTopologyServiceServer
    	topologyService topology.Service
    	nodeService     nodes.Service
    	portService     ports.Service
    }
    
    // NewTopologyServer receives a topologyService and returns a new TopologyServer.
    func NewTopologyServer(
    	service topology.Service,
    	nodeService nodes.Service,
    	portService ports.Service,
    ) *Topology {
    	return &Topology{
    		topologyService: service,
    		nodeService:     nodeService,
    		portService:     portService,
    	}
    }
    
    // AddLink adds a new link to the topology
    func (t *Topology) AddLink(ctx context.Context, request *topopb.AddLinkRequest) (*topopb.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 = t.topologyService.AddLink(link)
    	if err != nil {
    		return nil, status.Errorf(codes.Aborted, "%v", err)
    	}
    
    	return &topopb.AddLinkResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    topopb.Status_STATUS_OK,
    	}, nil
    }
    
    // GetTopology returns the current topology in the form of all links
    func (t *Topology) GetTopology(ctx context.Context, request *topopb.GetTopologyRequest) (*topopb.GetTopologyResponse, error) {
    	topo, err := t.topologyService.GetAll()
    	if err != nil {
    		return nil, status.Errorf(codes.Aborted, "%v", err)
    	}
    
    	topology := &topopb.Topology{}
    
    	for _, link := range topo {
    		topology.Links = append(topology.Links, &topopb.Link{
    			Id:   link.ID.String(),
    			Name: link.Name,
    			SourceNode: &topopb.Node{
    				Id:   link.SourceNode.ID.String(),
    				Name: link.SourceNode.Name,
    			},
    			SourcePort: &topopb.Port{
    				Id:   link.SourcePort.ID.String(),
    				Name: link.SourcePort.Name,
    				Configuration: &topopb.Configuration{
    					Ip:           link.SourcePort.Configuration.IP.String(),
    					PrefixLength: link.SourcePort.Configuration.PrefixLength,
    				},
    			},
    			TargetNode: &topopb.Node{
    				Id:   link.TargetNode.ID.String(),
    				Name: link.TargetNode.Name,
    			},
    			TargetPort: &topopb.Port{
    				Id:   link.TargetPort.ID.String(),
    				Name: link.TargetPort.Name,
    				Configuration: &topopb.Configuration{
    					Ip:           link.TargetPort.Configuration.IP.String(),
    					PrefixLength: link.TargetPort.Configuration.PrefixLength,
    				},
    			},
    		})
    	}
    
    	return &topopb.GetTopologyResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    topopb.Status_STATUS_OK,
    		Toplogy:   topology,
    	}, nil
    }
    
    // DeleteLink deletes a link
    func (t *Topology) DeleteLink(ctx context.Context, request *topopb.DeleteLinkRequest) (*topopb.DeleteLinkResponse, error) {
    	linkID, err := uuid.Parse(request.Id)
    	if err != nil {
    		return &topopb.DeleteLinkResponse{
    			Timestamp: time.Now().UnixNano(),
    			Status:    topopb.Status_STATUS_ERROR,
    		}, err
    	}
    
    	foundLink, err := t.topologyService.Get(query.Query{ID: linkID})
    	if err != nil {
    		return &topopb.DeleteLinkResponse{
    			Timestamp: time.Now().UnixNano(),
    			Status:    topopb.Status_STATUS_ERROR,
    		}, err
    	}
    
    	err = t.topologyService.DeleteLink(foundLink)
    	if err != nil {
    		return &topopb.DeleteLinkResponse{
    			Timestamp: time.Now().UnixNano(),
    			Status:    topopb.Status_STATUS_ERROR,
    		}, err
    	}
    
    	return &topopb.DeleteLinkResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    topopb.Status_STATUS_OK,
    	}, nil
    }
    
    func (t *Topology) ensureNodeAndPortExists(incomingNode *topopb.Node, incomingPort *topopb.Port) (nodes.Node, ports.Port, error) {
    	node, err := t.nodeService.EnsureExists(
    		nodes.Node{
    			ID:   getExistingOrCreateNilUUIDFromString(incomingNode.Id),
    			Name: incomingNode.Name,
    		},
    	)
    	if err != nil {
    		return node, ports.Port{}, status.Errorf(codes.Aborted, "%v", err)
    	}
    
    	portConf, err := configuration.New(incomingPort.Configuration.Ip, incomingPort.Configuration.PrefixLength)
    	if err != nil {
    		return node, ports.Port{}, status.Errorf(codes.Aborted, "%v", err)
    	}
    	port, err := t.portService.EnsureExists(
    		ports.Port{
    			ID:            getExistingOrCreateNilUUIDFromString(incomingPort.Id),
    			Name:          incomingPort.Name,
    			Configuration: portConf,
    		},
    	)
    	if err != nil {
    		return nodes.Node{}, port, status.Errorf(codes.Aborted, "%v", err)
    	}
    
    	return node, port, nil
    }
    
    func getExistingOrCreateNilUUIDFromString(id string) uuid.UUID {
    	if len(id) == 0 {
    		return uuid.Nil
    	}
    
    	parsedID, err := uuid.Parse(id)
    	if err != nil {
    		return uuid.Nil
    	}
    
    	return parsedID
    }