Skip to content

Commit

Permalink
feat: mount additional disks when using vz
Browse files Browse the repository at this point in the history
Signed-off-by: Justin Alvarez <alvajus@amazon.com>
  • Loading branch information
pendo324 committed Mar 20, 2023
1 parent 5a9bca3 commit eca1613
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ Use `<INSTANCE>:<FILENAME>` to specify a source or target inside an instance.

#### `limactl disk`

`limactl disk create <DISK> --size <SIZE>`: create a new external disk to attach to an instance
`limactl disk create <DISK> --size <SIZE> [--format qcow2]`: create a new external disk to attach to an instance

`limactl disk delete <DISK>`: delete an existing disk

Expand Down
20 changes: 16 additions & 4 deletions cmd/limactl/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func newDiskCommand() *cobra.Command {
Use: "disk",
Short: "Lima disk management",
Example: ` Create a disk:
$ limactl disk create DISK --size SIZE
$ limactl disk create DISK --size SIZE [--format qcow2]
List existing disks:
$ limactl disk ls
Expand All @@ -44,14 +44,15 @@ func newDiskCreateCommand() *cobra.Command {
Use: "create DISK",
Example: `
To create a new disk:
$ limactl disk create DISK --size SIZE
$ limactl disk create DISK --size SIZE [--format qcow2]
`,
Short: "Create a Lima disk",
Args: WrapArgsError(cobra.ExactArgs(1)),
RunE: diskCreateAction,
}
diskCreateCommand.Flags().String("size", "", "configure the disk size")
diskCreateCommand.MarkFlagRequired("size")
diskCreateCommand.Flags().String("format", "qcow2", "specify the disk format")
return diskCreateCommand
}

Expand All @@ -61,11 +62,22 @@ func diskCreateAction(cmd *cobra.Command, args []string) error {
return err
}

format, err := cmd.Flags().GetString("format")
if err != nil {
return err
}

diskSize, err := units.RAMInBytes(size)
if err != nil {
return err
}

switch format {
case "qcow2", "raw":
default:
return fmt.Errorf(`disk format %q not supported, use "qcow2" or "raw" instead`, format)
}

// only exactly one arg is allowed
name := args[0]

Expand All @@ -78,13 +90,13 @@ func diskCreateAction(cmd *cobra.Command, args []string) error {
return fmt.Errorf("disk %q already exists (%q)", name, diskDir)
}

logrus.Infof("Creating disk %q with size %s", name, units.BytesSize(float64(diskSize)))
logrus.Infof("Creating %s disk %q with size %s", format, name, units.BytesSize(float64(diskSize)))

if err := os.MkdirAll(diskDir, 0700); err != nil {
return err
}

if err := qemu.CreateDataDisk(diskDir, int(diskSize)); err != nil {
if err := qemu.CreateDataDisk(diskDir, format, int(diskSize)); err != nil {
return err
}

Expand Down
6 changes: 5 additions & 1 deletion docs/internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,15 @@ Host agent:
A disk directory contains the following files:

data disk:
- `datadisk`: the qcow2 disk that is attached to an instance
- `datadisk`: the qcow2 or raw disk that is attached to an instance

lock:
- `in_use_by`: symlink to the instance directory that is using the disk

When using `vmType: vz` (Virtualization.framework), on boot, any qcow2 (default) formatted disks that are specified in `additionalDisks` will be converted to RAW since [Virtualization.framework only supports mounting RAW disks](https://developer.apple.com/documentation/virtualization/vzdiskimagestoragedeviceattachment). This conversion enables additional disks to work with both Virtualization.framework and QEMU, but it has some consequences when it comes to interacting with the disks. Most importantly, a regular macOS default `cp` command will copy the _entire_ virtual disk size, instead of just the _used/allocated_ portion. The easiest way to copy only the used data is by adding the `-c` option to cp: `cp -c old_path new_path`. `cp -c` uses clonefile(2) to create a copy-on-write clone of the disk, and should return instantly.

`ls` will also only show the full/virtual size of the disks. To see the allocated space, `du -h disk_path` or `qemu-img info disk_path` can be used instead. See [#1405](https://github.com/lima-vm/lima/pull/1405) for more details.

## Lima cache directory (`~/Library/Caches/lima`)

Currently hard-coded to `~/Library/Caches/lima` on macOS.
Expand Down
4 changes: 2 additions & 2 deletions pkg/qemu/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,14 @@ func EnsureDisk(cfg Config) error {
return nil
}

func CreateDataDisk(dir string, size int) error {
func CreateDataDisk(dir, format string, size int) error {
dataDisk := filepath.Join(dir, filenames.DataDisk)
if _, err := os.Stat(dataDisk); err == nil || !errors.Is(err, fs.ErrNotExist) {
// datadisk already exists
return err
}

args := []string{"create", "-f", "qcow2", dataDisk, strconv.Itoa(size)}
args := []string{"create", "-f", format, dataDisk, strconv.Itoa(size)}
cmd := exec.Command("qemu-img", args...)
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to run %v: %q: %w", cmd.Args, string(out), err)
Expand Down
53 changes: 53 additions & 0 deletions pkg/vz/vm_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/lima-vm/lima/pkg/localpathutil"
"github.com/lima-vm/lima/pkg/networks"
"github.com/lima-vm/lima/pkg/qemu/imgutil"
"github.com/lima-vm/lima/pkg/store"
"github.com/lima-vm/lima/pkg/store/filenames"
"github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -334,6 +335,58 @@ func attachDisks(driver *driver.BaseDriver, vmConfig *vz.VirtualMachineConfigura
}
configurations = append(configurations, diffDisk)

for _, diskName := range driver.Yaml.AdditionalDisks {
d, err := store.InspectDisk(diskName)
if err != nil {
return fmt.Errorf("failed to run load disk %q: %q", diskName, err)
}

if d.Instance != "" {
return fmt.Errorf("failed to run attach disk %q, in use by instance %q", diskName, d.Instance)
}
logrus.Infof("Mounting disk %q on %q", diskName, d.MountPoint)
err = d.Lock(driver.Instance.Dir)
if err != nil {
return fmt.Errorf("failed to run lock disk %q: %q", diskName, err)
}
extraDiskPath := filepath.Join(d.Dir, filenames.DataDisk)

extraDiskFormat, err := imgutil.DetectFormat(extraDiskPath)
if err != nil {
return fmt.Errorf("failed to run detect disk format %q: %q", diskName, err)
}
if extraDiskFormat != "raw" {
rawPath := fmt.Sprintf("%s.raw", extraDiskPath)
qcow2Path := fmt.Sprintf("%s.qcow2", extraDiskPath)
if err = imgutil.QCOWToRaw(extraDiskPath, rawPath); err != nil {
return fmt.Errorf("failed to convert qcow2 disk %q to raw for vz driver: %w", diskName, err)
}
if err = os.Rename(extraDiskPath, qcow2Path); err != nil {
return fmt.Errorf("failed to rename additional disk for vz driver: %w", err)
}
if err = os.Rename(rawPath, extraDiskPath); err != nil {
return fmt.Errorf("failed to rename additional disk for vz driver: %w", err)
}
if err = os.Remove(qcow2Path); err != nil {
logrus.Errorf("Failed to delete unused qcow2 additional disk %q."+
"Disk is no longer needed by Lima and it can be removed manually.", qcow2Path)
}
}

if err = validateDiskFormat(extraDiskPath); err != nil {
return fmt.Errorf("failed to validate extra disk %q: %w", extraDiskPath, err)
}
extraDiskPathAttachment, err := vz.NewDiskImageStorageDeviceAttachmentWithCacheAndSync(extraDiskPath, false, vz.DiskImageCachingModeAutomatic, vz.DiskImageSynchronizationModeFsync)
if err != nil {
return fmt.Errorf("failed to create disk attachment for extra disk %q: %w", extraDiskPath, err)
}
extraDisk, err := vz.NewVirtioBlockDeviceConfiguration(extraDiskPathAttachment)
if err != nil {
return fmt.Errorf("failed to create new virtio block device config for extra disk %q: %w", extraDiskPath, err)
}
configurations = append(configurations, extraDisk)
}

if err = validateDiskFormat(ciDataPath); err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/vz/vz_driver_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func (l *LimaVzDriver) Validate() error {
"PropagateProxyEnv",
"CACertificates",
"Rosetta",
"AdditionalDisks",
); len(unknown) > 0 {
logrus.Warnf("Ignoring: vmType %s: %+v", *l.Yaml.VMType, unknown)
}
Expand Down

0 comments on commit eca1613

Please sign in to comment.