api: clone: fork before locking

using the familiar early+repeated checks pattern from other API calls.
Only intended functional changes are with regard to locking/forking.

For a full clone of a running VM without guest agent, this also fixes
issuing vm_{resume,suspend} calls for drive mirror completion.
Previously, those just timed out, because of not getting the lock:

> create full clone of drive scsi0 (rbdkvm:vm-104-disk-0)
> Formatting '/var/lib/vz/images/105/vm-105-disk-0.raw', fmt=raw
> size=4294967296 preallocation=off
> drive mirror is starting for drive-scsi0
> drive-scsi0: transferred 2.0 MiB of 4.0 GiB (0.05%) in 0s
> drive-scsi0: transferred 635.0 MiB of 4.0 GiB (15.50%) in 1s
> drive-scsi0: transferred 1.6 GiB of 4.0 GiB (40.50%) in 2s
> drive-scsi0: transferred 3.6 GiB of 4.0 GiB (90.23%) in 3s
> drive-scsi0: transferred 4.0 GiB of 4.0 GiB (100.00%) in 4s, ready
> all 'mirror' jobs are ready
> suspend vm
> trying to acquire lock...
> can't lock file '/var/lock/qemu-server/lock-104.conf' - got timeout
> drive-scsi0: Cancelling block job
> drive-scsi0: Done.
> resume vm
> trying to acquire lock...
> can't lock file '/var/lock/qemu-server/lock-104.conf' - got timeout

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
This commit is contained in:
Fabian Ebner 2022-01-27 15:01:53 +01:00 committed by Fabian Grünbichler
parent d6cdfae417
commit dbecb46f2a

View File

@ -3079,9 +3079,7 @@ __PACKAGE__->register_method({
my $running = PVE::QemuServer::check_running($vmid) || 0;
my $clonefn = sub {
# do all tests after lock but before forking worker - if possible
my $load_and_check = sub {
my $conf = PVE::QemuConfig->load_config($vmid);
PVE::QemuConfig->check_lock($conf);
@ -3091,7 +3089,7 @@ __PACKAGE__->register_method({
die "snapshot '$snapname' does not exist\n"
if $snapname && !defined( $conf->{snapshots}->{$snapname});
my $full = extract_param($param, 'full') // !PVE::QemuConfig->is_template($conf);
my $full = $param->{full} // !PVE::QemuConfig->is_template($conf);
die "parameter 'storage' not allowed for linked clones\n"
if defined($storage) && !$full;
@ -3156,6 +3154,12 @@ __PACKAGE__->register_method({
}
}
return ($conffile, $newconf, $oldconf, $vollist, $drives, $fullclone);
};
my $clonefn = sub {
my ($conffile, $newconf, $oldconf, $vollist, $drives, $fullclone) = $load_and_check->();
# auto generate a new uuid
my $smbios1 = PVE::QemuServer::parse_smbios1($newconf->{smbios1} || '');
$smbios1->{uuid} = PVE::QemuServer::generate_uuid();
@ -3181,8 +3185,7 @@ __PACKAGE__->register_method({
# FIXME use PVE::QemuConfig->create_and_lock_config and adapt code
PVE::Tools::file_set_contents($conffile, "# qmclone temporary file\nlock: clone\n");
my $realcmd = sub {
my $upid = shift;
PVE::Firewall::clone_vmfw_conf($vmid, $newid);
my $newvollist = [];
my $jobs = {};
@ -3277,22 +3280,23 @@ __PACKAGE__->register_method({
return;
};
PVE::Firewall::clone_vmfw_conf($vmid, $newid);
return $rpcenv->fork_worker('qmclone', $vmid, $authuser, $realcmd);
};
# Aquire exclusive lock lock for $newid
my $lock_target_vm = sub {
return PVE::QemuConfig->lock_config_full($newid, 1, $clonefn);
};
my $lock_source_vm = sub {
# exclusive lock if VM is running - else shared lock is enough;
if ($running) {
return PVE::QemuConfig->lock_config_full($vmid, 1, $lock_target_vm);
} else {
return PVE::QemuConfig->lock_config_shared($vmid, 1, $lock_target_vm);
}
};
$load_and_check->(); # early checks before forking/locking
return $rpcenv->fork_worker('qmclone', $vmid, $authuser, $lock_source_vm);
}});
__PACKAGE__->register_method({