]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
include additional info in snapshot list
[qemu-server.git] / PVE / QemuServer.pm
index 7a6e410179c7f1df46aef686f9a0d9c4b312dc53..8f95bf5341bee7ac1ceb1f806690c48ba66ba10c 100644 (file)
@@ -400,6 +400,11 @@ EODESCR
        type => 'integer',
        minimum => 0,
     },
+    vmstate => {
+       optional => 1,
+       type => 'string', format => 'pve-volume-id',
+       description => "Reference to a volume which stores the VM state. This is used internally for snapshots.",
+    },
 };
 
 # what about other qemu settings ?
@@ -1399,7 +1404,7 @@ sub json_config_properties {
     my $prop = shift;
 
     foreach my $opt (keys %$confdesc) {
-       next if $opt eq 'parent' || $opt eq 'snaptime';
+       next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate';
        $prop->{$opt} = $confdesc->{$opt};
     }
 
@@ -2817,36 +2822,13 @@ sub qemu_volume_snapshot {
 sub qemu_volume_snapshot_delete {
     my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
 
-     #need to implement statefile location
-    my $statefile="/tmp/$vmid-$snap";
-
-    unlink $statefile if -e $statefile;
-
     my $running = PVE::QemuServer::check_running($vmid);
 
     return if !PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
 
     return if !$running;
 
-    #need to split delvm monitor command like savevm
-
-}
-
-sub qemu_snapshot_start {
-    my ($vmid, $snap) = @_;
-
-    #need to implement statefile location
-    my $statefile="/tmp/$vmid-$snap";
-
-    vm_mon_cmd($vmid, "snapshot-start", statefile => $statefile);
-
-}
-
-sub qemu_snapshot_end {
-    my ($vmid) = @_;
-
-    vm_mon_cmd($vmid, "snapshot-end");
-
+    vm_mon_cmd($vmid, "delete-drive-snapshot", device => $deviceid, name => $snap);
 }
 
 sub qga_freezefs {
@@ -2915,9 +2897,11 @@ sub vm_start {
            if ($statefile eq 'tcp') {
                print "migration listens on port $migrate_port\n";
            } else {
-               unlink $statefile;
-               # fixme: send resume - is that necessary ?
-               eval { vm_mon_cmd($vmid, "cont"); };
+               if ($migratedfrom) {
+                   unlink $statefile;
+                   # fixme: send resume - is that necessary ?
+                   eval { vm_mon_cmd($vmid, "cont"); };
+               }
            }
        }
 
@@ -3583,8 +3567,10 @@ my $snapshot_copy_config = sub {
        next if $k eq 'snapshots';
        next if $k eq 'snapstate';
        next if $k eq 'snaptime';
+       next if $k eq 'vmstate';
        next if $k eq 'lock';
        next if $k eq 'digest';
+       next if $k eq 'description';
        next if $k =~ m/^unused\d+$/;
                
        $dest->{$k} = $source->{$k};
@@ -3599,9 +3585,9 @@ my $snapshot_apply_config = sub {
        snapshots => $conf->{snapshots},
     };
 
-    # keep list of unused disks
+    # keep description and list of unused disks
     foreach my $k (keys %$conf) {
-       next if $k !~ m/^unused\d+$/;
+       next if !($k =~ m/^unused\d+$/ || $k eq 'description');
        $newconf->{$k} = $conf->{$k};
     }
 
@@ -3610,8 +3596,71 @@ my $snapshot_apply_config = sub {
     return $newconf;
 };
 
+sub foreach_writable_storage {
+    my ($conf, $func) = @_;
+
+    my $sidhash = {};
+
+    foreach my $ds (keys %$conf) {
+       next if !valid_drivename($ds);
+
+       my $drive = parse_drive($ds, $conf->{$ds});
+       next if !$drive;
+       next if drive_is_cdrom($drive);
+
+       my $volid = $drive->{file};
+
+       my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+       $sidhash->{$sid} = $sid if $sid;        
+    }
+
+    foreach my $sid (sort keys %$sidhash) {
+       &$func($sid);
+    }
+}
+
+my $alloc_vmstate_volid = sub {
+    my ($storecfg, $vmid, $conf, $snapname) = @_;
+    
+    # Note: we try to be smart when selecting a $target storage
+
+    my $target;
+
+    # search shared storage first
+    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
+    });
+
+    if (!$target) {
+       # now search local storage
+       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;
+       });
+    }
+
+    $target = 'local' if !$target;
+
+    my $driver_state_size = 32; # assume 32MB is enough to safe all driver state;
+    my $size = $conf->{memory} + $driver_state_size;
+
+    my $name = "vm-$vmid-state-$snapname";
+    my $scfg = PVE::Storage::storage_config($storecfg, $target);
+    $name .= ".raw" if $scfg->{path}; # add filename extension for file base storage
+    my $volid = PVE::Storage::vdisk_alloc($storecfg, $target, $vmid, 'raw', $name, $size*1024);
+
+    return $volid;
+};
+
 my $snapshot_prepare = sub {
-    my ($vmid, $snapname, $comment) = @_;
+    my ($vmid, $snapname, $save_vmstate, $comment) = @_;
 
     my $snap;
 
@@ -3628,7 +3677,7 @@ my $snapshot_prepare = sub {
 
        my $storecfg = PVE::Storage::config();
 
-       PVE::QemuServer::foreach_drive($conf, sub {
+       foreach_drive($conf, sub {
            my ($ds, $drive) = @_;
 
            return if drive_is_cdrom($drive);
@@ -3648,8 +3697,13 @@ my $snapshot_prepare = sub {
            }
        });
 
+
        $snap = $conf->{snapshots}->{$snapname} = {};
 
+       if ($save_vmstate && check_running($vmid)) {
+           $snap->{vmstate} = &$alloc_vmstate_volid($storecfg, $vmid, $conf, $snapname);
+       }
+
        &$snapshot_copy_config($conf, $snap);
 
        $snap->{snapstate} = "prepare";
@@ -3701,11 +3755,16 @@ sub snapshot_rollback {
 
     my $prepare = 1;
 
+    my $storecfg = PVE::Storage::config();
     my $updatefn = sub {
 
        my $conf = load_config($vmid);
 
-       check_lock($conf) if $prepare;
+       if ($prepare) {
+           check_lock($conf);
+           vm_stop($storecfg, $vmid, undef, undef, 5, undef, undef);
+       }
 
        die "unable to rollback vm $vmid: vm is running\n"
            if check_running($vmid);
@@ -3731,11 +3790,16 @@ sub snapshot_rollback {
        }
 
        update_config_nolock($vmid, $conf, 1);
+
+       if (!$prepare && $snap->{vmstate}) {
+           my $statefile = PVE::Storage::path($storecfg, $snap->{vmstate});
+           # fixme: this only forws for files currently
+           vm_start($storecfg, $vmid, $statefile);
+       }
+
     };
 
     lock_config($vmid, $updatefn);
-
-    my $storecfg = PVE::Storage::config();
     
     foreach_drive($snap, sub {
        my ($ds, $drive) = @_;
@@ -3753,22 +3817,31 @@ sub snapshot_rollback {
 }
 
 sub snapshot_create {
-    my ($vmid, $snapname, $vmstate, $freezefs, $comment) = @_;
+    my ($vmid, $snapname, $save_vmstate, $freezefs, $comment) = @_;
 
-    my $snap = &$snapshot_prepare($vmid, $snapname, $comment);
+    my $snap = &$snapshot_prepare($vmid, $snapname, $save_vmstate, $comment);
 
-    $freezefs = $vmstate = 0 if !check_running($vmid);
+    $freezefs = $save_vmstate = 0 if !$snap->{vmstate}; # vm is not running
 
     my $drivehash = {};
 
+    my $running = check_running($vmid);
+
     eval {
        # create internal snapshots of all drives
-       
-       qemu_snapshot_start($vmid, $snapname) if $vmstate;
-
-       qga_freezefs($vmid) if $freezefs;
 
        my $storecfg = PVE::Storage::config();
+
+       if ($running) {
+           if ($snap->{vmstate}) {
+               my $path = PVE::Storage::path($storecfg, $snap->{vmstate});     
+               vm_mon_cmd($vmid, "snapshot-start", statefile => $path);
+           } else {
+               vm_mon_cmd($vmid, "snapshot-start");
+           }
+       };
+
+       qga_freezefs($vmid) if $running && $freezefs;
  
        foreach_drive($snap, sub {
            my ($ds, $drive) = @_;
@@ -3784,10 +3857,10 @@ sub snapshot_create {
     };
     my $err = $@;
 
-    eval { gqa_unfreezefs($vmid) if $freezefs; };
+    eval { gqa_unfreezefs($vmid) if $running && $freezefs; };
     warn $@ if $@;
 
-    eval { qemu_snapshot_end($vmid) if $vmstate; };
+    eval { vm_mon_cmd($vmid, "snapshot-end") if $running; };
     warn $@ if $@;
 
     if ($err) {
@@ -3840,10 +3913,14 @@ sub snapshot_delete {
        }
 
        if ($remove_drive) {
-           my $drive = parse_drive($remove_drive, $snap->{$remove_drive});
-           my $volid = $drive->{file};
-           delete $snap->{$remove_drive};
-           add_unused_volume($conf, $volid);
+           if ($remove_drive eq 'vmstate') {
+               delete $snap->{$remove_drive};
+           } else {
+               my $drive = parse_drive($remove_drive, $snap->{$remove_drive});
+               my $volid = $drive->{file};
+               delete $snap->{$remove_drive};
+               add_unused_volume($conf, $volid);
+           }
        }
 
        if ($prepare) {
@@ -3861,11 +3938,22 @@ sub snapshot_delete {
 
     lock_config($vmid, $updatefn);
 
-    # now remove all internal snapshots
+    # now remove vmstate file
 
     my $storecfg = PVE::Storage::config();
 
-    PVE::QemuServer::foreach_drive($snap, sub {
+    if ($snap->{vmstate}) {
+       eval {  PVE::Storage::vdisk_free($storecfg, $snap->{vmstate}); };
+       if (my $err = $@) {
+           die $err if !$force;
+           warn $err;
+       }
+       # save changes (remove vmstate from snapshot)
+       lock_config($vmid, $updatefn, 'vmstate') if !$force;
+    };
+
+    # now remove all internal snapshots
+    foreach_drive($snap, sub {
        my ($ds, $drive) = @_;
 
        return if drive_is_cdrom($drive);