// Copyright (c) 2017 Arista Networks, Inc. // Use of this source code is governed by the Apache License 2.0 // that can be found in the COPYING file. package main import ( "context" "flag" "fmt" "os" "strings" "time" "github.com/aristanetworks/goarista/gnmi" "github.com/aristanetworks/glog" pb "github.com/openconfig/gnmi/proto/gnmi" ) // TODO: Make this more clear var help = `Usage of gnmi: gnmi -addr [/]ADDRESS:PORT [options...] capabilities get PATH+ subscribe PATH+ ((update|replace (origin=ORIGIN) PATH JSON|FILE)|(delete (origin=ORIGIN) PATH))+ ` func usageAndExit(s string) { flag.Usage() if s != "" { fmt.Fprintln(os.Stderr, s) } os.Exit(1) } func main() { cfg := &gnmi.Config{} flag.StringVar(&cfg.Addr, "addr", "", "Address of gNMI gRPC server with optional VRF name") flag.StringVar(&cfg.CAFile, "cafile", "", "Path to server TLS certificate file") flag.StringVar(&cfg.CertFile, "certfile", "", "Path to client TLS certificate file") flag.StringVar(&cfg.KeyFile, "keyfile", "", "Path to client TLS private key file") flag.StringVar(&cfg.Password, "password", "", "Password to authenticate with") flag.StringVar(&cfg.Username, "username", "", "Username to authenticate with") flag.BoolVar(&cfg.TLS, "tls", false, "Enable TLS") subscribeOptions := &gnmi.SubscribeOptions{} flag.StringVar(&subscribeOptions.Prefix, "prefix", "", "Subscribe prefix path") flag.BoolVar(&subscribeOptions.UpdatesOnly, "updates_only", false, "Subscribe to updates only (false | true)") flag.StringVar(&subscribeOptions.Mode, "mode", "stream", "Subscribe mode (stream | once | poll)") flag.StringVar(&subscribeOptions.StreamMode, "stream_mode", "target_defined", "Subscribe stream mode, only applies for stream subscriptions "+ "(target_defined | on_change | sample)") sampleIntervalStr := flag.String("sample_interval", "0", "Subscribe sample interval, "+ "only applies for sample subscriptions (400ms, 2.5s, 1m, etc.)") heartbeatIntervalStr := flag.String("heartbeat_interval", "0", "Subscribe heartbeat "+ "interval, only applies for on-change subscriptions (400ms, 2.5s, 1m, etc.)") flag.Usage = func() { fmt.Fprintln(os.Stderr, help) flag.PrintDefaults() } flag.Parse() if cfg.Addr == "" { usageAndExit("error: address not specified") } var sampleInterval, heartbeatInterval time.Duration var err error if sampleInterval, err = time.ParseDuration(*sampleIntervalStr); err != nil { usageAndExit(fmt.Sprintf("error: sample interval (%s) invalid", *sampleIntervalStr)) } subscribeOptions.SampleInterval = uint64(sampleInterval) if heartbeatInterval, err = time.ParseDuration(*heartbeatIntervalStr); err != nil { usageAndExit(fmt.Sprintf("error: heartbeat interval (%s) invalid", *heartbeatIntervalStr)) } subscribeOptions.HeartbeatInterval = uint64(heartbeatInterval) args := flag.Args() ctx := gnmi.NewContext(context.Background(), cfg) client, err := gnmi.Dial(cfg) if err != nil { glog.Fatal(err) } var setOps []*gnmi.Operation for i := 0; i < len(args); i++ { switch args[i] { case "capabilities": if len(setOps) != 0 { usageAndExit("error: 'capabilities' not allowed after 'merge|replace|delete'") } err := gnmi.Capabilities(ctx, client) if err != nil { glog.Fatal(err) } return case "get": if len(setOps) != 0 { usageAndExit("error: 'get' not allowed after 'merge|replace|delete'") } err := gnmi.Get(ctx, client, gnmi.SplitPaths(args[i+1:])) if err != nil { glog.Fatal(err) } return case "subscribe": if len(setOps) != 0 { usageAndExit("error: 'subscribe' not allowed after 'merge|replace|delete'") } respChan := make(chan *pb.SubscribeResponse) errChan := make(chan error) defer close(errChan) subscribeOptions.Paths = gnmi.SplitPaths(args[i+1:]) go gnmi.Subscribe(ctx, client, subscribeOptions, respChan, errChan) for { select { case resp, open := <-respChan: if !open { return } if err := gnmi.LogSubscribeResponse(resp); err != nil { glog.Fatal(err) } case err := <-errChan: glog.Fatal(err) } } case "update", "replace", "delete": if len(args) == i+1 { usageAndExit("error: missing path") } op := &gnmi.Operation{ Type: args[i], } i++ if strings.HasPrefix(args[i], "origin=") { op.Origin = strings.TrimPrefix(args[i], "origin=") i++ } op.Path = gnmi.SplitPath(args[i]) if op.Type != "delete" { if len(args) == i+1 { usageAndExit("error: missing JSON or FILEPATH to data") } i++ op.Val = args[i] } setOps = append(setOps, op) default: usageAndExit(fmt.Sprintf("error: unknown operation %q", args[i])) } } if len(setOps) == 0 { usageAndExit("") } err = gnmi.Set(ctx, client, setOps) if err != nil { glog.Fatal(err) } }