Skip to content
Snippets Groups Projects

Commit-Confirm Mechanic for PND

Merged Ghost User requested to merge 99-commit-confirm-mechanic-for-ond-changes into develop
3 files
+ 16
2
Compare changes
  • Side-by-side
  • Inline
Files
3
+ 99
17
@@ -2,19 +2,21 @@ package nucleus
import (
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
pathutils "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/path"
"context"
gpb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/gnmi/proto/gnmi_ext"
"github.com/openconfig/goyang/pkg/yang"
"github.com/openconfig/ygot/ygot"
"github.com/openconfig/ygot/ytypes"
log "github.com/sirupsen/logrus"
"reflect"
"strings"
)
// CtxKeyType is a custom type to be used as key in a context.WithValue() or
// context.Value() call. For more information see:
// https://www.calhoun.io/pitfalls-of-context-values-and-how-to-avoid-or-mitigate-them/
// TODO: Unexport to comply with best practice
type CtxKeyType string
const (
@@ -22,8 +24,16 @@ const (
CtxKeyOpts CtxKeyType = "opts"
// CtxKeyConfig is a context key for gnmi.Config
CtxKeyConfig = "config"
// CtxKeyOperation is a context key for a gNMI operation (update, replace, delete)
CtxKeyOperation = "op"
)
var opmap = map[Operation]string{
TransportUpdate: "update",
TransportReplace: "replace",
TransportDelete: "delete",
}
// Gnmi implements the Transport interface and provides an SBI with the
// possibility to access a gNMI endpoint.
type Gnmi struct {
@@ -75,22 +85,55 @@ func (g *Gnmi) Get(ctx context.Context, params ...string) (interface{}, error) {
// Set takes a slice of params. This slice must contain at least one operation.
// It can contain an additional arbitrary amount of operations and extensions.
func (g *Gnmi) Set(ctx context.Context, args ...interface{}) (interface{}, error) {
func (g *Gnmi) Set(ctx context.Context, args ...interface{}) error {
if g.client == nil {
return nil, &ErrNilClient{}
return &ErrNilClient{}
}
if len(args) == 0 {
return nil, &ErrInvalidParameters{
return &ErrInvalidParameters{
f: "gnmi.Set()",
r: "no parameters provided",
}
}
if len(args) == 2 {
switch args[0].(type) {
case ygot.GoStruct:
return g.applyDiff(ctx, args[0])
default:
}
}
opts := make([]interface{}, 0)
for _, o := range args {
attrs, ok := o.([]string)
if !ok {
return &ErrInvalidTypeAssertion{
v: o,
t: reflect.TypeOf("placeholder"),
}
} else if attrs == nil || len(attrs) == 0 {
return &ErrInvalidParameters{
f: "gnmi.Set()",
r: "no parameters provided",
}
}
opts = append(opts, &gnmi.Operation{
// Hardcoded TransportUpdate until multiple operations are supported
Type: opmap[TransportUpdate],
Origin: "",
Target: "",
Path: gnmi.SplitPath(attrs[0]),
Val: attrs[1],
})
}
// Loop over args and create ops and exts
// Invalid args cause unhealable error
ops := make([]*gnmi.Operation, 0)
exts := make([]*gnmi_ext.Extension, 0)
for _, p := range args {
for _, p := range opts {
switch p.(type) {
case *gnmi.Operation:
op := p.(*gnmi.Operation)
@@ -101,19 +144,66 @@ func (g *Gnmi) Set(ctx context.Context, args ...interface{}) (interface{}, error
case *gnmi_ext.Extension:
exts = append(exts, p.(*gnmi_ext.Extension))
default:
return nil, &ErrInvalidParameters{
return &ErrInvalidParameters{
f: "gnmi.Set()",
r: "args contain invalid type",
}
}
}
if len(ops) == 0 {
return nil, &ErrInvalidParameters{
return &ErrInvalidParameters{
f: "gnmi.Set()",
r: "no operations provided",
}
}
return g.set(ctx, ops, exts...)
resp, err := g.set(ctx, ops, exts...)
if err != nil {
return err
}
log.Info(resp)
return nil
}
func (g *Gnmi) applyDiff(ctx context.Context, payload ...interface{}) error {
if len(payload) != 2 {
return &ErrInvalidParameters{}
}
op := ctx.Value(CtxKeyOperation)
oldstate, ok := payload[0].(ygot.GoStruct)
if !ok {
return &ErrInvalidTypeAssertion{
v: payload[0],
t: reflect.TypeOf("ygot.GoStruct"),
}
}
newstate, ok := payload[1].(ygot.GoStruct)
if !ok {
return &ErrInvalidTypeAssertion{
v: payload[1],
t: reflect.TypeOf("ygot.GoStruct"),
}
}
diff, err := ygot.Diff(oldstate, newstate)
if err != nil {
return err
}
req := &gpb.SetRequest{}
if diff.Update != nil {
switch op {
case TransportUpdate:
req.Update = diff.Update
case TransportReplace:
req.Replace = diff.Update
default:
return &ErrOperationNotSupported{}
}
} else if diff.Delete != nil {
req.Delete = diff.Delete
}
resp, err := g.client.Set(ctx, req)
log.Info(resp)
return err
}
//Subscribe subscribes to a gNMI target
@@ -141,7 +231,7 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch
val, ok := update.Val.Value.(*gpb.TypedValue_JsonIetfVal)
if ok {
opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}}
if err := g.Unmarshal(val.JsonIetfVal, extraxtPathElements(fullPath), root, opts...); err != nil {
if err := g.Unmarshal(val.JsonIetfVal, pathutils.ToStrings(fullPath), root, opts...); err != nil {
return err
}
return nil
@@ -157,14 +247,6 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch
return nil
}
func extraxtPathElements(path *gpb.Path) []string {
elems := make([]string, len(path.Elem))
for i, e := range path.Elem {
elems[i] = strings.Title(e.Name)
}
return elems
}
// Capabilities calls GNMI capabilities
func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) {
log.WithFields(log.Fields{
Loading