Merge pull request #96830 from mayflower/unifi-poller
unifi-poller: add service and prometheus-exporter
This commit is contained in:
commit
ef4e81d756
|
@ -554,6 +554,7 @@
|
||||||
./services/monitoring/telegraf.nix
|
./services/monitoring/telegraf.nix
|
||||||
./services/monitoring/thanos.nix
|
./services/monitoring/thanos.nix
|
||||||
./services/monitoring/tuptime.nix
|
./services/monitoring/tuptime.nix
|
||||||
|
./services/monitoring/unifi-poller.nix
|
||||||
./services/monitoring/ups.nix
|
./services/monitoring/ups.nix
|
||||||
./services/monitoring/uptime.nix
|
./services/monitoring/uptime.nix
|
||||||
./services/monitoring/vnstat.nix
|
./services/monitoring/vnstat.nix
|
||||||
|
|
|
@ -46,6 +46,7 @@ let
|
||||||
"surfboard"
|
"surfboard"
|
||||||
"tor"
|
"tor"
|
||||||
"unifi"
|
"unifi"
|
||||||
|
"unifi-poller"
|
||||||
"varnish"
|
"varnish"
|
||||||
"wireguard"
|
"wireguard"
|
||||||
] (name:
|
] (name:
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
{ config, lib, pkgs, options }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.prometheus.exporters.unifi-poller;
|
||||||
|
|
||||||
|
configFile = pkgs.writeText "prometheus-unifi-poller-exporter.json" (generators.toJSON {} {
|
||||||
|
poller = { inherit (cfg.log) debug quiet; };
|
||||||
|
unifi = { inherit (cfg) controllers; };
|
||||||
|
influxdb.disable = true;
|
||||||
|
prometheus = {
|
||||||
|
http_listen = "${cfg.listenAddress}:${toString cfg.port}";
|
||||||
|
report_errors = cfg.log.prometheusErrors;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
in {
|
||||||
|
port = 9130;
|
||||||
|
|
||||||
|
extraOpts = {
|
||||||
|
inherit (options.services.unifi-poller.unifi) controllers;
|
||||||
|
log = {
|
||||||
|
debug = mkEnableOption "debug logging including line numbers, high resolution timestamps, per-device logs.";
|
||||||
|
quiet = mkEnableOption "startup and error logs only.";
|
||||||
|
prometheusErrors = mkEnableOption "emitting errors to prometheus.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceOpts.serviceConfig = {
|
||||||
|
ExecStart = "${pkgs.unifi-poller}/bin/unifi-poller --config ${configFile}";
|
||||||
|
DynamicUser = false;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.unifi-poller;
|
||||||
|
|
||||||
|
configFile = pkgs.writeText "unifi-poller.json" (generators.toJSON {} {
|
||||||
|
inherit (cfg) poller influxdb prometheus unifi;
|
||||||
|
});
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.services.unifi-poller = {
|
||||||
|
enable = mkEnableOption "unifi-poller";
|
||||||
|
|
||||||
|
poller = {
|
||||||
|
debug = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Turns on line numbers, microsecond logging, and a per-device log.
|
||||||
|
This may be noisy if you have a lot of devices. It adds one line per device.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
quiet = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Turns off per-interval logs. Only startup and error logs will be emitted.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
plugins = mkOption {
|
||||||
|
type = with types; listOf str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Load additional plugins.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
prometheus = {
|
||||||
|
disable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to disable the prometheus ouput plugin.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
http_listen = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "[::]:9130";
|
||||||
|
description = ''
|
||||||
|
Bind the prometheus exporter to this IP or hostname.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
report_errors = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to report errors.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
influxdb = {
|
||||||
|
disable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to disable the influxdb ouput plugin.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
url = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "http://127.0.0.1:8086";
|
||||||
|
description = ''
|
||||||
|
URL of the influxdb host.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "unifipoller";
|
||||||
|
description = ''
|
||||||
|
Username for the influxdb.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
pass = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = pkgs.writeText "unifi-poller-influxdb-default.password" "unifipoller";
|
||||||
|
defaultText = "unifi-poller-influxdb-default.password";
|
||||||
|
description = ''
|
||||||
|
Path of a file containing the password for influxdb.
|
||||||
|
This file needs to be readable by the unifi-poller user.
|
||||||
|
'';
|
||||||
|
apply = v: "file://${v}";
|
||||||
|
};
|
||||||
|
db = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "unifi";
|
||||||
|
description = ''
|
||||||
|
Database name. Database should exist.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
verify_ssl = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Verify the influxdb's certificate.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
interval = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "30s";
|
||||||
|
description = ''
|
||||||
|
Setting this lower than the Unifi controller's refresh
|
||||||
|
interval may lead to zeroes in your database.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
unifi = let
|
||||||
|
controllerOptions = {
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "unifi";
|
||||||
|
description = ''
|
||||||
|
Unifi service user name.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
pass = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = pkgs.writeText "unifi-poller-unifi-default.password" "unifi";
|
||||||
|
defaultText = "unifi-poller-unifi-default.password";
|
||||||
|
description = ''
|
||||||
|
Path of a file containing the password for the unifi service user.
|
||||||
|
This file needs to be readable by the unifi-poller user.
|
||||||
|
'';
|
||||||
|
apply = v: "file://${v}";
|
||||||
|
};
|
||||||
|
url = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "https://unifi:8443";
|
||||||
|
description = ''
|
||||||
|
URL of the Unifi controller.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
sites = mkOption {
|
||||||
|
type = with types; either (enum [ "default" "all" ]) (listOf str);
|
||||||
|
default = "all";
|
||||||
|
description = ''
|
||||||
|
List of site names for which statistics should be exported.
|
||||||
|
Or the string "default" for the default site or the string "all" for all sites.
|
||||||
|
'';
|
||||||
|
apply = toList;
|
||||||
|
};
|
||||||
|
save_ids = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Collect and save data from the intrusion detection system to influxdb.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
save_dpi = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Collect and save data from deep packet inspection.
|
||||||
|
Adds around 150 data points and impacts performance.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
save_sites = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Collect and save site data.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
hash_pii = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Hash, with md5, client names and MAC addresses. This attempts
|
||||||
|
to protect personally identifiable information.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
verify_ssl = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Verify the Unifi controller's certificate.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
dynamic = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Let prometheus select which controller to poll when scraping.
|
||||||
|
Use with default credentials. See unifi-poller wiki for more.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
defaults = controllerOptions;
|
||||||
|
|
||||||
|
controllers = mkOption {
|
||||||
|
type = with types; listOf (submodule { options = controllerOptions; });
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
List of Unifi controllers to poll. Use defaults if empty.
|
||||||
|
'';
|
||||||
|
apply = map (flip removeAttrs [ "_module" ]);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
users.groups.unifi-poller = { };
|
||||||
|
users.users.unifi-poller = {
|
||||||
|
description = "unifi-poller Service User";
|
||||||
|
group = "unifi-poller";
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.unifi-poller = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${pkgs.unifi-poller}/bin/unifi-poller --config ${configFile}";
|
||||||
|
Restart = "always";
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
ProtectSystem = "full";
|
||||||
|
DevicePolicy = "closed";
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
User = "unifi-poller";
|
||||||
|
WorkingDirectory = "/tmp";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -22,6 +22,9 @@ let
|
||||||
* `metricProvider` (optional)
|
* `metricProvider` (optional)
|
||||||
* this attribute contains additional machine config
|
* this attribute contains additional machine config
|
||||||
*
|
*
|
||||||
|
* `nodeName` (optional)
|
||||||
|
* override an incompatible testnode name
|
||||||
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* exporterTests.<exporterName> = {
|
* exporterTests.<exporterName> = {
|
||||||
* exporterConfig = {
|
* exporterConfig = {
|
||||||
|
@ -590,6 +593,19 @@ let
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unifi-poller = {
|
||||||
|
nodeName = "unifi_poller";
|
||||||
|
exporterConfig.enable = true;
|
||||||
|
exporterConfig.controllers = [ { } ];
|
||||||
|
exporterTest = ''
|
||||||
|
wait_for_unit("prometheus-unifi-poller-exporter.service")
|
||||||
|
wait_for_open_port(9130)
|
||||||
|
succeed(
|
||||||
|
"curl -sSf localhost:9130/metrics | grep -q 'unifipoller_build_info{.\\+} 1'"
|
||||||
|
)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
varnish = {
|
varnish = {
|
||||||
exporterConfig = {
|
exporterConfig = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@ -646,24 +662,27 @@ let
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
mapAttrs (exporter: testConfig: (makeTest {
|
mapAttrs (exporter: testConfig: (makeTest (let
|
||||||
|
nodeName = testConfig.nodeName or exporter;
|
||||||
|
|
||||||
|
in {
|
||||||
name = "prometheus-${exporter}-exporter";
|
name = "prometheus-${exporter}-exporter";
|
||||||
|
|
||||||
nodes.${exporter} = mkMerge [{
|
nodes.${nodeName} = mkMerge [{
|
||||||
services.prometheus.exporters.${exporter} = testConfig.exporterConfig;
|
services.prometheus.exporters.${exporter} = testConfig.exporterConfig;
|
||||||
} testConfig.metricProvider or {}];
|
} testConfig.metricProvider or {}];
|
||||||
|
|
||||||
testScript = ''
|
testScript = ''
|
||||||
${exporter}.start()
|
${nodeName}.start()
|
||||||
${concatStringsSep "\n" (map (line:
|
${concatStringsSep "\n" (map (line:
|
||||||
if (builtins.substring 0 1 line == " " || builtins.substring 0 1 line == ")")
|
if (builtins.substring 0 1 line == " " || builtins.substring 0 1 line == ")")
|
||||||
then line
|
then line
|
||||||
else "${exporter}.${line}"
|
else "${nodeName}.${line}"
|
||||||
) (splitString "\n" (removeSuffix "\n" testConfig.exporterTest)))}
|
) (splitString "\n" (removeSuffix "\n" testConfig.exporterTest)))}
|
||||||
${exporter}.shutdown()
|
${nodeName}.shutdown()
|
||||||
'';
|
'';
|
||||||
|
|
||||||
meta = with maintainers; {
|
meta = with maintainers; {
|
||||||
maintainers = [ willibutz ];
|
maintainers = [ willibutz elseym ];
|
||||||
};
|
};
|
||||||
})) exporterTests
|
}))) exporterTests
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
{ stdenv, buildGoModule, fetchFromGitHub }:
|
||||||
|
|
||||||
|
buildGoModule rec {
|
||||||
|
pname = "unifi-poller";
|
||||||
|
version = "2.0.1";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "unifi-poller";
|
||||||
|
repo = "unifi-poller";
|
||||||
|
rev = "v${version}";
|
||||||
|
sha256 = "16q9hrbl9qgilj3vb7865l1yx0xhm7m4sx5j1ys5vi63drq59g93";
|
||||||
|
};
|
||||||
|
|
||||||
|
vendorSha256 = "1fgcbg34g0a0f85qv7bjanv2lpnnszcrspfppp2lnj9kv52j4c1w";
|
||||||
|
|
||||||
|
buildFlagsArray = ''
|
||||||
|
-ldflags=-w -s
|
||||||
|
-X github.com/prometheus/common/version.Branch=master
|
||||||
|
-X github.com/prometheus/common/version.BuildDate=unknown
|
||||||
|
-X github.com/prometheus/common/version.Revision=${src.rev}
|
||||||
|
-X github.com/prometheus/common/version.Version=${version}-0
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with stdenv.lib; {
|
||||||
|
description = "Collect ALL UniFi Controller, Site, Device & Client Data - Export to InfluxDB or Prometheus";
|
||||||
|
homepage = "https://github.com/unifi-poller/unifi-poller";
|
||||||
|
license = licenses.mit;
|
||||||
|
maintainers = with maintainers; [ elseym ];
|
||||||
|
platforms = platforms.unix;
|
||||||
|
};
|
||||||
|
}
|
|
@ -27896,4 +27896,7 @@ in
|
||||||
navidrome = callPackage ../servers/misc/navidrome {};
|
navidrome = callPackage ../servers/misc/navidrome {};
|
||||||
|
|
||||||
zettlr = callPackage ../applications/misc/zettlr { };
|
zettlr = callPackage ../applications/misc/zettlr { };
|
||||||
|
|
||||||
|
unifi-poller = callPackage ../servers/monitoring/unifi-poller {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue