From 43ffa15e77ddc73297523c2e33640d6ea28294b1 Mon Sep 17 00:00:00 2001
From: Manuel Kieweg <manuel.kieweg@h-da.de>
Date: Fri, 7 May 2021 18:14:28 +0200
Subject: [PATCH] first gRPB NBI draft

---
 controller.go        |  30 +++++++--
 go.mod               |   2 +-
 go.sum               |   4 +-
 northbound/client.go |   2 +-
 northbound/server.go | 150 +++++++++++++++++++++++++++++++++++++++----
 5 files changed, 167 insertions(+), 21 deletions(-)

diff --git a/controller.go b/controller.go
index 950022530..5adfd95a3 100644
--- a/controller.go
+++ b/controller.go
@@ -2,16 +2,21 @@ package gosdn
 
 import (
 	"context"
+	"github.com/spf13/viper"
+	"google.golang.org/grpc"
+	"net"
 	"net/http"
 	"os"
 	"os/signal"
 	"sync"
 	"time"
 
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
-
+	pb "code.fbi.h-da.de/cocsn/api/proto/gosdn"
+	ppb "code.fbi.h-da.de/cocsn/api/proto/gosdn/pnd"
 	"code.fbi.h-da.de/cocsn/gosdn/database"
+	"code.fbi.h-da.de/cocsn/gosdn/northbound"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
 	"github.com/google/uuid"
 	log "github.com/sirupsen/logrus"
 )
@@ -27,6 +32,8 @@ type Core struct {
 	pndc       *nucleus.PndStore
 	sbic       *nucleus.SbiStore
 	httpServer *http.Server
+	grpcServer *grpc.Server
+	nbi		   *nbi.NorthboundInterface
 	stopChan   chan os.Signal
 }
 
@@ -50,10 +57,25 @@ func initialize() error {
 		return err
 	}
 
-	// TODO: Start grpc listener here
 	coreLock.Lock()
-	defer coreLock.Unlock()
 	startHttpServer()
+	coreLock.Unlock()
+	return startGrpcServer()
+}
+
+func startGrpcServer() error {
+	sock := viper.GetString("socket")
+	lis, err := net.Listen("tcp", sock)
+	if err != nil {
+		return err
+	}
+	c.grpcServer = grpc.NewServer()
+	c.nbi = nbi.NewNBI(c.pndc, c.sbic)
+	pb.RegisterGosdnServer(c.grpcServer, c.nbi.Controller)
+	ppb.RegisterPndServer(c.grpcServer, c.nbi.Pnd)
+	go func() {
+		log.Fatal(c.grpcServer.Serve(lis))
+	}()
 	return nil
 }
 
