Skip to content

Commit

Permalink
fix: support user disks via symlinks
Browse files Browse the repository at this point in the history
The core blockdevice library already supported resolving symlinks, we
just need to get the raw block device name from it, and use it
afterwards.

In QEMU provisioner, leave the first (system) disk as virtio (for
performance), and mount user disks as 'ata', which allows `udevd` to
pick up the disk IDs (not available for `virtio`), and use the symlink
path in the tests.

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
  • Loading branch information
smira committed Dec 5, 2023
1 parent 4f195dd commit 270604b
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 29 deletions.
6 changes: 6 additions & 0 deletions hack/release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ cluster:
extraArgs:
- --iface-can-reach=192.168.1.1
```
"""

[notes.user-disks]
title = "User Disks"
description = """\
Talos Linux now supports specifying user disks in `.machine.disks` machine configuration links via `udev` symlinks, e.g. `/dev/disk/by-id/XXXX`.
"""

[make_deps]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ func MountUserDisks(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
return err
}

return mountDisks(r)
return mountDisks(logger, r)
}, "mountUserDisks"
}

Expand All @@ -838,6 +838,12 @@ func partitionAndFormatDisks(logger *log.Logger, r runtime.Runtime) error {
return err
}

deviceName := bd.Device().Name()

if disk.Device() != deviceName {
logger.Printf("using device name %q instead of %q", deviceName, disk.Device())
}

//nolint:errcheck
defer bd.Close()

Expand All @@ -857,21 +863,21 @@ func partitionAndFormatDisks(logger *log.Logger, r runtime.Runtime) error {

if pt != nil {
if len(pt.Partitions().Items()) > 0 {
logger.Printf(("skipping setup of %q, found existing partitions"), disk.Device())
logger.Printf(("skipping setup of %q, found existing partitions"), deviceName)

return nil
}
}

m.Devices[disk.Device()] = installer.Device{
Device: disk.Device(),
m.Devices[deviceName] = installer.Device{
Device: deviceName,
ResetPartitionTable: true,
SkipOverlayMountsCheck: true,
}

for _, part := range disk.Partitions() {
extraTarget := &installer.Target{
Device: disk.Device(),
Device: deviceName,
FormatOptions: &partition.FormatOptions{
Force: true,
FileSystemType: partition.FilesystemTypeXFS,
Expand All @@ -882,7 +888,7 @@ func partitionAndFormatDisks(logger *log.Logger, r runtime.Runtime) error {
},
}

m.Targets[disk.Device()] = append(m.Targets[disk.Device()], extraTarget)
m.Targets[deviceName] = append(m.Targets[deviceName], extraTarget)
}

return nil
Expand All @@ -894,14 +900,29 @@ func partitionAndFormatDisks(logger *log.Logger, r runtime.Runtime) error {
return m.Execute()
}

func mountDisks(r runtime.Runtime) (err error) {
func mountDisks(logger *log.Logger, r runtime.Runtime) (err error) {
mountpoints := mount.NewMountPoints()

for _, disk := range r.Config().Machine().Disks() {
bd, err := blockdevice.Open(disk.Device(), blockdevice.WithMode(blockdevice.ReadonlyMode))
if err != nil {
return err
}

deviceName := bd.Device().Name()

if disk.Device() != deviceName {
logger.Printf("using device name %q instead of %q", deviceName, disk.Device())
}

if err = bd.Close(); err != nil {
return err
}

for i, part := range disk.Partitions() {
var partname string

partname, err = util.PartPath(disk.Device(), i+1)
partname, err = util.PartPath(deviceName, i+1)
if err != nil {
return err
}
Expand Down Expand Up @@ -1137,10 +1158,25 @@ func UnmountUserDisks(runtime.Sequence, any) (runtime.TaskExecutionFunc, string)
mountpoints := mount.NewMountPoints()

for _, disk := range r.Config().Machine().Disks() {
bd, err := blockdevice.Open(disk.Device(), blockdevice.WithMode(blockdevice.ReadonlyMode))
if err != nil {
return err
}

deviceName := bd.Device().Name()

if deviceName != disk.Device() {
logger.Printf("using device name %q instead of %q", deviceName, disk.Device())
}

if err = bd.Close(); err != nil {
return err
}

for i, part := range disk.Partitions() {
var partname string

partname, err = util.PartPath(disk.Device(), i+1)
partname, err = util.PartPath(deviceName, i+1)
if err != nil {
return err
}
Expand Down
10 changes: 8 additions & 2 deletions pkg/provision/providers/qemu/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,14 @@ func launchVM(config *LaunchConfig) error {
"virtserialport,chardev=qga0,name=org.qemu.guest_agent.0",
}

for _, disk := range config.DiskPaths {
args = append(args, "-drive", fmt.Sprintf("format=raw,if=virtio,file=%s,cache=unsafe", disk))
for i, disk := range config.DiskPaths {
driver := "virtio"

if i > 0 {
driver = "ide"
}

args = append(args, "-drive", fmt.Sprintf("format=raw,if=%s,file=%s,cache=unsafe", driver, disk))
}

machineArg := config.MachineType
Expand Down
23 changes: 5 additions & 18 deletions pkg/provision/providers/vm/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,11 @@ import (

// UserDiskName returns disk device path.
func (p *Provisioner) UserDiskName(index int) string {
res := "/dev/vd"

var convert func(i int) string

convert = func(i int) string {
remainder := i % 26
divider := i / 26

prefix := ""

if divider != 0 {
prefix = convert(divider - 1)
}

return fmt.Sprintf("%s%s", prefix, string(rune('a'+remainder)))
}

return res + convert(index)
// the disk IDs are assigned in the following way:
// * ata-QEMU_HARDDISK_QM00001
// * ata-QEMU_HARDDISK_QM00003
// * ata-QEMU_HARDDISK_QM00005
return fmt.Sprintf("/dev/disk/by-id/ata-QEMU_HARDDISK_QM%05d", (index-1)*2+1)
}

// CreateDisks creates empty disk files for each disk.
Expand Down

0 comments on commit 270604b

Please sign in to comment.