client.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. // Copyright (c) 2016 Arista Networks, Inc.
  2. // Use of this source code is governed by the Apache License 2.0
  3. // that can be found in the COPYING file.
  4. // Package client provides helper functions for OpenConfig CLI tools.
  5. package client
  6. import (
  7. "io"
  8. "strings"
  9. "sync"
  10. "notabug.org/themusicgod1/glog"
  11. "github.com/golang/protobuf/proto"
  12. "github.com/openconfig/reference/rpc/openconfig"
  13. "golang.org/x/net/context"
  14. "google.golang.org/grpc"
  15. "google.golang.org/grpc/metadata"
  16. )
  17. const defaultPort = "6030"
  18. // PublishFunc is the method to publish responses
  19. type PublishFunc func(addr string, message proto.Message)
  20. // Client is a connected gRPC client
  21. type Client struct {
  22. client openconfig.OpenConfigClient
  23. ctx context.Context
  24. device string
  25. }
  26. // New creates a new gRPC client and connects it
  27. func New(username, password, addr string, opts []grpc.DialOption) *Client {
  28. device := addr
  29. if !strings.ContainsRune(addr, ':') {
  30. addr += ":" + defaultPort
  31. }
  32. // Make sure we don't move past the grpc.Dial() call until we actually
  33. // established an HTTP/2 connection successfully.
  34. opts = append(opts, grpc.WithBlock(), grpc.WithWaitForHandshake())
  35. conn, err := grpc.Dial(addr, opts...)
  36. if err != nil {
  37. glog.Fatalf("Failed to dial: %s", err)
  38. }
  39. glog.Infof("Connected to %s", addr)
  40. client := openconfig.NewOpenConfigClient(conn)
  41. ctx := context.Background()
  42. if username != "" {
  43. ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(
  44. "username", username,
  45. "password", password))
  46. }
  47. return &Client{
  48. client: client,
  49. device: device,
  50. ctx: ctx,
  51. }
  52. }
  53. // Get sends a get request and returns the responses
  54. func (c *Client) Get(path string) []*openconfig.Notification {
  55. req := &openconfig.GetRequest{
  56. Path: []*openconfig.Path{
  57. {
  58. Element: strings.Split(path, "/"),
  59. },
  60. },
  61. }
  62. response, err := c.client.Get(c.ctx, req)
  63. if err != nil {
  64. glog.Fatalf("Get failed: %s", err)
  65. }
  66. return response.Notification
  67. }
  68. // Subscribe sends subscriptions, and consumes responses.
  69. // The given publish function is used to publish SubscribeResponses received
  70. // for the given subscriptions, when connected to the given host, with the
  71. // given user/pass pair, or the client-side cert specified in the gRPC opts.
  72. // This function does not normally return so it should probably be run in its
  73. // own goroutine. When this function returns, the given WaitGroup is marked
  74. // as done.
  75. func (c *Client) Subscribe(wg *sync.WaitGroup, subscriptions []string,
  76. publish PublishFunc) {
  77. defer wg.Done()
  78. stream, err := c.client.Subscribe(c.ctx)
  79. if err != nil {
  80. glog.Fatalf("Subscribe failed: %s", err)
  81. }
  82. defer stream.CloseSend()
  83. for _, path := range subscriptions {
  84. sub := &openconfig.SubscribeRequest{
  85. Request: &openconfig.SubscribeRequest_Subscribe{
  86. Subscribe: &openconfig.SubscriptionList{
  87. Subscription: []*openconfig.Subscription{
  88. {
  89. Path: &openconfig.Path{Element: strings.Split(path, "/")},
  90. },
  91. },
  92. },
  93. },
  94. }
  95. glog.Infof("Sending subscribe request: %s", sub)
  96. err = stream.Send(sub)
  97. if err != nil {
  98. glog.Fatalf("Failed to subscribe: %s", err)
  99. }
  100. }
  101. for {
  102. resp, err := stream.Recv()
  103. if err != nil {
  104. if err != io.EOF {
  105. glog.Fatalf("Error received from the server: %s", err)
  106. }
  107. return
  108. }
  109. switch resp := resp.Response.(type) {
  110. case *openconfig.SubscribeResponse_SyncResponse:
  111. if !resp.SyncResponse {
  112. panic("initial sync failed," +
  113. " check that you're using a client compatible with the server")
  114. }
  115. }
  116. glog.V(3).Info(resp)
  117. publish(c.device, resp)
  118. }
  119. }