diff --git a/controller/interfaces/plugin/plugin.go b/controller/interfaces/plugin/plugin.go index dc01a0ee8145f5638cd4ca92d0bfa5b3017b68fe..f61bda8a8ec589486a6452141d4e07727790e2c3 100644 --- a/controller/interfaces/plugin/plugin.go +++ b/controller/interfaces/plugin/plugin.go @@ -27,11 +27,9 @@ 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 + // INITIALIZED state describes a plugin which is running and has been + // initialized with the model data of the associated network element. + INITIALIZED // FAULTY state describes a plugin which couldn't be built or loaded. FAULTY ) diff --git a/controller/northbound/server/networkElement.go b/controller/northbound/server/networkElement.go index 2041d576da3a7e41edbdcd4c78ac5e3fb01dce29..a7e0dda020ebb08dd6c1b22bf4be04ad61c7faae 100644 --- a/controller/northbound/server/networkElement.go +++ b/controller/northbound/server/networkElement.go @@ -438,17 +438,17 @@ func (n *NetworkElementServer) GetPath(ctx context.Context, request *mnepb.GetPa Name: pnd.GetName(), Description: pnd.GetDescription(), }, - MneNotification: resp.(*gnmi.GetResponse).Notification, + MneNotification: resp.Notification, }, nil } -func (n *NetworkElementServer) getPath(ctx context.Context, mne networkelement.NetworkElement, path string) (proto.Message, error) { +func (n *NetworkElementServer) getPath(ctx context.Context, mne networkelement.NetworkElement, path string) (*gnmi.GetResponse, error) { res, err := mne.Transport().Get(ctx, path) if err != nil { return nil, err } - resp, ok := res.(proto.Message) + protoMessage, ok := res.(proto.Message) if !ok { return nil, &customerrs.InvalidTypeAssertionError{ Value: res, @@ -456,12 +456,15 @@ func (n *NetworkElementServer) getPath(ctx context.Context, mne networkelement.N } } - err = mne.ProcessResponse(resp) - if err != nil { - return nil, err + getResponse, ok := protoMessage.(*gnmi.GetResponse) + if !ok { + return nil, &customerrs.InvalidTypeAssertionError{ + Value: res, + Type: (*gnmi.GetResponse)(nil), + } } - return resp, nil + return getResponse, nil } // GetIntendedPath gets a path as the intended state stored in the storage. @@ -689,7 +692,12 @@ func (n *NetworkElementServer) addMne(ctx context.Context, name string, opt *tpb } if mne.IsTransportValid() { - _, err = n.getPath(ctx, mne, "/") + resp, err := n.getPath(ctx, mne, "/") + if err != nil { + return uuid.Nil, err + } + + err = mne.ProcessResponse(resp) if err != nil { return uuid.Nil, err } diff --git a/controller/nucleus/networkElementService.go b/controller/nucleus/networkElementService.go index ccd7d547314da7a5a26f93edf13fcbf286d42abe..3a8111abf466eef5af69b71b97388ccec244c379 100644 --- a/controller/nucleus/networkElementService.go +++ b/controller/nucleus/networkElementService.go @@ -133,7 +133,8 @@ func (s *NetworkElementService) UpdateModel(networkElementID uuid.UUID, modelAsS }, } - // Use unmarshall from the network elements SBI to unmarshall ygot json in go struct. + // Use SetNode within the related plugin to map the path/value pair to the + // given ygot.GoStruct. err = exisitingNetworkElement.GetPlugin().SetNode(path, typedValue) if err != nil { return err @@ -235,22 +236,27 @@ func (s *NetworkElementService) createNetworkElementFromStore(loadedNetworkEleme return nil, err } - // Create 'root' path to be able to load the whole model from the store. - path, err := ygot.StringToPath("/", ygot.StructuredPath) - if err != nil { - return nil, err - } + // if the plugin is not initialized, we need to initialize it with the + // model data from the associated network element. + if mne.GetPlugin().State() != plugin.INITIALIZED { + // Create 'root' path to be able to load the whole model from the store. + path, err := ygot.StringToPath("/", ygot.StructuredPath) + if err != nil { + return nil, err + } - typedValue := &gnmi.TypedValue{ - Value: &gnmi.TypedValue_JsonIetfVal{ - JsonIetfVal: []byte(loadedNetworkElement.Model), - }, - } + typedValue := &gnmi.TypedValue{ + Value: &gnmi.TypedValue_JsonIetfVal{ + JsonIetfVal: []byte(loadedNetworkElement.Model), + }, + } - // Use unmarshall from the network elements SBI to unmarshall ygot json in go struct. - err = mne.GetPlugin().SetNode(path, typedValue) - if err != nil { - return nil, err + // Use SetNode within the related plugin to map the path/value pair to + // the given ygot.GoStruct. + err = mne.GetPlugin().SetNode(path, typedValue) + if err != nil { + return nil, err + } } return mne, nil diff --git a/controller/nucleus/networkElementService_test.go b/controller/nucleus/networkElementService_test.go index 7a02b94d8e8d4432fda45dea44f5c7fbdf65d6fd..1df0d17dc2272b3df896afe4c5cbfc2bc7b4f0d1 100644 --- a/controller/nucleus/networkElementService_test.go +++ b/controller/nucleus/networkElementService_test.go @@ -32,6 +32,7 @@ func getNetworkElementTestStores(t *testing.T, mneID uuid.UUID) (networkelement. mockPlugin.On("ID").Return(mockPluginID) mockPlugin.On("Model", mock.Anything).Return([]byte("hello"), nil) mockPlugin.On("SetNode", mock.Anything, mock.Anything).Return(nil) + mockPlugin.On("State").Return(plugin.CREATED) eventService := eventservice.NewMockEventService() pluginStore := NewMemoryPluginStore() networkElementStore := NewMemoryNetworkElementStore() diff --git a/controller/nucleus/networkElementWatcher.go b/controller/nucleus/networkElementWatcher.go index 9ff8ee31efdc05473b0ce038680be11c3c692260..2ead5fcdb1644d334978ffa1a8cebbfa155e2c56 100644 --- a/controller/nucleus/networkElementWatcher.go +++ b/controller/nucleus/networkElementWatcher.go @@ -151,11 +151,6 @@ func (n *NetworkElementWatcher) handleSubscribeResponseUpdate(resp *gpb.Subscrib log.Error(err) } - err = mne.Transport().ProcessControlPlaneSubscribeResponse(resp) - if err != nil { - log.Error(err) - } - pathsAndValues := make(map[string]string, len(resp.Update.Update)) for _, update := range resp.Update.Update { diff --git a/controller/nucleus/plugin.go b/controller/nucleus/plugin.go index adc0312b3d41c5ba03d695dcead5232d51e9563a..032c30ed4136f8741ff6a13de47b19f5a4a3275c 100644 --- a/controller/nucleus/plugin.go +++ b/controller/nucleus/plugin.go @@ -15,6 +15,7 @@ import ( "go.mongodb.org/mongo-driver/bson" ) +// Plugin is the controllers internal representation of a plugin. type Plugin struct { UUID uuid.UUID state plugin.State @@ -24,6 +25,7 @@ type Plugin struct { shared.DeviceModel } +// NewPlugin creates a new Plugin. func NewPlugin(id uuid.UUID, execPath string) (*Plugin, error) { client := hcplugin.NewClient(&hcplugin.ClientConfig{ HandshakeConfig: shared.Handshake, @@ -37,18 +39,24 @@ func NewPlugin(id uuid.UUID, execPath string) (*Plugin, error) { return nil, err } - // connect through grpc + // create a client that is within the AllowedProtocols. In this case this + // returns a gRPCClient. Allows to connect through gRPC. gRPCClient, err := client.Client() if err != nil { return nil, err } - // Request the plugin + // Request the plugin. This returns the gRPC client from the + // DeviceModelPlugin. This can then be casted to the interface that we are + // exposing through the plugin (in this case "DeviceModel"). raw, err := gRPCClient.Dispense("deviceModel") if err != nil { return nil, err } + // cast the raw plugin to the DeviceModel interface. This allows to call + // methods on the plugin as if it were a normal DeviceModel instance but + // actually they are executed on the plugin sent through gRPC. model, ok := raw.(shared.DeviceModel) if !ok { return nil, customerrs.InvalidTypeAssertionError{ @@ -67,6 +75,7 @@ func NewPlugin(id uuid.UUID, execPath string) (*Plugin, error) { }, nil } +// NewPluginThroughReattachConfig creates a new Plugin through a reattach config. func NewPluginThroughReattachConfig(loadedPlugin plugin.LoadedPlugin) (plugin.Plugin, error) { client := hcplugin.NewClient(&hcplugin.ClientConfig{ HandshakeConfig: shared.Handshake, @@ -75,18 +84,24 @@ func NewPluginThroughReattachConfig(loadedPlugin plugin.LoadedPlugin) (plugin.Pl AllowedProtocols: []hcplugin.Protocol{hcplugin.ProtocolGRPC}, }) - // connect through grpc + // create a client that is within the AllowedProtocols. In this case this + // returns a gRPCClient. Allows to connect through gRPC. gRPCClient, err := client.Client() if err != nil { return nil, err } - // Request the plugin + // Request the plugin. This returns the gRPC client from the + // DeviceModelPlugin. This can then be casted to the interface that we are + // exposing through the plugin (in this case "DeviceModel"). raw, err := gRPCClient.Dispense("deviceModel") if err != nil { return nil, err } + // cast the raw plugin to the DeviceModel interface. This allows to call + // methods on the plugin as if it were a normal DeviceModel instance but + // actually they are executed on the plugin sent through gRPC. model, ok := raw.(shared.DeviceModel) if !ok { return nil, customerrs.InvalidTypeAssertionError{ @@ -100,7 +115,7 @@ func NewPluginThroughReattachConfig(loadedPlugin plugin.LoadedPlugin) (plugin.Pl client: client, DeviceModel: model, manifest: &loadedPlugin.Manifest, - state: plugin.CREATED, + state: plugin.INITIALIZED, }, nil } @@ -116,13 +131,14 @@ func (p *Plugin) ReattachConfig() *hcplugin.ReattachConfig { // State returns the current state of the plugin. // Different states of the plugin can be: -// - built -// - loaded +// - created +// - initialized // - faulty func (p *Plugin) State() plugin.State { return p.state } +// ExecPath returns the path to the executable of the plugin. func (p *Plugin) ExecPath() string { return p.execPath } @@ -137,10 +153,12 @@ func (p *Plugin) Manifest() *plugin.Manifest { return p.manifest } +// Update updates the plugin to the latest available version. func (p *Plugin) Update() error { return fmt.Errorf("not implemented yet") } +// Restart restarts the plugin. func (p *Plugin) Restart() error { return fmt.Errorf("not implemented yet") } diff --git a/controller/plugin/shared/client.go b/controller/plugin/shared/client.go index 2fc69232677ef43268e6bd5f6faf21d0528d85ca..4bdfdb8399f7a96a663be4a13d89cd771a59ad33 100644 --- a/controller/plugin/shared/client.go +++ b/controller/plugin/shared/client.go @@ -12,8 +12,16 @@ import ( "github.com/sirupsen/logrus" ) +// DeviceModelClient is the implementation of a DeviceModel that can +// communicate via gRPC. The main purpose it to act as the gRPC client for the +// DeviceModelPlugin. A hashicorp client can consume and use it. The +// DeviceModelClient has to satisfy the DeviceModel interface and therefore +// implements all the needed methods. type DeviceModelClient struct{ client pb.PluginClient } +// Unmarshal calls the Unmarshal method of the DeviceModelClients client. A +// request is created with the given json and path. If the operation failed an +// error is returned. func (m *DeviceModelClient) Unmarshal(json []byte, path *gpb.Path) error { _, err := m.client.Unmarshal(context.Background(), &pb.UnmarshalRequest{ Json: json, @@ -22,6 +30,9 @@ func (m *DeviceModelClient) Unmarshal(json []byte, path *gpb.Path) error { return err } +// SetNode calls the SetNode method of the DeviceModelClients client. A request +// is created with the given path and value. If the operation failed an error is +// returned. func (m *DeviceModelClient) SetNode(path *gpb.Path, value *gpb.TypedValue) error { _, err := m.client.SetNode(context.Background(), &pb.SetNodeRequest{ Path: path, @@ -30,6 +41,10 @@ func (m *DeviceModelClient) SetNode(path *gpb.Path, value *gpb.TypedValue) error return err } +// GetNode calls the GetNode method of the DeviceModelClients client. A request +// is created with the given path and the provided boolean that decides if only +// the intended state should be requested. The response is returned as a slice +// of notifications. If the operation failed an error is returned. func (m *DeviceModelClient) GetNode(path *gpb.Path, requestForIntendedState bool) ([]*gpb.Notification, error) { resp, err := m.client.GetNode(context.Background(), &pb.GetNodeRequest{ Path: path, @@ -38,6 +53,9 @@ func (m *DeviceModelClient) GetNode(path *gpb.Path, requestForIntendedState bool return resp.GetNodes(), err } +// DeleteNode calls the DeleteNode method of the DeviceModelClients client. A +// request is created with the given path. If the operation failed an error is +// returned. func (m *DeviceModelClient) DeleteNode(path *gpb.Path) error { _, err := m.client.DeleteNode(context.Background(), &pb.DeleteNodeRequest{ Path: path, @@ -45,6 +63,10 @@ func (m *DeviceModelClient) DeleteNode(path *gpb.Path) error { return err } +// Model calls the Model method of the DeviceModelClients client. A request is +// created with the given boolean that decides if read-only nodes should be +// filtered. The response returns the model (filtered or not) as json. If the +// operation failed an error is returned. func (m *DeviceModelClient) Model(filterReadOnly bool) ([]byte, error) { resp, err := m.client.Model(context.Background(), &pb.ModelRequest{ FilterReadOnly: filterReadOnly, @@ -52,6 +74,9 @@ func (m *DeviceModelClient) Model(filterReadOnly bool) ([]byte, error) { return resp.Json, err } +// Diff calls the Diff method of the DeviceModelClients client. A request is +// created with the given original and modified model. The response returns the +// diff as a gnmi notifications. If the operation failed an error is returned. func (m *DeviceModelClient) Diff(original, modified []byte) (*gpb.Notification, error) { resp, err := m.client.Diff(context.Background(), &pb.DiffRequest{ Original: original, @@ -60,6 +85,10 @@ func (m *DeviceModelClient) Diff(original, modified []byte) (*gpb.Notification, return resp.GetNotification(), err } +// ValidateChange calls the ValidateChange method of the DeviceModelClients +// client. A request is created with the given operation, path and value (as +// gnmi.TypedValue). The response returns the model as json. If the operation +// failed an error is returned. func (m *DeviceModelClient) ValidateChange(operation mnepb.ApiOperation, path *gpb.Path, value *gpb.TypedValue) ([]byte, error) { resp, err := m.client.ValidateChange(context.Background(), &pb.ValidateChangeRequest{ Operation: operation, @@ -69,6 +98,10 @@ func (m *DeviceModelClient) ValidateChange(operation mnepb.ApiOperation, path *g return resp.GetModel(), err } +// PruneConfigFalse calls the PruneConfigFalse method of the DeviceModelClients +// client. A request is created with the given model value (json as byte[]). +// The response returns the model with the pruned config false nodes. If the +// operation failed an error is returned. func (m *DeviceModelClient) PruneConfigFalse(value []byte) ([]byte, error) { resp, err := m.client.PruneConfigFalse(context.Background(), &pb.PruneConfigFalseRequest{ Value: value, @@ -76,6 +109,8 @@ func (m *DeviceModelClient) PruneConfigFalse(value []byte) ([]byte, error) { return resp.GetModel(), err } +// SchemaTree calls the SchemaTree method of the DeviceModelClients client. The +// response stream is packed into a slice of bytes and returned. func (m *DeviceModelClient) SchemaTreeGzip() ([]byte, error) { schemaTreeGzipClient, err := m.client.SchemaTreeGzip(context.Background(), &pb.SchemaTreeGzipRequest{}) if err != nil { diff --git a/controller/plugin/shared/interface.go b/controller/plugin/shared/interface.go index 8f438f94d01ed497a853ce5e2bb1098df918711c..e690481b34e9747620a868f0000b1c714bdb9894 100644 --- a/controller/plugin/shared/interface.go +++ b/controller/plugin/shared/interface.go @@ -11,16 +11,19 @@ import ( gpb "github.com/openconfig/gnmi/proto/gnmi" ) +// Hanshake describes the handshake config for the plugin. var Handshake = plugin.HandshakeConfig{ ProtocolVersion: 1, MagicCookieKey: "GOSDN_PLUGIN_MAGIC_COOKIE", MagicCookieValue: "woux6tn7gbsm53ipb3w4zxb59qd3se43hnqeh5bieynzvfchchktsd32pbjqwuxq", } +// PluginMap is the map of plugins that can be used. var PluginMap = map[string]plugin.Plugin{ "deviceModel": &DeviceModelPlugin{}, } +// DeviceModel describes the interface that will be accessible through the plugin. type DeviceModel interface { // TODO: It should be possible to pass methods like Unmarshal, SetNode, // GetNode, etc. ytypes.Unmarshal-|Set-|GetOptions @@ -35,13 +38,18 @@ type DeviceModel interface { PruneConfigFalse(value []byte) ([]byte, error) } -// DeviceModelPlugin implements a hashicorp gRPC plugin. +// DeviceModelPlugin is the implementation of a plugin.GRPCPlugin. It embeds +// the Plugin interface from hashicorp/go-plugin as well a the DeviceModel +// interface. type DeviceModelPlugin struct { plugin.Plugin Impl DeviceModel } func (p *DeviceModelPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + // Register a PluginServer defined through the proto definitions to the + // provided server - a DeviceModelServer implements the PluginServer + // interface. pb.RegisterPluginServer(s, &DeviceModelServer{Impl: p.Impl}) return nil } diff --git a/controller/plugin/shared/server.go b/controller/plugin/shared/server.go index 56d0270b31893ab0c5925991c0d273853667f93e..b49c0558b5acd87f0c9f96f364a6490fb94c1284 100644 --- a/controller/plugin/shared/server.go +++ b/controller/plugin/shared/server.go @@ -11,11 +11,16 @@ import ( "github.com/sirupsen/logrus" ) +// DeviceModelServer is the gRPC server a DeviceModelClient can communicate +// with. A implementation of a DeviceModel should be provided. type DeviceModelServer struct { Impl DeviceModel pb.UnimplementedPluginServer } +// Unmarshal calls the Unmarshal method of the DeviceModel implementation. It +// returns a response with a boolean indicating if the operation was +// successful and an error if the operation failed. func (m *DeviceModelServer) Unmarshal( ctx context.Context, req *pb.UnmarshalRequest) (*pb.UnmarshalResponse, error) { @@ -26,6 +31,9 @@ func (m *DeviceModelServer) Unmarshal( return &pb.UnmarshalResponse{Valid: true}, err } +// SetNode calls the SetNode method of the DeviceModel implementation. It +// returns a response with a boolean indicating if the operation was +// successful and an error if the operation failed. func (m *DeviceModelServer) SetNode( ctx context.Context, req *pb.SetNodeRequest) (*pb.SetNodeResponse, error) { @@ -33,6 +41,9 @@ func (m *DeviceModelServer) SetNode( return &pb.SetNodeResponse{Valid: true}, err } +// GetNode calls the GetNode method of the DeviceModel implementation. It +// returns a response with a slice of notifications and an error if the +// operation failed. func (m *DeviceModelServer) GetNode( ctx context.Context, req *pb.GetNodeRequest) (*pb.GetNodeResponse, error) { @@ -40,6 +51,9 @@ func (m *DeviceModelServer) GetNode( return &pb.GetNodeResponse{Nodes: nodes}, err } +// DeleteNode calls the DeleteNode method of the DeviceModel implementation. It +// returns a response with a boolean indicating if the operation was +// successful and an error if the operation failed. func (m *DeviceModelServer) DeleteNode( ctx context.Context, req *pb.DeleteNodeRequest) (*pb.DeleteNodeResponse, error) { @@ -47,6 +61,8 @@ func (m *DeviceModelServer) DeleteNode( return &pb.DeleteNodeResponse{Valid: true}, err } +// Model calls the Model method of the DeviceModel implementation. It returns a +// response with the model as json and an error if the operation failed. func (m *DeviceModelServer) Model( ctx context.Context, req *pb.ModelRequest) (*pb.ModelResponse, error) { @@ -54,6 +70,9 @@ func (m *DeviceModelServer) Model( return &pb.ModelResponse{Json: model}, err } +// Diff calls the Diff method of the DeviceModel implementation. It returns a +// response with gnmi notifications containing the found Diffs and an error if +// the operation failed. func (m *DeviceModelServer) Diff( ctx context.Context, req *pb.DiffRequest) (*pb.DiffResponse, error) { @@ -61,6 +80,9 @@ func (m *DeviceModelServer) Diff( return &pb.DiffResponse{Notification: notification}, err } +// ValidateChange calls the ValidateChange method of the DeviceModel. It +// returns a response with the validated model as json and an error if the +// operation failed. func (m *DeviceModelServer) ValidateChange( ctx context.Context, req *pb.ValidateChangeRequest) (*pb.ValidateChangeResponse, error) { @@ -68,6 +90,9 @@ func (m *DeviceModelServer) ValidateChange( return &pb.ValidateChangeResponse{Model: model}, err } +// PruneConfigFalse calls the PruneConfigFalse method of the DeviceModel. It +// returns a response with the pruned model as json and an error if the +// operation failed. func (m *DeviceModelServer) PruneConfigFalse( ctx context.Context, req *pb.PruneConfigFalseRequest) (*pb.PruneConfigFalseResponse, error) { @@ -75,6 +100,8 @@ func (m *DeviceModelServer) PruneConfigFalse( return &pb.PruneConfigFalseResponse{Model: model}, err } +// SchemaTree calls the SchemaTree method of the DeviceModel. +// The SchemaTree is a byte array that will be sent through a stream. func (m *DeviceModelServer) SchemaTreeGzip( req *pb.SchemaTreeGzipRequest, stream pb.Plugin_SchemaTreeGzipServer) error { diff --git a/docker-compose.yml b/docker-compose.yml index f71ce23af9023e52b768062ecf2026b958d4fa80..82bb6b7502266c9a609592858bc5592656d93af3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,9 @@ services: - 127.0.0.1:15672:15672 plugin-registry: - image: plugin-registry:latest + build: + context: . + dockerfile: plugin-registry/plugin-registry.Dockerfile ports: - 127.0.0.1:55057:55057 diff --git a/plugins/examples/arista/cmd/main.go b/plugins/examples/arista/cmd/main.go index 6db446f5be08080a1788321147e9c6404d4d2581..58046ad7dd7464aa6f6202bf455c594bbc82b1a0 100644 --- a/plugins/examples/arista/cmd/main.go +++ b/plugins/examples/arista/cmd/main.go @@ -11,11 +11,13 @@ import ( ) func main() { + // Create a new DeviceModel. deviceModel, err := sdk.NewDeviceModel(generated.Schema, generated.Unmarshal, generated.SchemaTreeGzip) if err != nil { log.Println(err) os.Exit(1) } + // Serve the DeviceModelPlugin and provide the implemented deviceModel. plugin.Serve(&plugin.ServeConfig{ HandshakeConfig: shared.Handshake, Plugins: map[string]plugin.Plugin{ diff --git a/plugins/examples/openconfig/cmd/main.go b/plugins/examples/openconfig/cmd/main.go index fcdd8b0bc8720a1644a2dc1192da18d29d5d1ae6..a7c3a92fe42c9eafea5ce912dbe37d420c5f9463 100644 --- a/plugins/examples/openconfig/cmd/main.go +++ b/plugins/examples/openconfig/cmd/main.go @@ -11,11 +11,13 @@ import ( ) func main() { + // Create a new DeviceModel. deviceModel, err := sdk.NewDeviceModel(generated.Schema, generated.Unmarshal, generated.SchemaTreeGzip) if err != nil { log.Println(err) os.Exit(1) } + // Serve the DeviceModelPlugin and provide the implemented deviceModel. plugin.Serve(&plugin.ServeConfig{ HandshakeConfig: shared.Handshake, Plugins: map[string]plugin.Plugin{ diff --git a/plugins/sdk/deviceModel.go b/plugins/sdk/deviceModel.go index 3016c4a7aa2f83325b138cec14c4094e3e8c4ce3..8aec7a59d5729b5ce8cf8c94d4b2810b6bcd18b6 100644 --- a/plugins/sdk/deviceModel.go +++ b/plugins/sdk/deviceModel.go @@ -15,6 +15,8 @@ import ( log "github.com/sirupsen/logrus" ) +// Device model satisfies the DeviceModel interface of a plugin defined in +// "code.fbi.h-da.de/danet/gosdn/controller/plugin/shared". type DeviceModel struct { mu sync.RWMutex model ygot.ValidatedGoStruct @@ -23,6 +25,7 @@ type DeviceModel struct { genereatedSchemaTreeGzipFn func() []byte } +// NewDeviceModel creates a new DeviceModel. func NewDeviceModel(generatedSchemaFn func() (*ytypes.Schema, error), generatedUnmarshalFn func([]byte, ygot.GoStruct, ...ytypes.UnmarshalOpt) error, genereatedSchemaTreeGzipFn func() []byte) (*DeviceModel, error) { schema, err := generatedSchemaFn() if err != nil { @@ -228,6 +231,7 @@ func (d *DeviceModel) Diff(original, modified []byte) (*gpb.Notification, error) return ygot.Diff(originalAsValidatedCopy, modifiedAsValidatedCopy, diffOpts...) } +// ValidateChange validates that the given value can be set at the given path. func (d *DeviceModel) ValidateChange(operation mnepb.ApiOperation, path *gpb.Path, value *gpb.TypedValue) ([]byte, error) { d.mu.RLock() modelCopy, err := createValidatedCopy(d.model) @@ -255,6 +259,7 @@ func (d *DeviceModel) ValidateChange(operation mnepb.ApiOperation, path *gpb.Pat return ygot.Marshal7951(modelCopy, getYgotMarshal7951Config(), ygot.JSONIndent("")) } +// PruneConfigFalse removes all config false elements from the given model. func (d *DeviceModel) PruneConfigFalse(value []byte) ([]byte, error) { validatedCopy, err := createValidatedCopy(d.schema.Root) if err != nil { diff --git a/scripts/simple-dev-setup.sh b/scripts/simple-dev-setup.sh index a776153173a64ed20968c65d965da5a723f0a5ce..8f8abeaa23d1016f826bb157a40309e5c8022215 100755 --- a/scripts/simple-dev-setup.sh +++ b/scripts/simple-dev-setup.sh @@ -39,7 +39,7 @@ then echo "Need sudo rights." sudo echo "sudo rights granted" # Start databases, etc. - docker compose up -d + docker compose up --build -d sudo containerlab deploy -t $TOPOLOGY start_gosdn