From 094ccd6c19472a764ec56a12400563febc7dd5bd Mon Sep 17 00:00:00 2001
From: Manuel Kieweg <manuel.kieweg@h-da.de>
Date: Fri, 14 May 2021 14:16:32 +0200
Subject: [PATCH] mvp

---
 cli/get.go                                  |  40 -----
 cli/grpc.go                                 | 169 ++++++++++++++++++++
 cli/init.go                                 |  17 --
 cli/set.go                                  |  25 ---
 cmd/addDevice.go                            |   9 +-
 cmd/delete.go                               |  11 +-
 cmd/get.go                                  |  53 ------
 cmd/getIds.go                               |   2 +-
 cmd/init.go                                 |   2 +-
 cmd/replace.go                              |  13 +-
 cmd/request.go                              |  10 +-
 cmd/requestAll.go                           |   8 +-
 cmd/set.go                                  |  57 -------
 cmd/update.go                               |  13 +-
 controller.go                               |  23 ++-
 forks/google/gnmi/model.go                  |   5 +-
 http.go                                     |   3 +-
 northbound/client/client.go                 |   1 -
 northbound/client/core.go                   |  14 ++
 northbound/client/pnd.go                    |  14 ++
 northbound/server/core.go                   |  51 ++++++
 northbound/server/nbi.go                    |  24 +++
 northbound/server/pnd.go                    |  23 +--
 northbound/server/server.go                 |  41 -----
 nucleus/southbound.go                       |   3 +
 test/integration/cliIntegration_test.go     |   3 +-
 test/integration/cmdIntegration_test.go     |   5 +-
 test/integration/nucleusIntegration_test.go |  11 +-
 test/targets.go                             |   5 +-
 29 files changed, 343 insertions(+), 312 deletions(-)
 delete mode 100644 cli/get.go
 create mode 100644 cli/grpc.go
 delete mode 100644 cli/init.go
 delete mode 100644 cli/set.go
 delete mode 100644 cmd/get.go
 delete mode 100644 cmd/set.go
 delete mode 100644 northbound/client/client.go
 create mode 100644 northbound/client/core.go
 create mode 100644 northbound/client/pnd.go
 create mode 100644 northbound/server/core.go
 create mode 100644 northbound/server/nbi.go
 delete mode 100644 northbound/server/server.go