diff --git a/go.mod b/go.mod
index 2ac11d048..ea090a449 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module code.fbi.h-da.de/cocsn/gosdn
 go 1.14
 
 require (
-	code.fbi.h-da.de/cocsn/api v0.0.0-20210504143911-bdf77e6fac5b
+	code.fbi.h-da.de/cocsn/api v0.0.0-20210507152956-c57d49d31c36
 	code.fbi.h-da.de/cocsn/yang-models v0.0.7
 	github.com/aristanetworks/goarista v0.0.0-20201120222254-94a892eb0c6a
 	github.com/golang/protobuf v1.5.0
diff --git a/go.sum b/go.sum
index 3973ea97a..743f6025d 100644
--- a/go.sum
+++ b/go.sum
@@ -21,8 +21,8 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
 cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
-code.fbi.h-da.de/cocsn/api v0.0.0-20210504143911-bdf77e6fac5b h1:lWNT0sjX54rCJeRIQwbiCersboHfktJeStwmS0EftbQ=
-code.fbi.h-da.de/cocsn/api v0.0.0-20210504143911-bdf77e6fac5b/go.mod h1:E6nKMGt53n5w/EEqIjUZFS0ttJWvPm3pACzadZcYH/k=
+code.fbi.h-da.de/cocsn/api v0.0.0-20210507152956-c57d49d31c36 h1:P45QzFToLYzgKIFDmrG3ASs/xnu8RpxO+kDOGijUnis=
+code.fbi.h-da.de/cocsn/api v0.0.0-20210507152956-c57d49d31c36/go.mod h1:E6nKMGt53n5w/EEqIjUZFS0ttJWvPm3pACzadZcYH/k=
 code.fbi.h-da.de/cocsn/yang-models v0.0.7 h1:3TOo8J+EdAJKeq4o3aaNWZRhjSwguIS8wciW1U9PkSk=
 code.fbi.h-da.de/cocsn/yang-models v0.0.7/go.mod h1:M+2HinfhTT8nA8qvn2cpWNlOtuiizTNDWA3yfy72K/g=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
diff --git a/northbound/client.go b/northbound/client.go
index 040295490..d0b2c91c9 100644
--- a/northbound/client.go
+++ b/northbound/client.go
@@ -1 +1 @@
-package northbound
+package nbi
diff --git a/northbound/server.go b/northbound/server.go
index 75d6bdcc0..2e45ee2b9 100644
--- a/northbound/server.go
+++ b/northbound/server.go
@@ -1,43 +1,167 @@
-package northbound
+package nbi
 
 import (
-	"code.fbi.h-da.de/cocsn/api/proto/gosdn"
-	"code.fbi.h-da.de/cocsn/api/proto/gosdn/pnd"
+	pb "code.fbi.h-da.de/cocsn/api/proto/gosdn"
+	ppb "code.fbi.h-da.de/cocsn/api/proto/gosdn/pnd"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
 	"context"
+	"github.com/google/uuid"
+	"github.com/openconfig/ygot/ygot"
+	"sync"
+	"time"
 )
 
+var pndLock sync.RWMutex
+var sbiLock sync.RWMutex
+var pndc *nucleus.PndStore
+var sbic *nucleus.SbiStore
+
 type pndServer struct {
-	pnd.UnimplementedPndServer
+	ppb.UnimplementedPndServer
 }
 
-func (p pndServer) Get(ctx context.Context, request *pnd.GetRequest) (*pnd.GetResponse, error) {
-	panic("implement me")
+func (p pndServer) Get(ctx context.Context, request *ppb.GetRequest) (*ppb.GetResponse, error) {
+	switch request.Type {
+	case ppb.GetRequest_PND:
+		pid, err := uuid.Parse(request.Id)
+		if err != nil {
+			return nil, err
+		}
+		return handlePndRequest(pid)
+	case ppb.GetRequest_OND:
+		return handleOndRequest()
+	case ppb.GetRequest_ONDS:
+		return handleOndsRequest()
+	case ppb.GetRequest_SBI:
+		return handleSbiRequest()
+	case ppb.GetRequest_SBIS:
+		return handleSbisRequest()
+	case ppb.GetRequest_Changes:
+		return handleChangesRequest()
+	default:
+		return nil, errors.ErrOperationNotSupported{Op: request.Type}
+	}
+}
+
+func handlePndRequest(pid uuid.UUID) (*ppb.GetResponse, error) {
+	pndLock.RLock()
+	defer pndLock.RUnlock()
+	pnd, err := pndc.Get(pid)
+	if err != nil {
+		return nil, err
+	}
+	onds, err := fillOnds(pnd)
+	if err != nil {
+		return nil, err
+	}
+	sbis, err := fillSbis(pnd)
+	return &ppb.GetResponse{
+		Timestamp: time.Now().UnixNano(),
+		Payload: &ppb.GetResponse_Pnd{
+			Pnd: &ppb.PrincipalNetworkDomain{
+				Id:          pid.String(),
+				Name:        pnd.GetName(),
+				Description: pnd.GetDescription(),
+				Onds:        onds,
+				Sbis:        sbis,
+				Changes:     nil,
+			},
+		},
+	}, nil
+}
+
+func handleSbiRequest() (*ppb.GetResponse, error) {
+	return nil, errors.ErrNotYetImplemented{}
+}
+
+func handleSbisRequest() (*ppb.GetResponse, error) {
+	return nil, errors.ErrNotYetImplemented{}
+}
+
+func handleOndRequest() (*ppb.GetResponse, error) {
+	return nil, errors.ErrNotYetImplemented{}
+}
+
+func handleOndsRequest() (*ppb.GetResponse, error) {
+	return nil, errors.ErrNotYetImplemented{}
+}
+
+func handleChangesRequest() (*ppb.GetResponse, error) {
+	return nil, errors.ErrNotYetImplemented{}
+}
+
+func fillOnds(pnd nucleus.PrincipalNetworkDomain) (*ppb.OrchestratedNetworkingDevices, error) {
+	onds := make(map[string]*ppb.OrchestratedNetworkingDevice)
+	for _, id := range pnd.Devices() {
+		d, err := pnd.GetDevice(id)
+		if err != nil {
+			return nil, err
+		}
+		cfg := ygot.GNMINotificationsConfig{}
+		dev, err := ygot.TogNMINotifications(d, time.Now().UnixNano(), cfg)
+		if err != nil {
+			return nil, err
+		}
+		ond := &ppb.OrchestratedNetworkingDevice{
+			Id:     id.String(),
+			Name:   "",
+			Device: dev,
+		}
+		onds[id.String()] = ond
+	}
+	return &ppb.OrchestratedNetworkingDevices{Onds: onds}, nil
+}
+
+func fillSbis(pnd nucleus.PrincipalNetworkDomain) (*ppb.SouthboundInterfaces, error) {
+	sbis := make(map[string]*ppb.SouthboundInterface)
+	sbic := pnd.GetSBIs().(*nucleus.SbiStore)
+	for _,id := range sbic.UUIDs() {
+		sbi, err := sbic.Get(id)
+		if err != nil {
+			return nil, err
+		}
+		sbis[id.String()] = &ppb.SouthboundInterface{
+			Id:     id.String(),
+			Schema: sbi.Schema().RootSchema().Name,
+		}
+	}
+	return &ppb.SouthboundInterfaces{Sbis: sbis}, nil
+}
+
+func fillChanges(pnd nucleus.PrincipalNetworkDomain) (*ppb.Changes, error) {
+	// TODO: Allow access to change at leaso on
+	return nil, errors.ErrNotYetImplemented{}
 }
 
-func (p pndServer) Set(ctx context.Context, request *pnd.SetRequest) (*pnd.SetResponse, error) {
+func (p pndServer) Set(ctx context.Context, request *ppb.SetRequest) (*ppb.SetResponse, error) {
 	panic("implement me")
 }
 
 type gosdnServer struct {
-	gosdn.UnimplementedGosdnServer
+	pb.UnimplementedGosdnServer
 }
 
-func (s gosdnServer) Get(ctx context.Context, request *gosdn.GetRequest) (*gosdn.GetResponse, error) {
+func (s gosdnServer) Get(ctx context.Context, request *pb.GetRequest) (*pb.GetResponse, error) {
 	panic("implement me")
 }
 
-func (s gosdnServer) Set(ctx context.Context, request *gosdn.SetRequest) (*gosdn.SetResponse, error) {
+func (s gosdnServer) Set(ctx context.Context, request *pb.SetRequest) (*pb.SetResponse, error) {
 	panic("implement me")
 }
 
-func NewServer() *Server {
-	return &Server{
+func NewNBI(pnds *nucleus.PndStore, sbis *nucleus.SbiStore) *NorthboundInterface {
+	pndc = pnds
+	sbic = sbis
+	pndLock = sync.RWMutex{}
+	sbiLock = sync.RWMutex{}
+	return &NorthboundInterface{
 		Pnd:        &pndServer{},
 		Controller: &gosdnServer{},
 	}
 }
 
-type Server struct {
+type NorthboundInterface struct {
 	Pnd        *pndServer
 	Controller *gosdnServer
 }
-- 
GitLab