X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FQemuConfig.pm;h=8e8a7828e10e7f0ce2f57318e25d506755f46b89;hb=HEAD;hp=7f350b1e0eab535d2e2204afa6470e5fe4423102;hpb=185df962a52cec3ec8559a7d858b529fdab36995;p=qemu-server.git diff --git a/PVE/QemuConfig.pm b/PVE/QemuConfig.pm index 7f350b1..8e8a782 100644 --- a/PVE/QemuConfig.pm +++ b/PVE/QemuConfig.pm @@ -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;