diff --git a/cli/get.go b/cli/get.go
deleted file mode 100644
index 9b21ed48f..000000000
--- a/cli/get.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package cli
-
-import (
-	"context"
-
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
-
-	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	gpb "github.com/openconfig/gnmi/proto/gnmi"
-	log "github.com/sirupsen/logrus"
-)
-
-// Get sends a gNMI Get request to the specified target and prints the response to stdout
-func Get(a, u, p string, args ...string) (*gpb.GetResponse, error) {
-	sbi := &nucleus.OpenConfig{}
-	opts := &nucleus.GnmiTransportOptions{
-		Config: gnmi.Config{
-			Addr:     a,
-			Username: u,
-			Password: p,
-			Encoding: gpb.Encoding_JSON_IETF,
-		},
-		SetNode: sbi.SetNode(),
-	}
-	t, err := nucleus.NewGnmiTransport(opts)
-	if err != nil {
-		return nil, err
-	}
-	resp, err := t.Get(context.Background(), args...)
-	if err != nil {
-		return nil, err
-	}
-	log.Debug(resp)
-	r, ok := resp.(*gpb.GetResponse)
-	if !ok {
-		return nil, &errors.ErrInvalidTypeAssertion{}
-	}
-	return r, nil
-}
diff --git a/cli/grpc.go b/cli/grpc.go
new file mode 100644
index 000000000..4e5843f87
--- /dev/null
+++ b/cli/grpc.go
@@ -0,0 +1,169 @@
+package cli
+
+import (
+	ppb "code.fbi.h-da.de/cocsn/api/go/gosdn/pnd"
+	nbi "code.fbi.h-da.de/cocsn/gosdn/northbound/client"
+	"context"
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/viper"
+	"time"
+
+	pb "code.fbi.h-da.de/cocsn/api/go/gosdn/core"
+	"google.golang.org/grpc"
+)
+
+// TODO: Delete once proper certs are set up
+var grpcWithInsecure = grpc.WithInsecure()
+
+func Init(addr string) error {
+	ctx := context.Background()
+	resp, err := getAllCore(ctx, addr)
+	if err != nil {
+		return err
+	}
+	if len(resp.Pnd) > 0 {
+		pid := resp.Pnd[0].Id
+		viper.Set("CLI_PND", pid)
+		log.Infof("PND: %v", pid)
+		if len(resp.Pnd[0].Sbi) != 0 {
+			sbi := resp.Pnd[0].Sbi[0].Id
+			viper.Set("CLI_SBI", sbi)
+			log.Infof("SBI: %v", sbi)
+		}
+	}
+	return nil
+}
+
+func GetIds(addr string) error {
+	ctx := context.Background()
+	resp, err := getAllCore(ctx, addr)
+	if err != nil {
+		return err
+	}
+	for i,pnd := range resp.Pnd {
+		log.Infof("PND %v: %v", i+1, pnd.Id)
+		for j,sbi := range pnd.Sbi {
+			log.Infof("\tSBI %v: %v", j+1, sbi.Id)
+		}
+	}
+	return nil
+}
+
+func getAllCore(ctx context.Context, addr string) (*pb.GetResponse, error) {
+	coreClient, err := nbi.CoreClient(addr, grpcWithInsecure)
+	if err != nil {
+		return nil, err
+	}
+	req := &pb.GetRequest{
+		Timestamp: time.Now().UnixNano(),
+		All:       true,
+	}
+	return coreClient.Get(ctx, req)
+}
+
+func AddDevice(addr, username, password, sbi, pnd, deviceAddress string) error {
+	pndClient, err := nbi.PndClient(addr, grpcWithInsecure)
+	if err != nil {
+		return err
+	}
+
+	req := &ppb.SetRequest{
+		Timestamp: time.Now().UnixNano(),
+		Ond: []*ppb.SetOnd{
+			{
+				Address:  deviceAddress,
+				Username: username,
+				Password: password,
+				Sbi: &ppb.SouthboundInterface{
+					Type: 0,
+				},
+			},
+		},
+		Pid:           pnd,
+	}
+	ctx := context.Background()
+	resp, err := pndClient.Set(ctx, req)
+	if err != nil {
+		return err
+	}
+	log.Info(resp.String())
+	return nil
+}
+
+func GetDevice(addr, pid, path string, did ...string) error {
+	pndClient, err := nbi.PndClient(addr, grpcWithInsecure)
+	if err != nil {
+		return err
+	}
+
+	var all bool
+	if len(did) != 0 {
+		all = true
+	}
+
+	req := &ppb.GetRequest{
+		Timestamp: time.Now().UnixNano(),
+		Request:   &ppb.GetRequest_Ond{
+			Ond: &ppb.GetOnd{
+				All: all,
+				Did: did,
+			},
+		},
+		Pid:       pid,
+	}
+	ctx := context.Background()
+	resp, err := pndClient.Get(ctx, req)
+	if err != nil {
+		return err
+	}
+	log.Info(resp.String())
+	return nil
+}
+
+func Update(addr, did, pid, path, value string) error {
+	req := &ppb.ChangeRequest{
+		Id:    did,
+		Path:  path,
+		Value: value,
+		ApiOp: ppb.ChangeRequest_UPDATE,
+	}
+	return sendChangeRequest(addr, pid, req)
+}
+
+func Replace(addr, did, pid, path, value string) error {
+	req := &ppb.ChangeRequest{
+		Id:    did,
+		Path:  path,
+		Value: value,
+		ApiOp: ppb.ChangeRequest_REPLACE,
+	}
+	return sendChangeRequest(addr, pid, req)
+}
+
+func Delete(addr, did, pid, path string) error {
+	req := &ppb.ChangeRequest{
+		Id:    did,
+		Path:  path,
+		ApiOp: ppb.ChangeRequest_DELETE,
+	}
+	return sendChangeRequest(addr, pid, req)
+}
+
+func sendChangeRequest(addr, pid string, req *ppb.ChangeRequest) error {
+	pndClient, err := nbi.PndClient(addr, grpcWithInsecure)
+	if err != nil {
+		return err
+	}
+	ctx := context.Background()
+	r := &ppb.SetRequest{
+		Timestamp:     time.Now().UnixNano(),
+		ChangeRequest: []*ppb.ChangeRequest{req},
+		Pid:            pid,
+	}
+	resp, err := pndClient.Set(ctx, r)
+	if err != nil {
+		return err
+	}
+	log.Info(resp.String())
+	return nil
+}
\ No newline at end of file
diff --git a/cli/init.go b/cli/init.go
deleted file mode 100644
index d85e6bd00..000000000
--- a/cli/init.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package cli
-
-import (
-	model "code.fbi.h-da.de/cocsn/yang-models/generated/arista"
-	"github.com/openconfig/ygot/ytypes"
-	log "github.com/sirupsen/logrus"
-)
-
-var testSchema *ytypes.Schema
-
-func init() {
-	var err error
-	testSchema, err = model.Schema()
-	if err != nil {
-		log.Fatal(err)
-	}
-}
diff --git a/cli/set.go b/cli/set.go
deleted file mode 100644
index 7e5328b18..000000000
--- a/cli/set.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package cli
-
-import (
-	"context"
-
-	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-)
-
-// Set sends a gNMI Set request to the specified target. Only one
-// request per invocation supported.
-func Set(a, u, p, typ string, args ...string) error {
-	opts := &nucleus.GnmiTransportOptions{
-		Config: gnmi.Config{
-			Addr:     a,
-			Username: u,
-			Password: p,
-		},
-	}
-	t, err := nucleus.NewGnmiTransport(opts)
-	if err != nil {
-		return err
-	}
-	return t.Set(context.Background(), args)
-}
diff --git a/cmd/addDevice.go b/cmd/addDevice.go
index 95180504c..364cb833f 100644
--- a/cmd/addDevice.go
+++ b/cmd/addDevice.go
@@ -45,14 +45,13 @@ var addDeviceCmd = &cobra.Command{
 Device address and user credentials need to be provided
 if they diverge from the default credentials.`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.HTTPGet(
+		return cli.AddDevice(
 			apiEndpoint,
-			"addDevice",
-			"address="+address,
-			"password="+password,
 			"username="+username,
-			"sbi="+cliSbi,
+			"password="+password,
 			"pnd="+cliPnd,
+			"sbi="+cliSbi,
+			"address="+address,
 		)
 	},
 }
diff --git a/cmd/delete.go b/cmd/delete.go
index 3c4e4e760..7133444fe 100644
--- a/cmd/delete.go
+++ b/cmd/delete.go
@@ -44,14 +44,11 @@ var deleteCmd = &cobra.Command{
 	Long: `Set a path value for a given device. Only one path and
 only one value supported for now`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.HTTPGet(
+		return cli.Delete(
 			apiEndpoint,
-			"delete",
-			"uuid="+uuid,
-			"cliSbi="+cliSbi,
-			"cliPnd="+cliPnd,
-			"path="+args[0],
-			"address="+address,
+			uuid,
+			cliPnd,
+			args[0],
 		)
 	},
 }
diff --git a/cmd/get.go b/cmd/get.go
deleted file mode 100644
index d200f3a08..000000000
--- a/cmd/get.go
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-Copyright © 2021 da/net research group <danet.fbi.h-da.de>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-   this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package cmd
-
-import (
-	"code.fbi.h-da.de/cocsn/gosdn/cli"
-	"github.com/spf13/cobra"
-)
-
-// getCmd represents the get command
-var getCmd = &cobra.Command{
-	Use:   "gosdn get",
-	Short: "get request",
-	Long: `Sends a gNMI Get request to the specified target and
-prints the response to stdout`,
-	RunE: func(cmd *cobra.Command, args []string) error {
-		_, err := cli.Get(address, username, password, args...)
-		return err
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(getCmd)
-}
diff --git a/cmd/getIds.go b/cmd/getIds.go
index ec858fd9c..4aeebef79 100644
--- a/cmd/getIds.go
+++ b/cmd/getIds.go
@@ -42,7 +42,7 @@ var getIdsCmd = &cobra.Command{
 	Short: "gets device IDs from the controller",
 	Long:  `Gets device IDs from the controller and lists them.`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.HTTPGet(apiEndpoint, "getIDs")
+		return cli.GetIds(apiEndpoint)
 	},
 }
 
diff --git a/cmd/init.go b/cmd/init.go
index 6a4b3e9ba..949e6996b 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -44,7 +44,7 @@ var initCmd = &cobra.Command{
 	
 Same as invoking "gosdn cli" without any arguments`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.HTTPGet(apiEndpoint, "init")
+		return cli.Init(apiEndpoint)
 	},
 }
 
