[TASK] add output prometheus-sd (service discovery) (#213)

This commit is contained in:
genofire 2022-04-14 08:04:41 +02:00
parent 1dbd52c5cd
commit 60639f2e63
6 changed files with 259 additions and 1 deletions

View File

@ -27,7 +27,7 @@ func TestReadConfig(t *testing.T) {
assert.Contains(config.Respondd.Sites["ffhb"].Domains, "city")
// Test output plugins
assert.Len(config.Nodes.Output, 5)
assert.Len(config.Nodes.Output, 6)
outputs := config.Nodes.Output["meshviewer"].([]map[string]interface{})
assert.Len(outputs, 1)
meshviewer := outputs[0]

View File

@ -158,6 +158,19 @@ path = "/var/www/html/meshviewer/data/nodelist.json"
# WARNING: if it is not set, it will publish contact information of other persons
no_owner = true
# definition for prometheus-sd.json
[[nodes.output.prometheus-sd]]
enable = true
path = "/var/www/html/meshviewer/data/prometheus-sd.json"
# ip = lates recieved ip, node_id = node id from host
target_address = "ip"
# Labels of the data (optional)
[nodes.output.prometheus-sd.labels]
#labelname1 = "labelvalue 1"
## some useful e.g.:
#hosts = "ffhb"
#service = "yanic"
# definition for raw.json
[[nodes.output.raw]]

View File

@ -545,6 +545,67 @@ path = "/var/www/html/meshviewer/data/nodelist.json"
```
{% endmethod %}
## [[nodes.output.prometheus-sd]]
{% method %}
The Prometheus Service Discovery (SD) output is a output with the list of addresses of the nodes to use them in later exporter by prometheus.
For usage in Prometheus read there Documentation [Use file-based service discovery to discover scrape targets](https://prometheus.io/docs/guides/file-sd/).
{% sample lang="toml" %}
```toml
[[nodes.output.prometheus-sd]]
enable = false
path = "/var/www/html/meshviewer/data/prometheus-sd.json"
target_address = "ip"
[nodes.output.prometheus-sd.labels]
labelname1 = "labelvalue 1"
# some useful e.g.:
hosts = "ffhb"
service = "yanic"
```
{% endmethod %}
### path
{% method %}
The path, where to store prometheus-sd.json
{% sample lang="toml" %}
```toml
path = "/var/www/html/meshviewer/data/prometheus-sd.json"
```
{% endmethod %}
### target_address
{% method %}
In the prometheus-sd.json the usage of which information of the node as targets (address).
Use the `node_id` as value, to put the Node ID into the target list as address.
Use the `ip` as value to put the last IP address into the target list from where the respondd message is recieved (maybe a link-local address).
Default value is `ip`.
{% sample lang="toml" %}
```toml
path = "/var/www/html/meshviewer/data/prometheus-sd.json"
```
{% endmethod %}
### [nodes.output.prometheus-sd.labels]
{% method %}
You could optional set manuelle labels with inserting into a prometheus-sd.json.
Useful if you want to identify the yanic instance when you use multiple own on the same prometheus database (e.g. multisites).
{% sample lang="toml" %}
```toml
labelname1 = "labelvalue 1"
# some useful e.g.:
hosts = "ffhb"
service = "yanic"
```
{% endmethod %}
## [[nodes.output.raw]]
{% method %}
This output takes the respondd response as sent by the node and includes it in a JSON document.

View File

@ -5,6 +5,7 @@ import (
_ "github.com/FreifunkBremen/yanic/output/meshviewer"
_ "github.com/FreifunkBremen/yanic/output/meshviewer-ffrgb"
_ "github.com/FreifunkBremen/yanic/output/nodelist"
_ "github.com/FreifunkBremen/yanic/output/prometheus-sd"
_ "github.com/FreifunkBremen/yanic/output/raw"
_ "github.com/FreifunkBremen/yanic/output/raw-jsonl"
)

View File

@ -0,0 +1,93 @@
package prometheus_sd
import (
"errors"
"yanic/output"
"yanic/runtime"
)
type Output struct {
output.Output
path string
targetType TargetAddressType
labels map[string]interface{}
}
type Config map[string]interface{}
func (c Config) Path() string {
if path, ok := c["path"]; ok {
return path.(string)
}
return ""
}
type TargetAddressType string
const (
TargetAddressIP TargetAddressType = "ip"
TargetAddressNodeID TargetAddressType = "node_id"
)
func (c Config) TargetAddress() TargetAddressType {
if v, ok := c["target_address"]; ok {
return TargetAddressType(v.(string))
}
return TargetAddressIP
}
func (c Config) Labels() map[string]interface{} {
if v, ok := c["labels"]; ok {
return v.(map[string]interface{})
}
return nil
}
func init() {
output.RegisterAdapter("prometheus-sd", Register)
}
func Register(configuration map[string]interface{}) (output.Output, error) {
config := Config(configuration)
if path := config.Path(); path != "" {
return &Output{
path: path,
targetType: config.TargetAddress(),
labels: config.Labels(),
}, nil
}
return nil, errors.New("no path given")
}
type Targets struct {
Targets []string `json:"targets"`
Labels map[string]interface{} `json:"labels,omitempty"`
}
func (o *Output) Save(nodes *runtime.Nodes) {
nodes.RLock()
defer nodes.RUnlock()
targets := &Targets{
Targets: []string{},
Labels: o.labels,
}
if o.targetType == TargetAddressNodeID {
for _, n := range nodes.List {
if ni := n.Nodeinfo; ni != nil {
targets.Targets = append(targets.Targets, ni.NodeID)
}
}
} else {
for _, n := range nodes.List {
if addr := n.Address; addr != nil {
targets.Targets = append(targets.Targets, addr.IP.String())
}
}
}
runtime.SaveJSON([]interface{}{targets}, o.path)
}

View File

@ -0,0 +1,90 @@
package prometheus_sd
import (
"os"
"net"
"testing"
"github.com/stretchr/testify/assert"
"yanic/data"
"yanic/runtime"
)
func TestOutput(t *testing.T) {
assert := assert.New(t)
out, err := Register(map[string]interface{}{})
assert.Error(err)
assert.Nil(out)
nodes := runtime.NewNodes(&runtime.NodesConfig{})
ipAddress, err := net.ResolveUDPAddr("udp6", "[fe80::20de:a:3ac%eth0]:1001")
assert.NoError(err)
nodes.AddNode(&runtime.Node{
Online: true,
Address: ipAddress,
Nodeinfo: &data.Nodeinfo{
NodeID: "node_a",
Network: data.Network{
Mac: "node:a:mac",
Mesh: map[string]*data.NetworkInterface{
"bat0": {
Interfaces: struct {
Wireless []string `json:"wireless,omitempty"`
Other []string `json:"other,omitempty"`
Tunnel []string `json:"tunnel,omitempty"`
}{
Wireless: []string{"node:a:mac:wifi"},
Tunnel: []string{"node:a:mac:vpn"},
Other: []string{"node:a:mac:lan"},
},
},
},
},
},
Neighbours: &data.Neighbours{
NodeID: "node_a",
Batadv: map[string]data.BatadvNeighbours{
"node:a:mac:wifi": {
Neighbours: map[string]data.BatmanLink{
"node:b:mac:wifi": {Tq: 153},
},
},
"node:a:mac:lan": {
Neighbours: map[string]data.BatmanLink{
"node:b:mac:lan": {Tq: 51},
},
},
},
},
})
// IP
out, err = Register(map[string]interface{}{
"path": "/tmp/prometheus_sd.json",
})
os.Remove("/tmp/prometheus_sd.json")
assert.NoError(err)
assert.NotNil(out)
out.Save(nodes)
_, err = os.Stat("/tmp/prometheus_sd.json")
assert.NoError(err)
// NodeID
out, err = Register(map[string]interface{}{
"target_address": "node_id",
"path": "/tmp/prometheus_sd.json",
"labels": map[string]interface{}{
"hosts": "ffhb",
"service": "yanic",
},
})
os.Remove("/tmp/prometheus_sd.json")
assert.NoError(err)
assert.NotNil(out)
out.Save(nodes)
_, err = os.Stat("/tmp/prometheus_sd.json")
assert.NoError(err)
}