diff --git a/cli/cmd/pndCreate.go b/cli/cmd/pndCreate.go
index 0520eaed66069d99ef2042a57d7b093c2de1b228..12e1fbb5b354f56a830f9e29f455f4bb33c4808a 100644
--- a/cli/cmd/pndCreate.go
+++ b/cli/cmd/pndCreate.go
@@ -50,7 +50,7 @@ A description must be passed as positional argument.`,
 
 	Run: func(cmd *cobra.Command, args []string) {
 		spinner, _ := pterm.DefaultSpinner.Start("Creating new PND")
-		_, err := api.AddPnd(createContextWithAuthorization(), viper.GetString("controllerApiEndpoint"), pndName, pndDescription)
+		_, err := api.CreatePnd(createContextWithAuthorization(), viper.GetString("controllerApiEndpoint"), pndName, pndDescription)
 		if err != nil {
 			spinner.Fail("Failed creating the PND with name: ", pndName)
 			spinner.Fail(err)
diff --git a/cli/cmd/pndList.go b/cli/cmd/pndList.go
index f6b15d422ac28f12f0a5a943caf56c5d6bdb9714..d0da1b0a818d06f649c1f54cd9e5243d7cb8d0a3 100644
--- a/cli/cmd/pndList.go
+++ b/cli/cmd/pndList.go
@@ -47,7 +47,7 @@ var pndListCmd = &cobra.Command{
 
 	Run: func(cmd *cobra.Command, args []string) {
 		spinner, _ := pterm.DefaultSpinner.Start("Fetching PND list from controller")
-		resp, err := api.GetPnds(createContextWithAuthorization(), pndAdapter.Endpoint())
+		resp, err := api.GetPndList(createContextWithAuthorization(), pndAdapter.Endpoint())
 		if err != nil {
 			spinner.Fail(err)
 			return
diff --git a/controller/api/api_test.go b/controller/api/api_test.go
index b7715497db231256c952710bf585d50398ae64c7..3f6e07fce06da04ba95c8761eea0f64dc3083b90 100644
--- a/controller/api/api_test.go
+++ b/controller/api/api_test.go
@@ -55,7 +55,7 @@ func ensureStoreFileForTestsIsRemoved(storeName string) {
 func Test_AddPnd(t *testing.T) {
 	defer ensureFilesForTestsAreRemoved()
 
-	resp, err := AddPnd(context.TODO(), bufnet, "test", "test pnd")
+	resp, err := CreatePnd(context.TODO(), bufnet, "test", "test pnd")
 	if err != nil {
 		t.Error(err)
 		return
diff --git a/controller/api/app.go b/controller/api/app.go
new file mode 100644
index 0000000000000000000000000000000000000000..fcb66d5f4ac4453d72e50b10a88fd2ad7db6dd8e
--- /dev/null
+++ b/controller/api/app.go
@@ -0,0 +1,40 @@
+package api
+
+import (
+	"context"
+	"time"
+
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/app"
+	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client"
+)
+
+// Register checks if the app already exists and if not creates a new one.
+func Register(ctx context.Context, addr, appname, token string) (*apb.AppRegisterResponse, error) {
+	appClient, err := nbi.AppClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	req := &apb.AppRegisterRequest{
+		Timestamp: time.Now().UnixNano(),
+		Appname:   appname,
+		Token:     token,
+	}
+
+	return appClient.Register(ctx, req)
+}
+
+// Deregister deregisters an app.
+func Deregister(ctx context.Context, addr, appname, token string) (*apb.AppDeregisterResponse, error) {
+	appClient, err := nbi.AppClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	req := &apb.AppDeregisterRequest{
+		Timestamp: time.Now().UnixNano(),
+		Appname:   appname,
+	}
+
+	return appClient.Deregister(ctx, req)
+}
diff --git a/controller/api/configurationManagement.go b/controller/api/configurationManagement.go
new file mode 100644
index 0000000000000000000000000000000000000000..986a192af234ae077cfb128121f38618cc6a23ac
--- /dev/null
+++ b/controller/api/configurationManagement.go
@@ -0,0 +1,38 @@
+package api
+
+import (
+	"context"
+	"time"
+
+	cpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement"
+	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client"
+)
+
+// ExportSDNConfig returns the SDN configuration.
+func ExportSDNConfig(ctx context.Context, addr, pid string) (*cpb.ExportSDNConfigResponse, error) {
+	configClient, err := nbi.ConfigurationManagementClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	req := &cpb.ExportSDNConfigRequest{
+		Timestamp: time.Now().UnixNano(),
+		Pid:       pid,
+	}
+	return configClient.ExportSDNConfig(ctx, req)
+}
+
+// ImportSDNConfig receives an SDN configuration and imports it.
+func ImportSDNConfig(ctx context.Context, addr, pid, sdnConfigData string) (*cpb.ImportSDNConfigResponse, error) {
+	configClient, err := nbi.ConfigurationManagementClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	req := &cpb.ImportSDNConfigRequest{
+		Timestamp:     time.Now().UnixNano(),
+		Pid:           pid,
+		SdnConfigData: sdnConfigData,
+	}
+	return configClient.ImportSDNConfig(ctx, req)
+}
diff --git a/controller/api/managedNetworkElement.go b/controller/api/managedNetworkElement.go
index b07ad58e0d8156ff079401f0a57813926b061d92..0cc5fdecabf20ea9f35e2639e2e264fed086ebf2 100644
--- a/controller/api/managedNetworkElement.go
+++ b/controller/api/managedNetworkElement.go
@@ -2,19 +2,15 @@ package api
 
 import (
 	"context"
-	"errors"
-	"io"
 	"time"
 
+	"code.fbi.h-da.de/danet/gosdn/api/go/gosdn/conflict"
 	mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement"
-	pipb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-internal"
 	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
 	tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport"
 	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client"
 
 	"github.com/google/uuid"
-	"github.com/openconfig/goyang/pkg/yang"
-	"github.com/openconfig/ygot/ygot"
 	log "github.com/sirupsen/logrus"
 )
 
@@ -50,6 +46,23 @@ func AddNetworkElement(ctx context.Context, addr, mneName, mneUUID string, opt *
 	return client.AddList(ctx, req)
 }
 
+// AddNetworkElementList adds all the network elements to the controller. The name of each network element is optional.
+// If no name is provided a name will be generated upon network element creation.
+func AddNetworkElementList(ctx context.Context, addr, pid string, mneList []*mnepb.SetMne) (*mnepb.AddListResponse, error) {
+	client, err := nbi.NetworkElementClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	req := &mnepb.AddListRequest{
+		Timestamp: time.Now().UnixNano(),
+		Mne:       mneList,
+		Pid:       pid,
+	}
+
+	return client.AddList(ctx, req)
+}
+
 // GetNetworkElement requests one network element belonging to a given
 // PrincipalNetworkDomain from the controller. If no network element identifier
 // is provided, an error is thrown.
@@ -72,51 +85,19 @@ func GetNetworkElement(ctx context.Context, addr, pid string, mneid string) (*mn
 	return client.Get(ctx, req)
 }
 
-// GetPluginSchemaTree gets the schema tree for a plugin.
-func GetPluginSchemaTree(ctx context.Context, addr string, pluginID uuid.UUID) (map[string]*yang.Entry, error) {
-	pluginClient, err := nbi.PluginClient(addr, dialOptions...)
+// GetNetworkElements requests all available network elements related to one PND.
+func GetNetworkElements(ctx context.Context, addr, pid string) (*mnepb.GetAllResponse, error) {
+	client, err := nbi.NetworkElementClient(addr, dialOptions...)
 	if err != nil {
-		return map[string]*yang.Entry{}, err
+		return nil, err
 	}
 
-	req := &pipb.GetPluginSchemaRequest{
+	req := &mnepb.GetAllRequest{
 		Timestamp: time.Now().UnixNano(),
-		Pid:       pluginID.String(),
-	}
-
-	ctx, cancel := context.WithTimeout(ctx, time.Minute*10)
-	defer cancel()
-	sClient, err := pluginClient.GetPluginSchema(ctx, req)
-	if err != nil {
-		return map[string]*yang.Entry{}, err
-	}
-
-	sTreeBytes := []byte{}
-
-	for {
-		payload, err := sClient.Recv()
-		if err != nil {
-			if errors.Is(err, io.EOF) {
-				break
-			}
-			log.Error(err)
-
-			closeErr := sClient.CloseSend()
-			if closeErr != nil {
-				return nil, err
-			}
-
-			return map[string]*yang.Entry{}, err
-		}
-		sTreeBytes = append(sTreeBytes, payload.Chunk...)
-	}
-
-	sTreeMap, err := ygot.GzipToSchema(sTreeBytes)
-	if err != nil {
-		return map[string]*yang.Entry{}, err
+		Pid:       pid,
 	}
 
-	return sTreeMap, nil
+	return client.GetAll(ctx, req)
 }
 
 // GetFlattenedNetworkElement requests a network elements belonging to a given
@@ -218,3 +199,28 @@ func DeleteNetworkElement(ctx context.Context, addr, pid, mneid string) (*mnepb.
 
 	return client.Delete(ctx, req)
 }
+
+// UpdateNetworkElement changes the metadata of the network element which is stored in the controller storage.
+// Correct resource version needs to be provided to allow a change of the object in the storage.
+func UpdateNetworkElement(ctx context.Context, addr, mneid, updatedName, updatedTransportAddress, updatedPid string, resourceVersion *conflict.Metadata) (*mnepb.UpdateNetworkElementResponse, error) {
+	client, err := nbi.NetworkElementClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	req := &mnepb.UpdateNetworkElementRequest{
+		Timestamp: time.Now().UnixNano(),
+		NetworkElement: &mnepb.ManagedNetworkElement{
+			Id:               mneid,
+			Name:             updatedName,
+			TransportAddress: updatedTransportAddress,
+			TransportOption: &tpb.TransportOption{
+				Address: updatedTransportAddress,
+			},
+			Metadata:      resourceVersion,
+			AssociatedPnd: updatedPid,
+		},
+	}
+
+	return client.Update(ctx, req)
+}
diff --git a/controller/api/plugin.go b/controller/api/plugin.go
index 638e66932227d25497cf4b4de9b41e7bf005a847..8eed1aba0b10ae15a2bb9097db3a2994d93f0da1 100644
--- a/controller/api/plugin.go
+++ b/controller/api/plugin.go
@@ -2,11 +2,18 @@ package api
 
 import (
 	"context"
+	"errors"
+	"io"
 	"time"
 
 	pib "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-internal"
+	pipb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-internal"
 	prb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-registry"
 	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client"
+	"github.com/google/uuid"
+	"github.com/openconfig/goyang/pkg/yang"
+	"github.com/openconfig/ygot/ygot"
+	"github.com/sirupsen/logrus"
 )
 
 // GetAvailablePlugins requests all available plugins that can be registered
@@ -24,3 +31,50 @@ func GetAvailablePlugins(ctx context.Context, addr string) (*prb.GetResponse, er
 
 	return pluginRegistryClient.AvailablePlugins(ctx, req)
 }
+
+// GetPluginSchemaTree gets the schema tree for a plugin.
+func GetPluginSchemaTree(ctx context.Context, addr string, pluginID uuid.UUID) (map[string]*yang.Entry, error) {
+	pluginClient, err := nbi.PluginClient(addr, dialOptions...)
+	if err != nil {
+		return map[string]*yang.Entry{}, err
+	}
+
+	req := &pipb.GetPluginSchemaRequest{
+		Timestamp: time.Now().UnixNano(),
+		Pid:       pluginID.String(),
+	}
+
+	ctx, cancel := context.WithTimeout(ctx, time.Minute*10)
+	defer cancel()
+	sClient, err := pluginClient.GetPluginSchema(ctx, req)
+	if err != nil {
+		return map[string]*yang.Entry{}, err
+	}
+
+	sTreeBytes := []byte{}
+
+	for {
+		payload, err := sClient.Recv()
+		if err != nil {
+			if errors.Is(err, io.EOF) {
+				break
+			}
+			logrus.Error(err)
+
+			closeErr := sClient.CloseSend()
+			if closeErr != nil {
+				return nil, err
+			}
+
+			return map[string]*yang.Entry{}, err
+		}
+		sTreeBytes = append(sTreeBytes, payload.Chunk...)
+	}
+
+	sTreeMap, err := ygot.GzipToSchema(sTreeBytes)
+	if err != nil {
+		return map[string]*yang.Entry{}, err
+	}
+
+	return sTreeMap, nil
+}
diff --git a/controller/api/pnd.go b/controller/api/pnd.go
index c47eb5897df5ed20764206c1b620cc83a9f46671..b8ab4d66bb32705e31b604e07a219ab111dc5a35 100644
--- a/controller/api/pnd.go
+++ b/controller/api/pnd.go
@@ -9,9 +9,9 @@ import (
 	nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client"
 )
 
-// AddPnd takes a name, description and SBI UUID to create a new
+// CreatePnd takes an address, a name and a description to create a new
 // PrincipalNetworkDomain on the controller.
-func AddPnd(ctx context.Context, addr, name, description string) (*ppb.CreatePndListResponse, error) {
+func CreatePnd(ctx context.Context, addr, name, description string) (*ppb.CreatePndListResponse, error) {
 	pndClient, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
@@ -30,6 +30,21 @@ func AddPnd(ctx context.Context, addr, name, description string) (*ppb.CreatePnd
 	return pndClient.CreatePndList(ctx, req)
 }
 
+// CreatePndList uses the provided creation properties to add all the PNDs to the controller.
+func CreatePndList(ctx context.Context, addr string, pnds []*ppb.PndCreateProperties) (*ppb.CreatePndListResponse, error) {
+	pndClient, err := nbi.PndClient(addr, dialOptions...)
+	if err != nil {
+		return nil, err
+	}
+
+	req := &ppb.CreatePndListRequest{
+		Timestamp: time.Now().UnixNano(),
+		Pnd:       pnds,
+	}
+
+	return pndClient.CreatePndList(ctx, req)
+}
+
 // GetPnd requests one PrincipalNetworkDomain from the
 // controller.
 func GetPnd(ctx context.Context, addr string, args string) (*ppb.GetPndResponse, error) {
@@ -48,9 +63,9 @@ func GetPnd(ctx context.Context, addr string, args string) (*ppb.GetPndResponse,
 	return pndClient.GetPnd(ctx, req)
 }
 
-// GetPnds requests all PrincipalNetworkDomains from the
+// GetPndList requests all PrincipalNetworkDomains from the
 // controller.
-func GetPnds(ctx context.Context, addr string, args ...string) (*ppb.GetPndListResponse, error) {
+func GetPndList(ctx context.Context, addr string, args ...string) (*ppb.GetPndListResponse, error) {
 	pndClient, err := nbi.PndClient(addr, dialOptions...)
 	if err != nil {
 		return nil, err
diff --git a/controller/northbound/client/app.go b/controller/northbound/client/app.go
new file mode 100644
index 0000000000000000000000000000000000000000..750af3056c54d174b6c9c07716533b0ae0a247cc
--- /dev/null
+++ b/controller/northbound/client/app.go
@@ -0,0 +1,17 @@
+package client
+
+import (
+	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/app"
+	"google.golang.org/grpc"
+)
+
+// AppClient returns a client for the gRPC App service. It takes
+// the address of the gRPC endpoint and optional grpc.DialOption
+// as argument.
+func AppClient(addr string, opts ...grpc.DialOption) (apb.AppServiceClient, error) {
+	conn, err := grpc.Dial(addr, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return apb.NewAppServiceClient(conn), nil
+}
diff --git a/controller/northbound/client/configurationManagement.go b/controller/northbound/client/configurationManagement.go
new file mode 100644
index 0000000000000000000000000000000000000000..8e008d173165d70a5098f3c0ff20605b4b089004
--- /dev/null
+++ b/controller/northbound/client/configurationManagement.go
@@ -0,0 +1,17 @@
+package client
+
+import (
+	cpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/configurationmanagement"
+	"google.golang.org/grpc"
+)
+
+// ConfigurationManagementClient returns a client for the gRPC ConfigurationManagement service. It takes
+// the address of the gRPC endpoint and optional grpc.DialOption
+// as argument.
+func ConfigurationManagementClient(addr string, opts ...grpc.DialOption) (cpb.ConfigurationManagementServiceClient, error) {
+	conn, err := grpc.Dial(addr, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return cpb.NewConfigurationManagementServiceClient(conn), nil
+}
diff --git a/controller/northbound/server/networkElement.go b/controller/northbound/server/networkElement.go
index 5f95d4fec0c7f4cec0332bcb4df468e93f828cd4..4a3db1c4facc3b772a12d5f7ac9ebcb1710c8698 100644
--- a/controller/northbound/server/networkElement.go
+++ b/controller/northbound/server/networkElement.go
@@ -663,7 +663,7 @@ func (n *NetworkElementServer) addMne(ctx context.Context,
 	}
 
 	if mne.IsTransportValid() {
-		err = n.initialNetworkElementRootPathRequest(ctx, mne, plugin)
+		err = n.initialNetworkElementRootPathRequest(ctx, mne)
 		if err != nil {
 			return uuid.Nil, err
 		}
@@ -692,7 +692,7 @@ func (n *NetworkElementServer) addMne(ctx context.Context,
 	return mne.ID(), nil
 }
 
-func (n *NetworkElementServer) initialNetworkElementRootPathRequest(ctx context.Context, mne networkelement.NetworkElement, plugin plugin.Plugin) error {
+func (n *NetworkElementServer) initialNetworkElementRootPathRequest(ctx context.Context, mne networkelement.NetworkElement) error {
 	resp, err := n.getPath(ctx, mne, "/")
 	if err != nil {
 		return err
diff --git a/controller/nucleus/pndFilesystemStore.go b/controller/nucleus/pndFilesystemStore.go
index 863fe72052c5adfb872f7c441b8671ad34d00e1b..c7d2df700d02ffe8bec8fef8949af74ea973bd3c 100644
--- a/controller/nucleus/pndFilesystemStore.go
+++ b/controller/nucleus/pndFilesystemStore.go
@@ -59,14 +59,15 @@ func (t *FilesystemPndStore) readAllPndsFromFile() ([]networkdomain.LoadedPnd, e
 	// }
 
 	for i, loadedPND := range loadedPnds {
+		pndUUID, err := uuid.Parse(loadedPND.ID)
+		if err != nil {
+			return nil, err
+		}
 		newPnd := NewPND(
-			uuid.MustParse(loadedPND.ID),
+			pndUUID,
 			loadedPND.Name,
 			loadedPND.Description,
 		)
-		if err != nil {
-			return nil, err
-		}
 
 		pnds[i] = newPnd
 	}