diff --git a/cmd/replace.go b/cmd/replace.go
index 33529c6d0..7b8c22ddc 100644
--- a/cmd/replace.go
+++ b/cmd/replace.go
@@ -44,15 +44,12 @@ var replaceCmd = &cobra.Command{
 	Long: `Set a path value for a given device. Only one path and
 only one value supported for now`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.HTTPGet(
+		return cli.Replace(
 			apiEndpoint,
-			"replace",
-			"uuid="+uuid,
-			"cliSbi="+cliSbi,
-			"cliPnd="+cliPnd,
-			"path="+args[0],
-			"address="+address,
-			"value="+args[1],
+			uuid,
+			cliPnd,
+			args[0],
+			args[1],
 		)
 	},
 }
diff --git a/cmd/request.go b/cmd/request.go
index 54dd12bec..7365a1116 100644
--- a/cmd/request.go
+++ b/cmd/request.go
@@ -45,13 +45,11 @@ var requestCmd = &cobra.Command{
 	
 The request path is passed as positional argument.`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.HTTPGet(
+		return cli.GetDevice(
 			apiEndpoint,
-			"request",
-			"uuid="+uuid,
-			"sbi="+cliSbi,
-			"pnd="+cliPnd,
-			"path="+args[0],
+			cliPnd,
+			args[0],
+			uuid,
 		)
 	},
 }
diff --git a/cmd/requestAll.go b/cmd/requestAll.go
index 11234826e..84c31154e 100644
--- a/cmd/requestAll.go
+++ b/cmd/requestAll.go
@@ -46,12 +46,10 @@ the controller.
 	
 The request path is passed as positional argument.`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.HTTPGet(
+		return cli.GetDevice(
 			apiEndpoint,
-			"requestAll",
-			"sbi="+cliSbi,
-			"pnd="+cliPnd,
-			"path="+args[0],
+			cliPnd,
+			args[0],
 		)
 	},
 }
