]> 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 e10461f11ae1f5cb39fc04ed4922deec7858a811..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
@@ -2805,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};
@@ -2863,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) = @_;
@@ -3461,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);
@@ -3472,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();
@@ -3880,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;
@@ -5314,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})) {
@@ -5454,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);
@@ -5733,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) = @_;
 
@@ -5756,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);
@@ -6054,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;
@@ -6075,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})) {
@@ -6084,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($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";
            }
        }
     }
@@ -6100,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};
        }
@@ -6117,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)
     }
 
@@ -6151,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;
     };
@@ -7201,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);