Skip to content

Commit 2af5cc1

Browse files
authored
Merge pull request lxc#2282 from bensmrs/eject-disk
Implement disk ejection
2 parents 5e8806d + 1a7076e commit 2af5cc1

File tree

10 files changed

+235
-80
lines changed

10 files changed

+235
-80
lines changed

doc/api-extensions.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2838,3 +2838,7 @@ on schedule.
28382838
This adds tracking of CPU address sizes in the resources API.
28392839
The main use of this is within clusters to calculate a cluster-wide
28402840
maximum memory amount for hotplugging into virtual machines.
2841+
2842+
## `disk_attached`
2843+
2844+
This introduces a new `attached` property to disk devices describing whether disks are attached or ejected.

doc/config_options.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ User keys can be used in search.
4242

4343
<!-- config group cluster_group-common end -->
4444
<!-- config group devices-disk start -->
45+
```{config:option} attached devices-disk
46+
:default: "`true`"
47+
:required: "no"
48+
:shortdesc: "Only for VMs: Whether the disk is attached or ejected"
49+
:type: "bool"
50+
51+
```
52+
4553
```{config:option} boot.priority devices-disk
4654
:required: "no"
4755
:shortdesc: "Boot priority for VMs (higher value boots first)"

internal/server/device/config/device_runconfig.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type MountEntryItem struct {
3131
OwnerShift string // Ownership shifting mode, use constants MountOwnerShiftNone, MountOwnerShiftStatic or MountOwnerShiftDynamic.
3232
Limits *DiskLimits // Disk limits.
3333
Size int64 // Expected disk size in bytes.
34+
Attached bool // Whether the disk is attached
3435
}
3536

3637
// RootFSEntryItem represents the root filesystem options for an Instance.

internal/server/device/disk.go

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,15 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
363363
// required: no
364364
// shortdesc: Only for VMs: Override the bus for the device
365365
"io.bus": validate.Optional(validate.IsOneOf("nvme", "virtio-blk", "virtio-scsi", "auto", "9p", "virtiofs", "usb")),
366+
367+
// gendoc:generate(entity=devices, group=disk, key=attached)
368+
//
369+
// ---
370+
// type: bool
371+
// default: `true`
372+
// required: no
373+
// shortdesc: Only for VMs: Whether the disk is attached or ejected
374+
"attached": validate.Optional(validate.IsBool),
366375
}
367376

368377
err := d.config.Validate(rules)
@@ -570,6 +579,16 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
570579
return err
571580
}
572581

582+
if d.config["attached"] != "" {
583+
if instConf.Type() == instancetype.Container {
584+
return errors.New("Attached configuration cannot be applied to containers")
585+
} else if instConf.Type() == instancetype.Any {
586+
return errors.New("Attached configuration cannot be applied to profiles")
587+
} else if contentType != db.StoragePoolVolumeContentTypeISO {
588+
return errors.New("Attached configuration can only be applied to ISO volumes")
589+
}
590+
}
591+
573592
if contentType == db.StoragePoolVolumeContentTypeBlock {
574593
if instConf.Type() == instancetype.Container {
575594
return errors.New("Custom block volumes cannot be used on containers")
@@ -1038,6 +1057,9 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) {
10381057
opts = append(opts, fmt.Sprintf("cache=%s", d.config["io.cache"]))
10391058
}
10401059

1060+
// Setup the attached status.
1061+
attached := util.IsTrueOrEmpty(d.config["attached"])
1062+
10411063
// Add I/O limits if set.
10421064
var diskLimits *deviceConfig.DiskLimits
10431065
if d.config["limits.read"] != "" || d.config["limits.write"] != "" || d.config["limits.max"] != "" {
@@ -1094,10 +1116,11 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) {
10941116
// Encode the file descriptor and original isoPath into the DevPath field.
10951117
runConf.Mounts = []deviceConfig.MountEntryItem{
10961118
{
1097-
DevPath: fmt.Sprintf("%s:%d:%s", DiskFileDescriptorMountPrefix, f.Fd(), isoPath),
1098-
DevName: d.name,
1099-
FSType: "iso9660",
1100-
Opts: opts,
1119+
DevPath: fmt.Sprintf("%s:%d:%s", DiskFileDescriptorMountPrefix, f.Fd(), isoPath),
1120+
DevName: d.name,
1121+
FSType: "iso9660",
1122+
Opts: opts,
1123+
Attached: attached,
11011124
},
11021125
}
11031126

@@ -1124,10 +1147,11 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) {
11241147
// Encode the file descriptor and original isoPath into the DevPath field.
11251148
runConf.Mounts = []deviceConfig.MountEntryItem{
11261149
{
1127-
DevPath: fmt.Sprintf("%s:%d:%s", DiskFileDescriptorMountPrefix, f.Fd(), isoPath),
1128-
DevName: d.name,
1129-
FSType: "iso9660",
1130-
Opts: opts,
1150+
DevPath: fmt.Sprintf("%s:%d:%s", DiskFileDescriptorMountPrefix, f.Fd(), isoPath),
1151+
DevName: d.name,
1152+
FSType: "iso9660",
1153+
Opts: opts,
1154+
Attached: attached,
11311155
},
11321156
}
11331157

@@ -1142,19 +1166,21 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) {
11421166
clusterName, userName := d.cephCreds()
11431167
runConf.Mounts = []deviceConfig.MountEntryItem{
11441168
{
1145-
DevPath: DiskGetRBDFormat(clusterName, userName, fields[0], fields[1]),
1146-
DevName: d.name,
1147-
Opts: opts,
1148-
Limits: diskLimits,
1169+
DevPath: DiskGetRBDFormat(clusterName, userName, fields[0], fields[1]),
1170+
DevName: d.name,
1171+
Opts: opts,
1172+
Limits: diskLimits,
1173+
Attached: attached,
11491174
},
11501175
}
11511176
} else {
11521177
// Default to block device or image file passthrough first.
11531178
mount := deviceConfig.MountEntryItem{
1154-
DevPath: d.config["source"],
1155-
DevName: d.name,
1156-
Opts: opts,
1157-
Limits: diskLimits,
1179+
DevPath: d.config["source"],
1180+
DevName: d.name,
1181+
Opts: opts,
1182+
Limits: diskLimits,
1183+
Attached: attached,
11581184
}
11591185

11601186
// Mount the pool volume and update srcPath to mount path so it can be recognised as dir
@@ -1208,10 +1234,11 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) {
12081234
}
12091235

12101236
mount := deviceConfig.MountEntryItem{
1211-
DevPath: DiskGetRBDFormat(clusterName, userName, poolName, d.config["source"]),
1212-
DevName: d.name,
1213-
Opts: opts,
1214-
Limits: diskLimits,
1237+
DevPath: DiskGetRBDFormat(clusterName, userName, poolName, d.config["source"]),
1238+
DevName: d.name,
1239+
Opts: opts,
1240+
Limits: diskLimits,
1241+
Attached: attached,
12151242
}
12161243

12171244
if contentType == db.StoragePoolVolumeContentTypeISO {
@@ -1410,9 +1437,10 @@ func (d *disk) postStart() error {
14101437

14111438
// Update applies configuration changes to a started device.
14121439
func (d *disk) Update(oldDevices deviceConfig.Devices, isRunning bool) error {
1440+
expandedDevices := d.inst.ExpandedDevices()
1441+
14131442
if internalInstance.IsRootDiskDevice(d.config) {
14141443
// Make sure we have a valid root disk device (and only one).
1415-
expandedDevices := d.inst.ExpandedDevices()
14161444
newRootDiskDeviceKey, _, err := internalInstance.GetRootDiskDevice(expandedDevices.CloneNative())
14171445
if err != nil {
14181446
return fmt.Errorf("Detect root disk device: %w", err)
@@ -1486,7 +1514,7 @@ func (d *disk) Update(oldDevices deviceConfig.Devices, isRunning bool) error {
14861514
}
14871515
}
14881516

1489-
// Only apply IO limits if instance is running.
1517+
// Only apply IO limits and attach/detach logic if instance is running.
14901518
if isRunning {
14911519
runConf := deviceConfig.RunConfig{}
14921520

@@ -1518,6 +1546,20 @@ func (d *disk) Update(oldDevices deviceConfig.Devices, isRunning bool) error {
15181546
Limits: diskLimits,
15191547
},
15201548
}
1549+
1550+
oldAttached := util.IsTrueOrEmpty(oldDevices[d.name]["attached"])
1551+
newAttached := util.IsTrueOrEmpty(expandedDevices[d.name]["attached"])
1552+
if !oldAttached && newAttached {
1553+
runConf.Mounts = append(runConf.Mounts, deviceConfig.MountEntryItem{
1554+
DevName: d.name,
1555+
Attached: true,
1556+
})
1557+
} else if oldAttached && !newAttached {
1558+
runConf.Mounts = append(runConf.Mounts, deviceConfig.MountEntryItem{
1559+
DevName: d.name,
1560+
Attached: false,
1561+
})
1562+
}
15211563
}
15221564

15231565
err := d.inst.DeviceEventHandler(&runConf)

0 commit comments

Comments
 (0)