diff --git a/cmd/set.go b/cmd/set.go
deleted file mode 100644
index 486d9f594..000000000
--- a/cmd/set.go
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-Copyright © 2021 da/net research group <danet.fbi.h-da.de>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-   this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-
-package cmd
-
-import (
-	"code.fbi.h-da.de/cocsn/gosdn/cli"
-	"github.com/spf13/cobra"
-)
-
-var typ string
-
-// setCmd represents the set command
-var setCmd = &cobra.Command{
-	Use:   "set",
-	Short: "set request",
-	Long: `Sends a gNMI Set request to the specified target. Only one
-request per invocation supported.`,
-	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.Set(address, username, password, typ, args...)
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(setCmd)
-
-	setCmd.Flags().StringVarP(&typ, "type", "t", "update", "Type of the set request. "+
-		"Possible values: 'update', 'replace', and 'delete'")
-}
diff --git a/cmd/update.go b/cmd/update.go
index 93db8e374..e1ee314da 100644
--- a/cmd/update.go
+++ b/cmd/update.go
@@ -44,15 +44,12 @@ var updateCmd = &cobra.Command{
 	Long: `Update a path value for a given device. Only one path and
 only one value supported for now`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		return cli.HTTPGet(
+		return cli.Update(
 			apiEndpoint,
-			"update",
-			"uuid="+uuid,
-			"cliSbi="+cliSbi,
-			"cliPnd="+cliPnd,
-			"path="+args[0],
-			"address="+address,
-			"value="+args[1],
+			uuid,
+			cliPnd,
+			args[0],
+			args[1],
 		)
 	},
 }
