Skip to content

Commit 39cf750

Browse files
Merge pull request #15 from williamchanrico/major_refactor_inventory
Add support for longest-prefix CIDR address inventory match
2 parents d88757a + 4cbe1fc commit 39cf750

File tree

11 files changed

+408
-117
lines changed

11 files changed

+408
-117
lines changed

Makefile

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
NAME := planet-exporter
44
BIN_DIRECTORY := ./bin
55
REVISION := $(shell git rev-parse --short HEAD 2>/dev/null)
6-
VERSION := v0.2.0-dev
6+
VERSION := v0.3.0-dev
77

88
ifndef REVISION
99
override REVISION = none
@@ -66,6 +66,10 @@ go-build-linux: ## Build the app for linux platforms exclude: "arm64" CGO errors
6666
done;
6767
@rm -rf $(BIN_DIRECTORY)/$(APP_NAME)
6868

69+
.PHONY: go-test
70+
go-test: ## Run go test
71+
go test -v ./...
72+
6973
# DOCKER TASKS
7074
# Build the container
7175
.PHONY: docker-build

README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,12 @@ planet-exporter \
137137

138138
#### Inventory
139139

140-
Query inventory data to map IP into `hostgroup` (an identifier based on ansible convention) and `domain`.
140+
Query inventory data that will be used to map `ip_address` into `hostgroup` (an identifier based on Ansible convention) and `domain`.
141+
The `ip_address` may use CIDR notation (e.g. "10.1.0.0/16") and Inventory task will use the longest-prefix match.
141142

142-
Without this task enabled, those hostgroup and domain fields will be left empty.
143+
Without this task enabled, those hostgroup and domain fields will be empty.
143144

144-
The flag `--task-inventory-addr` should contain an HTTP endpoint that returns inventory data in the supported format:
145+
The flag `--task-inventory-addr` accepts an HTTP endpoint that returns inventory data in the supported format:
145146

146147
##### --task-inventory-format=arrayjson
147148

