aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flake.nix4
-rw-r--r--hosts/eden/default.nix8
-rw-r--r--hosts/oasis/default.nix5
-rw-r--r--modules/scripts/zquota/default.nix77
4 files changed, 92 insertions, 2 deletions
diff --git a/flake.nix b/flake.nix
index 6fe3878..2d0950d 100644
--- a/flake.nix
+++ b/flake.nix
@@ -44,7 +44,7 @@
type = "profiles";
modules = [ "common" "podman" "security" "upgrade" "wireguard" "zfs" ];
}
- { type = "scripts"; modules = [ "motd" "pushover" ]; }
+ { type = "scripts"; modules = [ "motd" "pushover" "zquota" ]; }
{
type = "services";
modules = [ "cgit" "proxy" "sftpgo" "ssh" ];
@@ -70,7 +70,7 @@
type = "profiles";
modules = [ "common" "podman" "security" "share" "upgrade" "wireguard" "zfs" ];
}
- { type = "scripts"; modules = [ "motd" "pushover" ]; }
+ { type = "scripts"; modules = [ "motd" "pushover" "zquota" ]; }
{ type = "services"; modules = [ "immich" "proxy" "ssh" ]; }
])
];
diff --git a/hosts/eden/default.nix b/hosts/eden/default.nix
index 02be58f..01951ef 100644
--- a/hosts/eden/default.nix
+++ b/hosts/eden/default.nix
@@ -49,4 +49,12 @@
"zfs-zed"
];
};
+
+ services.zquota = {
+ enable = true;
+ quotas = {
+ "lagoon/backups" = 512;
+ "lagoon/media" = 2048;
+ };
+ };
}
diff --git a/hosts/oasis/default.nix b/hosts/oasis/default.nix
index 8ef44f9..5b849fb 100644
--- a/hosts/oasis/default.nix
+++ b/hosts/oasis/default.nix
@@ -47,5 +47,10 @@
];
};
+ services.zquota = {
+ enable = true;
+ quotas = { "tank/sftpgo" = 512; };
+ };
+
services.sftpgo.dataDir = "/tank/sftpgo";
}
diff --git a/modules/scripts/zquota/default.nix b/modules/scripts/zquota/default.nix
new file mode 100644
index 0000000..1dc1fbd
--- /dev/null
+++ b/modules/scripts/zquota/default.nix
@@ -0,0 +1,77 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+ cfg = config.services.zquota;
+
+ zquota = let hostname = config.networking.hostName; in
+ pkgs.writeShellScriptBin "zquota" ''
+ #!/usr/bin/env bash
+
+ if [ "$#" -ne 2 ]; then
+ echo "failed to provide both a dataset and quota" >&2
+ exit 1
+ fi
+
+ DATASET="$1"
+ QUOTA="$2"
+
+ if [ -n "$(echo "$QUOTA" | tr -d 0-9.)" ]; then
+ echo "failed to provide a valid quota" >&2
+ exit 1
+ fi
+
+ USED=$(${getExe pkgs.zfs} list -Hpo used "$DATASET" 2>/dev/null) || {
+ echo "failed to provide a valid dataset" >&2
+ exit 1
+ }
+
+ USAGE=$(${getExe pkgs.bc} <<< "scale=2; $NUM / 1024^3")
+
+ DIFF=$(${getExe pkgs.bc} <<< "scale=2; $NUM - $QUOTA")
+
+ (( $(awk '{ print ($1 > $2) }' <<< "$USAGE $QUOTA") )) &&
+ /run/current-system/sw/bin/pushover -t "${hostname} quota exceeded" \
+ "dataset $DATASET on ${hostname} has exceeded quota by ''${DIFF}GB"
+ '';
+in
+{
+ options = {
+ services.zquota = {
+ enable = mkEnableOption "zquota";
+ quotas = mkOption {
+ default = { };
+ type = types.attrsOf types.int;
+ description = "Attribute set of ZFS dataset and disk quota (in GB).";
+ };
+ dates = mkOption {
+ default = "daily";
+ type = types.str;
+ description = ''
+ How often quota checks are performed.
+
+ This value must be a calendar event specified by
+ {manpage}`systemd.time(7)`.
+ '';
+ };
+ };
+ };
+
+ config = mkIf cfg.enable {
+ environment.systemPackages = [ zquota ];
+
+ systemd.services."zquota" = {
+ description = "Perform and report routine quota checks on ZFS datasets";
+ serviceConfig.Type = "oneshot";
+ script = strings.concatStringsSep "\n" <| mapAttrsToList (
+ dataset: quota: "/run/current-system/sw/bin/zquota ${dataset} ${builtins.toString quota}"
+ ) cfg.quotas;
+ };
+ systemd.timers."zquota" = {
+ wantedBy = [ "timers.target" ];
+ timerConfig = {
+ OnCalendar = cfg.dates;
+ Persistent = true;
+ };
+ };
+ };
+}