diff --git a/.gitignore b/.gitignore
index d5a3ac156facca5eb8e2a3451f4b6e0ef3a6955e..c76484002e90465087349e42f88690fcc99b26e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@ build-tools/
 
 # test files
 report.xml
+test/plugin/**/*.so
 nucleus/util/proto/*_test
 
 # Binary
@@ -31,3 +32,4 @@ gosdn
 
 # persistent data
 **/stores/**
+plugins
diff --git a/api/grpc.go b/api/grpc.go
index a9ef08f46326e98f8ae012ddef9e23a7426a1227..f0880b4e8249a182ee5aad3baed4a47355e2bf59 100644
--- a/api/grpc.go
+++ b/api/grpc.go
@@ -124,6 +124,20 @@ func GetPnds(addr string, args ...string) (*pb.GetPndListResponse, error) {
 	return coreClient.GetPndList(ctx, req)
 }
 
+// deletePnd requests a deletion of the provided PND.
+func deletePnd(addr string, pid string) (*pb.DeletePndResponse, error) {
+	coreClient, err := nbi.CoreClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+	ctx := context.Background()
+	req := &pb.DeletePndRequest{
+		Timestamp: time.Now().UnixNano(),
+		Pid:       pid,
+	}
+	return coreClient.DeletePnd(ctx, req)
+}
+
 // getChanges requests all pending and unconfirmed changes from the controller
 func getChanges(addr, pnd string) (*ppb.GetChangeListResponse, error) {
 	ctx := context.Background()
diff --git a/api/initialise_test.go b/api/initialise_test.go
index 4dbb90a3e363242f7e1a6afb8fe1ab4eb9594574..078588fce5d88f0d673f9e2e6de0db55d3f47777 100644
--- a/api/initialise_test.go
+++ b/api/initialise_test.go
@@ -53,8 +53,6 @@ func bootstrapUnitTest() {
 	}
 	lis = bufconn.Listen(bufSize)
 	s := grpc.NewServer()
-	pndStore = store.NewPndStore()
-	sbiStore = store.NewSbiStore()
 
 	changeUUID, err := uuid.Parse(changeID)
 	if err != nil {
@@ -76,6 +74,9 @@ func bootstrapUnitTest() {
 		log.Fatal(err)
 	}
 
+	pndStore = store.NewPndStore()
+	sbiStore = store.NewSbiStore(pndUUID)
+
 	mockChange := &mocks.Change{}
 	mockChange.On("Age").Return(time.Hour)
 	mockChange.On("State").Return(ppb.ChangeState_CHANGE_STATE_INCONSISTENT)
diff --git a/api/pnd.go b/api/pnd.go
index ea6e78a8f02e2c1643143940fcc18628d9609630..9b647906ac6ca064f4ee0efb91cae9d34f30c142 100644
--- a/api/pnd.go
+++ b/api/pnd.go
@@ -1,11 +1,10 @@
 package api
 
 import (
+	"code.fbi.h-da.de/danet/api/go/gosdn/core"
 	ppb "code.fbi.h-da.de/danet/api/go/gosdn/pnd"
 	tpb "code.fbi.h-da.de/danet/api/go/gosdn/transport"
 	"code.fbi.h-da.de/danet/gosdn/interfaces/change"
-	"code.fbi.h-da.de/danet/gosdn/interfaces/device"
-	"code.fbi.h-da.de/danet/gosdn/interfaces/networkdomain"
 	"code.fbi.h-da.de/danet/gosdn/interfaces/southbound"
 	"code.fbi.h-da.de/danet/gosdn/interfaces/store"
 	"code.fbi.h-da.de/danet/gosdn/nucleus/errors"
@@ -23,7 +22,7 @@ type PrincipalNetworkDomainAdapter struct {
 
 // NewAdapter creates a PND Adapter. It requires a valid PND UUID and a reachable
 // goSDN endpoint.
-func NewAdapter(id, endpoint string) (networkdomain.NetworkDomain, error) {
+func NewAdapter(id, endpoint string) (*PrincipalNetworkDomainAdapter, error) {
 	pid, err := uuid.Parse(id)
 	if err != nil {
 		return nil, err
@@ -51,41 +50,53 @@ func (p *PrincipalNetworkDomainAdapter) RemoveSbi(uuid.UUID) error {
 
 // AddDevice adds a new device to the controller. The device name is optional.
 // If no name is provided a name will be generated upon device creation.
-func (p *PrincipalNetworkDomainAdapter) AddDevice(name string, opts *tpb.TransportOption, sid uuid.UUID) error {
+func (p *PrincipalNetworkDomainAdapter) AddDevice(name string, opts *tpb.TransportOption, sid uuid.UUID) (*ppb.SetOndListResponse, error) {
 	resp, err := addDevice(p.endpoint, name, opts, sid, p.ID())
 	if err != nil {
-		return err
+		return nil, err
 	}
-	log.Info(resp)
-	return nil
-}
-
-// AddDeviceFromStore adds a new device from store to the controller. Currently not implemented
-func (p *PrincipalNetworkDomainAdapter) AddDeviceFromStore(name string, did uuid.UUID, opts *tpb.TransportOption, sid uuid.UUID) error {
-	return &errors.ErrNotYetImplemented{}
+	return resp, nil
 }
 
 // GetDevice requests one or multiple devices belonging to a given
 // PrincipalNetworkDomain from the controller. If no device identifier
 // is provided, all devices are requested.
-func (p *PrincipalNetworkDomainAdapter) GetDevice(identifier string) (device.Device, error) {
-	resp, err := getDevice(p.endpoint, p.id.String(), identifier)
+func (p *PrincipalNetworkDomainAdapter) GetDevice(identifier ...string) ([]*ppb.OrchestratedNetworkingDevice, error) {
+	resp, err := getDevice(p.endpoint, p.id.String(), identifier...)
 	if err != nil {
 		return nil, err
 	}
-	// TODO: Parse into device.Device
-	log.Info(resp)
-	return nil, nil
+	return resp.Ond, nil
+}
+
+// GetDevices requests all devices belonging to the PrincipalNetworkDomain
+// attached to this adapter.
+// TODO: this function could be removed, since GetDevice() with no identifier
+// provided returns all devices too.
+func (p *PrincipalNetworkDomainAdapter) GetDevices() ([]*ppb.OrchestratedNetworkingDevice, error) {
+	resp, err := getDevices(p.endpoint, p.id.String())
+	if err != nil {
+		return nil, err
+	}
+	return resp.Ond, nil
 }
 
-// RemoveDevice removes a device from the PND Adapter
-func (p *PrincipalNetworkDomainAdapter) RemoveDevice(did uuid.UUID) error {
+// RemoveDevice removes a device from the controller
+func (p *PrincipalNetworkDomainAdapter) RemoveDevice(did uuid.UUID) (*ppb.DeleteOndResponse, error) {
 	resp, err := deleteDevice(p.endpoint, p.id.String(), did.String())
 	if err != nil {
-		return err
+		return nil, err
 	}
-	log.Info(resp)
-	return nil
+	return resp, nil
+}
+
+// RemovePnd removes a PND from the controller
+func (p *PrincipalNetworkDomainAdapter) RemovePnd(pid uuid.UUID) (*core.DeletePndResponse, error) {
+	resp, err := deletePnd(p.endpoint, pid.String())
+	if err != nil {
+		return nil, err
+	}
+	return resp, nil
 }
 
 // Devices sends an API call to the controller requesting the UUIDs of all
@@ -159,6 +170,11 @@ func (p *PrincipalNetworkDomainAdapter) ID() uuid.UUID {
 	return p.id
 }
 
+// Endpoint returns the PND Adapter's endpoint
+func (p *PrincipalNetworkDomainAdapter) Endpoint() string {
+	return p.endpoint
+}
+
 // PendingChanges sends an API call to the controller requesting
 // the UUIDs of all pending changes
 func (p *PrincipalNetworkDomainAdapter) PendingChanges() []uuid.UUID {
diff --git a/api/pnd_test.go b/api/pnd_test.go
index 0a2e9d00ccdde690244e0ddcec8395f1feaff6c1..de515ca9d11ea4b6f34392e36c4a2c7854680d1d 100644
--- a/api/pnd_test.go
+++ b/api/pnd_test.go
@@ -148,7 +148,7 @@ func TestPrincipalNetworkDomainAdapter_AddDevice(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if err := p.AddDevice(tt.args.name, tt.args.opts, tt.args.sid); (err != nil) != tt.wantErr {
+			if _, err := p.AddDevice(tt.args.name, tt.args.opts, tt.args.sid); (err != nil) != tt.wantErr {
 				t.Errorf("PrincipalNetworkDomainAdapter.AddDevice() error = %v, wantErr %v", err, tt.wantErr)
 			}
 		})
@@ -212,7 +212,7 @@ func TestPrincipalNetworkDomainAdapter_RemoveDevice(t *testing.T) {
 				id:       tt.fields.id,
 				endpoint: tt.fields.endpoint,
 			}
-			if err := p.RemoveDevice(tt.args.did); (err != nil) != tt.wantErr {
+			if _, err := p.RemoveDevice(tt.args.did); (err != nil) != tt.wantErr {
 				t.Errorf("PrincipalNetworkDomainAdapter.RemoveDevice() error = %v, wantErr %v", err, tt.wantErr)
 			}
 		})
diff --git a/cmd/root.go b/cmd/root.go
index 47cbec82915ad111268370cea0dbb191b2265a7e..91866fb4176abc2907f80fb9e592f5d89593426b 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -48,6 +48,7 @@ var cfgFile string
 var loglevel string
 var grpcPort string
 var csbiOrchestrator string
+var pluginFolder string
 
 // rootCmd represents the base command when called without any subcommands
 var rootCmd = &cobra.Command{
@@ -79,6 +80,7 @@ func init() {
 
 	rootCmd.Flags().StringVar(&grpcPort, "grpc-port", "", "port for gRPC NBI")
 	rootCmd.Flags().StringVar(&csbiOrchestrator, "csbi-orchestrator", "", "csbi orchestrator address")
+	rootCmd.Flags().StringVar(&pluginFolder, "plugin-folder", "", "folder holding all goSDN specific plugins")
 }
 
 const (
@@ -111,8 +113,11 @@ func initConfig() {
 	if err := viper.BindPFlags(rootCmd.Flags()); err != nil {
 		log.Fatal(err)
 	}
+
+	// Set default configuration values
 	viper.SetDefault("socket", ":55055")
 	viper.SetDefault("csbi-orchestrator", "localhost:55056")
+	viper.SetDefault("plugin-folder", "plugins")
 
 	ll := viper.GetString("GOSDN_LOG")
 	if ll != "" {
diff --git a/go.mod b/go.mod
index e26fa1d1dfc768d26a78aeb57f4fac9bd53c7a73..b23e2355eceb2fa39da42e4b4a901614ba8c7307 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module code.fbi.h-da.de/danet/gosdn
 go 1.17
 
 require (
-	code.fbi.h-da.de/danet/api v0.2.5-0.20220301081709-d68d321135a7
+	code.fbi.h-da.de/danet/api v0.2.5-0.20220309155741-8041238ad200
 	code.fbi.h-da.de/danet/forks/goarista v0.0.0-20210709163519-47ee8958ef40
 	code.fbi.h-da.de/danet/forks/google v0.0.0-20210709163519-47ee8958ef40
 	code.fbi.h-da.de/danet/yang-models v0.1.0
@@ -23,6 +23,7 @@ require (
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 	google.golang.org/grpc v1.43.0
 	google.golang.org/protobuf v1.27.1
+	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
 )
 
 require (
@@ -55,5 +56,4 @@ require (
 	google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
 	gopkg.in/ini.v1 v1.64.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
-	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 )
diff --git a/go.sum b/go.sum
index 9321fe8926b9188a55d5077c218fc5b8ffff2a7f..7ba1b0af190a7b51ffbc68b1a3cb641039733a0e 100644
--- a/go.sum
+++ b/go.sum
@@ -48,6 +48,10 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 code.fbi.h-da.de/danet/api v0.2.5-0.20220301081709-d68d321135a7 h1:DxcOZDploBC0GdyURQQprwo6jOTUUQ8spXgRQXtK4xE=
 code.fbi.h-da.de/danet/api v0.2.5-0.20220301081709-d68d321135a7/go.mod h1:J1wwKAHhP3HprrzoNs6f5C56znzvns69FU56oItc3kc=
+code.fbi.h-da.de/danet/api v0.2.5-0.20220308110152-3bad30e00536 h1:Yi+0ZiROQ0GG7vbsx7jiGy1pimPw77SdNS+8unZpG30=
+code.fbi.h-da.de/danet/api v0.2.5-0.20220308110152-3bad30e00536/go.mod h1:J1wwKAHhP3HprrzoNs6f5C56znzvns69FU56oItc3kc=
+code.fbi.h-da.de/danet/api v0.2.5-0.20220309155741-8041238ad200 h1:ke5wQPnSg78OpMCBpdwPIW91dwKLBwS+K5rrBPGTagU=
+code.fbi.h-da.de/danet/api v0.2.5-0.20220309155741-8041238ad200/go.mod h1:J1wwKAHhP3HprrzoNs6f5C56znzvns69FU56oItc3kc=
 code.fbi.h-da.de/danet/forks/goarista v0.0.0-20210709163519-47ee8958ef40 h1:x7rVYGqfJSMWuYBp+JE6JVMcFP03Gx0mnR2ftsgqjVI=
 code.fbi.h-da.de/danet/forks/goarista v0.0.0-20210709163519-47ee8958ef40/go.mod h1:uVe3gCeF2DcIho8K9CIO46uAkHW/lUF+fAaUX1vHrF0=
 code.fbi.h-da.de/danet/forks/google v0.0.0-20210709163519-47ee8958ef40 h1:B45k5tGEdjjdsKK4f+0dQoyReFmsWdwYEzHofA7DPM8=
diff --git a/interfaces/plugin/plugin.go b/interfaces/plugin/plugin.go
new file mode 100644
index 0000000000000000000000000000000000000000..eb1541ace1eb79e378b3beadcdc6bbc36b9aa87a
--- /dev/null
+++ b/interfaces/plugin/plugin.go
@@ -0,0 +1,106 @@
+package plugin
+
+import (
+	"fmt"
+	"io/ioutil"
+	"regexp"
+
+	"code.fbi.h-da.de/danet/gosdn/nucleus/errors"
+	"github.com/google/uuid"
+	"gopkg.in/yaml.v3"
+)
+
+// State represents the current state of a plugin within the controller. Since
+// the plugins used within the controller are basic go plugins, they can be
+// CREATED, BUILT, LOADED or FAULTY. A plugin can not be unloaded (this is a
+// limitation of go plugins in general).
+type State int64
+
+const (
+	//CREATED state describes a plugin which has been created but is not yet
+	//built.
+	CREATED State = iota
+	// BUILT state describes a plugin which is built and can be loaded into the
+	// controller.
+	BUILT
+	// LOADED state describes a plugin which is running within the controller.
+	LOADED
+	// FAULTY state describes a plugin which couldn't be built or loaded.
+	FAULTY
+)
+
+// Plugin describes an interface for a plugin within the controller - a plugin
+// in the context of the controller is a basic go plugin. A plugin satisfies
+// the Storable interface and can be stored.
+//
+// Note(mbauch): Currently a plugin is built through the controller itself.
+// This is fine for the time being, but should be reconsidered for the time to
+// come. In the future we should provide a build environment that allows to
+// build plugins within the same environment as the controller itself.
+type Plugin interface {
+	ID() uuid.UUID
+	State() State
+	Path() string
+	Manifest() *Manifest
+	Update() error
+}
+
+// Manifest represents the manifest of a plugin.
+type Manifest struct {
+	// Name of the plugin
+	Name string `yaml:"name"`
+	// Author of the plugin
+	Author string `yaml:"author"`
+	// Version of the plugin
+	Version string `yaml:"version"`
+}
+
+// Validate is a method to check if the manifest is valid and is compliant with
+// the requirements.
+func (m *Manifest) Validate() error {
+	errs := []error{}
+	if m.Name == "" {
+		errs = append(errs, fmt.Errorf("Name is required"))
+	}
+	if m.Author == "" {
+		errs = append(errs, fmt.Errorf("Author is required"))
+	}
+	if m.Version == "" {
+		errs = append(errs, fmt.Errorf("Version is required"))
+	}
+	// regex from: https://stackoverflow.com/a/68921827
+	validVersion, err := regexp.MatchString(`^v([1-9]\d*|0)(\.(([1-9]\d*)|0)){2}$`,
+		m.Version)
+	if err != nil {
+		errs = append(errs, err)
+	}
+	if !validVersion {
+		errs = append(errs, fmt.Errorf("Version has to be of form: vX.X.X"))
+	}
+	if len(errs) != 0 {
+		return errors.ErrorList{Errors: errs}
+	}
+	return nil
+}
+
+// ReadManifestFromFile reads a manifest file and returns a pointer to a newly
+// created Manifest.
+func ReadManifestFromFile(path string) (*Manifest, error) {
+	manifest := &Manifest{}
+
+	manifestFile, err := ioutil.ReadFile(path)
+	if err != nil {
+		return nil, err
+	}
+	err = yaml.Unmarshal(manifestFile, manifest)
+	if err != nil {
+		return nil, err
+	}
+
+	// validate the loaded manifest
+	if err := manifest.Validate(); err != nil {
+		return nil, err
+	}
+
+	return manifest, nil
+}
diff --git a/interfaces/southbound/sbi.go b/interfaces/southbound/sbi.go
index 0024081c35a14b367adab9e077ad716ba3f8db03..0699b2763a8f7fd1d4966269c1e7a1b693739036 100644
--- a/interfaces/southbound/sbi.go
+++ b/interfaces/southbound/sbi.go
@@ -13,15 +13,13 @@ import (
 // SouthboundInterface provides an
 // interface for SBI implementations
 type SouthboundInterface interface { // nolint
-	// deprecated
-	SbiIdentifier() string
-
 	// SetNode injects SBI specific model
 	// representation to the transport.
 	// Needed for type assertion.
 	SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error
 	Schema() *ytypes.Schema
 	ID() uuid.UUID
+	SetID(id uuid.UUID)
 	Type() spb.Type
 	Unmarshal([]byte, *gpb.Path, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error
 }
diff --git a/mocks/Csbi.go b/mocks/Csbi.go
new file mode 100644
index 0000000000000000000000000000000000000000..e87f8d70a58170d018e4fa7aecaee24d9b6dca80
--- /dev/null
+++ b/mocks/Csbi.go
@@ -0,0 +1,177 @@
+// Code generated by mockery v2.9.4. DO NOT EDIT.
+// It says DO NOT EDIT but it has been edited. :-)
+// Combines a Plugin and a SouthboundInterface to represent a Csbi.
+
+package mocks
+
+import (
+	gosdnsouthbound "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
+	plugin "code.fbi.h-da.de/danet/gosdn/interfaces/plugin"
+	gnmi "github.com/openconfig/gnmi/proto/gnmi"
+
+	mock "github.com/stretchr/testify/mock"
+
+	uuid "github.com/google/uuid"
+
+	yang "github.com/openconfig/goyang/pkg/yang"
+
+	ygot "github.com/openconfig/ygot/ygot"
+
+	ytypes "github.com/openconfig/ygot/ytypes"
+)
+
+// Csbi is an autogenerated mock type for the Csbi type
+type Csbi struct {
+	mock.Mock
+}
+
+// ID provides a mock function with given fields:
+func (_m *Csbi) ID() uuid.UUID {
+	ret := _m.Called()
+
+	var r0 uuid.UUID
+	if rf, ok := ret.Get(0).(func() uuid.UUID); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(uuid.UUID)
+		}
+	}
+
+	return r0
+}
+
+// Manifest provides a mock function with given fields:
+func (_m *Csbi) Manifest() *plugin.Manifest {
+	ret := _m.Called()
+
+	var r0 *plugin.Manifest
+	if rf, ok := ret.Get(0).(func() *plugin.Manifest); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*plugin.Manifest)
+		}
+	}
+
+	return r0
+}
+
+// Path provides a mock function with given fields:
+func (_m *Csbi) Path() string {
+	ret := _m.Called()
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func() string); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// State provides a mock function with given fields:
+func (_m *Csbi) State() plugin.State {
+	ret := _m.Called()
+
+	var r0 plugin.State
+	if rf, ok := ret.Get(0).(func() plugin.State); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(plugin.State)
+	}
+
+	return r0
+}
+
+// Update provides a mock function with given fields:
+func (_m *Csbi) Update() error {
+	ret := _m.Called()
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func() error); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Schema provides a mock function with given fields:
+func (_m *Csbi) Schema() *ytypes.Schema {
+	ret := _m.Called()
+
+	var r0 *ytypes.Schema
+	if rf, ok := ret.Get(0).(func() *ytypes.Schema); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*ytypes.Schema)
+		}
+	}
+
+	return r0
+}
+
+// SetID provides a mock function with given fields: id
+func (_m *Csbi) SetID(id uuid.UUID) {
+	_m.Called(id)
+}
+
+// SetNode provides a mock function with given fields: schema, root, path, val, opts
+func (_m *Csbi) SetNode(schema *yang.Entry, root interface{}, path *gnmi.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, schema, root, path, val)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*yang.Entry, interface{}, *gnmi.Path, interface{}, ...ytypes.SetNodeOpt) error); ok {
+		r0 = rf(schema, root, path, val, opts...)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Type provides a mock function with given fields:
+func (_m *Csbi) Type() gosdnsouthbound.Type {
+	ret := _m.Called()
+
+	var r0 gosdnsouthbound.Type
+	if rf, ok := ret.Get(0).(func() gosdnsouthbound.Type); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(gosdnsouthbound.Type)
+	}
+
+	return r0
+}
+
+// Unmarshal provides a mock function with given fields: _a0, _a1, _a2, _a3
+func (_m *Csbi) Unmarshal(_a0 []byte, _a1 *gnmi.Path, _a2 ygot.ValidatedGoStruct, _a3 ...ytypes.UnmarshalOpt) error {
+	_va := make([]interface{}, len(_a3))
+	for _i := range _a3 {
+		_va[_i] = _a3[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, _a0, _a1, _a2)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func([]byte, *gnmi.Path, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error); ok {
+		r0 = rf(_a0, _a1, _a2, _a3...)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
diff --git a/mocks/GenericGoStructClient.go b/mocks/GenericGoStructClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..9aa0aee7ee2c83ca716d32bd1c66c5ff4ab5f6d4
--- /dev/null
+++ b/mocks/GenericGoStructClient.go
@@ -0,0 +1,137 @@
+// Code generated by mockery v2.9.4. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	csbi "code.fbi.h-da.de/danet/api/go/gosdn/csbi"
+	metadata "google.golang.org/grpc/metadata"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// GenericGoStructClient is an autogenerated mock type for the GenericGoStructClient type
+type GenericGoStructClient struct {
+	mock.Mock
+}
+
+// CloseSend provides a mock function with given fields:
+func (_m *GenericGoStructClient) CloseSend() error {
+	ret := _m.Called()
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func() error); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Context provides a mock function with given fields:
+func (_m *GenericGoStructClient) Context() context.Context {
+	ret := _m.Called()
+
+	var r0 context.Context
+	if rf, ok := ret.Get(0).(func() context.Context); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(context.Context)
+		}
+	}
+
+	return r0
+}
+
+// Header provides a mock function with given fields:
+func (_m *GenericGoStructClient) Header() (metadata.MD, error) {
+	ret := _m.Called()
+
+	var r0 metadata.MD
+	if rf, ok := ret.Get(0).(func() metadata.MD); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(metadata.MD)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Recv provides a mock function with given fields:
+func (_m *GenericGoStructClient) Recv() (*csbi.Payload, error) {
+	ret := _m.Called()
+
+	var r0 *csbi.Payload
+	if rf, ok := ret.Get(0).(func() *csbi.Payload); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*csbi.Payload)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func() error); ok {
+		r1 = rf()
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// RecvMsg provides a mock function with given fields: m
+func (_m *GenericGoStructClient) RecvMsg(m interface{}) error {
+	ret := _m.Called(m)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(interface{}) error); ok {
+		r0 = rf(m)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// SendMsg provides a mock function with given fields: m
+func (_m *GenericGoStructClient) SendMsg(m interface{}) error {
+	ret := _m.Called(m)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(interface{}) error); ok {
+		r0 = rf(m)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// Trailer provides a mock function with given fields:
+func (_m *GenericGoStructClient) Trailer() metadata.MD {
+	ret := _m.Called()
+
+	var r0 metadata.MD
+	if rf, ok := ret.Get(0).(func() metadata.MD); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(metadata.MD)
+		}
+	}
+
+	return r0
+}
diff --git a/mocks/Plugin.go b/mocks/Plugin.go
new file mode 100644
index 0000000000000000000000000000000000000000..7fe84bb458744064a0165df164b6048fa3a605ca
--- /dev/null
+++ b/mocks/Plugin.go
@@ -0,0 +1,88 @@
+// Code generated by mockery v2.9.4. DO NOT EDIT.
+
+package mocks
+
+import (
+	plugin "code.fbi.h-da.de/danet/gosdn/interfaces/plugin"
+	uuid "github.com/google/uuid"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// Plugin is an autogenerated mock type for the Plugin type
+type Plugin struct {
+	mock.Mock
+}
+
+// ID provides a mock function with given fields:
+func (_m *Plugin) ID() uuid.UUID {
+	ret := _m.Called()
+
+	var r0 uuid.UUID
+	if rf, ok := ret.Get(0).(func() uuid.UUID); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(uuid.UUID)
+		}
+	}
+
+	return r0
+}
+
+// Manifest provides a mock function with given fields:
+func (_m *Plugin) Manifest() *plugin.Manifest {
+	ret := _m.Called()
+
+	var r0 *plugin.Manifest
+	if rf, ok := ret.Get(0).(func() *plugin.Manifest); ok {
+		r0 = rf()
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*plugin.Manifest)
+		}
+	}
+
+	return r0
+}
+
+// Path provides a mock function with given fields:
+func (_m *Plugin) Path() string {
+	ret := _m.Called()
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func() string); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// State provides a mock function with given fields:
+func (_m *Plugin) State() plugin.State {
+	ret := _m.Called()
+
+	var r0 plugin.State
+	if rf, ok := ret.Get(0).(func() plugin.State); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(plugin.State)
+	}
+
+	return r0
+}
+
+// Update provides a mock function with given fields:
+func (_m *Plugin) Update() error {
+	ret := _m.Called()
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func() error); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
diff --git a/mocks/SouthboundInterface.go b/mocks/SouthboundInterface.go
index 22dd94e9ef59d87afb370f2a09f2fa4642b53de9..12bd7100f01ea6c1df65833f3bcd9fe50ff791b6 100644
--- a/mocks/SouthboundInterface.go
+++ b/mocks/SouthboundInterface.go
@@ -38,20 +38,6 @@ func (_m *SouthboundInterface) ID() uuid.UUID {
 	return r0
 }
 
-// SbiIdentifier provides a mock function with given fields:
-func (_m *SouthboundInterface) SbiIdentifier() string {
-	ret := _m.Called()
-
-	var r0 string
-	if rf, ok := ret.Get(0).(func() string); ok {
-		r0 = rf()
-	} else {
-		r0 = ret.Get(0).(string)
-	}
-
-	return r0
-}
-
 // Schema provides a mock function with given fields:
 func (_m *SouthboundInterface) Schema() *ytypes.Schema {
 	ret := _m.Called()
@@ -68,6 +54,11 @@ func (_m *SouthboundInterface) Schema() *ytypes.Schema {
 	return r0
 }
 
+// SetID provides a mock function with given fields: id
+func (_m *SouthboundInterface) SetID(id uuid.UUID) {
+	_m.Called(id)
+}
+
 // SetNode provides a mock function with given fields: schema, root, path, val, opts
 func (_m *SouthboundInterface) SetNode(schema *yang.Entry, root interface{}, path *gnmi.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
 	_va := make([]interface{}, len(opts))
diff --git a/northbound/server/core.go b/northbound/server/core.go
index d269fad776b087d52769f993a83328e7ee193329..65ba3b82c42ffda30872438a244958b29e237659 100644
--- a/northbound/server/core.go
+++ b/northbound/server/core.go
@@ -6,7 +6,6 @@ import (
 
 	pb "code.fbi.h-da.de/danet/api/go/gosdn/core"
 	ppb "code.fbi.h-da.de/danet/api/go/gosdn/pnd"
-	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
 	"code.fbi.h-da.de/danet/gosdn/metrics"
 	"code.fbi.h-da.de/danet/gosdn/nucleus"
 	"github.com/google/uuid"
@@ -77,12 +76,7 @@ func (s core) CreatePndList(ctx context.Context, request *pb.CreatePndListReques
 	start := metrics.StartHook(labels, grpcRequestsTotal)
 	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
 	for _, r := range request.Pnd {
-		sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
-		if err != nil {
-			return nil, handleRPCError(labels, err)
-		}
-
-		pnd, err := nucleus.NewPND(r.Name, r.Description, uuid.New(), sbi, nil, nil)
+		pnd, err := nucleus.NewPND(r.Name, r.Description, uuid.New(), nil, nil, nil)
 		if err != nil {
 			return nil, handleRPCError(labels, err)
 		}
diff --git a/northbound/server/pnd_test.go b/northbound/server/pnd_test.go
index b2e7595ab319184f92ed2fdf07a414e9150bbe1f..9eedf11338595a0efcfbc5feeae003f6d6adb8ca 100644
--- a/northbound/server/pnd_test.go
+++ b/northbound/server/pnd_test.go
@@ -128,14 +128,15 @@ func TestMain(m *testing.M) {
 		},
 		UUID: deviceUUID,
 	}
-	sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
+
+	sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG, sbiUUID)
 	if err != nil {
 		log.Fatal(err)
 	}
 	mockDevice.(*nucleus.CommonDevice).SetSBI(sbi)
 	mockDevice.(*nucleus.CommonDevice).SetTransport(&mocks.Transport{})
 	mockDevice.(*nucleus.CommonDevice).SetName(hostname)
-	sbiStore = store.NewSbiStore()
+	sbiStore = store.NewSbiStore(pndUUID)
 	if err := sbiStore.Add(mockDevice.SBI()); err != nil {
 		log.Fatal(err)
 	}
diff --git a/nucleus/device.go b/nucleus/device.go
index 8ded445919e525849b7280431555cf23a852ae79..464c9fd89b6561f90ae820fa6421199212c29f6a 100644
--- a/nucleus/device.go
+++ b/nucleus/device.go
@@ -21,7 +21,8 @@ func NewDevice(name string, uuidInput uuid.UUID, opt *tpb.TransportOption, sbi s
 		return nil, err
 	}
 
-	// TODO: this needs to check the case that the uuidInput is set, as the same uuid may be already stored.
+	// TODO: this needs to check the case that the uuidInput is set, as the
+	// same uuid may be already stored.
 	if uuidInput == uuid.Nil {
 		uuidInput = uuid.New()
 	}
diff --git a/nucleus/errors/errors.go b/nucleus/errors/errors.go
index 7654c6ec36a56a6270f8a0431399f528a67ead6c..2e75d8a5da928457b8c03f8107172359b6509193 100644
--- a/nucleus/errors/errors.go
+++ b/nucleus/errors/errors.go
@@ -110,6 +110,42 @@ func (e ErrOperationNotSupported) Error() string {
 	return fmt.Sprintf("transport operation not supported: %v", reflect.TypeOf(e.Op))
 }
 
+// ErrUnsupportedSbiType implements the Error interface and is called if the
+// wrong Type for a SBI has been provided.
+type ErrUnsupportedSbiType struct {
+	Type interface{}
+}
+
+func (e ErrUnsupportedSbiType) Error() string {
+	return fmt.Sprintf("SBI type not supported: %v", e.Type)
+}
+
+// ErrPluginVersion implements the Error interface and is called if the Version
+// of a Plugin is older than a Plugin in use.
+type ErrPluginVersion struct {
+	PlugID, ProvidedVer, UsedVer string
+}
+
+func (e ErrPluginVersion) Error() string {
+	return fmt.Sprintf("Version of Plugin: %s is older than the one in use. Provided: %s, in use: %s", e.PlugID, e.ProvidedVer, e.UsedVer)
+}
+
+// ErrorList implements the Error interface and is called if a slice of errors
+// should be returned. The slice of errors is combined into a single error
+// message and returned.
+type ErrorList struct {
+	Errors []error
+}
+
+func (e ErrorList) Error() string {
+	combinedErrString := "Errors found:"
+	for i, err := range e.Errors {
+		errString := fmt.Sprintf("\n %v. %v", i+1, err)
+		combinedErrString = combinedErrString + errString
+	}
+	return combinedErrString
+}
+
 // ErrTypeNotSupported implements the Error interface and is called if the
 // wrong Type has been provided.
 type ErrTypeNotSupported struct {
diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go
index b7178956eee5763affb1e415029d5e1279e1ea07..040ee2cc3d9699d24d28c0e1325053b7988a79f5 100644
--- a/nucleus/gnmi_transport_test.go
+++ b/nucleus/gnmi_transport_test.go
@@ -459,11 +459,13 @@ func TestNewGnmiTransport(t *testing.T) {
 			if tt.name == "default" {
 				startGnmiTarget <- gnmiConfig.Addr
 			}
-			oc, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
+
+			sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
 			if err != nil {
 				t.Errorf("NewSBI() error = %v", err)
+				return
 			}
-			got, err := newGnmiTransport(tt.args.opts, oc)
+			got, err := newGnmiTransport(tt.args.opts, sbi)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/nucleus/initialise_test.go b/nucleus/initialise_test.go
index f0d9b10981d71eb2b8b6c0617aa6809b7c064c17..438dfcaba0a920787bc7f102be49f2f16efedf26 100644
--- a/nucleus/initialise_test.go
+++ b/nucleus/initialise_test.go
@@ -152,7 +152,7 @@ func newPnd() pndImplementation {
 	return pndImplementation{
 		Name:        "default",
 		Description: "default test pnd",
-		sbic:        store.NewSbiStore(),
+		sbic:        store.NewSbiStore(defaultPndID),
 		devices:     store.NewDeviceStore(defaultPndID),
 		changes:     store.NewChangeStore(),
 		Id:          defaultPndID,
diff --git a/nucleus/plugin.go b/nucleus/plugin.go
new file mode 100644
index 0000000000000000000000000000000000000000..05f19b8096657bd848e0e7c32af2a4f744dbd848
--- /dev/null
+++ b/nucleus/plugin.go
@@ -0,0 +1,91 @@
+package nucleus
+
+import (
+	"bytes"
+	"os/exec"
+	"path/filepath"
+	goPlugin "plugin"
+
+	"code.fbi.h-da.de/danet/gosdn/interfaces/plugin"
+	"code.fbi.h-da.de/danet/gosdn/nucleus/errors"
+	log "github.com/sirupsen/logrus"
+)
+
+// BuildPlugin builds a new plugin within the given path. The source file must
+// be present at the given path. The built plugin is written to a 'plugin.so'
+// file.
+func BuildPlugin(path, sourceFileName string, args ...string) error {
+	pPath := filepath.Join(path, "plugin.so")
+	sPath := filepath.Join(path, sourceFileName)
+	// Provide standard build arguments within a string slice
+	buildCommand := []string{
+		"go",
+		"build",
+		"-o",
+		pPath,
+		"-buildmode=plugin",
+	}
+	// Append build arguments
+	buildCommand = append(buildCommand, args...)
+	buildCommand = append(buildCommand, sPath)
+
+	var stderr bytes.Buffer
+	// Create the command to be executed
+	cmd := exec.Command(buildCommand[0], buildCommand[1:]...)
+	cmd.Dir = "./"
+	cmd.Stderr = &stderr
+	// Run the command and build the plugin
+	err := cmd.Run()
+	if err != nil {
+		log.Error(stderr.String())
+		return err
+	}
+	return nil
+}
+
+// LoadPlugin opens a go plugin binary which must be named 'plugin.so' at the
+// given path. LoadPlugin checks for the symbol 'PluginSymbol' that has to be
+// provided within the plugin to be loaded. If the symbol is found, the loaded
+// plugin is returned.
+func LoadPlugin(path string) (goPlugin.Symbol, error) {
+	path = filepath.Join(path, "plugin.so")
+	// Open the plugin, which is a binary (named 'plugin.so') within the given
+	// path.
+	gp, err := goPlugin.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	// Search for the specific symbol within the plugin. If it does not contain
+	// the symbol is not usable and an error is thrown.
+	symbol, err := gp.Lookup("PluginSymbol")
+	if err != nil {
+		return nil, err
+	}
+
+	return symbol, nil
+}
+
+// UpdatePlugin updates a given Plugin. Therefore the version of the
+// `plugin.yml` manifest file is compared to the version in use. If a new
+// version is within the plugin folder, the new version of the plugin is built.
+// NOTE:This should only be used with caution.
+func UpdatePlugin(p plugin.Plugin) (updated bool, err error) {
+	tmpManifest, err := plugin.ReadManifestFromFile(filepath.Join(p.Path(), "plugin.yml"))
+	if err != nil {
+		return false, err
+	}
+
+	if p.Manifest().Version < tmpManifest.Version {
+		err := BuildPlugin(p.Path(), "gostructs.go")
+		if err != nil {
+			return false, err
+		}
+		log.Info("Plugin update executed.")
+		return true, nil
+	}
+	return false, errors.ErrPluginVersion{
+		PlugID:      p.ID().String(),
+		ProvidedVer: tmpManifest.Version,
+		UsedVer:     p.Manifest().Version,
+	}
+}
diff --git a/nucleus/plugin_test.go b/nucleus/plugin_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..daeeae3a27d1c4f86521a1750ec578fbd3437bf5
--- /dev/null
+++ b/nucleus/plugin_test.go
@@ -0,0 +1,243 @@
+package nucleus
+
+import (
+	"crypto/sha256"
+	"encoding/hex"
+	"io"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"code.fbi.h-da.de/danet/gosdn/interfaces/plugin"
+	"code.fbi.h-da.de/danet/gosdn/interfaces/southbound"
+	"code.fbi.h-da.de/danet/gosdn/mocks"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/uuid"
+)
+
+// Helper function to generate hash value of a file.
+func generateHash(path, fileName string) (string, error) {
+	fp := filepath.Join(path, fileName)
+	f, err := os.Open(fp)
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	h := sha256.New()
+	if _, err := io.Copy(h, f); err != nil {
+		return "", err
+	}
+
+	return hex.EncodeToString(h.Sum(nil)), nil
+}
+
+func Test_plugin_BuildPlugin(t *testing.T) {
+	type args struct {
+		path, goStructName, pluginName string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    string
+		wantErr bool
+	}{
+		{
+			name: "build success",
+			args: args{
+				path:         "../test/plugin",
+				goStructName: "gostructs.go",
+				pluginName:   "plugin.so",
+			},
+			// hacky, but if run locally use:
+			// a5332eff85bfab88cd6ccba38c3c8f7137d00d7cc1cf7da81666b3af4829d330
+			want:    "ab36591e1af5ef34bca12927a58e2131954a5ae5ae3f13e02954ae6a0b89e88e",
+			wantErr: false,
+		},
+		{
+			name: "fail: file does not exist",
+			args: args{
+				path:         "../test/plugin",
+				goStructName: "doesNotExist.go",
+			},
+			want:    "",
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			err := BuildPlugin(tt.args.path, tt.args.goStructName, "-race")
+			if (err != nil) != tt.wantErr {
+				t.Errorf("BuildPlugin() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			hash, err := generateHash(tt.args.path, tt.args.pluginName)
+			if err != nil {
+				if tt.name != "fail: file does not exist" {
+					t.Errorf("BuildPlugin() error = %v", err)
+					return
+				}
+			}
+			if !cmp.Equal(hash, tt.want) {
+				t.Errorf("BuildPlugin() got = %v, want %v", hash, tt.want)
+			}
+		})
+	}
+}
+
+func Test_plugin_LoadPlugin(t *testing.T) {
+	type args struct {
+		path string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "load success",
+			args: args{
+				path: "../test/plugin",
+			},
+			wantErr: false,
+		},
+		{
+			name: "symbol not provided",
+			args: args{
+				path: "../test/plugin/faulty",
+			},
+			wantErr: true,
+		},
+		{
+			name: "fail: wrong path",
+			args: args{
+				path: "../test/plugin/wrong/path",
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.name != "fail: wrong path" {
+				if err := BuildPlugin(tt.args.path, "gostructs.go", "-race"); err != nil {
+					t.Errorf("LoadPlugin() error = %v", err)
+					return
+				}
+			}
+
+			ps, err := LoadPlugin(tt.args.path)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("LoadPlugin() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			// the plugin provided for test cases should be assertable to type
+			// southbound.SouthboundInterface
+			if tt.name == "load success" {
+				if _, ok := ps.(southbound.SouthboundInterface); !ok {
+					t.Error("LoadPlugin() error = plugin.Symbol was not assertable to southbound.SouthboundInterface")
+				}
+			}
+		})
+	}
+}
+
+func Test_plugin_UpdatePlugin(t *testing.T) {
+	newPlugin := func(id uuid.UUID, path string, state plugin.State, manifest *plugin.Manifest) *mocks.Plugin {
+		p := &mocks.Plugin{}
+		p.On("Path").Return(path)
+		p.On("ID").Return(id)
+		p.On("State").Return(state)
+		p.On("Manifest").Return(manifest)
+		return p
+	}
+
+	type args struct {
+		id       uuid.UUID
+		path     string
+		state    plugin.State
+		manifest *plugin.Manifest
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    bool
+		wantErr bool
+	}{
+		{
+			name: "Update success",
+			args: args{
+				id:    uuid.New(),
+				path:  "../test/plugin",
+				state: plugin.State(2),
+				manifest: &plugin.Manifest{
+					Name:    "test",
+					Author:  "goSDN-Team",
+					Version: "v1.0.2",
+				},
+			},
+			want:    true,
+			wantErr: false,
+		},
+		{
+			name: "Plugin to Update is of older version",
+			args: args{
+				id:    uuid.New(),
+				path:  "../test/plugin",
+				state: plugin.State(2),
+				manifest: &plugin.Manifest{
+					Name:    "test",
+					Author:  "goSDN-Team",
+					Version: "v2.2.2",
+				},
+			},
+			want:    false,
+			wantErr: true,
+		},
+		{
+			name: "Faulty manifest file",
+			args: args{
+				id:    uuid.New(),
+				path:  "../test/plugin/faulty",
+				state: plugin.State(2),
+				manifest: &plugin.Manifest{
+					Name:    "test",
+					Author:  "goSDN-Team",
+					Version: "v1.0.0",
+				},
+			},
+			want:    false,
+			wantErr: true,
+		},
+		{
+			name: "wrong path",
+			args: args{
+				id:    uuid.New(),
+				path:  "../test/plugin/wrong/path",
+				state: plugin.State(2),
+				manifest: &plugin.Manifest{
+					Name:    "test",
+					Author:  "goSDN-Team",
+					Version: "v1.0.2",
+				},
+			},
+			want:    false,
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := newPlugin(tt.args.id, tt.args.path, tt.args.state, tt.args.manifest)
+
+			got, err := UpdatePlugin(p)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("UpdatePlugin() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if !cmp.Equal(got, tt.want) {
+				t.Errorf("BuildPlugin() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/nucleus/principalNetworkDomain.go b/nucleus/principalNetworkDomain.go
index 49792756d32d88a53dba0dfbe8eda90b22e40a6b..9e92cd43d873158b5e457a83da95e76e0884371e 100644
--- a/nucleus/principalNetworkDomain.go
+++ b/nucleus/principalNetworkDomain.go
@@ -5,7 +5,7 @@ import (
 	"encoding/json"
 	"io"
 	"os"
-	"plugin"
+	"path/filepath"
 	"time"
 
 	"code.fbi.h-da.de/danet/gosdn/metrics"
@@ -16,6 +16,7 @@ import (
 	ppb "code.fbi.h-da.de/danet/api/go/gosdn/pnd"
 	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
 	tpb "code.fbi.h-da.de/danet/api/go/gosdn/transport"
+	"google.golang.org/grpc"
 	"google.golang.org/protobuf/proto"
 
 	"code.fbi.h-da.de/danet/forks/goarista/gnmi"
@@ -33,6 +34,7 @@ import (
 	"github.com/openconfig/ygot/ytypes"
 	"github.com/prometheus/client_golang/prometheus"
 	log "github.com/sirupsen/logrus"
+	"github.com/spf13/viper"
 )
 
 // NewPND creates a Principle Network Domain
@@ -40,7 +42,7 @@ func NewPND(name, description string, id uuid.UUID, sbi southbound.SouthboundInt
 	pnd := &pndImplementation{
 		Name:        name,
 		Description: description,
-		sbic:        store.NewSbiStore(),
+		sbic:        store.NewSbiStore(id),
 		devices:     store.NewDeviceStore(id),
 		changes:     store.NewChangeStore(),
 		Id:          id,
@@ -49,10 +51,19 @@ func NewPND(name, description string, id uuid.UUID, sbi southbound.SouthboundInt
 		callback:   callback,
 	}
 
-	if err := pnd.sbic.Add(sbi); err != nil {
+	if err := pnd.loadStoredSbis(); err != nil {
 		return nil, err
 	}
 
+	// If the SBI is not provided, then do not add a SBI to the store
+	if sbi != nil {
+		if !pnd.sbic.Exists(sbi.ID()) {
+			if err := pnd.sbic.Add(sbi); err != nil {
+				return nil, err
+			}
+		}
+	}
+
 	if err := pnd.loadStoredDevices(); err != nil {
 		return nil, err
 	}
@@ -143,7 +154,30 @@ func (pnd *pndImplementation) AddSbi(s southbound.SouthboundInterface) error {
 	return pnd.addSbi(s)
 }
 
-// RemoveSbi removes a SBI and all the associated devices from the PND
+// AddSbiFromStore creates a SBI based on the given ID, type and path provided.
+// The type determines if a SouthboundPlugin or a standard OpenConfig SBI is
+// created. The SBI is then added to the PND's SBI store.
+func (pnd *pndImplementation) AddSbiFromStore(id uuid.UUID, sbiType string, path string) error {
+	var sbi southbound.SouthboundInterface
+	var err error
+	if spb.Type_value[sbiType] != int32(spb.Type_TYPE_OPENCONFIG) {
+		sbi, err = NewSouthboundPlugin(id, path, false)
+		if err != nil {
+			return err
+		}
+	} else {
+		sbi, err = NewSBI(spb.Type_TYPE_OPENCONFIG, id)
+		if err != nil {
+			return err
+		}
+	}
+	return pnd.addSbi(sbi)
+}
+
+// RemoveSbi removes a SBI from the PND
+// TODO: this should to recursively through
+// devices and remove the devices using
+// this SBI
 func (pnd *pndImplementation) RemoveSbi(id uuid.UUID) error {
 	associatedDevices, err := pnd.devices.GetDevicesAssociatedWithSbi(id)
 	if err != nil {
@@ -227,20 +261,22 @@ func (pnd *pndImplementation) RemoveDevice(uuid uuid.UUID) error {
 	return pnd.removeDevice(uuid)
 }
 
-// Actual implementation, bind to struct if
-// neccessary
+// Actual implementation, bind to struct if neccessary
 func destroy() error {
 	return nil
 }
 
+// addSbi adds a SBI to the PND's SBI store.
 func (pnd *pndImplementation) addSbi(sbi southbound.SouthboundInterface) error {
 	return pnd.sbic.Add(sbi)
 }
 
+// removeSbi removes an SBI based on the given ID from the PND's SBI store.
 func (pnd *pndImplementation) removeSbi(id uuid.UUID) error {
 	return pnd.sbic.Delete(id)
 }
 
+// addDevice adds a device to the PND's device store.
 func (pnd *pndImplementation) addDevice(device device.Device) error {
 	err := pnd.devices.Add(device, device.Name())
 	if err != nil {
@@ -255,7 +291,7 @@ func (pnd *pndImplementation) removeDevice(id uuid.UUID) error {
 	if err != nil {
 		return err
 	}
-	labels := prometheus.Labels{"type": d.SBI().SbiIdentifier()}
+	labels := prometheus.Labels{"type": d.SBI().Type().String()}
 	start := metrics.StartHook(labels, deviceDeletionsTotal)
 	defer metrics.FinishHook(labels, start, deviceDeletionDurationSecondsTotal, deviceDeletionDurationSeconds)
 	switch d.(type) {
@@ -462,6 +498,38 @@ func (pnd *pndImplementation) createCsbiDevice(ctx context.Context, name string,
 			TransportOption: opt.TransportOption,
 		}
 		log.WithField("transport option", csbiTransportOptions).Debug("gosdn gnmi transport options")
+		req := &cpb.GetRequest{
+			Timestamp: time.Now().UnixNano(),
+			All:       false,
+			Did:       []string{d.Id},
+		}
+		idd := uuid.New()
+		ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10)
+		defer cancel()
+		gClient, err := pnd.csbiClient.GetGoStruct(ctx, req)
+		if err != nil {
+			return err
+		}
+		csbiID, err := saveGenericClientStreamToFile(gClient, "gostructs.go", idd)
+		if err != nil {
+			return err
+		}
+		mClient, err := pnd.csbiClient.GetManifest(ctx, req)
+		if err != nil {
+			return err
+		}
+		_, err = saveGenericClientStreamToFile(mClient, "plugin.yml", idd)
+		if err != nil {
+			return err
+		}
+		csbi, err := NewSBI(spb.Type_TYPE_CONTAINERISED, csbiID)
+		if err != nil {
+			return err
+		}
+		err = pnd.sbic.Add(csbi)
+		if err != nil {
+			return err
+		}
 		d, err := NewDevice(name, uuid.Nil, csbiTransportOptions, csbi)
 		if err != nil {
 			return err
@@ -472,78 +540,111 @@ func (pnd *pndImplementation) createCsbiDevice(ctx context.Context, name string,
 			return err
 		}
 	}
-
 	return nil
 }
 
+// requestPlugin is a feature for cSBIs and sends a plugin request to the cSBI
+// orchestrator and processes the received ygot generated go code, builds the
+// plugin and integrates the Plugin within the goSDN as SouthboundInterface.
+// The generated code is passed into a gostructs.go file, which is the
+// foundation for the created plugin.
 func (pnd *pndImplementation) requestPlugin(name string, opt *tpb.TransportOption) (southbound.SouthboundInterface, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10)
 	defer cancel()
-	req := &cpb.CreatePluginRequest{
+	req := &cpb.CreateRequest{
 		Timestamp:       time.Now().UnixNano(),
 		TransportOption: []*tpb.TransportOption{opt},
 	}
-	client, err := pnd.csbiClient.CreatePlugin(ctx, req)
+	client, err := pnd.csbiClient.CreateGoStruct(ctx, req)
 	if err != nil {
 		return nil, err
 	}
 
-	id := uuid.New()
-	f, err := os.Create("plugin-" + id.String() + ".so")
+	id, err := saveGenericClientStreamToFile(client, "gostructs.go", uuid.New())
 	if err != nil {
 		return nil, err
 	}
+
+	sbi, err := NewSBI(spb.Type_TYPE_PLUGIN, id)
+	if err != nil {
+		return nil, err
+	}
+	err = pnd.sbic.Add(sbi)
+	if err != nil {
+		return nil, err
+	}
+	return sbi, nil
+}
+
+// GenericGrpcClient allows to distinguish between the different ygot
+// generated GoStruct clients, which return a stream of bytes.
+type GenericGrpcClient interface {
+	Recv() (*cpb.Payload, error)
+	grpc.ClientStream
+}
+
+// saveGenericClientStreamToFile takes a GenericGoStructClient and processes the included
+// gRPC stream. A 'gostructs.go' file is created within the goSDN's
+// 'plugin-folder'. Each 'gostructs.go' file is stored in its own folder based
+// on a new uuid.UUID.
+func saveGenericClientStreamToFile(t GenericGrpcClient, filename string, id uuid.UUID) (uuid.UUID, error) {
+	//id := uuid.New()
+	folderName := viper.GetString("plugin-folder")
+	path := filepath.Join(folderName, id.String(), filename)
+
+	// create the directory hierarchy based on the path
+	if err := os.MkdirAll(filepath.Dir(path), 0770); err != nil {
+		return uuid.Nil, err
+	}
+	// create the gostructs.go file at path
+	f, err := os.Create(path)
+	if err != nil {
+		return uuid.Nil, err
+	}
 	defer f.Close()
+
+	// receive byte stream
 	for {
-		payload, err := client.Recv()
+		payload, err := t.Recv()
 		if err != nil {
 			if err == io.EOF {
 				break
 			}
-			client.CloseSend()
-			return nil, err
+			t.CloseSend()
+			return uuid.Nil, err
 		}
 		n, err := f.Write(payload.Chunk)
 		if err != nil {
-			client.CloseSend()
-			return nil, err
+			t.CloseSend()
+			return uuid.Nil, err
 		}
 		log.WithField("n", n).Trace("wrote bytes")
 	}
 	if err := f.Sync(); err != nil {
-		return nil, err
+		return uuid.Nil, err
 	}
 
-	return loadPlugin(id)
+	return id, nil
 }
 
-func loadPlugin(id uuid.UUID) (southbound.SouthboundInterface, error) {
-	p, err := plugin.Open("plugin-" + id.String() + ".so")
+// loadStoredSbis loads all stored SBIs and add each one of them to the PND's
+// SBI store.
+func (pnd *pndImplementation) loadStoredSbis() error {
+	sbis, err := pnd.sbic.Load()
 	if err != nil {
-		return nil, err
-	}
-
-	symbol, err := p.Lookup("PluginSymbol")
-	if err != nil {
-		return nil, err
+		return err
 	}
-
-	var sbi southbound.SouthboundInterface
-	sbi, ok := symbol.(southbound.SouthboundInterface)
-	if !ok {
-		return nil, &errors.ErrInvalidTypeAssertion{
-			Value: symbol,
-			Type:  (*southbound.SouthboundInterface)(nil),
+	for _, sbi := range sbis {
+		err := pnd.AddSbiFromStore(sbi.ID, sbi.Type, sbi.Path)
+		if err != nil {
+			return err
 		}
 	}
-	log.WithFields(log.Fields{
-		"identifier": sbi.SbiIdentifier(),
-		"id":         sbi.ID(),
-		"type":       sbi.Type(),
-	}).Trace("plugin information")
-	return sbi, nil
+	return nil
 }
 
+// loadStoredDevices loads all stored devices and adds each one of them to the
+// PND's device store.
 func (pnd *pndImplementation) loadStoredDevices() error {
 	devices, err := pnd.devices.Load()
 	if err != nil {
diff --git a/nucleus/principalNetworkDomain_test.go b/nucleus/principalNetworkDomain_test.go
index 71670294ee1167612ccdef5b34c437795c00d20a..2622c91a0ab6baaa90937c8381d8698cda1e22af 100644
--- a/nucleus/principalNetworkDomain_test.go
+++ b/nucleus/principalNetworkDomain_test.go
@@ -3,10 +3,12 @@ package nucleus
 import (
 	"errors"
 	"fmt"
+	"io"
 	"os"
 	"reflect"
 	"testing"
 
+	"code.fbi.h-da.de/danet/api/go/gosdn/csbi"
 	ppb "code.fbi.h-da.de/danet/api/go/gosdn/pnd"
 	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
 	tpb "code.fbi.h-da.de/danet/api/go/gosdn/transport"
@@ -199,6 +201,58 @@ func Test_pndImplementation_AddSbi(t *testing.T) {
 	}
 }
 
+func Test_pndImplementation_AddSbiFromStore(t *testing.T) {
+	type args struct {
+		uuid  uuid.UUID
+		ttype spb.Type
+		sbi   southbound.SouthboundInterface
+		path  string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "default",
+			args: args{
+				uuid:  defaultSbiID,
+				ttype: spb.Type_TYPE_OPENCONFIG,
+				path:  "",
+			},
+			wantErr: false,
+		},
+		{
+			name: "already exists",
+			args: args{
+				uuid:  defaultSbiID,
+				ttype: spb.Type_TYPE_OPENCONFIG,
+				path:  "",
+				sbi: &OpenConfig{
+					id:     defaultSbiID,
+					schema: nil,
+					path:   "",
+				},
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			pnd := newPnd()
+
+			if tt.name == "already exists" {
+				pnd.sbic.Store[defaultSbiID] = tt.args.sbi.(southbound.SouthboundInterface)
+			}
+
+			err := pnd.AddSbiFromStore(tt.args.uuid, tt.args.ttype.String(), tt.args.path)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("AddSbiFromStore() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
 func Test_pndImplementation_ContainsDevice(t *testing.T) {
 	removeExistingPNDStore()
 
@@ -373,11 +427,6 @@ func Test_pndImplementation_MarshalDevice(t *testing.T) {
 func Test_pndImplementation_RemoveDevice(t *testing.T) {
 	removeExistingPNDStore()
 
-	sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
-	if err != nil {
-		t.Errorf("NewSBI() error = %v", err)
-	}
-
 	type args struct {
 		uuid uuid.UUID
 	}
@@ -395,6 +444,11 @@ func Test_pndImplementation_RemoveDevice(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			pnd := newPnd()
 			if tt.name != "fails empty" {
+				sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
+				if err != nil {
+					t.Error(err)
+					return
+				}
 				d := &CommonDevice{
 					UUID: did,
 					sbi:  sbi,
@@ -437,7 +491,7 @@ func Test_pndImplementation_RemoveSbi(t *testing.T) {
 			pnd := &pndImplementation{
 				Name:        "test-remove-sbi",
 				Description: "test-remove-sbi",
-				sbic:        store.NewSbiStore(),
+				sbic:        store.NewSbiStore(defaultPndID),
 				devices:     store.NewDeviceStore(defaultPndID),
 				Id:          defaultPndID,
 			}
@@ -715,8 +769,8 @@ func Test_pndImplementation_GetDevice(t *testing.T) {
 	sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
 	if err != nil {
 		t.Errorf("NewSBI() error = %v", err)
+		return
 	}
-
 	d, err := NewDevice("", uuid.Nil, newGnmiTransportOptions(), sbi)
 	if err != nil {
 		t.Error(err)
@@ -769,8 +823,8 @@ func Test_pndImplementation_GetDeviceByName(t *testing.T) {
 	sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
 	if err != nil {
 		t.Errorf("NewSBI() error = %v", err)
+		return
 	}
-
 	d, err := NewDevice("my-device", uuid.Nil, newGnmiTransportOptions(), sbi)
 	if err != nil {
 		t.Error(err)
@@ -1038,6 +1092,8 @@ func Test_pndImplementation_LoadStoredDevices(t *testing.T) {
 	}
 }
 
+// TODO(mbauch): This test case looks unfinished. For example there are no
+// cases for 'already exists' and 'fails wrong type'.
 func Test_pndImplementation_AddDeviceWithUUID(t *testing.T) {
 	type args struct {
 		uuid   uuid.UUID
@@ -1099,3 +1155,92 @@ func Test_pndImplementation_AddDeviceWithUUID(t *testing.T) {
 		})
 	}
 }
+
+func Test_pndImplementation_saveGoStructsToFile(t *testing.T) {
+	type genericGoStructClientArg struct {
+		fn    string
+		rtrn  []interface{}
+		times int
+	}
+	// Create a new mock for GenericGoStructClient. With
+	// genericGoStructClientArg it is possible to set the Return values of the
+	// mocks methods.
+	newGenericGoStructClient := func(args ...genericGoStructClientArg) *mocks.GenericGoStructClient {
+		ggsc := &mocks.GenericGoStructClient{}
+		for _, arg := range args {
+			ggsc.On(arg.fn).Return(arg.rtrn...).Times(arg.times)
+		}
+		ggsc.On("CloseSend").Return(nil)
+		return ggsc
+	}
+
+	type args struct {
+		id     uuid.UUID
+		client GenericGrpcClient
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "default",
+			args: args{
+				id: uuid.New(),
+				client: newGenericGoStructClient(
+					[]genericGoStructClientArg{
+						{
+							fn: "Recv",
+							rtrn: []interface{}{
+								&csbi.Payload{Chunk: []byte("test")},
+								nil,
+							},
+							times: 3,
+						},
+						{
+							fn: "Recv",
+							rtrn: []interface{}{
+								&csbi.Payload{Chunk: nil},
+								io.EOF,
+							},
+							times: 1,
+						},
+					}...),
+			},
+			wantErr: false,
+		},
+		{
+			name: "unexpected EOF error",
+			args: args{
+				id: uuid.New(),
+				client: newGenericGoStructClient(
+					[]genericGoStructClientArg{
+						{
+							fn: "Recv",
+							rtrn: []interface{}{
+								&csbi.Payload{Chunk: nil},
+								io.ErrUnexpectedEOF,
+							},
+							times: 1,
+						},
+						{
+							fn: "CloseSend",
+							rtrn: []interface{}{
+								nil,
+							},
+							times: 1,
+						},
+					}...),
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			_, err := saveGenericClientStreamToFile(tt.args.client, "gostructs.go", tt.args.id)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("saveGoStructsToFile() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
diff --git a/nucleus/southbound.go b/nucleus/southbound.go
index 4bf8e06ec0a7d2f73bc79d8cd6fe6a689dde6ff1..78ec76b90c886c4b5c215270ba973c67875986eb 100644
--- a/nucleus/southbound.go
+++ b/nucleus/southbound.go
@@ -1,9 +1,13 @@
 package nucleus
 
 import (
+	"path/filepath"
+	"reflect"
+
 	"code.fbi.h-da.de/danet/gosdn/nucleus/errors"
 
 	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
+	"code.fbi.h-da.de/danet/gosdn/interfaces/plugin"
 	"code.fbi.h-da.de/danet/gosdn/interfaces/southbound"
 
 	"code.fbi.h-da.de/danet/yang-models/generated/openconfig"
@@ -13,14 +17,9 @@ import (
 	"github.com/openconfig/ygot/ygot"
 	"github.com/openconfig/ygot/ytypes"
 	log "github.com/sirupsen/logrus"
+	"github.com/spf13/viper"
 )
 
-var csbi *Csbi
-
-func init() {
-	csbi = &Csbi{id: uuid.New()}
-}
-
 // NewSBI creates a SouthboundInterface of a given type.
 func NewSBI(southbound spb.Type, sbUUID ...uuid.UUID) (southbound.SouthboundInterface, error) {
 	var id uuid.UUID
@@ -34,9 +33,41 @@ func NewSBI(southbound spb.Type, sbUUID ...uuid.UUID) (southbound.SouthboundInte
 	switch southbound {
 	case spb.Type_TYPE_OPENCONFIG:
 		return &OpenConfig{id: id}, nil
+	case spb.Type_TYPE_CONTAINERISED, spb.Type_TYPE_PLUGIN:
+		p := filepath.Join(viper.GetString("plugin-folder"), id.String())
+		sbip, err := NewSouthboundPlugin(id, p, true)
+		if err != nil {
+			return nil, err
+		}
+		return sbip, nil
 	default:
-		return nil, errors.ErrTypeNotSupported{Type: southbound}
+		return nil, errors.ErrUnsupportedSbiType{Type: southbound}
+	}
+}
+
+// NewSouthboundPlugin that returns a new SouthboundPlugin. The plugin is built
+// within this process and loaded as southbound.SouthboundInterface afterwards.
+func NewSouthboundPlugin(id uuid.UUID, path string, build bool) (*SouthboundPlugin, error) {
+	manifest, err := plugin.ReadManifestFromFile(filepath.Join(path, "plugin.yml"))
+	if err != nil {
+		return nil, err
+	}
+	sp := &SouthboundPlugin{
+		state:      plugin.CREATED,
+		BinaryPath: path,
+		manifest:   manifest,
+		sbi:        nil,
+	}
+	if build {
+		if err := BuildPlugin(sp.Path(), "gostructs.go"); err != nil {
+			return nil, err
+		}
+	}
+	if err := sp.load(id); err != nil {
+		return nil, err
 	}
+
+	return sp, nil
 }
 
 // OpenConfig is the implementation of an OpenConfig SBI.
@@ -45,6 +76,7 @@ func NewSBI(southbound spb.Type, sbUUID ...uuid.UUID) (southbound.SouthboundInte
 type OpenConfig struct {
 	schema *ytypes.Schema
 	id     uuid.UUID
+	path   string
 }
 
 // SbiIdentifier returns the string representation of
@@ -124,52 +156,118 @@ func (oc *OpenConfig) ID() uuid.UUID {
 	return oc.id
 }
 
-// Type returns the Southbound's type
-func (oc *OpenConfig) Type() spb.Type { return spb.Type_TYPE_OPENCONFIG }
+// SetID sets the ID of the OpenConfig SBI
+func (oc *OpenConfig) SetID(id uuid.UUID) {
+	oc.id = id
+}
 
-// Csbi is a stub for the containerised SBI functionality.
-// It holds the standard goSDN OPENCONFIG schema for minimum
-// compatibility
-type Csbi struct {
-	schema *ytypes.Schema
-	id     uuid.UUID
+// Type returns the Southbound's type
+func (oc *OpenConfig) Type() spb.Type {
+	return spb.Type_TYPE_OPENCONFIG
 }
 
-// SbiIdentifier returns the identifier as a
-func (csbi *Csbi) SbiIdentifier() string {
-	return "csbi"
+// SouthboundPlugin is the implementation of a southbound goSDN plugin.
+type SouthboundPlugin struct {
+	sbi        southbound.SouthboundInterface
+	state      plugin.State
+	BinaryPath string
+	manifest   *plugin.Manifest
 }
 
-// SetNode injects schema specific model representation to the transport.
+// SetNode injects SBI specific model representation to the transport.
 // Needed for type assertion.
-func (csbi *Csbi) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
-	return ytypes.SetNode(schema, root.(*openconfig.Device), path, val, opts...)
+func (p *SouthboundPlugin) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
+	return p.sbi.SetNode(schema, root, path, val, opts...)
+}
+
+// Schema returns a ygot generated Schema as ytypes.Schema
+func (p *SouthboundPlugin) Schema() *ytypes.Schema {
+	return p.sbi.Schema()
+}
+
+// ID returns the ID of the plugin.
+func (p *SouthboundPlugin) ID() uuid.UUID {
+	return p.sbi.ID()
+}
+
+// SetID sets the ID of the SouthboundPlugin's SBI
+func (p *SouthboundPlugin) SetID(id uuid.UUID) {
+	p.sbi.SetID(id)
 }
 
-// Unmarshal injects schema specific model representation to the transport.
+// Type returns the Southbound's type of the SouthboundPlugin
+func (p *SouthboundPlugin) Type() spb.Type {
+	return p.sbi.Type()
+}
+
+// Unmarshal injects SBI specific model representation to the transport.
 // Needed for type assertion.
-func (csbi *Csbi) Unmarshal(bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
-	oc := OpenConfig{}
-	return unmarshal(oc.Schema(), bytes, path, goStruct, opt...)
+func (p *SouthboundPlugin) Unmarshal(data []byte, path *gpb.Path, root ygot.ValidatedGoStruct, opts ...ytypes.UnmarshalOpt) error {
+	return p.sbi.Unmarshal(data, path, root, opts...)
 }
 
-// Schema is holding the default OpenConfig schema for minimal compatibility
-// to gosdn interfaces
-func (csbi *Csbi) Schema() *ytypes.Schema {
-	schema, err := openconfig.Schema()
-	csbi.schema = schema
+// State returns the current state of the plugin.
+func (p *SouthboundPlugin) State() plugin.State {
+	return p.state
+}
+
+// Path returns the path of the plugins binary.
+func (p *SouthboundPlugin) Path() string {
+	return p.BinaryPath
+}
+
+// Manifest returns the Manifest of the plugin.
+func (p *SouthboundPlugin) Manifest() *plugin.Manifest {
+	return p.manifest
+}
+
+// load is a helper function that loads a plugin and casts it to the type of
+// southbound.SouthboundInterface. Therefore a SouthboundPlugin has to be provided
+// so it can be loaded by using its BinaryPath. The loaded plugin is typecasted to
+// southbound.SouthboundInterface and is set as the plugin's southbound interface.
+func (p *SouthboundPlugin) load(id uuid.UUID) error {
+	// load the SouthboundPlugin
+	symbol, err := LoadPlugin(p.BinaryPath)
 	if err != nil {
-		log.Fatal(err)
+		p.state = plugin.FAULTY
+		return err
 	}
-	return schema
-}
+	// Typecast the go plugins symbol to southbound.SouthboundInterface
+	sbi, ok := symbol.(southbound.SouthboundInterface)
+	if !ok {
+		p.state = plugin.FAULTY
+		return &errors.ErrInvalidTypeAssertion{
+			Value: reflect.TypeOf(symbol),
+			Type:  reflect.TypeOf((*southbound.SouthboundInterface)(nil)).Elem(),
+		}
+	}
+	// Note(mbauch): We could consider moving this into plugin creation.
+	// set the ID of the southbound interface to the plugins ID
+	sbi.SetID(id)
+	// Update the state of the plugin to LOADED
+	p.state = plugin.LOADED
+	// Update the plugins sbi to the loaded go plugin
+	p.sbi = sbi
+
+	log.WithFields(log.Fields{
+		"id":   sbi.ID(),
+		"type": sbi.Type(),
+	}).Trace("plugin information")
 
-// ID returns the Southbound's UUID
-func (csbi *Csbi) ID() uuid.UUID {
-	return csbi.id
+	return nil
 }
 
-// Type returns the Southbound's type
-func (csbi *Csbi) Type() spb.Type {
-	return spb.Type_TYPE_CONTAINERISED
+// Update updates the SouthboundPlugin's SBI.
+func (p *SouthboundPlugin) Update() error {
+	updated, err := UpdatePlugin(p)
+	if err != nil {
+		return err
+	}
+	if updated {
+		err = p.load(p.ID())
+		if err != nil {
+			return err
+		}
+	}
+	return nil
 }
diff --git a/nucleus/southbound_test.go b/nucleus/southbound_test.go
index f483d02ade589fa514ee40587137dfefd712f3e7..344f3e07737c5af04c2b4fc3ed332df8b23c88fa 100644
--- a/nucleus/southbound_test.go
+++ b/nucleus/southbound_test.go
@@ -167,10 +167,9 @@ func Test_unmarshal(t *testing.T) {
 			bytes := resp.Notification[0].Update[0].Val.GetJsonIetfVal()
 			oc, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
 			if err != nil {
-				t.Errorf("unmarshal() error = %v", err)
+				t.Error(err)
 				return
 			}
-
 			if err := unmarshal(oc.Schema(), bytes, resp.Notification[0].Update[0].Path, tt.args.goStruct, tt.args.opt...); err != nil {
 				if !tt.wantErr {
 					t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr)
@@ -187,11 +186,12 @@ func Test_unmarshal(t *testing.T) {
 func Test_CreateNewUUID(t *testing.T) {
 	sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
 	if err != nil {
-		t.Errorf("NewSBI() error = %v", err)
+		t.Errorf("CreateNewUUID() error = %v", err)
+		return
 	}
 
 	if sbi.ID().String() == "" {
-		t.Errorf("sbi.ID().String() is not set.")
+		t.Errorf("CreateNewUUID() error = sbi.ID().String() is not set.")
 	}
 }
 
@@ -199,10 +199,44 @@ func Test_UseProvidedUUID(t *testing.T) {
 	providedSBIId := uuid.New()
 	sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG, providedSBIId)
 	if err != nil {
-		t.Errorf("NewSBI() error = %v", err)
+		t.Errorf("UseProvidedUUID() error = %v", err)
+		return
 	}
 
 	if sbi.ID() != providedSBIId {
-		t.Errorf("sbi.ID() is not %s. got=%s", providedSBIId.String(), sbi.ID().String())
+		t.Errorf("UseProvidedUUID() error = sbi.ID() is not %s. got=%s", providedSBIId.String(), sbi.ID().String())
+	}
+}
+
+func Test_SetID(t *testing.T) {
+	tests := []struct {
+		name    string
+		sbiID   uuid.UUID
+		wantErr bool
+	}{
+		{
+			name:    "default",
+			sbiID:   defaultSbiID,
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG)
+			if err != nil {
+				t.Error(err)
+				return
+			}
+
+			if sbi.ID() == defaultSbiID {
+				t.Errorf("SetID() error = sbi.ID() is already %s", sbi.ID().String())
+			}
+
+			sbi.SetID(defaultSbiID)
+
+			if (sbi.ID() != defaultSbiID) != tt.wantErr {
+				t.Errorf("SetID() error = sbi.ID() is not %s; got=%s", defaultSbiID.String(), sbi.ID().String())
+			}
+		})
 	}
 }
diff --git a/store/deviceStore.go b/store/deviceStore.go
index df0a9ba85b8fdb266e16dc11148ed03eec8d7e75..c76ccc604c0544e3b4177f6310e6cb98821a9033 100644
--- a/store/deviceStore.go
+++ b/store/deviceStore.go
@@ -143,12 +143,14 @@ func (s *DeviceStore) Delete(id uuid.UUID) error {
 	return nil
 }
 
+// persist is a helper method that persists the given store.Storable within
+// the storage file.
 func (s *DeviceStore) persist(item store.Storable, name string) error {
 	ensureFilesystemStorePathExists(s.deviceStoreName)
 
 	_, ok := item.(device.Device)
 	if !ok {
-		return fmt.Errorf("item is no Device. got=%T", item)
+		return fmt.Errorf("item is no Device, got=%T", item)
 	}
 
 	var devicesToPersist []device.Device
@@ -156,7 +158,7 @@ func (s *DeviceStore) persist(item store.Storable, name string) error {
 	for _, value := range s.genericStore.Store {
 		dev, ok := value.(device.Device)
 		if !ok {
-			return fmt.Errorf("item is no Device. got=%T", item)
+			return fmt.Errorf("item is no Device, got=%T", item)
 		}
 		devicesToPersist = append(devicesToPersist, dev)
 	}
diff --git a/store/pndStore.go b/store/pndStore.go
index deff904fdf1cea5cba708484164486139fd65977..40f884f3f4f8ef12920768f4924e597279af27d7 100644
--- a/store/pndStore.go
+++ b/store/pndStore.go
@@ -100,12 +100,14 @@ func (s *PndStore) RemovePendingChannel(id uuid.UUID) {
 	delete(s.pendingChannels, id)
 }
 
+// persist is a helper method that persists the given store.Storable within
+// the storage file.
 func (s *PndStore) persist(item store.Storable) error {
 	ensureFilesystemStorePathExists(s.pndStoreName)
 
 	_, ok := item.(networkdomain.NetworkDomain)
 	if !ok {
-		return fmt.Errorf("item is no NetworkDoman. got=%T", item)
+		return fmt.Errorf("item is no NetworkDomain, got=%T", item)
 	}
 
 	var networkDomainsToPersist []LoadedPnd
@@ -113,7 +115,7 @@ func (s *PndStore) persist(item store.Storable) error {
 	for _, value := range s.genericStore.Store {
 		networkDomain, ok := value.(networkdomain.NetworkDomain)
 		if !ok {
-			return fmt.Errorf("item is no Device. got=%T", item)
+			return fmt.Errorf("item is no NetworkdDomain, got=%T", item)
 		}
 		networkDomainsToPersist = append(networkDomainsToPersist, LoadedPnd{
 			Name:        networkDomain.GetName(),
diff --git a/store/sbiStore.go b/store/sbiStore.go
index f74d31092cef87ace0682015110a1a0195f8845b..601512d0b00632368fca28e4c9411219714f316c 100644
--- a/store/sbiStore.go
+++ b/store/sbiStore.go
@@ -1,7 +1,15 @@
 package store
 
 import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"reflect"
+
+	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
+	"code.fbi.h-da.de/danet/gosdn/interfaces/plugin"
 	"code.fbi.h-da.de/danet/gosdn/interfaces/southbound"
+	"code.fbi.h-da.de/danet/gosdn/interfaces/store"
 	"code.fbi.h-da.de/danet/gosdn/nucleus/errors"
 
 	"github.com/google/uuid"
@@ -10,12 +18,16 @@ import (
 
 // SbiStore is used to store SouthboundInterfaces
 type SbiStore struct {
+	sbiStoreName string
 	*genericStore
 }
 
 // NewSbiStore returns a SbiStore
-func NewSbiStore() *SbiStore {
-	return &SbiStore{genericStore: newGenericStore()}
+func NewSbiStore(pndUUID uuid.UUID) *SbiStore {
+	return &SbiStore{
+		genericStore: newGenericStore(),
+		sbiStoreName: fmt.Sprintf("sbi-store-%s.json", pndUUID.String()),
+	}
 }
 
 // GetSBI takes a SouthboundInterface's UUID and returns the SouthboundInterface. If the requested
@@ -37,3 +49,121 @@ func (s *SbiStore) GetSBI(id uuid.UUID) (southbound.SouthboundInterface, error)
 	}).Debug("southbound interface was accessed")
 	return sbi, nil
 }
+
+// Add adds a Southbound Interface to the southbound store.
+func (s *SbiStore) Add(item store.Storable) error {
+	if s.Exists(item.ID()) {
+		return &errors.ErrAlreadyExists{Item: item}
+	}
+
+	// check if item is a SouthboundInterface
+	_, ok := item.(southbound.SouthboundInterface)
+	if !ok {
+		return &errors.ErrInvalidTypeAssertion{
+			Value: reflect.TypeOf(item),
+			Type:  reflect.TypeOf((*southbound.SouthboundInterface)(nil)).Elem(),
+		}
+	}
+
+	s.storeLock.Lock()
+	s.genericStore.Store[item.ID()] = item
+	s.storeLock.Unlock()
+
+	log.WithFields(log.Fields{
+		"type": reflect.TypeOf(item),
+		"uuid": item.ID(),
+	}).Debug("storable was added")
+
+	err := s.persist()
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// persist is a helper method that persists the given store.Storable within
+// the storage file.
+func (s *SbiStore) persist() error {
+	err := ensureFilesystemStorePathExists(s.sbiStoreName)
+	if err != nil {
+		return err
+	}
+
+	var southboundInterfacesToPersist []LoadedSbi
+
+	for _, value := range s.genericStore.Store {
+		southboundInterface, ok := value.(southbound.SouthboundInterface)
+		if !ok {
+			return &errors.ErrInvalidTypeAssertion{
+				Value: reflect.TypeOf(value),
+				Type:  reflect.TypeOf((*southbound.SouthboundInterface)(nil)).Elem(),
+			}
+		}
+		if southboundInterface.Type() == spb.Type_TYPE_CONTAINERISED || southboundInterface.Type() == spb.Type_TYPE_PLUGIN {
+			southboundPlugin, ok := southboundInterface.(plugin.Plugin)
+			if !ok {
+				return &errors.ErrInvalidTypeAssertion{
+					Value: reflect.TypeOf(southboundInterface),
+					Type:  reflect.TypeOf((*plugin.Plugin)(nil)).Elem(),
+				}
+			}
+			southboundInterfacesToPersist = append(southboundInterfacesToPersist, LoadedSbi{
+				ID:   southboundPlugin.ID(),
+				Type: southboundInterface.Type().String(),
+				Path: southboundPlugin.Path(),
+			})
+		} else {
+			southboundInterfacesToPersist = append(southboundInterfacesToPersist, LoadedSbi{
+				ID:   southboundInterface.ID(),
+				Type: southboundInterface.Type().String(),
+			})
+		}
+	}
+
+	storeDataAsJSON, err := json.MarshalIndent(southboundInterfacesToPersist, "", " ")
+	if err != nil {
+		return err
+	}
+
+	err = ioutil.WriteFile(getCompletePathToFileStore(s.sbiStoreName), storeDataAsJSON, 0644)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// LoadedSbi represents a Southbound Interface that was loaeded by using
+// the Load() method of the SbiStore.
+type LoadedSbi struct {
+	ID   uuid.UUID `json:"id,omitempty"`
+	Type string    `json:"type,omitempty"`
+	Path string    `json:"path,omitempty"`
+}
+
+// Load unmarshals the contents of the storage file associated with a SbiStore
+// and returns it as []LoadedSbi.
+func (s *SbiStore) Load() ([]LoadedSbi, error) {
+	var loadedSouthboundInterfaces []LoadedSbi
+
+	err := ensureFilesystemStorePathExists(s.sbiStoreName)
+	if err != nil {
+		log.Debug(fmt.Printf("Err: %+v\n", err))
+		return loadedSouthboundInterfaces, err
+	}
+
+	dat, err := ioutil.ReadFile(getCompletePathToFileStore(s.sbiStoreName))
+	if err != nil {
+		log.Debug(fmt.Printf("Err: %+v\n", err))
+		return loadedSouthboundInterfaces, err
+	}
+
+	err = json.Unmarshal(dat, &loadedSouthboundInterfaces)
+	if err != nil {
+		log.Debug(fmt.Printf("Err: %+v\n", err))
+		return loadedSouthboundInterfaces, err
+	}
+
+	return loadedSouthboundInterfaces, nil
+}
diff --git a/store/sbi_store_test.go b/store/sbi_store_test.go
index 2dc294639a6947f6c31374a97b75034297647027..282d9ca93da2e1cd5ea23215d1c200587603ef7d 100644
--- a/store/sbi_store_test.go
+++ b/store/sbi_store_test.go
@@ -1,18 +1,30 @@
 package store
 
 import (
+	"encoding/json"
+	"io/ioutil"
 	"reflect"
 	"sync"
 	"testing"
 
+	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
 	"code.fbi.h-da.de/danet/gosdn/interfaces/southbound"
 	"code.fbi.h-da.de/danet/gosdn/interfaces/store"
 	"code.fbi.h-da.de/danet/gosdn/mocks"
 
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/google/uuid"
 )
 
-func Test_sbiStore_get(t *testing.T) {
+func newStorableSbi(id uuid.UUID, t spb.Type) *mocks.SouthboundInterface {
+	s := &mocks.SouthboundInterface{}
+	s.On("ID").Return(id)
+	s.On("Type").Return(t)
+	return s
+}
+
+func Test_sbi_store_GetSBI(t *testing.T) {
 	openConfig := &mocks.SouthboundInterface{}
 	openConfig.On("ID").Return(defaultSbiID)
 
@@ -69,13 +81,229 @@ func Test_sbiStore_get(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			s := SbiStore{genericStore: tt.fields.genericStore}
 
-			got, err := s.Get(tt.args.id)
+			got, err := s.GetSBI(tt.args.id)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("get() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("GetSBI() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("get() got = %v, want %v", got, tt.want)
+				t.Errorf("GetSBI() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_sbi_store_Add(t *testing.T) {
+	faultySbi := &mocks.NetworkDomain{}
+	faultySbi.On("ID").Return(defaultPndID)
+
+	type fields struct {
+		genericStore *genericStore
+	}
+	type args struct {
+		storable store.Storable
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+	}{
+		{
+			name: "default",
+			fields: fields{
+				&genericStore{
+					Store:     map[uuid.UUID]store.Storable{},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			args:    args{storable: newStorableSbi(defaultSbiID, spb.Type_TYPE_OPENCONFIG)},
+			wantErr: false,
+		},
+		{
+			name: "does not implement SouthboundInterface",
+			fields: fields{
+				&genericStore{
+					Store:     map[uuid.UUID]store.Storable{},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			args:    args{storable: faultySbi},
+			wantErr: true,
+		},
+		{
+			name: "already exists",
+			fields: fields{
+				&genericStore{
+					Store: map[uuid.UUID]store.Storable{
+						defaultSbiID: newStorableSbi(defaultSbiID, spb.Type_TYPE_OPENCONFIG),
+					},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			args:    args{storable: newStorableSbi(defaultSbiID, spb.Type_TYPE_OPENCONFIG)},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := SbiStore{
+				sbiStoreName: "sbi-store_test.json",
+				genericStore: tt.fields.genericStore,
+			}
+
+			err := s.Add(tt.args.storable)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("Add() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+		})
+	}
+}
+
+func Test_sbi_store_persist(t *testing.T) {
+	faultySbi := &mocks.NetworkDomain{}
+	faultySbi.On("ID").Return(defaultPndID)
+
+	csbi := &mocks.Csbi{}
+	csbi.On("ID").Return(defaultSbiID)
+	csbi.On("Type").Return(spb.Type_TYPE_CONTAINERISED)
+	csbi.On("Path").Return("a/test/path/")
+
+	plugin := &mocks.Csbi{}
+	plugin.On("ID").Return(defaultSbiID)
+	plugin.On("Type").Return(spb.Type_TYPE_PLUGIN)
+	plugin.On("Path").Return("a/test/path/")
+
+	// options to apply to cmp.Equal
+	opts := []cmp.Option{
+		// compare option to treat slices of length zero as equal
+		cmpopts.EquateEmpty(),
+		// sort the slices based on the ID
+		cmpopts.SortSlices(func(x, y LoadedSbi) bool {
+			return x.ID.ID() < y.ID.ID()
+		}),
+	}
+
+	type fields struct {
+		genericStore *genericStore
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		want    []LoadedSbi
+		wantErr bool
+	}{
+		{
+			name: "default",
+			fields: fields{
+				&genericStore{
+					Store: map[uuid.UUID]store.Storable{
+						defaultSbiID: newStorableSbi(defaultSbiID, spb.Type_TYPE_OPENCONFIG),
+					},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			want:    []LoadedSbi{{ID: defaultSbiID, Type: spb.Type_TYPE_OPENCONFIG.String()}},
+			wantErr: false,
+		},
+		{
+			name: "multiple items in store",
+			fields: fields{
+				&genericStore{
+					Store: map[uuid.UUID]store.Storable{
+						defaultPndID: newStorableSbi(defaultPndID, spb.Type_TYPE_OPENCONFIG),
+						defaultSbiID: newStorableSbi(defaultSbiID, spb.Type_TYPE_OPENCONFIG),
+					},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			want: []LoadedSbi{
+				{ID: defaultSbiID, Type: spb.Type_TYPE_OPENCONFIG.String()},
+				{ID: defaultPndID, Type: spb.Type_TYPE_OPENCONFIG.String()},
+			},
+			wantErr: false,
+		},
+		{
+			name: "storable is not of type SouthboundInterface",
+			fields: fields{
+				&genericStore{
+					Store: map[uuid.UUID]store.Storable{
+						defaultPndID: faultySbi,
+					},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "storable is of type csbi",
+			fields: fields{
+				&genericStore{
+					Store: map[uuid.UUID]store.Storable{
+						defaultSbiID: csbi,
+					},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			want:    []LoadedSbi{{ID: defaultSbiID, Type: spb.Type_TYPE_CONTAINERISED.String(), Path: csbi.Path()}},
+			wantErr: false,
+		},
+		{
+			name: "storable is of type plugin",
+			fields: fields{
+				&genericStore{
+					Store: map[uuid.UUID]store.Storable{
+						defaultSbiID: plugin,
+					},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			want:    []LoadedSbi{{ID: defaultSbiID, Type: spb.Type_TYPE_PLUGIN.String(), Path: csbi.Path()}},
+			wantErr: false,
+		},
+		{
+			name: "type of spb.CONTAINERISED but does not implement Csbi",
+			fields: fields{
+				&genericStore{
+					Store: map[uuid.UUID]store.Storable{
+						defaultSbiID: newStorableSbi(defaultSbiID, spb.Type_TYPE_CONTAINERISED),
+					},
+					storeLock: sync.RWMutex{},
+				},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			s := SbiStore{
+				sbiStoreName: "sbi-store_test.json",
+				genericStore: tt.fields.genericStore}
+
+			err := s.persist()
+			if (err != nil) != tt.wantErr {
+				t.Errorf("persist() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if err == nil {
+				var got []LoadedSbi
+				dat, err := ioutil.ReadFile(getCompletePathToFileStore(s.sbiStoreName))
+				if err != nil {
+					t.Errorf("persist() error = %v", err)
+				}
+
+				err = json.Unmarshal(dat, &got)
+				if err != nil {
+					t.Errorf("persist() error = %v", err)
+				}
+
+				if !cmp.Equal(got, tt.want, opts...) {
+					t.Errorf("persist() got = %v, want %v", got, tt.want)
+				}
 			}
 		})
 	}
diff --git a/store/utils.go b/store/utils.go
index 074edab53ca811f440092b1c05530f4b3c43111a..3d96d81b323a71020ef6435840ef923e1d490daa 100644
--- a/store/utils.go
+++ b/store/utils.go
@@ -1,7 +1,6 @@
 package store
 
 import (
-	"fmt"
 	"os"
 	"path/filepath"
 
@@ -70,5 +69,5 @@ func ensureDirExists(fileName string) error {
 }
 
 func getCompletePathToFileStore(storeName string) string {
-	return fmt.Sprintf("%s/%s", pathToStores, storeName)
+	return filepath.Join(pathToStores, storeName)
 }
diff --git a/test/integration/nucleusIntegration_test.go b/test/integration/nucleusIntegration_test.go
index 026476882154ff7fd82378998b94a8deda2681ea..cc59d807c569212b113729d80ffd1cebf670d260 100644
--- a/test/integration/nucleusIntegration_test.go
+++ b/test/integration/nucleusIntegration_test.go
@@ -135,19 +135,19 @@ func TestGnmi_SetInvalidIntegration(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			oc, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
+			sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
 			if err != nil {
-				t.Errorf("NewSBI() error = %v", err)
+				t.Errorf("SetInvalidIntegration() error = %v", err)
+				return
 			}
-			g, err := nucleus.NewTransport(tt.fields.opt, oc)
-
-			if err != nil {
-				t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr)
+			g, err := nucleus.NewTransport(tt.fields.opt, sbi)
+			if (err != nil) != tt.wantErr {
+				t.Errorf("SetInvalidIntegration() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 			err = g.Set(tt.args.ctx, tt.args.payload)
 			if (err != nil) != tt.wantErr {
-				t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr)
+				t.Errorf("SetInvalidIntegration() error = %v, wantErr %v", err, tt.wantErr)
 				return
 			}
 		})
@@ -162,8 +162,8 @@ func TestGnmi_SetValidIntegration(t *testing.T) {
 	sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
 	if err != nil {
 		t.Errorf("SetValidIntegration() err = %v", err)
+		return
 	}
-
 	opt := &tpb.TransportOption{
 		Address:  testAddress,
 		Username: testUsername,
@@ -304,13 +304,12 @@ func TestGnmi_GetIntegration(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			oc, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
+			sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
 			if err != nil {
 				t.Errorf("Get() error = %v", err)
 				return
 			}
-			g, err := nucleus.NewTransport(tt.fields.opt, oc)
-
+			g, err := nucleus.NewTransport(tt.fields.opt, sbi)
 			if err != nil {
 				t.Error(err)
 				return
@@ -419,13 +418,12 @@ func TestGnmi_SubscribeIntegration(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			var wantErr = tt.wantErr
-			oc, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
+			sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
 			if err != nil {
 				t.Errorf("Subscribe() error = %v", err)
 				return
 			}
-			g, err := nucleus.NewTransport(tt.fields.opt, oc)
-
+			g, err := nucleus.NewTransport(tt.fields.opt, sbi)
 			if err != nil {
 				t.Error(err)
 				return
@@ -502,12 +500,12 @@ func TestGnmi_CapabilitiesIntegration(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			oc, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
+			sbi, err := nucleus.NewSBI(spb.Type_TYPE_OPENCONFIG)
 			if err != nil {
 				t.Errorf("Capabilities() error = %v", err)
 				return
 			}
-			tr, err := nucleus.NewTransport(tt.fields.opt, oc)
+			tr, err := nucleus.NewTransport(tt.fields.opt, sbi)
 			if err != nil {
 				t.Error(err)
 				return
diff --git a/test/plugin/faulty/gostructs.go b/test/plugin/faulty/gostructs.go
new file mode 100644
index 0000000000000000000000000000000000000000..e0c548a5369a74adf57d8f652569bafe53ea03a7
--- /dev/null
+++ b/test/plugin/faulty/gostructs.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+	gpb "github.com/openconfig/gnmi/proto/gnmi"
+	"github.com/openconfig/goyang/pkg/yang"
+	"github.com/openconfig/ygot/ygot"
+	"github.com/openconfig/ygot/ytypes"
+
+	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
+	"github.com/google/uuid"
+)
+
+type FaultyPlugin struct {
+	schema *ytypes.Schema
+	id     uuid.UUID
+}
+
+// SetNode injects schema specific model representation to the transport.
+// Needed for type assertion.
+func (fp *FaultyPlugin) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
+	return nil
+}
+
+// Unmarshal injects schema specific model representation to the transport.
+// Needed for type assertion.
+func (fp *FaultyPlugin) Unmarshal(bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
+	return nil
+}
+
+func (fp *FaultyPlugin) Schema() *ytypes.Schema {
+	return nil
+}
+
+// SetID sets the ID of the cSBI to the provided UUID
+func (fp *FaultyPlugin) SetID(id uuid.UUID) {
+	fp.id = id
+}
+
+// ID returns the Southbound's UUID
+func (fp *FaultyPlugin) ID() uuid.UUID {
+	return fp.id
+}
+
+// Type returns the Southbound's type
+func (fp *FaultyPlugin) Type() spb.Type {
+	return spb.Type_TYPE_PLUGIN
+}
diff --git a/test/plugin/faulty/plugin.yml b/test/plugin/faulty/plugin.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e20c02acb00783cf1f827edbab5564e5610725fd
--- /dev/null
+++ b/test/plugin/faulty/plugin.yml
@@ -0,0 +1 @@
+version: "1.0.1"
diff --git a/test/plugin/gostructs.go b/test/plugin/gostructs.go
new file mode 100644
index 0000000000000000000000000000000000000000..fbd757b210b0fdcd47b0c1164dd9b41d3284228c
--- /dev/null
+++ b/test/plugin/gostructs.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+	gpb "github.com/openconfig/gnmi/proto/gnmi"
+	"github.com/openconfig/goyang/pkg/yang"
+	"github.com/openconfig/ygot/ygot"
+	"github.com/openconfig/ygot/ytypes"
+
+	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
+	"github.com/google/uuid"
+)
+
+var PluginSymbol Csbi
+
+type Csbi struct {
+	schema *ytypes.Schema
+	id     uuid.UUID
+}
+
+// SetNode injects schema specific model representation to the transport.
+// Needed for type assertion.
+func (csbi *Csbi) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error {
+	return nil
+}
+
+// Unmarshal injects schema specific model representation to the transport.
+// Needed for type assertion.
+func (csbi *Csbi) Unmarshal(bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
+	return nil
+}
+
+func (csbi *Csbi) Schema() *ytypes.Schema {
+	return nil
+}
+
+// SetID sets the ID of the cSBI to the provided UUID
+func (csbi *Csbi) SetID(id uuid.UUID) {
+	csbi.id = id
+}
+
+// ID returns the Southbound's UUID
+func (csbi *Csbi) ID() uuid.UUID {
+	return csbi.id
+}
+
+// Type returns the Southbound's type
+func (csbi *Csbi) Type() spb.Type {
+	return spb.Type_TYPE_PLUGIN
+}
diff --git a/test/plugin/plugin.yml b/test/plugin/plugin.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bef9de49acb8ef101e286146553f5847f0308392
--- /dev/null
+++ b/test/plugin/plugin.yml
@@ -0,0 +1,3 @@
+name: test
+author: goSDN-Team
+version: "v1.0.5"