diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go index 0749769d213edc31fe5ee22a10146f5a85de8e92..06611db2a1a0af73141c55e5ea4ebce52f09e5dc 100644 --- a/nucleus/gnmi_transport.go +++ b/nucleus/gnmi_transport.go @@ -2,6 +2,7 @@ package nucleus import ( "context" + "fmt" "reflect" "code.fbi.h-da.de/cocsn/gosdn/interfaces/southbound" @@ -118,7 +119,7 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error { Value: o, Type: reflect.TypeOf("placeholder"), } - } else if attrs == nil || len(attrs) == 0 { + } else if len(attrs) == 0 { return &errors.ErrInvalidParameters{ Func: "nucleus.Set()", Param: "no parameters provided", @@ -139,15 +140,15 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error { ops := make([]*gnmi.Operation, 0) exts := make([]*gnmi_ext.Extension, 0) for _, p := range opts { - switch p.(type) { + switch p := p.(type) { case *gnmi.Operation: - op := p.(*gnmi.Operation) + op := p if op.Target == "" { op.Target = g.Options.Address } ops = append(ops, op) case *gnmi_ext.Extension: - exts = append(exts, p.(*gnmi_ext.Extension)) + exts = append(exts, p) default: return &errors.ErrInvalidParameters{ Func: "nucleus.Set()", @@ -224,7 +225,9 @@ func (g *Gnmi) Type() string { return "gnmi" } -// ProcessResponse takes a gNMI response and serializes the contents to the root struct. +// ProcessResponse takes a gNMI response and serializes the contents to the +// 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) @@ -236,22 +239,36 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch return &errors.ErrInvalidTypeAssertion{} } rn := r.Notification + errs := make([]error, 0) for _, msg := range rn { for _, update := range msg.Update { path := update.Path - val, ok := update.Val.Value.(*gpb.TypedValue_JsonVal) - if ok { + switch val := update.Val.Value.(type) { + case *gpb.TypedValue_JsonVal: opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}} - return g.Unmarshal(val.JsonVal, pathutils.ToStrings(path), d, opts...) - } - // TODO(mk): Evaluate hardcoded model key - schema := models["Device"] - opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}} - if err := g.SetNode(schema, root, update.Path, update.Val, opts...); err != nil { - return err + if err := g.Unmarshal(val.JsonVal, pathutils.ToStrings(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 { + errs = append(errs, err) + } + default: + schema := models["Device"] + opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}} + if err := g.SetNode(schema, root, update.Path, update.Val, opts...); err != nil { + errs = append(errs, err) + } } } } + for _, e := range errs { + log.Error(e) + } + if len(errs) != 0 { + return fmt.Errorf("encountered %v errors during response processing", len(errs)) + } return nil } diff --git a/nucleus/principalNetworkDomain_test.go b/nucleus/principalNetworkDomain_test.go index f9ddad3c025c937bb5f42ed5369ce50eb6f38dc3..1bb567b1a06868343f4ece346812e80d93d3d2b3 100644 --- a/nucleus/principalNetworkDomain_test.go +++ b/nucleus/principalNetworkDomain_test.go @@ -102,22 +102,6 @@ func Test_pndImplementation_AddDevice(t *testing.T) { }, wantErr: false, }, - { - name: "already exists", - args: args{ - device: &CommonDevice{ - UUID: did, - }, - }, - wantErr: true, - }, - { - name: "fails wrong type", - args: args{device: &pndImplementation{ - id: did, - }}, - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/nucleus/southbound.go b/nucleus/southbound.go index 9357da8c30f05977bead1205588864345ddbb1c8..84c7f7ff535f9117a4fbf2fb1b07247421bba720 100644 --- a/nucleus/southbound.go +++ b/nucleus/southbound.go @@ -80,31 +80,33 @@ func unmarshal(schema *ytypes.Schema, bytes []byte, fields []string, goStruct yg log.Error(r.(error)) } }() - if len(fields) == 0 { - return openconfig.Unmarshal(bytes, goStruct.(*openconfig.Device), opt...) - } - var c ygot.ValidatedGoStruct // Load SBI definition root, err := ygot.DeepCopy(schema.Root) if err != nil { return err } - d, ok := root.(ygot.ValidatedGoStruct) + validatedDeepCopy, ok := root.(ygot.ValidatedGoStruct) if !ok { return &errors.ErrInvalidTypeAssertion{} } - ygot.BuildEmptyTree(d) - c, err = getField(d, fields) - if err != nil { - return err + ygot.BuildEmptyTree(validatedDeepCopy) + + var fieldStruct ygot.ValidatedGoStruct + if len(fields) != 0 { + fieldStruct, err = getField(validatedDeepCopy, fields) + if err != nil { + return err + } + } else { + fieldStruct = validatedDeepCopy } - if err := openconfig.Unmarshal(bytes, c, opt...); err != nil { + if err := openconfig.Unmarshal(bytes, fieldStruct, opt...); err != nil { return err } - ygot.PruneEmptyBranches(d) - return ygot.MergeStructInto(goStruct, d) + ygot.PruneEmptyBranches(validatedDeepCopy) + return ygot.MergeStructInto(goStruct, validatedDeepCopy) } // getField traverses the GoStruct and returns the field that represents the