Merge pull request #120492 from SuperSandro2000/prometheus-unbound-exporter

Prometheus unbound exporter
This commit is contained in:
WilliButz 2021-04-29 10:54:22 +02:00 committed by GitHub
commit 674cea17a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 274 additions and 148 deletions

View file

@ -59,6 +59,7 @@ let
"surfboard"
"systemd"
"tor"
"unbound"
"unifi"
"unifi-poller"
"varnish"

View file

@ -0,0 +1,59 @@
{ config, lib, pkgs, options }:
with lib;
let
cfg = config.services.prometheus.exporters.unbound;
in
{
port = 9167;
extraOpts = {
fetchType = mkOption {
# TODO: add shm when upstream implemented it
type = types.enum [ "tcp" "uds" ];
default = "uds";
description = ''
Which methods the exporter uses to get the information from unbound.
'';
};
telemetryPath = mkOption {
type = types.str;
default = "/metrics";
description = ''
Path under which to expose metrics.
'';
};
controlInterface = mkOption {
type = types.nullOr types.str;
default = null;
example = "/run/unbound/unbound.socket";
description = ''
Path to the unbound socket for uds mode or the control interface port for tcp mode.
Example:
uds-mode: /run/unbound/unbound.socket
tcp-mode: 127.0.0.1:8953
'';
};
};
serviceOpts = mkMerge ([{
serviceConfig = {
ExecStart = ''
${pkgs.prometheus-unbound-exporter}/bin/unbound-telemetry \
${cfg.fetchType} \
--bind ${cfg.listenAddress}:${toString cfg.port} \
--path ${cfg.telemetryPath} \
${optionalString (cfg.controlInterface != null) "--control-interface ${cfg.controlInterface}"} \
${toString cfg.extraFlags}
'';
};
}] ++ [
(mkIf config.services.unbound.enable {
after = [ "unbound.service" ];
requires = [ "unbound.service" ];
})
]);
}

View file