@@ -156,6 +157,11 @@ The flag `--task-inventory-addr` should contain an HTTP endpoint that returns in
156157
"ip_address": "10.2.3.4",
157158
"domain": "debugapp.service.consul",
158159
"hostgroup": "debugapp"
160+
},
161+
{
162+
"ip_address": "10.3.0.0/16",
163+
"domain": "",
164+
"hostgroup": "unknown-but-its-network-xyz"
159165
}
160166
]
161167
```
@@ -165,6 +171,7 @@ The flag `--task-inventory-addr` should contain an HTTP endpoint that returns in
165171
```json
166172
{"ip_address":"10.0.1.2","domain":"xyz.service.consul","hostgroup":"xyz"}
167173
{"ip_address":"172.16.1.2","domain":"abc.service.consul","hostgroup":"abc"}
174+
{"ip_address":"10.3.0.0/16","domain":"","hostgroup":"unknown-but-its-network-xyz"}
168175
```
169176

170177
#### Socketstat

cmd/planet-exporter/internal/internal.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (s Service) collect(ctx context.Context, interval time.Duration) {
160160
log.Infof("Task Darkstat: %v", s.Config.TaskDarkstatEnabled)
161161
taskdarkstat.InitTask(ctx, s.Config.TaskDarkstatEnabled, s.Config.TaskDarkstatAddr)
162162

163-
log.Infof("Task ebpf: %v", s.Config.TaskEbpfEnabled)
163+
log.Infof("Task EBPF: %v", s.Config.TaskEbpfEnabled)
164164
taskebpf.InitTask(ctx, s.Config.TaskEbpfEnabled, s.Config.TaskEbpfAddr)
165165

166166
log.Infof("Task Inventory: %v", s.Config.TaskInventoryEnabled)

collector/task/darkstat/darkstat.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -155,24 +155,25 @@ func toHostMetrics(darkstatHostBytesTotal *prom2json.Family) ([]Metric, error) {
155155
// To label source traffic that we need to build dependency graph
156156
localHostgroup := localAddr.String()
157157
localDomain := localAddr.String()
158-
localInventory, ok := inventoryHosts[localAddr.String()]
158+
localInventory, ok := inventoryHosts.GetHost(localAddr.String())
159159
if ok {
160160
localHostgroup = localInventory.Hostgroup
161161
localDomain = localInventory.Domain
162+
} else {
163+
log.Warnf("Local address don't exist in inventory: %v", localAddr.String())
162164
}
163-
log.Debugf("Local address don't exist in inventory: %v", localAddr.String())
164165

165166
for _, m := range darkstatHostBytesTotal.Metrics {
166167
metric := m.(prom2json.Metric)
167168

168-
ip := net.ParseIP(metric.Labels["ip"])
169-
170-
// Skip its own IP as we don't need it
171-
if ip.Equal(nil) || ip.Equal(localAddr) {
169+
// Skip its own IP.
170+
// We're not interested in traffic coming from and going to itself.
171+
remoteIP := net.ParseIP(metric.Labels["ip"])
172+
if remoteIP.Equal(nil) || remoteIP.Equal(localAddr) {
172173
continue
173174
}
174175

175-
inventoryHostInfo := inventoryHosts[metric.Labels["ip"]]
176+
remoteInventoryHost, _ := inventoryHosts.GetHost(metric.Labels["ip"])
176177

177178
bandwidth, err := strconv.ParseFloat(metric.Value, 64)
178179
if err != nil {
@@ -191,10 +192,10 @@ func toHostMetrics(darkstatHostBytesTotal *prom2json.Family) ([]Metric, error) {
191192

192193
hosts = append(hosts, Metric{
193194
LocalHostgroup: localHostgroup,
194-
RemoteHostgroup: inventoryHostInfo.Hostgroup,
195+
RemoteHostgroup: remoteInventoryHost.Hostgroup,
195196
RemoteIPAddr: metric.Labels["ip"],
196197
LocalDomain: localDomain,
197-
RemoteDomain: inventoryHostInfo.Domain,
198+
RemoteDomain: remoteInventoryHost.Domain,
198199
Direction: direction,
199200
Bandwidth: bandwidth,
200201
})

collector/task/ebpf/tcptop.go

+10-7
Original file line numberDiff line numberDiff line change
@@ -176,22 +176,25 @@ func toHostMetrics(bytesMetric *prom2json.Family, direction string) ([]Metric, e
176176
// To label source traffic that we need to build dependency graph.
177177
localHostgroup := localAddr.String()
178178
localDomain := localAddr.String()
179-
localInventory, ok := inventoryHosts[localAddr.String()]
179+
localInventory, ok := inventoryHosts.GetHost(localAddr.String())
180180
if ok {
181181
localHostgroup = localInventory.Hostgroup
182182
localDomain = localInventory.Domain
183+
} else {
184+
log.Warnf("Local address doesn't exist in the inventory: %v", localAddr.String())
183185
}
184-
log.Debugf("Local address doesn't exist in the inventory: %v", localAddr.String())
185186

186187
for _, m := range bytesMetric.Metrics {
187188
metric := m.(prom2json.Metric)
188-
destIP := net.ParseIP(metric.Labels["daddr"])
189189

190-
if destIP.Equal(nil) || destIP.Equal(localAddr) {
190+
// Skip its own IP.
191+
// We're not interested in traffic coming from and going to itself.
192+
remoteIP := net.ParseIP(metric.Labels["daddr"])
193+
if remoteIP.Equal(nil) || remoteIP.Equal(localAddr) {
191194
continue
192195
}
193196

194-
inventoryHostInfo := inventoryHosts[metric.Labels["daddr"]]
197+
remoteInventoryHost, _ := inventoryHosts.GetHost(metric.Labels["daddr"])
195198

196199
bandwidth, err := strconv.ParseFloat(metric.Value, 64)
197200
if err != nil {
@@ -201,10 +204,10 @@ func toHostMetrics(bytesMetric *prom2json.Family, direction string) ([]Metric, e
201204

202205
hosts = append(hosts, Metric{
203206
LocalHostgroup: localHostgroup,
204-
RemoteHostgroup: inventoryHostInfo.Hostgroup,
207+
RemoteHostgroup: remoteInventoryHost.Hostgroup,
205208
RemoteIPAddr: metric.Labels["daddr"],
206209
LocalDomain: localDomain,
207-
RemoteDomain: inventoryHostInfo.Domain,
210+
RemoteDomain: remoteInventoryHost.Domain,
208211
Direction: direction,
209212
Bandwidth: bandwidth,
210213
})

collector/task/inventory/host.go

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package inventory
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"io"
7+
"io/ioutil"
8+
"net/http"
9+
10+
log "github.com/sirupsen/logrus"
11+
)
12+
13+
// Host contains an inventory entry
14+
type Host struct {
15+
Domain string `json:"domain"`
16+
Hostgroup string `json:"hostgroup"`
17+
IPAddress string `json:"ip_address"`
18+
}
19+
20+
// requestHosts requests a new inventory host entries from upstream inventoryAddr
21+
func requestHosts(ctx context.Context, httpClient *http.Client, inventoryFormat, inventoryAddr string) ([]Host, error) {
22+
request, err := http.NewRequestWithContext(ctx, http.MethodGet, inventoryAddr, nil)
23+
if err != nil {
24+
return nil, err
25+
}
26+
response, err := httpClient.Do(request)
27+
if err != nil {
28+
return nil, err
29+
}
30+
defer response.Body.Close()
31+
32+
return parseHosts(inventoryFormat, response.Body)
33+
}
34+
35+
// parseHosts parses inventory data as a list of Host
36+
func parseHosts(format string, data io.Reader) ([]Host, error) {
37+
var result []Host
38+
39+
decoder := json.NewDecoder(data)
40+
decoder.DisallowUnknownFields()
41+
42+
switch format {
43+
case "ndjson":
44+
inventoryEntry := Host{}
45+
for decoder.More() {
46+
err := decoder.Decode(&inventoryEntry)
47+
if err != nil {
48+
log.Errorf("Skip an inventory host entry due to parser error: %v", err)
49+
continue
50+
}
51+
result = append(result, inventoryEntry)
52+
}
53+
54+
case "arrayjson":
55+
err := decoder.Decode(&result)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
// We expect a single JSON array object here. Clear unexpected data that's left in the io.ReadCloser
61+
if decoder.More() {
62+
bytesCopied, _ := io.Copy(ioutil.Discard, data)
63+
log.Warnf("Unexpected remaining data (%v Bytes) while parsing inventory hosts", bytesCopied)
64+
}
65+
}
66+
log.Debugf("Parsed %v inventory hosts", len(result))
67+
68+
return result, nil
69+
}

0 commit comments

Comments
 (0)