From cea6d010998fad762c68739011842b40a84f7c3d Mon Sep 17 00:00:00 2001 From: Linoy Hadad Date: Wed, 15 Jan 2025 12:17:50 +0200 Subject: [PATCH] Enable multipath + iSCSI as installation disk This commit removes the disablement of multipath+iSCSI, updates the required kernel arguments, and extends the `nicReapplyManifest` workaround previously added for iSCSI to also support multipath+iSCSI. --- internal/hardware/validator.go | 39 -------------------- internal/host/hostcommands/install_cmd.go | 43 ++++++++++++++++++++--- internal/network/manifests_generator.go | 15 ++++++-- 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/internal/hardware/validator.go b/internal/hardware/validator.go index f7196f0e9cf..ef027a7f76e 100644 --- a/internal/hardware/validator.go +++ b/internal/hardware/validator.go @@ -147,25 +147,7 @@ func (v *validator) DiskIsEligible(ctx context.Context, disk *models.Disk, infra fmt.Sprintf(wrongDriveTypeTemplate, disk.DriveType, strings.Join(v.getValidDeviceStorageTypes(hostArchitecture, clusterVersion), ", "))) } - // We only allow multipath if all paths are FC - if disk.DriveType == models.DriveTypeMultipath { - for _, inventoryDisk := range inventory.Disks { - if lo.Contains(strings.Split(inventoryDisk.Holders, ","), disk.Name) { - if inventoryDisk.DriveType != models.DriveTypeFC { - notEligibleReasons = append(notEligibleReasons, - fmt.Sprintf(wrongMultipathTypeTemplate, inventoryDisk.DriveType, string(models.DriveTypeFC))) - break - } - } - } - } - if disk.DriveType == models.DriveTypeISCSI { - err := areISCSIHoldersValid(disk, inventory) - if err != nil { - notEligibleReasons = append(notEligibleReasons, err.Error()) - } - // Check if network is configured properly to install on iSCSI boot drive err = isISCSINetworkingValid(disk, inventory) if err != nil { @@ -176,27 +158,6 @@ func (v *validator) DiskIsEligible(ctx context.Context, disk *models.Disk, infra return notEligibleReasons, nil } -// Validate holders of iSCSI disk. We do not allow iSCSI disk with multipath holder. -func areISCSIHoldersValid(disk *models.Disk, inventory *models.Inventory) error { - multipathDiskNamesMap := make(map[string]struct{}) - for _, inventoryDisk := range inventory.Disks { - if inventoryDisk.DriveType == models.DriveTypeMultipath { - multipathDiskNamesMap[inventoryDisk.Name] = struct{}{} - } - } - - // Check if the iSCSI disk has any holders that are multipath disks - holders := strings.Split(disk.Holders, ",") - for _, holder := range holders { - if _, exists := multipathDiskNamesMap[holder]; exists { - return fmt.Errorf(iSCSIWithMultipathHolder) - } - } - - return nil - -} - // isISCSINetworkingValid checks if the iSCSI disk is not connected through the // default network interface. The default network interface is the interface // which is used by the default gateway. diff --git a/internal/host/hostcommands/install_cmd.go b/internal/host/hostcommands/install_cmd.go index 385007e17f6..dd96c0dcd23 100644 --- a/internal/host/hostcommands/install_cmd.go +++ b/internal/host/hostcommands/install_cmd.go @@ -300,7 +300,10 @@ func constructHostInstallerArgs(cluster *common.Cluster, host *models.Host, inve // append kargs depending on installation drive type installationDisk := hostutil.GetDiskByInstallationPath(inventory.Disks, hostutil.GetHostInstallationPath(host)) if installationDisk != nil { - installerArgs = appendMultipathArgs(installerArgs, installationDisk) + installerArgs, err = appendMultipathArgs(installerArgs, installationDisk, inventory, hasUserConfiguredIP) + if err != nil { + return "", err + } installerArgs, err = appendISCSIArgs(installerArgs, installationDisk, inventory, hasUserConfiguredIP) if err != nil { return "", err @@ -396,11 +399,43 @@ func appendISCSIArgs(installerArgs []string, installationDisk *models.Disk, inve return installerArgs, nil } -func appendMultipathArgs(installerArgs []string, installationDisk *models.Disk) []string { +func appendMultipathArgs(installerArgs []string, installationDisk *models.Disk, inventory *models.Inventory, hasUserConfiguredIP bool) ([]string, error) { if installationDisk.DriveType != models.DriveTypeMultipath { - return installerArgs + return installerArgs, nil } - return append(installerArgs, "--append-karg", "root=/dev/disk/by-label/dm-mpath-root", "--append-karg", "rw", "--append-karg", "rd.multipath=default") + + installerArgs = append(installerArgs, "--append-karg", "root=/dev/disk/by-label/dm-mpath-root", "--append-karg", "rw", "--append-karg", "rd.multipath=default") + var iSCSIHostIP netip.Addr + var err error + + for _, disk := range inventory.Disks { + if disk.DriveType == models.DriveTypeISCSI && strings.Contains(disk.Holders, installationDisk.Name) { + // enable iSCSI on boot + installerArgs = append(installerArgs, "--append-karg", "rd.iscsi.firmware=1") + + if hasUserConfiguredIP { + return installerArgs, nil + } + + // configure DHCP on the interface used by the iSCSI boot volume + iSCSIHostIP, err = netip.ParseAddr(disk.Iscsi.HostIPAddress) + if err != nil { + return nil, fmt.Errorf("Cannot parse iSCSI host IP %s: %w", installationDisk.Iscsi.HostIPAddress, err) + } + nic, err := network.FindInterfaceByIP(iSCSIHostIP, inventory.Interfaces) + if err != nil { + return nil, fmt.Errorf("Cannot find the interface belonging to iSCSI host IP: %w", err) + } + + dhcp := "dhcp" + if iSCSIHostIP.Is6() { + dhcp = "dhcp6" + } + installerArgs = append(installerArgs, "--append-karg", fmt.Sprintf("ip=%s:%s", nic.Name, dhcp)) + break + } + } + return installerArgs, nil } func appends390xArgs(inventory *models.Inventory, installerArgs []string, log logrus.FieldLogger) ([]string, bool) { diff --git a/internal/network/manifests_generator.go b/internal/network/manifests_generator.go index 30003be1d5c..18086e0d13f 100644 --- a/internal/network/manifests_generator.go +++ b/internal/network/manifests_generator.go @@ -556,7 +556,7 @@ spec: [Service] Type=oneshot ExecStart=-/bin/sh -c ' \ - lsblk -o NAME,TRAN,MOUNTPOINTS --json | jq -e \'.blockdevices[] | select(.tran == "iscsi") | select(.children) | .children[].mountpoints | select(.) | index("/sysroot") | select(.)\' > /dev/null; \ + (lsblk -o NAME,TRAN,MOUNTPOINTS --json | jq -e ".blockdevices[] | select(.tran == \"iscsi\") | select(.children) | .children[] | select(.children) | .children[].mountpoints | select(.) | index(\"/sysroot\")" > /dev/null) || (lsblk -o NAME,TRAN,MOUNTPOINTS --json | jq -e ".blockdevices[] | select(.tran == \"iscsi\") | select(.children) | .children[].mountpoints | select(.) | index(\"/sysroot\")" > /dev/null); \ if [ $? = 0 ]; \ then \ echo "iSCSI boot volume detected, force network reconfiguration..."; \ @@ -569,12 +569,23 @@ spec: ` func (m *ManifestsGenerator) AddNicReapply(ctx context.Context, log logrus.FieldLogger, c *common.Cluster) error { - // Add this manifest only is one of the host is installting on an iSCSI boot drive + // Add this manifest only if one of the host is installing on an iSCSI / multiapth + iSCSI boot drive _, isUsingISCSIBootDrive := lo.Find(c.Cluster.Hosts, func(h *models.Host) bool { installationDisk, err := hostutil.GetHostInstallationDisk(h) if err != nil { return false } + if installationDisk.DriveType == models.DriveTypeMultipath { + inventory, err := common.UnmarshalInventory(h.Inventory) + if err != nil { + return false + } + for _, disk := range inventory.Disks { + if disk.DriveType == models.DriveTypeISCSI && strings.Contains(disk.Holders, installationDisk.Name) { + return true + } + } + } return installationDisk.DriveType == models.DriveTypeISCSI })