@ -1,65 +1,65 @@
{ system ? builtins.currentSystem
, config ? {}
, config ? { }
, pkgs ? import ../.. { inherit system config; }
}:
let
inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
inherit (pkgs.lib) concatStringsSep maintainers mapAttrs mkMerge
removeSuffix replaceChars singleton splitString;
removeSuffix replaceChars singleton splitString;
/*
* The attrset `exporterTests` contains one attribute
* for each exporter test. Each of these attributes
* is expected to be an attrset containing:
*
* `exporterConfig`:
* this attribute set contains config for the exporter itself
*
* `exporterTest`
* this attribute set contains test instructions
*
* `metricProvider` (optional)
* this attribute contains additional machine config
*
* `nodeName` (optional)
* override an incompatible testnode name
*
* Example:
* exporterTests.<exporterName> = {
* exporterConfig = {
* enable = true;
* };
* metricProvider = {
* services.<metricProvider>.enable = true;
* };
* exporterTest = ''
* wait_for_unit("prometheus-<exporterName>-exporter.service")
* wait_for_open_port("1234")
* succeed("curl -sSf 'localhost:1234/metrics'")
* '';
* };
*
* # this would generate the following test config:
*
* nodes.<exporterName> = {
* services.prometheus.<exporterName> = {
* enable = true;
* };
* services.<metricProvider>.enable = true;
* };
*
* testScript = ''
* <exporterName>.start()
* <exporterName>.wait_for_unit("prometheus-<exporterName>-exporter.service")
* <exporterName>.wait_for_open_port("1234")
* <exporterName>.succeed("curl -sSf 'localhost:1234/metrics'")
* <exporterName>.shutdown()
* '';
*/
/*
* The attrset `exporterTests` contains one attribute
* for each exporter test. Each of these attributes
* is expected to be an attrset containing:
*
* `exporterConfig`:
* this attribute set contains config for the exporter itself
*
* `exporterTest`
* this attribute set contains test instructions
*
* `metricProvider` (optional)
* this attribute contains additional machine config
*
* `nodeName` (optional)
* override an incompatible testnode name
*
* Example:
* exporterTests.<exporterName> = {
* exporterConfig = {
* enable = true;
* };
* metricProvider = {
* services.<metricProvider>.enable = true;
* };
* exporterTest = ''
* wait_for_unit("prometheus-<exporterName>-exporter.service")
* wait_for_open_port("1234")
* succeed("curl -sSf 'localhost:1234/metrics'")
* '';
* };
*
* # this would generate the following test config:
*
* nodes.<exporterName> = {
* services.prometheus.<exporterName> = {
* enable = true;
* };
* services.<metricProvider>.enable = true;
* };
*
* testScript = ''
* <exporterName>.start()
* <exporterName>.wait_for_unit("prometheus-<exporterName>-exporter.service")
* <exporterName>.wait_for_open_port("1234")
* <exporterName>.succeed("curl -sSf 'localhost:1234/metrics'")
* <exporterName>.shutdown()
* '';
*/
exporterTests = {
apcupsd = {
apcupsd = {
exporterConfig = {
enable = true;
};
@ -192,20 +192,21 @@ let
"plugin":"testplugin",
"time":DATE
}]
''; in ''
wait_for_unit("prometheus-collectd-exporter.service")
wait_for_open_port(9103)
succeed(
'echo \'${postData}\'> /tmp/data.json'
)
succeed('sed -ie "s DATE $(date +%s) " /tmp/data.json')
succeed(
"curl -sSfH 'Content-Type: application/json' -X POST --data @/tmp/data.json localhost:9103/collectd"
)
succeed(
"curl -sSf localhost:9103/metrics | grep -q 'collectd_testplugin_gauge{instance=\"testhost\"} 23'"
)
'';
''; in
''
wait_for_unit("prometheus-collectd-exporter.service")
wait_for_open_port(9103)
succeed(
'echo \'${postData}\'> /tmp/data.json'
)
succeed('sed -ie "s DATE $(date +%s) " /tmp/data.json')
succeed(
"curl -sSfH 'Content-Type: application/json' -X POST --data @/tmp/data.json localhost:9103/collectd"
)
succeed(
"curl -sSf localhost:9103/metrics | grep -q 'collectd_testplugin_gauge{instance=\"testhost\"} 23'"
)
'';
};
dnsmasq = {
@ -258,7 +259,8 @@ let
'';
};
fritzbox = { # TODO add proper test case
fritzbox = {
# TODO add proper test case
exporterConfig = {
enable = true;
};
@ -377,19 +379,19 @@ let
'';
systemd.services.lnd = {
serviceConfig.ExecStart = ''
${pkgs.lnd}/bin/lnd \
--datadir=/var/lib/lnd \
--tlscertpath=/var/lib/lnd/tls.cert \
--tlskeypath=/var/lib/lnd/tls.key \
--logdir=/var/log/lnd \
--bitcoin.active \
--bitcoin.mainnet \
--bitcoin.node=bitcoind \
--bitcoind.rpcuser=bitcoinrpc \
--bitcoind.rpcpass=hunter2 \
--bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 \
--bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 \
--readonlymacaroonpath=/var/lib/lnd/readonly.macaroon
${pkgs.lnd}/bin/lnd \
--datadir=/var/lib/lnd \
--tlscertpath=/var/lib/lnd/tls.cert \
--tlskeypath=/var/lib/lnd/tls.key \
--logdir=/var/log/lnd \
--bitcoin.active \
--bitcoin.mainnet \
--bitcoin.node=bitcoind \
--bitcoind.rpcuser=bitcoinrpc \
--bitcoind.rpcpass=hunter2 \
--bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 \
--bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 \
--readonlymacaroonpath=/var/lib/lnd/readonly.macaroon
'';
serviceConfig.StateDirectory = "lnd";
wantedBy = [ "multi-user.target" ];
@ -411,14 +413,14 @@ let
configuration = {
monitoringInterval = "2s";
mailCheckTimeout = "10s";
servers = [ {
servers = [{
name = "testserver";
server = "localhost";
port = 25;
from = "mail-exporter@localhost";
to = "mail-exporter@localhost";
detectionDir = "/var/spool/mail/mail-exporter/new";
} ];
}];
};
};
metricProvider = {
@ -520,15 +522,17 @@ let
url = "http://localhost";
};
metricProvider = {
systemd.services.nc-pwfile = let
passfile = (pkgs.writeText "pwfile" "snakeoilpw");
in {
requiredBy = [ "prometheus-nextcloud-exporter.service" ];
before = [ "prometheus-nextcloud-exporter.service" ];
serviceConfig.ExecStart = ''
${pkgs.coreutils}/bin/install -o nextcloud-exporter -m 0400 ${passfile} /var/nextcloud-pwfile
'';
};
systemd.services.nc-pwfile =
let
passfile = (pkgs.writeText "pwfile" "snakeoilpw");
in
{
requiredBy = [ "prometheus-nextcloud-exporter.service" ];
before = [ "prometheus-nextcloud-exporter.service" ];
serviceConfig.ExecStart = ''
${pkgs.coreutils}/bin/install -o nextcloud-exporter -m 0400 ${passfile} /var/nextcloud-pwfile
'';
};
services.nginx = {
enable = true;
virtualHosts."localhost" = {
@ -585,7 +589,7 @@ let
syslog = {
listen_address = "udp://127.0.0.1:10000";
format = "rfc3164";
tags = ["nginx"];
tags = [ "nginx" ];
};
};
}
@ -705,10 +709,10 @@ let
exporterConfig = {
enable = true;
group = "openvpn";
statusPaths = ["/run/openvpn-test"];
statusPaths = [ "/run/openvpn-test" ];
};
metricProvider = {
users.groups.openvpn = {};
users.groups.openvpn = { };
services.openvpn.servers.test = {
config = ''
dev tun
@ -828,19 +832,21 @@ let
};
metricProvider = {
# Mock rtl_433 binary to return a dummy metric stream.
nixpkgs.overlays = [ (self: super: {
rtl_433 = self.runCommand "rtl_433" {} ''
mkdir -p "$out/bin"
cat <<EOF > "$out/bin/rtl_433"
#!/bin/sh
while true; do
printf '{"time" : "2020-04-26 13:37:42", "model" : "zopieux", "id" : 55, "channel" : 3, "temperature_C" : 18.000}\n'
sleep 4
done
EOF
chmod +x "$out/bin/rtl_433"
'';
}) ];
nixpkgs.overlays = [
(self: super: {
rtl_433 = self.runCommand "rtl_433" { } ''
mkdir -p "$out/bin"
cat <<EOF > "$out/bin/rtl_433"
#!/bin/sh
while true; do
printf '{"time" : "2020-04-26 13:37:42", "model" : "zopieux", "id" : 55, "channel" : 3, "temperature_C" : 18.000}\n'
sleep 4
done
EOF
chmod +x "$out/bin/rtl_433"
'';
})
];
};
exporterTest = ''
wait_for_unit("prometheus-rtl_433-exporter.service")
@ -856,7 +862,7 @@ let
smokeping = {
exporterConfig = {
enable = true;
hosts = ["127.0.0.1"];
hosts = [ "127.0.0.1" ];
};
exporterTest = ''
wait_for_unit("prometheus-smokeping-exporter.service")
@ -994,7 +1000,7 @@ let
unifi-poller = {
nodeName = "unifi_poller";
exporterConfig.enable = true;
exporterConfig.controllers = [ { } ];
exporterConfig.controllers = [{ }];
exporterTest = ''
wait_for_unit("prometheus-unifi-poller-exporter.service")
wait_for_open_port(9130)
@ -1004,6 +1010,29 @@ let
'';
};
unbound = {
exporterConfig = {
enable = true;
fetchType = "uds";
controlInterface = "/run/unbound/unbound.ctl";
};
metricProvider = {
services.unbound = {
enable = true;
localControlSocketPath = "/run/unbound/unbound.ctl";
};
systemd.services.prometheus-unbound-exporter.serviceConfig = {
SupplementaryGroups = [ "unbound" ];
};
};
exporterTest = ''
wait_for_unit("unbound.service")
wait_for_unit("prometheus-unbound-exporter.service")
wait_for_open_port(9167)
succeed("curl -sSf localhost:9167/metrics | grep -q 'unbound_up 1'")
'';
};
varnish = {
exporterConfig = {
enable = true;
@ -1033,54 +1062,60 @@ let
'';
};
wireguard = let snakeoil = import ./wireguard/snakeoil-keys.nix; in {
exporterConfig.enable = true;
metricProvider = {
networking.wireguard.interfaces.wg0 = {
ips = [ "10.23.42.1/32" "fc00::1/128" ];
listenPort = 23542;
wireguard = let snakeoil = import ./wireguard/snakeoil-keys.nix; in
{
exporterConfig.enable = true;
metricProvider = {
networking.wireguard.interfaces.wg0 = {
ips = [ "10.23.42.1/32" "fc00::1/128" ];
listenPort = 23542;
inherit (snakeoil.peer0) privateKey;
inherit (snakeoil.peer0) privateKey;
peers = singleton {
allowedIPs = [ "10.23.42.2/32" "fc00::2/128" ];
peers = singleton {
allowedIPs = [ "10.23.42.2/32" "fc00::2/128" ];
inherit (snakeoil.peer1) publicKey;
inherit (snakeoil.peer1) publicKey;
};
};
systemd.services.prometheus-wireguard-exporter.after = [ "wireguard-wg0.service" ];
};
systemd.services.prometheus-wireguard-exporter.after = [ "wireguard-wg0.service" ];
exporterTest = ''
wait_for_unit("prometheus-wireguard-exporter.service")
wait_for_open_port(9586)
wait_until_succeeds(
"curl -sSf http://localhost:9586/metrics | grep '${snakeoil.peer1.publicKey}'"
)
'';
};
exporterTest = ''
wait_for_unit("prometheus-wireguard-exporter.service")
wait_for_open_port(9586)
wait_until_succeeds(
"curl -sSf http://localhost:9586/metrics | grep '${snakeoil.peer1.publicKey}'"
)
'';
};
};
in
mapAttrs (exporter: testConfig: (makeTest (let
nodeName = testConfig.nodeName or exporter;
mapAttrs
(exporter: testConfig: (makeTest (
let
nodeName = testConfig.nodeName or exporter;
in {
name = "prometheus-${exporter}-exporter";
in
{
name = "prometheus-${exporter}-exporter";
nodes.${nodeName} = mkMerge [{
services.prometheus.exporters.${exporter} = testConfig.exporterConfig;
} testConfig.metricProvider or {}];
nodes.${nodeName} = mkMerge [{
services.prometheus.exporters.${exporter} = testConfig.exporterConfig;
} testConfig.metricProvider or { }];
testScript = ''
${nodeName}.start()
${concatStringsSep "\n" (map (line:
if (builtins.substring 0 1 line == " " || builtins.substring 0 1 line == ")")
then line
else "${nodeName}.${line}"
) (splitString "\n" (removeSuffix "\n" testConfig.exporterTest)))}
${nodeName}.shutdown()
'';
testScript = ''
${nodeName}.start()
${concatStringsSep "\n" (map (line:
if (builtins.substring 0 1 line == " " || builtins.substring 0 1 line == ")")
then line
else "${nodeName}.${line}"
) (splitString "\n" (removeSuffix "\n" testConfig.exporterTest)))}
${nodeName}.shutdown()
'';
meta = with maintainers; {
maintainers = [ willibutz elseym ];
};
}))) exporterTests
meta = with maintainers; {
maintainers = [ willibutz elseym ];
};
}
)))
exporterTests

View file

@ -0,0 +1,30 @@
{ lib, rustPlatform, fetchFromGitHub, openssl, pkg-config, nixosTests }:
rustPlatform.buildRustPackage rec {
pname = "unbound-telemetry";
version = "unstable-2021-03-17";
src = fetchFromGitHub {
owner = "svartalf";
repo = pname;
rev = "7f1b6d4e9e4b6a3216a78c23df745bcf8fc84021";
sha256 = "xCelL6WGaTRhDJkkUdpdwj1zcKKAU2dyUv3mHeI4oAw=";
};
cargoSha256 = "P3nAtYOuwNSLMP7q1L5zKTsZ6rJA/qL1mhVHzP3szi4=";
nativeBuildInputs = [ pkg-config ];
buildInputs = [ openssl ];
passthru.tests = {
inherit (nixosTests.prometheus-exporters) unbound;
};
meta = with lib; {
description = "Prometheus exporter for Unbound DNS resolver";
homepage = "https://github.com/svartalf/unbound-telemetry";
license = licenses.mit;
maintainers = with maintainers; [ SuperSandro2000 ];
};
}

View file

@ -19184,6 +19184,7 @@ in
prometheus-tor-exporter = callPackage ../servers/monitoring/prometheus/tor-exporter.nix { };
prometheus-statsd-exporter = callPackage ../servers/monitoring/prometheus/statsd-exporter.nix { };
prometheus-surfboard-exporter = callPackage ../servers/monitoring/prometheus/surfboard-exporter.nix { };
prometheus-unbound-exporter = callPackage ../servers/monitoring/prometheus/unbound-exporter.nix { };
prometheus-unifi-exporter = callPackage ../servers/monitoring/prometheus/unifi-exporter { };
prometheus-varnish-exporter = callPackage ../servers/monitoring/prometheus/varnish-exporter.nix { };
prometheus-jmx-httpserver = callPackage ../servers/monitoring/prometheus/jmx-httpserver.nix { };