Skip to content
Snippets Groups Projects
Commit b58db67f authored by Malte Bauch's avatar Malte Bauch
Browse files

Resolve "Specific paths cant be requested, because the underlying device goStruct can't be filled."

parent e36a276c
Branches
Tags
6 merge requests!246Develop,!245Develop into Master,!244Master into develop2 into master,!228Merge develop into stmakurz_http_server,!213Resolve "Specific paths cant be requested, because the underlying device goStruct can't be filled.",!138Develop
...@@ -23,5 +23,5 @@ type SouthboundInterface interface { // nolint ...@@ -23,5 +23,5 @@ type SouthboundInterface interface { // nolint
Schema() *ytypes.Schema Schema() *ytypes.Schema
ID() uuid.UUID ID() uuid.UUID
Type() spb.Type Type() spb.Type
Unmarshal([]byte, []string, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error Unmarshal([]byte, *gpb.Path, ygot.ValidatedGoStruct, ...ytypes.UnmarshalOpt) error
} }
// Code generated by mockery 2.7.5. DO NOT EDIT. // Code generated by mockery v2.9.4. DO NOT EDIT.
package mocks package mocks
......
// Code generated by mockery 2.7.5. DO NOT EDIT. // Code generated by mockery v2.9.4. DO NOT EDIT.
package mocks package mocks
......
// Code generated by mockery 2.7.5. DO NOT EDIT. // Code generated by mockery v2.9.4. DO NOT EDIT.
package mocks package mocks
...@@ -104,7 +104,7 @@ func (_m *SouthboundInterface) Type() gosdnsouthbound.Type { ...@@ -104,7 +104,7 @@ func (_m *SouthboundInterface) Type() gosdnsouthbound.Type {
} }
// Unmarshal provides a mock function with given fields: _a0, _a1, _a2, _a3 // 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)) _va := make([]interface{}, len(_a3))
for _i := range _a3 { for _i := range _a3 {
_va[_i] = _a3[_i] _va[_i] = _a3[_i]
...@@ -115,7 +115,7 @@ func (_m *SouthboundInterface) Unmarshal(_a0 []byte, _a1 []string, _a2 ygot.Vali ...@@ -115,7 +115,7 @@ func (_m *SouthboundInterface) Unmarshal(_a0 []byte, _a1 []string, _a2 ygot.Vali
ret := _m.Called(_ca...) ret := _m.Called(_ca...)
var r0 error 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...) r0 = rf(_a0, _a1, _a2, _a3...)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
......
// Code generated by mockery 2.7.5. DO NOT EDIT. // Code generated by mockery v2.9.4. DO NOT EDIT.
package mocks package mocks
......
// Code generated by mockery 2.7.5. DO NOT EDIT. // Code generated by mockery v2.9.4. DO NOT EDIT.
package mocks package mocks
......
// Code generated by mockery 2.7.5. DO NOT EDIT. // Code generated by mockery v2.9.4. DO NOT EDIT.
package mocks package mocks
......
...@@ -14,7 +14,6 @@ import ( ...@@ -14,7 +14,6 @@ import (
"code.fbi.h-da.de/danet/forks/goarista/gnmi" "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/errors"
"code.fbi.h-da.de/danet/gosdn/nucleus/types" "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" gpb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/goyang/pkg/yang"
"github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ygot"
...@@ -29,7 +28,7 @@ import ( ...@@ -29,7 +28,7 @@ import (
type Gnmi struct { type Gnmi struct {
SetNode func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error SetNode func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error
RespChan chan *gpb.SubscribeResponse 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 Options *tpb.TransportOption
client gpb.GNMIClient client gpb.GNMIClient
config *gnmi.Config config *gnmi.Config
...@@ -130,7 +129,6 @@ func (g *Gnmi) Type() string { ...@@ -130,7 +129,6 @@ func (g *Gnmi) Type() string {
// root struct. It logs all errors and returns an error containing the number // root struct. It logs all errors and returns an error containing the number
// off errors encountered during the process. // off errors encountered during the process.
func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Schema) error { func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Schema) error {
models := s.SchemaTree
d, ok := root.(ygot.ValidatedGoStruct) d, ok := root.(ygot.ValidatedGoStruct)
if !ok { if !ok {
return &errors.ErrInvalidTypeAssertion{} return &errors.ErrInvalidTypeAssertion{}
...@@ -147,16 +145,16 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch ...@@ -147,16 +145,16 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch
switch val := update.Val.Value.(type) { switch val := update.Val.Value.(type) {
case *gpb.TypedValue_JsonVal: case *gpb.TypedValue_JsonVal:
opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}} 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) errs = append(errs, err)
} }
case *gpb.TypedValue_JsonIetfVal: case *gpb.TypedValue_JsonIetfVal:
opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}} 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) errs = append(errs, err)
} }
default: default:
schema := models["Device"] schema := s.RootSchema()
opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}} opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}}
if err := g.SetNode(schema, root, update.Path, update.Val, opts...); err != nil { if err := g.SetNode(schema, root, update.Path, update.Val, opts...); err != nil {
errs = append(errs, err) errs = append(errs, err)
......
...@@ -234,7 +234,7 @@ func TestGnmi_ProcessResponse(t *testing.T) { ...@@ -234,7 +234,7 @@ func TestGnmi_ProcessResponse(t *testing.T) {
path: "../test/proto/resp-interfaces-interface-arista-ceos", path: "../test/proto/resp-interfaces-interface-arista-ceos",
root: &openconfig.Device{}, root: &openconfig.Device{},
}, },
wantErr: true, wantErr: false,
}, },
{ {
name: "Interfaces Wildcard", name: "Interfaces Wildcard",
......
package nucleus package nucleus
import ( import (
"fmt"
"reflect"
"code.fbi.h-da.de/danet/gosdn/nucleus/errors" "code.fbi.h-da.de/danet/gosdn/nucleus/errors"
spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound" spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
...@@ -13,7 +10,6 @@ import ( ...@@ -13,7 +10,6 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
gpb "github.com/openconfig/gnmi/proto/gnmi" gpb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/goyang/pkg/yang"
"github.com/openconfig/ygot/util"
"github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ygot"
"github.com/openconfig/ygot/ytypes" "github.com/openconfig/ygot/ytypes"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
...@@ -76,13 +72,12 @@ func (oc *OpenConfig) SetNode(schema *yang.Entry, root interface{}, path *gpb.Pa ...@@ -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. // Unmarshal injects OpenConfig specific model representation to the transport.
// Needed for type assertion. // Needed for type assertion.
func (oc *OpenConfig) Unmarshal(bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error { func (oc *OpenConfig) Unmarshal(bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
return unmarshal(oc.Schema(), bytes, fields, goStruct, opt...) return unmarshal(oc.Schema(), bytes, path, goStruct, opt...)
} }
// unmarshal parses gNMI response to a go struct. If it's a root level response //unmarshal parses a gNMI response to a go struct.
// it uses func unmarshal(schema *ytypes.Schema, bytes []byte, path *gpb.Path, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct ygot.ValidatedGoStruct, opt ...ytypes.UnmarshalOpt) error {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
log.Error(r.(error)) log.Error(r.(error))
...@@ -98,53 +93,26 @@ func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct yg ...@@ -98,53 +93,26 @@ func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct yg
if !ok { if !ok {
return &errors.ErrInvalidTypeAssertion{} return &errors.ErrInvalidTypeAssertion{}
} }
ygot.BuildEmptyTree(validatedDeepCopy)
var fieldStruct ygot.ValidatedGoStruct // returns the node we want to fill with the data contained in 'bytes',
if len(fields) != 0 { // using the specified 'path'.
fieldStruct, err = getField(validatedDeepCopy, fields) createdNode, _, err := ytypes.GetOrCreateNode(schema.RootSchema(), validatedDeepCopy, path)
if err != nil { if err != nil {
return err return err
} }
} else { validatedCreatedNode, ok := createdNode.(ygot.ValidatedGoStruct)
fieldStruct = validatedDeepCopy 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 return err
} }
ygot.PruneEmptyBranches(validatedDeepCopy)
opts := []ygot.MergeOpt{&ygot.MergeOverwriteExistingFields{}} opts := []ygot.MergeOpt{&ygot.MergeOverwriteExistingFields{}}
return ygot.MergeStructInto(goStruct, validatedDeepCopy, opts...) 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 // ID returns the ID of the OpenConfig SBI
func (oc *OpenConfig) ID() uuid.UUID { func (oc *OpenConfig) ID() uuid.UUID {
return oc.id return oc.id
...@@ -174,9 +142,9 @@ func (csbi *Csbi) SetNode(schema *yang.Entry, root interface{}, path *gpb.Path, ...@@ -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. // Unmarshal injects schema specific model representation to the transport.
// Needed for type assertion. // 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{} 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 // Schema is holding the default OpenConfig schema for minimal compatibility
......
...@@ -6,7 +6,6 @@ import ( ...@@ -6,7 +6,6 @@ import (
spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound" 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/gosdn/nucleus/util/proto"
"code.fbi.h-da.de/danet/yang-models/generated/openconfig" "code.fbi.h-da.de/danet/yang-models/generated/openconfig"
"github.com/google/uuid" "github.com/google/uuid"
...@@ -165,10 +164,9 @@ func Test_unmarshal(t *testing.T) { ...@@ -165,10 +164,9 @@ func Test_unmarshal(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
fields := path.ToStrings(resp.Notification[0].Update[0].Path)
bytes := resp.Notification[0].Update[0].Val.GetJsonIetfVal() bytes := resp.Notification[0].Update[0].Val.GetJsonIetfVal()
oc := NewSBI(spb.Type_OPENCONFIG) 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 { if !tt.wantErr {
t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr)
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment