diff --git a/test/integration/cmdIntegration_test.go b/api/apiIntegration_test.go
similarity index 87%
rename from test/integration/cmdIntegration_test.go
rename to api/apiIntegration_test.go
index 59a30290197c80bd4054918a64be145dfa25efef..35475bc98c6e6ed305b117854946f8e41a36f0d2 100644
--- a/test/integration/cmdIntegration_test.go
+++ b/api/apiIntegration_test.go
@@ -1,17 +1,17 @@
-package integration
+package api
 
 import (
+	"fmt"
+	"os"
+	"testing"
+
 	tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
-	"code.fbi.h-da.de/cocsn/gosdn/api"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto"
-	"fmt"
 	guuid "github.com/google/uuid"
 	gpb "github.com/openconfig/gnmi/proto/gnmi"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 	pb "google.golang.org/protobuf/proto"
-	"os"
-	"testing"
 )
 
 const unreachable = "203.0.113.10:6030"
@@ -20,10 +20,11 @@ const testPath = "/system/config/hostname"
 var testIP = "141.100.70.171"
 var testPort = "6030"
 var testAddress = testIP + ":" + testPort
-var testAPIEndpoint = "http://gosdn-latest.apps.ocp.fbi.h-da.de/api"
+var testAPIEndpoint = "gosdn-latest.apps.ocp.fbi.h-da.de"
 var testUsername = "admin"
 var testPassword = "arista"
 var opt *tpb.TransportOption
