Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

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

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

17 changes: 3 additions & 14 deletions internal/host/hostcommands/install_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,20 +382,9 @@ func appendISCSIArgs(installerArgs []string, installationDisk *models.Disk, inve
return nil, fmt.Errorf("Cannot parse iSCSI host IP %s: %w", installationDisk.Iscsi.HostIPAddress, err)
}

nic, ok := lo.Find(inventory.Interfaces, func(nic *models.Interface) bool {
ips := nic.IPV4Addresses
if iSCSIHostIP.Is6() {
ips = nic.IPV6Addresses
}
_, ok := lo.Find(ips, func(ip string) bool {
prefix, err := netip.ParsePrefix(ip)
return err == nil && iSCSIHostIP.Compare(prefix.Addr()) == 0
})
return ok
})

if !ok {
return nil, fmt.Errorf("Cannot find the interface belonging to iSCSI host IP %s", iSCSIHostIP.String())
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"
Expand Down
2 changes: 1 addition & 1 deletion internal/host/hostcommands/install_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ var _ = Describe("construct host install arguments", func() {
inventory, _ := common.UnmarshalInventory(host.Inventory)
_, err := constructHostInstallerArgs(cluster, host, inventory, infraEnv, log)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("Cannot find the interface belonging to iSCSI host IP 10.56.21.80"))
Expect(err.Error()).To(Equal("Cannot find the interface belonging to iSCSI host IP: Cannot find the network interface on which the IP 10.56.21.80 is set"))
})
It("iSCSI installation disk - OCI - IPv4 address is left un-configured on machine networks interface", func() {
cluster.MachineNetworks = []*models.MachineNetwork{{Cidr: "10.56.21.0/25"}}
Expand Down
4 changes: 4 additions & 0 deletions internal/host/refresh_status_preprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ func newValidations(v *validator) []validation {
id: NoIPCollisionsInNetwork,
condition: v.noIPCollisionsInNetwork,
},
{
id: NoIscsiNicBelongsToMachineCidr,
condition: v.noIscsiNicBelongsToMachineCidr,
},
}
}

