]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
update ostype documentation
[qemu-server.git] / PVE / QemuServer.pm
index a01c0ba82a7df97a86f9ffebc6738f49d1f6eaf7..2e822f37dae88991aae3fb4509edff9e5bd898f2 100644 (file)
@@ -39,7 +39,6 @@ use URI::Escape;
 
 my $OVMF_CODE = '/usr/share/kvm/OVMF_CODE-pure-efi.fd';
 my $OVMF_VARS = '/usr/share/kvm/OVMF_VARS-pure-efi.fd';
-my $OVMF_IMG = '/usr/share/kvm/OVMF-pure-efi.fd';
 
 my $qemu_snap_storage = {rbd => 1, sheepdog => 1};
 
@@ -215,9 +214,9 @@ my $confdesc = {
        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.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0.",
-       minimum => 0,
-       maximum => 500000,
+       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,
     },
     memory => {
@@ -282,7 +281,8 @@ w2k3;; Microsoft Windows 2003
 w2k8;; Microsoft Windows 2008
 wvista;; Microsoft Windows Vista
 win7;; Microsoft Windows 7
-win8;; Microsoft Windows 8/2012
+win8;; Microsoft Windows 8/2012/2012r2
+win10;; Microsoft Windows 10/2016
 l24;; Linux 2.4 Kernel
 l26;; Linux 2.6/3.X Kernel
 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
@@ -478,6 +478,10 @@ EODESCR
        type => 'string', format => 'pve-volume-id',
        description => "Reference to a volume which stores the VM state. This is used internally for snapshots.",
     },
