commit 67f679aaa6b1327f866c596b1f241123be4e0350
parent ae494a36a6467b28f55fd5c96c9cec0868a0332c
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 27 Jan 2022 20:40:22 +0100
parent ae494a36a6467b28f55fd5c96c9cec0868a0332c
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 27 Jan 2022 20:40:22 +0100
modules: add vnstati module
3 files changed, 188 insertions(+), 0 deletions(-)
A
|
91
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
|
96
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/modules/default.nix b/modules/default.nix @@ -2,6 +2,7 @@ imports = [ ./restic-backups.nix + ./vnstati ]; }
diff --git a/modules/vnstati/default.nix b/modules/vnstati/default.nix @@ -0,0 +1,91 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.vnstati; + +in { + + options.services.vnstati = with lib; { + + enable = mkEnableOption "just some fancy traffic pics"; + + title = mkOption { + type = types.str; + default = "${if config.networking ? domain then config.networking.hostName else config.networking.fqdn}"; + }; + + domain = mkOption { + type = types.str; + default = "${config.networking.fqdn}"; + }; + + subdirectory = mkOption { + type = types.str; + default = "/traffic/"; + }; + + }; + + config = lib.mkIf cfg.enable { + + assertions = [ + ({ + assertion = config.services.vnstat.enable; + message = "vnstati requires vnstat.enable == true"; + }) + ]; + + + fileSystems."/var/lib/vnstati" = { + device = "tmpfs"; + fsType = "tmpfs"; + options = [ "rw" "size=30M" ]; + }; + + services.nginx.virtualHosts.${cfg.domain} = { + locations.${cfg.subdirectory} = { + alias = "/var/lib/vnstati/"; + index = "index.html"; + }; + }; + + systemd.services.vnstati = { + wantedBy = [ "multi-user.target" ]; + after = [ "var-lib-vnstati.mount" "vnstat.service" ]; + startAt = "*-*-* *:0/10:00"; + path = with pkgs; [ vnstat jq nix ]; + + serviceConfig = { + User = "vnstatd"; + Group = "vnstatd"; + StateDirectory = "vnstati"; + PrivateTmp = true; + ProtectHome = true; + ProtectSystem = "strict"; + Environment = "NIX_PATH=/etc/src"; + }; + + script = '' + set -x + ifaces=$(vnstat --json | jq -r .interfaces[].name | grep -v "^lo$") + echo $ifaces + + nix eval --raw -f ${./vnstati-html.nix} html \ + --argstr ifaces "$ifaces" \ + --argstr hostname "${cfg.title}" \ + > /var/lib/vnstati/index.html + + for iface in $ifaces + do + vnstati -s -nh -i $iface -o /var/lib/vnstati/$iface-summary.png + vnstati -h -nh -i $iface -o /var/lib/vnstati/$iface-hourly.png + vnstati -d -nh -i $iface -o /var/lib/vnstati/$iface-daily.png + vnstati -m -nh -i $iface -o /var/lib/vnstati/$iface-monthly.png + vnstati -t -nh -i $iface -o /var/lib/vnstati/$iface-top10.png + done + ''; + }; + + }; + +}
diff --git a/modules/vnstati/vnstati-html.nix b/modules/vnstati/vnstati-html.nix @@ -0,0 +1,96 @@ +{ ifaces, hostname }: + +let + pkgs = import <nixpkgs> {}; + lib = pkgs.lib; + ifacesL = lib.splitString "\n" ifaces; + + types = [ + { name = "summary"; title = "Summary"; } + { name = "hourly"; title = "Hourly"; } + { name = "daily"; title = "Daily"; } + { name = "monthly"; title = "Monthly"; } + { name = "top10"; title = "Top 10"; } + ]; + +in { + + html = '' + <!DOCTYPE html> + <html lang="en"> + <head> + <title>${hostname} - stats</title> + <meta charset="utf-8"> + <meta name="robots" content="none"> + <link rel="icon" href="static/favicon.ico" type="image/x-icon"> + <style> + body { + padding: 0; + margin: 0; + background: #eee; + font-family: monospace; + min-height: 100vh; + } + img:not(:last-child) { + margin-bottom: 15px; + } + img { + flex-shrink: 0; + } + .ui.segment { + margin: 0 1em 10px 1em; + padding: 1em; + background-color: white; + box-shadow: 0 1px 2px 0 rgba(34,36,38,.15); + border-radius: .286rem; + border: 1px solid rgba(34,36,38,.15); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + } + div.heading { + min-width: 100vw; + height: 70px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + div.table.vertical { + overflow-y: auto; + min-width: 100vw; + height: calc(100vh - 70px); + display: flex; + flex-direction: row; + } + div.iface { + display: flex; + flex-direction: column; + align-items: center; + flex-grow: 1; + } + </style> + </head> + <body> + <div class="heading"> + <h1>${hostname}</h1> + </div> + <div class="table vertical"> + ${lib.concatMapStringsSep "\n" (iface: '' + <div class="iface"> + <h3>${iface}</h3> + ${lib.concatMapStrings (type: '' + <div class="ui segment"> + <img src="${iface}-${type.name}.png" alt="${type.title}"> + </div> + '') types} + </div> + '') ifacesL} + </div> + </body> + </html> + ''; + +}