github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/influxlib/lib.go (about)

     1  // Copyright (c) 2018 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 influxlib
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"time"
    11  
    12  	influxdb "github.com/influxdata/influxdb1-client/v2"
    13  )
    14  
    15  // Row is defined as a map where the key (string) is the name of the
    16  // column (field name) and the value is left as an interface to
    17  // accept any value.
    18  type Row map[string]interface{}
    19  
    20  // InfluxDBConnection is an object that the wrapper uses.
    21  // Holds a client of the type v2.Client and the configuration
    22  type InfluxDBConnection struct {
    23  	Client influxdb.Client
    24  	Config *InfluxConfig
    25  }
    26  
    27  // Point represents a datapoint to be written.
    28  // Measurement:
    29  //
    30  //	The measurement to write to
    31  //
    32  // Tags:
    33  //
    34  //	A dictionary of tags in the form string=string
    35  //
    36  // Fields:
    37  //
    38  //	A dictionary of fields(keys) with their associated values
    39  type Point struct {
    40  	Measurement string
    41  	Tags        map[string]string
    42  	Fields      map[string]interface{}
    43  	Timestamp   time.Time
    44  }
    45  
    46  // Connect takes an InfluxConfig and establishes a connection
    47  // to InfluxDB. It returns an InfluxDBConnection structure.
    48  // InfluxConfig may be nil for a default connection.
    49  func Connect(config *InfluxConfig) (*InfluxDBConnection, error) {
    50  	var con influxdb.Client
    51  	var err error
    52  
    53  	switch config.Protocol {
    54  	case HTTP:
    55  		addr := fmt.Sprintf("http://%s:%v", config.Hostname, config.Port)
    56  		con, err = influxdb.NewHTTPClient(influxdb.HTTPConfig{
    57  			Addr:    addr,
    58  			Timeout: 1 * time.Second,
    59  		})
    60  	case UDP:
    61  		addr := fmt.Sprintf("%s:%v", config.Hostname, config.Port)
    62  		con, err = influxdb.NewUDPClient(influxdb.UDPConfig{
    63  			Addr: addr,
    64  		})
    65  	default:
    66  		return nil, errors.New("Invalid Protocol")
    67  	}
    68  
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	return &InfluxDBConnection{Client: con, Config: config}, nil
    74  }
    75  
    76  // RecordBatchPoints takes in a slice of influxlib.Point and writes them to the
    77  // database.
    78  func (conn *InfluxDBConnection) RecordBatchPoints(points []Point) error {
    79  	var err error
    80  	bp, err := influxdb.NewBatchPoints(influxdb.BatchPointsConfig{
    81  		Database:        conn.Config.Database,
    82  		Precision:       "ns",
    83  		RetentionPolicy: conn.Config.RetentionPolicy,
    84  	})
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	var influxPoints []*influxdb.Point
    90  
    91  	for _, p := range points {
    92  		if p.Timestamp.IsZero() {
    93  			p.Timestamp = time.Now()
    94  		}
    95  
    96  		point, err := influxdb.NewPoint(p.Measurement, p.Tags, p.Fields,
    97  			p.Timestamp)
    98  		if err != nil {
    99  			return err
   100  		}
   101  		influxPoints = append(influxPoints, point)
   102  	}
   103  
   104  	bp.AddPoints(influxPoints)
   105  
   106  	if err = conn.Client.Write(bp); err != nil {
   107  		return err
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // WritePoint stores a datapoint to the database.
   114  // Measurement:
   115  //
   116  //	The measurement to write to
   117  //
   118  // Tags:
   119  //
   120  //	A dictionary of tags in the form string=string
   121  //
   122  // Fields:
   123  //
   124  //	A dictionary of fields(keys) with their associated values
   125  func (conn *InfluxDBConnection) WritePoint(measurement string,
   126  	tags map[string]string, fields map[string]interface{}) error {
   127  	return conn.RecordPoint(Point{
   128  		Measurement: measurement,
   129  		Tags:        tags,
   130  		Fields:      fields,
   131  		Timestamp:   time.Now(),
   132  	})
   133  }
   134  
   135  // RecordPoint implements the same as WritePoint but used a point struct
   136  // as the argument instead.
   137  func (conn *InfluxDBConnection) RecordPoint(p Point) error {
   138  	return conn.RecordBatchPoints([]Point{p})
   139  }
   140  
   141  // Query sends a query to the influxCli and returns a slice of
   142  // rows. Rows are of type map[string]interface{}
   143  func (conn *InfluxDBConnection) Query(query string) ([]Row, error) {
   144  	q := influxdb.NewQuery(query, conn.Config.Database, "ns")
   145  	var rows []Row
   146  	var index = 0
   147  
   148  	response, err := conn.Client.Query(q)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	if response.Error() != nil {
   153  		return nil, response.Error()
   154  	}
   155  
   156  	// The intent here is to combine the separate client v2
   157  	// series into a single array. As a result queries that
   158  	// utilize "group by" will be combined into a single
   159  	// array. And the tag value will be added to the query.
   160  	// Similar to what you would expect from a SQL query
   161  	for _, result := range response.Results {
   162  		for _, series := range result.Series {
   163  			columnNames := series.Columns
   164  			for _, row := range series.Values {
   165  				rows = append(rows, make(Row))
   166  				for columnIdx, value := range row {
   167  					rows[index][columnNames[columnIdx]] = value
   168  				}
   169  				for tagKey, tagValue := range series.Tags {
   170  					rows[index][tagKey] = tagValue
   171  				}
   172  				index++
   173  			}
   174  		}
   175  	}
   176  
   177  	return rows, nil
   178  }
   179  
   180  // Close closes the connection opened by Connect()
   181  func (conn *InfluxDBConnection) Close() {
   182  	conn.Client.Close()
   183  }