Skip to content
Snippets Groups Projects
Commit f6a5e620 authored by Manuel Kieweg's avatar Manuel Kieweg
Browse files

Merge branch '77-job-failed-240443' into 'develop'

Resolve "Job Failed #240443"

See merge request cocsn/gosdn!106
parents b3aa9bd5 445408d7
No related branches found
No related tags found
2 merge requests!106Resolve "Job Failed #240443",!90Develop
Pipeline #66537 failed
Showing
with 8011 additions and 67 deletions
package main
import (
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus"
"context"
"fmt"
gpb "github.com/openconfig/gnmi/proto/gnmi"
log "github.com/sirupsen/logrus"
"strings"
)
func main() {
cfg := &gnmi.Config{
Addr: "[2003:e6:1722:fed0:0:242:ac11:5]:6030",
Username: "admin",
Password: "arista",
Encoding: gpb.Encoding_JSON_IETF,
}
transport,err := nucleus.NewGnmiTransport(cfg)
if err != nil {
log.Error(err)
}
resp, err := transport.Capabilities(context.Background())
if err != nil {
log.Error(err)
}
modelData := resp.(*gpb.CapabilityResponse).SupportedModels
b := strings.Builder{}
for _,elem := range modelData {
_, err := b.WriteString(elem.Name)
if err != nil {
log.Error(err)
}
_, err = b.WriteString("\n")
if err != nil {
log.Error(err)
}
}
fmt.Println(b.String())
}
...@@ -16,7 +16,7 @@ import ( ...@@ -16,7 +16,7 @@ import (
func main() { func main() {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
sbi := &nucleus.AristaOC{} sbi := &nucleus.Arista{}
device := nucleus.Device{ device := nucleus.Device{
GoStruct: sbi.Schema().Root, GoStruct: sbi.Schema().Root,
......
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
func main() { func main() {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
sbi := &nucleus.AristaOC{} sbi := &nucleus.OpenConfig{}
device := &nucleus.Device{ device := &nucleus.Device{
GoStruct: sbi.Schema().Root, GoStruct: sbi.Schema().Root,
SBI: sbi, SBI: sbi,
...@@ -42,10 +42,11 @@ func main() { ...@@ -42,10 +42,11 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
transport.SetNode = sbi.SetNode() transport.SetNode = sbi.SetNode()
transport.Unmarshal = sbi.Unmarshal()
device.Transport = transport device.Transport = transport
p := []string{"/interfaces/interface/name"} p := []string{"/interfaces"}
errors := 0 errors := 0
for _, path := range p { for _, path := range p {
err := pnd.RequestAll(path) err := pnd.RequestAll(path)
......
...@@ -57,7 +57,7 @@ func (c *Core) AttachDatabase() { ...@@ -57,7 +57,7 @@ func (c *Core) AttachDatabase() {
// CreateSouthboundInterfaces initializes the controller with its supported SBIs // CreateSouthboundInterfaces initializes the controller with its supported SBIs
func (c *Core) CreateSouthboundInterfaces() error { func (c *Core) CreateSouthboundInterfaces() error {
if err := c.sbic.add(&AristaOC{id: uuid.New()}); err != nil { if err := c.sbic.add(&Arista{id: uuid.New()}); err != nil {
return err return err
} }
if err := c.sbic.add(&OpenConfig{id: uuid.New()}); err != nil { if err := c.sbic.add(&OpenConfig{id: uuid.New()}); err != nil {
......
...@@ -33,3 +33,11 @@ type ErrInvalidTypeAssertion struct { ...@@ -33,3 +33,11 @@ type ErrInvalidTypeAssertion struct {
func (e ErrInvalidTypeAssertion) Error() string { func (e ErrInvalidTypeAssertion) Error() string {
return fmt.Sprintf("%v does not implement %v", e.v, e.t) return fmt.Sprintf("%v does not implement %v", e.v, e.t)
} }
type ErrUnsupportedPath struct {
p interface{}
}
func (e ErrUnsupportedPath) Error() string {
return fmt.Sprintf("path %v is not supported", e.p)
}
\ No newline at end of file
...@@ -19,7 +19,7 @@ var tapProto bool ...@@ -19,7 +19,7 @@ var tapProto bool
func init() { func init() {
// tapProto taps gpb.getResponse and gpb.Getrequests // tapProto taps gpb.getResponse and gpb.Getrequests
// to binary file // to binary file
// CAUTION only set true if you know what you do // CAUTION only set true if you know what you're doing
tapProto = false tapProto = false
} }
...@@ -29,18 +29,17 @@ func NewGnmiTransport(config *gnmi.Config) (*Gnmi, error) { ...@@ -29,18 +29,17 @@ func NewGnmiTransport(config *gnmi.Config) (*Gnmi, error) {
return nil, err return nil, err
} }
return &Gnmi{ return &Gnmi{
SetNode: nil, config: config,
RespChan: nil, client: c,
config: config,
client: c,
}, nil }, nil
} }
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 Unmarshal func([]byte, []string, interface{}, ...ytypes.UnmarshalOpt) error
config *gnmi.Config RespChan chan *gpb.SubscribeResponse
client gpb.GNMIClient config *gnmi.Config
client gpb.GNMIClient
} }
func (g *Gnmi) SetConfig(config *gnmi.Config) { func (g *Gnmi) SetConfig(config *gnmi.Config) {
...@@ -81,7 +80,6 @@ func (g *Gnmi) Type() string { ...@@ -81,7 +80,6 @@ func (g *Gnmi) Type() string {
// 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.
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 models := s.SchemaTree
opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}}
r := resp.(*gpb.GetResponse) r := resp.(*gpb.GetResponse)
rn := r.Notification rn := r.Notification
for _, msg := range rn { for _, msg := range rn {
...@@ -96,11 +94,17 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch ...@@ -96,11 +94,17 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch
return err return err
} }
} }
modelKey := extractModelKey(fullPath) val,ok := update.Val.Value.(*gpb.TypedValue_JsonIetfVal)
if ok {
log.Debug(modelKey) opts := []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}}
if err := g.Unmarshal(val.JsonIetfVal, extraxtPathElements(fullPath), root, opts...); err != nil {
return err
}
return nil
}
// TODO(mk): Evaluate hardcoded model key
schema := models["Device"] schema := models["Device"]
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 {
return err return err
} }
...@@ -109,6 +113,14 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch ...@@ -109,6 +113,14 @@ func (g *Gnmi) ProcessResponse(resp interface{}, root interface{}, s *ytypes.Sch
return nil 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
}
// extractModelKey extracts the model's key from the full path. Highly model specific for now // extractModelKey extracts the model's key from the full path. Highly model specific for now
// TODO: Remove hard coded model prefix // TODO: Remove hard coded model prefix
// TODO: Figure out why path.Elem() is empty but path.Elememt() is deprecated // TODO: Figure out why path.Elem() is empty but path.Elememt() is deprecated
...@@ -116,7 +128,7 @@ func extractModelKey(path *gpb.Path) string { ...@@ -116,7 +128,7 @@ func extractModelKey(path *gpb.Path) string {
var b strings.Builder var b strings.Builder
delim := "_" delim := "_"
b.WriteString("OpenconfigInterfaces") b.WriteString("OpenconfigInterfaces")
for i := 0; i < len(path.Elem)-1; i++ { for i := 0; i < len(path.Elem); i++ {
pathElement := path.Elem[i] pathElement := path.Elem[i]
b.WriteString(delim) b.WriteString(delim)
b.WriteString(strings.Title(pathElement.Name)) b.WriteString(strings.Title(pathElement.Name))
...@@ -139,6 +151,8 @@ func gnmiFullPath(prefix, path *gpb.Path) (*gpb.Path, error) { ...@@ -139,6 +151,8 @@ func gnmiFullPath(prefix, path *gpb.Path) (*gpb.Path, error) {
// Capabilities calls GNMI capabilities // Capabilities calls GNMI capabilities
func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) { func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) {
ctx = gnmi.NewContext(ctx, g.config)
ctx = context.WithValue(ctx, "config", g.config)
resp, err := g.client.Capabilities(ctx, &gpb.CapabilityRequest{}) resp, err := g.client.Capabilities(ctx, &gpb.CapabilityRequest{})
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -162,10 +176,10 @@ func (g *Gnmi) get(ctx context.Context, paths [][]string, origin string) (interf ...@@ -162,10 +176,10 @@ func (g *Gnmi) get(ctx context.Context, paths [][]string, origin string) (interf
func (g *Gnmi) getWithRequest(ctx context.Context, req *gpb.GetRequest) (interface{}, error) { func (g *Gnmi) getWithRequest(ctx context.Context, req *gpb.GetRequest) (interface{}, error) {
resp, err := g.client.Get(ctx, req) resp, err := g.client.Get(ctx, req)
if tapProto { if tapProto {
if err := util.Write(req, "req-"+time.Now().String()); err != nil { if err := util.Write(req, "get-req-"+time.Now().String()); err != nil {
log.Errorf("error while writing request: %v", err) log.Errorf("error while writing request: %v", err)
} }
if err := util.Write(resp, "resp-"+time.Now().String()); err != nil { if err := util.Write(resp, "get-resp-"+time.Now().String()); err != nil {
log.Errorf("error while writing request: %v", err) log.Errorf("error while writing request: %v", err)
} }
} }
......
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"code.fbi.h-da.de/cocsn/gosdn/mocks" "code.fbi.h-da.de/cocsn/gosdn/mocks"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/util" "code.fbi.h-da.de/cocsn/gosdn/nucleus/util"
"code.fbi.h-da.de/cocsn/gosdn/test" "code.fbi.h-da.de/cocsn/gosdn/test"
"code.fbi.h-da.de/cocsn/yang-models/generated/arista"
"code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig"
"context" "context"
log "github.com/golang/glog" log "github.com/golang/glog"
...@@ -24,6 +23,7 @@ func TestMain(m *testing.M) { ...@@ -24,6 +23,7 @@ func TestMain(m *testing.M) {
testSetupGnmi() testSetupGnmi()
testSetupPnd() testSetupPnd()
testSetupStore() testSetupStore()
testSetupSbi()
os.Exit(m.Run()) os.Exit(m.Run())
} }
...@@ -76,9 +76,11 @@ func TestGnmi_Capabilities(t *testing.T) { ...@@ -76,9 +76,11 @@ func TestGnmi_Capabilities(t *testing.T) {
capabilityRequest := &gpb.CapabilityRequest{} capabilityRequest := &gpb.CapabilityRequest{}
ctx := context.Background()
transport.client.(*mocks.GNMIClient). transport.client.(*mocks.GNMIClient).
On("Capabilities", ctx, capabilityRequest). On("NewContext", mockContext, mock.Anything).
Return(mockContext)
transport.client.(*mocks.GNMIClient).
On("Capabilities", mockContext, capabilityRequest).
Return(capabilityResponse, nil) Return(capabilityResponse, nil)
type fields struct { type fields struct {
...@@ -281,37 +283,28 @@ func TestGnmi_ProcessResponse(t *testing.T) { ...@@ -281,37 +283,28 @@ func TestGnmi_ProcessResponse(t *testing.T) {
wantErr bool wantErr bool
}{ }{
{ {
name: "Arista Full Node", name: "Interfaces Interface",
fields: fields{Sbi: &AristaOC{}}, fields: fields{Sbi: &OpenConfig{}},
args: args{ args: args{
path: "../test/resp-full-node", path: "../test/resp-full-node",
root: &arista.Device{}, root: &openconfig.Device{},
},
wantErr: false,
},
{
name: "Arista Interfaces Wildcard",
fields: fields{Sbi: &AristaOC{}},
args: args{
path: "../test/resp-interfaces-wildcard",
root: &arista.Device{},
}, },
wantErr: false, wantErr: true,
}, },
{ {
name: "OC Full Node", name: "Interfaces Wildcard",
fields: fields{Sbi: &OpenConfig{}}, fields: fields{Sbi: &OpenConfig{}},
args: args{ args: args{
path: "../test/resp-full-node", path: "../test/resp-interfaces-wildcard",
root: &openconfig.Device{}, root: &openconfig.Device{},
}, },
wantErr: false, wantErr: false,
}, },
{ {
name: "OC Interfaces Wildcard", name: "Root",
fields: fields{Sbi: &OpenConfig{}}, fields: fields{Sbi: &OpenConfig{}},
args: args{ args: args{
path: "../test/resp-interfaces-wildcard", path: "../test/resp-full-node-arista-ceos",
root: &openconfig.Device{}, root: &openconfig.Device{},
}, },
wantErr: false, wantErr: false,
...@@ -321,6 +314,7 @@ func TestGnmi_ProcessResponse(t *testing.T) { ...@@ -321,6 +314,7 @@ func TestGnmi_ProcessResponse(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
g := &Gnmi{ g := &Gnmi{
SetNode: tt.fields.Sbi.SetNode(), SetNode: tt.fields.Sbi.SetNode(),
Unmarshal: tt.fields.Sbi.(*OpenConfig).Unmarshal(),
} }
s := tt.fields.Sbi.Schema() s := tt.fields.Sbi.Schema()
resp := &gpb.GetResponse{} resp := &gpb.GetResponse{}
...@@ -564,7 +558,7 @@ func Test_extractModelKey(t *testing.T) { ...@@ -564,7 +558,7 @@ func Test_extractModelKey(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := extractModelKey(tt.args.path); got != tt.want { if got := extractModelKey(tt.args.path); got != tt.want {
t.Errorf("extractModelKey() = %v, want %v", got, tt.want) t.Errorf("extraxtPathElements() = %v, want %v", got, tt.want)
} }
}) })
} }
......
...@@ -2,6 +2,7 @@ package nucleus ...@@ -2,6 +2,7 @@ package nucleus
import ( import (
"code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/util"
"context" "context"
gpb "github.com/openconfig/gnmi/proto/gnmi" gpb "github.com/openconfig/gnmi/proto/gnmi"
"testing" "testing"
...@@ -73,7 +74,11 @@ func TestGnmi_SubscribeIntegration(t *testing.T) { ...@@ -73,7 +74,11 @@ func TestGnmi_SubscribeIntegration(t *testing.T) {
Password: "arista", Password: "arista",
Encoding: gpb.Encoding_JSON_IETF, Encoding: gpb.Encoding_JSON_IETF,
} }
transport,_ := NewGnmiTransport(cfg) transport,err := NewGnmiTransport(cfg)
if err != nil {
t.Error(err)
}
transport.RespChan = make(chan *gpb.SubscribeResponse)
paths := []string{"/interfaces/interface/name"} paths := []string{"/interfaces/interface/name"}
...@@ -106,25 +111,34 @@ func TestGnmi_CapabilitiesIntegration(t *testing.T) { ...@@ -106,25 +111,34 @@ func TestGnmi_CapabilitiesIntegration(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("skipping integration test") t.Skip("skipping integration test")
} }
t.Run("Test GNMI Capabilities", func(t *testing.T){ respones := map[string]*gpb.CapabilityResponse{
cfg := &gnmi.Config{ "../test/cap-resp-arista-ceos": {},
Addr: address,
Username: "admin",
Password: "arista",
Encoding: gpb.Encoding_JSON_IETF,
} }
transport,err := NewGnmiTransport(cfg) for k,v := range respones {
if err != nil { if err := util.Read(k, v); err != nil {
t.Error(err) t.Errorf("error parsing %v: %v", k, err)
} }
resp, err := transport.Capabilities(context.Background())
if err != nil { t.Run("Test GNMI Capabilities", func(t *testing.T) {
t.Error(err) cfg := &gnmi.Config{
} Addr: address,
if resp == nil { Username: "admin",
t.Error("resp is nil") 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) { func TestPndImplementation_RequestAllIntegration(t *testing.T) {
......
...@@ -3,11 +3,14 @@ package nucleus ...@@ -3,11 +3,14 @@ package nucleus
import ( import (
"code.fbi.h-da.de/cocsn/yang-models/generated/arista" "code.fbi.h-da.de/cocsn/yang-models/generated/arista"
"code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig"
log "github.com/golang/glog"
"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/ytypes" "github.com/openconfig/ygot/ytypes"
log "github.com/sirupsen/logrus"
"reflect"
) )
// SouthboundInterface provides an // SouthboundInterface provides an
...@@ -47,6 +50,7 @@ func (oc *OpenConfig) SbiIdentifier() string { ...@@ -47,6 +50,7 @@ func (oc *OpenConfig) SbiIdentifier() string {
func (oc *OpenConfig) Schema() *ytypes.Schema { func (oc *OpenConfig) Schema() *ytypes.Schema {
schema, err := openconfig.Schema() schema, err := openconfig.Schema()
oc.schema = schema
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
...@@ -65,6 +69,86 @@ func (oc *OpenConfig) SetNode() func(schema *yang.Entry, root interface{}, path ...@@ -65,6 +69,86 @@ 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 unmarshal
}
// unmarshal parses a root or 1st level gNMI response to a go struct
// Named return due to how recover works here
func unmarshal(bytes []byte, fields []string, goStruct interface{}, opt ...ytypes.UnmarshalOpt) (err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
switch l := len(fields); l {
case 0:
return openconfig.Unmarshal(bytes, goStruct.(*openconfig.Device), opt...)
case 1:
default:
return &ErrUnsupportedPath{fields}
}
var c ygot.GoStruct
var field string
// Load SBI definition
d := openconfig.Device{}
c, field, err = iter(&d, fields)
if err != nil {
return
}
if err = openconfig.Unmarshal(bytes, c, opt...); err != nil {
return
}
reflect.ValueOf(goStruct.(*openconfig.Device)).Elem().FieldByName(field).Set(reflect.ValueOf(c))
return nil
}
// iter walks down the provided paths and initializes the ygot.GoStruct. It only works for
// the root level. Watch out for named returns here
// TODO(mk): Fix deeper layers
func iter(a ygot.GoStruct, fields []string) (b ygot.GoStruct, f string, err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
var c ygot.GoStruct
var configStruct reflect.Value
f = fields[0]
s := reflect.ValueOf(a).Elem()
h := s.FieldByName(f)
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(mk): Prettify
p2 := configStruct.Elem()
// If we have KeyHelperGoStruct we need make and modify map instead of plain struct
if p2.Kind() == reflect.Map {
p2.Set(reflect.MakeMap(p2.Type()))
configStruct.Elem().Set(p2)
if err := util.InsertIntoMapStructField(a, f, "", p2); err != nil {
panic(err)
}
} else {
configStruct.Elem().Set(reflect.New(p2.Type().Elem()))
b = configStruct.Elem().Interface().(ygot.GoStruct)
}
if len(fields) > 1 {
c, _, _ = iter(b, fields[1:])
} else {
return
}
reflect.ValueOf(b).Elem().FieldByName(f).Set(reflect.ValueOf(c))
return
}
func (oc *OpenConfig) Id() uuid.UUID { func (oc *OpenConfig) Id() uuid.UUID {
return oc.id return oc.id
} }
...@@ -72,7 +156,7 @@ func (oc *OpenConfig) Id() uuid.UUID { ...@@ -72,7 +156,7 @@ func (oc *OpenConfig) Id() uuid.UUID {
// deprecated // deprecated
// Use for prototyping only. // Use for prototyping only.
// Use OpenConfig instead // Use OpenConfig instead
type AristaOC struct { type Arista struct {
// deprecated // deprecated
transport Transport transport Transport
...@@ -80,26 +164,27 @@ type AristaOC struct { ...@@ -80,26 +164,27 @@ type AristaOC struct {
id uuid.UUID id uuid.UUID
} }
func (oc *AristaOC) Id() uuid.UUID { func (oc *Arista) Id() uuid.UUID {
return oc.id return oc.id
} }
func (oc *AristaOC) SbiIdentifier() string { func (oc *Arista) SbiIdentifier() string {
return "arista" return "arista"
} }
func (oc *AristaOC) Schema() *ytypes.Schema { func (oc *Arista) Schema() *ytypes.Schema {
schema, err := arista.Schema() schema, err := arista.Schema()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
oc.schema = schema
return schema return schema
} }
// SetNode injects AristaOC specific model // SetNode injects Arista specific model
// representation to the transport. // representation to the transport.
// Needed for type assertion. // 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 { 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 { if err := ytypes.SetNode(schema, root.(*arista.Device), path, val, opts...); err != nil {
return err return err
...@@ -107,3 +192,17 @@ func (oc *AristaOC) SetNode() func(schema *yang.Entry, root interface{}, path *g ...@@ -107,3 +192,17 @@ func (oc *AristaOC) SetNode() func(schema *yang.Entry, root interface{}, path *g
return nil 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
}
}
package nucleus package nucleus
import (
"code.fbi.h-da.de/cocsn/gosdn/nucleus/util"
"code.fbi.h-da.de/cocsn/yang-models/generated/openconfig"
"github.com/google/uuid"
gpb "github.com/openconfig/gnmi/proto/gnmi"
"github.com/openconfig/ygot/ygot"
"github.com/openconfig/ygot/ytypes"
log "github.com/sirupsen/logrus"
"reflect"
"testing"
)
func testSetupSbi() {
var err error
aristaUUID, err = uuid.Parse("d3795249-579c-4be7-8818-29f113cb86ee")
if err != nil {
log.Fatal(err)
}
ocUUID, err = uuid.Parse("5e252b70-38f2-4c99-a0bf-1b16af4d7e67")
if err != nil {
log.Fatal(err)
}
}
var aristaUUID uuid.UUID
var ocUUID uuid.UUID
func TestOpenConfig_Id(t *testing.T) {
type fields struct {
transport Transport
schema *ytypes.Schema
id uuid.UUID
}
tests := []struct {
name string
fields fields
want uuid.UUID
}{
{
name: "default",
fields: fields{
transport: nil,
schema: nil,
id: ocUUID,
},
want: ocUUID,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
oc := &OpenConfig{
transport: tt.fields.transport,
schema: tt.fields.schema,
id: tt.fields.id,
}
if got := oc.Id(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Id() = %v, want %v", got, tt.want)
}
})
}
}
func TestOpenConfig_SbiIdentifier(t *testing.T) {
type fields struct {
transport Transport
schema *ytypes.Schema
id uuid.UUID
}
tests := []struct {
name string
fields fields
want string
}{
{name: "default", fields: fields{}, want: "openconfig"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
oc := &OpenConfig{
transport: tt.fields.transport,
schema: tt.fields.schema,
id: tt.fields.id,
}
if got := oc.SbiIdentifier(); got != tt.want {
t.Errorf("SbiIdentifier() = %v, want %v", got, tt.want)
}
})
}
}
func TestOpenConfig_Schema(t *testing.T) {
schema, err := openconfig.Schema()
if err != nil {
t.Error(err)
}
type fields struct {
transport Transport
schema *ytypes.Schema
id uuid.UUID
}
tests := []struct {
name string
fields fields
want *ytypes.Schema
}{
{name: "default", fields: fields{}, want: schema},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
oc := &OpenConfig{
transport: tt.fields.transport,
schema: tt.fields.schema,
id: tt.fields.id,
}
if got := oc.Schema(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("Schema() = %v, want %v", got, tt.want)
}
})
}
}
func Test_unmarshal(t *testing.T) {
type args struct {
path string
goStruct interface{}
opt []ytypes.UnmarshalOpt
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "fail",
args: args{
goStruct: &openconfig.Device{},
path: "../test/resp-interfaces-interface-arista-ceos",
},
wantErr: true,
},
{
name: "root w/opts",
args: args{
path: "../test/resp-full-node-arista-ceos",
goStruct: &openconfig.Device{},
opt: []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}},
},
wantErr: false,
},
{
name: "root w/o opts",
args: args{
path: "../test/resp-full-node-arista-ceos",
goStruct: &openconfig.Device{},
opt: nil,
},
wantErr: true,
},
{
name: "interfaces w/opts",
args: args{
path: "../test/resp-interfaces-arista-ceos",
goStruct: &openconfig.Device{},
opt: []ytypes.UnmarshalOpt{&ytypes.IgnoreExtraFields{}},
},
wantErr: false,
},
{
name: "interfaces w/o opts",
args: args{
path: "../test/resp-interfaces-arista-ceos",
goStruct: &openconfig.Device{},
opt: nil,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp := &gpb.GetResponse{}
err := util.Read(tt.args.path, resp)
if err != nil {
t.Error(err)
}
fields := extraxtPathElements(resp.Notification[0].Update[0].Path)
bytes := resp.Notification[0].Update[0].Val.GetJsonIetfVal()
if err := unmarshal(bytes, fields, tt.args.goStruct, tt.args.opt...); (err != nil) != tt.wantErr {
t.Errorf("unmarshal() error = %v, wantErr %v", err, tt.wantErr)
}
if tt.args.goStruct.(*openconfig.Device).Interfaces == nil && tt.args.opt != nil{
t.Errorf("unmarshal() error: field Interfaces must not be nil")
}
})
}
}
func Test_iter(t *testing.T) {
type args struct {
a ygot.GoStruct
fields []string
}
tests := []struct {
name string
args args
wantB ygot.GoStruct
wantField string
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotB, gotField, err := iter(tt.args.a, tt.args.fields)
if (err != nil) != tt.wantErr {
t.Errorf("iter() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotB, tt.wantB) {
t.Errorf("iter() gotB = %v, want %v", gotB, tt.wantB)
}
if gotField != tt.wantField {
t.Errorf("iter() gotField = %v, want %v", gotField, tt.wantField)
}
})
}
}
[
{
"time": "1970-01-01T01:00:00+01:00",
"updates": [
{
"Path": "interfaces/interface[name=Ethernet510]",
"values": {
"interfaces/interface": {
"openconfig-if-ethernet:ethernet": {
"arista-intf-augments:pfc": {
"priorities": {
"priority": [
{
"index": 0,
"state": {
"in-frames": "0",
"index": 0,
"out-frames": "0"
}
},
{
"index": 1,
"state": {
"in-frames": "0",
"index": 1,
"out-frames": "0"
}
},
{
"index": 2,
"state": {
"in-frames": "0",
"index": 2,
"out-frames": "0"
}
},
{
"index": 3,
"state": {
"in-frames": "0",
"index": 3,
"out-frames": "0"
}
},
{
"index": 4,
"state": {
"in-frames": "0",
"index": 4,
"out-frames": "0"
}
},
{
"index": 5,
"state": {
"in-frames": "0",
"index": 5,
"out-frames": "0"
}
},
{
"index": 6,
"state": {
"in-frames": "0",
"index": 6,
"out-frames": "0"
}
},
{
"index": 7,
"state": {
"in-frames": "0",
"index": 7,
"out-frames": "0"
}
}
]
}
},
"config": {
"arista-intf-augments:fec-encoding": {
"disabled": false,
"fire-code": false,
"reed-solomon": false,
"reed-solomon544": false
},
"arista-intf-augments:sfp-1000base-t": false,
"mac-address": "00:00:00:00:00:00",
"openconfig-hercules-interfaces:forwarding-viable": true,
"port-speed": "SPEED_UNKNOWN"
},
"state": {
"arista-intf-augments:supported-speeds": [
"SPEED_200GB_8LANE",
"SPEED_100MB",
"SPEED_1GB",
"SPEED_10GB",
"SPEED_400GB",
"SPEED_40GB",
"SPEED_2500MB",
"SPEED_50GB",
"SPEED_50GB_1LANE",
"SPEED_25GB",
"SPEED_100GB",
"SPEED_100GB_2LANE",
"SPEED_10MB",
"SPEED_200GB_4LANE",
"SPEED_5GB"
],
"auto-negotiate": false,
"counters": {
"in-crc-errors": "0",
"in-fragment-frames": "0",
"in-jabber-frames": "0",
"in-mac-control-frames": "0",
"in-mac-pause-frames": "0",
"in-oversize-frames": "0",
"out-mac-control-frames": "0",
"out-mac-pause-frames": "0"
},
"duplex-mode": "FULL",
"enable-flow-control": false,
"hw-mac-address": "02:42:c0:a8:02:42",
"mac-address": "02:42:c0:a8:02:42",
"negotiated-port-speed": "SPEED_UNKNOWN",
"openconfig-hercules-interfaces:forwarding-viable": true,
"port-speed": "SPEED_UNKNOWN"
}
},
"openconfig-interfaces:config": {
"arista-intf-augments:load-interval": 300,
"description": "",
"enabled": true,
"loopback-mode": false,
"mtu": 0,
"name": "Ethernet510",
"openconfig-vlan:tpid": "openconfig-vlan-types:TPID_0X8100",
"type": "iana-if-type:ethernetCsmacd"
},
"openconfig-interfaces:hold-time": {
"config": {
"down": 0,
"up": 0
},
"state": {
"down": 0,
"up": 0
}
},
"openconfig-interfaces:name": "Ethernet510",
"openconfig-interfaces:state": {
"admin-status": "UP",
"arista-intf-augments:inactive": false,
"counters": {
"in-broadcast-pkts": "344691",
"in-discards": "0",
"in-errors": "0",
"in-fcs-errors": "0",
"in-multicast-pkts": "1",
"in-octets": "93260151",
"in-unicast-pkts": "0",
"out-broadcast-pkts": "0",
"out-discards": "0",
"out-errors": "0",
"out-multicast-pkts": "0",
"out-octets": "0",
"out-unicast-pkts": "0"
},
"description": "",
"enabled": true,
"ifindex": 510,
"last-change": "1614091948142304000",
"loopback-mode": false,
"mtu": 0,
"name": "Ethernet510",
"openconfig-platform-port:hardware-port": "Port510",
"openconfig-vlan:tpid": "openconfig-vlan-types:TPID_0X8100",
"oper-status": "UP",
"type": "iana-if-type:ethernetCsmacd"
},
"openconfig-interfaces:subinterfaces": {
"subinterface": [
{
"config": {
"description": "",
"enabled": true,
"index": 0
},
"index": 0,
"openconfig-if-ip:ipv4": {
"config": {
"dhcp-client": false,
"enabled": false,
"mtu": 1500
},
"state": {
"dhcp-client": false,
"enabled": false,
"mtu": 1500
},
"unnumbered": {
"config": {
"enabled": false
},
"state": {
"enabled": false
}
}
},
"openconfig-if-ip:ipv6": {
"config": {
"dhcp-client": false,
"enabled": false,
"mtu": 1500
},
"state": {
"dhcp-client": false,
"enabled": false,
"mtu": 1500
}
},
"state": {
"counters": {
"in-fcs-errors": "0"
},
"description": "",
"enabled": true,
"index": 0
}
}
]
}
}
}
}
]
}
]
[
{
"time": "1970-01-01T01:00:00+01:00",
"updates": [
{
"Path": "interfaces",
"values": {
"interfaces": {
"openconfig-interfaces:interface": [
{
"config": {
"arista-intf-augments:load-interval": 300,
"description": "",
"enabled": true,
"loopback-mode": false,
"mtu": 0,
"name": "Ethernet510",
"openconfig-vlan:tpid": "openconfig-vlan-types:TPID_0X8100",
"type": "iana-if-type:ethernetCsmacd"
},
"hold-time": {
"config": {
"down": 0,
"up": 0
},
"state": {
"down": 0,
"up": 0
}
},
"name": "Ethernet510",
"openconfig-if-ethernet:ethernet": {
"arista-intf-augments:pfc": {
"priorities": {
"priority": [
{
"index": 0,
"state": {
"in-frames": "0",
"index": 0,
"out-frames": "0"
}
},
{
"index": 1,
"state": {
"in-frames": "0",
"index": 1,
"out-frames": "0"
}
},
{
"index": 2,
"state": {
"in-frames": "0",
"index": 2,
"out-frames": "0"
}
},
{
"index": 3,
"state": {
"in-frames": "0",
"index": 3,
"out-frames": "0"
}
},
{
"index": 4,
"state": {
"in-frames": "0",
"index": 4,
"out-frames": "0"
}
},
{
"index": 5,
"state": {
"in-frames": "0",
"index": 5,
"out-frames": "0"
}
},
{
"index": 6,
"state": {
"in-frames": "0",
"index": 6,
"out-frames": "0"
}
},
{
"index": 7,
"state": {
"in-frames": "0",
"index": 7,
"out-frames": "0"
}
}
]
}
},
"config": {
"arista-intf-augments:fec-encoding": {
"disabled": false,
"fire-code": false,
"reed-solomon": false,
"reed-solomon544": false
},
"arista-intf-augments:sfp-1000base-t": false,
"mac-address": "00:00:00:00:00:00",
"openconfig-hercules-interfaces:forwarding-viable": true,
"port-speed": "SPEED_UNKNOWN"
},
"state": {
"arista-intf-augments:supported-speeds": [
"SPEED_200GB_8LANE",
"SPEED_100MB",
"SPEED_1GB",
"SPEED_10GB",
"SPEED_400GB",
"SPEED_40GB",
"SPEED_2500MB",
"SPEED_50GB",
"SPEED_50GB_1LANE",
"SPEED_25GB",
"SPEED_100GB",
"SPEED_100GB_2LANE",
"SPEED_10MB",
"SPEED_200GB_4LANE",
"SPEED_5GB"
],
"auto-negotiate": false,
"counters": {
"in-crc-errors": "0",
"in-fragment-frames": "0",
"in-jabber-frames": "0",
"in-mac-control-frames": "0",
"in-mac-pause-frames": "0",
"in-oversize-frames": "0",
"out-mac-control-frames": "0",
"out-mac-pause-frames": "0"
},
"duplex-mode": "FULL",
"enable-flow-control": false,
"hw-mac-address": "02:42:c0:a8:02:42",
"mac-address": "02:42:c0:a8:02:42",
"negotiated-port-speed": "SPEED_UNKNOWN",
"openconfig-hercules-interfaces:forwarding-viable": true,
"port-speed": "SPEED_UNKNOWN"
}
},
"state": {
"admin-status": "UP",
"arista-intf-augments:inactive": false,
"counters": {
"in-broadcast-pkts": "344691",
"in-discards": "0",
"in-errors": "0",
"in-fcs-errors": "0",
"in-multicast-pkts": "1",
"in-octets": "93260151",
"in-unicast-pkts": "0",
"out-broadcast-pkts": "0",
"out-discards": "0",
"out-errors": "0",
"out-multicast-pkts": "0",
"out-octets": "0",
"out-unicast-pkts": "0"
},
"description": "",
"enabled": true,
"ifindex": 510,
"last-change": "1614091948142304000",
"loopback-mode": false,
"mtu": 0,
"name": "Ethernet510",
"openconfig-platform-port:hardware-port": "Port510",
"openconfig-vlan:tpid": "openconfig-vlan-types:TPID_0X8100",
"oper-status": "UP",
"type": "iana-if-type:ethernetCsmacd"
},
"subinterfaces": {
"subinterface": [
{
"config": {
"description": "",
"enabled": true,
"index": 0
},
"index": 0,
"openconfig-if-ip:ipv4": {
"config": {
"dhcp-client": false,
"enabled": false,
"mtu": 1500
},
"state": {
"dhcp-client": false,
"enabled": false,
"mtu": 1500
},
"unnumbered": {
"config": {
"enabled": false
},
"state": {
"enabled": false
}
}
},
"openconfig-if-ip:ipv6": {
"config": {
"dhcp-client": false,
"enabled": false,
"mtu": 1500
},
"state": {
"dhcp-client": false,
"enabled": false,
"mtu": 1500
}
},
"state": {
"counters": {
"in-fcs-errors": "0"
},
"description": "",
"enabled": true,
"index": 0
}
}
]
}
}
]
}
}
}
]
}
]
This diff is collapsed.
File added
File added

interfaces
interfaces(
\ No newline at end of file
2
interfaces
interface
interfaces
interface(
\ No newline at end of file
File added
"

interfacesZ{"openconfig-interfaces:interface":[{"config":{"description":"","enabled":true,"arista-intf-augments:load-interval":300,"loopback-mode":false,"mtu":0,"name":"Ethernet510","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"openconfig-if-ethernet:ethernet":{"config":{"arista-intf-augments:fec-encoding":{"disabled":false,"fire-code":false,"reed-solomon":false,"reed-solomon544":false},"openconfig-hercules-interfaces:forwarding-viable":true,"mac-address":"00:00:00:00:00:00","port-speed":"SPEED_UNKNOWN","arista-intf-augments:sfp-1000base-t":false},"arista-intf-augments:pfc":{"priorities":{"priority":[{"index":0,"state":{"in-frames":"0","index":0,"out-frames":"0"}},{"index":1,"state":{"in-frames":"0","index":1,"out-frames":"0"}},{"index":2,"state":{"in-frames":"0","index":2,"out-frames":"0"}},{"index":3,"state":{"in-frames":"0","index":3,"out-frames":"0"}},{"index":4,"state":{"in-frames":"0","index":4,"out-frames":"0"}},{"index":5,"state":{"in-frames":"0","index":5,"out-frames":"0"}},{"index":6,"state":{"in-frames":"0","index":6,"out-frames":"0"}},{"index":7,"state":{"in-frames":"0","index":7,"out-frames":"0"}}]}},"state":{"auto-negotiate":false,"counters":{"in-crc-errors":"0","in-fragment-frames":"0","in-jabber-frames":"0","in-mac-control-frames":"0","in-mac-pause-frames":"0","in-oversize-frames":"0","out-mac-control-frames":"0","out-mac-pause-frames":"0"},"duplex-mode":"FULL","enable-flow-control":false,"openconfig-hercules-interfaces:forwarding-viable":true,"hw-mac-address":"02:42:c0:a8:02:42","mac-address":"02:42:c0:a8:02:42","negotiated-port-speed":"SPEED_UNKNOWN","port-speed":"SPEED_UNKNOWN","arista-intf-augments:supported-speeds":["SPEED_200GB_8LANE","SPEED_100MB","SPEED_1GB","SPEED_10GB","SPEED_400GB","SPEED_40GB","SPEED_2500MB","SPEED_50GB","SPEED_50GB_1LANE","SPEED_25GB","SPEED_100GB","SPEED_100GB_2LANE","SPEED_10MB","SPEED_200GB_4LANE","SPEED_5GB"]}},"hold-time":{"config":{"down":0,"up":0},"state":{"down":0,"up":0}},"name":"Ethernet510","state":{"admin-status":"UP","counters":{"in-broadcast-pkts":"344691","in-discards":"0","in-errors":"0","in-fcs-errors":"0","in-multicast-pkts":"1","in-octets":"93260151","in-unicast-pkts":"0","out-broadcast-pkts":"0","out-discards":"0","out-errors":"0","out-multicast-pkts":"0","out-octets":"0","out-unicast-pkts":"0"},"description":"","enabled":true,"openconfig-platform-port:hardware-port":"Port510","ifindex":510,"arista-intf-augments:inactive":false,"last-change":"1614091948142304000","loopback-mode":false,"mtu":0,"name":"Ethernet510","oper-status":"UP","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"subinterfaces":{"subinterface":[{"config":{"description":"","enabled":true,"index":0},"index":0,"openconfig-if-ip:ipv4":{"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"state":{"dhcp-client":false,"enabled":false,"mtu":1500},"unnumbered":{"config":{"enabled":false},"state":{"enabled":false}}},"openconfig-if-ip:ipv6":{"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"state":{"dhcp-client":false,"enabled":false,"mtu":1500}},"state":{"counters":{"in-fcs-errors":"0"},"description":"","enabled":true,"index":0}}]}}]}
\ No newline at end of file
"
0
interfaces
interface
name Ethernet510Z{"openconfig-interfaces:config":{"description":"","enabled":true,"arista-intf-augments:load-interval":300,"loopback-mode":false,"mtu":0,"name":"Ethernet510","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"openconfig-if-ethernet:ethernet":{"config":{"arista-intf-augments:fec-encoding":{"disabled":false,"fire-code":false,"reed-solomon":false,"reed-solomon544":false},"openconfig-hercules-interfaces:forwarding-viable":true,"mac-address":"00:00:00:00:00:00","port-speed":"SPEED_UNKNOWN","arista-intf-augments:sfp-1000base-t":false},"arista-intf-augments:pfc":{"priorities":{"priority":[{"index":0,"state":{"in-frames":"0","index":0,"out-frames":"0"}},{"index":1,"state":{"in-frames":"0","index":1,"out-frames":"0"}},{"index":2,"state":{"in-frames":"0","index":2,"out-frames":"0"}},{"index":3,"state":{"in-frames":"0","index":3,"out-frames":"0"}},{"index":4,"state":{"in-frames":"0","index":4,"out-frames":"0"}},{"index":5,"state":{"in-frames":"0","index":5,"out-frames":"0"}},{"index":6,"state":{"in-frames":"0","index":6,"out-frames":"0"}},{"index":7,"state":{"in-frames":"0","index":7,"out-frames":"0"}}]}},"state":{"auto-negotiate":false,"counters":{"in-crc-errors":"0","in-fragment-frames":"0","in-jabber-frames":"0","in-mac-control-frames":"0","in-mac-pause-frames":"0","in-oversize-frames":"0","out-mac-control-frames":"0","out-mac-pause-frames":"0"},"duplex-mode":"FULL","enable-flow-control":false,"openconfig-hercules-interfaces:forwarding-viable":true,"hw-mac-address":"02:42:c0:a8:02:42","mac-address":"02:42:c0:a8:02:42","negotiated-port-speed":"SPEED_UNKNOWN","port-speed":"SPEED_UNKNOWN","arista-intf-augments:supported-speeds":["SPEED_200GB_8LANE","SPEED_100MB","SPEED_1GB","SPEED_10GB","SPEED_400GB","SPEED_40GB","SPEED_2500MB","SPEED_50GB","SPEED_50GB_1LANE","SPEED_25GB","SPEED_100GB","SPEED_100GB_2LANE","SPEED_10MB","SPEED_200GB_4LANE","SPEED_5GB"]}},"openconfig-interfaces:hold-time":{"config":{"down":0,"up":0},"state":{"down":0,"up":0}},"openconfig-interfaces:name":"Ethernet510","openconfig-interfaces:state":{"admin-status":"UP","counters":{"in-broadcast-pkts":"344691","in-discards":"0","in-errors":"0","in-fcs-errors":"0","in-multicast-pkts":"1","in-octets":"93260151","in-unicast-pkts":"0","out-broadcast-pkts":"0","out-discards":"0","out-errors":"0","out-multicast-pkts":"0","out-octets":"0","out-unicast-pkts":"0"},"description":"","enabled":true,"openconfig-platform-port:hardware-port":"Port510","ifindex":510,"arista-intf-augments:inactive":false,"last-change":"1614091948142304000","loopback-mode":false,"mtu":0,"name":"Ethernet510","oper-status":"UP","openconfig-vlan:tpid":"openconfig-vlan-types:TPID_0X8100","type":"iana-if-type:ethernetCsmacd"},"openconfig-interfaces:subinterfaces":{"subinterface":[{"config":{"description":"","enabled":true,"index":0},"index":0,"openconfig-if-ip:ipv4":{"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"state":{"dhcp-client":false,"enabled":false,"mtu":1500},"unnumbered":{"config":{"enabled":false},"state":{"enabled":false}}},"openconfig-if-ip:ipv6":{"config":{"dhcp-client":false,"enabled":false,"mtu":1500},"state":{"dhcp-client":false,"enabled":false,"mtu":1500}},"state":{"counters":{"in-fcs-errors":"0"},"description":"","enabled":true,"index":0}}]}}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment