diff --git a/.gitignore b/.gitignore index f17e66e9afd3d960108202b4bd109159f8d96895..0ff7746cc07e234f4c3325080bf14d2bcc12e169 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ documentation/design/*.pdf restconf/bin/bin test/.terraform.local/ configs/gosdn.toml +debug.test \ No newline at end of file diff --git a/nucleus/southbound.go b/nucleus/southbound.go index e46a932357260cd96f32195024e9f57842341243..ac930032e630cd3f4f5ab920b650d015884393ad 100644 --- a/nucleus/southbound.go +++ b/nucleus/southbound.go @@ -1,9 +1,11 @@ package nucleus import ( - "code.fbi.h-da.de/cocsn/gosdn/nucleus/errors" + "fmt" "reflect" + "code.fbi.h-da.de/cocsn/gosdn/nucleus/errors" + spb "code.fbi.h-da.de/cocsn/api/go/gosdn/southbound" "code.fbi.h-da.de/cocsn/gosdn/interfaces/southbound" @@ -11,6 +13,7 @@ import ( "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/util" "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" log "github.com/sirupsen/logrus" @@ -73,21 +76,19 @@ func (oc *OpenConfig) Unmarshal() func([]byte, []string, ygot.ValidatedGoStruct, // unmarshal parses gNMI response to a go struct. If it's a root level response // it uses -func (oc *OpenConfig)unmarshal(bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { +func (oc *OpenConfig) unmarshal(bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { defer func() { if r := recover(); r != nil { log.Error(r.(error)) } }() - switch l := len(fields); l { - case 0: + if len(fields) == 0 { return openconfig.Unmarshal(bytes, goStruct.(*openconfig.Device), opt...) - default: } var c ygot.ValidatedGoStruct // Load SBI definition - root, err := ygot.DeepCopy(oc.schema.Root) + root, err := ygot.DeepCopy(oc.Schema().Root) if err != nil { return err } @@ -96,7 +97,10 @@ func (oc *OpenConfig)unmarshal(bytes []byte, fields []string, goStruct ygot.Vali return &errors.ErrInvalidTypeAssertion{} } ygot.BuildEmptyTree(d) - c = getField(d, fields) + c, err = getField(d, fields) + if err != nil { + return err + } if err := openconfig.Unmarshal(bytes, c, opt...); err != nil { return err @@ -110,7 +114,7 @@ func (oc *OpenConfig)unmarshal(bytes []byte, fields []string, goStruct ygot.Vali // getField traverses the GoStruct and returns the field that represents the // tail of the path -func getField(inStruct ygot.ValidatedGoStruct, fields []string) ygot.ValidatedGoStruct { +func getField(inStruct ygot.ValidatedGoStruct, fields []string) (ygot.ValidatedGoStruct, error) { defer func() { if r := recover(); r != nil { log.Error(r.(error)) @@ -119,13 +123,18 @@ func getField(inStruct ygot.ValidatedGoStruct, fields []string) ygot.ValidatedGo f := fields[0] s := reflect.ValueOf(inStruct) h := reflect.Indirect(s).FieldByName(f).Interface() - outStruct := h.(ygot.ValidatedGoStruct) + outStruct, ok := h.(ygot.ValidatedGoStruct) + if !ok { + t := reflect.TypeOf(h) + if !(util.IsTypeStruct(t) || util.IsTypeStructPtr(t)) { + return nil, fmt.Errorf("cannot process entry of type %v, request longer or shorter path", t) + } + return nil, fmt.Errorf("expected ValidatedGoStruct got %v", t) + } if len(fields) > 1 { - outStruct = getField(outStruct, fields[1:]) - } else { - return outStruct + return getField(outStruct, fields[1:]) } - return outStruct + return outStruct, nil } // ID returns the ID of the OpenConfig SBI diff --git a/nucleus/southbound_test.go b/nucleus/southbound_test.go index 65f9d4b2faa87d3d0c319e8185036042a0eccab4..ba26c157c2191b0fc7c5b6e481a2f56e1e2bbc3f 100644 --- a/nucleus/southbound_test.go +++ b/nucleus/southbound_test.go @@ -4,6 +4,8 @@ import ( "reflect" "testing" + spb "code.fbi.h-da.de/cocsn/api/go/gosdn/southbound" + "code.fbi.h-da.de/cocsn/gosdn/interfaces/transport" "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/path" @@ -11,6 +13,7 @@ import ( "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" ) @@ -105,7 +108,7 @@ func TestOpenConfig_Schema(t *testing.T) { func Test_unmarshal(t *testing.T) { type args struct { path string - goStruct interface{} + goStruct ygot.ValidatedGoStruct opt []ytypes.UnmarshalOpt } tests := []struct { @@ -114,7 +117,7 @@ func Test_unmarshal(t *testing.T) { wantErr bool }{ { - name: "fail", + name: "interfaces-interface w/o opts", args: args{ goStruct: &openconfig.Device{}, path: "../test/proto/resp-interfaces-interface-arista-ceos", @@ -122,36 +125,36 @@ func Test_unmarshal(t *testing.T) { wantErr: true, }, { - name: "root w/opts", + name: "interfaces w/opts", args: args{ - path: "../test/proto/resp-full-node-arista-ceos", + path: "../test/proto/resp-interfaces-arista-ceos", goStruct: &openconfig.Device{}, opt: []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}}, }, wantErr: false, }, { - name: "root w/o opts", + name: "interfaces w/o opts", args: args{ - path: "../test/proto/resp-full-node-arista-ceos", + path: "../test/proto/resp-interfaces-arista-ceos", goStruct: &openconfig.Device{}, opt: nil, }, wantErr: true, }, { - name: "interfaces w/opts", + name: "root w/opts", args: args{ - path: "../test/proto/resp-interfaces-arista-ceos", + path: "../test/proto/resp-full-node-arista-ceos", goStruct: &openconfig.Device{}, opt: []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}}, }, wantErr: false, }, { - name: "interfaces w/o opts", + name: "root w/o opts", args: args{ - path: "../test/proto/resp-interfaces-arista-ceos", + path: "../test/proto/resp-full-node-arista-ceos", goStruct: &openconfig.Device{}, opt: nil, }, @@ -167,8 +170,13 @@ func Test_unmarshal(t *testing.T) { } fields := path.ToStrings(resp.Notification[0].Update[0].Path) bytes := resp.Notification[0].Update[0].Val.GetJsonIetfVal() - if err := unmarshal(bytes, fields, tt.args.goStruct, tt.args.opt...); (err != nil) != tt.wantErr { - t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr) + sbi := NewSBI(spb.Type_OPENCONFIG) + oc := sbi.(*OpenConfig) + if err := oc.unmarshal(bytes, fields, tt.args.goStruct, tt.args.opt...); err != nil { + if !tt.wantErr { + t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr) + } + return } if tt.args.goStruct.(*openconfig.Device).Interfaces == nil && tt.args.opt != nil { t.Errorf("unmarshal() error: field Interfaces must not be nil")