package shared import ( "errors" "io" "golang.org/x/net/context" mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement" pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin" gpb "github.com/openconfig/gnmi/proto/gnmi" "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, Path: path, }) 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, Value: value, }) 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, RequestForIntendedState: requestForIntendedState, }) if err != nil { return nil, err } return resp.GetNodes(), nil } // 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, }) 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, }) if err != nil { return nil, err } return resp.Json, nil } // 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, Modified: modified, }) if err != nil { return nil, err } return resp.GetNotification(), nil } // 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, Path: path, Value: value, }) if err != nil { return nil, err } return resp.GetModel(), nil } // 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, }) if err != nil { return nil, err } 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 { return nil, err } sTreeBytes := []byte{} for { payload, err := schemaTreeGzipClient.Recv() if err != nil { if errors.Is(err, io.EOF) { break } logrus.Error(err) closeErr := schemaTreeGzipClient.CloseSend() if closeErr != nil { return nil, err } return nil, err } sTreeBytes = append(sTreeBytes, payload.Chunk...) } return sTreeBytes, nil }