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 }