[pve-devel] [PATCH qemu-server v3 23/51] blockdev: add helpers for attaching and detaching block devices
Fiona Ebner
f.ebner at proxmox.com
Wed Jul 2 18:27:56 CEST 2025
Signed-off-by: Fiona Ebner <f.ebner at proxmox.com>
---
Changes in v3:
* Adapt to latest version of QEMU patch for child node name info.
src/PVE/QemuServer/Blockdev.pm | 141 +++++++++++++++++++++++++++++++++
1 file changed, 141 insertions(+)
diff --git a/src/PVE/QemuServer/Blockdev.pm b/src/PVE/QemuServer/Blockdev.pm
index 0f30f328..50b0b0c6 100644
--- a/src/PVE/QemuServer/Blockdev.pm
+++ b/src/PVE/QemuServer/Blockdev.pm
@@ -11,6 +11,7 @@ use PVE::JSONSchema qw(json_bool);
use PVE::Storage;
use PVE::QemuServer::Drive qw(drive_is_cdrom);
+use PVE::QemuServer::Monitor qw(mon_cmd);
my sub get_node_name {
my ($type, $drive_id, $volid, $snap) = @_;
@@ -244,4 +245,144 @@ sub generate_drive_blockdev {
};
}
+my sub blockdev_add {
+ my ($vmid, $blockdev) = @_;
+
+ eval { mon_cmd($vmid, 'blockdev-add', $blockdev->%*); };
+ if (my $err = $@) {
+ my $node_name = $blockdev->{'node-name'} // 'undefined';
+ die "adding blockdev '$node_name' failed : $err\n" if $@;
+ }
+
+ return;
+}
+
+=pod
+
+=head3 attach
+
+ my $node_name = attach($storecfg, $vmid, $drive, $options);
+
+Attach the drive C<$drive> to the VM C<$vmid> considering the additional options C<$options>.
+Returns the node name of the (topmost) attached block device node.
+
+Parameters:
+
+=over
+
+=item C<$storecfg>: The storage configuration.
+
+=item C<$vmid>: The ID of the virtual machine.
+
+=item C<$drive>: The drive as parsed from a virtual machine configuration.
+
+=item C<$options>: A hash reference with additional options.
+
+=over
+
+=item C<< $options->{'read-only'} >>: Attach the image as read-only irrespective of the
+configuration in C<$drive>.
+
+=item C<< $options->{size} >>: Attach the image with this virtual size. Must be smaller than the
+actual size of the image. The image format must be C<raw>.
+
+=item C<< $options->{'snapshot-name'} >>: Attach this snapshot of the volume C<< $drive->{file} >>,
+rather than the volume itself.
+
+=back
+
+=back
+
+=cut
+
+sub attach {
+ my ($storecfg, $vmid, $drive, $options) = @_;
+
+ my $blockdev = generate_drive_blockdev($storecfg, $drive, $options);
+
+ my $throttle_group_id;
+ if (parse_top_node_name($blockdev->{'node-name'})) { # device top nodes need a throttle group
+ my $drive_id = PVE::QemuServer::Drive::get_drive_id($drive);
+ $throttle_group_id = throttle_group_id($drive_id);
+ }
+
+ eval {
+ if ($throttle_group_id) {
+ # Try to remove potential left-over.
+ eval { mon_cmd($vmid, 'object-del', id => $throttle_group_id); };
+
+ my $throttle_group = generate_throttle_group($drive);
+ mon_cmd($vmid, 'object-add', $throttle_group->%*);
+ }
+
+ blockdev_add($vmid, $blockdev);
+ };
+ if (my $err = $@) {
+ if ($throttle_group_id) {
+ eval { mon_cmd($vmid, 'object-del', id => $throttle_group_id); };
+ }
+ die $err;
+ }
+
+ return $blockdev->{'node-name'};
+}
+
+=pod
+
+=head3 detach
+
+ detach($vmid, $node_name);
+
+Detach the block device C<$node_name> from the VM C<$vmid>. Also removes associated child block
+nodes.
+
+Parameters:
+
+=over
+
+=item C<$vmid>: The ID of the virtual machine.
+
+=item C<$node_name>: The node name identifying the block node in QEMU.
+
+=back
+
+=cut
+
+sub detach {
+ my ($vmid, $node_name) = @_;
+
+ die "Blockdev::detach - no node name\n" if !$node_name;
+
+ my $block_info = mon_cmd($vmid, "query-named-block-nodes");
+ $block_info = { map { $_->{'node-name'} => $_ } $block_info->@* };
+
+ my $remove_throttle_group_id;
+ if ((my $drive_id = parse_top_node_name($node_name)) && $block_info->{$node_name}) {
+ $remove_throttle_group_id = throttle_group_id($drive_id);
+ }
+
+ while ($node_name) {
+ last if !$block_info->{$node_name}; # already gone
+
+ eval { mon_cmd($vmid, 'blockdev-del', 'node-name' => "$node_name"); };
+ if (my $err = $@) {
+ last if $err =~ m/Failed to find node with node-name/; # already gone
+ die "deleting blockdev '$node_name' failed : $err\n";
+ }
+
+ my $children = { map { $_->{child} => $_ } $block_info->{$node_name}->{children}->@* };
+ # Recursively remove 'file' child nodes. QEMU will auto-remove implicitly added child nodes,
+ # but e.g. the child of the top throttle node might have been explicitly added as a mirror
+ # target, and needs to be removed manually.
+ $node_name = $children->{file}->{'node-name'};
+ }
+
+ if ($remove_throttle_group_id) {
+ eval { mon_cmd($vmid, 'object-del', id => $remove_throttle_group_id); };
+ die "removing throttle group failed - $@\n" if $@;
+ }
+
+ return;
+}
+
1;
--
2.47.2
More information about the pve-devel
mailing list