Merge pull request #229767 from mberndt123/mberndt123/stratis-rootfs

nixos/stratis: initrd support for stratis root volumes

authored by

Will Fancher and committed by
GitHub
fe43923a a941557f

+141 -2
+2
nixos/doc/manual/release-notes/rl-2305.section.md
··· 567 568 - `boot.initrd.luks.device.<name>` has a new `tryEmptyPassphrase` option, this is useful for OEM's who need to install an encrypted disk with a future settable passphrase 569 570 - Lisp gained a [manual section](https://nixos.org/manual/nixpkgs/stable/#lisp), documenting a new and backwards incompatible interface. The previous interface will be removed in a future release. 571 572 - The `bind` module now allows the per-zone `allow-query` setting to be configured (previously it was hard-coded to `any`; it still defaults to `any` to retain compatibility).
··· 567 568 - `boot.initrd.luks.device.<name>` has a new `tryEmptyPassphrase` option, this is useful for OEM's who need to install an encrypted disk with a future settable passphrase 569 570 + - there is a new `boot/stratisroot.nix` module that enables booting from a volume managed by the Stratis storage management daemon. Use `fileSystems.<name>.stratis.poolUuid` to configure the pool containing the fs. 571 + 572 - Lisp gained a [manual section](https://nixos.org/manual/nixpkgs/stable/#lisp), documenting a new and backwards incompatible interface. The previous interface will be removed in a future release. 573 574 - The `bind` module now allows the per-zone `allow-query` setting to be configured (previously it was hard-coded to `any`; it still defaults to `any` to retain compatibility).
+19 -2
nixos/modules/installer/tools/nixos-generate-config.pl
··· 335 336 my $st = stat($dev) or return $dev; 337 338 - foreach my $dev2 (glob("/dev/disk/by-uuid/*"), glob("/dev/mapper/*"), glob("/dev/disk/by-label/*")) { 339 my $st2 = stat($dev2) or next; 340 return $dev2 if $st->rdev == $st2->rdev; 341 } ··· 467 } 468 } 469 470 # Don't emit tmpfs entry for /tmp, because it most likely comes from the 471 # boot.tmp.useTmpfs option in configuration.nix (managed declaratively). 472 next if ($mountPoint eq "/tmp" && $fsType eq "tmpfs"); ··· 474 # Emit the filesystem. 475 $fileSystems .= <<EOF; 476 fileSystems.\"$mountPoint\" = 477 - { device = \"${\(findStableDevPath $device)}\"; 478 fsType = \"$fsType\"; 479 EOF 480 481 if (scalar @extraOptions > 0) { 482 $fileSystems .= <<EOF; 483 options = \[ ${\join " ", map { "\"" . $_ . "\"" } uniq(@extraOptions)} \]; 484 EOF 485 } 486
··· 335 336 my $st = stat($dev) or return $dev; 337 338 + foreach my $dev2 (glob("/dev/stratis/*/*"), glob("/dev/disk/by-uuid/*"), glob("/dev/mapper/*"), glob("/dev/disk/by-label/*")) { 339 my $st2 = stat($dev2) or next; 340 return $dev2 if $st->rdev == $st2->rdev; 341 } ··· 467 } 468 } 469 470 + # is this a stratis fs? 471 + my $stableDevPath = findStableDevPath $device; 472 + my $stratisPool; 473 + if ($stableDevPath =~ qr#/dev/stratis/(.*)/.*#) { 474 + my $poolName = $1; 475 + my ($header, @lines) = split "\n", qx/stratis pool list/; 476 + my $uuidIndex = index $header, 'UUID'; 477 + my ($line) = grep /^$poolName /, @lines; 478 + $stratisPool = substr $line, $uuidIndex - 32, 36; 479 + } 480 + 481 # Don't emit tmpfs entry for /tmp, because it most likely comes from the 482 # boot.tmp.useTmpfs option in configuration.nix (managed declaratively). 483 next if ($mountPoint eq "/tmp" && $fsType eq "tmpfs"); ··· 485 # Emit the filesystem. 486 $fileSystems .= <<EOF; 487 fileSystems.\"$mountPoint\" = 488 + { device = \"$stableDevPath\"; 489 fsType = \"$fsType\"; 490 EOF 491 492 if (scalar @extraOptions > 0) { 493 $fileSystems .= <<EOF; 494 options = \[ ${\join " ", map { "\"" . $_ . "\"" } uniq(@extraOptions)} \]; 495 + EOF 496 + } 497 + 498 + if ($stratisPool) { 499 + $fileSystems .= <<EOF; 500 + stratis.poolUuid = "$stratisPool"; 501 EOF 502 } 503
+1
nixos/modules/module-list.nix
··· 1345 ./system/boot/loader/raspberrypi/raspberrypi.nix 1346 ./system/boot/loader/systemd-boot/systemd-boot.nix 1347 ./system/boot/luksroot.nix 1348 ./system/boot/modprobe.nix 1349 ./system/boot/networkd.nix 1350 ./system/boot/plymouth.nix
··· 1345 ./system/boot/loader/raspberrypi/raspberrypi.nix 1346 ./system/boot/loader/systemd-boot/systemd-boot.nix 1347 ./system/boot/luksroot.nix 1348 + ./system/boot/stratisroot.nix 1349 ./system/boot/modprobe.nix 1350 ./system/boot/networkd.nix 1351 ./system/boot/plymouth.nix
+64
nixos/modules/system/boot/stratisroot.nix
···
··· 1 + { config, lib, pkgs, utils, ... }: 2 + let 3 + requiredStratisFilesystems = lib.attrsets.filterAttrs (_: x: utils.fsNeededForBoot x && x.stratis.poolUuid != null) config.fileSystems; 4 + in 5 + { 6 + options = {}; 7 + config = lib.mkIf (requiredStratisFilesystems != {}) { 8 + assertions = [ 9 + { 10 + assertion = config.boot.initrd.systemd.enable; 11 + message = "stratis root fs requires systemd stage 1"; 12 + } 13 + ]; 14 + boot.initrd = { 15 + systemd = { 16 + storePaths = [ 17 + "${pkgs.stratisd}/lib/udev/stratis-base32-decode" 18 + "${pkgs.stratisd}/lib/udev/stratis-str-cmp" 19 + "${pkgs.lvm2.bin}/bin/dmsetup" 20 + "${pkgs.stratisd}/libexec/stratisd-min" 21 + "${pkgs.stratisd.initrd}/bin/stratis-rootfs-setup" 22 + ]; 23 + packages = [pkgs.stratisd.initrd]; 24 + extraBin = { 25 + thin_check = "${pkgs."thin-provisioning-tools"}/bin/thin_check"; 26 + thin_repair = "${pkgs."thin-provisioning-tools"}/bin/thin_repair"; 27 + thin_metadata_size = "${pkgs."thin-provisioning-tools"}/bin/thin_metadata_size"; 28 + stratis-min = "${pkgs.stratisd}/bin/stratis-min"; 29 + }; 30 + services = 31 + lib.attrsets.mapAttrs' ( 32 + mountPoint: fileSystem: { 33 + name = "stratis-setup-${fileSystem.stratis.poolUuid}"; 34 + value = { 35 + description = "setup for Stratis root filesystem"; 36 + unitConfig.DefaultDependencies = "no"; 37 + conflicts = [ "shutdown.target" "initrd-switch-root.target" ]; 38 + onFailure = [ "emergency.target" ]; 39 + unitConfig.OnFailureJobMode = "isolate"; 40 + wants = [ "stratisd-min.service" "plymouth-start.service" ]; 41 + wantedBy = [ "initrd.target" ]; 42 + after = [ "paths.target" "plymouth-start.service" "stratisd-min.service" ]; 43 + before = [ "initrd.target" "shutdown.target" "initrd-switch-root.target" ]; 44 + environment.STRATIS_ROOTFS_UUID = fileSystem.stratis.poolUuid; 45 + serviceConfig = { 46 + Type = "oneshot"; 47 + ExecStart = "${pkgs.stratisd.initrd}/bin/stratis-rootfs-setup"; 48 + RemainAfterExit = "yes"; 49 + }; 50 + }; 51 + } 52 + ) requiredStratisFilesystems; 53 + }; 54 + availableKernelModules = [ "dm-thin-pool" "dm-crypt" ] ++ [ "aes" "aes_generic" "blowfish" "twofish" 55 + "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512" 56 + "af_alg" "algif_skcipher" 57 + ]; 58 + services.udev.packages = [ 59 + pkgs.stratisd.initrd 60 + pkgs.lvm2 61 + ]; 62 + }; 63 + }; 64 + }
+9
nixos/modules/tasks/filesystems.nix
··· 36 description = lib.mdDoc "Location of the mounted file system."; 37 }; 38 39 device = mkOption { 40 default = null; 41 example = "/dev/sda";
··· 36 description = lib.mdDoc "Location of the mounted file system."; 37 }; 38 39 + stratis.poolUuid = lib.mkOption { 40 + type = types.uniq (types.nullOr types.str); 41 + description = lib.mdDoc '' 42 + UUID of the stratis pool that the fs is located in 43 + ''; 44 + example = "04c68063-90a5-4235-b9dd-6180098a20d9"; 45 + default = null; 46 + }; 47 + 48 device = mkOption { 49 default = null; 50 example = "/dev/sda";
+1
nixos/tests/installer-systemd-stage-1.nix
··· 27 simpleUefiGrub 28 simpleUefiGrubSpecialisation 29 simpleUefiSystemdBoot 30 # swraid 31 zfsroot 32 ;
··· 27 simpleUefiGrub 28 simpleUefiGrubSpecialisation 29 simpleUefiSystemdBoot 30 + stratisRoot 31 # swraid 32 zfsroot 33 ;
+35
nixos/tests/installer.nix
··· 989 ) 990 ''; 991 }; 992 }
··· 989 ) 990 ''; 991 }; 992 + } // optionalAttrs systemdStage1 { 993 + stratisRoot = makeInstallerTest "stratisRoot" { 994 + createPartitions = '' 995 + machine.succeed( 996 + "sgdisk --zap-all /dev/vda", 997 + "sgdisk --new=1:0:+100M --typecode=0:ef00 /dev/vda", # /boot 998 + "sgdisk --new=2:0:+1G --typecode=0:8200 /dev/vda", # swap 999 + "sgdisk --new=3:0:+5G --typecode=0:8300 /dev/vda", # / 1000 + "udevadm settle", 1001 + 1002 + "mkfs.vfat /dev/vda1", 1003 + "mkswap /dev/vda2 -L swap", 1004 + "swapon -L swap", 1005 + "stratis pool create my-pool /dev/vda3", 1006 + "stratis filesystem create my-pool nixos", 1007 + "udevadm settle", 1008 + 1009 + "mount /dev/stratis/my-pool/nixos /mnt", 1010 + "mkdir -p /mnt/boot", 1011 + "mount /dev/vda1 /mnt/boot" 1012 + ) 1013 + ''; 1014 + bootLoader = "systemd-boot"; 1015 + extraInstallerConfig = { modulesPath, ...}: { 1016 + config = { 1017 + services.stratis.enable = true; 1018 + environment.systemPackages = [ 1019 + pkgs.stratis-cli 1020 + pkgs.thin-provisioning-tools 1021 + pkgs.lvm2.bin 1022 + pkgs.stratisd.initrd 1023 + ]; 1024 + }; 1025 + }; 1026 + }; 1027 }
+10
pkgs/tools/filesystems/stratisd/default.nix
··· 76 lvm2 77 ]; 78 79 EXECUTABLES_PATHS = lib.makeBinPath ([ 80 xfsprogs 81 thin-provisioning-tools ··· 97 98 # remove files for supporting dracut 99 postInstall = '' 100 rm -r "$out/lib/dracut" 101 rm -r "$out/lib/systemd/system-generators" 102 '';
··· 76 lvm2 77 ]; 78 79 + outputs = ["out" "initrd"]; 80 + 81 EXECUTABLES_PATHS = lib.makeBinPath ([ 82 xfsprogs 83 thin-provisioning-tools ··· 99 100 # remove files for supporting dracut 101 postInstall = '' 102 + mkdir -p "$initrd/bin" 103 + cp "dracut/90stratis/stratis-rootfs-setup" "$initrd/bin" 104 + mkdir -p "$initrd/lib/systemd/system" 105 + substitute "dracut/90stratis/stratisd-min.service" "$initrd/lib/systemd/system/stratisd-min.service" \ 106 + --replace /usr "$out" \ 107 + --replace mkdir "${coreutils}/bin/mkdir" 108 + mkdir -p "$initrd/lib/udev/rules.d" 109 + cp udev/61-stratisd.rules "$initrd/lib/udev/rules.d" 110 rm -r "$out/lib/dracut" 111 rm -r "$out/lib/systemd/system-generators" 112 '';