Skip to content

Commit

Permalink
MGMT-16273: Allow installing on iSCSI disks on OCI
Browse files Browse the repository at this point in the history
Allow booting from iSCSI for x86_64 OpenShift versions at least 4.15.0
on the OCI platform.
  • Loading branch information
avishayt committed Nov 26, 2023
1 parent a9a125d commit 1f120d2
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 16 deletions.
8 changes: 4 additions & 4 deletions internal/hardware/mock_validator.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 21 additions & 6 deletions internal/hardware/validator.go
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/openshift/assisted-service/internal/feature"
"github.com/openshift/assisted-service/internal/host/hostutil"
"github.com/openshift/assisted-service/internal/operators"
"github.com/openshift/assisted-service/internal/provider/registry"
"github.com/openshift/assisted-service/models"
"github.com/openshift/assisted-service/pkg/conversions"
"github.com/sirupsen/logrus"
Expand All @@ -37,7 +38,7 @@ type Validator interface {
GetInfraEnvHostRequirements(ctx context.Context, infraEnv *common.InfraEnv) (*models.ClusterHostRequirements, error)
DiskIsEligible(ctx context.Context, disk *models.Disk, infraEnv *common.InfraEnv, cluster *common.Cluster, host *models.Host, hostArchitecture string, allDisks []*models.Disk) ([]string, error)
ListEligibleDisks(inventory *models.Inventory) []*models.Disk
IsValidStorageDeviceType(disk *models.Disk, hostArchitecture string) bool
IsValidStorageDeviceType(disk *models.Disk, hostArchitecture string, openshiftVersion string, ociPlatformType bool) bool
GetInstallationDiskSpeedThresholdMs(ctx context.Context, cluster *common.Cluster, host *models.Host) (int64, error)
// GetPreflightHardwareRequirements provides hardware (host) requirements that can be calculated only using cluster information.
// Returned information describe requirements coming from OCP and OLM operators.
Expand Down Expand Up @@ -77,6 +78,7 @@ type validator struct {
operatorsAPI operators.API
diskEligibilityMatchers []*regexp.Regexp
edgeWorkersProductList []string
providerRegistry registry.ProviderRegistry
}

// DiskEligibilityInitialized is used to detect inventories created by older versions of the agent/service
Expand Down Expand Up @@ -126,9 +128,13 @@ func (v *validator) DiskIsEligible(ctx context.Context, disk *models.Disk, infra
humanize.Bytes(uint64(disk.SizeBytes)), humanize.Bytes(uint64(minSizeBytes))))
}

if !v.IsValidStorageDeviceType(disk, hostArchitecture) {
ociPlatformType, err := v.providerRegistry.IsHostSupported(models.PlatformTypeOci, host)
if err != nil {
return nil, err
}
if !v.IsValidStorageDeviceType(disk, hostArchitecture, cluster.OpenshiftVersion, ociPlatformType) {
notEligibleReasons = append(notEligibleReasons,
fmt.Sprintf(wrongDriveTypeTemplate, disk.DriveType, strings.Join(v.getValidDeviceStorageTypes(hostArchitecture), ", ")))
fmt.Sprintf(wrongDriveTypeTemplate, disk.DriveType, strings.Join(v.getValidDeviceStorageTypes(hostArchitecture, cluster.OpenshiftVersion, ociPlatformType), ", ")))
}

// We only allow multipath if all paths are FC
Expand All @@ -147,8 +153,8 @@ func (v *validator) DiskIsEligible(ctx context.Context, disk *models.Disk, infra
return notEligibleReasons, nil
}