diff --git a/controller.go b/controller.go
index 46d20105c..18fc13b36 100644
--- a/controller.go
+++ b/controller.go
@@ -2,8 +2,6 @@ package gosdn
 
 import (
 	"context"
-	"github.com/spf13/viper"
-	"google.golang.org/grpc"
 	"net"
 	"net/http"
 	"os"
@@ -11,8 +9,11 @@ import (
 	"sync"
 	"time"
 
-	pb "code.fbi.h-da.de/cocsn/api/proto/gosdn"
-	ppb "code.fbi.h-da.de/cocsn/api/proto/gosdn/pnd"
+	"github.com/spf13/viper"
+	"google.golang.org/grpc"
+
+	pb "code.fbi.h-da.de/cocsn/api/go/gosdn/core"
+	ppb "code.fbi.h-da.de/cocsn/api/go/gosdn/pnd"
 	"code.fbi.h-da.de/cocsn/gosdn/database"
 	nbi "code.fbi.h-da.de/cocsn/gosdn/northbound/server"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
@@ -24,16 +25,15 @@ import (
 var coreLock sync.RWMutex
 var coreOnce sync.Once
 
-// Core is the representation of the controllers core
+// Core is the representation of the controller's core
 type Core struct {
 	// deprecated
 	database database.Database
 
 	pndc       *nucleus.PndStore
-	sbic       *nucleus.SbiStore
 	httpServer *http.Server
 	grpcServer *grpc.Server
-	nbi		   *nbi.NorthboundInterface
+	nbi        *nbi.NorthboundInterface
 	stopChan   chan os.Signal
 }
 
@@ -43,7 +43,6 @@ func init() {
 	c = &Core{
 		database: database.Database{},
 		pndc:     nucleus.NewPndStore(),
-		sbic:     nucleus.NewSbiStore(),
 		stopChan: make(chan os.Signal, 1),
 	}
 
@@ -70,8 +69,8 @@ func startGrpcServer() error {
 		return err
 	}
 	c.grpcServer = grpc.NewServer()
-	c.nbi = nbi.NewNBI(c.pndc, c.sbic)
-	pb.RegisterGosdnServer(c.grpcServer, c.nbi.Controller)
+	c.nbi = nbi.NewNBI(c.pndc)
+	pb.RegisterCoreServer(c.grpcServer, c.nbi.Core)
 	ppb.RegisterPndServer(c.grpcServer, c.nbi.Pnd)
 	go func() {
 		log.Fatal(c.grpcServer.Serve(lis))
@@ -82,9 +81,6 @@ func startGrpcServer() error {
 // createSouthboundInterfaces initializes the controller with its supported SBIs
 func createSouthboundInterfaces() error {
 	sbi := nucleus.NewSBI(types.Openconfig)
-	if err := c.sbic.Add(sbi); err != nil {
-		return err
-	}
 	return createPrincipalNetworkDomain(sbi)
 }
 
@@ -128,5 +124,6 @@ func shutdown() error {
 	log.Info("shutting down controller")
 	coreLock.Lock()
 	defer coreLock.Unlock()
+	c.grpcServer.GracefulStop()
 	return stopHttpServer()
 }
diff --git a/forks/google/gnmi/model.go b/forks/google/gnmi/model.go
index 8d6a4998b..b30a1de2b 100644
--- a/forks/google/gnmi/model.go
+++ b/forks/google/gnmi/model.go
@@ -16,14 +16,15 @@ limitations under the License.
 package gnmi
 
 import (
-	oc "code.fbi.h-da.de/cocsn/yang-models/generated/arista"
 	"errors"
 	"fmt"
-	log "github.com/sirupsen/logrus"
 	"net"
 	"reflect"
 	"sort"
 
+	oc "code.fbi.h-da.de/cocsn/yang-models/generated/arista"
+	log "github.com/sirupsen/logrus"
+
 	"github.com/openconfig/goyang/pkg/yang"
 	"github.com/openconfig/ygot/ygot"
 	"github.com/openconfig/ygot/ytypes"
diff --git a/http.go b/http.go
index d672ababc..778b3750c 100644
--- a/http.go
+++ b/http.go
@@ -3,9 +3,10 @@ package gosdn
 import (
 	"context"
 	"fmt"
-	log "github.com/sirupsen/logrus"
 	"net/http"
 	"time"
+
+	log "github.com/sirupsen/logrus"
 )
 
 func stopHttpServer() error {
diff --git a/northbound/client/client.go b/northbound/client/client.go
deleted file mode 100644
index da13c8ef3..000000000
--- a/northbound/client/client.go
+++ /dev/null
@@ -1 +0,0 @@
-package client
diff --git a/northbound/client/core.go b/northbound/client/core.go
new file mode 100644
index 000000000..4a79fa8c9
--- /dev/null
+++ b/northbound/client/core.go
@@ -0,0 +1,14 @@
+package client
+
+import (
+	pb "code.fbi.h-da.de/cocsn/api/go/gosdn/core"
+	"google.golang.org/grpc"
+)
+
+func CoreClient(addr string, opts ...grpc.DialOption) (pb.CoreClient, error) {
+	conn, err := grpc.Dial(addr, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return pb.NewCoreClient(conn), nil
+}
\ No newline at end of file
diff --git a/northbound/client/pnd.go b/northbound/client/pnd.go
new file mode 100644
index 000000000..3ab659c82
--- /dev/null
+++ b/northbound/client/pnd.go
@@ -0,0 +1,14 @@
+package client
+
+import (
+	ppb "code.fbi.h-da.de/cocsn/api/go/gosdn/pnd"
+	"google.golang.org/grpc"
+)
+
+func PndClient(addr string, opts ...grpc.DialOption) (ppb.PndClient, error) {
+	conn, err := grpc.Dial(addr, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return ppb.NewPndClient(conn), nil
+}
diff --git a/northbound/server/core.go b/northbound/server/core.go
new file mode 100644
index 000000000..0a9304579
--- /dev/null
+++ b/northbound/server/core.go
@@ -0,0 +1,51 @@
+package server
+
+import (
+	"context"
+	"time"
+
+	pb "code.fbi.h-da.de/cocsn/api/go/gosdn/core"
+	ppb "code.fbi.h-da.de/cocsn/api/go/gosdn/pnd"
+	"github.com/google/uuid"
+)
+
+type core struct {
+	pb.UnimplementedCoreServer
+}
+
+func (s core) Get(ctx context.Context, request *pb.GetRequest) (*pb.GetResponse, error) {
+	pndLock.RLock()
+	defer pndLock.RUnlock()
+	var pndList []uuid.UUID
+	switch request.All {
+	case true:
+		pndList = pndc.UUIDs()
+	default:
+		var err error
+		pndList, err = stringToUUID(request.Pid)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	pnds := make([]*ppb.PrincipalNetworkDomain, len(pndList))
+	for i, id := range pndList {
+		pnd, err := pndc.Get(id)
+		if err != nil {
+			return nil, err
+		}
+		pnds[i] = &ppb.PrincipalNetworkDomain{
+			Id:          pnd.ID().String(),
+			Name:        pnd.GetName(),
+			Description: pnd.GetDescription(),
+		}
+	}
+	return &pb.GetResponse{
+		Timestamp: time.Now().UnixNano(),
+		Pnd:       pnds,
+	}, nil
+}
+
+func (s core) Set(ctx context.Context, request *pb.SetRequest) (*pb.SetResponse, error) {
+	panic("implement me")
+}
diff --git a/northbound/server/nbi.go b/northbound/server/nbi.go
new file mode 100644
index 000000000..95bf5ad14
--- /dev/null
+++ b/northbound/server/nbi.go
@@ -0,0 +1,24 @@
+package server
+
+import (
+	"sync"
+
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
+)
+
+var pndLock sync.RWMutex
+var pndc *nucleus.PndStore
+
+func NewNBI(pnds *nucleus.PndStore) *NorthboundInterface {
+	pndc = pnds
+	pndLock = sync.RWMutex{}
+	return &NorthboundInterface{
+		Pnd:  &pnd{},
+		Core: &core{},
+	}
+}
+
+type NorthboundInterface struct {
+	Pnd  *pnd
+	Core *core
+}
diff --git a/northbound/server/pnd.go b/northbound/server/pnd.go
index f080fc1ac..e694a2c52 100644
--- a/northbound/server/pnd.go
+++ b/northbound/server/pnd.go
@@ -1,21 +1,22 @@
 package server
 
 import (
-	ppb "code.fbi.h-da.de/cocsn/api/proto/gosdn/pnd"
+	"context"
+	"reflect"
+	"time"
+
+	ppb "code.fbi.h-da.de/cocsn/api/go/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"
-	"reflect"
-	"time"
 )
 
-type pndServer struct {
+type pnd struct {
 	ppb.UnimplementedPndServer
 }
 
-func (p pndServer) Get(ctx context.Context, request *ppb.GetRequest) (*ppb.GetResponse, error) {
+func (p pnd) Get(ctx context.Context, request *ppb.GetRequest) (*ppb.GetResponse, error) {
 	pid, err := uuid.Parse(request.Pid)
 	if err != nil {
 		return nil, err
@@ -115,7 +116,7 @@ func fillSbis(pnd nucleus.PrincipalNetworkDomain, all bool, sid ...string) ([]*p
 		if len(sid) == 0 {
 			return nil, &errors.ErrInvalidParameters{
 				Func:  fillSbis,
-				Param: "lenth of 'sid' cannot be '0' when 'all' is set to 'false'",
+				Param: "length of 'sid' cannot be '0' when 'all' is set to 'false'",
 			}
 		}
 		sbiList, err = stringToUUID(sid)
@@ -130,8 +131,8 @@ func fillSbis(pnd nucleus.PrincipalNetworkDomain, all bool, sid ...string) ([]*p
 			return nil, err
 		}
 		sbis[i] = &ppb.SouthboundInterface{
-			Id:     id.String(),
-			Schema: sbi.Schema().RootSchema().Name,
+			Id:   id.String(),
+			Type: int32(sbi.Type()),
 		}
 	}
 	return sbis, nil
@@ -148,7 +149,7 @@ func fillOnds(pnd nucleus.PrincipalNetworkDomain, all bool, did ...string) ([]*p
 		if len(did) == 0 {
 			return nil, &errors.ErrInvalidParameters{
 				Func:  fillOnds,
-				Param: "lenth of 'did' cannot be '0' when 'all' is set to 'false'",
+				Param: "length of 'did' cannot be '0' when 'all' is set to 'false'",
 			}
 		}
 		ondList, err = stringToUUID(did)
@@ -182,6 +183,6 @@ func fillChanges(pnd nucleus.PrincipalNetworkDomain) ([]*ppb.Change, error) {
 	return nil, errors.ErrNotYetImplemented{}
 }
 
-func (p pndServer) Set(ctx context.Context, request *ppb.SetRequest) (*ppb.SetResponse, error) {
+func (p pnd) Set(ctx context.Context, request *ppb.SetRequest) (*ppb.SetResponse, error) {
 	panic("implement me")
 }
diff --git a/northbound/server/server.go b/northbound/server/server.go
deleted file mode 100644
index 592504b0a..000000000
--- a/northbound/server/server.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package server
-
-import (
-	pb "code.fbi.h-da.de/cocsn/api/proto/gosdn"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	"context"
-	"sync"
-)
-
-var pndLock sync.RWMutex
-var sbiLock sync.RWMutex
-var pndc *nucleus.PndStore
-var sbic *nucleus.SbiStore
-
-type gosdnServer struct {
-	pb.UnimplementedGosdnServer
-}
-
-func (s gosdnServer) Get(ctx context.Context, request *pb.GetRequest) (*pb.GetResponse, error) {
-	panic("implement me")
-}
-
-func (s gosdnServer) Set(ctx context.Context, request *pb.SetRequest) (*pb.SetResponse, error) {
-	panic("implement me")
-}
-
-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 NorthboundInterface struct {
-	Pnd        *pndServer
-	Controller *gosdnServer
-}
diff --git a/nucleus/southbound.go b/nucleus/southbound.go
index b8eec7772..6421a8f53 100644
--- a/nucleus/southbound.go
+++ b/nucleus/southbound.go
@@ -28,6 +28,7 @@ type SouthboundInterface interface {
 	SetNode() func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error
 	Schema() *ytypes.Schema
 	ID() uuid.UUID
+	Type() types.Southbound
 }
 
 // NewSBI creates a SouthboundInterface of a given type.
@@ -161,3 +162,5 @@ func iter(a ygot.GoStruct, fields []string) (b ygot.GoStruct, f string, err erro
 func (oc *OpenConfig) ID() uuid.UUID {
 	return oc.id
 }
+
+func (oc *OpenConfig)Type() types.Southbound {return types.Openconfig}
\ No newline at end of file
diff --git a/test/integration/cliIntegration_test.go b/test/integration/cliIntegration_test.go
index d2800b0e0..dac342c5d 100644
--- a/test/integration/cliIntegration_test.go
+++ b/test/integration/cliIntegration_test.go
@@ -1,8 +1,9 @@
 package integration
 
 import (
-	"code.fbi.h-da.de/cocsn/gosdn/cli"
 	"testing"
+
+	"code.fbi.h-da.de/cocsn/gosdn/cli"
 )
 
 func TestCapabilities(t *testing.T) {
diff --git a/test/integration/cmdIntegration_test.go b/test/integration/cmdIntegration_test.go
index d5596ab70..cfe01aad5 100644
--- a/test/integration/cmdIntegration_test.go
+++ b/test/integration/cmdIntegration_test.go
@@ -1,6 +1,9 @@
 package integration
 
 import (
+	"os"
+	"testing"
+
 	"code.fbi.h-da.de/cocsn/gosdn/cli"
 	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
@@ -10,8 +13,6 @@ import (
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 	pb "google.golang.org/protobuf/proto"
-	"os"
-	"testing"
 )
 
 const unreachable = "203.0.113.10:6030"
diff --git a/test/integration/nucleusIntegration_test.go b/test/integration/nucleusIntegration_test.go
index ccd5d942a..e6048faf4 100644
--- a/test/integration/nucleusIntegration_test.go
+++ b/test/integration/nucleusIntegration_test.go
@@ -1,16 +1,17 @@
 package integration
 
 import (
-	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
 	"context"
-	gpb "github.com/openconfig/gnmi/proto/gnmi"
-	pb "google.golang.org/protobuf/proto"
 	"reflect"
 	"sort"
 	"testing"
 	"time"
+
+	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
+	gpb "github.com/openconfig/gnmi/proto/gnmi"
+	pb "google.golang.org/protobuf/proto"
 )
 
 var gnmiMessages map[string]pb.Message
diff --git a/test/targets.go b/test/targets.go
index 309b81847..2471d5509 100644
--- a/test/targets.go
+++ b/test/targets.go
@@ -1,6 +1,9 @@
 package test
 
 import (
+	"net"
+	"reflect"
+
 	"code.fbi.h-da.de/cocsn/gosdn/forks/google/gnmi"
 	oc "code.fbi.h-da.de/cocsn/yang-models/generated/arista"
 	pb "github.com/openconfig/gnmi/proto/gnmi"
@@ -10,8 +13,6 @@ import (
 	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/reflection"
-	"net"
-	"reflect"
 )
 
 type server struct {
-- 
GitLab