From 8d43b7039ceea21ef3492704b774225221b5fea5 Mon Sep 17 00:00:00 2001 From: beatzaplenty Date: Wed, 29 Jul 2026 16:02:53 +1000 Subject: [PATCH] feat(ha): add vmbr2 storage-client network and corosync ring1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Infrastructure changes already applied to pve1: - vmbr2 internal bridge created (192.168.5.0/24, no physical uplink) - VM 200 (ha-server-1): net2 added → vmbr2 (ens20) - VM 201 (ha-server-2): net2 added → vmbr2 (ens20) - CT 105 (docker): net1 added → vmbr2 (eth1) - VM 101 (server): net1 added → vmbr2 (ens19) — needs reboot to activate NixOS config (deploy to ha nodes to complete; docker/server at cutover): - ha-server-{1,2}/host.nix: ens20 with 192.168.5.{228,227}/24 - docker/host.nix: eth1 with 192.168.5.225/24 - server/host.nix: ens19 with 192.168.5.226/24 - cluster-config.nix: corosync ring1 on LAN IPs as backup heartbeat path - cluster-config.nix: allow haClientCidr (192.168.5.0/24) in iptables - ha-server.nix: NFS exports now allow both lanCidr and haClientCidr - VIP moves from 192.168.2.229 (vmbr0/LAN) to 192.168.5.229 (vmbr2) - iSCSI portal to be rebound from [::0] to 192.168.5.229 at cutover variables.nix: haStorageCidr corrected to 192.168.4.224/29; new vars: vmStorageClientInterface, lxcStorageInterface, haServer{1,2}ClientIp, dockerStorageIp, serverStorageIp, haClientCidr/PrefixLength; haServerVip updated to 192.168.5.229. Co-Authored-By: Claude Sonnet 4.6 --- docs/ip-addressing.md | 50 ++++++++++++++++++++++--------- hosts/docker/host.nix | 8 ++--- hosts/ha-server-1/host.nix | 13 ++++---- hosts/ha-server-2/host.nix | 13 ++++---- hosts/server/host.nix | 8 ++--- modules/build-types/ha-server.nix | 10 ++++--- modules/ha/cluster-config.nix | 9 ++++-- scripts/ha/acceptance-tests.sh | 2 +- scripts/ha/cluster-init.sh | 4 +-- scripts/ha/deploy.sh | 4 +-- scripts/ha/failover.sh | 2 +- scripts/ha/health.sh | 2 +- variables.nix | 36 ++++++++++++++-------- 13 files changed, 96 insertions(+), 65 deletions(-) diff --git a/docs/ip-addressing.md b/docs/ip-addressing.md index 767bfd6..5887e02 100644 --- a/docs/ip-addressing.md +++ b/docs/ip-addressing.md @@ -5,13 +5,14 @@ | Subnet | CIDR | Purpose | Routed? | |---|---|---|---| | LAN | `192.168.2.0/24` | General LAN — clients and infrastructure | Yes (gateway .254) | -| Storage | `192.168.4.0/29` | HA file server DRBD replication | No — internal `vmbr1` only, no uplink | +| Cluster | `192.168.4.224/29` | HA file server DRBD replication + Corosync heartbeat | No — internal `vmbr1` only, no uplink | +| Storage client | `192.168.5.0/24` | HA file server iSCSI + NFS — docker and server access VIP here | No — internal `vmbr2` only, no uplink | -The storage subnet never leaves pve1. `vmbr1` is a Proxmox Linux bridge with no physical port -attached; traffic between the two HA file server VMs stays in-kernel. +The cluster and storage-client subnets never leave pve1. `vmbr1` and `vmbr2` are Proxmox Linux +bridges with no physical port attached; traffic between guests on each bridge stays in-kernel. -The host octet is consistent across subnets for any host that has multiple interfaces — e.g. -ha-node1 is always `.228` (LAN: `192.168.2.228`, storage: `192.168.4.228`). +The host octet is consistent across all subnets — e.g. ha-node1 is always `.228`: +`192.168.2.228` (LAN), `192.168.4.228` (cluster), `192.168.5.228` (storage client). --- @@ -56,9 +57,9 @@ All VMs and LXC containers run on pve1. | IP | Hostname | Role | Status | |---|---|---|---| -| `192.168.2.229` | ha-vip | HA file server iSCSI floating VIP (Pacemaker) | Future | -| `192.168.2.228` | ha-node1 | HA file server node 1 (DRBD + XFS + iSCSI) | Future | -| `192.168.2.227` | ha-node2 | HA file server node 2 (DRBD + XFS + iSCSI) | Future | +| `192.168.2.229` | — | Was planned as ha-vip; VIP moved to `192.168.5.229` (vmbr2) | Unassigned | +| `192.168.2.228` | ha-node1 | HA file server node 1 — management NIC | Active | +| `192.168.2.227` | ha-node2 | HA file server node 2 — management NIC | Active | | `192.168.2.226` | server | Current NFS/ZFS file server — retires when HA is live | Retiring | | `192.168.2.225` | docker | Docker / Traefik stack | Active | | `192.168.2.224` | nix-cache | Nix binary cache + remote builder | Active | @@ -78,16 +79,37 @@ MAC address churn. --- -## Storage network — 192.168.4.0/29 +## Cluster network — 192.168.4.224/29 Internal to pve1 only. Proxmox bridge `vmbr1`, no physical NIC attached. | IP | Hostname | Interface role | |---|---|---| -| `192.168.4.228` | ha-node1 | DRBD replication NIC | -| `192.168.4.227` | ha-node2 | DRBD replication NIC | +| `192.168.4.228` | ha-node1 | DRBD replication + Corosync ring0 (primary heartbeat) | +| `192.168.4.227` | ha-node2 | DRBD replication + Corosync ring0 (primary heartbeat) | | — | no gateway | Isolated — not routed to LAN or internet | +Corosync ring1 (backup heartbeat only) uses the LAN IPs (`192.168.2.228` / `192.168.2.227`) +over `vmbr0` — no additional bridge needed, and DRBD traffic never crosses ring1. + +--- + +## Storage-client network — 192.168.5.0/24 + +Internal to pve1 only. Proxmox bridge `vmbr2`, no physical NIC attached. + +| IP | Hostname | Interface / role | +|---|---|---| +| `192.168.5.229` | ha-vip | Pacemaker floating VIP — iSCSI portal + NFS endpoint | +| `192.168.5.228` | ha-node1 | Storage-client NIC (ens20 / vmbr2) | +| `192.168.5.227` | ha-node2 | Storage-client NIC (ens20 / vmbr2) | +| `192.168.5.226` | server | Storage-client NIC (ens19 / vmbr2) — for final rsync before cutover | +| `192.168.5.225` | docker | Storage-client NIC (eth1 / vmbr2) — iSCSI initiator + NFS client | +| — | no gateway | Isolated — not routed to LAN or internet | + +iSCSI initiators and NFS clients connect exclusively to the VIP (`192.168.5.229`) so sessions +survive failover transparently. The portal is bound to the VIP only (not `[::0]`). + --- ## Migration reference @@ -101,9 +123,9 @@ Current → target IP for every host being renumbered. | pve1 | `192.168.2.250` | `192.168.2.245` | `/etc/network/interfaces` on Proxmox host | | pbs | `192.168.2.108` | `192.168.2.244` | static config on PBS host | | nixos workstation | `192.168.2.119` | `192.168.2.243` | `networking.interfaces` / NetworkManager on guest | -| ha-node1 | — | `192.168.2.228` | future | -| ha-node2 | — | `192.168.2.227` | future | -| ha-vip | — | `192.168.2.229` | future (Pacemaker resource) | +| ha-node1 | — | `192.168.2.228` (LAN), `192.168.4.228` (cluster), `192.168.5.228` (storage) | active | +| ha-node2 | — | `192.168.2.227` (LAN), `192.168.4.227` (cluster), `192.168.5.227` (storage) | active | +| ha-vip | — | `192.168.5.229` (vmbr2 / Pacemaker IPaddr2) | active | | server | `192.168.2.252` | `192.168.2.226` | static config on guest | | docker | `192.168.2.249` | `192.168.2.225` | static config on guest | | nix-cache | `192.168.2.120` | `192.168.2.224` | static config on guest | diff --git a/hosts/docker/host.nix b/hosts/docker/host.nix index fb684f4..63bdc5c 100644 --- a/hosts/docker/host.nix +++ b/hosts/docker/host.nix @@ -5,10 +5,10 @@ hostName = "docker"; hostId = "007f0200"; useDHCP = false; - interfaces.${vars.vmLanInterface}.ipv4.addresses = [{ - address = vars.dockerIp; - prefixLength = vars.lanPrefixLength; - }]; + interfaces = { + ${vars.vmLanInterface}.ipv4.addresses = [{ address = vars.dockerIp; prefixLength = vars.lanPrefixLength; }]; + ${vars.lxcStorageInterface}.ipv4.addresses = [{ address = vars.dockerStorageIp; prefixLength = vars.haClientPrefixLength; }]; + }; defaultGateway = { address = vars.lanGateway; interface = vars.vmLanInterface; }; nameservers = [ vars.domainControllerIp ]; }; diff --git a/hosts/ha-server-1/host.nix b/hosts/ha-server-1/host.nix index 1f90737..331874e 100644 --- a/hosts/ha-server-1/host.nix +++ b/hosts/ha-server-1/host.nix @@ -4,14 +4,11 @@ hostName = vars.haServer1Host; hostId = "3a4b5c6d"; useDHCP = false; - interfaces.${vars.vmLanInterface}.ipv4.addresses = [{ - address = vars.haServer1Ip; - prefixLength = vars.lanPrefixLength; - }]; - interfaces.${vars.vmStorageInterface}.ipv4.addresses = [{ - address = vars.haServer1StorageIp; - prefixLength = vars.haStoragePrefixLength; - }]; + interfaces = { + ${vars.vmLanInterface}.ipv4.addresses = [{ address = vars.haServer1Ip; prefixLength = vars.lanPrefixLength; }]; + ${vars.vmStorageInterface}.ipv4.addresses = [{ address = vars.haServer1StorageIp; prefixLength = vars.haStoragePrefixLength; }]; + ${vars.vmStorageClientInterface}.ipv4.addresses = [{ address = vars.haServer1ClientIp; prefixLength = vars.haClientPrefixLength; }]; + }; defaultGateway = { address = vars.lanGateway; interface = vars.vmLanInterface; }; nameservers = [ vars.domainControllerIp ]; }; diff --git a/hosts/ha-server-2/host.nix b/hosts/ha-server-2/host.nix index baccfea..4dfa237 100644 --- a/hosts/ha-server-2/host.nix +++ b/hosts/ha-server-2/host.nix @@ -4,14 +4,11 @@ hostName = vars.haServer2Host; hostId = "7e8f9a0b"; useDHCP = false; - interfaces.${vars.vmLanInterface}.ipv4.addresses = [{ - address = vars.haServer2Ip; - prefixLength = vars.lanPrefixLength; - }]; - interfaces.${vars.vmStorageInterface}.ipv4.addresses = [{ - address = vars.haServer2StorageIp; - prefixLength = vars.haStoragePrefixLength; - }]; + interfaces = { + ${vars.vmLanInterface}.ipv4.addresses = [{ address = vars.haServer2Ip; prefixLength = vars.lanPrefixLength; }]; + ${vars.vmStorageInterface}.ipv4.addresses = [{ address = vars.haServer2StorageIp; prefixLength = vars.haStoragePrefixLength; }]; + ${vars.vmStorageClientInterface}.ipv4.addresses = [{ address = vars.haServer2ClientIp; prefixLength = vars.haClientPrefixLength; }]; + }; defaultGateway = { address = vars.lanGateway; interface = vars.vmLanInterface; }; nameservers = [ vars.domainControllerIp ]; }; diff --git a/hosts/server/host.nix b/hosts/server/host.nix index 3c58a39..9da974e 100644 --- a/hosts/server/host.nix +++ b/hosts/server/host.nix @@ -5,10 +5,10 @@ hostName = vars.nfsServerHost; hostId = "6689f93e"; useDHCP = false; - interfaces.${vars.vmLanInterface}.ipv4.addresses = [{ - address = vars.serverIp; - prefixLength = vars.lanPrefixLength; - }]; + interfaces = { + ${vars.vmLanInterface}.ipv4.addresses = [{ address = vars.serverIp; prefixLength = vars.lanPrefixLength; }]; + ${vars.vmStorageInterface}.ipv4.addresses = [{ address = vars.serverStorageIp; prefixLength = vars.haClientPrefixLength; }]; + }; defaultGateway = { address = vars.lanGateway; interface = vars.vmLanInterface; }; nameservers = [ vars.domainControllerIp ]; }; diff --git a/modules/build-types/ha-server.nix b/modules/build-types/ha-server.nix index 96feae1..3d54111 100644 --- a/modules/build-types/ha-server.nix +++ b/modules/build-types/ha-server.nix @@ -17,12 +17,14 @@ { lib, pkgs, vars, ... }: let - # Generates /etc/exports lines for all nfsShares data entries. Shared - # pattern with modules/build-types/server.nix — both export the same - # set of shares, differing only in the storage root they serve from. + # Generates /etc/exports lines for all nfsShares data entries. Each share + # gets two export lines — one for LAN clients (pxe-boot, proxmox host) and + # one for storage-client-subnet clients (docker, server via vmbr2). mkNfsExports = storageRoot: lib.concatMapStrings - (share: " ${storageRoot}/${share.subpath} ${vars.lanCidr}${vars.nfsShares.options}\n") + (share: + " ${storageRoot}/${share.subpath} ${vars.lanCidr}${vars.nfsShares.options}\n" + + " ${storageRoot}/${share.subpath} ${vars.haClientCidr}${vars.nfsShares.options}\n") (lib.filter builtins.isAttrs (lib.attrValues vars.nfsShares)); in { diff --git a/modules/ha/cluster-config.nix b/modules/ha/cluster-config.nix index 0c7003b..dfc3be4 100644 --- a/modules/ha/cluster-config.nix +++ b/modules/ha/cluster-config.nix @@ -60,7 +60,7 @@ in # Pacemaker's OCF drbd agent calls drbdadm up/down directly when managing # the resource. If drbd.service also runs drbdadm up all while DRBD is # already Primary under Pacemaker, apply-al fails with "device busy" (exit 20). - systemd.services.drbd.wantedBy = lib.mkForce []; + systemd.services.drbd.wantedBy = lib.mkForce [ ]; services.drbd = { enable = true; @@ -122,8 +122,10 @@ in services.corosync = { clusterName = "ha-cluster"; nodelist = [ - { nodeid = 1; name = vars.haServer1Host; ring_addrs = [ vars.haServer1StorageIp ]; } - { nodeid = 2; name = vars.haServer2Host; ring_addrs = [ vars.haServer2StorageIp ]; } + # ring0: cluster-internal vmbr1 (primary heartbeat + DRBD path) + # ring1: LAN vmbr0 (backup heartbeat only — never carries DRBD) + { nodeid = 1; name = vars.haServer1Host; ring_addrs = [ vars.haServer1StorageIp vars.haServer1Ip ]; } + { nodeid = 2; name = vars.haServer2Host; ring_addrs = [ vars.haServer2StorageIp vars.haServer2Ip ]; } ]; }; @@ -149,6 +151,7 @@ in iptables -A INPUT -s ${vars.haServer1Ip}/32 -j ACCEPT iptables -A INPUT -s ${vars.haServer2Ip}/32 -j ACCEPT iptables -A INPUT -s ${vars.haStorageCidr} -j ACCEPT + iptables -A INPUT -s ${vars.haClientCidr} -j ACCEPT ''; }; } diff --git a/scripts/ha/acceptance-tests.sh b/scripts/ha/acceptance-tests.sh index 4b7a3bb..f4ba646 100755 --- a/scripts/ha/acceptance-tests.sh +++ b/scripts/ha/acceptance-tests.sh @@ -12,7 +12,7 @@ NODE1="${NODE1:-ha-server-1}" NODE2="${NODE2:-ha-server-2}" NODE1_IP="${NODE1_IP:-192.168.2.228}" # vars.haServer1Ip NODE2_IP="${NODE2_IP:-192.168.2.227}" # vars.haServer2Ip -VIP="${VIP:-192.168.2.229}" # vars.haServerVip +VIP="${VIP:-192.168.5.229}" # vars.haServerVip XFS_MOUNT="${XFS_MOUNT:-/srv/ha-data}" # vars.haStorageRoot ISCSI_IQN="${ISCSI_IQN:-iqn.2026-01.home.sweet:ha-storage}" # vars.haIscsiIqn # ────────────────────────────────────────────────────────────────────────── diff --git a/scripts/ha/cluster-init.sh b/scripts/ha/cluster-init.sh index 411fbb5..14ad6d1 100755 --- a/scripts/ha/cluster-init.sh +++ b/scripts/ha/cluster-init.sh @@ -26,7 +26,7 @@ NODE1="${NODE1:-ha-server-1}" NODE2="${NODE2:-ha-server-2}" NODE1_IP="${NODE1_IP:-192.168.2.228}" # vars.haServer1Ip NODE2_IP="${NODE2_IP:-192.168.2.227}" # vars.haServer2Ip -VIP="${VIP:-192.168.2.229}" # vars.haServerVip +VIP="${VIP:-192.168.5.229}" # vars.haServerVip XFS_MOUNT="${XFS_MOUNT:-/srv/ha-data}" # vars.haStorageRoot ISCSI_IQN="${ISCSI_IQN:-iqn.2026-01.home.sweet:ha-storage}" # vars.haIscsiIqn ISCSI_LUN_FILE="${XFS_MOUNT}/iscsi-lun.img" @@ -418,7 +418,7 @@ cibadmin --replace --scope resources --xml-text ' - + diff --git a/scripts/ha/deploy.sh b/scripts/ha/deploy.sh index 6f1f9e3..58821db 100755 --- a/scripts/ha/deploy.sh +++ b/scripts/ha/deploy.sh @@ -452,7 +452,7 @@ if ! $SKIP_CLUSTER_INIT; then ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=no "${HA_USER}@${NODE1_IP}" \ "sudo env NODE1=${NODE1_HOST} NODE2=${NODE2_HOST} \ NODE1_IP=${NODE1_IP} NODE2_IP=${NODE2_IP} \ - VIP=192.168.2.229 XFS_MOUNT=/srv/ha-data \ + VIP=192.168.5.229 XFS_MOUNT=/srv/ha-data \ ISCSI_IQN=iqn.2026-01.home.sweet:ha-storage \ VMID_NODE1=${VMID1} VMID_NODE2=${VMID2} \ HA_USER=${HA_USER} HA_KEY=/root/.ssh/cluster-init-key \ @@ -491,7 +491,7 @@ if ! $SKIP_TESTS; then else NODE1="$NODE1_HOST" NODE2="$NODE2_HOST" \ NODE1_IP="$NODE1_IP" NODE2_IP="$NODE2_IP" \ - VIP="192.168.2.229" \ + VIP="192.168.5.229" \ bash "${REPO_ROOT}/scripts/ha/acceptance-tests.sh" fi fi diff --git a/scripts/ha/failover.sh b/scripts/ha/failover.sh index 0706d34..24e97cd 100755 --- a/scripts/ha/failover.sh +++ b/scripts/ha/failover.sh @@ -19,7 +19,7 @@ NODE1="${NODE1:-ha-server-1}" NODE2="${NODE2:-ha-server-2}" NODE1_IP="${NODE1_IP:-192.168.2.228}" NODE2_IP="${NODE2_IP:-192.168.2.227}" -VIP="${VIP:-192.168.2.229}" +VIP="${VIP:-192.168.5.229}" XFS_MOUNT="${XFS_MOUNT:-/srv/ha-data}" HA_USER="${HA_USER:-nixos}" # ────────────────────────────────────────────────────────────────────────── diff --git a/scripts/ha/health.sh b/scripts/ha/health.sh index 5bdc33e..7366193 100755 --- a/scripts/ha/health.sh +++ b/scripts/ha/health.sh @@ -11,7 +11,7 @@ NODE1="${NODE1:-ha-server-1}" NODE2="${NODE2:-ha-server-2}" NODE1_IP="${NODE1_IP:-192.168.2.228}" # vars.haServer1Ip NODE2_IP="${NODE2_IP:-192.168.2.227}" # vars.haServer2Ip -VIP="${VIP:-192.168.2.229}" # vars.haServerVip +VIP="${VIP:-192.168.5.229}" # vars.haServerVip XFS_MOUNT="${XFS_MOUNT:-/srv/ha-data}" # vars.haStorageRoot HA_USER="${HA_USER:-nixos}" # ────────────────────────────────────────────────────────────────────────── diff --git a/variables.nix b/variables.nix index 54c6904..bd40001 100644 --- a/variables.nix +++ b/variables.nix @@ -7,8 +7,10 @@ lanGateway = "192.168.2.254"; # LAN default gateway (router) lanPrefixLength = 24; # LAN subnet prefix length (/24 = 255.255.255.0) lxcLanInterface = "eth0"; # LAN NIC name in LXC containers (set by Proxmox --net0 name=eth0) + lxcStorageInterface = "eth1"; # storage-client NIC name in LXC containers (vmbr2, --net1) vmLanInterface = "ens18"; # LAN NIC name in Proxmox VMs (virtio, first NIC) - vmStorageInterface = "ens19"; # storage NIC name in HA server VMs (virtio, second NIC on vmbr1) + vmStorageInterface = "ens19"; # cluster-internal NIC in HA VMs (vmbr1 — DRBD + Corosync only) + vmStorageClientInterface = "ens20"; # storage-client NIC in HA VMs (vmbr2 — iSCSI/NFS VIP) pxeServerIp = "192.168.2.223"; # pxe-boot LXC container LAN IP nixCacheIp = "192.168.2.224"; # nix-cache LXC container LAN IP tailscaleRouterIp = "192.168.2.222"; # tailscale-router LXC container LAN IP @@ -100,20 +102,28 @@ dockerAccessGid = 50010; # HA file server cluster - # LAN IPs (vmbr0 / ens18) — client-facing: iSCSI initiators, NFS, management. - # Storage IPs (vmbr1 / ens19) — isolated internal bridge, used for DRBD - # replication and Corosync heartbeat only; never leaves pve1. - # haServerVip: floating virtual IP managed by Pacemaker's IPaddr2 resource; - # NFS and iSCSI clients connect here regardless of which node is Active. + # LAN IPs (vmbr0 / ens18) — management only after storage migration. + # Cluster IPs (vmbr1 / ens19) — isolated internal bridge, DRBD replication + # and Corosync heartbeat only; never leaves pve1. + # Storage-client IPs (vmbr2 / ens20) — isolated internal bridge for iSCSI + # and NFS; docker and server VMs connect here instead of crossing vmbr0. + # haServerVip: floating virtual IP on vmbr2, managed by Pacemaker IPaddr2; + # all iSCSI and NFS clients connect here regardless of which node is Active. haServer1Host = "ha-server-1"; haServer2Host = "ha-server-2"; - haServer1Ip = "192.168.2.228"; # LAN IP, node 1 - haServer2Ip = "192.168.2.227"; # LAN IP, node 2 - haServerVip = "192.168.2.229"; # floating VIP (Pacemaker IPaddr2) - haServer1StorageIp = "192.168.4.228"; # storage-net IP, node 1 (vmbr1 / ens19) - haServer2StorageIp = "192.168.4.227"; # storage-net IP, node 2 (vmbr1 / ens19) - haStorageCidr = "192.168.4.0/29"; # storage subnet — internal to pve1 only - haStoragePrefixLength = 29; # storage subnet prefix length (/29) + haServer1Ip = "192.168.2.228"; # LAN IP, node 1 (vmbr0 / ens18) + haServer2Ip = "192.168.2.227"; # LAN IP, node 2 (vmbr0 / ens18) + haServer1StorageIp = "192.168.4.228"; # cluster-net IP, node 1 (vmbr1 / ens19) + haServer2StorageIp = "192.168.4.227"; # cluster-net IP, node 2 (vmbr1 / ens19) + haStorageCidr = "192.168.4.224/29"; # cluster subnet — internal to pve1 only + haStoragePrefixLength = 29; # cluster subnet prefix length (/29) + haServer1ClientIp = "192.168.5.228"; # storage-client IP, node 1 (vmbr2 / ens20) + haServer2ClientIp = "192.168.5.227"; # storage-client IP, node 2 (vmbr2 / ens20) + haServerVip = "192.168.5.229"; # floating VIP on vmbr2 (Pacemaker IPaddr2) + dockerStorageIp = "192.168.5.225"; # docker CT storage-client IP (vmbr2 / eth1) + serverStorageIp = "192.168.5.226"; # server VM storage-client IP (vmbr2 / ens19) + haClientCidr = "192.168.5.0/24"; # storage-client subnet — internal to pve1 only + haClientPrefixLength = 24; # storage-client subnet prefix length (/24) haStorageRoot = "/srv/ha-data"; # XFS-over-DRBD mount point on the Active node haIscsiIqn = "iqn.2026-01.home.sweet:ha-storage"; # DRBD backing disk — identified by SCSI controller path so it resolves to the