]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuConfig.pm
suspend: continue cleanup even if savevm-end QMP command fails
[qemu-server.git] / PVE / QemuConfig.pm
index 7f350b1e0eab535d2e2204afa6470e5fe4423102..8e8a7828e10e7f0ce2f57318e25d506755f46b89 100644 (file)
@@ -12,8 +12,10 @@ use PVE::QemuServer::Helpers;
 use PVE::QemuServer::Monitor qw(mon_cmd);
 use PVE::QemuServer;
 use PVE::QemuServer::Machine;
+use PVE::QemuServer::Memory qw(get_current_memory);
 use PVE::Storage;
 use PVE::Tools;
+use PVE::Format qw(render_bytes render_duration);
 
 use base qw(PVE::AbstractConfig);
 
@@ -95,7 +97,7 @@ sub parse_volume {
     if ($key eq 'vmstate') {
        eval { PVE::JSONSchema::check_format('pve-volume-id', $volume_string) };
        if (my $err = $@) {
-           return undef if $noerr;
+           return if $noerr;
            die $err;
        }
        $volume = { 'file' => $volume_string };
@@ -207,8 +209,7 @@ sub __snapshot_save_vmstate {
        $target = PVE::QemuServer::find_vmstate_storage($conf, $storecfg);
     }
 
-    my $defaults = PVE::QemuServer::load_defaults();
-    my $mem_size = $conf->{memory} // $defaults->{memory};
+    my $mem_size = get_current_memory($conf->{memory});
     my $driver_state_size = 500; # assume 500MB is enough to safe all driver state;
     # our savevm-start does live-save of the memory until the space left in the
     # volume is just enough for the remaining memory content + internal state
@@ -240,6 +241,25 @@ sub __snapshot_save_vmstate {
     return $statefile;
 }
 
+sub __snapshot_activate_storages {
+    my ($class, $conf, $include_vmstate) = @_;
+
+    my $storecfg = PVE::Storage::config();
+    my $opts = $include_vmstate ? { 'extra_keys' => ['vmstate'] } : {};
+    my $storage_hash = {};
+
+    $class->foreach_volume_full($conf, $opts, sub {
+       my ($key, $drive) = @_;
+
+       return if PVE::QemuServer::drive_is_cdrom($drive);
+
+       my ($storeid) = PVE::Storage::parse_volume_id($drive->{file});
+       $storage_hash->{$storeid} = 1;
+    });
+
+    PVE::Storage::activate_storage_list($storecfg, [ sort keys $storage_hash->%* ]);
+}
+
 sub __snapshot_check_running {
     my ($class, $vmid) = @_;
     return PVE::QemuServer::Helpers::vm_running_locally($vmid);
@@ -278,19 +298,40 @@ sub __snapshot_create_vol_snapshots_hook {
            if ($snap->{vmstate}) {
                my $path = PVE::Storage::path($storecfg, $snap->{vmstate});
                PVE::Storage::activate_volumes($storecfg, [$snap->{vmstate}]);
+               my $state_storage_id = PVE::Storage::parse_volume_id($snap->{vmstate});
 
+               PVE::QemuServer::set_migration_caps($vmid, 1);
                mon_cmd($vmid, "savevm-start", statefile => $path);
+               print "saving VM state and RAM using storage '$state_storage_id'\n";
+               my $render_state = sub {
+                   my ($stat) = @_;
+                   my $b = render_bytes($stat->{bytes});
+                   my $t = render_duration($stat->{'total-time'} / 1000);
+                   return ($b, $t);
+               };
+               my $round = 0;
                for(;;) {
+                   $round++;
                    my $stat = mon_cmd($vmid, "query-savevm");
                    if (!$stat->{status}) {
                        die "savevm not active\n";
                    } elsif ($stat->{status} eq 'active') {
+                       if ($round < 60 || $round % 10 == 0) {
+                           my ($b, $t) = $render_state->($stat);
+                           print "$b in $t\n";
+                       }
+                       print "reducing reporting rate to every 10s\n" if $round == 60;
                        sleep(1);
                        next;
                    } elsif ($stat->{status} eq 'completed') {
+                       my ($b, $t) = $render_state->($stat);
+                       print "completed saving the VM state in $t, saved $b\n";
                        last;
+                   } elsif ($stat->{status} eq 'failed') {
+                       my $err = $stat->{error} || 'unknown error';
+                       die "unable to save VM state and RAM - $err\n";
                    } else {
-                       die "query-savevm returned status '$stat->{status}'\n";
+                       die "query-savevm returned unexpected status '$stat->{status}'\n";
                    }
                }
            } else {
@@ -327,6 +368,8 @@ sub __snapshot_create_vol_snapshot {
     my $device = "drive-$ds";
     my $storecfg = PVE::Storage::config();
 
+    print "snapshotting '$device' ($drive->{file})\n";
+
     PVE::QemuServer::qemu_volume_snapshot($vmid, $device, $storecfg, $volid, $snapname);
 }
 
@@ -363,9 +406,8 @@ sub __snapshot_delete_vol_snapshot {
     return if PVE::QemuServer::drive_is_cdrom($drive);
     my $storecfg = PVE::Storage::config();
     my $volid = $drive->{file};
-    my $device = "drive-$ds";
 
-    PVE::QemuServer::qemu_volume_snapshot_delete($vmid, $device, $storecfg, $volid, $snapname);
+    PVE::QemuServer::qemu_volume_snapshot_delete($vmid, $storecfg, $volid, $snapname);
 
     push @$unused, $volid;
 }
@@ -390,7 +432,8 @@ sub __snapshot_rollback_hook {
        } else {
            # Note: old code did not store 'machine', so we try to be smart
            # and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4).
-           $data->{forcemachine} = $conf->{machine} || 'pc-i440fx-1.4';
+           my $machine_conf = PVE::QemuServer::Machine::parse_machine($conf->{machine});
+           $data->{forcemachine} = $machine_conf->{type} || 'pc-i440fx-1.4';
 
            # we remove the 'machine' configuration if not explicitly specified
            # in the original config.
@@ -409,14 +452,14 @@ sub __snapshot_rollback_hook {
 }
 
 sub __snapshot_rollback_vol_possible {
-    my ($class, $drive, $snapname) = @_;
+    my ($class, $drive, $snapname, $blockers) = @_;
 
     return if PVE::QemuServer::drive_is_cdrom($drive);
 
     my $storecfg = PVE::Storage::config();
     my $volid = $drive->{file};
 
-    PVE::Storage::volume_rollback_is_possible($storecfg, $volid, $snapname);
+    PVE::Storage::volume_rollback_is_possible($storecfg, $volid, $snapname, $blockers);
 }
 
 sub __snapshot_rollback_vol_rollback {
@@ -455,7 +498,7 @@ sub __snapshot_rollback_get_unused {
     $class->foreach_volume($conf, sub {
        my ($vs, $volume) = @_;
 
-       return if PVE::QemuServer::drive_is_cdrom($volume);
+       return if PVE::QemuServer::drive_is_cdrom($volume, 1);
 
        my $found = 0;
        my $volid = $volume->{file};
@@ -464,7 +507,7 @@ sub __snapshot_rollback_get_unused {
            my ($ds, $drive) = @_;
 
            return if $found;
-           return if PVE::QemuServer::drive_is_cdrom($drive);
+           return if PVE::QemuServer::drive_is_cdrom($drive, 1);
 
            $found = 1
                if ($drive->{file} && $drive->{file} eq $volid);
@@ -476,6 +519,58 @@ sub __snapshot_rollback_get_unused {
     return $unused;
 }
 
+sub add_unused_volume {
+    my ($class, $config, $volid) = @_;
+
+    if ($volid =~ m/vm-\d+-cloudinit/) {
+       print "found unused cloudinit disk '$volid', removing it\n";
+       my $storecfg = PVE::Storage::config();
+       PVE::Storage::vdisk_free($storecfg, $volid);
+       return undef;
+    } else {
+        return $class->SUPER::add_unused_volume($config, $volid);
+    }
+}
+
+sub load_current_config {
+    my ($class, $vmid, $current) = @_;
+
+    my $conf = $class->SUPER::load_current_config($vmid, $current);
+    delete $conf->{cloudinit};
+    return $conf;
+}
+
+sub get_derived_property {
+    my ($class, $conf, $name) = @_;
+
+    my $defaults = PVE::QemuServer::load_defaults();
+
+    if ($name eq 'max-cpu') {
+       my $cpus =
+           ($conf->{sockets} || $defaults->{sockets}) * ($conf->{cores} || $defaults->{cores});
+       return $conf->{vcpus} || $cpus;
+    } elsif ($name eq 'max-memory') { # current usage maximum, not maximum hotpluggable
+       return get_current_memory($conf->{memory}) * 1024 * 1024;
+    } else {
+       die "unknown derived property - $name\n";
+    }
+}
+
 # END implemented abstract methods from PVE::AbstractConfig
 
+sub has_cloudinit {
+    my ($class, $conf, $skip) = @_;
+
+    my $found;
+
+    $class->foreach_volume($conf, sub {
+       my ($key, $volume) = @_;
+
+       return if ($skip && $skip eq $key) || $found;
+       $found = $key if PVE::QemuServer::Drive::drive_is_cloudinit($volume);
+    });
+
+    return $found;
+}
+
 1;