Archived
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m37s
Deploy/init fixes: - iscsi-target.nix: targetctl binary is in rtslib-fb (python3 env), not targetcli-fb — fixes ExecStart and ExecStop for the targetctl.service - deploy.sh: _patch_targetctl() applies runtime dropin to both nodes before cluster-init so Pacemaker can manage the iSCSI target from first start - cluster-init.sh: replace crm configure heredoc with cibadmin --replace XML (pacemaker-4.0 schema: globally-unique in meta_attributes, promoted-max/ promoted-node-max, Promoted role in constraints); force_unmount=true on xfs-data; DRBD promote timeout 240s - cluster-config.nix: add crm-fence-peer.sh/crm-unfence-peer.sh handlers; update fencing comment to reflect resource-only + Pacemaker-aware handler replacing STONITH during testing phase - ha-server.nix: add openiscsi to systemPackages for T4 iscsiadm availability Acceptance test fixes: - acceptance-tests.sh: fix ((PASS++)) set -e bug → PASS=$((PASS+1)); detect Active/Standby dynamically via drbdadm role (Pacemaker can promote either node); T4 bash TCP probe instead of iscsiadm; T5 timeout 120s; T6 echo|sudo tee for root-owned XFS write (bash -c redirect runs as nixos not sudo — permission denied); use ns cat / ns rm for root-owned reads Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HaH1cSGvhogRP5ExoF6nD8
100 lines
3.3 KiB
Nix
100 lines
3.3 KiB
Nix
# LIO iSCSI target service (targetctl) for NixOS HA clusters.
|
|
#
|
|
# Provides the targetctl.service that saves/restores LIO configuration from
|
|
# /etc/target/saveconfig.json. Pacemaker manages this service via its
|
|
# systemd resource agent (class="systemd" type="targetctl").
|
|
#
|
|
# Why ExecStop is not simply "targetctl save":
|
|
# targetctl save writes the LIO config to JSON but does NOT remove the LIO
|
|
# target from the kernel's configfs. As a result, any fileio backing store
|
|
# that LIO has open (e.g. iscsi-lun.img on an XFS-over-DRBD filesystem)
|
|
# stays referenced in the kernel. The subsequent XFS umount from the
|
|
# Filesystem OCF resource then returns EBUSY and either hangs for the full
|
|
# op-stop timeout or fails outright, blocking the entire failover.
|
|
#
|
|
# The ExecStop script here additionally tears down the kernel LIO state
|
|
# via rtslib_fb after saving, so the backing-store file descriptor is
|
|
# released and umount succeeds immediately.
|
|
#
|
|
# Empty-config guard:
|
|
# The save step is skipped when no iSCSI targets are currently active.
|
|
# This prevents the secondary node (where LIO was never started) from
|
|
# overwriting a valid saveconfig.json with an empty one when Pacemaker
|
|
# stops the iscsi-target resource as part of a failover or cleanup.
|
|
{ pkgs, ... }:
|
|
|
|
let
|
|
python3 = pkgs.python3.withPackages (ps: [ ps.rtslib-fb ]);
|
|
targetctl = "${python3}/bin/targetctl";
|
|
|
|
targetctlStop = pkgs.writeScript "targetctl-stop" ''
|
|
#!${python3}/bin/python3
|
|
import subprocess, sys
|
|
import rtslib_fb
|
|
|
|
root = rtslib_fb.RTSRoot()
|
|
targets = list(root.targets)
|
|
if targets:
|
|
subprocess.run(
|
|
["${targetctl}", "save", "/etc/target/saveconfig.json"],
|
|
capture_output=True,
|
|
)
|
|
print(f"saved {len(targets)} iSCSI target(s)")
|
|
else:
|
|
print("no active LIO targets — saveconfig.json unchanged")
|
|
|
|
for target in targets:
|
|
try:
|
|
for tpg in list(target.tpgs):
|
|
tpg.enable = False
|
|
target.delete()
|
|
except Exception as e:
|
|
print(f"warn (target): {e}", file=sys.stderr)
|
|
for so in list(root.storage_objects):
|
|
try:
|
|
so.delete()
|
|
except Exception as e:
|
|
print(f"warn (backstore): {e}", file=sys.stderr)
|
|
print("LIO kernel target cleared")
|
|
'';
|
|
in
|
|
{
|
|
boot.kernelModules = [
|
|
"target_core_mod"
|
|
"iscsi_target_mod"
|
|
"target_core_file"
|
|
"target_core_pscsi"
|
|
"target_core_user"
|
|
"configfs"
|
|
];
|
|
|
|
systemd = {
|
|
mounts = [{
|
|
where = "/sys/kernel/config";
|
|
what = "configfs";
|
|
type = "configfs";
|
|
wantedBy = [ "multi-user.target" ];
|
|
before = [ "targetctl.service" ];
|
|
}];
|
|
services.targetctl = {
|
|
description = "LIO iSCSI target config save/restore";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [ "sys-kernel-config.mount" "network.target" ];
|
|
requires = [ "sys-kernel-config.mount" ];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
RemainAfterExit = true;
|
|
ExecStart = "${targetctl} restore /etc/target/saveconfig.json";
|
|
ExecStop = "${targetctlStop}";
|
|
};
|
|
unitConfig.ConditionFileNotEmpty = "/etc/target/saveconfig.json";
|
|
};
|
|
tmpfiles.rules = [
|
|
"d /etc/target 0750 root root -"
|
|
"f /etc/target/saveconfig.json 0640 root root -"
|
|
];
|
|
};
|
|
|
|
environment.systemPackages = [ pkgs.targetcli-fb ];
|
|
}
|