-
-
Notifications
You must be signed in to change notification settings - Fork 15k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #115590 from grahamc/iscsi
NixOS: services.{openiscsi, target}, boot.iscsi-initiator: init
- Loading branch information
Showing
6 changed files
with
483 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
{ config, lib, pkgs, ... }: with lib; | ||
let | ||
cfg = config.services.openiscsi; | ||
in | ||
{ | ||
options.services.openiscsi = with types; { | ||
enable = mkEnableOption "the openiscsi iscsi daemon"; | ||
enableAutoLoginOut = mkEnableOption '' | ||
automatic login and logout of all automatic targets. | ||
You probably do not want this. | ||
''; | ||
discoverPortal = mkOption { | ||
type = nullOr str; | ||
default = null; | ||
description = "Portal to discover targets on"; | ||
}; | ||
name = mkOption { | ||
type = str; | ||
description = "Name of this iscsi initiator"; | ||
example = "iqn.2020-08.org.linux-iscsi.initiatorhost:example"; | ||
}; | ||
package = mkOption { | ||
type = package; | ||
description = "openiscsi package to use"; | ||
default = pkgs.openiscsi; | ||
defaultText = "pkgs.openiscsi"; | ||
}; | ||
|
||
extraConfig = mkOption { | ||
type = str; | ||
default = ""; | ||
description = "Lines to append to default iscsid.conf"; | ||
}; | ||
|
||
extraConfigFile = mkOption { | ||
description = '' | ||
Append an additional file's contents to /etc/iscsid.conf. Use a non-store path | ||
and store passwords in this file. | ||
''; | ||
default = null; | ||
type = nullOr str; | ||
}; | ||
}; | ||
|
||
config = mkIf cfg.enable { | ||
environment.etc."iscsi/iscsid.conf.fragment".source = pkgs.runCommand "iscsid.conf" {} '' | ||
cat "${cfg.package}/etc/iscsi/iscsid.conf" > $out | ||
cat << 'EOF' >> $out | ||
${cfg.extraConfig} | ||
${optionalString cfg.enableAutoLoginOut "node.startup = automatic"} | ||
EOF | ||
''; | ||
environment.etc."iscsi/initiatorname.iscsi".text = "InitiatorName=${cfg.name}"; | ||
|
||
system.activationScripts.iscsid = let | ||
extraCfgDumper = optionalString (cfg.extraConfigFile != null) '' | ||
if [ -f "${cfg.extraConfigFile}" ]; then | ||
printf "\n# The following is from ${cfg.extraConfigFile}:\n" | ||
cat "${cfg.extraConfigFile}" | ||
else | ||
echo "Warning: services.openiscsi.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2 | ||
fi | ||
''; | ||
in '' | ||
( | ||
cat ${config.environment.etc."iscsi/iscsid.conf.fragment".source} | ||
${extraCfgDumper} | ||
) > /etc/iscsi/iscsid.conf | ||
''; | ||
|
||
systemd.packages = [ cfg.package ]; | ||
|
||
systemd.services."iscsid".wantedBy = [ "multi-user.target" ]; | ||
systemd.sockets."iscsid".wantedBy = [ "sockets.target" ]; | ||
|
||
systemd.services."iscsi" = mkIf cfg.enableAutoLoginOut { | ||
wantedBy = [ "remote-fs.target" ]; | ||
serviceConfig.ExecStartPre = mkIf (cfg.discoverPortal != null) "${cfg.package}/bin/iscsiadm --mode discoverydb --type sendtargets --portal ${escapeShellArg cfg.discoverPortal} --discover"; | ||
}; | ||
|
||
environment.systemPackages = [ cfg.package ]; | ||
boot.kernelModules = [ "iscsi_tcp" ]; | ||
}; | ||
} |
181 changes: 181 additions & 0 deletions
181
nixos/modules/services/networking/iscsi/root-initiator.nix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
{ config, lib, pkgs, ... }: with lib; | ||
let | ||
cfg = config.boot.iscsi-initiator; | ||
in | ||
{ | ||
# If you're booting entirely off another machine you may want to add | ||
# this snippet to always boot the latest "system" version. It is not | ||
# enabled by default in case you have an initrd on a local disk: | ||
# | ||
# boot.initrd.postMountCommands = '' | ||
# ln -sfn /nix/var/nix/profiles/system/init /mnt-root/init | ||
# stage2Init=/init | ||
# ''; | ||
# | ||
# Note: Theoretically you might want to connect to multiple portals and | ||
# log in to multiple targets, however the authors of this module so far | ||
# don't have the need or expertise to reasonably implement it. Also, | ||
# consider carefully before making your boot chain depend on multiple | ||
# machines to be up. | ||
options.boot.iscsi-initiator = with types; { | ||
name = mkOption { | ||
description = '' | ||
Name of the iSCSI initiator to boot from. Note, booting from iscsi | ||
requires networkd based networking. | ||
''; | ||
default = null; | ||
example = "iqn.2020-08.org.linux-iscsi.initiatorhost:example"; | ||
type = nullOr str; | ||
}; | ||
|
||
discoverPortal = mkOption { | ||
description = '' | ||
iSCSI portal to boot from. | ||
''; | ||
default = null; | ||
example = "192.168.1.1:3260"; | ||
type = nullOr str; | ||
}; | ||
|
||
target = mkOption { | ||
description = '' | ||
Name of the iSCSI target to boot from. | ||
''; | ||
default = null; | ||
example = "iqn.2020-08.org.linux-iscsi.targethost:example"; | ||
type = nullOr str; | ||
}; | ||
|
||
logLevel = mkOption { | ||
description = '' | ||
Higher numbers elicits more logs. | ||
''; | ||
default = 1; | ||
example = 8; | ||
type = int; | ||
}; | ||
|
||
loginAll = mkOption { | ||
description = '' | ||
Do not log into a specific target on the portal, but to all that we discover. | ||
This overrides setting target. | ||
''; | ||
type = bool; | ||
default = false; | ||
}; | ||
|
||
extraConfig = mkOption { | ||
description = "Extra lines to append to /etc/iscsid.conf"; | ||
default = null; | ||
type = nullOr lines; | ||
}; | ||
|
||
extraConfigFile = mkOption { | ||
description = '' | ||
Append an additional file's contents to `/etc/iscsid.conf`. Use a non-store path | ||
and store passwords in this file. Note: the file specified here must be available | ||
in the initrd, see: `boot.initrd.secrets`. | ||
''; | ||
default = null; | ||
type = nullOr str; | ||
}; | ||
}; | ||
|
||
config = mkIf (cfg.name != null) { | ||
# The "scripted" networking configuration (ie: non-networkd) | ||
# doesn't properly order the start and stop of the interfaces, and the | ||
# network interfaces are torn down before unmounting disks. Since this | ||
# module is specifically for very-early-boot network mounts, we need | ||
# the network to stay on. | ||
# | ||
# We could probably fix the scripted options to properly order, but I'm | ||
# not inclined to invest that time today. Hopefully this gets users far | ||
# enough along and they can just use networkd. | ||
networking.useNetworkd = true; | ||
networking.useDHCP = false; # Required to set useNetworkd = true | ||
|
||
boot.initrd = { | ||
network.enable = true; | ||
|
||
# By default, the stage-1 disables the network and resets the interfaces | ||
# on startup. Since our startup disks are on the network, we can't let | ||
# the network not work. | ||
network.flushBeforeStage2 = false; | ||
|
||
kernelModules = [ "iscsi_tcp" ]; | ||
|
||
extraUtilsCommands = '' | ||
copy_bin_and_libs ${pkgs.openiscsi}/bin/iscsid | ||
copy_bin_and_libs ${pkgs.openiscsi}/bin/iscsiadm | ||
${optionalString (!config.boot.initrd.network.ssh.enable) "cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib"} | ||
mkdir -p $out/etc/iscsi | ||
cp ${config.environment.etc.hosts.source} $out/etc/hosts | ||
cp ${pkgs.openiscsi}/etc/iscsi/iscsid.conf $out/etc/iscsi/iscsid.fragment.conf | ||
chmod +w $out/etc/iscsi/iscsid.fragment.conf | ||
cat << 'EOF' >> $out/etc/iscsi/iscsid.fragment.conf | ||
${optionalString (cfg.extraConfig != null) cfg.extraConfig} | ||
EOF | ||
''; | ||
|
||
extraUtilsCommandsTest = '' | ||
$out/bin/iscsiadm --version | ||
''; | ||
|
||
preLVMCommands = let | ||
extraCfgDumper = optionalString (cfg.extraConfigFile != null) '' | ||
if [ -f "${cfg.extraConfigFile}" ]; then | ||
printf "\n# The following is from ${cfg.extraConfigFile}:\n" | ||
cat "${cfg.extraConfigFile}" | ||
else | ||
echo "Warning: boot.iscsi-initiator.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2 | ||
fi | ||
''; | ||
in '' | ||
${optionalString (!config.boot.initrd.network.ssh.enable) '' | ||
# stolen from initrd-ssh.nix | ||
echo 'root:x:0:0:root:/root:/bin/ash' > /etc/passwd | ||
echo 'passwd: files' > /etc/nsswitch.conf | ||
''} | ||
cp -f $extraUtils/etc/hosts /etc/hosts | ||
mkdir -p /etc/iscsi /run/lock/iscsi | ||
echo "InitiatorName=${cfg.name}" > /etc/iscsi/initiatorname.iscsi | ||
( | ||
cat "$extraUtils/etc/iscsi/iscsid.fragment.conf" | ||
printf "\n" | ||
${optionalString cfg.loginAll ''echo "node.startup = automatic"''} | ||
${extraCfgDumper} | ||
) > /etc/iscsi/iscsid.conf | ||
iscsid --foreground --no-pid-file --debug ${toString cfg.logLevel} & | ||
iscsiadm --mode discoverydb \ | ||
--type sendtargets \ | ||
--discover \ | ||
--portal ${escapeShellArg cfg.discoverPortal} \ | ||
--debug ${toString cfg.logLevel} | ||
${if cfg.loginAll then '' | ||
iscsiadm --mode node --loginall all | ||
'' else '' | ||
iscsiadm --mode node --targetname ${escapeShellArg cfg.target} --login | ||
''} | ||
pkill -9 iscsid | ||
''; | ||
}; | ||
|
||
services.openiscsi = { | ||
enable = true; | ||
inherit (cfg) name; | ||
}; | ||
|
||
assertions = [ | ||
{ | ||
assertion = cfg.loginAll -> cfg.target == null; | ||
message = "iSCSI target name is set while login on all portals is enabled."; | ||
} | ||
]; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
{ config, lib, pkgs, ... }: | ||
|
||
with lib; | ||
|
||
let | ||
cfg = config.services.target; | ||
in | ||
{ | ||
###### interface | ||
options = { | ||
services.target = with types; { | ||
enable = mkEnableOption "the kernel's LIO iscsi target"; | ||
|
||
config = mkOption { | ||
type = attrs; | ||
default = {}; | ||
description = '' | ||
Content of /etc/target/saveconfig.json | ||
This file is normally read and written by targetcli | ||
''; | ||
}; | ||
}; | ||
}; | ||
|
||
###### implementation | ||
config = mkIf cfg.enable { | ||
environment.etc."target/saveconfig.json" = { | ||
text = builtins.toJSON cfg.config; | ||
mode = "0600"; | ||
}; | ||
|
||
environment.systemPackages = with pkgs; [ targetcli ]; | ||
|
||
boot.kernelModules = [ "configfs" "target_core_mod" "iscsi_target_mod" ]; | ||
|
||
systemd.services.iscsi-target = { | ||
enable = true; | ||
after = [ "network.target" "local-fs.target" ]; | ||
requires = [ "sys-kernel-config.mount" ]; | ||
wantedBy = [ "multi-user.target" ]; | ||
serviceConfig = { | ||
Type = "oneshot"; | ||
ExecStart = "${pkgs.python3.pkgs.rtslib}/bin/targetctl restore"; | ||
ExecStop = "${pkgs.python3.pkgs.rtslib}/bin/targetctl clear"; | ||
RemainAfterExit = "yes"; | ||
}; | ||
}; | ||
|
||
systemd.tmpfiles.rules = [ | ||
"d /etc/target 0700 root root - -" | ||
]; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.