Skip to content
Snippets Groups Projects
topology.go 5.82 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/bufbuild/protovalidate-go"
    
    	"github.com/google/uuid"
    	"google.golang.org/grpc/codes"
    	"google.golang.org/grpc/status"
    
    	"google.golang.org/protobuf/reflect/protoreflect"
    
    // TopologyServer holds a topologyService and represents a TopologyServiceServer.
    type TopologyServer struct {
    
    	topopb.UnimplementedTopologyServiceServer
    	topologyService topology.Service
    	nodeService     nodes.Service
    	portService     ports.Service
    
    	protoValidator  *protovalidate.Validator
    
    }
    
    // NewTopologyServer receives a topologyService and returns a new TopologyServer.
    func NewTopologyServer(
    	service topology.Service,
    	nodeService nodes.Service,
    	portService ports.Service,
    
    	protoValidator *protovalidate.Validator,
    
    ) *TopologyServer {
    	return &TopologyServer{
    
    		topologyService: service,
    		nodeService:     nodeService,
    		portService:     portService,
    
    		protoValidator:  protoValidator,
    
    func (t TopologyServer) checkForValidationErrors(request protoreflect.ProtoMessage) error {
    	err := t.protoValidator.Validate(request)
    	if err != nil {
    		var valErr *protovalidate.ValidationError
    
    		if ok := errors.As(err, &valErr); ok {
    			protoErr := valErr.ToProto()
    			grpcError, _ := status.New(codes.Aborted, "Validation failed").WithDetails(protoErr)
    
    			return grpcError.Err()
    		}
    
    		return status.Errorf(codes.Aborted, "%v", err)
    	}
    
    	return nil
    }
    
    
    // AddLink adds a new link to the topology.
    
    func (t *TopologyServer) AddLink(ctx context.Context, request *topopb.AddLinkRequest) (*topopb.AddLinkResponse, error) {
    
    	err := t.checkForValidationErrors(request)
    	if err != nil {
    		return nil, err
    
    	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(),
    	}, nil
    }
    
    
    // GetTopology returns the current topology in the form of all links.
    
    func (t *TopologyServer) GetTopology(ctx context.Context, request *topopb.GetTopologyRequest) (*topopb.GetTopologyResponse, error) {
    
    	err := t.checkForValidationErrors(request)
    	if err != nil {
    		return nil, err
    
    	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(),
    		Toplogy:   topology,
    	}, nil
    }
    
    
    func (t *TopologyServer) DeleteLink(ctx context.Context, request *topopb.DeleteLinkRequest) (*topopb.DeleteLinkResponse, error) {
    
    	err := t.checkForValidationErrors(request)
    	if err != nil {
    		return nil, err
    
    	linkID, err := uuid.Parse(request.Id)
    	if err != nil {
    
    	}
    
    	foundLink, err := t.topologyService.Get(query.Query{ID: linkID})
    	if err != nil {
    
    	}
    
    	err = t.topologyService.DeleteLink(foundLink)
    	if err != nil {
    
    	}
    
    	return &topopb.DeleteLinkResponse{
    		Timestamp: time.Now().UnixNano(),
    	}, nil
    }
    
    
    func (t *TopologyServer) 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
    }