Skip to content

Commit

Permalink
✨ multiple block devices support (#4892)
Browse files Browse the repository at this point in the history
* feat: add unpartitioned volumes support

* feat: device-names option & multiple devices support

* test: TestGetMountablePartitions with unpartitioned device

* fix: use deprecated flag option
  • Loading branch information
slntopp authored Nov 20, 2024
1 parent 769aae4 commit 5ad0984
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 12 deletions.
6 changes: 6 additions & 0 deletions providers/os/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ var Config = plugin.Provider{
Long: "device-name",
Type: plugin.FlagType_String,
Desc: "The target device to scan, e.g. /dev/sda. Supported only for Linux scanning. Do not use together with --lun or --serial-number",
Option: plugin.FlagOption_Hidden | plugin.FlagOption_Deprecated,
},
{
Long: "device-names",
Type: plugin.FlagType_List,
Desc: "The target devices to scan, e.g. /dev/sda. Supported only for Linux scanning. Do not use together with --lun or --serial-number",
Option: plugin.FlagOption_Hidden,
},
{
Expand Down
39 changes: 28 additions & 11 deletions providers/os/connection/device/linux/device_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package linux

import (
"strconv"
"strings"

"github.com/cockroachdb/errors"
"github.com/rs/zerolog/log"
Expand All @@ -14,6 +15,7 @@ import (
const (
LunOption = "lun"
DeviceName = "device-name"
DeviceNames = "device-names"
MountAllPartitions = "mount-all-partitions"
)

Expand Down Expand Up @@ -53,11 +55,23 @@ func (d *LinuxDeviceManager) IdentifyMountTargets(opts map[string]string) ([]*sn
return []*snapshot.PartitionInfo{pi}, nil
}

partitions, err := d.identifyViaDeviceName(opts[DeviceName], opts[MountAllPartitions] == "true")
if err != nil {
return nil, err
deviceNames := strings.Split(opts[DeviceNames], ",")
if opts[DeviceName] != "" {
deviceNames = append(deviceNames, opts[DeviceName])
}
return partitions, nil

var partitions []*snapshot.PartitionInfo
var errs []error
for _, deviceName := range deviceNames {
partitionsForDevice, err := d.identifyViaDeviceName(deviceName, opts[MountAllPartitions] == "true")
if err != nil {
errs = append(errs, err)
continue
}
partitions = append(partitions, partitionsForDevice...)
}

return partitions, errors.Join(errs...)
}

func (d *LinuxDeviceManager) Mount(pi *snapshot.PartitionInfo) (string, error) {
Expand Down Expand Up @@ -86,18 +100,21 @@ func (d *LinuxDeviceManager) UnmountAndClose() {
// we cannot have both LUN and device name provided, those are mutually exclusive
func validateOpts(opts map[string]string) error {
lun := opts[LunOption]
deviceName := opts[DeviceName]

// this is needed only for the validation purposes
deviceNames := opts[DeviceNames] + opts[DeviceName]

mountAll := opts[MountAllPartitions] == "true"
if lun != "" && deviceName != "" {
return errors.New("both lun and device name provided")
if lun != "" && deviceNames != "" {
return errors.New("both lun and device names provided")
}

if lun == "" && deviceName == "" {
return errors.New("either lun or device name must be provided")
if lun == "" && deviceNames == "" {
return errors.New("either lun or device names must be provided")
}

if deviceName == "" && mountAll {
return errors.New("mount-all-partitions requires a device name")
if deviceNames == "" && mountAll {
return errors.New("mount-all-partitions requires device names")
}

return nil
Expand Down
3 changes: 3 additions & 0 deletions providers/os/connection/snapshot/blockdevices.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ func (device BlockDevice) GetMountablePartitions(includeAll bool) ([]*PartitionI
blockDevices := &BlockDevices{
BlockDevices: device.Children,
}
if len(blockDevices.BlockDevices) == 0 && device.FsType != "" {
blockDevices.BlockDevices = append(blockDevices.BlockDevices, device)
}

// sort the candidates by size, so we can pick the largest one
sortBlockDevicesBySize(blockDevices.BlockDevices)
Expand Down
15 changes: 15 additions & 0 deletions providers/os/connection/snapshot/blockdevices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,21 @@ func TestGetMountablePartitions(t *testing.T) {
}
require.ElementsMatch(t, expected, parts)
})

t.Run("get all non-mounted partitions (unpartitioned)", func(t *testing.T) {
block := BlockDevice{
Name: "sda",
FsType: "xfs",
Label: "ROOT",
Uuid: "1234",
}
parts, err := block.GetMountablePartitions(true)
require.NoError(t, err)
expected := []*PartitionInfo{
{Name: "/dev/sda", FsType: "xfs", Uuid: "1234", Label: "ROOT"},
}
require.ElementsMatch(t, expected, parts)
})
}

func TestLongestMatchingSuffix(t *testing.T) {
Expand Down
12 changes: 11 additions & 1 deletion providers/os/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,19 @@ func (s *Service) ParseCLI(req *plugin.ParseCLIReq) (*plugin.ParseCLIRes, error)
if lun, ok := flags["lun"]; ok {
conf.Options["lun"] = lun.RawData().Value.(string)
}

deviceNames := []string{}
if deviceName, ok := flags["device-name"]; ok {
conf.Options["device-name"] = deviceName.RawData().Value.(string)
deviceNames = append(deviceNames, deviceName.RawData().Value.(string))
}
if deviceName, ok := flags["device-names"]; ok {
deviceNamesList := deviceName.RawData().Value.([]any)
for _, deviceName := range deviceNamesList {
deviceNames = append(deviceNames, deviceName.(string))
}
}
conf.Options["device-names"] = strings.Join(deviceNames, ",")

if serialNumber, ok := flags["serial-number"]; ok {
conf.Options["serial-number"] = serialNumber.RawData().Value.(string)
}
Expand Down

0 comments on commit 5ad0984

Please sign in to comment.