Skip to content

Commit

Permalink
use raw format in lsblk in order to support a wider range of versions
Browse files Browse the repository at this point in the history
  • Loading branch information
plaffitt committed Nov 2, 2021
1 parent 75f4b8e commit b0ce999
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 34 deletions.
58 changes: 49 additions & 9 deletions iscsi/iscsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ type iscsiSession struct {
Name string
}

type deviceInfo struct {
BlockDevices []Device
}
type deviceInfo []Device

// Device contains informations about a device
type Device struct {
Expand Down Expand Up @@ -483,7 +481,7 @@ func GetSCSIDevices(devicePaths []string, strict bool) ([]Device, error) {
return nil, err
}

return deviceInfo.BlockDevices, nil
return deviceInfo, nil
}

// GetISCSIDevices get iSCSI devices from device paths
Expand All @@ -505,8 +503,8 @@ func GetISCSIDevices(devicePaths []string, strict bool) (devices []Device, err e
}

// lsblk execute the lsblk commands
func lsblk(devicePaths []string, strict bool) (*deviceInfo, error) {
flags := []string{"-J", "-o", "NAME,HCTL,TYPE,TRAN,SIZE"}
func lsblk(devicePaths []string, strict bool) (deviceInfo, error) {
flags := []string{"-rn", "-o", "NAME,KNAME,PKNAME,HCTL,TYPE,TRAN,SIZE"}
command := execCommand("lsblk", append(flags, devicePaths...)...)
debug.Println(command.String())
out, err := command.Output()
Expand All @@ -522,12 +520,54 @@ func lsblk(devicePaths []string, strict bool) (*deviceInfo, error) {
}
}

var devices []*Device
devicesMap := make(map[string]*Device)
pkNames := []string{}

// Parse devices
lines := strings.Split(strings.Trim(string(out), "\n"), "\n")
for _, line := range lines {
columns := strings.Split(line, " ")
if len(columns) < 5 {
return nil, fmt.Errorf("invalid output from lsblk: %s", line)
}
device := &Device{
Name: columns[0],
Hctl: columns[3],
Type: columns[4],
Transport: columns[5],
Size: columns[6],
}
devices = append(devices, device)
pkNames = append(pkNames, columns[2])
devicesMap[columns[1]] = device
}

// Reconstruct devices tree
for i, pkName := range pkNames {
if pkName == "" {
continue
}
device := devices[i]
parent, ok := devicesMap[pkName]
if !ok {
return nil, fmt.Errorf("invalid output from lsblk: parent device %q not found", pkName)
}
if parent.Children == nil {
parent.Children = []Device{}
}
parent.Children = append(devicesMap[pkName].Children, *device)
}

// Filter devices to keep only the roots of the tree
var deviceInfo deviceInfo
if jsonErr := json.Unmarshal(out, &deviceInfo); jsonErr != nil {
return nil, jsonErr
for i, device := range devices {
if pkNames[i] == "" {
deviceInfo = append(deviceInfo, *device)
}
}

return &deviceInfo, nil
return deviceInfo, nil
}

// writeInSCSIDeviceFile write into special devices files to change devices state
Expand Down
63 changes: 38 additions & 25 deletions iscsi/iscsi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,20 @@ func makeFakeExecWithTimeout(withTimeout bool, output []byte, err error) func(st
}
}

func marshalDeviceInfo(d *deviceInfo) string {
var output string
pkNames := map[string]string{}
for _, device := range *d {
for _, child := range device.Children {
pkNames[child.Name] = device.Name
}
}
for _, device := range *d {
output += fmt.Sprintf("%s %s %s %s %s %s %s\n", device.Name, device.Name, pkNames[device.Name], device.Hctl, device.Type, device.Transport, device.Size)
}
return output
}

func TestExecCommandHelper(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
Expand Down Expand Up @@ -579,16 +593,15 @@ func Test_getMultipathDevice(t *testing.T) {
}

func Test_lsblk(t *testing.T) {
sda := Device{Name: "sda", Children: []Device{{}}}

sdaOutput, err := json.Marshal(deviceInfo{BlockDevices: []Device{sda}})
assert.New(t).Nil(err, "could not setup test")
sda1 := Device{Name: "sda1"}
sda := Device{Name: "sda", Children: []Device{sda1}}
sdaOutput := marshalDeviceInfo(&deviceInfo{sda, sda1})

tests := map[string]struct {
devicePaths []string
strict bool
mockedStdout string
mockedDevices []Device
mockedDevices deviceInfo
mockedExitStatus int
wantErr bool
}{
Expand All @@ -603,7 +616,7 @@ func Test_lsblk(t *testing.T) {
mockedExitStatus: 32,
wantErr: true,
},
"InvalidJson": {
"InvalidOutput": {
mockedStdout: "{",
mockedExitStatus: 0,
wantErr: true,
Expand Down Expand Up @@ -636,7 +649,7 @@ func Test_lsblk(t *testing.T) {
assert.NotNil(err)
} else {
assert.NotNil(deviceInfo)
assert.Equal(tt.mockedDevices, deviceInfo.BlockDevices)
assert.Equal(tt.mockedDevices, deviceInfo)
assert.Nil(err)
}
})
Expand All @@ -654,17 +667,17 @@ func TestConnectorPersistance(t *testing.T) {
PasswordIn: "fake password in",
}
childDevice := Device{
Name: "child name",
Hctl: "child hctl",
Type: "child type",
Transport: "child transport",
Name: "child-name",
Hctl: "child-hctl",
Type: "child-type",
Transport: "child-transport",
}
device := Device{
Name: "device name",
Hctl: "device hctl",
Name: "device-name",
Hctl: "device-hctl",
Children: []Device{childDevice},
Type: "device type",
Transport: "device transport",
Type: "device-type",
Transport: "device-transport",
}
c := Connector{
VolumeName: "fake volume name",
Expand All @@ -687,32 +700,32 @@ func TestConnectorPersistance(t *testing.T) {
devicesByPath[device.GetPath()] = &device

defer gostub.Stub(&execCommand, func(name string, arg ...string) *exec.Cmd {
blockDevices := []Device{}
blockDevices := deviceInfo{}
for _, path := range arg[3:] {
blockDevices = append(blockDevices, *devicesByPath[path])
}

out, err := json.Marshal(deviceInfo{BlockDevices: blockDevices})
assert.Nil(err, "could not setup test")
out := marshalDeviceInfo(&blockDevices)
return makeFakeExecCommand(0, string(out))(name, arg...)
}).Reset()

defer gostub.Stub(&execCommand, func(cmd string, args ...string) *exec.Cmd {
mockedDevice := device
if args[3] == "/dev/child name" {
mockedDevice = childDevice
devInfo := &deviceInfo{device, childDevice}
if args[3] == "/dev/child-name" {
devInfo = &deviceInfo{childDevice}
}

mockedOutput, err := json.Marshal(deviceInfo{BlockDevices: []Device{mockedDevice}})
assert.Nil(err, "could not setup test")

mockedOutput := marshalDeviceInfo(devInfo)
return makeFakeExecCommand(0, string(mockedOutput))(cmd, args...)
}).Reset()

c.Persist("/tmp/connector.json")
c2, err := GetConnectorFromFile("/tmp/connector.json")
assert.Nil(err)
assert.Equal(c, *c2)
assert.NotNil(c2)
if c2 != nil {
assert.Equal(c, *c2)
}

err = c.Persist("/tmp")
assert.NotNil(err)
Expand Down

0 comments on commit b0ce999

Please sign in to comment.