Skip to content
Snippets Groups Projects
Commit f7ee571e authored by Manuel Kieweg's avatar Manuel Kieweg
Browse files

moved transport options to proto

parent 8af07150
No related branches found
No related tags found
1 merge request!155Northbound Interface
Pipeline #72421 failed
This commit is part of merge request !155. Comments created here will be created in the context of that merge request.
......@@ -47,11 +47,11 @@ if they diverge from the default credentials.`,
RunE: func(cmd *cobra.Command, args []string) error {
return cli.AddDevice(
apiEndpoint,
"username="+username,
"password="+password,
"pnd="+cliPnd,
"sbi="+cliSbi,
"address="+address,
username,
password,
cliPnd,
cliSbi,
address,
)
},
}
......
......@@ -33,6 +33,7 @@ package cmd
import (
"code.fbi.h-da.de/cocsn/gosdn/cli"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
......@@ -46,11 +47,16 @@ the controller.
The request path is passed as positional argument.`,
RunE: func(cmd *cobra.Command, args []string) error {
return cli.GetDevice(
resp, err := cli.GetDevice(
apiEndpoint,
cliPnd,
args[0],
)
if err != nil {
return err
}
log.Info(resp)
return nil
},
}
......
module code.fbi.h-da.de/cocsn/gosdn
go 1.14
go 1.16
replace (
code.fbi.h-da.de/cocsn/api/go v0.0.0-20210526102447-811b12caa119 => ../api/go
)
require (
code.fbi.h-da.de/cocsn/api/go v0.0.0-20210526102447-811b12caa119
......
package nucleus
import (
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
)
......@@ -12,7 +12,7 @@ type Device struct {
// UUID represents the Devices UUID
UUID uuid.UUID
// Device inherits properties of ygot.GoStruct
// Device embeds a ygot.GoStruct containing the device details
ygot.GoStruct
// SBI is the device's southbound interface implementation
......@@ -23,25 +23,17 @@ type Device struct {
}
// NewDevice creates a Device
func NewDevice(sbi SouthboundInterface, opts TransportOptions) (*Device, error) {
var transport Transport
var err error
switch o := opts.(type) {
case *GnmiTransportOptions:
transport, err = NewGnmiTransport(o)
if err != nil {
return nil, err
}
default:
return nil, &errors.ErrInvalidTransportOptions{Opt: o}
func NewDevice(name string, opt *tpb.TransportOption, sbi SouthboundInterface) (*Device, error) {
t, err := NewTransport(opt, sbi)
if err != nil {
return nil, err
}
return &Device{
UUID: uuid.New(),
GoStruct: sbi.Schema().Root,
SBI: sbi,
Transport: transport,
}, nil
Transport: t,
}, err
}
// ID returns the UUID of the Device
......
package nucleus
import (
tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
"reflect"
"testing"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/yang-models/generated/openconfig"
"github.com/google/uuid"
"github.com/openconfig/ygot/ygot"
......@@ -49,7 +49,7 @@ func TestNewDevice(t *testing.T) {
sbi := &OpenConfig{}
type args struct {
sbi SouthboundInterface
opts TransportOptions
opts *tpb.TransportOption
}
tests := []struct {
name string
......@@ -61,11 +61,17 @@ func TestNewDevice(t *testing.T) {
name: "default",
args: args{
sbi: sbi,
opts: &GnmiTransportOptions{
Config: gnmi.Config{
Addr: "test:///",
Username: "test",
Password: "test",
opts: &tpb.TransportOption{
Address: "test:///",
Username: "test",
Password: "test",
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{
Compression: "",
GrpcDialOptions: nil,
Token: "",
Encoding: 0,
},
},
},
},
......@@ -73,30 +79,33 @@ func TestNewDevice(t *testing.T) {
GoStruct: &openconfig.Device{},
SBI: sbi,
UUID: uuid.New(),
Transport: &Gnmi{
Options: &GnmiTransportOptions{
Config: gnmi.Config{
Addr: "test:///",
Username: "test",
Password: "test",
},
},
},
},
{
name: "invalid options",
args: args{
sbi: sbi,
opts: &tpb.TransportOption{
Address: "test:///",
Username: "test",
Password: "test",
},
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewDevice(tt.args.sbi, tt.args.opts)
if err != nil {
t.Error(err)
}
tt.want.Transport.(*Gnmi).client = got.Transport.(*Gnmi).client
tt.want.UUID = got.ID()
got, err := NewDevice("", tt.args.opts, tt.args.sbi)
if (err != nil) != tt.wantErr {
t.Errorf("NewDevice() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != nil {
tt.want.Transport = got.Transport
tt.want.UUID = got.ID()
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewDevice() got = %v, want %v", got, tt.want)
}
......
......@@ -2,6 +2,7 @@ package nucleus
import (
"context"
"google.golang.org/grpc"
"reflect"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
......@@ -14,6 +15,8 @@ import (
"github.com/openconfig/ygot/ygot"
"github.com/openconfig/ygot/ytypes"
log "github.com/sirupsen/logrus"
tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
)
var opmap = map[types.Operation]string{
......@@ -28,38 +31,44 @@ type Gnmi struct {
SetNode func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error
RespChan chan *gpb.SubscribeResponse
Unmarshal func([]byte, []string, interface{}, ...ytypes.UnmarshalOpt) error
Options *GnmiTransportOptions
Options *tpb.TransportOption
client gpb.GNMIClient
config *gnmi.Config
}
// NewGnmiTransport takes a struct of GnmiTransportOptions and returns a Gnmi
// transport based on the values of it.
func NewGnmiTransport(opts *GnmiTransportOptions) (*Gnmi, error) {
c, err := gnmi.Dial(&opts.Config)
func NewGnmiTransport(opts *tpb.TransportOption, sbi SouthboundInterface) (*Gnmi, error) {
gnmiConfig := &gnmi.Config{
Addr: opts.Address,
Password: opts.Password,
Username: opts.Username,
TLS: opts.Tls,
Compression: opts.GetGnmiTransportOption().Compression,
DialOptions: parseDialOptions(opts.GetGnmiTransportOption().GrpcDialOptions),
Token: "",
Encoding: 0,
}
c, err := gnmi.Dial(gnmiConfig)
if err != nil {
return nil, err
}
log.WithFields(log.Fields{
"target": opts.Addr,
"tls": opts.TLS,
"encoding": opts.Encoding,
"target": opts.Address,
"tls": opts.Tls,
"encoding": opts.GetGnmiTransportOption().Encoding,
}).Info("building new gNMI transport")
return &Gnmi{
SetNode: opts.SetNode,
RespChan: opts.RespChan,
SetNode: sbi.SetNode(),
RespChan: make(chan *gpb.SubscribeResponse),
Options: opts,
client: c,
config: gnmiConfig,
}, nil
}
//SetOptions sets Gnmi Options
func (g *Gnmi) SetOptions(to TransportOptions) {
g.Options = to.(*GnmiTransportOptions)
}
//GetOptions returns the Gnmi options
func (g *Gnmi) GetOptions() interface{} {
return g.Options
func parseDialOptions(opts map[string]string) []grpc.DialOption {
return nil
}
// Get takes a slice of gnmi paths, splits them and calls get for each one of them.
......@@ -126,7 +135,7 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
case *gnmi.Operation:
op := p.(*gnmi.Operation)
if op.Target == "" {
op.Target = g.Options.Addr
op.Target = g.Options.Address
}
ops = append(ops, op)
case *gnmi_ext.Extension:
......@@ -238,10 +247,10 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch
// Capabilities calls GNMI capabilities
func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) {
log.WithFields(log.Fields{
"target": g.Options.Addr,
"target": g.Options.Address,
}).Info("sending gNMI capabilities request")
ctx = gnmi.NewContext(ctx, &g.Options.Config)
ctx = context.WithValue(ctx, types.CtxKeyConfig, &g.Options.Config) //nolint
ctx = gnmi.NewContext(ctx, g.config)
ctx = context.WithValue(ctx, types.CtxKeyConfig, g.config) //nolint
resp, err := g.client.Capabilities(ctx, &gpb.CapabilityRequest{})
if err != nil {
return nil, err
......@@ -252,8 +261,8 @@ func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) {
// get calls GNMI get
func (g *Gnmi) get(ctx context.Context, paths [][]string, origin string) (interface{}, error) {
ctx = gnmi.NewContext(ctx, &g.Options.Config)
ctx = context.WithValue(ctx, types.CtxKeyConfig, &g.Options.Config) //nolint
ctx = gnmi.NewContext(ctx, g.config)
ctx = context.WithValue(ctx, types.CtxKeyConfig, g.config) //nolint
req, err := gnmi.NewGetRequest(ctx, paths, origin)
if err != nil {
return nil, err
......@@ -268,7 +277,7 @@ func (g *Gnmi) getWithRequest(ctx context.Context, req *gpb.GetRequest) (interfa
return nil, &errors.ErrNil{}
}
log.WithFields(log.Fields{
"target": g.Options.Addr,
"target": g.Options.Address,
"path": req.Path,
}).Info("sending gNMI get request")
......@@ -282,7 +291,7 @@ func (g *Gnmi) getWithRequest(ctx context.Context, req *gpb.GetRequest) (interfa
// Set calls GNMI set
func (g *Gnmi) set(ctx context.Context, setOps []*gnmi.Operation,
exts ...*gnmi_ext.Extension) (*gpb.SetResponse, error) {
ctx = gnmi.NewContext(ctx, &g.Options.Config)
ctx = gnmi.NewContext(ctx, g.config)
targets := make([]string, len(setOps))
paths := make([][]string, len(setOps))
values := make([]string, len(setOps))
......@@ -301,7 +310,7 @@ func (g *Gnmi) set(ctx context.Context, setOps []*gnmi.Operation,
// Subscribe calls GNMI subscribe
func (g *Gnmi) subscribe(ctx context.Context) error {
ctx = gnmi.NewContext(ctx, &g.Options.Config)
ctx = gnmi.NewContext(ctx, g.config)
opts, ok := ctx.Value(types.CtxKeyOpts).(*gnmi.SubscribeOptions)
if !ok {
return &errors.ErrInvalidTypeAssertion{
......@@ -332,38 +341,3 @@ func (g *Gnmi) subscribe(ctx context.Context) error {
func (g *Gnmi) Close() error {
return nil
}
// GnmiTransportOptions implements the TransportOptions interface.
// GnmiTransportOptions contains all needed information to setup a Gnmi
// transport and therefore inherits gnmi.Config.
type GnmiTransportOptions struct {
// all needed gnmi transport parameters
gnmi.Config
SetNode func(schema *yang.Entry, root interface{}, path *gpb.Path,
val interface{}, opts ...ytypes.SetNodeOpt) error
Unmarshal func([]byte, []string, interface{}, ...ytypes.UnmarshalOpt) error
RespChan chan *gpb.SubscribeResponse
}
// GetAddress returns the address used by the transport to connect to a
// gRPC endpoint.
func (gto *GnmiTransportOptions) GetAddress() string {
return gto.Config.Addr
}
// GetUsername returns the username used by the transport to connect to a
// gRPC endpoint.
func (gto *GnmiTransportOptions) GetUsername() string {
return gto.Config.Username
}
// GetPassword returns the password used by the transport to connect to a
// gRPC endpoint.
func (gto *GnmiTransportOptions) GetPassword() string {
return gto.Config.Password
}
// IsTransportOption is needed to fulfill the requirements of the
// TransportOptions interface. It does not need any further implementation.
func (gto *GnmiTransportOptions) IsTransportOption() {}
package nucleus
import (
tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
"context"
"errors"
"reflect"
......@@ -191,7 +192,7 @@ func TestGnmi_Get(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.runEndpoint {
startGnmiTarget <- tt.fields.transport.Options.Addr
startGnmiTarget <- tt.fields.transport.config.Addr
}
got, err := tt.fields.transport.Get(context.Background(), tt.args.params...)
if (err != nil) != tt.wantErr {
......@@ -431,7 +432,7 @@ func TestGnmi_getWithRequest(t *testing.T) {
func TestNewGnmiTransport(t *testing.T) {
type args struct {
opts *GnmiTransportOptions
opts *tpb.TransportOption
}
tests := []struct {
name string
......@@ -441,21 +442,21 @@ func TestNewGnmiTransport(t *testing.T) {
}{
{
name: "default",
args: args{opts: &GnmiTransportOptions{
Config: gnmi.Config{
Username: "test",
Password: "test",
Addr: "localhost:13371",
Encoding: gpb.Encoding_PROTO,
args: args{opts: &tpb.TransportOption{
Address: "localhost:13371",
Username: "test",
Password: "test",
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{},
},
}},
want: &Gnmi{
Options: &GnmiTransportOptions{
Config: gnmi.Config{
Username: "test",
Password: "test",
Addr: "localhost:13371",
Encoding: gpb.Encoding_PROTO,
Options: &tpb.TransportOption{
Address: "localhost:13371",
Username: "test",
Password: "test",
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{},
},
},
client: nil,
......@@ -463,20 +464,42 @@ func TestNewGnmiTransport(t *testing.T) {
wantErr: false,
},
{
name: "unsupported compression",
args: args{opts: &GnmiTransportOptions{Config: gnmi.Config{Compression: "brotli"}}},
name: "unsupported compression",
args: args{
opts: &tpb.TransportOption{
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{
Compression: "brotli",
},
}}},
want: nil,
wantErr: true,
},
{
name: "certificate error no key file",
args: args{opts: &GnmiTransportOptions{Config: gnmi.Config{TLS: true, CertFile: "invalid", KeyFile: ""}}},
name: "certificate error no key file",
args: args{
opts: &tpb.TransportOption{
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{
GrpcDialOptions: map[string]string{
"cert-file":"invalid",
},
},
}}},
want: nil,
wantErr: true,
},
{
name: "certificate error no ca file",
args: args{opts: &GnmiTransportOptions{Config: gnmi.Config{TLS: true, CAFile: "invalid"}}},
args: args{
opts: &tpb.TransportOption{
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{
GrpcDialOptions: map[string]string{
"ca-file":"invalid",
},
},
}}},
want: nil,
wantErr: true,
},
......@@ -486,7 +509,7 @@ func TestNewGnmiTransport(t *testing.T) {
if tt.name == "default" {
startGnmiTarget <- gnmiConfig.Addr
}
got, err := NewGnmiTransport(tt.args.opts)
got, err := NewGnmiTransport(tt.args.opts, &OpenConfig{})
if (err != nil) != tt.wantErr {
t.Errorf("NewGnmiTransport() error = %v, wantErr %v", err, tt.wantErr)
return
......
package nucleus
import (
tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
"context"
"os"
"testing"
......@@ -86,16 +87,14 @@ func mockTransport() Gnmi {
}
}
func newGnmiTransportOptions() *GnmiTransportOptions {
return &GnmiTransportOptions{
Config: gnmi.Config{
Username: "test",
Password: "test",
Addr: "localhost:13371",
Encoding: gpb.Encoding_PROTO,
func newGnmiTransportOptions() *tpb.TransportOption {
return &tpb.TransportOption{
Address: "localhost:13371",
Username: "test",
Password: "test",
TransportOption: &tpb.TransportOption_GnmiTransportOption{
GnmiTransportOption: &tpb.GnmiTransportOption{},
},
SetNode: nil,
RespChan: make(chan *gpb.SubscribeResponse),
}
}
......
package nucleus
import (
"context"
tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/types"
"context"
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"github.com/openconfig/ygot/ygot"
......@@ -22,7 +22,7 @@ type PrincipalNetworkDomain interface {
Destroy() error
AddSbi(interface{}) error
RemoveSbi(uuid.UUID) error
AddDevice(interface{}) error
AddDevice(name string, opts *tpb.TransportOption, sid uuid.UUID) error
GetDevice(uuid uuid.UUID) (ygot.GoStruct, error)
RemoveDevice(uuid.UUID) error
Devices() []uuid.UUID
......@@ -35,8 +35,8 @@ type PrincipalNetworkDomain interface {
ContainsDevice(uuid.UUID) bool
GetSBIs() interface{}
ID() uuid.UUID
Pending() []uuid.UUID
Committed() []uuid.UUID
PendingChanges() []uuid.UUID
CommittedChanges() []uuid.UUID
Commit(uuid.UUID) error
Confirm(uuid.UUID) error
}
......@@ -72,11 +72,11 @@ type pndImplementation struct {
errChans map[uuid.UUID]chan error
}
func (pnd *pndImplementation) Pending() []uuid.UUID {
func (pnd *pndImplementation) PendingChanges() []uuid.UUID {
return pnd.pendingChanges.UUIDs()
}
func (pnd *pndImplementation) Committed() []uuid.UUID {
func (pnd *pndImplementation) CommittedChanges() []uuid.UUID {
return pnd.committedChanges.UUIDs()
}
......@@ -173,13 +173,15 @@ func (pnd *pndImplementation) RemoveSbi(id uuid.UUID) error {
}
//AddDevice adds a new device to the PND
func (pnd *pndImplementation) AddDevice(device interface{}) error {
d, ok := device.(*Device)
if !ok {
return &errors.ErrInvalidTypeAssertion{
Value: device,
Type: "Device",
}
func (pnd *pndImplementation) AddDevice(name string, opt *tpb.TransportOption, sid uuid.UUID) error {
sbi, err := pnd.sbic.Get(sid)
if err != nil {
return err
}
d, err := NewDevice(name, opt, sbi)
if err != nil {
return err
}
return pnd.addDevice(d)
}
......
......@@ -114,7 +114,7 @@ func Test_pndImplementation_AddDevice(t *testing.T) {
if tt.name == "already exists" {
pnd.devices.store[did] = &Device{UUID: did}
}
err := pnd.AddDevice(tt.args.device)
err := pnd.AddDevice("", nil, defaultSbiID)
if (err != nil) != tt.wantErr {
t.Errorf("AddDevice() error = %v, wantErr %v", err, tt.wantErr)
}
......@@ -640,8 +640,7 @@ func Test_pndImplementation_ChangeOND(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := newPnd()
d := mockDevice()
if err := p.AddDevice(&d); err != nil {
if err := p.AddDevice("", nil, defaultSbiID); err != nil {
t.Error(err)
return
}
......@@ -661,7 +660,7 @@ func Test_pndImplementation_ChangeOND(t *testing.T) {
func Test_pndImplementation_GetDevice(t *testing.T) {
pnd := newPnd()
sbi := NewSBI(types.Openconfig)
d, err := NewDevice(sbi, &GnmiTransportOptions{})
d, err := NewDevice("", newGnmiTransportOptions(), sbi)
if err != nil {
t.Error(err)
return
......@@ -734,7 +733,7 @@ func Test_pndImplementation_Confirm(t *testing.T) {
t.Error(err)
return
}
u := pnd.Pending()[0]
u := pnd.PendingChanges()[0]
if tt.name != "uncommitted" {
if err := pnd.Commit(u); (err != nil) != tt.wantErr {
t.Errorf("Confirm() error = %v, wantErr %v", err, tt.wantErr)
......
package nucleus
import (
"bytes"
tpb "code.fbi.h-da.de/cocsn/api/go/gosdn/transport"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
"context"
"io"
"github.com/openconfig/ygot/ytypes"
)
......@@ -15,27 +15,17 @@ type Transport interface {
Set(ctx context.Context, params ...interface{}) error
Subscribe(ctx context.Context, params ...string) error
Type() string
GetOptions() interface{}
ProcessResponse(resp interface{}, root interface{}, models *ytypes.Schema) error
}
// YANGConsumer is a auxillary type to redirect the response
// of an RESTCONF call to a ygot YANG unmarshaler
type YANGConsumer struct {
Data *bytes.Buffer
}
// Consume reads the received data into a byte buffer
func (yc YANGConsumer) Consume(reader io.Reader, _ interface{}) error {
_, err := yc.Data.ReadFrom(reader)
return err
}
// NewTransport receives TransportOptions and returns an appropriate Transport
// implementation
func NewTransport(opts *tpb.TransportOption, sbi SouthboundInterface) (Transport, error) {
switch o := opts.TransportOption.(type) {
case *tpb.TransportOption_GnmiTransportOption:
return NewGnmiTransport(opts, sbi)
default:
return nil, &errors.ErrInvalidTransportOptions{Opt: o}
// TransportOptions provides an interface for TransportOptions implementations
// for Transports like RESTCONF or gNMI
type TransportOptions interface {
GetAddress() string
GetUsername() string
GetPassword() string
IsTransportOption()
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment