]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
live-restore: fail early if target storage doesn't exist
[qemu-server.git] / PVE / QemuServer.pm
index 1467254663c87f35430e008ea232764894a2a881..3126e14ea8f26e67f1c64417a3ae51647ee36862 100644 (file)
@@ -48,7 +48,7 @@ use PVE::QemuServer::Helpers qw(min_version config_aware_timeout);
 use PVE::QemuServer::Cloudinit;
 use PVE::QemuServer::CGroup;
 use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options);
-use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom parse_drive print_drive);
+use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
 use PVE::QemuServer::Machine;
 use PVE::QemuServer::Memory;
 use PVE::QemuServer::Monitor qw(mon_cmd);
@@ -315,11 +315,13 @@ my $confdesc = {
     cpuunits => {
        optional => 1,
        type => 'integer',
-        description => "CPU weight for a VM.",
-       verbose_description => "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.",
+        description => "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
+       verbose_description => "CPU weight for a VM. Argument is used in the kernel fair scheduler."
+           ." The larger the number is, the more CPU time this VM gets. Number is relative to"
+           ." weights of all the other running VMs.",
        minimum => 2,
        maximum => 262144,
-       default => 1024,
+       default => 'cgroup v1: 1024, cgroup v2: 100',
     },
     memory => {
        optional => 1,
@@ -365,7 +367,9 @@ my $confdesc = {
     description => {
        optional => 1,
        type => 'string',
-       description => "Description for the VM. Only used on the configuration web interface. This is saved as comment inside the configuration file.",
+       description => "Description for the VM. Shown in the web-interface VM's summary."
+           ." This is saved as comment inside the configuration file.",
+       maxLength => 1024 * 8,
     },
     ostype => {
        optional => 1,
@@ -828,9 +832,22 @@ for (my $i = 0; $i < $MAX_NUMA; $i++)  {
     $confdesc->{"numa$i"} = $numadesc;
 }
 
-my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000',  'pcnet',  'virtio',
-                     'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3',
-                     'e1000-82540em', 'e1000-82544gc', 'e1000-82545em'];
+my $nic_model_list = [
+    'e1000',
+    'e1000-82540em',
+    'e1000-82544gc',
+    'e1000-82545em',
+    'e1000e',
+    'i82551',
+    'i82557b',
+    'i82559er',
+    'ne2k_isa',
+    'ne2k_pci',
+    'pcnet',
+    'rtl8139',
+    'virtio',
+    'vmxnet3',
+];
 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
 
 my $net_fmt_bridge_descr = <<__EOD__;
@@ -1451,7 +1468,7 @@ sub print_drivedevice_full {
            }
        }
 
-       if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)){
+       if (!$conf->{scsihw} || $conf->{scsihw} =~ m/^lsi/ || $conf->{scsihw} eq 'pvscsi') {
            $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
        } else {
            $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
@@ -1521,21 +1538,22 @@ sub get_initiator_name {
 }
 
 sub print_drive_commandline_full {
-    my ($storecfg, $vmid, $drive, $pbs_name) = @_;
+    my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
 
     my $path;
     my $volid = $drive->{file};
     my $format = $drive->{format};
     my $drive_id = get_drive_id($drive);
 
+    my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+    my $scfg = $storeid ? PVE::Storage::storage_config($storecfg, $storeid) : undef;
+
     if (drive_is_cdrom($drive)) {
        $path = get_iso_path($storecfg, $vmid, $volid);
         die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
     } else {
-       my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
        if ($storeid) {
            $path = PVE::Storage::path($storecfg, $volid);
-           my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
            $format //= qemu_img_format($scfg, $volname);
        } else {
            $path = $volid;
@@ -1592,17 +1610,29 @@ sub print_drive_commandline_full {
 
     if (my $cache = $drive->{cache}) {
        $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
-    } elsif (!drive_is_cdrom($drive)) {
+    } elsif (!drive_is_cdrom($drive) && !($scfg && $scfg->{type} eq 'btrfs' && !$scfg->{nocow})) {
        $opts .= ",cache=none";
        $cache_direct = 1;
     }
 
-    # aio native works only with O_DIRECT
+    # io_uring with cache mode writeback or writethrough on krbd will hang...
+    my $rbd_no_io_uring = $scfg && $scfg->{type} eq 'rbd' && $scfg->{krbd} && !$cache_direct;
+
+    # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
+    # sometimes, just plain disable...
+    my $lvm_no_io_uring = $scfg && $scfg->{type} eq 'lvm';
+
     if (!$drive->{aio}) {
-       if($cache_direct) {
-           $opts .= ",aio=native";
+       if ($io_uring && !$rbd_no_io_uring && !$lvm_no_io_uring) {
+           # io_uring supports all cache modes
+           $opts .= ",aio=io_uring";
        } else {
-           $opts .= ",aio=threads";
+           # aio native works only with O_DIRECT
+           if($cache_direct) {
+               $opts .= ",aio=native";
+           } else {
+               $opts .= ",aio=threads";
+           }
        }
     }
 
@@ -1691,6 +1721,8 @@ sub print_netdevice_full {
            $romfile = 'pxe-virtio.rom';
        } elsif ($device eq 'e1000') {
            $romfile = 'pxe-e1000.rom';
+       } elsif ($device eq 'e1000e') {
+           $romfile = 'pxe-e1000e.rom';
        } elsif ($device eq 'ne2k') {
            $romfile = 'pxe-ne2k_pci.rom';
        } elsif ($device eq 'pcnet') {
@@ -2127,7 +2159,7 @@ sub destroy_vm {
 
     if ($conf->{template}) {
        # check if any base image is still used by a linked clone
-       PVE::QemuConfig->foreach_volume($conf, sub {
+       PVE::QemuConfig->foreach_volume_full($conf, { include_unused => 1 }, sub {
                my ($ds, $drive) = @_;
                return if drive_is_cdrom($drive);
 
@@ -2140,8 +2172,7 @@ sub destroy_vm {
        });
     }
 
-    # only remove disks owned by this VM (referenced in the config)
-    PVE::QemuConfig->foreach_volume_full($conf, { include_unused => 1 }, sub {
+    my $remove_owned_drive = sub {
        my ($ds, $drive) = @_;
        return if drive_is_cdrom($drive, 1);
 
@@ -2153,10 +2184,24 @@ sub destroy_vm {
 
        eval { PVE::Storage::vdisk_free($storecfg, $volid) };
        warn "Could not remove disk '$volid', check manually: $@" if $@;
-    });
+    };
+
+    # only remove disks owned by this VM (referenced in the config)
+    my $include_opts = {
+       include_unused => 1,
+       extra_keys => ['vmstate'],
+    };
+    PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $remove_owned_drive);
+
+    for my $snap (values %{$conf->{snapshots}}) {
+       next if !defined($snap->{vmstate});
+       my $drive = PVE::QemuConfig->parse_volume('vmstate', $snap->{vmstate}, 1);
+       next if !defined($drive);
+       $remove_owned_drive->('vmstate', $drive);
+    }
 
     if ($purge_unreferenced) { # also remove unreferenced disk
-       my $vmdisks = PVE::Storage::vdisk_list($storecfg, undef, $vmid);
+       my $vmdisks = PVE::Storage::vdisk_list($storecfg, undef, $vmid, undef, 'images');
        PVE::Storage::foreach_volid($vmdisks, sub {
            my ($volid, $sid, $volname, $d) = @_;
            eval { PVE::Storage::vdisk_free($storecfg, $volid) };
@@ -2443,8 +2488,13 @@ sub check_storage_availability {
        return if !$sid;
 
        # check if storage is available on both nodes
-       my $scfg = PVE::Storage::storage_check_node($storecfg, $sid);
-       PVE::Storage::storage_check_node($storecfg, $sid, $node);
+       my $scfg = PVE::Storage::storage_check_enabled($storecfg, $sid);
+       PVE::Storage::storage_check_enabled($storecfg, $sid, $node);
+
+       my ($vtype) = PVE::Storage::parse_volname($storecfg, $volid);
+
+       die "$volid: content type '$vtype' is not available on storage '$sid'\n"
+           if !$scfg->{content}->{$vtype};
    });
 }
 
@@ -2638,8 +2688,8 @@ sub vmstatus {
 
        my $conf = PVE::QemuConfig->load_config($vmid);
 
-       my $d = { vmid => $vmid };
-       $d->{pid} = $list->{$vmid}->{pid};
+       my $d = { vmid => int($vmid) };
+       $d->{pid} = int($list->{$vmid}->{pid}) if $list->{$vmid}->{pid};
 
        # fixme: better status?
        $d->{status} = $list->{$vmid}->{pid} ? 'running' : 'stopped';
@@ -2678,7 +2728,7 @@ sub vmstatus {
        $d->{diskread} = 0;
        $d->{diskwrite} = 0;
 
-        $d->{template} = PVE::QemuConfig->is_template($conf);
+        $d->{template} = 1 if PVE::QemuConfig->is_template($conf);
 
        $d->{serial} = 1 if conf_has_serial($conf);
        $d->{lock} = $conf->{lock} if $conf->{lock};
@@ -2698,8 +2748,8 @@ sub vmstatus {
        $d->{netin} += $netdev->{$dev}->{transmit};
 
        if ($full) {
-           $d->{nics}->{$dev}->{netout} = $netdev->{$dev}->{receive};
-           $d->{nics}->{$dev}->{netin} = $netdev->{$dev}->{transmit};
+           $d->{nics}->{$dev}->{netout} = int($netdev->{$dev}->{receive});
+           $d->{nics}->{$dev}->{netin} = int($netdev->{$dev}->{transmit});
        }
 
     }
@@ -2986,10 +3036,15 @@ sub get_vm_machine {
        }
     }
 
-    if ($add_pve_version && $machine !~ m/\+pve\d+$/) {
+    if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
+       my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
+       $machine = $1 if $is_pxe;
+
        # for version-pinned machines that do not include a pve-version (e.g.
        # pc-q35-4.1), we assume 0 to keep them stable in case we bump
        $machine .= '+pve0';
+
+       $machine .= '.pxe' if $is_pxe;
     }
 
     return $machine;
@@ -3064,7 +3119,7 @@ sub query_supported_cpu_flags {
            $qemu_cmd,
            '-machine', $default_machine,
            '-display', 'none',
-           '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server,nowait",
+           '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
            '-mon', 'chardev=qmp,mode=control',
            '-pidfile', $pidfile,
            '-S', '-daemonize'
@@ -3136,6 +3191,10 @@ sub query_understood_cpu_flags {
     return \@flags;
 }
 
+my sub get_cpuunits {
+    my ($conf) = @_;
+    return $conf->{cpuunits} // (PVE::CGroup::cgroup_mode() == 2 ? 100 : 1024);
+}
 sub config_to_command {
     my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
         $pbs_backing) = @_;
@@ -3205,8 +3264,7 @@ sub config_to_command {
     my $use_old_bios_files = undef;
     ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
 
-    my $cpuunits = defined($conf->{cpuunits}) ?
-            $conf->{cpuunits} : $defaults->{cpuunits};
+    my $cpuunits = get_cpuunits($conf);
 
     push @$cmd, $kvm_binary;
 
@@ -3221,7 +3279,7 @@ sub config_to_command {
     my $use_virtio = 0;
 
     my $qmpsocket = PVE::QemuServer::Helpers::qmp_socket($vmid);
-    push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
+    push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
     push @$cmd, '-mon', "chardev=qmp,mode=control";
 
     if (min_version($machine_version, 2, 12)) {
@@ -3261,6 +3319,7 @@ sub config_to_command {
        die "uefi base image '$ovmf_code' not found\n" if ! -f $ovmf_code;
 
        my ($path, $format);
+       my $read_only_str = '';
        if (my $efidisk = $conf->{efidisk0}) {
            my $d = parse_drive('efidisk0', $efidisk);
            my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1);
@@ -3276,6 +3335,8 @@ sub config_to_command {
                die "efidisk format must be specified\n"
                    if !defined($format);
            }
+
+           $read_only_str = ',readonly=on' if drive_is_read_only($conf, $d);
        } else {
            warn "no efidisk configured! Using temporary efivars disk.\n";
            $path = "/tmp/$vmid-ovmf.fd";
@@ -3289,8 +3350,15 @@ sub config_to_command {
            $size_str = ",size=" . (-s $ovmf_vars);
        }
 
-       push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmf_code";
-       push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0$size_str,file=$path";
+       # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329
+       my $cache = "";
+       if ($path =~ m/^rbd:/) {
+               $cache = ',cache=writeback';
+               $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too
+       }
+
+       push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code";
+       push @$cmd, '-drive', "if=pflash,unit=1$cache,format=$format,id=drive-efidisk0$size_str,file=${path}${read_only_str}";
     }
 
     # load q35 config
@@ -3361,7 +3429,7 @@ sub config_to_command {
        if (my $path = $conf->{"serial$i"}) {
            if ($path eq 'socket') {
                my $socket = "/var/run/qemu-server/${vmid}.serial$i";
-               push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait";
+               push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
                # On aarch64, serial0 is the UART device. Qemu only allows
                # connecting UART devices via the '-serial' command line, as
                # the device has a fixed slot on the hardware...
@@ -3433,7 +3501,7 @@ sub config_to_command {
        push @$devices, '-device', print_vga_device(
            $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
        my $socket = PVE::QemuServer::Helpers::vnc_socket($vmid);
-       push @$cmd,  '-vnc', "unix:$socket,password";
+       push @$cmd,  '-vnc', "unix:$socket,password=on";
     } else {
        push @$cmd, '-vga', 'none' if $vga->{type} eq 'none';
        push @$cmd, '-nographic';
@@ -3481,7 +3549,7 @@ sub config_to_command {
 
     if ($guest_agent->{enabled}) {
        my $qgasocket = PVE::QemuServer::Helpers::qmp_socket($vmid, 1);
-       push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
+       push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
 
        if (!$guest_agent->{type} || $guest_agent->{type} eq 'virtio') {
            my $pciaddr = print_pci_addr("qga0", $bridges, $arch, $machine_type);
@@ -3584,6 +3652,7 @@ sub config_to_command {
        my ($ds, $drive) = @_;
 
        if (PVE::Storage::parse_volume_id($drive->{file}, 1)) {
+           check_volume_storage_type($storecfg, $drive->{file});
            push @$vollist, $drive->{file};
        }
 
@@ -3641,8 +3710,11 @@ sub config_to_command {
            push @$devices, '-blockdev', print_pbs_blockdev($pbs_conf, $pbs_name);
        }
 
-       my $drive_cmd = print_drive_commandline_full($storecfg, $vmid, $drive, $pbs_name);
-       $drive_cmd .= ',readonly' if PVE::QemuConfig->is_template($conf);
+       my $drive_cmd = print_drive_commandline_full(
+           $storecfg, $vmid, $drive, $pbs_name, min_version($kvmver, 6, 0));
+
+       # extra protection for templates, but SATA and IDE don't support it..
+       $drive_cmd .= ',readonly=on' if drive_is_read_only($conf, $drive);
 
        push @$devices, '-drive',$drive_cmd;
        push @$devices, '-device', print_drivedevice_full(
@@ -3742,6 +3814,11 @@ sub config_to_command {
        print "activating and using '$vmstate' as vmstate\n";
     }
 
+    if (PVE::QemuConfig->is_template($conf)) {
+       # needed to workaround base volumes being read-only
+       push @$cmd, '-snapshot';
+    }
+
     # add custom args
     if ($conf->{args}) {
        my $aa = PVE::Tools::split_args($conf->{args});
@@ -4049,7 +4126,9 @@ sub qemu_objectdel {
 sub qemu_driveadd {
     my ($storecfg, $vmid, $device) = @_;
 
-    my $drive = print_drive_commandline_full($storecfg, $vmid, $device);
+    my $kvmver = get_running_qemu_version($vmid);
+    my $io_uring = min_version($kvmver, 6, 0);
+    my $drive = print_drive_commandline_full($storecfg, $vmid, $device, undef, $io_uring);
     $drive =~ s/\\/\\\\/g;
     my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_add auto \"$drive\"");
 
@@ -4453,6 +4532,8 @@ sub foreach_volid {
 
        $volhash->{$volid}->{is_unused} //= 0;
        $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
+
+       $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
     };
 
     my $include_opts = {
@@ -4564,7 +4645,7 @@ sub vmconfig_hotplug_pending {
                die "skip\n" if !$hotplug_features->{memory};
                PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
            } elsif ($opt eq 'cpuunits') {
-               $cgroup->change_cpu_shares(undef, $defaults->{cpuunits});
+               $cgroup->change_cpu_shares(undef, 1024);
            } elsif ($opt eq 'cpulimit') {
                $cgroup->change_cpu_quota(-1, 100000);
            } else {
@@ -4658,7 +4739,7 @@ sub vmconfig_hotplug_pending {
                die "skip\n" if !$hotplug_features->{memory};
                $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
            } elsif ($opt eq 'cpuunits') {
-               $cgroup->change_cpu_shares($conf->{pending}->{$opt}, $defaults->{cpuunits});
+               $cgroup->change_cpu_shares($conf->{pending}->{$opt}, 1024);
            } elsif ($opt eq 'cpulimit') {
                my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
                $cgroup->change_cpu_quota($cpulimit, 100000);
@@ -5225,8 +5306,7 @@ sub vm_start_nolock {
     # timeout should be more than enough here...
     PVE::Systemd::wait_for_unit_removed("$vmid.scope", 5);
 
-    my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
-                                             : $defaults->{cpuunits};
+    my $cpuunits = get_cpuunits($conf);
 
     my $start_timeout = $params->{timeout} // config_aware_timeout($conf, $resume);
     my %run_params = (
@@ -5244,10 +5324,13 @@ sub vm_start_nolock {
 
     my %properties = (
        Slice => 'qemu.slice',
-       KillMode => 'none'
+       KillMode => 'process',
+       SendSIGKILL => 0,
+       TimeoutStopUSec => ULONG_MAX, # infinity
     );
 
     if (PVE::CGroup::cgroup_mode() == 2) {
+       $cpuunits = 10000 if $cpuunits >= 10000; # else we get an error
        $properties{CPUWeight} = $cpuunits;
     } else {
        $properties{CPUShares} = $cpuunits;
@@ -5966,8 +6049,8 @@ my $restore_allocate_devices = sub {
     return $map;
 };
 
-my $restore_update_config_line = sub {
-    my ($cookie, $vmid, $map, $line, $unique) = @_;
+sub restore_update_config_line {
+    my ($cookie, $map, $line, $unique) = @_;
 
     return '' if $line =~ m/^\#qmdump\#/;
     return '' if $line =~ m/^\#vzdump\#/;
@@ -6033,7 +6116,7 @@ my $restore_update_config_line = sub {
     }
 
     return $res;
-};
+}
 
 my $restore_deactivate_volumes = sub {
     my ($storecfg, $devinfo) = @_;
@@ -6068,7 +6151,7 @@ my $restore_destroy_volumes = sub {
 sub scan_volids {
     my ($cfg, $vmid) = @_;
 
-    my $info = PVE::Storage::vdisk_list($cfg, undef, $vmid);
+    my $info = PVE::Storage::vdisk_list($cfg, undef, $vmid, undef, 'images');
 
     my $volid_hash = {};
     foreach my $storeid (keys %$info) {
@@ -6161,12 +6244,6 @@ sub rescan {
 
     my $cfg = PVE::Storage::config();
 
-    # FIXME: Remove once our RBD plugin can handle CT and VM on a single storage
-    # see: https://pve.proxmox.com/pipermail/pve-devel/2018-July/032900.html
-    foreach my $stor (keys %{$cfg->{ids}}) {
-       delete($cfg->{ids}->{$stor}) if ! $cfg->{ids}->{$stor}->{content}->{images};
-    }
-
     print "rescan volumes...\n";
     my $volid_hash = scan_volids($cfg, $vmid);
 
@@ -6316,44 +6393,44 @@ sub restore_proxmox_backup_archive {
        # allocate volumes
        my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
 
-       if (!$options->{live}) {
-           foreach my $virtdev (sort keys %$virtdev_hash) {
-               my $d = $virtdev_hash->{$virtdev};
-               next if $d->{is_cloudinit}; # no need to restore cloudinit
+       foreach my $virtdev (sort keys %$virtdev_hash) {
+           my $d = $virtdev_hash->{$virtdev};
+           next if $d->{is_cloudinit}; # no need to restore cloudinit
 
-               my $volid = $d->{volid};
+           # this fails if storage is unavailable
+           my $volid = $d->{volid};
+           my $path = PVE::Storage::path($storecfg, $volid);
 
-               my $path = PVE::Storage::path($storecfg, $volid);
+           # for live-restore we only want to preload the efidisk
+           next if $options->{live} && $virtdev ne 'efidisk0';
 
-               my $pbs_restore_cmd = [
-                   '/usr/bin/pbs-restore',
-                   '--repository', $repo,
-                   $pbs_backup_name,
-                   "$d->{devname}.img.fidx",
-                   $path,
-                   '--verbose',
-                   ];
+           my $pbs_restore_cmd = [
+               '/usr/bin/pbs-restore',
+               '--repository', $repo,
+               $pbs_backup_name,
+               "$d->{devname}.img.fidx",
+               $path,
+               '--verbose',
+               ];
 
-               push @$pbs_restore_cmd, '--format', $d->{format} if $d->{format};
-               push @$pbs_restore_cmd, '--keyfile', $keyfile if -e $keyfile;
+           push @$pbs_restore_cmd, '--format', $d->{format} if $d->{format};
+           push @$pbs_restore_cmd, '--keyfile', $keyfile if -e $keyfile;
 
-               if (PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $volid)) {
-                   push @$pbs_restore_cmd, '--skip-zero';
-               }
-
-               my $dbg_cmdstring = PVE::Tools::cmd2string($pbs_restore_cmd);
-               print "restore proxmox backup image: $dbg_cmdstring\n";
-               run_command($pbs_restore_cmd);
+           if (PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $volid)) {
+               push @$pbs_restore_cmd, '--skip-zero';
            }
+
+           my $dbg_cmdstring = PVE::Tools::cmd2string($pbs_restore_cmd);
+           print "restore proxmox backup image: $dbg_cmdstring\n";
+           run_command($pbs_restore_cmd);
        }
 
        $fh->seek(0, 0) || die "seek failed - $!\n";
 
        my $cookie = { netcount => 0 };
        while (defined(my $line = <$fh>)) {
-           $new_conf_raw .= $restore_update_config_line->(
+           $new_conf_raw .= restore_update_config_line(
                $cookie,
-               $vmid,
                $map,
                $line,
                $options->{unique},
@@ -6375,6 +6452,11 @@ sub restore_proxmox_backup_archive {
        die $err;
     }
 
+    if ($options->{live}) {
+       # keep lock during live-restore
+       $new_conf_raw .= "\nlock: create";
+    }
+
     PVE::Tools::file_set_contents($conffile, $new_conf_raw);
 
     PVE::Cluster::cfs_update(); # make sure we read new file
@@ -6385,45 +6467,42 @@ sub restore_proxmox_backup_archive {
     PVE::AccessControl::add_vm_to_pool($vmid, $options->{pool}) if $options->{pool};
 
     if ($options->{live}) {
-       eval {
-           # enable interrupts
-           local $SIG{INT} =
-               local $SIG{TERM} =
-               local $SIG{QUIT} =
-               local $SIG{HUP} =
-               local $SIG{PIPE} = sub { die "got signal ($!) - abort\n"; };
+       # enable interrupts
+       local $SIG{INT} =
+           local $SIG{TERM} =
+           local $SIG{QUIT} =
+           local $SIG{HUP} =
+           local $SIG{PIPE} = sub { die "got signal ($!) - abort\n"; };
 
-           my $conf = PVE::QemuConfig->load_config($vmid);
-           die "cannot do live-restore for template\n" if PVE::QemuConfig->is_template($conf);
+       my $conf = PVE::QemuConfig->load_config($vmid);
+       die "cannot do live-restore for template\n" if PVE::QemuConfig->is_template($conf);
 
-           pbs_live_restore($vmid, $conf, $storecfg, $devinfo, $repo, $keyfile, $pbs_backup_name);
-       };
+       delete $devinfo->{'drive-efidisk0'}; # this special drive is already restored before start
+       pbs_live_restore($vmid, $conf, $storecfg, $devinfo, $repo, $keyfile, $pbs_backup_name);
 
-       $err = $@;
-       if ($err) {
-           warn "destroying partially live-restored VM, all temporary data will be lost!\n";
-           $restore_deactivate_volumes->($storecfg, $devinfo);
-           $restore_destroy_volumes->($storecfg, $devinfo);
-           PVE::QemuConfig->destroy_config($vmid);
-           die $err;
-       }
+       PVE::QemuConfig->remove_lock($vmid, "create");
     }
 }
 
 sub pbs_live_restore {
     my ($vmid, $conf, $storecfg, $restored_disks, $repo, $keyfile, $snap) = @_;
 
-    print "Starting VM for live-restore\n";
+    print "starting VM for live-restore\n";
+    print "repository: '$repo', snapshot: '$snap'\n";
 
     my $pbs_backing = {};
     for my $ds (keys %$restored_disks) {
        $ds =~ m/^drive-(.*)$/;
-       $pbs_backing->{$1} = {
+       my $confname = $1;
+       $pbs_backing->{$confname} = {
            repository => $repo,
            snapshot => $snap,
            archive => "$ds.img.fidx",
        };
-       $pbs_backing->{$1}->{keyfile} = $keyfile if -e $keyfile;
+       $pbs_backing->{$confname}->{keyfile} = $keyfile if -e $keyfile;
+
+       my $drive = parse_drive($confname, $conf->{$confname});
+       print "restoring '$ds' to '$drive->{file}'\n";
     }
 
     my $drives_streamed = 0;
@@ -6618,9 +6697,8 @@ sub restore_vma_archive {
 
        my $cookie = { netcount => 0 };
        while (defined(my $line = <$fh>)) {
-           $new_conf_raw .= $restore_update_config_line->(
+           $new_conf_raw .= restore_update_config_line(
                $cookie,
-               $vmid,
                $map,
                $line,
                $opts->{unique},
@@ -6775,9 +6853,8 @@ sub restore_tar_archive {
 
        my $cookie = { netcount => 0 };
        while (defined (my $line = <$srcfd>)) {
-           $new_conf_raw .= $restore_update_config_line->(
+           $new_conf_raw .= restore_update_config_line(
                $cookie,
-               $vmid,
                $map,
                $line,
                $opts->{unique},
@@ -6963,7 +7040,7 @@ sub qemu_img_convert {
            my $total_h = render_bytes($size, 1);
            my $transferred_h = render_bytes($transferred, 1);
 
-           print "transferred $transferred_h of $total_h ($percent%)";
+           print "transferred $transferred_h of $total_h ($percent%)\n";
        }
 
     };
@@ -7100,7 +7177,8 @@ sub qemu_drive_mirror_monitor {
                            $status .= ", ready";
                        }
                    }
-                   print "$job_id: $status\n";
+                   print "$job_id: $status\n" if !$jobs->{$job_id}->{ready};
+                   $jobs->{$job_id}->{ready} = $ready;
                }
 
                $readycounter++ if $job->{ready};
@@ -7709,4 +7787,17 @@ sub vm_is_paused {
     return $qmpstatus && $qmpstatus->{status} eq "paused";
 }
 
+sub check_volume_storage_type {
+    my ($storecfg, $vol) = @_;
+
+    my ($storeid, $volname) = PVE::Storage::parse_volume_id($vol);
+    my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+    my ($vtype) = PVE::Storage::parse_volname($storecfg, $vol);
+
+    die "storage '$storeid' does not support content-type '$vtype'\n"
+       if !$scfg->{content}->{$vtype};
+
+    return 1;
+}
+
 1;