Skip to content
Open
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
52 changes: 52 additions & 0 deletions mantle/kola/tests/misc/multipath.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package misc

import (
"fmt"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -103,6 +104,13 @@ systemd:

[Install]
WantedBy=multi-user.target`)

mpath_single_disk = conf.Butane(`
variant: fcos
version: 1.6.0
kernel_arguments:
should_exist:
- rd.multipath=default`)
)

func init() {
Expand Down Expand Up @@ -132,6 +140,16 @@ func init() {
UserData: mpath_on_var_lib_containers,
AdditionalDisks: []string{"1G:mpath,wwn=1"},
})
// See https://issues.redhat.com/browse/OCPBUGS-56597
register.RegisterTest(&register.Test{
Name: "multipath.single-disk",
Description: "Verify that multipath can be reduced to one path",
Run: runMultipathReduceDisk,
ClusterSize: 1,
Platforms: []string{"qemu"},
UserData: mpath_single_disk,
MultiPathDisk: true,
})
}

func verifyMultipathBoot(c cluster.TestCluster, m platform.Machine) {
Expand Down Expand Up @@ -223,3 +241,37 @@ func waitForCompleteFirstboot(c cluster.TestCluster) {
c.Fatalf("Timed out while waiting for first-boot-complete.target to be ready: %v", err)
}
}

func verifyMultipathDisks(c cluster.TestCluster, m platform.Machine, expect int) {
device := strings.TrimSpace(string(c.MustSSH(m, "sudo multipath -l -v 1")))
if device == "" {
c.Fatalf("Failed to find multipath device")
}
output := string(c.MustSSHf(m, "lsblk --pairs --paths --inverse --output NAME /dev/mapper/%s | grep -v /dev/mapper | wc -l", device))
count, err := strconv.Atoi(strings.TrimSpace(output))
if err != nil {
c.Fatalf("Failed to parse device count: %v", err)
}

if count != expect {
c.Fatalf("Expected %d multipath devices, but found %d", expect, count)
}
}

func runMultipathReduceDisk(c cluster.TestCluster) {
m := c.Machines()[0]
verifyMultipathBoot(c, m)
// wait until first-boot-complete.target is reached
waitForCompleteFirstboot(c)
verifyMultipathDisks(c, m, 2)

if err := m.(platform.QEMUMachine).RemoveBlockDeviceForMultipath("mpath11"); err != nil {
c.Fatalf("Failed to remove multipath disk: %v", err)
}

if err := m.Reboot(); err != nil {
c.Fatalf("Failed to reboot the machine: %v", err)
}
verifyMultipathDisks(c, m, 1)
c.RunCmdSync(m, "grep mpath.wwid= /proc/cmdline")
}
4 changes: 4 additions & 0 deletions mantle/platform/machine/qemu/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,7 @@ func (m *machine) JournalOutput() string {
func (m *machine) RemovePrimaryBlockDevice() error {
return m.inst.RemovePrimaryBlockDevice()
}

func (m *machine) RemoveBlockDeviceForMultipath(device string) error {
return m.inst.RemoveBlockDeviceForMultipath(device)
}
26 changes: 26 additions & 0 deletions mantle/platform/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ type QEMUMachine interface {
// RemovePrimaryBlockDevice removes the primary device from a given qemu
// instance and sets the secondary device as primary.
RemovePrimaryBlockDevice() error
// RemoveBlockDeviceForMultipath removes the specified device on multipath.
RemoveBlockDeviceForMultipath(device string) error
}

// Disk holds the details of a virtual disk.
Expand Down Expand Up @@ -445,6 +447,30 @@ func (inst *QemuInstance) RemovePrimaryBlockDevice() (err2 error) {
return nil
}

// RemoveBlockDeviceForMultipath remove the specified device on multipath.
func (inst *QemuInstance) RemoveBlockDeviceForMultipath(device string) error {
blkdevs, err := inst.listBlkDevices()
if err != nil {
return errors.Wrapf(err, "Could not list block devices through qmp")
}

var devicePath string
for _, dev := range blkdevs.Return {
if dev.Device == device {
devicePath = dev.DevicePath
break
}
}
if devicePath == "" {
return fmt.Errorf("Target device %q not found in block device list", device)
}

if err = inst.deleteBlockDevice(devicePath); err != nil {
return errors.Wrapf(err, "Could not delete device %v", devicePath)
}
return nil
}

// A directory mounted from the host into the guest, via 9p or virtiofs
type HostMount struct {
src string
Expand Down
Loading