func (v *validator) IsValidStorageDeviceType(disk *models.Disk, hostArchitecture string) bool {
return funk.ContainsString(v.getValidDeviceStorageTypes(hostArchitecture), string(disk.DriveType))
func (v *validator) IsValidStorageDeviceType(disk *models.Disk, hostArchitecture string, openshiftVersion string, ociPlatformType bool) bool {
return funk.ContainsString(v.getValidDeviceStorageTypes(hostArchitecture, openshiftVersion, ociPlatformType), string(disk.DriveType))
}

func (v *validator) purgeServiceReasons(reasons []string) []string {
Expand Down Expand Up @@ -424,11 +430,20 @@ func (v *validator) getOCPRequirementsForVersion(openshiftVersion string) (*mode
return v.VersionedRequirements.GetVersionedHostRequirements(openshiftVersion)
}

func (v *validator) getValidDeviceStorageTypes(hostArchitecture string) []string {
func (v *validator) getValidDeviceStorageTypes(hostArchitecture string, openshiftVersion string, ociPlatformType bool) []string {
validTypes := []string{string(models.DriveTypeHDD), string(models.DriveTypeSSD), string(models.DriveTypeMultipath)}

if ociPlatformType {
isGreater, err := common.BaseVersionGreaterOrEqual("4.15.0", openshiftVersion)
if err != nil && isGreater {
validTypes = append(validTypes, string(models.DriveTypeISCSI))
}
}

if hostArchitecture == models.ClusterCPUArchitectureS390x {
validTypes = append(validTypes, string(models.DriveTypeFC), string(models.DriveTypeECKDESE), string(models.DriveTypeECKD), string(models.DriveTypeFBA))
}

return validTypes
}

Expand Down
26 changes: 26 additions & 0 deletions internal/hardware/validator_test.go
Expand Up @@ -157,6 +157,32 @@ var _ = Describe("Disk eligibility", func() {
Expect(eligible).To(BeEmpty())
})

It("Check if iSCSI is eligible only on newer OCI clusters", func() {
testDisk.DriveType = models.DriveTypeISCSI
cluster.OpenshiftVersion = "4.15.0"
inventory := models.Inventory{SystemVendor: &models.SystemVendor{Manufacturer: "OracleCloud.com"}}
hw, _ := json.Marshal(&inventory)
host.Inventory = string(hw)
eligible, err := hwvalidator.DiskIsEligible(ctx, &testDisk, infraEnv, &cluster, &host, models.ClusterCPUArchitectureX8664, []*models.Disk{&testDisk})

Expect(err).ToNot(HaveOccurred())
Expect(eligible).To(BeEmpty())

By("Check iSCSI on older version is not eligible")
operatorsMock.EXPECT().GetRequirementsBreakdownForHostInCluster(gomock.Any(), gomock.Any(), gomock.Any()).Return([]*models.OperatorHostRequirements{}, nil)
cluster.OpenshiftVersion = "4.14.1"
eligible, err = hwvalidator.DiskIsEligible(ctx, &testDisk, infraEnv, &cluster, &host, models.ClusterCPUArchitectureX8664, []*models.Disk{&testDisk})

Expect(err).ToNot(HaveOccurred())
Expect(eligible).ToNot(BeEmpty())

By("Check infra env iSCSI is eligible")
eligible, err = hwvalidator.DiskIsEligible(ctx, &testDisk, infraEnv, nil, &host, models.ClusterCPUArchitectureX8664, []*models.Disk{&testDisk})

Expect(err).ToNot(HaveOccurred())
Expect(eligible).To(BeEmpty())
})

It("Check that FC multipath is eligible", func() {
testDisk.Name = "dm-0"
testDisk.DriveType = models.DriveTypeMultipath
Expand Down
9 changes: 7 additions & 2 deletions internal/host/hostcommands/install_cmd.go
Expand Up @@ -303,8 +303,13 @@ func constructHostInstallerArgs(cluster *common.Cluster, host *models.Host, inve
}

for _, disk := range inventory.Disks {
if disk.DriveType == models.DriveTypeMultipath && disk.ID == host.InstallationDiskID {
installerArgs = append(installerArgs, "--append-karg", "root=/dev/disk/by-label/dm-mpath-root", "--append-karg", "rw", "--append-karg", "rd.multipath=default")
if disk.ID == host.InstallationDiskID {
if disk.DriveType == models.DriveTypeMultipath {
installerArgs = append(installerArgs, "--append-karg", "root=/dev/disk/by-label/dm-mpath-root", "--append-karg", "rw", "--append-karg", "rd.multipath=default")
} else if disk.DriveType == models.DriveTypeISCSI {
// Currently only allowed on the OCI platform
installerArgs = append(installerArgs, "--append-karg", "rd.iscsi.firmware=1", "--append-karg", "ip=ibft")
}
}
}

Expand Down
26 changes: 26 additions & 0 deletions internal/host/hostcommands/install_cmd_test.go
Expand Up @@ -694,6 +694,32 @@ var _ = Describe("construct host install arguments", func() {
Expect(err).NotTo(HaveOccurred())
Expect(args).To(BeEmpty())
})
It("iSCSI installation disk on OCI", func() {
cluster.MachineNetworks = []*models.MachineNetwork{{Cidr: "192.186.10.0/24"}}
cluster.Platform = &models.Platform{Type: common.PlatformTypePtr(models.PlatformTypeOci)}
host.Inventory = fmt.Sprintf(`{
"disks":[
{
"id": "install-id",
"drive_type": "%s"
},
{
"id": "other-id",
"drive_type": "%s"
}
],
"interfaces":[
{
"name": "eth1",
"ipv4_addresses":["10.56.20.80/25"]
}
]
}`, models.DriveTypeISCSI, models.DriveTypeSSD)
inventory, _ := common.UnmarshalInventory(host.Inventory)
args, err := constructHostInstallerArgs(cluster, host, inventory, infraEnv, log)
Expect(err).NotTo(HaveOccurred())
Expect(args).To(Equal(`["--append-karg","rd.iscsi.firmware=1","--append-karg","ip=ibft"]`))
})
It("ip=<nic>:dhcp6 added when machine CIDR is IPv6", func() {
cluster.MachineNetworks = []*models.MachineNetwork{{Cidr: "2001:db8::/64"}}
host.Inventory = `{
Expand Down
2 changes: 1 addition & 1 deletion internal/host/transition_test.go
Expand Up @@ -1577,7 +1577,7 @@ var _ = Describe("Refresh Host", func() {
)).AnyTimes()

mockDefaultClusterHostRequirements(mockHwValidator)
mockHwValidator.EXPECT().IsValidStorageDeviceType(gomock.Any(), gomock.Any()).Return(true).AnyTimes()
mockHwValidator.EXPECT().IsValidStorageDeviceType(gomock.Any(), gomock.Any(), "", false).Return(true).AnyTimes()
mockHwValidator.EXPECT().ListEligibleDisks(gomock.Any()).Return([]*models.Disk{}).AnyTimes()
mockHwValidator.EXPECT().GetHostInstallationPath(gomock.Any()).Return("/dev/sda").AnyTimes()
}
Expand Down
4 changes: 2 additions & 2 deletions internal/host/validations_test.go
Expand Up @@ -82,7 +82,7 @@ var _ = Describe("Validations test", func() {

mockAndRefreshStatusWithoutEvents := func(h *models.Host) {
mockDefaultClusterHostRequirements(mockHwValidator)
mockHwValidator.EXPECT().IsValidStorageDeviceType(gomock.Any(), gomock.Any()).Return(true).AnyTimes()
mockHwValidator.EXPECT().IsValidStorageDeviceType(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(true).AnyTimes()
mockHwValidator.EXPECT().ListEligibleDisks(gomock.Any()).Return([]*models.Disk{}).AnyTimes()
mockHwValidator.EXPECT().GetHostInstallationPath(gomock.Any()).Return("/dev/sda").AnyTimes()
mockOperators.EXPECT().ValidateHost(gomock.Any(), gomock.Any(), gomock.Any()).Return([]api.ValidationResult{
Expand Down Expand Up @@ -1557,7 +1557,7 @@ var _ = Describe("Validations test", func() {
updateClusterPlatform()
host = hostutil.GetHostFromDB(*host.ID, host.InfraEnvID, db).Host

mockHwValidator.EXPECT().IsValidStorageDeviceType(CDROM, models.ClusterCPUArchitectureX8664).Return(false).Times(1)
mockHwValidator.EXPECT().IsValidStorageDeviceType(CDROM, models.ClusterCPUArchitectureX8664, "", false).Return(false).Times(1)
mockAndRefreshStatus(&host)
host = hostutil.GetHostFromDB(*host.ID, host.InfraEnvID, db).Host
status, _, _ := getValidationResult(host.ValidationsInfo, hostUUIDValidation)
Expand Down
4 changes: 3 additions & 1 deletion internal/host/validator.go
Expand Up @@ -1703,7 +1703,9 @@ func (v *validator) isVSphereDiskUUIDEnabled(c *validationContext) (ValidationSt
// if any of them doesn't have that flag, it's likely because the user has forgotten to
// enable `disk.EnableUUID` for this virtual machine
// See https://access.redhat.com/solutions/4606201
if v.hwValidator.IsValidStorageDeviceType(disk, c.inventory.CPU.Architecture) && !disk.HasUUID {
if v.hwValidator.IsValidStorageDeviceType(disk, c.inventory.CPU.Architecture, "", false) &&
funk.ContainsString([]string{string(models.DriveTypeHDD), string(models.DriveTypeSSD)}, string(disk.DriveType)) && // skip network disks
!disk.HasUUID {
return ValidationFailure, "VSphere disk.EnableUUID isn't enabled for this virtual machine, it's necessary for disks to be mounted properly"
}
}
Expand Down

0 comments on commit 1f120d2

Please sign in to comment.