]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
migrate: keep VM paused after migration if it was before
[qemu-server.git] / PVE / QemuServer.pm
index 9fdb4c43817ed287c2c7c1184cdfd485823ebf75..0be6be909ae21336252cdcdae6839a5147f6e638 100644 (file)
@@ -1036,11 +1036,17 @@ PVE::JSONSchema::register_format('pve-volume-id-or-qm-path', \&verify_volume_id_
 sub verify_volume_id_or_qm_path {
     my ($volid, $noerr) = @_;
 
-    if ($volid eq 'none' || $volid eq 'cdrom' || $volid =~ m|^/|) {
-       return $volid;
-    }
+    return $volid if $volid eq 'none' || $volid eq 'cdrom';
+
+    return verify_volume_id_or_absolute_path($volid, $noerr);
+}
+
+PVE::JSONSchema::register_format('pve-volume-id-or-absolute-path', \&verify_volume_id_or_absolute_path);
+sub verify_volume_id_or_absolute_path {
+    my ($volid, $noerr) = @_;
+
+    return $volid if $volid =~ m|^/|;
 
-    # if its neither 'none' nor 'cdrom' nor a path, check if its a volume-id
     $volid = eval { PVE::JSONSchema::check_format('pve-volume-id', $volid, '') };
     if ($@) {
        return if $noerr;
@@ -2178,7 +2184,7 @@ sub verify_usb_device {
 
 # add JSON properties for create and set function
 sub json_config_properties {
-    my $prop = shift;
+    my ($prop, $with_disk_alloc) = @_;
 
     my $skip_json_config_opts = {
        parent => 1,
@@ -2191,12 +2197,49 @@ sub json_config_properties {
 
     foreach my $opt (keys %$confdesc) {
        next if $skip_json_config_opts->{$opt};
-       $prop->{$opt} = $confdesc->{$opt};
+
+       if ($with_disk_alloc && is_valid_drivename($opt)) {
+           $prop->{$opt} = $PVE::QemuServer::Drive::drivedesc_hash_with_alloc->{$opt};
+       } else {
+           $prop->{$opt} = $confdesc->{$opt};
+       }
     }
 
     return $prop;
 }
 
+# Properties that we can read from an OVF file
+sub json_ovf_properties {
+    my $prop = {};
+
+    for my $device (PVE::QemuServer::Drive::valid_drive_names()) {
+       $prop->{$device} = {
+           type => 'string',
+           format => 'pve-volume-id-or-absolute-path',
+           description => "Disk image that gets imported to $device",
+           optional => 1,
+       };
+    }
+
+    $prop->{cores} = {
+       type => 'integer',
+       description => "The number of CPU cores.",
+       optional => 1,
+    };
+    $prop->{memory} = {
+       type => 'integer',
+       description => "Amount of RAM for the VM in MB.",
+       optional => 1,
+    };
+    $prop->{name} = {
+       type => 'string',
+       description => "Name of the VM.",
+       optional => 1,
+    };
+
+    return $prop;
+}
+
 # return copy of $confdesc_cloudinit to generate documentation
 sub cloudinit_config_properties {
 
@@ -4249,6 +4292,7 @@ sub vm_deviceunplug {
        my $device = parse_drive($deviceid, $conf->{$deviceid});
 
        qemu_devicedel($vmid, $deviceid);
+       qemu_devicedelverify($vmid, $deviceid);
        qemu_drivedel($vmid, $deviceid);
        qemu_deletescsihw($conf, $vmid, $deviceid);
 
@@ -5551,7 +5595,7 @@ sub vm_start_nolock {
     };
     # Issues with the above 'stop' not being fully completed are extremely rare, a very low
     # timeout should be more than enough here...
-    PVE::Systemd::wait_for_unit_removed("$vmid.scope", 5);
+    PVE::Systemd::wait_for_unit_removed("$vmid.scope", 20);
 
     my $cpuunits = get_cpuunits($conf);
 
@@ -6257,9 +6301,6 @@ my $parse_backup_hints = sub {
            $devinfo->{$devname}->{storeid} = $storeid;
 
            my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
-           die "Content type 'images' is not available on storage '$storeid'\n"
-               if !$scfg->{content}->{images};
-
            $check_storage->($storeid, $scfg); # permission and content type check
 
            $virtdev_hash->{$virtdev} = $devinfo->{$devname};
@@ -6629,7 +6670,6 @@ sub restore_proxmox_backup_archive {
        my $index = PVE::Tools::file_get_contents($index_fn);
        $index = decode_json($index);
 
-       # print Dumper($index);
        foreach my $info (@{$index->{files}}) {
            if ($info->{filename} =~ m/^(drive-\S+).img.fidx$/) {
                my $devname = $1;
@@ -6833,7 +6873,7 @@ sub pbs_live_restore {
     my $err = $@;
 
     if ($err) {
-       warn "An error occured during live-restore: $err\n";
+       warn "An error occurred during live-restore: $err\n";
        _do_vm_stop($storecfg, $vmid, 1, 1, 10, 0, 1);
        die "live-restore failed\n";
     }
@@ -7276,7 +7316,7 @@ sub qemu_img_convert {
        $src_path = PVE::Storage::path($storecfg, $src_volid, $snapname);
        $src_is_iscsi = ($src_path =~ m|^iscsi://|);
        $cachemode = 'none' if $src_scfg->{type} eq 'zfspool';
-    } elsif (-f $src_volid) {
+    } elsif (-f $src_volid || -b $src_volid) {
        $src_path = $src_volid;
        if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
            $src_format = $1;
@@ -7573,13 +7613,37 @@ sub qemu_blockjobs_cancel {
 }
 
 sub clone_disk {
-    my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
-       $newvmid, $storage, $format, $full, $newvollist, $jobs, $completion, $qga, $bwlimit, $conf) = @_;
+    my ($storecfg, $source, $dest, $full, $newvollist, $jobs, $completion, $qga, $bwlimit) = @_;
+
+    my ($vmid, $running) = $source->@{qw(vmid running)};
+    my ($src_drivename, $drive, $snapname) = $source->@{qw(drivename drive snapname)};
+
+    my ($newvmid, $dst_drivename, $efisize) = $dest->@{qw(vmid drivename efisize)};
+    my ($storage, $format) = $dest->@{qw(storage format)};
+
+    my $use_drive_mirror = $full && $running && $src_drivename && !$snapname;
+
+    if ($src_drivename && $dst_drivename && $src_drivename ne $dst_drivename) {
+       die "cloning from/to EFI disk requires EFI disk\n"
+           if $src_drivename eq 'efidisk0' || $dst_drivename eq 'efidisk0';
+       die "cloning from/to TPM state requires TPM state\n"
+           if $src_drivename eq 'tpmstate0' || $dst_drivename eq 'tpmstate0';
+
+       # This would lead to two device nodes in QEMU pointing to the same backing image!
+       die "cannot change drive name when cloning disk from/to the same VM\n"
+           if $use_drive_mirror && $vmid == $newvmid;
+    }
+
+    die "cannot move TPM state while VM is running\n"
+       if $use_drive_mirror && $src_drivename eq 'tpmstate0';
 
     my $newvolid;
 
+    print "create " . ($full ? 'full' : 'linked') . " clone of drive ";
+    print "$src_drivename " if $src_drivename;
+    print "($drive->{file})\n";
+
     if (!$full) {
-       print "create linked clone of drive $drivename ($drive->{file})\n";
        $newvolid = PVE::Storage::vdisk_clone($storecfg,  $drive->{file}, $newvmid, $snapname);
        push @$newvollist, $newvolid;
     } else {
@@ -7589,7 +7653,6 @@ sub clone_disk {
 
        my $dst_format = resolve_dst_disk_format($storecfg, $storeid, $volname, $format);
 
-       print "create full clone of drive $drivename ($drive->{file})\n";
        my $name = undef;
        my $size = undef;
        if (drive_is_cloudinit($drive)) {
@@ -7600,9 +7663,9 @@ sub clone_disk {
            }
            $snapname = undef;
            $size = PVE::QemuServer::Cloudinit::CLOUDINIT_DISK_SIZE;
-       } elsif ($drivename eq 'efidisk0') {
-           $size = get_efivars_size($conf);
-       } elsif ($drivename eq 'tpmstate0') {
+       } elsif ($dst_drivename eq 'efidisk0') {
+           $size = $efisize or die "internal error - need to specify EFI disk size\n";
+       } elsif ($dst_drivename eq 'tpmstate0') {
            $dst_format = 'raw';
            $size = PVE::QemuServer::Drive::TPMSTATE_DISK_SIZE;
        } else {
@@ -7626,35 +7689,35 @@ sub clone_disk {
        }
 
        my $sparseinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $newvolid);
-       if (!$running || $snapname) {
+       if ($use_drive_mirror) {
+           qemu_drive_mirror($vmid, $src_drivename, $newvolid, $newvmid, $sparseinit, $jobs,
+               $completion, $qga, $bwlimit);
+       } else {
            # TODO: handle bwlimits
-           if ($drivename eq 'efidisk0') {
+           if ($dst_drivename eq 'efidisk0') {
                # the relevant data on the efidisk may be smaller than the source
                # e.g. on RBD/ZFS, so we use dd to copy only the amount
                # that is given by the OVMF_VARS.fd
-               my $src_path = PVE::Storage::path($storecfg, $drive->{file});
+               my $src_path = PVE::Storage::path($storecfg, $drive->{file}, $snapname);
                my $dst_path = PVE::Storage::path($storecfg, $newvolid);
 
+               my $src_format = (PVE::Storage::parse_volname($storecfg, $drive->{file}))[6];
+
                # better for Ceph if block size is not too small, see bug #3324
                my $bs = 1024*1024;
 
-               run_command(['qemu-img', 'dd', '-n', '-O', $dst_format, "bs=$bs", "osize=$size",
-                   "if=$src_path", "of=$dst_path"]);
+               my $cmd = ['qemu-img', 'dd', '-n', '-O', $dst_format];
+
+               if ($src_format eq 'qcow2' && $snapname) {
+                   die "cannot clone qcow2 EFI disk snapshot - requires QEMU >= 6.2\n"
+                       if !min_version(kvm_user_version(), 6, 2);
+                   push $cmd->@*, '-l', $snapname;
+               }
+               push $cmd->@*, "bs=$bs", "osize=$size", "if=$src_path", "of=$dst_path";
+               run_command($cmd);
            } else {
                qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
            }
-       } else {
-
-           die "cannot move TPM state while VM is running\n" if $drivename eq 'tpmstate0';
-
-           my $kvmver = get_running_qemu_version ($vmid);
-           if (!min_version($kvmver, 2, 7)) {
-               die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
-                   if $drive->{iothread};
-           }
-
-           qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs,
-               $completion, $qga, $bwlimit);
        }
     }
 
@@ -7698,9 +7761,10 @@ sub qemu_use_old_bios_files {
 }
 
 sub get_efivars_size {
-    my ($conf) = @_;
+    my ($conf, $efidisk) = @_;
+
     my $arch = get_vm_arch($conf);
-    my $efidisk = $conf->{efidisk0} ? parse_drive('efidisk0', $conf->{efidisk0}) : undef;
+    $efidisk //= $conf->{efidisk0} ? parse_drive('efidisk0', $conf->{efidisk0}) : undef;
     my $smm = PVE::QemuServer::Machine::machine_type_is_q35($conf);
     my (undef, $ovmf_vars) = get_ovmf_files($arch, $efidisk, $smm);
     die "uefi vars image '$ovmf_vars' not found\n" if ! -f $ovmf_vars;