+    vmstatestorage => get_standard_option('pve-storage-id', {
+       description => "Default storage for VM state volumes/files.",
+       optional => 1,
+    }),
     machine => {
        description => "Specific the Qemu machine type.",
        type => 'string',
@@ -858,12 +862,18 @@ $add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool',  'io
 $add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations per second');
 
 # burst lengths
-$add_throttle_desc->('bps_max_length',  'integer', 'length of I/O bursts',       'seconds', 'seconds', 1);
-$add_throttle_desc->('bps_rd_length',   'integer', 'length of read I/O bursts',  'seconds', 'seconds', 1);
-$add_throttle_desc->('bps_wr_length',   'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
-$add_throttle_desc->('iops_max_length', 'integer', 'length of I/O bursts',       'seconds', 'seconds', 1);
-$add_throttle_desc->('iops_rd_length',  'integer', 'length of read I/O bursts',  'seconds', 'seconds', 1);
-$add_throttle_desc->('iops_wr_length',  'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('bps_max_length',     'integer', 'length of I/O bursts',       'seconds', 'seconds', 1);
+$add_throttle_desc->('bps_rd_max_length',  'integer', 'length of read I/O bursts',  'seconds', 'seconds', 1);
+$add_throttle_desc->('bps_wr_max_length',  'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_max_length',    'integer', 'length of I/O bursts',       'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_rd_max_length', 'integer', 'length of read I/O bursts',  'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
+
+# legacy support
+$drivedesc_base{'bps_rd_length'} = { alias => 'bps_rd_max_length' };
+$drivedesc_base{'bps_wr_length'} = { alias => 'bps_wr_max_length' };
+$drivedesc_base{'iops_rd_length'} = { alias => 'iops_rd_max_length' };
+$drivedesc_base{'iops_wr_length'} = { alias => 'iops_wr_max_length' };
 
 my $ide_fmt = {
     %drivedesc_base,
@@ -1358,6 +1368,12 @@ sub parse_drive {
 
     # can't use the schema's 'requires' because of the mbps* => bps* "transforming aliases"
     for my $requirement (
+       [mbps_max => 'mbps'],
+       [mbps_rd_max => 'mbps_rd'],
+       [mbps_wr_max => 'mbps_wr'],
+       [miops_max => 'miops'],
+       [miops_rd_max => 'miops_rd'],
+       [miops_wr_max => 'miops_wr'],
        [bps_max_length => 'mbps_max'],
        [bps_rd_max_length => 'mbps_rd_max'],
        [bps_wr_max_length => 'mbps_wr_max'],
@@ -1582,10 +1598,32 @@ sub print_drive_full {
    }
 
     my $opts = '';
-    my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio discard iops iops_rd iops_wr iops_max iops_rd_max iops_wr_max);
+    my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio discard);
     foreach my $o (@qemu_drive_options) {
        $opts .= ",$o=$drive->{$o}" if $drive->{$o};
     }
+    foreach my $type (['', '-total'], [_rd => '-read'], [_wr => '-write']) {
+       my ($dir, $qmpname) = @$type;
+       if (my $v = $drive->{"mbps$dir"}) {
+           $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
+       }
+       if (my $v = $drive->{"mbps${dir}_max"}) {
+           $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
+       }
+       if (my $v = $drive->{"bps${dir}_max_length"}) {
+           $opts .= ",throttling.bps$qmpname-max-length=$v";
+       }
+       if (my $v = $drive->{"iops${dir}"}) {
+           $opts .= ",throttling.iops$qmpname=$v";
+       }
+       if (my $v = $drive->{"iops${dir}_max"}) {
+           $opts .= ",throttling.iops$qmpname-max=$v";
+       }
+       if (my $v = $drive->{"iops${dir}_max_length"}) {
+           $opts .= ",throttling.iops$qmpname-max-length=$v";
+       }
+    }
+
     if (my $serial = $drive->{serial}) {
        $serial = URI::Escape::uri_unescape($serial);
        $opts .= ",serial=$serial";
@@ -1593,11 +1631,6 @@ sub print_drive_full {
 
     $opts .= ",format=$format" if $format && !$drive->{format};
 
-    foreach my $o (qw(bps bps_rd bps_wr)) {
-       my $v = $drive->{"m$o"};
-       $opts .= ",$o=" . int($v*1024*1024) if $v;
-    }
-
     my $cache_direct = 0;
 
     if (my $cache = $drive->{cache}) {
@@ -1711,8 +1744,8 @@ sub print_netdev_full {
 sub print_cpu_device {
     my ($conf, $id) = @_;
 
-    my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
-    my $cpu = $nokvm ? "qemu64" : "kvm64";
+    my $kvm = $conf->{kvm} // 1;
+    my $cpu = $kvm ? "kvm64" : "qemu64";
     if (my $cputype = $conf->{cpu}) {
        my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
            or die "Cannot parse cpu description: $cputype\n";
@@ -2079,6 +2112,23 @@ sub destroy_vm {
 
     PVE::QemuConfig->check_lock($conf) if !$skiplock;
 
+    if ($conf->{template}) {
+       # check if any base image is still used by a linked clone
+       foreach_drive($conf, sub {
+               my ($ds, $drive) = @_;
+
+               return if drive_is_cdrom($drive);
+
+               my $volid = $drive->{file};
+
+               return if !$volid || $volid =~ m|^/|;
+
+               die "base volume '$volid' is still in use by linked cloned\n"
+                   if PVE::Storage::volume_is_base_and_used($storecfg, $volid);
+
+       });
+    }
+
     # only remove disks owned by this VM
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
@@ -2747,24 +2797,34 @@ sub foreach_volid {
     my $volhash = {};
 
     my $test_volid = sub {
-       my ($volid, $is_cdrom) = @_;
+       my ($volid, $is_cdrom, $replicate, $snapname) = @_;
 
        return if !$volid;
 
-       $volhash->{$volid} = $is_cdrom || 0;
+       $volhash->{$volid}->{cdrom} //= 1;
+       $volhash->{$volid}->{cdrom} = 0 if !$is_cdrom;
+
+       $volhash->{$volid}->{replicate} //= 0;
+       $volhash->{$volid}->{replicate} = 1 if $replicate;
+
+       $volhash->{$volid}->{referenced_in_config} //= 0;
+       $volhash->{$volid}->{referenced_in_config} = 1 if !defined($snapname);
+
+       $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
+           if defined($snapname);
     };
 
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
-       &$test_volid($drive->{file}, drive_is_cdrom($drive));
+       $test_volid->($drive->{file}, drive_is_cdrom($drive), $drive->{replicate} // 1, undef);
     });
 
     foreach my $snapname (keys %{$conf->{snapshots}}) {
        my $snap = $conf->{snapshots}->{$snapname};
-       &$test_volid($snap->{vmstate}, 0);
+       $test_volid->($snap->{vmstate}, 0, 1, $snapname);
        foreach_drive($snap, sub {
            my ($ds, $drive) = @_;
-           &$test_volid($drive->{file}, drive_is_cdrom($drive));
+           $test_volid->($drive->{file}, drive_is_cdrom($drive), $drive->{replicate} // 1, $snapname);
         });
     }
 
@@ -2796,6 +2856,9 @@ sub config_to_command {
     my $vernum = 0; # unknown
     my $ostype = $conf->{ostype};
     my $winversion = windows_version($ostype);
+    my $kvm = $conf->{kvm} // 1;
+
+    die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n" if (!$cpuinfo->{hvm} && $kvm);
 
     if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
        $vernum = $1*1000000+$2*1000;
@@ -2836,44 +2899,27 @@ sub config_to_command {
     }
 
     if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
-       my $ovmfbase;
+       die "uefi base image not found\n" if ! -f $OVMF_CODE;
 
-       # prefer the OVMF_CODE variant
-       if (-f $OVMF_CODE) {
-           $ovmfbase = $OVMF_CODE;
-       } elsif (-f $OVMF_IMG) {
-           $ovmfbase = $OVMF_IMG;
-       }
-
-       die "no uefi base img found\n" if !$ovmfbase;
-       push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmfbase";
-
-       if (defined($conf->{efidisk0}) && ($ovmfbase eq $OVMF_CODE)) {
-           my $d = PVE::JSONSchema::parse_property_string($efidisk_fmt, $conf->{efidisk0});
-           my $format = $d->{format} // 'raw';
-           my $path;
+       my $path;
+       my $format = 'raw';
+       if (my $efidisk = $conf->{efidisk0}) {
+           my $d = PVE::JSONSchema::parse_property_string($efidisk_fmt, $efidisk);
            my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1);
            if ($storeid) {
                $path = PVE::Storage::path($storecfg, $d->{file});
-               my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
-               $format = qemu_img_format($scfg, $volname);
            } else {
                $path = $d->{file};
-               $format = "raw";
            }
-           push @$cmd, '-drive', "if=pflash,unit=1,id=drive-efidisk0,format=$format,file=$path";
-       } elsif ($ovmfbase eq $OVMF_CODE) {
-           warn "using uefi without permanent efivars disk\n";
-           my $ovmfvar_dst = "/tmp/$vmid-ovmf.fd";
-           PVE::Tools::file_copy($OVMF_VARS, $ovmfvar_dst, 256*1024);
-           push @$cmd, '-drive', "if=pflash,unit=1,format=raw,file=$ovmfvar_dst";
+           $format = $d->{format} if $d->{format};
        } else {
-           # if the base img is not OVMF_CODE, we do not have to bother
-           # to create/use a vars image, since it will not be used anyway
-           # this can only happen if someone manually deletes the OVMF_CODE image
-           # or has an old pve-qemu-kvm version installed.
-           # both should not happen, but we ignore it here
+           warn "no efidisk configured! Using temporary efivars disk.\n";
+           $path = "/tmp/$vmid-ovmf.fd";
+           PVE::Tools::file_copy($OVMF_VARS, $path, -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,file=$path";
     }
 
 
@@ -2886,7 +2932,11 @@ sub config_to_command {
     $vga = 'qxl' if $qxlnum;
 
     if (!$vga) {
-       $vga = $winversion >= 6 ? 'std' : 'cirrus';
+       if (qemu_machine_feature_enabled($machine_type, $kvmver, 2, 9)) {
+           $vga = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus';
+       } else {
+           $vga = ($winversion >= 6) ? 'std' : 'cirrus';
+       }
     }
 
     # enable absolute mouse coordinates (needed by vnc)
@@ -3042,7 +3092,6 @@ sub config_to_command {
     # time drift fix
     my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
 
-    my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
     my $useLocaltime = $conf->{localtime};
 
     if ($winversion >= 5) { # windows
@@ -3061,10 +3110,8 @@ sub config_to_command {
 
     push @$rtcFlags, 'driftfix=slew' if $tdf;
 
-    if ($nokvm) {
+    if (!$kvm) {
        push @$machineFlags, 'accel=tcg';
-    } else {
-       die "No accelerator found!\n" if !$cpuinfo->{hvm};
     }
 
     if ($machine_type) {
@@ -3077,7 +3124,7 @@ sub config_to_command {
        push @$rtcFlags, 'base=localtime';
     }
 
-    my $cpu = $nokvm ? "qemu64" : "kvm64";
+    my $cpu = $kvm ? "kvm64" : "qemu64";
     if (my $cputype = $conf->{cpu}) {
        my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
            or die "Cannot parse cpu description: $cputype\n";
@@ -3096,13 +3143,13 @@ sub config_to_command {
 
     if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
 
-       push @$cpuFlags , '+kvm_pv_unhalt' if !$nokvm;
-       push @$cpuFlags , '+kvm_pv_eoi' if !$nokvm;
+       push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
+       push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
     }
 
-    add_hyperv_enlighments($cpuFlags, $winversion, $machine_type, $kvmver, $nokvm, $conf->{bios}, $gpu_passthrough);
+    add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough) if $kvm;
 
-    push @$cpuFlags, 'enforce' if $cpu ne 'host' && !$nokvm;
+    push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm;
 
     push @$cpuFlags, 'kvm=off' if $kvm_off;
 
@@ -3200,6 +3247,9 @@ sub config_to_command {
            push @$vollist, $drive->{file};
        }
 
+       # ignore efidisk here, already added in bios/fw handling code above
+       return if $drive->{interface} eq 'efidisk';
+
        $use_virtio = 1 if $ds =~ m/^virtio/;
 
        if (drive_is_cdrom ($drive)) {
@@ -3249,11 +3299,6 @@ sub config_to_command {
            $ahcicontroller->{$controller}=1;
         }
 
-       if ($drive->{interface} eq 'efidisk') {
-           # this will be added somewhere else
-           return;
-       }
-
        my $drive_cmd = print_drive_full($storecfg, $vmid, $drive);
        push @$devices, '-drive',$drive_cmd;
        push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
@@ -4051,6 +4096,7 @@ my $fast_plug_option = {
     'startup' => 1,
     'description' => 1,
     'protection' => 1,
+    'vmstatestorage' => 1,
 };
 
 # hotplug changes in [PENDING]
@@ -4817,7 +4863,7 @@ sub get_vm_volumes {
 
     my $vollist = [];
     foreach_volid($conf, sub {
-       my ($volid, $is_cdrom) = @_;
+       my ($volid, $attr) = @_;
 
        return if $volid =~ m|^/|;
 
@@ -5439,9 +5485,10 @@ sub restore_vma_archive {
     rmtree $tmpdir;
 
     # disable interrupts (always do cleanups)
-    local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
-       warn "got interrupt - ignored\n";
-    };
+    local $SIG{INT} =
+       local $SIG{TERM} =
+       local $SIG{QUIT} =
+       local $SIG{HUP} = sub { warn "got interrupt - ignored\n"; };
 
     my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
     POSIX::mkfifo($mapfifo, 0600);
@@ -5600,9 +5647,11 @@ sub restore_vma_archive {
 
     eval {
        # enable interrupts
-       local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
-           die "interrupted by signal\n";
-       };
+       local $SIG{INT} =
+           local $SIG{TERM} =
+           local $SIG{QUIT} =
+           local $SIG{HUP} =
+           local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
        local $SIG{ALRM} = sub { die "got timeout\n"; };
 
        $oldtimeout = alarm($timeout);
@@ -5716,15 +5765,18 @@ sub restore_tar_archive {
     my $tmpfn = "$conffile.$$.tmp";
 
     # disable interrupts (always do cleanups)
-    local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
-       print STDERR "got interrupt - ignored\n";
-    };
+    local $SIG{INT} =
+       local $SIG{TERM} =
+       local $SIG{QUIT} =
+       local $SIG{HUP} = sub { print STDERR "got interrupt - ignored\n"; };
 
     eval {
        # enable interrupts
-       local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
-           die "interrupted by signal\n";
-       };
+       local $SIG{INT} =
+           local $SIG{TERM} =
+           local $SIG{QUIT} =
+           local $SIG{HUP} =
+           local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
 
        if ($archive eq '-') {
            print "extracting archive from STDIN\n";
@@ -5788,23 +5840,20 @@ sub restore_tar_archive {
     warn $@ if $@;
 };
 
-sub foreach_writable_storage {
+sub foreach_storage_used_by_vm {
     my ($conf, $func) = @_;
 
     my $sidhash = {};
 
-    foreach my $ds (keys %$conf) {
-       next if !is_valid_drivename($ds);
-
-       my $drive = parse_drive($ds, $conf->{$ds});
-       next if !$drive;
-       next if drive_is_cdrom($drive);
+    foreach_drive($conf, sub {
+       my ($ds, $drive) = @_;
+       return if drive_is_cdrom($drive);
 
        my $volid = $drive->{file};
 
        my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
        $sidhash->{$sid} = $sid if $sid;
-    }
+    });
 
     foreach my $sid (sort keys %$sidhash) {
        &$func($sid);
@@ -6146,20 +6195,11 @@ sub clone_disk {
        my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
        $storeid = $storage if $storage;
 
-       my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
-       if (!$format) {
-           my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
-           $format = qemu_img_format($scfg, $volname);
-       }
-
-       # test if requested format is supported - else use default
-       my $supported = grep { $_ eq $format } @$validFormats;
-       $format = $defFormat if !$supported;
-
+       my $dst_format = resolve_dst_disk_format($storecfg, $storeid, $volname, $format);
        my ($size) = PVE::Storage::volume_size_info($storecfg, $drive->{file}, 3);
 
        print "create full clone of drive $drivename ($drive->{file})\n";
-       $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $format, undef, ($size/1024));
+       $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $dst_format, undef, ($size/1024));
        push @$newvollist, $newvolid;
 
        PVE::Storage::activate_volumes($storecfg, [$newvolid]);
@@ -6274,6 +6314,24 @@ sub qemu_use_old_bios_files {
     return ($use_old_bios_files, $machine_type);
 }
 
+sub create_efidisk {
+    my ($storecfg, $storeid, $vmid, $fmt) = @_;
+
+    die "EFI vars default image not found\n" if ! -f $OVMF_VARS;
+
+    my $vars_size = PVE::Tools::convert_size(-s $OVMF_VARS, 'b' => 'kb');
+    my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
+    PVE::Storage::activate_volumes($storecfg, [$volid]);
+
+    my $path = PVE::Storage::path($storecfg, $volid);
+    eval {
+       run_command(['/usr/bin/qemu-img', 'convert', '-n', '-f', 'raw', '-O', $fmt, $OVMF_VARS, $path]);
+    };
+    die "Copying EFI vars image failed: $@" if $@;
+
+    return ($volid, $vars_size);
+}
+
 sub lspci {
 
     my $devices = {};
@@ -6284,6 +6342,12 @@ sub lspci {
            push @{$devices->{$id}}, $res;
     });
 
+    # Entries should be sorted by functions.
+    foreach my $id (keys %$devices) {
+       my $dev = $devices->{$id};
+       $devices->{$id} = [ sort { $a->{function} <=> $b->{function} } @$dev ];
+    }
+
     return $devices;
 }
 
@@ -6319,10 +6383,9 @@ sub scsihw_infos {
     return ($maxdev, $controller, $controller_prefix);
 }
 
-sub add_hyperv_enlighments {
-    my ($cpuFlags, $winversion, $machine_type, $kvmver, $nokvm, $bios, $gpu_passthrough) = @_;
+sub add_hyperv_enlightenments {
+    my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough) = @_;
 
-    return if $nokvm;
     return if $winversion < 6;
     return if $bios && $bios eq 'ovmf' && $winversion < 8;
 
@@ -6365,6 +6428,46 @@ sub windows_version {
     return $winversion;
 }
 
+sub resolve_dst_disk_format {
+       my ($storecfg, $storeid, $src_volname, $format) = @_;
+       my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
+
+       if (!$format) {
+           # if no target format is specified, use the source disk format as hint
+           if ($src_volname) {
+               my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+               $format = qemu_img_format($scfg, $src_volname);
+           } else {
+               return $defFormat;
+           }
+       }
+
+       # test if requested format is supported - else use default
+       my $supported = grep { $_ eq $format } @$validFormats;
+       $format = $defFormat if !$supported;
+       return $format;
+}
+
+sub resolve_first_disk {
+    my $conf = shift;
+    my @disks = PVE::QemuServer::valid_drive_names();
+    my $firstdisk;
+    foreach my $ds (reverse @disks) {
+       next if !$conf->{$ds};
+       my $disk = PVE::QemuServer::parse_drive($ds, $conf->{$ds});
+       next if PVE::QemuServer::drive_is_cdrom($disk);
+       $firstdisk = $ds;
+    }
+    return $firstdisk;
+}
+
+sub generate_smbios1_uuid {
+    my ($uuid, $uuid_str);
+    UUID::generate($uuid);
+    UUID::unparse($uuid, $uuid_str);
+    return "uuid=$uuid_str";
+}
+
 # bash completion helper
 
 sub complete_backup_archives {