Expand Down
1 change: 1 addition & 0 deletions internal/host/statemachine.go
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,7 @@ func NewHostStateMachine(sm stateswitch.StateMachine, th TransitionHandler) stat
If(NoSkipInstallationDisk),
If(NoSkipMissingDisk),
If(NoIPCollisionsInNetwork),
If(NoIscsiNicBelongsToMachineCidr),
/*
* MGMT-15213: The release domain is not resolved correctly when there is a mirror or proxy. In this case
* validation might fail, but the installation may succeed.
Expand Down
14 changes: 14 additions & 0 deletions internal/host/transition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6556,6 +6556,7 @@ var allValidationIDs = []validationID{
NoSkipMissingDisk,
NoIPCollisionsInNetwork,
IsReleaseDomainNameResolvedCorrectly,
NoIscsiNicBelongsToMachineCidr,
}

var allConditions = []conditionId{
Expand Down Expand Up @@ -6708,6 +6709,19 @@ var _ = Describe("State machine test - refresh transition", func() {
Expect(stateMachine.Run(TransitionTypeRefresh, testState, &refreshHostArgs)).To(Succeed())
Expect(string(testState.State())).To(Equal(models.HostStatusKnown))
})

It("Moves from known to insufficient when validation fails", func() {

refreshHostArgs.conditions[string(NoIscsiNicBelongsToMachineCidr)] = false

Expect(stateMachine.Run(TransitionTypeRefresh, testState, &refreshHostArgs)).To(Succeed())
Expect(string(testState.State())).To(Equal(models.HostStatusInsufficient))

refreshHostArgs.conditions[string(NoIscsiNicBelongsToMachineCidr)] = true

Expect(stateMachine.Run(TransitionTypeRefresh, testState, &refreshHostArgs)).To(Succeed())
Expect(string(testState.State())).To(Equal(models.HostStatusKnown))
})
})

})
4 changes: 3 additions & 1 deletion internal/host/validation_id.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const (
NoSkipInstallationDisk = validationID(models.HostValidationIDNoSkipInstallationDisk)
NoSkipMissingDisk = validationID(models.HostValidationIDNoSkipMissingDisk)
NoIPCollisionsInNetwork = validationID(models.HostValidationIDNoIPCollisionsInNetwork)
NoIscsiNicBelongsToMachineCidr = validationID(models.HostValidationIDNoIscsiNicBelongsToMachineCidr)
)

func (v validationID) category() (string, error) {
Expand All @@ -75,7 +76,8 @@ func (v validationID) category() (string, error) {
IsDNSWildcardNotConfigured,
NonOverlappingSubnets,
IsReleaseDomainNameResolvedCorrectly,
NoIPCollisionsInNetwork:
NoIPCollisionsInNetwork,
NoIscsiNicBelongsToMachineCidr:
return "network", nil
case HasInventory,
HasMinCPUCores,
Expand Down
251 changes: 251 additions & 0 deletions internal/host/validations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2339,4 +2339,255 @@ var _ = Describe("Validations test", func() {
Expect(ValidationError).To(Equal(status))
})
})
Context("No iSCSI NIC belongs to machine networks", func() {
var (
cluster common.Cluster
host models.Host
)
BeforeEach(func() {
cluster = hostutil.GenerateTestCluster(clusterID)
hostId, infraEnvId := strfmt.UUID(uuid.New().String()), strfmt.UUID(uuid.New().String())

mockProviderRegistry.EXPECT().IsHostSupported(commontesting.EqPlatformType(models.PlatformTypeVsphere), gomock.Any()).Return(false, nil).AnyTimes()

host = hostutil.GenerateTestHostByKind(hostId, infraEnvId, &clusterID, models.HostStatusKnown, models.HostKindHost, models.HostRoleMaster)
host.Inventory = hostutil.GenerateMasterInventory()
})

It("No installation disk", func() {
var inventory models.Inventory
err := json.Unmarshal([]byte(host.Inventory), &inventory)
Expect(err).ShouldNot(HaveOccurred())

host.InstallationDiskID = "not-foo"
inventory.Disks = []*models.Disk{
{ID: "foo", DriveType: models.DriveTypeSSD},
}

inventoryByte, _ := json.Marshal(inventory)
host.Inventory = string(inventoryByte)

Expect(db.Create(&cluster).Error).ToNot(HaveOccurred())
Expect(db.Create(&host).Error).ShouldNot(HaveOccurred())
mockAndRefreshStatus(&host)

refreshedHost := hostutil.GetHostFromDB(*host.ID, host.InfraEnvID, db).Host

_, _, ok := getValidationResult(refreshedHost.ValidationsInfo, NoIscsiNicBelongsToMachineCidr)
Expect(ok).To(BeFalse())
})
It("Not iSCSI drive", func() {
var inventory models.Inventory
err := json.Unmarshal([]byte(host.Inventory), &inventory)
Expect(err).ShouldNot(HaveOccurred())

host.InstallationDiskID = "install-disk"
inventory.Disks = []*models.Disk{
{ID: "install-disk", DriveType: models.DriveTypeSSD},
}

inventoryByte, _ := json.Marshal(inventory)
host.Inventory = string(inventoryByte)

Expect(db.Create(&cluster).Error).ToNot(HaveOccurred())
Expect(db.Create(&host).Error).ShouldNot(HaveOccurred())
mockAndRefreshStatus(&host)

refreshedHost := hostutil.GetHostFromDB(*host.ID, host.InfraEnvID, db).Host

_, _, ok := getValidationResult(refreshedHost.ValidationsInfo, NoIscsiNicBelongsToMachineCidr)
Expect(ok).To(BeFalse())
})
It("iSCSI drive - no network interface set with host IP", func() {
var inventory models.Inventory
err := json.Unmarshal([]byte(host.Inventory), &inventory)
Expect(err).ShouldNot(HaveOccurred())

host.InstallationDiskID = "install-disk"
inventory.Disks = []*models.Disk{
{
ID: "install-disk",
DriveType: models.DriveTypeISCSI,
Iscsi: &models.Iscsi{
HostIPAddress: "99.99.99.99",
}},
}

inventoryByte, _ := json.Marshal(inventory)
host.Inventory = string(inventoryByte)

Expect(db.Create(&cluster).Error).ToNot(HaveOccurred())
Expect(db.Create(&host).Error).ShouldNot(HaveOccurred())
mockAndRefreshStatus(&host)

refreshedHost := hostutil.GetHostFromDB(*host.ID, host.InfraEnvID, db).Host

status, message, ok := getValidationResult(refreshedHost.ValidationsInfo, NoIscsiNicBelongsToMachineCidr)
Expect(ok).To(BeTrue())
Expect(status).To(Equal(ValidationError))
Expect(message).To(Equal("Cannot find network interface associated to iSCSI host IP address"))
})

DescribeTable(
"iSCSI drive - malformed iSCSI properties",
func(iSCSI *models.Iscsi, expectedMessage string) {
var inventory models.Inventory
err := json.Unmarshal([]byte(host.Inventory), &inventory)
Expect(err).ShouldNot(HaveOccurred())

host.InstallationDiskID = "install-disk"
inventory.Disks = []*models.Disk{
{
ID: "install-disk",
DriveType: models.DriveTypeISCSI,
Iscsi: iSCSI,
},
}

inventoryByte, _ := json.Marshal(inventory)
host.Inventory = string(inventoryByte)

Expect(db.Create(&cluster).Error).ToNot(HaveOccurred())
Expect(db.Create(&host).Error).ShouldNot(HaveOccurred())
mockAndRefreshStatus(&host)

refreshedHost := hostutil.GetHostFromDB(*host.ID, host.InfraEnvID, db).Host

status, message, ok := getValidationResult(refreshedHost.ValidationsInfo, NoIscsiNicBelongsToMachineCidr)
Expect(ok).To(BeTrue())
Expect(status).To(Equal(ValidationError))
Expect(message).To(Equal(expectedMessage))
},
Entry("iSCSI is nil", nil, "iSCSI installation disk is missing host IP address"),
Entry("Host IP address is empty", &models.Iscsi{}, "Cannot find network interface associated to iSCSI host IP address"),
Entry("Host IP address is invalid", &models.Iscsi{HostIPAddress: "invalid"}, "Cannot find network interface associated to iSCSI host IP address"),
)
DescribeTable(
"iSCSI drive - machine networks",
func(iSCSIHostIPAdress string, machineNetworks []*models.MachineNetwork, interfaces []*models.Interface, expectedStatus ValidationStatus, expectedMessage string) {
var inventory models.Inventory
err := json.Unmarshal([]byte(host.Inventory), &inventory)
Expect(err).ShouldNot(HaveOccurred())

host.InstallationDiskID = "install-disk"
inventory.Disks = []*models.Disk{
{
ID: "install-disk",
DriveType: models.DriveTypeISCSI,
Iscsi: &models.Iscsi{
HostIPAddress: iSCSIHostIPAdress,
},
},
}
inventory.Interfaces = interfaces

inventoryByte, _ := json.Marshal(inventory)
host.Inventory = string(inventoryByte)
cluster.MachineNetworks = machineNetworks
Expect(db.Create(&cluster).Error).ToNot(HaveOccurred())
Expect(db.Create(&host).Error).ShouldNot(HaveOccurred())
mockAndRefreshStatus(&host)

refreshedHost := hostutil.GetHostFromDB(*host.ID, host.InfraEnvID, db).Host

status, message, ok := getValidationResult(refreshedHost.ValidationsInfo, NoIscsiNicBelongsToMachineCidr)
Expect(ok).To(BeTrue())
Expect(message).To(Equal(expectedMessage))
Expect(status).To(Equal(expectedStatus))
},
Entry("Machine networks is nil", "192.168.1.1", nil, nil, ValidationPending, "Missing inventory or machine network CIDR"),
Entry("Machine networks is empty", "192.168.1.1", []*models.MachineNetwork{}, nil, ValidationPending, "Missing inventory or machine network CIDR"),
Entry("Machine networks IPv4 overlaps with host IPv4 address", "192.168.1.1",
[]*models.MachineNetwork{
{
Cidr: "192.168.1.0/24",
ClusterID: clusterID,
},
},
[]*models.Interface{
{
IPV4Addresses: []string{"192.168.1.1/24"},
},
},
ValidationFailure, "Network interface connected to iSCSI disk cannot belong to machine network CIDRs"),
Entry("Machine networks IPv6 overlaps with host IPv6 address", "2001:0db8:85a3::8a2e:0370:7334",
[]*models.MachineNetwork{
{
Cidr: "2001:0db8:85a3::/48",
ClusterID: clusterID,
},
},
[]*models.Interface{
{
IPV6Addresses: []string{"2001:0db8:85a3::8a2e:0370:7334/48"},
},
},
ValidationFailure, "Network interface connected to iSCSI disk cannot belong to machine network CIDRs"),
Entry("Machine networks IPv6 uses the same interface as host IPv4 address", "192.168.1.1",
[]*models.MachineNetwork{
{
Cidr: "2001:0db8:85a3::/48",
ClusterID: clusterID,
},
},
[]*models.Interface{
{
IPV4Addresses: []string{"192.168.1.1/24"},
IPV6Addresses: []string{"2001:0db8:85a3::8a2e:0370:7334/48"},
},
},
ValidationFailure, "Network interface connected to iSCSI disk cannot belong to machine network CIDRs"),
Entry("Machine networks IPv4 uses the same interface as host IPv6 address", "2001:0db8:85a3::8a2e:0370:7334",
[]*models.MachineNetwork{
{
Cidr: "192.168.1.1/24",
ClusterID: clusterID,
},
},
[]*models.Interface{
{
IPV4Addresses: []string{"192.168.1.1/24"},
IPV6Addresses: []string{"2001:0db8:85a3::8a2e:0370:7334/48"},
},
},
ValidationFailure, "Network interface connected to iSCSI disk cannot belong to machine network CIDRs"),
Entry("Machine networks IPv4 and host IPv4 address use different interfaces", "192.168.1.1",
[]*models.MachineNetwork{
{
Cidr: "192.168.2.1/24",
ClusterID: clusterID,
},
},
[]*models.Interface{
{
IPV4Addresses: []string{"192.168.1.1/24"},
},
{
IPV4Addresses: []string{"192.168.2.1/24"},
},
},
ValidationSuccess, "Network interface connected to iSCSI disk does not belong to machine network CIDRs"),
Entry("Machine networks IPv4/IPv6 and host IPv4 address use different interfaces", "192.168.1.1",
[]*models.MachineNetwork{
{
Cidr: "192.168.2.1/24",
ClusterID: clusterID,
},
{
Cidr: "2001:0db8:85a3::/48",
ClusterID: clusterID,
},
},
[]*models.Interface{
Comment thread
adriengentil marked this conversation as resolved.
{
IPV4Addresses: []string{"192.168.1.1/24"},
},
{
IPV4Addresses: []string{"192.168.2.1/24"},
IPV6Addresses: []string{"2001:0db8:85a3::8a2e:0370:7334/48"},
},
},
ValidationSuccess, "Network interface connected to iSCSI disk does not belong to machine network CIDRs"),
)
})
})
Loading