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

Merge branch 'develop' into...

Merge branch 'develop' into 156-a-setrequest-to-change-a-specific-path-of-an-ond-only-works-for-paths-with-string-values
parents 393dcdbb ce85c068
No related branches found
No related tags found
1 merge request!225Draft: Resolve "A SetRequest to change a specific path of an OND only works for paths with string values"
This commit is part of merge request !225. Comments created here will be created in the context of that merge request.
...@@ -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
......
...@@ -63,6 +63,7 @@ func Test_core_Get(t *testing.T) { ...@@ -63,6 +63,7 @@ func Test_core_Get(t *testing.T) {
name string name string
args args args args
want []string want []string
length int
wantErr bool wantErr bool
}{ }{
{ {
...@@ -70,15 +71,28 @@ func Test_core_Get(t *testing.T) { ...@@ -70,15 +71,28 @@ func Test_core_Get(t *testing.T) {
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
request: &pb.GetRequest{ request: &pb.GetRequest{
All: true, Pid: []string{
pndID,
},
}, },
}, },
length: 1,
want: []string{ want: []string{
pndID, pndID,
"test", "test",
"test", "test",
}, },
}, },
{
name: "getAll",
args: args{
ctx: context.Background(),
request: &pb.GetRequest{
All: true,
},
},
length: 2,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
...@@ -91,13 +105,19 @@ func Test_core_Get(t *testing.T) { ...@@ -91,13 +105,19 @@ func Test_core_Get(t *testing.T) {
return return
} }
got := []string{ if tt.name == "default" {
resp.Pnd[0].Id, got := []string{
resp.Pnd[0].Name, resp.Pnd[0].Id,
resp.Pnd[0].Description, resp.Pnd[0].Name,
resp.Pnd[0].Description,
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("core.Get() = %v, want %v", got, tt.want)
}
} }
if !reflect.DeepEqual(got, tt.want) { length := len(resp.Pnd)
t.Errorf("core.Get() = %v, want %v", got, tt.want) if tt.length != length {
t.Errorf("core.Get() = %v, want %v", length, tt.length)
} }
}) })
} }
......
...@@ -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