369 lines
9.2 KiB
Nix
369 lines
9.2 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
with lib; let
|
|
cfg = config.homelab.services.grafana;
|
|
homelabCfg = config.homelab;
|
|
|
|
# Dashboard provisioning
|
|
provisionDashboard = name: source: {
|
|
"grafana-dashboards/${name}.json" = {
|
|
inherit source;
|
|
user = "grafana";
|
|
group = "grafana";
|
|
mode = "0644";
|
|
};
|
|
};
|
|
|
|
# Generate all dashboard files
|
|
dashboardFiles =
|
|
fold (
|
|
dashboard: acc:
|
|
acc // (provisionDashboard dashboard.name dashboard.source)
|
|
) {}
|
|
cfg.dashboards.files;
|
|
in {
|
|
options.homelab.services.grafana = {
|
|
enable = mkEnableOption "Grafana dashboard service";
|
|
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 3000;
|
|
description = "Port for Grafana web interface";
|
|
};
|
|
|
|
openFirewall = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Whether to open firewall ports";
|
|
};
|
|
|
|
dataDir = mkOption {
|
|
type = types.str;
|
|
default = "/var/lib/grafana";
|
|
description = "Directory to store Grafana data";
|
|
};
|
|
|
|
domain = mkOption {
|
|
type = types.str;
|
|
default = "grafana.${homelabCfg.externalDomain}";
|
|
description = "Domain for Grafana";
|
|
};
|
|
|
|
rootUrl = mkOption {
|
|
type = types.str;
|
|
default = "https://grafana.${homelabCfg.externalDomain}";
|
|
description = "Root URL for Grafana";
|
|
};
|
|
|
|
# Authentication settings
|
|
auth = {
|
|
disableLoginForm = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Disable the login form";
|
|
};
|
|
|
|
oauthAutoLogin = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Enable OAuth auto-login";
|
|
};
|
|
|
|
genericOauth = {
|
|
enabled = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Enable generic OAuth";
|
|
};
|
|
};
|
|
};
|
|
|
|
# Data source configuration
|
|
datasources = {
|
|
prometheus = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Enable Prometheus datasource";
|
|
};
|
|
|
|
url = mkOption {
|
|
type = types.str;
|
|
default = "http://127.0.0.1:9090";
|
|
description = "Prometheus URL";
|
|
};
|
|
|
|
uid = mkOption {
|
|
type = types.str;
|
|
default = "prometheus";
|
|
description = "Unique identifier for Prometheus datasource";
|
|
};
|
|
};
|
|
|
|
loki = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Enable Loki datasource";
|
|
};
|
|
|
|
url = mkOption {
|
|
type = types.str;
|
|
default = "http://127.0.0.1:3100";
|
|
description = "Loki URL";
|
|
};
|
|
|
|
uid = mkOption {
|
|
type = types.str;
|
|
default = "loki";
|
|
description = "Unique identifier for Loki datasource";
|
|
};
|
|
};
|
|
|
|
influxdb = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Enable InfluxDB datasource";
|
|
};
|
|
|
|
url = mkOption {
|
|
type = types.str;
|
|
default = "http://127.0.0.1:8086";
|
|
description = "InfluxDB URL";
|
|
};
|
|
|
|
database = mkOption {
|
|
type = types.str;
|
|
default = "homelab";
|
|
description = "InfluxDB database name";
|
|
};
|
|
|
|
tokenPath = mkOption {
|
|
type = types.nullOr types.path;
|
|
default = null;
|
|
description = "Path to InfluxDB token file";
|
|
};
|
|
|
|
uid = mkOption {
|
|
type = types.str;
|
|
default = "influxdb";
|
|
description = "Unique identifier for InfluxDB datasource";
|
|
};
|
|
};
|
|
|
|
extra = mkOption {
|
|
type = types.listOf types.attrs;
|
|
default = [];
|
|
description = "Additional data sources";
|
|
};
|
|
};
|
|
|
|
# Dashboard configuration
|
|
dashboards = {
|
|
path = mkOption {
|
|
type = types.str;
|
|
default = "/etc/grafana-dashboards";
|
|
description = "Path to dashboard files";
|
|
};
|
|
|
|
files = mkOption {
|
|
type = types.listOf (types.submodule {
|
|
options = {
|
|
name = mkOption {
|
|
type = types.str;
|
|
description = "Dashboard name (without .json extension)";
|
|
example = "node-exporter";
|
|
};
|
|
source = mkOption {
|
|
type = types.path;
|
|
description = "Path to dashboard JSON file";
|
|
};
|
|
};
|
|
});
|
|
default = [];
|
|
description = "Dashboard files to provision";
|
|
example = literalExpression ''
|
|
[
|
|
{
|
|
name = "node-exporter";
|
|
source = ./dashboards/node-exporter.json;
|
|
}
|
|
{
|
|
name = "traefik";
|
|
source = ./dashboards/traefik.json;
|
|
}
|
|
]
|
|
'';
|
|
};
|
|
};
|
|
|
|
# Extra user groups for accessing secrets
|
|
extraGroups = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "Additional groups for the grafana user";
|
|
example = ["influxdb2"];
|
|
};
|
|
|
|
# Additional settings
|
|
extraSettings = mkOption {
|
|
type = types.attrs;
|
|
default = {};
|
|
description = "Additional Grafana settings";
|
|
};
|
|
|
|
plugins = mkOption {
|
|
type = types.listOf types.package;
|
|
default = [];
|
|
description = "Grafana plugins to install";
|
|
example = literalExpression "with pkgs.grafanaPlugins; [ grafana-piechart-panel ]";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
# Add grafana user to extra groups (e.g., for accessing secrets)
|
|
users.users.grafana.extraGroups = cfg.extraGroups;
|
|
|
|
services.grafana = {
|
|
enable = true;
|
|
dataDir = cfg.dataDir;
|
|
declarativePlugins = cfg.plugins;
|
|
|
|
settings =
|
|
recursiveUpdate {
|
|
server = {
|
|
http_port = cfg.port;
|
|
http_addr = "0.0.0.0";
|
|
domain = cfg.domain;
|
|
root_url = cfg.rootUrl;
|
|
oauth_auto_login = cfg.auth.oauthAutoLogin;
|
|
};
|
|
|
|
"auth.generic_oauth" = {
|
|
enabled = cfg.auth.genericOauth.enabled;
|
|
};
|
|
|
|
auth = {
|
|
disable_login_form = cfg.auth.disableLoginForm;
|
|
};
|
|
}
|
|
cfg.extraSettings;
|
|
|
|
provision = {
|
|
enable = true;
|
|
|
|
datasources.settings = {
|
|
datasources = let
|
|
# Build datasource list
|
|
datasources =
|
|
[]
|
|
++ optional cfg.datasources.prometheus.enable {
|
|
uid = cfg.datasources.prometheus.uid;
|
|
name = "Prometheus";
|
|
type = "prometheus";
|
|
url = cfg.datasources.prometheus.url;
|
|
}
|
|
++ optional cfg.datasources.loki.enable {
|
|
uid = cfg.datasources.loki.uid;
|
|
name = "Loki";
|
|
type = "loki";
|
|
url = cfg.datasources.loki.url;
|
|
}
|
|
++ optional cfg.datasources.influxdb.enable {
|
|
uid = cfg.datasources.influxdb.uid;
|
|
name = "InfluxDB";
|
|
type = "influxdb";
|
|
url = cfg.datasources.influxdb.url;
|
|
access = "proxy";
|
|
jsonData = {
|
|
dbName = cfg.datasources.influxdb.database;
|
|
httpHeaderName1 = "Authorization";
|
|
};
|
|
secureJsonData = mkIf (cfg.datasources.influxdb.tokenPath != null) {
|
|
httpHeaderValue1 = "$__file{${cfg.datasources.influxdb.tokenPath}}";
|
|
};
|
|
}
|
|
++ cfg.datasources.extra;
|
|
in
|
|
datasources;
|
|
};
|
|
|
|
dashboards.settings.providers = mkIf (cfg.dashboards.files != []) [
|
|
{
|
|
name = "homelab-dashboards";
|
|
options.path = cfg.dashboards.path;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
# Open firewall if requested
|
|
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.port];
|
|
|
|
# Provision dashboard files
|
|
environment.etc = dashboardFiles;
|
|
|
|
# Add to monitoring endpoints
|
|
homelab.monitoring.metrics = [
|
|
{
|
|
name = "grafana";
|
|
port = cfg.port;
|
|
path = "/metrics";
|
|
jobName = "grafana";
|
|
labels = {
|
|
service = "grafana";
|
|
component = "monitoring";
|
|
};
|
|
}
|
|
];
|
|
|
|
# Add health checks
|
|
homelab.monitoring.healthChecks = [
|
|
{
|
|
name = "grafana-web-interface";
|
|
port = cfg.port;
|
|
path = "/api/health";
|
|
interval = "30s";
|
|
conditions = [
|
|
"[STATUS] == 200"
|
|
"[BODY].database == ok"
|
|
"[RESPONSE_TIME] < 2000"
|
|
];
|
|
group = "monitoring";
|
|
labels = {
|
|
service = "grafana";
|
|
component = "web-interface";
|
|
};
|
|
}
|
|
{
|
|
name = "grafana-login-page";
|
|
port = cfg.port;
|
|
path = "/login";
|
|
interval = "60s";
|
|
conditions = [
|
|
"[STATUS] == 200"
|
|
"[RESPONSE_TIME] < 3000"
|
|
];
|
|
group = "monitoring";
|
|
labels = {
|
|
service = "grafana";
|
|
component = "login";
|
|
};
|
|
}
|
|
];
|
|
|
|
# Add reverse proxy entry
|
|
homelab.reverseProxy.entries = [
|
|
{
|
|
subdomain = "grafana";
|
|
host = homelabCfg.hostname;
|
|
port = cfg.port;
|
|
}
|
|
];
|
|
};
|
|
}
|