}
sub __config_max_unused_disks {
- my ($class) =@_;
+ my ($class) = @_;
return $MAX_UNUSED_DISKS;
}
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);
});
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
my ($class, $vmid, $config, $save_vmstate) = @_;
my $running = $class->__snapshot_check_running($vmid);
- if ($save_vmstate) {
+ if (!$save_vmstate) {
return ($running, $running && $config->{agent} && PVE::QemuServer::qga_check_running($vmid));
} else {
return ($running, 0);
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");
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