ipld-eth-server/vendor/github.com/aristanetworks/goarista/gnmi/operation.go
Matt K 293dd2e848 Add vendor dir (#16) (#4)
* Add vendor dir so builds dont require dep

* Pin specific version go-eth version
2018-01-29 13:44:18 -06:00

252 lines
5.8 KiB
Go

// 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 gnmi
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"path"
pb "github.com/openconfig/gnmi/proto/gnmi"
"google.golang.org/grpc/codes"
)
// Get sents a GetRequest to the given client.
func Get(ctx context.Context, client pb.GNMIClient, paths [][]string) error {
req, err := NewGetRequest(paths)
if err != nil {
return err
}
resp, err := client.Get(ctx, req)
if err != nil {
return err
}
for _, notif := range resp.Notification {
for _, update := range notif.Update {
fmt.Printf("%s:\n", StrPath(update.Path))
fmt.Println(strUpdateVal(update))
}
}
return nil
}
// Capabilities retuns the capabilities of the client.
func Capabilities(ctx context.Context, client pb.GNMIClient) error {
resp, err := client.Capabilities(ctx, &pb.CapabilityRequest{})
if err != nil {
return err
}
fmt.Printf("Version: %s\n", resp.GNMIVersion)
for _, mod := range resp.SupportedModels {
fmt.Printf("SupportedModel: %s\n", mod)
}
for _, enc := range resp.SupportedEncodings {
fmt.Printf("SupportedEncoding: %s\n", enc)
}
return nil
}
// val may be a path to a file or it may be json. First see if it is a
// file, if so return its contents, otherwise return val
func extractJSON(val string) []byte {
jsonBytes, err := ioutil.ReadFile(val)
if err != nil {
jsonBytes = []byte(val)
}
return jsonBytes
}
// strUpdateVal will return a string representing the value within the supplied update
func strUpdateVal(u *pb.Update) string {
if u.Value != nil {
return string(u.Value.Value) // Backwards compatibility with pre-v0.4 gnmi
}
return strVal(u.Val)
}
// strVal will return a string representing the supplied value
func strVal(val *pb.TypedValue) string {
switch v := val.GetValue().(type) {
case *pb.TypedValue_StringVal:
return v.StringVal
case *pb.TypedValue_JsonIetfVal:
return string(v.JsonIetfVal)
case *pb.TypedValue_IntVal:
return fmt.Sprintf("%v", v.IntVal)
case *pb.TypedValue_UintVal:
return fmt.Sprintf("%v", v.UintVal)
case *pb.TypedValue_BoolVal:
return fmt.Sprintf("%v", v.BoolVal)
case *pb.TypedValue_BytesVal:
return string(v.BytesVal)
case *pb.TypedValue_DecimalVal:
return strDecimal64(v.DecimalVal)
case *pb.TypedValue_LeaflistVal:
return strLeaflist(v.LeaflistVal)
default:
panic(v)
}
}
func strDecimal64(d *pb.Decimal64) string {
var i, frac uint64
if d.Precision > 0 {
div := uint64(10)
it := d.Precision - 1
for it > 0 {
div *= 10
it--
}
i = d.Digits / div
frac = d.Digits % div
} else {
i = d.Digits
}
return fmt.Sprintf("%d.%d", i, frac)
}
// strLeafList builds a human-readable form of a leaf-list. e.g. [1,2,3] or [a,b,c]
func strLeaflist(v *pb.ScalarArray) string {
s := make([]string, 0, len(v.Element))
sz := 2 // []
// convert arbitrary TypedValues to string form
for _, elm := range v.Element {
str := strVal(elm)
s = append(s, str)
sz += len(str) + 1 // %v + ,
}
b := make([]byte, sz)
buf := bytes.NewBuffer(b)
buf.WriteRune('[')
for i := range v.Element {
buf.WriteString(s[i])
if i < len(v.Element)-1 {
buf.WriteRune(',')
}
}
buf.WriteRune(']')
return buf.String()
}
func update(p *pb.Path, val string) *pb.Update {
var v *pb.TypedValue
switch p.Origin {
case "":
v = &pb.TypedValue{
Value: &pb.TypedValue_JsonIetfVal{JsonIetfVal: extractJSON(val)}}
case "cli":
v = &pb.TypedValue{
Value: &pb.TypedValue_AsciiVal{AsciiVal: val}}
default:
panic(fmt.Errorf("unexpected origin: %q", p.Origin))
}
return &pb.Update{Path: p, Val: v}
}
// Operation describes an gNMI operation.
type Operation struct {
Type string
Path []string
Val string
}
func newSetRequest(setOps []*Operation) (*pb.SetRequest, error) {
req := &pb.SetRequest{}
for _, op := range setOps {
p, err := ParseGNMIElements(op.Path)
if err != nil {
return nil, err
}
switch op.Type {
case "delete":
req.Delete = append(req.Delete, p)
case "update":
req.Update = append(req.Update, update(p, op.Val))
case "replace":
req.Replace = append(req.Replace, update(p, op.Val))
}
}
return req, nil
}
// Set sends a SetRequest to the given client.
func Set(ctx context.Context, client pb.GNMIClient, setOps []*Operation) error {
req, err := newSetRequest(setOps)
if err != nil {
return err
}
resp, err := client.Set(ctx, req)
if err != nil {
return err
}
if resp.Message != nil && codes.Code(resp.Message.Code) != codes.OK {
return errors.New(resp.Message.Message)
}
// TODO: Iterate over SetResponse.Response for more detailed error message?
return nil
}
// Subscribe sends a SubscribeRequest to the given client.
func Subscribe(ctx context.Context, client pb.GNMIClient, paths [][]string,
respChan chan<- *pb.SubscribeResponse, errChan chan<- error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
stream, err := client.Subscribe(ctx)
if err != nil {
errChan <- err
return
}
req, err := NewSubscribeRequest(paths)
if err != nil {
errChan <- err
return
}
if err := stream.Send(req); err != nil {
errChan <- err
return
}
for {
resp, err := stream.Recv()
if err != nil {
if err == io.EOF {
return
}
errChan <- err
return
}
respChan <- resp
}
}
// LogSubscribeResponse logs update responses to stderr.
func LogSubscribeResponse(response *pb.SubscribeResponse) error {
switch resp := response.Response.(type) {
case *pb.SubscribeResponse_Error:
return errors.New(resp.Error.Message)
case *pb.SubscribeResponse_SyncResponse:
if !resp.SyncResponse {
return errors.New("initial sync failed")
}
case *pb.SubscribeResponse_Update:
prefix := StrPath(resp.Update.Prefix)
for _, update := range resp.Update.Update {
fmt.Printf("%s = %s\n", path.Join(prefix, StrPath(update.Path)),
strUpdateVal(update))
}
}
return nil
}