]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
update disk size before local disk migration
[qemu-server.git] / PVE / QemuServer.pm
index 76146ca22f43d5ae31e60d9aa77d02d53f8625cd..c8e2b6678ebfa19711c92bd14ccf7b46b5d8c46d 100644 (file)
@@ -108,7 +108,11 @@ sub cgroups_write {
 
 }
 
-my $nodename = PVE::INotify::nodename();
+my $nodename_cache;
+sub nodename {
+    $nodename_cache //= PVE::INotify::nodename();
+    return $nodename_cache;
+}
 
 my $cpu_vendor_list = {
     # Intel CPUs
@@ -1751,7 +1755,7 @@ sub parse_drive {
 }
 
 sub print_drive {
-    my ($vmid, $drive) = @_;
+    my ($drive) = @_;
     my $data = { %$drive };
     delete $data->{$_} for qw(index interface);
     return PVE::JSONSchema::print_property_string($data, $alldrive_fmt);
@@ -2246,13 +2250,9 @@ sub parse_hostpci {
     my @idlist = split(/;/, $res->{host});
     delete $res->{host};
     foreach my $id (@idlist) {
-       if ($id =~ m/\./) { # full id 00:00.1
-           push @{$res->{pciid}}, {
-               id => $id,
-           };
-       } else { # partial id 00:00
-           $res->{pciid} = PVE::SysFSTools::lspci($id);
-       }
+       my $devs = PVE::SysFSTools::lspci($id);
+       die "no PCI device found for '$id'\n" if !scalar(@$devs);
+       push @{$res->{pciid}}, @$devs;
     }
     return $res;
 }
@@ -2667,7 +2667,7 @@ sub parse_vm_config {
                    my $v = parse_drive($key, $value);
                    if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
                        $v->{file} = $volid;
-                       $value = print_drive($vmid, $v);
+                       $value = print_drive($v);
                    } else {
                        warn "vm $vmid - unable to parse value of '$key'\n";
                        next;
@@ -2809,6 +2809,7 @@ sub config_list {
     my $res = {};
     return $res if !$vmlist || !$vmlist->{ids};
     my $ids = $vmlist->{ids};
+    my $nodename = nodename();
 
     foreach my $vmid (keys %$ids) {
        my $d = $ids->{$vmid};
@@ -2867,7 +2868,7 @@ sub shared_nodes {
 
     my $nodelist = PVE::Cluster::get_nodelist();
     my $nodehash = { map { $_ => 1 } @$nodelist };
-    my $nodename = PVE::INotify::nodename();
+    my $nodename = nodename();
 
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
@@ -3465,6 +3466,7 @@ sub config_to_command {
     my $ostype = $conf->{ostype};
     my $winversion = windows_version($ostype);
     my $kvm = $conf->{kvm};
+    my $nodename = nodename();
 
     my $arch = get_vm_arch($conf);
     my $kvm_binary = get_command_for_arch($arch);
@@ -3476,6 +3478,10 @@ sub config_to_command {
     my $machine_version = PVE::QemuServer::Machine::extract_version($machine_type, $kvmver);
     $kvm //= 1 if is_native($arch);
 
+    $machine_version =~ m/(\d+)\.(\d+)/;
+    die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type', please upgrade node '$nodename'\n"
+       if !PVE::QemuServer::min_version($kvmver, $1, $2);
+
     if ($kvm) {
        die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n"
            if !defined kvm_version();
@@ -3884,7 +3890,6 @@ sub config_to_command {
 
        my $pciaddr = print_pci_addr("spice", $bridges, $arch, $machine_type);
 
-       my $nodename = PVE::INotify::nodename();
        my $pfamily = PVE::Tools::get_host_address_family($nodename);
        my @nodeaddrs = PVE::Tools::getaddrinfo_all('localhost', family => $pfamily);
        die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
@@ -5267,7 +5272,7 @@ sub vm_start {
                my $newdrive = $drive;
                $newdrive->{format} = $format;
                $newdrive->{file} = $newvolid;
-               my $drivestr = PVE::QemuServer::print_drive($vmid, $newdrive);
+               my $drivestr = print_drive($newdrive);
                $local_volumes->{$opt} = $drivestr;
                #pass drive to conf for command line
                $conf->{$opt} = $drivestr;
@@ -5318,7 +5323,7 @@ sub vm_start {
            if ($statefile eq 'tcp') {
                my $localip = "localhost";
                my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
-               my $nodename = PVE::INotify::nodename();
+               my $nodename = nodename();
 
                if (!defined($migration_type)) {
                    if (defined($datacenterconf->{migration}->{type})) {
@@ -5369,7 +5374,6 @@ sub vm_start {
          my $pcidevices = $d->{pciid};
          foreach my $pcidevice (@$pcidevices) {
                my $pciid = $pcidevice->{id};
-               $pciid = "0000:$pciid" if $pciid !~ m/^[0-9a-f]{4}:/;
 
                my $info = PVE::SysFSTools::pci_device_info("$pciid");
                die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support();
@@ -5459,7 +5463,7 @@ sub vm_start {
 
        #start nbd server for storage migration
        if ($targetstorage) {
-           my $nodename = PVE::INotify::nodename();
+           my $nodename = nodename();
            my $localip = $get_migration_ip->($migration_network, $nodename);
            my $pfamily = PVE::Tools::get_host_address_family($nodename);
            my $storage_migrate_port = PVE::Tools::next_migrate_port($pfamily);
@@ -5738,6 +5742,7 @@ sub vm_reboot {
    });
 }
 
+# note: if using the statestorage parameter, the caller has to check privileges
 sub vm_suspend {
     my ($vmid, $skiplock, $includestate, $statestorage) = @_;
 
@@ -5761,6 +5766,17 @@ sub vm_suspend {
            $conf->{lock} = 'suspending';
            my $date = strftime("%Y-%m-%d", localtime(time()));
            $storecfg = PVE::Storage::config();
+           if (!$statestorage) {
+               $statestorage = find_vmstate_storage($conf, $storecfg);
+               # check permissions for the storage
+               my $rpcenv = PVE::RPCEnvironment::get();
+               if ($rpcenv->{type} ne 'cli') {
+                   my $authuser = $rpcenv->get_user();
+                   $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
+               }
+           }
+
+
            $vmstate = PVE::QemuConfig->__snapshot_save_vmstate($vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
            $path = PVE::Storage::path($storecfg, $vmstate);
            PVE::QemuConfig->write_config($vmid, $conf);
@@ -5976,7 +5992,7 @@ sub restore_update_config_line {
        } elsif ($map->{$virtdev}) {
            delete $di->{format}; # format can change on restore
            $di->{file} = $map->{$virtdev};
-           $value = print_drive($vmid, $di);
+           $value = print_drive($di);
            print $outfd "$virtdev: $value\n";
        } else {
            print $outfd $line;
@@ -6059,6 +6075,27 @@ sub is_volume_in_use {
 }
 
 sub update_disksize {
+    my ($drive, $volid_hash) = @_;
+
+    my $volid = $drive->{file};
+    return undef if !defined($volid);
+
+    my $oldsize = $drive->{size};
+    my $newsize = $volid_hash->{$volid}->{size};
+
+    if (defined($newsize) && defined($oldsize) && $newsize != $oldsize) {
+       $drive->{size} = $newsize;
+
+       my $old_fmt = PVE::JSONSchema::format_size($oldsize);
+       my $new_fmt = PVE::JSONSchema::format_size($newsize);
+
+       return wantarray ? ($drive, $old_fmt, $new_fmt) : $drive;
+    }
+
+    return undef;
+}
+
+sub update_disk_config {
     my ($vmid, $conf, $volid_hash) = @_;
 
     my $changes;
@@ -6080,6 +6117,7 @@ sub update_disksize {
            my $volid = $drive->{file};
            next if !$volid;
 
+           # mark volid as "in-use" for next step
            $referenced->{$volid} = 1;
            if ($volid_hash->{$volid} &&
                (my $path = $volid_hash->{$volid}->{path})) {
@@ -6089,12 +6127,11 @@ sub update_disksize {
            next if drive_is_cdrom($drive);
            next if !$volid_hash->{$volid};
 
-           $drive->{size} = $volid_hash->{$volid}->{size};
-           my $new = print_drive($vmid, $drive);
-           if ($new ne $conf->{$opt}) {
+           my ($updated, $old_size, $new_size) = update_disksize($drive, $volid_hash);
+           if (defined($updated)) {
                $changes = 1;
-               $conf->{$opt} = $new;
-               print "$prefix update disk '$opt' information.\n";
+               $conf->{$opt} = print_drive($updated);
+               print "$prefix size of disk '$volid' ($opt) updated from $old_size to $new_size\n";
            }
        }
     }
@@ -6105,7 +6142,7 @@ sub update_disksize {
        my $volid = $conf->{$opt};
        my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
        if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
-           print "$prefix remove entry '$opt', its volume '$volid' is in use.\n";
+           print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
            $changes = 1;
            delete $conf->{$opt};
        }
@@ -6122,7 +6159,7 @@ sub update_disksize {
        next if $referencedpath->{$path};
        $changes = 1;
        my $key = PVE::QemuConfig->add_unused_volume($conf, $volid);
-       print "$prefix add unreferenced volume '$volid' as '$key' to config.\n";
+       print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
        $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
     }
 
@@ -6156,7 +6193,7 @@ sub rescan {
            $vm_volids->{$volid} = $info if $info->{vmid} && $info->{vmid} == $vmid;
        }
 
-       my $changes = update_disksize($vmid, $conf, $vm_volids);
+       my $changes = update_disk_config($vmid, $conf, $vm_volids);
 
        PVE::QemuConfig->write_config($vmid, $conf) if $changes && !$dryrun;
     };
@@ -6688,7 +6725,7 @@ sub template_create {
 
        my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
        $drive->{file} = $voliddst;
-       $conf->{$ds} = print_drive($vmid, $drive);
+       $conf->{$ds} = print_drive($drive);
        PVE::QemuConfig->write_config($vmid, $conf);
     });
 }
@@ -7206,6 +7243,30 @@ sub resolve_first_disk {
     return $firstdisk;
 }
 
+# NOTE: if this logic changes, please update docs & possibly gui logic
+sub find_vmstate_storage {
+    my ($conf, $storecfg) = @_;
+
+    # first, return storage from conf if set
+    return $conf->{vmstatestorage} if $conf->{vmstatestorage};
+
+    my ($target, $shared, $local);
+
+    foreach_storage_used_by_vm($conf, sub {
+       my ($sid) = @_;
+       my $scfg = PVE::Storage::storage_config($storecfg, $sid);
+       my $dst = $scfg->{shared} ? \$shared : \$local;
+       $$dst = $sid if !$$dst || $scfg->{path}; # prefer file based storage
+    });
+
+    # second, use shared storage where VM has at least one disk
+    # third, use local storage where VM has at least one disk
+    # fall back to local storage
+    $target = $shared // $local // 'local';
+
+    return $target;
+}
+
 sub generate_uuid {
     my ($uuid, $uuid_str);
     UUID::generate($uuid);