+var gnmiMessages map[string]pb.Message
 
 func TestMain(m *testing.M) {
 	testSetupIntegration()
@@ -31,6 +32,11 @@ func TestMain(m *testing.M) {
 }
 
 func testSetupIntegration() {
+	viper.SetConfigFile(".k8s.toml")
+	if err := viper.ReadInConfig(); err != nil {
+		log.Error(err)
+	}
+
 	if os.Getenv("GOSDN_LOG") == "nolog" {
 		log.SetLevel(log.PanicLevel)
 	}
@@ -91,7 +97,9 @@ func testSetupIntegration() {
 	}
 }
 
-func TestCmdIntegration(t *testing.T) {
+func TestApiIntegration(t *testing.T) {
+	// TDOO: Remove once openshift grpc support is available
+	t.Skip("skipped due to openshift limitations")
 	if testing.Short() {
 		t.Skip("skipping integration test")
 	}
@@ -107,7 +115,7 @@ func TestCmdIntegration(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			defer viper.Reset()
-			if err := api.Init(testAPIEndpoint); (err != nil) != tt.wantErr {
+			if err := Init(testAPIEndpoint); (err != nil) != tt.wantErr {
 				switch err.(type) {
 				case viper.ConfigFileNotFoundError:
 				default:
@@ -118,7 +126,7 @@ func TestCmdIntegration(t *testing.T) {
 			cliPnd := viper.GetString("CLI_PND")
 			cliSbi := viper.GetString("CLI_SBI")
 
-			if _, err := api.AddDevice(
+			if _, err := AddDevice(
 				testAPIEndpoint,
 				testUsername,
 				testPassword,
@@ -132,7 +140,7 @@ func TestCmdIntegration(t *testing.T) {
 			}
 			did := viper.GetString("LAST_DEVICE_UUID")
 
-			_, err := api.GetDevice(
+			_, err := GetDevice(
 				testAPIEndpoint,
 				cliPnd,
 				testPath,
@@ -143,7 +151,7 @@ func TestCmdIntegration(t *testing.T) {
 				return
 			}
 
-			_, err = api.GetDevice(
+			_, err = GetDevice(
 				testAPIEndpoint,
 				cliPnd,
 				"",
@@ -155,7 +163,7 @@ func TestCmdIntegration(t *testing.T) {
 			}
 
 			hostname := guuid.New().String()
-			_, err = api.Update(
+			_, err = Update(
 				testAPIEndpoint,
 				did,
 				cliPnd,
@@ -167,10 +175,10 @@ func TestCmdIntegration(t *testing.T) {
 				return
 			}
 
-			resp, err := api.GetDevice(testAddress, testUsername, testPassword, testPath)
+			resp, err := GetDevice(testAddress, testUsername, testPassword, testPath)
 			if err != nil {
 				if !tt.wantErr {
-					t.Errorf("api.Get() error = %v, wantErr %v", err, tt.wantErr)
+					t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
 				}
 				return
 			}
diff --git a/api/capabilities.go b/api/capabilities.go
deleted file mode 100644
index 93735adf1798d761a354e312584cb5b3b2c0524e..0000000000000000000000000000000000000000
--- a/api/capabilities.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package api
-
-import (
-	"context"
-	"fmt"
-	"strings"
-
-	spb "code.fbi.h-da.de/cocsn/api/go/gosdn/southbound"
-	tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
-
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	gpb "github.com/openconfig/gnmi/proto/gnmi"
-)
-
-// Capabilities sends a gNMI Capabilities request to the specified target
-// and prints the supported models to stdout
-func Capabilities(a, u, p string) error {
-	opts := &tpb.TransportOption{
-		Address:  a,
-		Username: u,
-		Password: p,
-	}
-	transport, err := nucleus.NewGnmiTransport(opts, nucleus.NewSBI(spb.Type_OPENCONFIG))
-	if err != nil {
-		return err
-	}
-	resp, err := transport.Capabilities(context.Background())
-	if err != nil {
-		return err
-	}
-	modelData := resp.(*gpb.CapabilityResponse).SupportedModels
-	b := strings.Builder{}
-	for _, elem := range modelData {
-		_, err := b.WriteString(elem.Name)
-		if err != nil {
-			return err
-		}
-		_, err = b.WriteString("\n")
-		if err != nil {
-			return err
-		}
-	}
-	fmt.Println(b.String())
-	return nil
-}
diff --git a/api/go.mod b/api/go.mod
index 0e0e8f2c94ce0c188d5d000abf9d84185009d28b..e3c75264d71e73e5a04413b5717b77667865118c 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -8,10 +8,9 @@ require (
 	code.fbi.h-da.de/cocsn/yang-models v0.0.7
 	github.com/google/uuid v1.2.0
 	github.com/openconfig/gnmi v0.0.0-20210527163611-d3a3e30199da
-	github.com/openconfig/goyang v0.2.4
-	github.com/openconfig/ygot v0.10.11
 	github.com/sirupsen/logrus v1.8.1
 	github.com/spf13/viper v1.7.1
 	github.com/stretchr/testify v1.7.0
 	google.golang.org/grpc v1.37.0
+	google.golang.org/protobuf v1.26.0
 )
diff --git a/api/go.sum b/api/go.sum
index 3054b8cd0aefee49f78fabf0040db1706546a087..697c8302ec0d6fb98a2900b54d8c6ea3c9d4bdce 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -171,7 +171,6 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
 github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/gnxi v0.0.0-20210423111716-4b504ef806a7 h1:cJ62uhbZcclaYm9gq4JNyazqSY7bUEggwZdw0nHTT7o=
 github.com/google/gnxi v0.0.0-20210423111716-4b504ef806a7/go.mod h1:dPTuHPVOqxZ2yGKPjymiMt1vrZa8KHXWKX+Lx1z5d88=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
diff --git a/api/subscribe.go b/api/subscribe.go
deleted file mode 100644
index 7c05ebc243b51b21b62eb287b2c50e3469fdedde..0000000000000000000000000000000000000000
--- a/api/subscribe.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package api
-
-import (
-	"context"
-	"fmt"
-	"os"
-	"os/signal"
-	"syscall"
-	"time"
-
-	tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
-
-	"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
-	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
-	log "github.com/sirupsen/logrus"
-)
-
-// Subscribe starts a gNMI subscriber requersting the specified paths on the target and
-// logs the response to stdout. Only 'stream' mode with 'sample' operation supported.
-func Subscribe(address, username, password, deviceName string, sample, heartbeat int64, args ...string) error {
-	sbi := &nucleus.OpenConfig{}
-	tOpts := &tpb.TransportOption{
-		Address:  address,
-		Username: username,
-		Password: password,
-		Tls:      false,
-		TransportOption: &tpb.TransportOption_GnmiTransportOption{
-			GnmiTransportOption: &tpb.GnmiTransportOption{
-				Compression:     "",
-				GrpcDialOptions: nil,
-				Token:           "",
-				Encoding:        0,
-			},
-		},
-	}
-
-	device, err := nucleus.NewDevice(deviceName, tOpts, sbi)
-	if err != nil {
-		return err
-	}
-
-	opts := &gnmi.SubscribeOptions{
-		UpdatesOnly:       false,
-		Prefix:            "",
-		Mode:              "stream",
-		StreamMode:        "sample",
-		SampleInterval:    uint64(sample * time.Second.Nanoseconds()),
-		SuppressRedundant: false,
-		HeartbeatInterval: uint64(heartbeat * time.Second.Nanoseconds()),
-		Paths:             gnmi.SplitPaths(args),
-		Origin:            "",
-		Target:            address,
-	}
-	done := make(chan os.Signal, 1)
-	signal.Notify(done, syscall.SIGILL, syscall.SIGTERM)
-	ctx := context.WithValue(context.Background(), types.CtxKeyOpts, opts) //nolint
-	go func() {
-		if err := device.Transport().Subscribe(ctx); err != nil {
-			log.Fatal(err)
-		}
-	}()
-	fmt.Println("awaiting signal")
-	<-done
-	fmt.Println("exiting")
-
-	return nil
-}
diff --git a/api/target.go b/api/target.go
deleted file mode 100644
index abaf5c1db2aadd15464639fe5aa30acf65ea4cb3..0000000000000000000000000000000000000000
--- a/api/target.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package api
-
-import (
-	"context"
-	"net"
-	"reflect"
-
-	"code.fbi.h-da.de/cocsn/gosdn/forks/google/gnmi"
-	oc "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig"
-	pb "github.com/openconfig/gnmi/proto/gnmi"
-	"github.com/openconfig/goyang/pkg/yang"
-	"github.com/openconfig/ygot/util"
-	"github.com/openconfig/ygot/ygot"
-	log "github.com/sirupsen/logrus"
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/reflection"
-)
-
-type server struct {
-	*gnmi.Server
-}
-
-func callback(newConfig ygot.ValidatedGoStruct) error {
-	// Apply the config to your device and return nil if success. return error if fails.
-	//
-	// Do something ...
-	return nil
-}
-
-func newServer(model *gnmi.Model, config []byte) (*server, error) {
-	s, err := gnmi.NewServer(model, config, callback)
-	if err != nil {
-		return nil, err
-	}
-	return &server{Server: s}, nil
-}
-
-// Get overrides the Get func of gnmi.Target to provide user auth.
-func (s *server) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) {
-	return s.Server.Get(ctx, req)
-}
-
-// Set overrides the Set func of gnmi.Target to provide user auth.
-/*
-func (s *server) Set(ctx context.Context, req *pb.SetRequest) (*pb.SetResponse, error) {
-	msg, ok := credentials.AuthorizeUser(ctx)
-	if !ok {
-		log.Infof("denied a Set request: %v", msg)
-		return nil, status.Error(codes.PermissionDenied, msg)
-	}
-	log.Infof("allowed a Set request: %v", msg)
-	return s.Server.Set(ctx, req)
-}
-*/
-
-// Target starts a gNMI target listening on the specified port.
-func Target(bindAddr string) error {
-	entries := make([]*yang.Entry, 0)
-	for _, e := range oc.SchemaTree {
-		entries = append(entries, e)
-	}
-
-	modelData, err := util.FindModelData(entries)
-	if err != nil {
-		return err
-	}
-	// Google stuff from here
-	model := gnmi.NewModel(
-		modelData,
-		reflect.TypeOf((*oc.Device)(nil)),
-		oc.SchemaTree["Device"],
-		oc.Unmarshal,
-		oc.ΛEnum)
-
-	g := grpc.NewServer()
-
-	var configData []byte
-	s, err := newServer(model, configData)
-	if err != nil {
-		return err
-	}
-	pb.RegisterGNMIServer(g, s)
-	reflection.Register(g)
-
-	log.Infof("starting to listen on %s", bindAddr)
-	listen, err := net.Listen("tcp", bindAddr)
-	if err != nil {
-		return err
-	}
-
-	log.Info("starting to serve")
-	if err := g.Serve(listen); err != nil {
-		return err
-	}
-	return nil
-}
diff --git a/build/cd/deploy.go b/build/cd/deploy.go
index 121bd08a794ee8609e40131b0ee654969e470892..45b3b0998d8017bb1d1af6504bc0956eaa1fbe60 100644
--- a/build/cd/deploy.go
+++ b/build/cd/deploy.go
@@ -3,12 +3,13 @@ package main
 import (
 	"context"
 	"os"
+	"strconv"
 
 	nucleus "code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
 	log "github.com/sirupsen/logrus"
+	"github.com/spf13/viper"
 	appv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
-	netv1 "k8s.io/api/networking/v1beta1"
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
@@ -16,6 +17,16 @@ import (
 	"k8s.io/client-go/tools/clientcmd"
 )
 
+// GRPC defines the grpc port used by the deployment
+const GRPC = 55055
+
+// HTTP defines the http port used by the deployment
+const HTTP = 8080
+
+var service *corev1.Service
+var deployment *appv1.Deployment
+var config *corev1.ConfigMap
+
 func main() {
 	log.SetFormatter(&log.JSONFormatter{})
 	kubeconfig, err := clientcmd.BuildConfigFromFlags("https://api.ocp.fbi.h-da.de:6443", "")
@@ -37,15 +48,6 @@ func main() {
 		tag = os.Getenv("CI_COMMIT_SHA")
 	}
 
-	switch os.Getenv("CI_NIGHTLY") {
-	case "mainline":
-		tag = "nightly"
-	case "develop":
-		tag = "nightly-develop"
-	default:
-		tag = os.Getenv("CI_COMMIT_SHA")
-	}
-
 	switch os.Getenv("K8S_OP") {
 	case "create":
 		if err := create(clientset, tag); err != nil {
@@ -55,27 +57,56 @@ func main() {
 		if err := remove(clientset, tag); err != nil {
 			log.Fatal(err)
 		}
+	case "getenv":
+		if err := getenv(clientset, tag); err != nil {
+			log.Fatal(err)
+		}
 	default:
 		log.Fatal("invalid option")
 	}
 }
 
+func getenv(clientset *kubernetes.Clientset, tag string) error {
+	env := "gosdn-" + tag
+	opts := metav1.GetOptions{}
+	ctx := context.Background()
+	service, err := clientset.CoreV1().Services("cocsn").Get(ctx, env, opts)
+	if err != nil {
+		return err
+	}
+	var ip string
+	for _, ingress := range service.Status.LoadBalancer.Ingress {
+		ip = ingress.IP
+	}
+
+	var port string
+	for _, p := range service.Spec.Ports {
+		if p.Name == "grpc" {
+			port = strconv.Itoa(int(p.NodePort))
+		}
+	}
+	log.WithFields(log.Fields{
+		"ip":   ip,
+		"port": port,
+	}).Info(env)
+	viper.Set("GOSDN_TEST_API_ENDPOINT", ip+":"+port)
+	return viper.SafeWriteConfigAs(".k8s.toml")
+}
+
 // nolint
 func create(clientset *kubernetes.Clientset, tag string) error {
 	env := "gosdn-" + tag
-	service := createService(env)
-	ingress := createIngress(env)
-	config := createConfigMap(env)
-	deployment := createDeployment(env, tag)
+	deployment = createDeployment(env, tag)
 	opts := metav1.CreateOptions{}
 	ctx := context.Background()
-	_, err := clientset.CoreV1().Services("cocsn").Create(ctx, service, opts)
+
+	_, err := clientset.AppsV1().Deployments("cocsn").Create(ctx, deployment, opts)
 	if err != nil {
 		switch err.(type) {
 		case *errors.StatusError:
 			if err.(*errors.StatusError).ErrStatus.Code == 409 {
-				if err := update(clientset, service, env); err != nil {
-					return err
+				if err := update(clientset, deployment, env); err != nil {
+					log.Error(err)
 				}
 			} else {
 				log.Error(err)
@@ -84,25 +115,14 @@ func create(clientset *kubernetes.Clientset, tag string) error {
 			log.Error(err)
 		}
 	} else {
-		log.Printf("service %v created", service.Name)
+		log.Printf("deployment %v created", deployment.Name)
 	}
-	_, err = clientset.NetworkingV1beta1().Ingresses("cocsn").Create(ctx, ingress, opts)
+	deployment, err = clientset.AppsV1().Deployments("cocsn").Get(ctx, env, metav1.GetOptions{})
 	if err != nil {
-		switch err.(type) {
-		case *errors.StatusError:
-			if err.(*errors.StatusError).ErrStatus.Code == 409 {
-				if err := update(clientset, ingress, env); err != nil {
-					log.Error(err)
-				}
-			} else {
-				log.Error(err)
-			}
-		default:
-			log.Error(err)
-		}
-	} else {
-		log.Printf("ingress %v created", ingress.Name)
+		log.Error(err)
 	}
+
+	config = createConfigMap(env)
 	_, err = clientset.CoreV1().ConfigMaps("cocsn").Create(ctx, config, opts)
 	if err != nil {
 		switch err.(type) {
@@ -120,13 +140,15 @@ func create(clientset *kubernetes.Clientset, tag string) error {
 	} else {
 		log.Printf("configMap %v created", config.Name)
 	}
-	_, err = clientset.AppsV1().Deployments("cocsn").Create(ctx, deployment, opts)
+
+	service = createService(env)
+	_, err = clientset.CoreV1().Services("cocsn").Create(ctx, service, opts)
 	if err != nil {
 		switch err.(type) {
 		case *errors.StatusError:
 			if err.(*errors.StatusError).ErrStatus.Code == 409 {
-				if err := update(clientset, deployment, env); err != nil {
-					log.Error(err)
+				if err := update(clientset, service, env); err != nil {
+					return err
 				}
 			} else {
 				log.Error(err)
@@ -135,7 +157,7 @@ func create(clientset *kubernetes.Clientset, tag string) error {
 			log.Error(err)
 		}
 	} else {
-		log.Printf("deployment %v created", deployment.Name)
+		log.Printf("service %v created", service.Name)
 	}
 	return nil
 }
@@ -157,18 +179,6 @@ func update(clientset *kubernetes.Clientset, resource metav1.Common, env string)
 			return err
 		}
 		log.Printf("service %v updated", service.Name)
-	case *netv1.Ingress:
-		ingress := resource
-		i, err := clientset.NetworkingV1beta1().Ingresses("cocsn").Get(ctx, env, getOpts)
-		if err != nil {
-			return err
-		}
-		i.DeepCopyInto(ingress)
-		_, err = clientset.NetworkingV1beta1().Ingresses("cocsn").Update(ctx, ingress, opts)
-		if err != nil {
-			return err
-		}
-		log.Printf("ingress %v updated", ingress.Name)
 	case *corev1.ConfigMap:
 		config := resource
 		c, err := clientset.CoreV1().ConfigMaps("cocsn").Get(ctx, env+"-config", getOpts)
@@ -221,12 +231,6 @@ func remove(clientset *kubernetes.Clientset, tag string) error {
 	} else {
 		log.Printf("deployment %v deleted", env)
 	}
-	err = clientset.NetworkingV1beta1().Ingresses("cocsn").Delete(ctx, env, opts)
-	if err != nil {
-		log.Error(err)
-	} else {
-		log.Printf("ingress %v deleted", env)
-	}
 	return err
 }
 
@@ -240,23 +244,33 @@ func createService(environment string) *corev1.Service {
 			Name:      environment,
 			Namespace: "cocsn",
 			Labels:    map[string]string{"run": environment},
+			Annotations: map[string]string{
+				"metallb.universe.tf/address-pool": "hdanet",
+			},
+			OwnerReferences: []metav1.OwnerReference{
+				{
+					APIVersion: "v1",
+					Kind:       "Deployment",
+					Name:       environment,
+					UID:        deployment.GetUID(),
+				},
+			},
 		},
 		Spec: corev1.ServiceSpec{
 			Ports: []corev1.ServicePort{
 				{
 					Name:       "http",
-					Port:       8080,
-					TargetPort: intstr.IntOrString{IntVal: 8080},
+					Port:       HTTP,
+					TargetPort: intstr.IntOrString{IntVal: HTTP},
 				},
 				{
 					Name:       "grpc",
-					Port:       55055,
-					TargetPort: intstr.IntOrString{IntVal: 55055},
+					Port:       GRPC,
+					TargetPort: intstr.IntOrString{IntVal: GRPC},
 				},
 			},
-			Selector:     map[string]string{"run": environment},
-			Type:         "NodePort",
-			ExternalName: environment + ".apps.ocp.fbi.h-da.de",
+			Selector: map[string]string{"run": environment},
+			Type:     "LoadBalancer",
 		},
 	}
 }
@@ -301,11 +315,11 @@ func createDeployment(environment, hash string) *appv1.Deployment {
 							Ports: []corev1.ContainerPort{
 								{
 									Name:          "grpc",
-									ContainerPort: 55055,
+									ContainerPort: GRPC,
 								},
 								{
 									Name:          "http",
-									ContainerPort: 8080,
+									ContainerPort: HTTP,
 								},
 							},
 							VolumeMounts: []corev1.VolumeMount{
@@ -318,7 +332,7 @@ func createDeployment(environment, hash string) *appv1.Deployment {
 								Handler: corev1.Handler{
 									HTTPGet: &corev1.HTTPGetAction{
 										Path: "/livez",
-										Port: intstr.IntOrString{IntVal: 8080},
+										Port: intstr.IntOrString{IntVal: HTTP},
 									},
 								},
 								InitialDelaySeconds: 5,
@@ -328,7 +342,7 @@ func createDeployment(environment, hash string) *appv1.Deployment {
 								Handler: corev1.Handler{
 									HTTPGet: &corev1.HTTPGetAction{
 										Path: "/readyz",
-										Port: intstr.IntOrString{IntVal: 8080},
+										Port: intstr.IntOrString{IntVal: HTTP},
 									},
 								},
 								InitialDelaySeconds: 10,
@@ -358,41 +372,15 @@ func createConfigMap(env string) *corev1.ConfigMap {
 		},
 		ObjectMeta: metav1.ObjectMeta{
 			Name: env + "-config",
-		},
-		Data: map[string]string{"gosdn.toml": "#empty"},
-	}
-}
-
-func createIngress(env string) *netv1.Ingress {
-	return &netv1.Ingress{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "Ingress",
-			APIVersion: "v1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      env,
-			Namespace: "cocsn",
-		},
-		Spec: netv1.IngressSpec{
-			Rules: []netv1.IngressRule{
+			OwnerReferences: []metav1.OwnerReference{
 				{
-					Host: env + ".apps.ocp.fbi.h-da.de",
-					IngressRuleValue: netv1.IngressRuleValue{
-						HTTP: &netv1.HTTPIngressRuleValue{
-							Paths: []netv1.HTTPIngressPath{
-								{
-									Path: "/api",
-									Backend: netv1.IngressBackend{
-										ServiceName: env,
-										ServicePort: intstr.IntOrString{IntVal: 8080},
-									},
-								},
-							},
-						},
-					},
+					APIVersion: "v1",
+					Kind:       "Deployment",
+					Name:       env,
+					UID:        deployment.GetUID(),
 				},
 			},
 		},
-		Status: netv1.IngressStatus{},
+		Data: map[string]string{"gosdn.toml": "#empty"},
 	}
 }
diff --git a/build/ci/.build-container.yml b/build/ci/.build-container.yml
index 8a092f8a3f88e0b8147052d8032219379d68e294..2035443422a42301e9af33ea999ab0c0761a3173 100644
--- a/build/ci/.build-container.yml
+++ b/build/ci/.build-container.yml
@@ -1,6 +1,7 @@
 
 services:
-  - docker:19.03.12-dind
+  - name: docker:19.03.12-dind
+    command: ["--registry-mirror", "http://141.100.70.170:6000"]
 
 variables:
   DOCKER_TLS_CERTDIR: "/certs"
@@ -14,6 +15,7 @@ variables:
   tags:
     - dind
   script:
+    - docker info
     - >
       docker build \
         --build-arg GITLAB_USER=$GO_MODULES_USER \
diff --git a/build/ci/.deploy-k8s.yml b/build/ci/.deploy-k8s.yml
index becc83abf853f78228e31eead79b75064956815a..37c76a364c89c6993d22708998054017f7f0322c 100644
--- a/build/ci/.deploy-k8s.yml
+++ b/build/ci/.deploy-k8s.yml
@@ -17,7 +17,6 @@ build:k8s-bot:
   image: 
     name: bitnami/kubectl:latest
     entrypoint: [""]
-
   before_script:
     - echo "override global before script"
   variables:
diff --git a/build/ci/.golangci-config/.golangci.yml b/build/ci/.golangci-config/.golangci.yml
index 3492086f612c3f9ff49695cd0164f995a3051062..1a2b9183717c0c1a95b8782e15a5189ee2059c10 100644
--- a/build/ci/.golangci-config/.golangci.yml
+++ b/build/ci/.golangci-config/.golangci.yml
@@ -27,7 +27,7 @@ linters:
   enable:
     - gofmt
     - goimports
-    - golint
+    - revive 
     - gocyclo
     - govet
 issues:
diff --git a/build/ci/.test.yml b/build/ci/.test.yml
index 461480c311a45cb4a8721f501cd37c7910078dfc..4af5c562e81006edf8758d781a69e9cdba840773 100644
--- a/build/ci/.test.yml
+++ b/build/ci/.test.yml
@@ -1,12 +1,13 @@
-integration-test:
+.integration-test: &integration-test
   image: golang:1.16
   stage: integration-test
   needs:
     - job: "apply"
     - job: "deploy:integration-test"
+
   variables:
     GOSDN_LOG: "nolog"
-    GOSDN_TEST_API_ENDPOINT: http://gosdn-$CI_COMMIT_SHA.apps.ocp.fbi.h-da.de/api
+    GOSDN_TEST_API_ENDPOINT: gosdn-$CI_COMMIT_SHA.apps.ocp.fbi.h-da.de
     GOSDN_TEST_ENDPOINT_IP: 141.100.70.171
     GOSDN_CHANGE_TIMEOUT: "100ms"
   rules:
@@ -20,10 +21,38 @@ integration-test:
       allow_failure: true
       when: delayed
       start_in: 2 minutes
+
+integration-test:nucleus:
+  <<: *integration-test
+  script:
+    - cd ./test/integration
+    - go test -race -v -run TestGnmi_SetIntegration
+    - go test -race -v -run TestGnmi_GetIntegration
+    - go test -race -v -run TestGnmi_SubscribeIntegration
+    - go test -race -v -run TestGnmi_CapabilitiesIntegration
+
+integration-test:cmd:
+  <<: *integration-test
+  variables:
+    K8S_OP: "getenv"
+  needs:
+    - job: "build:merge-request"
+    - job: "apply"
+    - job: "deploy:integration-test"
+    - job: "build:k8s-bot"
+      artifacts: true
+  script:
+    - cd ./test/integration
+    - ./../../build/cd/k8s-bot
+    - go test -race -v -run TestCmdIntegration
+
+integration-test:cli:
+  <<: *integration-test
   script:
     - ./build/ci/generate_port.sh $CI_PIPELINE_ID >> port
     - export GOSDN_TEST_ENDPOINT_PORT=$(cat port)
-    - go test -race ./test/integration -v -coverprofile=coverage.out
+    - cd ./api
+    - go test -race -v -run TestApiIntegration
 
 .test: &test
   image: golang:1.16
@@ -49,4 +78,4 @@ unit-test:
 controller-test:
   script:
     - go test -race -v -run TestRun
-  <<: *test
\ No newline at end of file
+  <<: *test
diff --git a/controller.go b/controller.go
index f286874790320c61744951965ac6891a329105e4..f7f90f4668d26dbf9f7cfee92f58775b9c8ac592 100644
--- a/controller.go
+++ b/controller.go
@@ -71,6 +71,7 @@ func startGrpcServer() error {
 	if err != nil {
 		return err
 	}
+	log.Infof("listening to %v", lis.Addr())
 	c.grpcServer = grpc.NewServer()
 	c.nbi = nbi.NewNBI(c.pndc)
 	pb.RegisterCoreServer(c.grpcServer, c.nbi.Core)
diff --git a/go.mod b/go.mod
index f00c205ddc15db40bc315bd1d1935a69c6e32b61..60e1819514a94ca9fc97f316e0e7dcff6ad31380 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,6 @@ go 1.16
 
 require (
 	code.fbi.h-da.de/cocsn/api/go v0.0.0-20210609143151-4dabee5ab99a
-	code.fbi.h-da.de/cocsn/gosdn/api v0.0.0-20210610093733-cbc69d7e537d
 	code.fbi.h-da.de/cocsn/yang-models v0.0.7
 	github.com/aristanetworks/goarista v0.0.0-20201120222254-94a892eb0c6a
 	github.com/docker/docker v20.10.6+incompatible
diff --git a/go.sum b/go.sum
index 74dfc0175dd4ab8ef2afc908b4936cd85b46f5e1..22b65647e51ec81fcc37ff1f8df5a26f412b6d70 100644
--- a/go.sum
+++ b/go.sum
@@ -21,12 +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/go v0.0.0-20210609120033-1ef56612bd26/go.mod h1:2+rnE92IyXLbiy3/92EM7JrtsY5tXPAKX90QmsT2+m0=
 code.fbi.h-da.de/cocsn/api/go v0.0.0-20210609143151-4dabee5ab99a h1:C2tdh2A4RckZJXUymtF2ec8hE8mTkhn7EQJShv+i/Jk=
 code.fbi.h-da.de/cocsn/api/go v0.0.0-20210609143151-4dabee5ab99a/go.mod h1:2+rnE92IyXLbiy3/92EM7JrtsY5tXPAKX90QmsT2+m0=
-code.fbi.h-da.de/cocsn/gosdn v0.0.3-0.20210609130706-9cca50b3d195/go.mod h1:EK0GUnwNB+qMMjSJxGzLpfDw+KsUHS2LZkdzshldm4Q=
-code.fbi.h-da.de/cocsn/gosdn/api v0.0.0-20210610093733-cbc69d7e537d h1:olcB+ojVhToetF73X1waLRaTG7oZpdFvZ2yUIAKiiI8=
-code.fbi.h-da.de/cocsn/gosdn/api v0.0.0-20210610093733-cbc69d7e537d/go.mod h1:rn/FEXBauGQyFOIS2NhGd0EzF29Ibds0xt34Oo5wTgk=
 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=
@@ -237,11 +233,11 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
 github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
 github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
-github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
@@ -317,22 +313,18 @@ github.com/neo4j/neo4j-go-driver v1.8.3/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcL
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
 github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
-github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
 github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
-github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
-github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
 github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc=
 github.com/openconfig/gnmi v0.0.0-20200414194230-1597cc0f2600/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A=
 github.com/openconfig/gnmi v0.0.0-20200508230933-d19cebf5e7be/go.mod h1:M/EcuapNQgvzxo1DDXHK4tx3QpYM/uG4l591v33jG2A=
@@ -533,7 +525,6 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -570,7 +561,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -725,6 +715,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
diff --git a/http_test.go b/http_test.go
index 1d1d311bce7969e2c6b18dfd0fbab48deca3b148..7b4926e16be0ee4548124b825d02e1cb8fcdcc99 100644
--- a/http_test.go
+++ b/http_test.go
@@ -6,9 +6,7 @@ import (
 )
 
 func Test_httpApi(t *testing.T) {
-	if testing.Short() {
-		t.Skip("this test is executed separately")
-	}
+
 	tests := []struct {
 		name    string
 		request string
diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go
index 1185107479694aae1b22f77ee9e2d9ea26035169..77f078aa69c32a501adde00c46d2d1fc3f5a1677 100644
--- a/nucleus/gnmi_transport.go
+++ b/nucleus/gnmi_transport.go
@@ -41,24 +41,29 @@ type Gnmi struct {
 	config    *gnmi.Config
 }
 
-// NewGnmiTransport takes a struct of GnmiTransportOptions and returns a Gnmi
+// newGnmiTransport takes a struct of GnmiTransportOptions and returns a Gnmi
 // transport based on the values of it.
-func NewGnmiTransport(opts *tpb.TransportOption, sbi southbound.SouthboundInterface) (*Gnmi, error) {
+// Do not call directly. Use NewTransport() instead.
+func newGnmiTransport(opts *tpb.TransportOption, sbi southbound.SouthboundInterface) (*Gnmi, error) {
+	if opts == nil || sbi == nil {
+		return nil, &errors.ErrInvalidParameters{}
+	} else if opts.TransportOption == nil {
+		return nil, &errors.ErrInvalidParameters{}
+	}
 	gnmiConfig := &gnmi.Config{
 		Addr:        opts.Address,
 		Password:    opts.Password,
 		Username:    opts.Username,
 		TLS:         opts.Tls,
-		Compression: opts.GetGnmiTransportOption().Compression,
+		Compression: opts.GetGnmiTransportOption().GetCompression(),
 	}
 	c, err := gnmi.Dial(gnmiConfig)
 	if err != nil {
 		return nil, err
 	}
 	log.WithFields(log.Fields{
-		"target":   opts.Address,
-		"tls":      opts.Tls,
-		"encoding": opts.GetGnmiTransportOption().Encoding,
+		"target": opts.Address,
+		"tls":    opts.Tls,
 	}).Info("building new gNMI transport")
 	return &Gnmi{
 		SetNode:  sbi.SetNode(),
@@ -230,10 +235,7 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch
 			val, ok := update.Val.Value.(*gpb.TypedValue_JsonIetfVal)
 			if ok {
 				opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}}
-				if err := g.Unmarshal(val.JsonIetfVal, pathutils.ToStrings(fullPath), root, opts...); err != nil {
-					return err
-				}
-				return nil
+				return g.Unmarshal(val.JsonIetfVal, pathutils.ToStrings(fullPath), root, opts...)
 			}
 			// TODO(mk): Evaluate hardcoded model key
 			schema := models["Device"]
diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go
index 597cecd8ff954aa2b623df9f339115014ce08266..e3d418e741e8d25ff903b15ee856131b82db7a44 100644
--- a/nucleus/gnmi_transport_test.go
+++ b/nucleus/gnmi_transport_test.go
@@ -431,8 +431,6 @@ func TestGnmi_getWithRequest(t *testing.T) {
 }
 
 func TestNewGnmiTransport(t *testing.T) {
-	// TODO: revise test
-	t.Skip("deep equal for gnmi.Config broken")
 	type args struct {
 		opts *tpb.TransportOption
 	}
@@ -442,35 +440,6 @@ func TestNewGnmiTransport(t *testing.T) {
 		want    *Gnmi
 		wantErr bool
 	}{
-		{
-			name: "default",
-			args: args{
-				opts: &tpb.TransportOption{
-					Address:  "localhost:13371",
-					Username: "test",
-					Password: "test",
-					TransportOption: &tpb.TransportOption_GnmiTransportOption{
-						GnmiTransportOption: &tpb.GnmiTransportOption{},
-					},
-				}},
-			want: &Gnmi{
-				Options: &tpb.TransportOption{
-					Address:  "localhost:13371",
-					Username: "test",
-					Password: "test",
-					TransportOption: &tpb.TransportOption_GnmiTransportOption{
-						GnmiTransportOption: &tpb.GnmiTransportOption{},
-					},
-				},
-				client: nil,
-				config: &gnmi.Config{
-					Addr:     "localhost:13371",
-					Password: "test",
-					Username: "test",
-				},
-			},
-			wantErr: false,
-		},
 		{
 			name: "unsupported compression",
 			args: args{
@@ -483,34 +452,6 @@ func TestNewGnmiTransport(t *testing.T) {
 			want:    nil,
 			wantErr: true,
 		},
-		{
-			name: "certificate error no key file",
-			args: args{
-				opts: &tpb.TransportOption{
-					TransportOption: &tpb.TransportOption_GnmiTransportOption{
-						GnmiTransportOption: &tpb.GnmiTransportOption{
-							GrpcDialOptions: map[string]string{
-								"cert-file": "invalid",
-							},
-						},
-					}}},
-			want:    nil,
-			wantErr: true,
-		},
-		{
-			name: "certificate error no ca file",
-			args: args{
-				opts: &tpb.TransportOption{
-					TransportOption: &tpb.TransportOption_GnmiTransportOption{
-						GnmiTransportOption: &tpb.GnmiTransportOption{
-							GrpcDialOptions: map[string]string{
-								"ca-file": "invalid",
-							},
-						},
-					}}},
-			want:    nil,
-			wantErr: true,
-		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -518,7 +459,7 @@ func TestNewGnmiTransport(t *testing.T) {
 				startGnmiTarget <- gnmiConfig.Addr
 			}
 
-			got, err := NewGnmiTransport(tt.args.opts, NewSBI(spb.Type_OPENCONFIG))
+			got, err := newGnmiTransport(tt.args.opts, NewSBI(spb.Type_OPENCONFIG))
 			if (err != nil) != tt.wantErr {
 				t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -530,7 +471,7 @@ func TestNewGnmiTransport(t *testing.T) {
 				tt.want.RespChan = got.RespChan
 			}
 			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("NewGnmiTransport() got = %v, want %v", got, tt.want)
+				t.Errorf("NewGnmiTransport() got = %#v, want %#v", got, tt.want)
 			}
 			if tt.name == "default" {
 				stopGnmiTarget <- true
diff --git a/nucleus/southbound.go b/nucleus/southbound.go
index 0f2eda136b167053125b66d67890f6657b39699c..8538d7537e7fba8a72cab2f0a83e011ec455a3d5 100644
--- a/nucleus/southbound.go
+++ b/nucleus/southbound.go
@@ -57,10 +57,7 @@ func (oc *OpenConfig) Schema() *ytypes.Schema {
 // Needed for type assertion.
 func (oc *OpenConfig) SetNode() func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
 	return func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
-		if err := ytypes.SetNode(schema, root.(*openconfig.Device), path, val, opts...); err != nil {
-			return err
-		}
-		return nil
+		return ytypes.SetNode(schema, root.(*openconfig.Device), path, val, opts...)
 	}
 }
 
diff --git a/nucleus/store.go b/nucleus/store.go
index 94a741bc69e395b833e0267fecd3452774a49b6b..1a20b5833ba4202e3e996b5bfffdc6435e14678c 100644
--- a/nucleus/store.go
+++ b/nucleus/store.go
@@ -68,7 +68,9 @@ func (s genericStore) Add(item store.Storable) error {
 }
 
 // Get takes a Storable's UUID and returns the Storable. If the requested
-// Storable does not exist an error is returned.
+// Storable does not exist an error is returned. Get is only type safe for
+// this Storable interface. For type safe get operations on specialised stores
+// use GetDevice, GetPND, GetSBI, or GetChange respectively.
 func (s genericStore) Get(id uuid.UUID) (store.Storable, error) {
 	if !s.Exists(id) {
 		return nil, &errors.ErrNotFound{ID: id}
diff --git a/nucleus/store_test.go b/nucleus/store_test.go
index abba8efcc2153e642bca10c222ea629aa3834eb3..8468a34d458e438b390bb25ce6a57602e32374b7 100644
--- a/nucleus/store_test.go
+++ b/nucleus/store_test.go
@@ -272,18 +272,6 @@ func Test_sbiStore_get(t *testing.T) {
 			args:    args{id: defaultSbiID},
 			wantErr: true,
 		},
-		{
-			name: "fails wrong type",
-			fields: fields{
-				genericStore: genericStore{
-					did: &CommonDevice{
-						UUID: did,
-					},
-				},
-			},
-			args:    args{id: did},
-			wantErr: true,
-		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -345,18 +333,6 @@ func Test_pndStore_get(t *testing.T) {
 			args:    args{id: defaultPndID},
 			wantErr: true,
 		},
-		{
-			name: "fails wrong type",
-			fields: fields{
-				genericStore: genericStore{
-					did: &CommonDevice{
-						UUID: did,
-					},
-				},
-			},
-			args:    args{id: did},
-			wantErr: true,
-		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/nucleus/transport.go b/nucleus/transport.go
index 3d8523e954d448a9fa461b42bacf56bf8b83e668..f946dc332ddca903417f6f922635547e997074a1 100644
--- a/nucleus/transport.go
+++ b/nucleus/transport.go
@@ -16,11 +16,24 @@ func NewTransport(opts *tpb.TransportOption, sbi southbound.SouthboundInterface)
 			Param: "'opt' cannot be 'nil'",
 		}
 	}
+	if sbi == nil {
+		return nil, errors.ErrInvalidParameters{
+			Param: "'sbi' cannot be 'nil'",
+		}
+	}
+	if !validTransportOptions(opts) {
+		return nil, &errors.ErrInvalidTransportOptions{Opt: opts}
+	}
 	switch o := opts.TransportOption.(type) {
 	case *tpb.TransportOption_GnmiTransportOption:
-		return NewGnmiTransport(opts, sbi)
+		return newGnmiTransport(opts, sbi)
 	default:
 		return nil, &errors.ErrInvalidTransportOptions{Opt: o}
 
 	}
 }
+
+func validTransportOptions(opt *tpb.TransportOption) bool {
+	return opt.GetGnmiTransportOption() != nil ||
+		opt.GetRestconfTransportOption() != nil
+}
diff --git a/nucleus/transport_test.go b/nucleus/transport_test.go
index 787c12a36021b1efe304a388b49092dd8515910d..5a6264c261065d043531897a6896eb76e6a18ab8 100644
--- a/nucleus/transport_test.go
+++ b/nucleus/transport_test.go
@@ -1,14 +1,15 @@
 package nucleus
 
 import (
-	"reflect"
 	"testing"
 
 	tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
 	"code.fbi.h-da.de/cocsn/gosdn/interfaces/southbound"
-	"code.fbi.h-da.de/cocsn/gosdn/interfaces/transport"
+	gpb "github.com/openconfig/gnmi/proto/gnmi"
 )
 
+// TestNewTransport is for input validation only. Functional tests
+// are conducted at the transport implementation constructors.
 func TestNewTransport(t *testing.T) {
 	type args struct {
 		opts *tpb.TransportOption
@@ -17,21 +18,91 @@ func TestNewTransport(t *testing.T) {
 	tests := []struct {
 		name    string
 		args    args
-		want    transport.Transport
 		wantErr bool
 	}{
-		// TODO: Add test cases.
+		{
+			name: "default",
+			args: args{
+				opts: &tpb.TransportOption{
+					Address:  "test",
+					Username: "test",
+					Password: "test",
+					Tls:      false,
+					Csbi:     false,
+					TransportOption: &tpb.TransportOption_GnmiTransportOption{
+						GnmiTransportOption: &tpb.GnmiTransportOption{
+							Encoding: gpb.Encoding_PROTO,
+						},
+					},
+				},
+				sbi: &OpenConfig{},
+			},
+			wantErr: false,
+		},
+		{
+			name: "no opt",
+			args: args{
+				opts: nil,
+				sbi:  &OpenConfig{},
+			},
+			wantErr: true,
+		},
+		{
+			name: "no sbi",
+			args: args{
+				opts: &tpb.TransportOption{
+					Address:  "test",
+					Username: "test",
+					Password: "test",
+					Tls:      false,
+					Csbi:     false,
+					TransportOption: &tpb.TransportOption_GnmiTransportOption{
+						GnmiTransportOption: &tpb.GnmiTransportOption{
+							Encoding: gpb.Encoding_PROTO,
+						},
+					},
+				},
+				sbi: nil,
+			},
+			wantErr: true,
+		},
+		{
+			name: "no implementation options",
+			args: args{
+				opts: &tpb.TransportOption{
+					Address:  "test",
+					Username: "test",
+					Password: "test",
+					Tls:      false,
+					Csbi:     false,
+				},
+				sbi: &OpenConfig{},
+			},
+			wantErr: true,
+		},
+		{
+			name: "no inner implementation options",
+			args: args{
+				opts: &tpb.TransportOption{
+					Address:         "test",
+					Username:        "test",
+					Password:        "test",
+					Tls:             false,
+					Csbi:            false,
+					TransportOption: &tpb.TransportOption_GnmiTransportOption{},
+				},
+				sbi: &OpenConfig{},
+			},
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := NewTransport(tt.args.opts, tt.args.sbi)
+			_, err := NewTransport(tt.args.opts, tt.args.sbi)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("NewTransport() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("NewTransport() got = %v, want %v", got, tt.want)
-			}
 		})
 	}
 }
diff --git a/test/integration/cliIntegration_test.go b/test/integration/cliIntegration_test.go
deleted file mode 100644
index 97eda04e8cb0175fd76d3301bb42d5ec91bf8c73..0000000000000000000000000000000000000000
--- a/test/integration/cliIntegration_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package integration
-
-import (
-	"testing"
-
-	"code.fbi.h-da.de/cocsn/gosdn/api"
-)
-
-func TestCapabilities(t *testing.T) {
-	if testing.Short() {
-		t.Skip("skipping integration test")
-	}
-	type args struct {
-		a string
-		u string
-		p string
-	}
-	tests := []struct {
-		name    string
-		args    args
-		wantErr bool
-	}{
-		{
-			name: "default",
-			args: args{
-				a: testAddress,
-				u: testUsername,
-				p: testPassword,
-			},
-			wantErr: false,
-		},
-		{
-			name: "destination unreachable",
-			args: args{
-				a: unreachable,
-				u: testUsername,
-				p: testPassword,
-			},
-			wantErr: true,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if err := api.Capabilities(tt.args.a, tt.args.u, tt.args.p); (err != nil) != tt.wantErr {
-				t.Errorf("Capabilities() error = %v, wantErr %v", err, tt.wantErr)
-			}
-		})
-	}
-}
diff --git a/test/integration/nucleusIntegration_test.go b/test/integration/nucleusIntegration_test.go
index 4a5a29eebeb8d7acb0c0fdf7310314b83000694a..f92e93168ab2b35c4c16360d36e4fc1a3ea5593a 100644
--- a/test/integration/nucleusIntegration_test.go
+++ b/test/integration/nucleusIntegration_test.go
@@ -2,6 +2,8 @@ package integration
 
 import (
 	"context"
+	"fmt"
+	"os"
 	"reflect"
 	"sort"
 	"testing"
@@ -12,13 +14,95 @@ 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/errors"
 	"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
+	"code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto"
 	gpb "github.com/openconfig/gnmi/proto/gnmi"
+	log "github.com/sirupsen/logrus"
 	pb "google.golang.org/protobuf/proto"
 )
 
+const unreachable = "203.0.113.10:6030"
+const testPath = "/system/config/hostname"
+
+var testIP = "141.100.70.171"
+var testPort = "6030"
+var testAddress = testIP + ":" + testPort
+var testAPIEndpoint = "gosdn-latest.apps.ocp.fbi.h-da.de"
+var testUsername = "admin"
+var testPassword = "arista"
+var opt *tpb.TransportOption
 var gnmiMessages map[string]pb.Message
 
+func TestMain(m *testing.M) {
+	testSetupIntegration()
+	os.Exit(m.Run())
+}
+
+func testSetupIntegration() {
+	if os.Getenv("GOSDN_LOG") == "nolog" {
+		log.SetLevel(log.PanicLevel)
+	}
+
+	a := os.Getenv("GOSDN_TEST_ENDPOINT_IP")
+	if a != "" {
+		testIP = a
+		log.Infof("GOSDN_TEST_ENDPOINT_IP set to %v", testIP)
+	}
+	port := os.Getenv("GOSDN_TEST_ENDPOINT_PORT")
+	if port != "" {
+		testPort = port
+		log.Infof("GOSDN_TEST_ENDPOINT_PORT set to %v", testPort)
+	}
+	testAddress = testIP + ":" + testPort
+	fmt.Printf("Testadress: %s, Testport: %s, Port: %s", testAddress, testPort, port)
+
+	api := os.Getenv("GOSDN_TEST_API_ENDPOINT")
+	if api != "" {
+		testAPIEndpoint = api
+		log.Infof("GOSDN_TEST_API_ENDPOINT set to %v", testAPIEndpoint)
+	}
+	u := os.Getenv("GOSDN_TEST_USER")
+	if u != "" {
+		testUsername = u
+		log.Infof("GOSDN_TEST_USER set to %v", testUsername)
+	}
+	p := os.Getenv("GOSDN_TEST_PASSWORD")
+	if p != "" {
+		testPassword = p
+		log.Infof("GOSDN_TEST_PASSWORD set to %v", testPassword)
+	}
+
+	gnmiMessages = map[string]pb.Message{
+		"../proto/cap-resp-arista-ceos":                  &gpb.CapabilityResponse{},
+		"../proto/req-full-node":                         &gpb.GetRequest{},
+		"../proto/req-full-node-arista-ceos":             &gpb.GetRequest{},
+		"../proto/req-interfaces-arista-ceos":            &gpb.GetRequest{},
+		"../proto/req-interfaces-interface-arista-ceos":  &gpb.GetRequest{},
+		"../proto/req-interfaces-wildcard":               &gpb.GetRequest{},
+		"../proto/resp-full-node":                        &gpb.GetResponse{},
+		"../proto/resp-full-node-arista-ceos":            &gpb.GetResponse{},
+		"../proto/resp-interfaces-arista-ceos":           &gpb.GetResponse{},
+		"../proto/resp-interfaces-interface-arista-ceos": &gpb.GetResponse{},
+		"../proto/resp-interfaces-wildcard":              &gpb.GetResponse{},
+		"../proto/resp-set-system-config-hostname":       &gpb.SetResponse{},
+	}
+	for k, v := range gnmiMessages {
+		if err := proto.Read(k, v); err != nil {
+			log.Fatalf("error parsing %v: %v", k, err)
+		}
+	}
+
+	opt = &tpb.TransportOption{
+		Address:  testAddress,
+		Username: testUsername,
+		Password: testPassword,
+		TransportOption: &tpb.TransportOption_GnmiTransportOption{
+			GnmiTransportOption: &tpb.GnmiTransportOption{},
+		},
+	}
+}
+
 func TestGnmi_SetIntegration(t *testing.T) {
 	if testing.Short() {
 		t.Skip("skipping integration test")
@@ -41,6 +125,8 @@ func TestGnmi_SetIntegration(t *testing.T) {
 			fields: fields{
 				opt: &tpb.TransportOption{
 					Address: "203.0.113.10:6030",
+					TransportOption: &tpb.TransportOption_GnmiTransportOption{
+						GnmiTransportOption: &tpb.GnmiTransportOption{}},
 				},
 			},
 			args: args{
@@ -70,7 +156,7 @@ func TestGnmi_SetIntegration(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			g, err := nucleus.NewGnmiTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG))
+			g, err := nucleus.NewTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG))
 			if err != nil {
 				t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -134,7 +220,7 @@ func TestGnmi_GetIntegration(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			g, err := nucleus.NewGnmiTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG))
+			g, err := nucleus.NewTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG))
 			if err != nil {
 				t.Error(err)
 				return
@@ -172,9 +258,9 @@ func TestGnmi_SubscribeIntegration(t *testing.T) {
 			name: "default",
 			fields: fields{
 				opt: &tpb.TransportOption{
-					Address:  "",
-					Username: "",
-					Password: "",
+					Address:  testAddress,
+					Username: testUsername,
+					Password: testPassword,
 					Tls:      false,
 					TransportOption: &tpb.TransportOption_GnmiTransportOption{
 						GnmiTransportOption: &tpb.GnmiTransportOption{
@@ -238,7 +324,7 @@ func TestGnmi_SubscribeIntegration(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			var wantErr = tt.wantErr
-			g, err := nucleus.NewGnmiTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG))
+			g, err := nucleus.NewTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG))
 			if err != nil {
 				t.Error(err)
 				return
@@ -250,7 +336,7 @@ func TestGnmi_SubscribeIntegration(t *testing.T) {
 				if (subErr != nil) != wantErr {
 					if !wantErr && subErr != nil {
 						if subErr.Error() != "rpc error: code = Canceled desc = context canceled" {
-							t.Errorf("Subscribe() error = %v, wantErr %v", err, tt.wantErr)
+							t.Errorf("Subscribe() error = %v, wantErr %v", subErr, tt.wantErr)
 						}
 					}
 				}
@@ -313,11 +399,15 @@ func TestGnmi_CapabilitiesIntegration(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			g, err := nucleus.NewGnmiTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG))
+			tr, err := nucleus.NewTransport(tt.fields.opt, nucleus.NewSBI(spb.Type_OPENCONFIG))
 			if err != nil {
 				t.Error(err)
 				return
 			}
+			g, ok := tr.(*nucleus.Gnmi)
+			if !ok {
+				t.Error(&errors.ErrInvalidTypeAssertion{})
+			}
 			resp, err := g.Capabilities(tt.args.ctx)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Capabilities() error = %v, wantErr %v", err, tt.wantErr)