From 527fb19cb773ea3b93bb95c49b2a5edebbd0f2b4 Mon Sep 17 00:00:00 2001 From: Florian Sesser <florian@private.storage> Date: Tue, 3 May 2022 17:04:37 +0000 Subject: [PATCH] Backup: Start at a different time of day per machine --- morph/lib/borgbackup.nix | 11 +- nixos/lib/ip-util.nix | 219 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 nixos/lib/ip-util.nix diff --git a/morph/lib/borgbackup.nix b/morph/lib/borgbackup.nix index 30e26cf9..72861dca 100644 --- a/morph/lib/borgbackup.nix +++ b/morph/lib/borgbackup.nix @@ -8,6 +8,12 @@ let cfg = config.services.private-storage.borgbackup; inherit (config.grid) publicKeyPath privateKeyPath; + # Get a per-host number of hours to start the backup at a + # time that should be "night" in most of the USA: + ip-util = import ../../nixos/lib/ip-util.nix; + backupDelayHours = with builtins; bitAnd (ip-util.fromHexString + (hashString "md5" config.networking.hostName)) 15; + in { options.services.private-storage.borgbackup = { enable = lib.mkEnableOption "Borgbackup daily backup job"; @@ -50,7 +56,10 @@ in { BORG_RSH = "ssh -i /run/keys/borgbackup/ssh-key"; }; compression = "none"; - startAt = "*-*-* 05:00:00 UTC"; + + # Start the backup at a different time per machine, + # and not at the full hour, but somewhat later + startAt = "*-*-* " + toString backupDelayHours + ":22:11 UTC"; }; }; }; diff --git a/nixos/lib/ip-util.nix b/nixos/lib/ip-util.nix new file mode 100644 index 00000000..9d9c4789 --- /dev/null +++ b/nixos/lib/ip-util.nix @@ -0,0 +1,219 @@ +# Thank you: https://gist.github.com/petabyteboy/558ffddb9aeb24e1eab2d5d6d021b5d7 + +with import <nixpkgs/lib>; + +rec { + # FIXME: add case for negative numbers + pow = base: exponent: if exponent == 0 then 1 else fold ( + x: y: y * base + ) base ( + range 2 exponent + ); + + fromHexString = hex: foldl ( + x: y: 16 * x + ( + ( + listToAttrs ( + map ( + x: nameValuePair ( + toString x + ) x + ) ( + range 0 9 + ) + ) // { + "a" = 10; + "b" = 11; + "c" = 12; + "d" = 13; + "e" = 14; + "f" = 15; + } + ).${y} + ) + ) 0 ( + stringToCharacters ( + removePrefix "0x" ( + hex + ) + ) + ); + + ipv4 = rec { + + decode = address: foldl ( + x: y: 256 * x + y + ) 0 ( + map toInt ( + splitString "." address + ) + ); + + encode = num: concatStringsSep "." ( + map ( + x: toString (mod (num / x) 256) + ) ( + reverseList ( + genList ( + x: pow 2 (x * 8) + ) 4 + ) + ) + ); + + netmask = prefixLength: ( + foldl ( + x: y: 2 * x + 1 + ) 0 ( + range 1 prefixLength + ) + ) * ( + pow 2 ( + 32 - prefixLength + ) + ); + + reverseZone = net: ( + concatStringsSep "." ( + reverseList ( + splitString "." net + ) + ) + ) + ".in-addr.arpa"; + + eachAddress = net: prefixLength: genList ( + x: decode ( + x + ( + decode net + ) + ) + ) ( + pow 2 ( + 32 - prefixLength + ) + ); + + networkOf = address: prefixLength: encode ( + bitAnd ( + decode address + ) ( + netmask prefixLength + ) + ); + + isInNetwork = net: address: networkOf address == net; + + /* nixos-specific stuff */ + + findOwnAddress = config: net: head ( + filter ( + isInNetwork net + ) ( + configuredAddresses config + ) + ); + + configuredAddresses = config: concatLists ( + mapAttrsToList ( + name: iface: iface.ipv4.addresses + ) config.networking.interfaces + ); + + }; + + ipv6 = rec { + + expand = address: ( + replaceStrings ["::"] [( + concatStringsSep "0" ( + genList (x: ":") ( + 9 - (count (x: x == ":") (stringToCharacters address)) + ) + ) + )] address + ) + ( + if hasSuffix "::" address then + "0" + else + "" + ); + + decode = address: map fromHexString ( + splitString ":" ( + expand address + ) + ); + + encode = address: toLower ( + concatStringsSep ":" ( + map toHexString address + ) + ); + + netmask = prefixLength: map ( + x: if prefixLength > x + 16 then + (pow 2 16) - 1 + else if prefixLength < x then + 0 + else + ( + foldl ( + x: y: 2 * x + 1 + ) 0 ( + range 1 (prefixLength - x) + ) + ) * ( + pow 2 ( + 16 - (prefixLength - x) + ) + ) + ) ( + genList ( + x: x * 16 + ) 8 + ); + + reverseZone = net: ( + concatStringsSep "." ( + concatLists ( + reverseList ( + map ( + x: stringToCharacters (fixedWidthString 4 "0" x) + ) ( + splitString ":" ( + expand net + ) + ) + ) + ) + ) + ) + ".ip6.arpa"; + + networkOf = address: prefixLength: encode ( + zipListsWith bitAnd ( + decode address + ) ( + netmask prefixLength + ) + ); + + isInNetwork = net: address: networkOf address == (expand net); + + /* nixos-specific stuff */ + + findOwnAddress = config: net: head ( + filter ( + isInNetwork net + ) ( + configuredAddresses config + ) + ); + + configuredAddresses = config: concatLists ( + mapAttrsToList ( + name: iface: iface.ipv6.addresses + ) config.networking.interfaces + ); + + }; +} -- GitLab