]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuConfig.pm
rollback: vmgenid: comment why we regenerate vmgenid
[qemu-server.git] / PVE / QemuConfig.pm
index 3ce95ac86b5cc4de7d5e17cff7ad829326575aa3..c2192cf181313e4a28c63a816f04c547ddba655d 100644 (file)
@@ -29,7 +29,7 @@ sub guest_type {
 }
 
 sub __config_max_unused_disks {
-    my ($class) =@_;
+    my ($class) = @_;
 
     return $MAX_UNUSED_DISKS;
 }
@@ -55,7 +55,7 @@ sub has_feature {
        my ($ds, $drive) = @_;
 
        return if PVE::QemuServer::drive_is_cdrom($drive);
-       return if $backup_only && !$drive->{backup};
+       return if $backup_only && defined($drive->{backup}) && !$drive->{backup};
        my $volid = $drive->{file};
        $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $volid, $snapname, $running);
    });
@@ -63,34 +63,80 @@ sub has_feature {
     return $err ? 0 : 1;
 }
 
+sub get_replicatable_volumes {
+    my ($class, $storecfg, $vmid, $conf, $cleanup, $noerr) = @_;
+
+    my $volhash = {};
+
+    my $test_volid = sub {
+       my ($volid, $attr) = @_;
+
+       return if $attr->{cdrom};
+
+       return if !$cleanup && !$attr->{replicate};
+
+       if ($volid =~ m|^/|) {
+           return if !$attr->{replicate};
+           return if $cleanup || $noerr;
+           die "unable to replicate local file/device '$volid'\n";
+       }
+
+       my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, $noerr);
+       return if !$storeid;
+
+       my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+       return if $scfg->{shared};
+
+       my ($path, $owner, $vtype) = PVE::Storage::path($storecfg, $volid);
+       return if !$owner || ($owner != $vmid);
+
+       if ($vtype ne 'images') {
+           return if $cleanup || $noerr;
+           die "unable to replicate volume '$volid', type '$vtype'\n";
+       }
+
+       if (!PVE::Storage::volume_has_feature($storecfg, 'replicate', $volid)) {
+           return if $cleanup || $noerr;
+           die "missing replicate feature on volume '$volid'\n";
+       }
+
+       $volhash->{$volid} = 1;
+    };
+
+    PVE::QemuServer::foreach_volid($conf, $test_volid);
+
+    # add 'unusedX' volumes to volhash
+    foreach my $key (keys %$conf) {
+       if ($key =~ m/^unused/) {
+           $test_volid->($conf->{$key}, { replicate => 1 });
+       }
+    }
+
+    return $volhash;
+}
+
 sub __snapshot_save_vmstate {
     my ($class, $vmid, $conf, $snapname, $storecfg) = @_;
 
     my $snap = $conf->{snapshots}->{$snapname};
 
-    my $target;
-
-    # search shared storage first
-    PVE::QemuServer::foreach_writable_storage($conf, sub {
-       my ($sid) = @_;
-       my $scfg = PVE::Storage::storage_config($storecfg, $sid);
-       return if !$scfg->{shared};
-
-       $target = $sid if !$target || $scfg->{path}; # prefer file based storage
-    });
+    # first, use explicitly configured storage
+    my $target = $conf->{vmstatestorage};
 
     if (!$target) {
-       # now search local storage
-       PVE::QemuServer::foreach_writable_storage($conf, sub {
+       my ($shared, $local);
+       PVE::QemuServer::foreach_storage_used_by_vm($conf, sub {
            my ($sid) = @_;
            my $scfg = PVE::Storage::storage_config($storecfg, $sid);
-           return if $scfg->{shared};
-
-           $target = $sid if !$target || $scfg->{path}; # prefer file based storage;
+           my $dst = $scfg->{shared} ? \$shared : \$local;
+           $$dst = $sid if !$$dst || $scfg->{path}; # prefer file based storage
        });
-    }
 
-    $target = 'local' if !$target;
+       # 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';
+    }
 
     my $driver_state_size = 500; # assume 32MB is enough to safe all driver state;
     # we abort live save after $conf->{memory}, so we need at max twice that space
@@ -100,9 +146,7 @@ sub __snapshot_save_vmstate {
     my $scfg = PVE::Storage::storage_config($storecfg, $target);
     $name .= ".raw" if $scfg->{path}; # add filename extension for file base storage
     $snap->{vmstate} = PVE::Storage::vdisk_alloc($storecfg, $target, $vmid, 'raw', $name, $size*1024);
-    # always overwrite machine if we save vmstate. This makes sure we
-    # can restore it later using correct machine type
-    $snap->{machine} = PVE::QemuServer::get_current_qemu_machine($vmid);
+    $snap->{runningmachine} = PVE::QemuServer::get_current_qemu_machine($vmid);
 }
 
 sub __snapshot_check_running {
@@ -114,8 +158,8 @@ sub __snapshot_check_freeze_needed {
     my ($class, $vmid, $config, $save_vmstate) = @_;
 
     my $running = $class->__snapshot_check_running($vmid);
-    if ($save_vmstate) {
-       return ($running, $running && $config->{agent} && PVE::QemuServer::qga_check_running($vmid));
+    if (!$save_vmstate) {
+       return ($running, $running && PVE::QemuServer::parse_guest_agent($config)->{enabled} && PVE::QemuServer::qga_check_running($vmid));
     } else {
        return ($running, 0);
     }
@@ -137,10 +181,13 @@ sub __snapshot_create_vol_snapshots_hook {
     my ($class, $vmid, $snap, $running, $hook) = @_;
 
     if ($running) {
+       my $storecfg = PVE::Storage::config();
+
        if ($hook eq "before") {
            if ($snap->{vmstate}) {
-               my $storecfg = PVE::Storage::config();
                my $path = PVE::Storage::path($storecfg, $snap->{vmstate});
+               PVE::Storage::activate_volumes($storecfg, [$snap->{vmstate}]);
+
                PVE::QemuServer::vm_mon_cmd($vmid, "savevm-start", statefile => $path);
                for(;;) {
                    my $stat = PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "query-savevm");
@@ -159,7 +206,10 @@ sub __snapshot_create_vol_snapshots_hook {
                PVE::QemuServer::vm_mon_cmd($vmid, "savevm-start");
            }
        } elsif ($hook eq "after") {
-           eval { PVE::QemuServer::vm_mon_cmd($vmid, "savevm-end")  };
+           eval { 
+               PVE::QemuServer::vm_mon_cmd($vmid, "savevm-end");
+               PVE::Storage::deactivate_volumes($storecfg, [$snap->{vmstate}]) if $snap->{vmstate};
+           };
            warn $@ if $@;
        } elsif ($hook eq "after-freeze") {
            # savevm-end is async, we need to wait
@@ -229,6 +279,39 @@ sub __snapshot_delete_vol_snapshot {
     push @$unused, $volid;
 }
 
+sub __snapshot_rollback_hook {
+    my ($class, $vmid, $conf, $snap, $prepare, $data) = @_;
+
+    if ($prepare) {
+       # we save the machine of the current config
+       $data->{oldmachine} = $conf->{machine};
+    } else {
+       # if we have a 'runningmachine' entry in the snapshot we use that
+       # for the forcemachine parameter, else we use the old logic
+       if (defined($conf->{runningmachine})) {
+           $data->{forcemachine} = $conf->{runningmachine};
+           delete $conf->{runningmachine};
+       } 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';
+
+           # we remove the 'machine' configuration if not explicitly specified
+           # in the original config.
+           delete $conf->{machine} if $snap->{vmstate} && !defined($data->{oldmachine});
+       }
+
+       if ($conf->{vmgenid}) {
+           # tell the VM that it's another generation, so it can react
+           # appropriately, e.g. dirty-mark copies of distributed databases or
+           # re-initializing its random number generator
+           $conf->{vmgenid} = PVE::QemuServer::generate_uuid();
+       }
+    }
+
+    return;
+}
+
 sub __snapshot_rollback_vol_possible {
     my ($class, $drive, $snapname) = @_;
 
@@ -257,11 +340,11 @@ sub __snapshot_rollback_vm_stop {
 }
 
 sub __snapshot_rollback_vm_start {
-    my ($class, $vmid, $vmstate, $forcemachine) = @_;
+    my ($class, $vmid, $vmstate, $data) = @_;
 
     my $storecfg = PVE::Storage::config();
     my $statefile = PVE::Storage::path($storecfg, $vmstate);
-    PVE::QemuServer::vm_start($storecfg, $vmid, $statefile, undef, undef, undef, $forcemachine);
+    PVE::QemuServer::vm_start($storecfg, $vmid, $statefile, undef, undef, undef, $data->{forcemachine});
 }
 
 sub __snapshot_rollback_get_unused {