diff --git a/cmd/gnmi-telemetry/telemetry.go b/cmd/gnmi-telemetry/telemetry.go index 4abcced5f0ae47ae15d090bcf3ac178ed05d487c..3cb85fd508cdc8331d0331c358748a1509a00913 100644 --- a/cmd/gnmi-telemetry/telemetry.go +++ b/cmd/gnmi-telemetry/telemetry.go @@ -16,7 +16,7 @@ import ( func main() { log.SetLevel(log.DebugLevel) - sbi := &nucleus.AristaOC{} + sbi := &nucleus.Arista{} device := nucleus.Device{ GoStruct: sbi.Schema().Root, diff --git a/nucleus/integration_test.go b/nucleus/integration_test.go index def64439c1c6530aad2b7669cdcbfe35efe8d08c..157380b64571cadba41f88e6f952c1d3eacde244 100644 --- a/nucleus/integration_test.go +++ b/nucleus/integration_test.go @@ -2,6 +2,7 @@ package nucleus import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" + "code.fbi.h-da.de/cocsn/gosdn/nucleus/util" "context" gpb "github.com/openconfig/gnmi/proto/gnmi" "testing" @@ -106,25 +107,34 @@ func TestGnmi_CapabilitiesIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } - t.Run("Test GNMI Capabilities", func(t *testing.T){ - cfg := &gnmi.Config{ - Addr: address, - Username: "admin", - Password: "arista", - Encoding: gpb.Encoding_JSON_IETF, + respones := map[string]*gpb.CapabilityResponse{ + "../test/cap-resp-arista-ceos": {}, } - transport,err := NewGnmiTransport(cfg) - if err != nil { - t.Error(err) - } - resp, err := transport.Capabilities(context.Background()) - if err != nil { - t.Error(err) - } - if resp == nil { - t.Error("resp is nil") + for k,v := range respones { + if err := util.Read(k, v); err != nil { + t.Errorf("error parsing %v: %v", k, err) + } + + t.Run("Test GNMI Capabilities", func(t *testing.T) { + cfg := &gnmi.Config{ + Addr: address, + Username: "admin", + Password: "arista", + Encoding: gpb.Encoding_JSON_IETF, + } + transport, err := NewGnmiTransport(cfg) + if err != nil { + t.Error(err) + } + resp, err := transport.Capabilities(context.Background()) + if err != nil { + t.Error(err) + } + if resp != v { + t.Error("resp is nil") + } + }) } - }) } func TestPndImplementation_RequestAllIntegration(t *testing.T) { diff --git a/nucleus/southbound.go b/nucleus/southbound.go index 6d5db29f1deaffa4943bcda114dd6c78f194ab9b..81aeb8afa8ff987366f81652e81ce3031f591776 100644 --- a/nucleus/southbound.go +++ b/nucleus/southbound.go @@ -3,11 +3,13 @@ package nucleus import ( "code.fbi.h-da.de/cocsn/yang-models/generated/arista" "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" - log "github.com/golang/glog" "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" + log "github.com/sirupsen/logrus" + "reflect" ) // SouthboundInterface provides an @@ -47,6 +49,7 @@ func (oc *OpenConfig) SbiIdentifier() string { func (oc *OpenConfig) Schema() *ytypes.Schema { schema, err := openconfig.Schema() + oc.schema = schema if err != nil { log.Fatal(err) } @@ -65,6 +68,34 @@ func (oc *OpenConfig) SetNode() func(schema *yang.Entry, root interface{}, path } } +// Unmarshal injects OpenConfig specific model +// representation to the transport. +// Needed for type assertion. +func (oc *OpenConfig) Unmarshal() func([]byte, string, interface{}, ...ytypes.UnmarshalOpt) error { + return func(bytes []byte, path string, goStruct interface{}, opt ...ytypes.UnmarshalOpt) error { + // Load SBI definition + d := openconfig.Device{} + s := reflect.ValueOf(&d).Elem() + h := s.FieldByName("Interfaces") + configStruct := reflect.New(h.Type()) + + // Pointer of field needs to be initialized. + // Very convoluted russian doll trick + // https://stackoverflow.com/a/57469950/4378176 + // https://golang.org/src/encoding/json/decode.go?s#L474 + // TODO: Prettify (mk) + p2 := configStruct.Elem() + configStruct.Elem().Set(reflect.New(p2.Type().Elem())) + c := configStruct.Elem().Interface().(ygot.GoStruct) + + if err := oc.schema.Unmarshal(bytes, c, opt...); err != nil { + return err + } + reflect.ValueOf(goStruct.(*openconfig.Device)).Elem().FieldByName("Interfaces").Set(reflect.ValueOf(c)) + return nil + } +} + func (oc *OpenConfig) Id() uuid.UUID { return oc.id } @@ -72,7 +103,7 @@ func (oc *OpenConfig) Id() uuid.UUID { // deprecated // Use for prototyping only. // Use OpenConfig instead -type AristaOC struct { +type Arista struct { // deprecated transport Transport @@ -80,26 +111,27 @@ type AristaOC struct { id uuid.UUID } -func (oc *AristaOC) Id() uuid.UUID { +func (oc *Arista) Id() uuid.UUID { return oc.id } -func (oc *AristaOC) SbiIdentifier() string { +func (oc *Arista) SbiIdentifier() string { return "arista" } -func (oc *AristaOC) Schema() *ytypes.Schema { +func (oc *Arista) Schema() *ytypes.Schema { schema, err := arista.Schema() if err != nil { log.Fatal(err) } + oc.schema = schema return schema } -// SetNode injects AristaOC specific model +// SetNode injects Arista specific model // representation to the transport. // Needed for type assertion. -func (oc *AristaOC) SetNode() func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error { +func (oc *Arista) SetNode() func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error { return func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error { if err := ytypes.SetNode(schema, root.(*arista.Device), path, val, opts...); err != nil { return err @@ -107,3 +139,17 @@ func (oc *AristaOC) SetNode() func(schema *yang.Entry, root interface{}, path *g return nil } } + +// Unmarshal injects Arista specific model +// representation to the transport. +// Needed for type assertion. +func (oc *Arista) Unmarshal() func([]byte, interface{}, ...ytypes.UnmarshalOpt) error { + return func(bytes []byte, goStruct interface{}, opt ...ytypes.UnmarshalOpt) error { + ifaces := &arista.OpenconfigInterfaces_Interfaces{} + if err := oc.schema.Unmarshal(bytes, ifaces, opt...); err != nil { + return err + } + goStruct.(*arista.Device).Interfaces = ifaces + return nil + } +}