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 }