diff --git a/interfaces/southbound/sbi.go b/interfaces/southbound/sbi.go index eaacad5b544c4330041d25ed865a111db52bcc2f..0024081c35a14b367adab9e077ad716ba3f8db03 100644 --- a/interfaces/southbound/sbi.go +++ b/interfaces/southbound/sbi.go @@ -23,5 +23,5 @@ type SouthboundInterface interface { // nolint Schema() *ytypes.Schema ID() uuid.UUID Type() spb.Type - Unmarshal([]byte, []string, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error + Unmarshal([]byte, *gpb.Path, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error } diff --git a/mocks/Change.go b/mocks/Change.go index a77d75992fcb55bdce1a440a4a80513fa33dc2da..d9f014299831206d664acce3714019c61d0d3f32 100644 --- a/mocks/Change.go +++ b/mocks/Change.go @@ -1,4 +1,4 @@ -// Code generated by mockery 2.7.5. DO NOT EDIT. +// Code generated by mockery v2.9.4. DO NOT EDIT. package mocks diff --git a/mocks/Device.go b/mocks/Device.go index 9893daacab14ba16072a3355c3b1187f7d382222..ce8fd9c174c8f01da839e8e4a86491ef6f044243 100644 --- a/mocks/Device.go +++ b/mocks/Device.go @@ -1,4 +1,4 @@ -// Code generated by mockery 2.7.5. DO NOT EDIT. +// Code generated by mockery v2.9.4. DO NOT EDIT. package mocks diff --git a/mocks/SouthboundInterface.go b/mocks/SouthboundInterface.go index 0a530bcee437e67e47a5dbcff7d84e0e5f43acfa..22dd94e9ef59d87afb370f2a09f2fa4642b53de9 100644 --- a/mocks/SouthboundInterface.go +++ b/mocks/SouthboundInterface.go @@ -1,4 +1,4 @@ -// Code generated by mockery 2.7.5. DO NOT EDIT. +// Code generated by mockery v2.9.4. DO NOT EDIT. package mocks @@ -104,7 +104,7 @@ func (_m *SouthboundInterface) Type() gosdnsouthbound.Type { } // Unmarshal provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *SouthboundInterface) Unmarshal(_a0 []byte, _a1 []string, _a2 ygot.ValidatedGoStruct, _a3 ...ytypes.UnmarshalOpt) error { +func (_m *SouthboundInterface) Unmarshal(_a0 []byte, _a1 *gnmi.Path, _a2 ygot.ValidatedGoStruct, _a3 ...ytypes.UnmarshalOpt) error { _va := make([]interface{}, len(_a3)) for _i := range _a3 { _va[_i] = _a3[_i] @@ -115,7 +115,7 @@ func (_m *SouthboundInterface) Unmarshal(_a0 []byte, _a1 []string, _a2 ygot.Vali ret := _m.Called(_ca...) var r0 error - if rf, ok := ret.Get(0).(func([]byte, []string, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error); ok { + if rf, ok := ret.Get(0).(func([]byte, *gnmi.Path, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error); ok { r0 = rf(_a0, _a1, _a2, _a3...) } else { r0 = ret.Error(0) diff --git a/mocks/Storable.go b/mocks/Storable.go index 5c986ae2befaa84c3069e4e07b05cb209ecdf9f5..e00e47c4b8f687d671e714deb3e22088b1ddc47c 100644 --- a/mocks/Storable.go +++ b/mocks/Storable.go @@ -1,4 +1,4 @@ -// Code generated by mockery 2.7.5. DO NOT EDIT. +// Code generated by mockery v2.9.4. DO NOT EDIT. package mocks diff --git a/mocks/Store.go b/mocks/Store.go index dcfd7d4675de24ac5cb033d906ba93b7c331a9be..308c2dd2222ff698b6ebbc8fc56301ec51364b6a 100644 --- a/mocks/Store.go +++ b/mocks/Store.go @@ -1,4 +1,4 @@ -// Code generated by mockery 2.7.5. DO NOT EDIT. +// Code generated by mockery v2.9.4. DO NOT EDIT. package mocks diff --git a/mocks/Transport.go b/mocks/Transport.go index 06d9676057391d1ad89d18ded641bc6182aca2bc..85653a57a49abf727461ca4885df9e9f0c9ea9f6 100644 --- a/mocks/Transport.go +++ b/mocks/Transport.go @@ -1,4 +1,4 @@ -// Code generated by mockery 2.7.5. DO NOT EDIT. +// Code generated by mockery v2.9.4. DO NOT EDIT. package mocks diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go index 3619696c839c5821b4e5ebed9ed2fe5afea2d5f4..8b56dc80e65254f9e95d7ab555ffb6b0a1f1b4cc 100644 --- a/nucleus/gnmi_transport.go +++ b/nucleus/gnmi_transport.go @@ -14,7 +14,6 @@ import ( "code.fbi.h-da.de/danet/forks/goarista/gnmi" "code.fbi.h-da.de/danet/gosdn/nucleus/errors" "code.fbi.h-da.de/danet/gosdn/nucleus/types" - pathutils "code.fbi.h-da.de/danet/gosdn/nucleus/util/path" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/ygot" @@ -29,7 +28,7 @@ import ( 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, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error + Unmarshal func([]byte, *gpb.Path, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error Options *tpb.TransportOption client gpb.GNMIClient config *gnmi.Config @@ -130,7 +129,6 @@ func (g *Gnmi) Type() string { // root struct. It logs all errors and returns an error containing the number // off errors encountered during the process. func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Schema) error { - models := s.SchemaTree d, ok := root.(ygot.ValidatedGoStruct) if !ok { return &errors.ErrInvalidTypeAssertion{} @@ -147,16 +145,16 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch switch val := update.Val.Value.(type) { case *gpb.TypedValue_JsonVal: opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}} - if err := g.Unmarshal(val.JsonVal, pathutils.ToStrings(path), d, opts...); err != nil { + if err := g.Unmarshal(val.JsonVal, path, d, opts...); err != nil { errs = append(errs, err) } case *gpb.TypedValue_JsonIetfVal: opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}} - if err := g.Unmarshal(val.JsonIetfVal, pathutils.ToStrings(path), d, opts...); err != nil { + if err := g.Unmarshal(val.JsonIetfVal, path, d, opts...); err != nil { errs = append(errs, err) } default: - schema := models["Device"] + schema := s.RootSchema() opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}} if err := g.SetNode(schema, root, update.Path, update.Val, opts...); err != nil { errs = append(errs, err) diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go index 578826b564552ceb39e7dd4f59baf47ffa9c7939..376f0363209eb0b9856b67511cefc0bf7e5ad18a 100644 --- a/nucleus/gnmi_transport_test.go +++ b/nucleus/gnmi_transport_test.go @@ -234,7 +234,7 @@ func TestGnmi_ProcessResponse(t *testing.T) { path: "../test/proto/resp-interfaces-interface-arista-ceos", root: &openconfig.Device{}, }, - wantErr: true, + wantErr: false, }, { name: "Interfaces Wildcard", diff --git a/nucleus/southbound.go b/nucleus/southbound.go index a49f1c3d99196acefdd17c131b1cbf69b587a737..7271eb8d408bb60bd6eaf62465be379ecf221b01 100644 --- a/nucleus/southbound.go +++ b/nucleus/southbound.go @@ -1,9 +1,6 @@ package nucleus import ( - "fmt" - "reflect" - "code.fbi.h-da.de/danet/gosdn/nucleus/errors" spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound" @@ -13,7 +10,6 @@ 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" @@ -76,13 +72,12 @@ func (oc *OpenConfig) SetNode(schema *yang.Entry, root interface{}, path *gpb.Pa // Unmarshal injects OpenConfig specific model representation to the transport. // Needed for type assertion. -func (oc *OpenConfig) Unmarshal(bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { - return unmarshal(oc.Schema(), bytes, fields, goStruct, opt...) +func (oc *OpenConfig) Unmarshal(bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { + return unmarshal(oc.Schema(), bytes, path, goStruct, opt...) } -// unmarshal parses gNMI response to a go struct. If it's a root level response -// it uses -func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { +//unmarshal parses a gNMI response to a go struct. +func unmarshal(schema *ytypes.Schema, bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { defer func() { if r := recover(); r != nil { log.Error(r.(error)) @@ -98,53 +93,26 @@ func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct yg if !ok { return &errors.ErrInvalidTypeAssertion{} } - ygot.BuildEmptyTree(validatedDeepCopy) - var fieldStruct ygot.ValidatedGoStruct - if len(fields) != 0 { - fieldStruct, err = getField(validatedDeepCopy, fields) - if err != nil { - return err - } - } else { - fieldStruct = validatedDeepCopy + // returns the node we want to fill with the data contained in 'bytes', + // using the specified 'path'. + createdNode, _, err := ytypes.GetOrCreateNode(schema.RootSchema(), validatedDeepCopy, path) + if err != nil { + return err + } + validatedCreatedNode, ok := createdNode.(ygot.ValidatedGoStruct) + if !ok { + return &errors.ErrInvalidTypeAssertion{} } - if err := openconfig.Unmarshal(bytes, fieldStruct, opt...); err != nil { + if err := openconfig.Unmarshal(bytes, validatedCreatedNode, opt...); err != nil { return err } - ygot.PruneEmptyBranches(validatedDeepCopy) opts := []ygot.MergeOpt{&ygot.MergeOverwriteExistingFields{}} - return ygot.MergeStructInto(goStruct, validatedDeepCopy, opts...) } -// getField traverses the GoStruct and returns the field that represents the -// tail of the path -func getField(inStruct ygot.ValidatedGoStruct, fields []string) (ygot.ValidatedGoStruct, error) { - defer func() { - if r := recover(); r != nil { - log.Error(r.(error)) - } - }() - f := fields[0] - s := reflect.ValueOf(inStruct) - h := reflect.Indirect(s).FieldByName(f).Interface() - 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 { - return getField(outStruct, fields[1:]) - } - return outStruct, nil -} - // ID returns the ID of the OpenConfig SBI func (oc *OpenConfig) ID() uuid.UUID { return oc.id @@ -174,9 +142,9 @@ func (csbi *Csbi) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, // Unmarshal injects schema specific model representation to the transport. // Needed for type assertion. -func (csbi *Csbi) Unmarshal(bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { +func (csbi *Csbi) Unmarshal(bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { oc := OpenConfig{} - return unmarshal(oc.Schema(), bytes, fields, goStruct, opt...) + return unmarshal(oc.Schema(), bytes, path, goStruct, opt...) } // Schema is holding the default OpenConfig schema for minimal compatibility diff --git a/nucleus/southbound_test.go b/nucleus/southbound_test.go index 1994cf71d0335501025b28ba0d9eb2fc85458485..b13f3bb30e1cf672a95f01ceac177978c12d01b1 100644 --- a/nucleus/southbound_test.go +++ b/nucleus/southbound_test.go @@ -6,7 +6,6 @@ import ( spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound" - "code.fbi.h-da.de/danet/gosdn/nucleus/util/path" "code.fbi.h-da.de/danet/gosdn/nucleus/util/proto" "code.fbi.h-da.de/danet/yang-models/generated/openconfig" "github.com/google/uuid" @@ -165,10 +164,9 @@ func Test_unmarshal(t *testing.T) { if err != nil { t.Error(err) } - fields := path.ToStrings(resp.Notification[0].Update[0].Path) bytes := resp.Notification[0].Update[0].Val.GetJsonIetfVal() oc := NewSBI(spb.Type_OPENCONFIG) - if err := unmarshal(oc.Schema(), bytes, fields, tt.args.goStruct, tt.args.opt...); err != nil { + if err := unmarshal(oc.Schema(), bytes, resp.Notification[0].Update[0].Path, tt.args.goStruct, tt.args.opt...); err != nil { if !tt.wantErr { t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr) }