}
sub is_volume_in_use {
- my ($config, $volid) = @_;
+ my ($config, $volid, $include_snapshots) = @_;
my $used = 0;
foreach_mountpoint($config, sub {
}
});
+ my $snapshots = $config->{snapshots};
+ if ($include_snapshots && $snapshots) {
+ foreach my $snap (keys %$snapshots) {
+ $used ||= is_volume_in_use($snapshots->{$snap}, $volid);
+ }
+ }
+
return $used;
}
my $storage_cfg = PVE::Storage::config();
foreach my $volume (@deleted_volumes) {
next if $used_volids->{$volume}; # could have been re-added, too
+ # also check for references in snapshots
+ next if is_volume_in_use($conf, $volume, 1);
delete_mountpoint_volume($storage_cfg, $vmid, $volume);
}
}
return $newconf;
};
-my $snapshot_save_vmstate = sub {
+sub snapshot_save_vmstate {
die "implement me - snapshot_save_vmstate\n";
-};
+}
sub snapshot_prepare {
my ($vmid, $snapname, $save_vmstate, $comment) = @_;
if defined($conf->{snapshots}->{$snapname});
my $storecfg = PVE::Storage::config();
-
- # workaround until mp snapshots are implemented
- my $feature = $snapname eq 'vzdump' ? 'vzdump' : 'snapshot';
- die "snapshot feature is not available\n" if !has_feature($feature, $conf, $storecfg);
+ die "snapshot feature is not available\n"
+ if !has_feature('snapshot', $conf, $storecfg, undef, undef, $snapname eq 'vzdump');
$snap = $conf->{snapshots}->{$snapname} = {};
if ($save_vmstate && check_running($vmid)) {
- &$snapshot_save_vmstate($vmid, $conf, $snapname, $storecfg);
+ snapshot_save_vmstate($vmid, $conf, $snapname, $storecfg);
}
&$snapshot_copy_config($conf, $snap);
}
sub has_feature {
- my ($feature, $conf, $storecfg, $snapname) = @_;
+ my ($feature, $conf, $storecfg, $snapname, $running, $backup_only) = @_;
my $err;
- my $vzdump = $feature eq 'vzdump';
- $feature = 'snapshot' if $vzdump;
foreach_mountpoint($conf, sub {
my ($ms, $mountpoint) = @_;
return if $err; # skip further test
- return if $vzdump && $ms ne 'rootfs' && !$mountpoint->{backup};
+ return if $backup_only && $ms ne 'rootfs' && !$mountpoint->{backup};
- $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $mountpoint->{volume}, $snapname);
-
- # TODO: implement support for mountpoints
- die "unable to handle mountpoint '$ms' - feature not implemented\n"
- if $ms ne 'rootfs';
+ $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $mountpoint->{volume}, $snapname, $running);
});
return $err ? 0 : 1;
die "failed to sync container namespace\n" if $? != 0;
}
+sub check_freeze_needed {
+ my ($vmid, $config, $save_vmstate) = @_;
+
+ my $ret = check_running($vmid);
+ return ($ret, $ret);
+}
+
sub snapshot_create {
my ($vmid, $snapname, $save_vmstate, $comment) = @_;
my $snap = snapshot_prepare($vmid, $snapname, $save_vmstate, $comment);
+ $save_vmstate = 0 if !$snap->{vmstate};
+
my $conf = load_config($vmid);
- my $running = check_running($vmid);
-
- my $unfreeze = 0;
+ my ($running, $freezefs) = check_freeze_needed($vmid, $conf, $snap->{vmstate});
my $drivehash = {};
eval {
- if ($running) {
- $unfreeze = 1;
+ if ($freezefs) {
PVE::Tools::run_command(['/usr/bin/lxc-freeze', '-n', $vmid]);
sync_container_namespace($vmid);
- };
+ }
my $storecfg = PVE::Storage::config();
- my $rootinfo = parse_ct_rootfs($conf->{rootfs});
- my $volid = $rootinfo->{volume};
+ foreach_mountpoint($conf, sub {
+ my ($ms, $mountpoint) = @_;
- PVE::Storage::volume_snapshot($storecfg, $volid, $snapname);
- $drivehash->{rootfs} = 1;
+ return if $snapname eq 'vzdump' && $ms ne 'rootfs' && !$mountpoint->{backup};
+ PVE::Storage::volume_snapshot($storecfg, $mountpoint->{volume}, $snapname);
+ $drivehash->{$ms} = 1;
+ });
};
my $err = $@;
- if ($unfreeze) {
- eval { PVE::Tools::run_command(['/usr/bin/lxc-unfreeze', '-n', $vmid]); };
- warn $@ if $@;
+ if ($running) {
+ if ($freezefs) {
+ eval { PVE::Tools::run_command(['/usr/bin/lxc-unfreeze', '-n', $vmid]); };
+ warn $@ if $@;
+ }
}
if ($err) {
+ warn "snapshot create failed: starting cleanup\n";
eval { snapshot_delete($vmid, $snapname, 1, $drivehash); };
- warn "$@\n" if $@;
+ warn "$@" if $@;
die "$err\n";
}
my $prepare = 1;
my $snap;
+ my $unused = [];
my $unlink_parent = sub {
my ($confref, $new_parent) = @_;
if ($remove_drive eq 'vmstate') {
die "implement me - saving vmstate\n";
} else {
- die "implement me - remove drive\n";
+ my $value = $snap->{$remove_drive};
+ my $mountpoint = $remove_drive eq 'rootfs' ? parse_ct_rootfs($value, 1) : parse_ct_mountpoint($value, 1);
+ delete $snap->{$remove_drive};
+ add_unused_volume($snap, $mountpoint->{volume})
+ if (!is_volume_in_use($snap, $mountpoint->{volume}));
}
}
} else {
delete $conf->{snapshots}->{$snapname};
delete $conf->{lock} if $drivehash;
+ foreach my $volid (@$unused) {
+ add_unused_volume($conf, $volid)
+ if (!is_volume_in_use($conf, $volid));
+ }
}
write_config($vmid, $conf);
};
# now remove all volume snapshots
- # only rootfs for now!
- eval {
- my $rootfs = $snap->{rootfs};
- my $rootinfo = parse_ct_rootfs($rootfs);
- my $volid = $rootinfo->{volume};
- PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snapname);
- };
- if (my $err = $@) {
- die $err if !$force;
- warn $err;
- }
+ foreach_mountpoint($snap, sub {
+ my ($ms, $mountpoint) = @_;
+
+ return if $snapname eq 'vzdump' && $ms ne 'rootfs' && !$mountpoint->{backup};
+ if (!$drivehash || $drivehash->{$ms}) {
+ eval { PVE::Storage::volume_snapshot_delete($storecfg, $mountpoint->{volume}, $snapname); };
+ if (my $err = $@) {
+ die $err if !$force;
+ warn $err;
+ }
+ }
+
+ # save changes (remove mp from snapshot)
+ lock_config($vmid, $updatefn, $ms) if !$force;
+ push @$unused, $mountpoint->{volume};
+ });
# now cleanup config
$prepare = 0;
my $snap = &$get_snapshot_config();
- # only for rootfs for now!
- my $rootfs = $snap->{rootfs};
- my $rootinfo = parse_ct_rootfs($rootfs);
- my $volid = $rootinfo->{volume};
+ foreach_mountpoint($snap, sub {
+ my ($ms, $mountpoint) = @_;
- PVE::Storage::volume_rollback_is_possible($storecfg, $volid, $snapname);
+ PVE::Storage::volume_rollback_is_possible($storecfg, $mountpoint->{volume}, $snapname);
+ });
my $updatefn = sub {
lock_config($vmid, $updatefn);
- # only rootfs for now!
- PVE::Storage::volume_snapshot_rollback($storecfg, $volid, $snapname);
+ foreach_mountpoint($snap, sub {
+ my ($ms, $mountpoint) = @_;
+
+ PVE::Storage::volume_snapshot_rollback($storecfg, $mountpoint->{volume}, $snapname);
+ });
$prepare = 0;
lock_config($vmid, $updatefn);