diff --git a/cmd/gnmi-telemetry/telemetry.go b/cmd/gnmi-telemetry/telemetry.go new file mode 100644 index 0000000000000000000000000000000000000000..e2f369d6f7b5d1b08e99f8ceb1cd7c4ade9eab71 --- /dev/null +++ b/cmd/gnmi-telemetry/telemetry.go @@ -0,0 +1,74 @@ +package main + +import ( + "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" + "code.fbi.h-da.de/cocsn/gosdn/nucleus" + schema "code.fbi.h-da.de/cocsn/yang-models/generated/arista" + "context" + "fmt" + "github.com/google/uuid" + gpb "github.com/openconfig/gnmi/proto/gnmi" + log "github.com/sirupsen/logrus" + "os" + "os/signal" + "syscall" + "time" +) + +func main() { + log.SetLevel(log.DebugLevel) + sbi := &nucleus.AristaOC{} + transport := &nucleus.Gnmi{ + SetNode: sbi.SetNode(), + RespChan: make(chan *gpb.SubscribeResponse), + } + device := nucleus.Device{ + Device: &schema.Device{}, + SBI: sbi, + Config: nucleus.DeviceConfig{ + Uuid: uuid.New(), + Address: "[fdfd::ce05]:6030", + Username: "admin", + Password: "arista", + }, + Transport: transport, + } + pnd := nucleus.NewPND("openconfig", sbi) + if err := pnd.AddDevice(device); err != nil { + log.Fatal(err) + } + + cfg := &gnmi.Config{ + Addr: device.Config.Address, + Password: device.Config.Password, + Username: device.Config.Username, + } + ctx := gnmi.NewContext(context.Background(), cfg) + ctx = context.WithValue(ctx, "config", cfg) + + paths := []string{"/interfaces/interface/name"} + + opts := &gnmi.SubscribeOptions{ + UpdatesOnly: false, + Prefix: "", + Mode: "stream", + StreamMode: "sample", + SampleInterval: uint64(10 * time.Second.Nanoseconds()), + SuppressRedundant: false, + HeartbeatInterval: uint64(time.Second.Nanoseconds()), + Paths: gnmi.SplitPaths(paths), + Origin: "", + Target: device.Config.Address, + } + done := make(chan os.Signal, 1) + signal.Notify(done, syscall.SIGILL, syscall.SIGTERM) + ctx = context.WithValue(ctx, "opts", opts) + go func() { + if err := transport.Subscribe(ctx); err != nil { + log.Fatal(err) + } + }() + fmt.Println("awaiting signal") + <-done + fmt.Println("exiting") +} \ No newline at end of file diff --git a/cmd/gnmi/gnmi.go b/cmd/gnmi/gnmi.go index 5ac6dd04c1f01d270016f7dd4c3659306945e428..301e8ba131f6484a7167c20ddb3b39446af57ef1 100644 --- a/cmd/gnmi/gnmi.go +++ b/cmd/gnmi/gnmi.go @@ -3,25 +3,27 @@ package main import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" "code.fbi.h-da.de/cocsn/gosdn/nucleus" - "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" + "code.fbi.h-da.de/cocsn/gosdn/nucleus/util" + schema "code.fbi.h-da.de/cocsn/yang-models/generated/arista" "context" - "fmt" "github.com/google/uuid" log "github.com/sirupsen/logrus" ) func main() { log.SetLevel(log.DebugLevel) - sbi := &nucleus.OpenConfig{} + sbi := &nucleus.AristaOC{} + transport := &nucleus.Gnmi{SetNode: sbi.SetNode()} device := nucleus.Device{ Uuid: uuid.New(), - Device: &openconfig.Device{}, + Device: &schema.Device{}, SBI: sbi, Config: nucleus.DeviceConfig{ Address: "[fdfd::ce05]:6030", Username: "admin", Password: "arista", }, + Transport: transport, } pnd := nucleus.NewPND("openconfig", "test description", sbi) if err := pnd.AddDevice(device); err != nil { @@ -36,14 +38,25 @@ func main() { ctx := gnmi.NewContext(context.Background(), cfg) ctx = context.WithValue(ctx, "config", cfg) - paths := []string{"/interfaces/interface[name=*]/name"} - req, err := gnmi.NewGetRequest(gnmi.SplitPaths(paths), "") - resp, err := nucleus.GetWithRequest(ctx, req) - if err != nil { - log.Fatal(err) - } - if err := device.Add(resp); err != nil { - panic(err) + paths := util.NewPaths() + paths.ParseSchema(sbi.Schema(), "device") + p := paths.StringBuilder() + errors := 0 + for _, path := range p { + req, err := gnmi.NewGetRequest(gnmi.SplitPaths([]string{path}), "") + resp, err := nucleus.GetWithRequest(ctx, req) + if err != nil { + log.Debug(err) + errors++ + break + } + if err := device.Add(resp); err != nil { + log.Debug(err) + errors++ + } } - fmt.Println(device.Config) + + percentage := float64(errors) / float64(len(p)) * 100.0 + log.Debugf("%v errors", errors) + log.Debugf("%v percent failed", percentage) } diff --git a/nucleus/device.go b/nucleus/device.go index b580a2169d5d4d64bd4f76a9b513f7114b82cb00..b575ef3dfffeda91fd4dc3e377a42edb9ffaf66f 100644 --- a/nucleus/device.go +++ b/nucleus/device.go @@ -1,11 +1,8 @@ package nucleus import ( - "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" "github.com/google/uuid" - pb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/ygot/ygot" - "github.com/openconfig/ygot/ytypes" ) type Device struct { @@ -20,23 +17,7 @@ type Device struct { // use better naming in further develop // Also all that Interface Call specific logic belongs to SBI! func (d Device) Add(resp interface{}) error { - s, err := d.SBI.Schema() - if err != nil { - return err - } - models := s.SchemaTree - opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}} - r := resp.(*pb.GetResponse) - rn := r.Notification - for _, msg := range rn { - for _, val := range msg.Update { - schema := models["Device"] - if err := ytypes.SetNode(schema, d.Device.(*openconfig.Device), val.Path, val.Val, opts...); err != nil { - return err - } - } - } - return nil + return d.Transport.ProcessResponse(resp, d.Device, d.SBI.Schema()) } type DeviceConfig struct { diff --git a/nucleus/gnmi_transport.go b/nucleus/gnmi_transport.go index 867fdea28c1f39d163cc2dfe4318fae8e3bfe32a..c5f4054b9d7b70e1abcf489e624ee793d60a1947 100644 --- a/nucleus/gnmi_transport.go +++ b/nucleus/gnmi_transport.go @@ -3,13 +3,17 @@ package nucleus import ( "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" "context" - "github.com/google/uuid" - "github.com/openconfig/gnmi/proto/gnmi_ext" - gpb "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/gnmi/proto/gnmi_ext" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/ytypes" + log "github.com/sirupsen/logrus" ) -type Gnmi uuid.UUID +type Gnmi struct { + SetNode func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error + RespChan chan *gpb.SubscribeResponse +} func (g *Gnmi) SetConfig(interface{}) error { return nil @@ -19,6 +23,34 @@ func (g *Gnmi) GetConfig() interface{} { return nil } +// interface satisfaction for now +// TODO: Convert to meaningfiul calls +func (g *Gnmi)Get(ctx context.Context, params ...string) (interface{}, error){return nil, nil} +func (g *Gnmi)Set(ctx context.Context, params ...string) (interface{}, error){return nil, nil} +func (g *Gnmi)Subscribe(ctx context.Context, params ...string) error{ + return g.subscribe(ctx) +} + +func (g *Gnmi)Type() string { + return "gnmi" +} + +func (g *Gnmi)ProcessResponse(resp interface{},root interface{}, s *ytypes.Schema) error { + models := s.SchemaTree + opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}} + r := resp.(*gpb.GetResponse) + rn := r.Notification + for _, msg := range rn { + for _, val := range msg.Update { + schema := models["Device"] + if err := g.SetNode(schema, root, val.Path, val.Val, opts...); err != nil { + return err + } + } + } + return nil +} + // Capabilities calls GNMI capabilities func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) { client, err := gnmi.Dial(ctx.Value("config").(*gnmi.Config)) @@ -33,7 +65,7 @@ func (g *Gnmi) Capabilities(ctx context.Context) (interface{}, error) { } // Get calls GNMI get -func (g *Gnmi) Get(ctx context.Context, paths [][]string, origin string) (interface{}, error) { +func (g *Gnmi) get(ctx context.Context, paths [][]string, origin string) (interface{}, error) { req, err := gnmi.NewGetRequest(paths, origin) if err != nil { return nil, err @@ -56,7 +88,7 @@ func GetWithRequest(ctx context.Context, req *gpb.GetRequest) (interface{}, erro } // Set calls GNMI set -func (g *Gnmi) Set(ctx context.Context, setOps []*gnmi.Operation, +func (g *Gnmi) set(ctx context.Context, setOps []*gnmi.Operation, exts ...*gnmi_ext.Extension) error { client, err := gnmi.Dial(ctx.Value("config").(*gnmi.Config)) if err != nil { @@ -66,13 +98,21 @@ func (g *Gnmi) Set(ctx context.Context, setOps []*gnmi.Operation, } // Subscribe calls GNMI subscribe -func (g *Gnmi) Subscribe(ctx context.Context, subscribeOptions *gnmi.SubscribeOptions, - respChan chan<- *gpb.SubscribeResponse) error { +func (g *Gnmi) subscribe(ctx context.Context) error { client, err := gnmi.Dial(ctx.Value("config").(*gnmi.Config)) if err != nil { return err } - return gnmi.SubscribeErr(ctx, client, subscribeOptions, respChan) + opts := ctx.Value("opts").(*gnmi.SubscribeOptions) + go func() { + for { + resp := <- g.RespChan + if err := gnmi.LogSubscribeResponse(resp); err != nil { + log.Fatal(err) + } + } + }() + return gnmi.SubscribeErr(ctx, client, opts, g.RespChan) } // Close calls GNMI close diff --git a/nucleus/southbound.go b/nucleus/southbound.go index 1d2583e02423137e3f3a0156403dd546730f5725..4924845ac7d1fe8ffb95724f176288c12957f628 100644 --- a/nucleus/southbound.go +++ b/nucleus/southbound.go @@ -1,8 +1,11 @@ package nucleus import ( - "code.fbi.h-da.de/cocsn/gosdn/nucleus/util" + "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" + gpb "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/ytypes" ) @@ -12,7 +15,8 @@ type SouthboundInterface interface { // Deprecated SbiIdentifier() string - Schema() (*ytypes.Schema, error) + SetNode() func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error + Schema() *ytypes.Schema } type Tapi struct { @@ -27,21 +31,45 @@ func (oc *OpenConfig) SbiIdentifier() string { return "openconfig" } -func (oc *OpenConfig) Schema() (*ytypes.Schema, error) { - return openconfig.Schema() -} - -func (oc *OpenConfig) OpenconfigInterfaces(device Device) { - resp, err := oc.Transport.Get(nil, nil...) +func (oc *OpenConfig) Schema() *ytypes.Schema { + schema, err := openconfig.Schema() if err != nil { + log.Fatal(err) + } + return schema +} +func (oc *OpenConfig) 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.(*openconfig.Device), path, val, opts...); err != nil { + return err + } + return nil } - device.Add(resp) } -func (oc *OpenConfig) GetFullDeviceInfo(device Device) error { - paths := util.NewPaths() - paths.ParseSchema(oc.schema, "device") +type AristaOC struct { + transport Transport + schema *ytypes.Schema +} - return nil +func (oc *AristaOC) SbiIdentifier() string { + return "arista" +} + +func (oc *AristaOC) Schema() *ytypes.Schema { + schema, err := arista.Schema() + if err != nil { + log.Fatal(err) + } + return schema +} + +func (oc *AristaOC) 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 + } + return nil + } } diff --git a/nucleus/transport.go b/nucleus/transport.go index 809ddd603ba4143c97e1fead882b6c620ddb8efe..b3e397890bbd00ccd6082b1dd344f142ccdcc1d6 100644 --- a/nucleus/transport.go +++ b/nucleus/transport.go @@ -3,6 +3,7 @@ package nucleus import ( "bytes" "context" + "github.com/openconfig/ygot/ytypes" "io" ) @@ -13,6 +14,8 @@ type Transport interface { Get(ctx context.Context, params ...string) (interface{}, error) Set(ctx context.Context, params ...string) (interface{}, error) Subscribe(ctx context.Context, params ...string) error + Type() string + ProcessResponse(resp interface{},root interface{}, models *ytypes.Schema) error } // YANGConsumer is a auxillary type to redirect the response diff --git a/nucleus/util/path_traversal.go b/nucleus/util/path_traversal.go index 65dd9eeb351898ee2369a550d0e95ce6b47d799c..c9a1ac39d870fffc1d5e400a9715ee1a9830f800 100644 --- a/nucleus/util/path_traversal.go +++ b/nucleus/util/path_traversal.go @@ -93,7 +93,7 @@ func appendix(c chan string, stop chan bool,p chan []string) { paths = append(paths, path) log.Debug(path) case sig = <-stop: - log.Debug("Signal received: %v", sig) + log.Debugf("Signal received: %v", sig) } if sig {break}