github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/cmd/ocsplunk/main.go (about)

     1  // Copyright (c) 2017 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  
     5  package main
     6  
     7  import (
     8  	"context"
     9  	"crypto/tls"
    10  	"flag"
    11  	"fmt"
    12  	"net/http"
    13  	"os"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/aristanetworks/goarista/gnmi"
    18  
    19  	"github.com/aristanetworks/glog"
    20  	hec "github.com/aristanetworks/splunk-hec-go"
    21  	pb "github.com/openconfig/gnmi/proto/gnmi"
    22  	"golang.org/x/sync/errgroup"
    23  )
    24  
    25  func exitWithError(s string) {
    26  	fmt.Fprintln(os.Stderr, s)
    27  	os.Exit(1)
    28  }
    29  
    30  func main() {
    31  	// gNMI options
    32  	cfg := &gnmi.Config{}
    33  	flag.StringVar(&cfg.Addr, "addr", "localhost", "gNMI gRPC server `address`")
    34  	flag.StringVar(&cfg.CAFile, "cafile", "", "Path to server TLS certificate file")
    35  	flag.StringVar(&cfg.CertFile, "certfile", "", "Path to client TLS certificate file")
    36  	flag.StringVar(&cfg.KeyFile, "keyfile", "", "Path to client TLS private key file")
    37  	flag.StringVar(&cfg.Username, "username", "", "Username to authenticate with")
    38  	flag.StringVar(&cfg.Password, "password", "", "Password to authenticate with")
    39  	flag.BoolVar(&cfg.TLS, "tls", false, "Enable TLS")
    40  	flag.StringVar(&cfg.TLSMinVersion, "tls-min-version", "",
    41  		fmt.Sprintf("Set minimum TLS version for connection (%s)", gnmi.TLSVersions))
    42  	flag.StringVar(&cfg.TLSMaxVersion, "tls-max-version", "",
    43  		fmt.Sprintf("Set maximum TLS version for connection (%s)", gnmi.TLSVersions))
    44  	subscribePaths := flag.String("paths", "/", "Comma-separated list of paths to subscribe to")
    45  
    46  	// Splunk options
    47  	splunkURLs := flag.String("splunkurls", "https://localhost:8088",
    48  		"Comma-separated list of URLs of the Splunk servers")
    49  	splunkToken := flag.String("splunktoken", "", "Token to connect to the Splunk servers")
    50  	splunkIndex := flag.String("splunkindex", "", "Index for the data in Splunk")
    51  
    52  	flag.Parse()
    53  
    54  	// gNMI connection
    55  	ctx := gnmi.NewContext(context.Background(), cfg)
    56  	// Store the address without the port so it can be used as the host in the Splunk event.
    57  	addr := cfg.Addr
    58  	client, err := gnmi.Dial(cfg)
    59  	if err != nil {
    60  		glog.Fatal(err)
    61  	}
    62  
    63  	// Splunk connection
    64  	urls := strings.Split(*splunkURLs, ",")
    65  	cluster := hec.NewCluster(urls, *splunkToken)
    66  	cluster.SetHTTPClient(&http.Client{
    67  		Transport: &http.Transport{
    68  			// TODO: add flags for TLS
    69  			TLSClientConfig: &tls.Config{
    70  				// TODO: add flag to enable TLS
    71  				InsecureSkipVerify: true,
    72  			},
    73  		},
    74  	})
    75  
    76  	// gNMI subscription
    77  	respChan := make(chan *pb.SubscribeResponse)
    78  	paths := strings.Split(*subscribePaths, ",")
    79  	subscribeOptions := &gnmi.SubscribeOptions{
    80  		Mode:       "stream",
    81  		StreamMode: "target_defined",
    82  		Paths:      gnmi.SplitPaths(paths),
    83  	}
    84  	var g errgroup.Group
    85  	g.Go(func() error { return gnmi.SubscribeErr(ctx, client, subscribeOptions, respChan) })
    86  
    87  	// Forward subscribe responses to Splunk
    88  	for resp := range respChan {
    89  		// We got a subscribe response
    90  		response := resp.GetResponse()
    91  		update, ok := response.(*pb.SubscribeResponse_Update)
    92  		if !ok {
    93  			continue
    94  		}
    95  
    96  		// Convert the response into a map[string]interface{}
    97  		notification, err := gnmi.NotificationToMap(update.Update)
    98  		if err != nil {
    99  			exitWithError(err.Error())
   100  		}
   101  
   102  		// Build the Splunk event
   103  		path := notification["path"].(string)
   104  		delete(notification, "path")
   105  		timestamp := notification["timestamp"].(int64)
   106  		delete(notification, "timestamp")
   107  		// Should this be configurable?
   108  		sourceType := "openconfig"
   109  		event := &hec.Event{
   110  			Host:       &addr,
   111  			Index:      splunkIndex,
   112  			Source:     &path,
   113  			SourceType: &sourceType,
   114  			Event:      notification,
   115  		}
   116  		event.SetTime(time.Unix(timestamp/1e9, timestamp%1e9))
   117  
   118  		// Write the event to Splunk
   119  		if err := cluster.WriteEvent(event); err != nil {
   120  			exitWithError("failed to write event: " + err.Error())
   121  		}
   122  
   123  	}
   124  	if err := g.Wait(); err != nil {
   125  		exitWithError(err.Error())
   126  	}
   127  }