]> git.proxmox.com Git - pve-container.git/blame - src/PVE/VZDump/LXC.pm
cleanup
[pve-container.git] / src / PVE / VZDump / LXC.pm
CommitLineData
d14a9a1b
DM
1package PVE::VZDump::LXC;
2
3use strict;
4use warnings;
5use File::Path;
6use File::Basename;
7use PVE::INotify;
8use PVE::Cluster qw(cfs_read_file);
9use PVE::Storage;
10use PVE::VZDump;
11use PVE::LXC;
514b5f82 12use PVE::Tools;
d14a9a1b
DM
13
14use base qw (PVE::VZDump::Plugin);
15
4ca61ce8
DM
16my $default_mount_point = "/mnt/vzsnap0";
17
d14a9a1b
DM
18my $rsync_vm = sub {
19 my ($self, $task, $from, $to, $text) = @_;
20
21 $self->loginfo ("starting $text sync $from to $to");
22
23 my $starttime = time();
24
25 my $opts = $self->{vzdump}->{opts};
26
27 my $rsyncopts = "--stats -x -X --numeric-ids";
28
29 $rsyncopts .= " --bwlimit=$opts->{bwlimit}" if $opts->{bwlimit};
30
31 $self->cmd ("rsync $rsyncopts -aH --delete --no-whole-file --inplace '$from' '$to'");
32
33 my $delay = time () - $starttime;
34
35 $self->loginfo ("$text sync finished ($delay seconds)");
36};
37
38sub new {
39 my ($class, $vzdump) = @_;
40
41 PVE::VZDump::check_bin('lxc-stop');
42 PVE::VZDump::check_bin('lxc-start');
43 PVE::VZDump::check_bin('lxc-freeze');
44 PVE::VZDump::check_bin('lxc-unfreeze');
45
46 my $self = bless {};
47
48 $self->{vzdump} = $vzdump;
49 $self->{storecfg} = PVE::Storage::config();
50
51 $self->{vmlist} = PVE::LXC::config_list();
52
53 return $self;
54}
55
56sub type {
57 return 'lxc';
58}
59
60sub vm_status {
61 my ($self, $vmid) = @_;
62
63 my $running = PVE::LXC::check_running($vmid) ? 1 : 0;
64
65 return wantarray ? ($running, $running ? 'running' : 'stopped') : $running;
66}
67
4ca61ce8 68my $check_mountpoint_empty = sub {
b739f640
DM
69 my ($mountpoint) = @_;
70
4ca61ce8
DM
71 die "mountpoint '$mountpoint' is not a directory\n" if ! -d $mountpoint;
72
b739f640
DM
73 PVE::Tools::dir_glob_foreach($mountpoint, qr/.*/, sub {
74 my $entry = shift;
75 return if $entry eq '.' || $entry eq '..';
4ca61ce8 76 die "mountpoint '$mountpoint' not empty\n";
b739f640 77 });
d14a9a1b
DM
78};
79
80sub prepare {
81 my ($self, $task, $vmid, $mode) = @_;
82
83 my $conf = $self->{vmlist}->{$vmid} = PVE::LXC::load_config($vmid);
57ed5ed0 84 my $storage_cfg = $self->{storecfg};
d14a9a1b 85
14731ec6
DM
86 PVE::LXC::foreach_mountpoint($conf, sub {
87 my ($ms, $mountpoint) = @_;
88
89 return if $ms eq 'rootfs';
90 # TODO: implement support for mountpoints
91 die "unable to backup mountpoint '$ms' - feature not implemented\n";
92 });
93
d14a9a1b
DM
94 my $running = PVE::LXC::check_running($vmid);
95
4ca61ce8 96 my $diskinfo = $task->{diskinfo} = {};
d14a9a1b 97
27916659 98 $task->{hostname} = $conf->{'hostname'} || "CT$vmid";
d14a9a1b 99
27916659 100 my $rootinfo = PVE::LXC::parse_ct_mountpoint($conf->{rootfs});
4ca61ce8 101 $diskinfo->{volid} = $rootinfo->{volume};
d14a9a1b 102
4ca61ce8 103 die "missing root volid (no volid)\n" if !$diskinfo->{volid};
c135fbf1 104
27916659 105 # fixme: when do we deactivate ??
57ed5ed0 106 PVE::Storage::activate_volumes($storage_cfg, [$diskinfo->{volid}]);
d14a9a1b 107
d14a9a1b 108 if ($mode eq 'snapshot') {
57ed5ed0 109 if (!PVE::LXC::has_feature('snapshot', $conf, $storage_cfg)) {
4ca61ce8
DM
110 die "mode failure - some volumes does not support snapshots\n";
111 }
d14a9a1b 112
4ca61ce8
DM
113 if ($conf->{snapshots} && $conf->{snapshots}->{vzdump}) {
114 $self->loginfo("found old vzdump snapshot (force removal)");
115 PVE::LXC::snapshot_delete($vmid, 'vzdump', 0);
116 }
d14a9a1b 117
22a91261
WB
118 my $rootdir = $default_mount_point;
119 mkpath $rootdir;
120 &$check_mountpoint_empty($rootdir);
d14a9a1b 121
4ca61ce8
DM
122 # set snapshot_count (freezes CT it snapshot_count > 1)
123 my $volid_list = PVE::LXC::get_vm_volumes($conf);
124 $task->{snapshot_count} = scalar(@$volid_list);
f5313774 125 } elsif ($mode eq 'stop') {
22a91261
WB
126 my $rootdir = $default_mount_point;
127 mkpath $rootdir;
128 &$check_mountpoint_empty($rootdir);
f5313774 129 } elsif ($mode eq 'suspend') {
632eca5a
DM
130 my $pid = PVE::LXC::find_lxc_pid($vmid);
131 $diskinfo->{dir} = "/proc/$pid/root";
f5313774
DM
132 $task->{snapdir} = $task->{tmpdir};
133 } else {
134 die "unknown mode '$mode'\n"; # should not happen
d14a9a1b 135 }
d14a9a1b
DM
136}
137
138sub lock_vm {
139 my ($self, $vmid) = @_;
140
141 PVE::LXC::lock_aquire($vmid);
142}
143
144sub unlock_vm {
145 my ($self, $vmid) = @_;
146
147 PVE::LXC::lock_release($vmid);
148}
149
4ca61ce8
DM
150sub snapshot {
151 my ($self, $task, $vmid) = @_;
152
153 my $diskinfo = $task->{diskinfo};
154
155 $self->loginfo("create storage snapshot snapshot");
156
157 # todo: freeze/unfreeze if we have more than one volid
158 PVE::LXC::snapshot_create($vmid, 'vzdump', "vzdump backup snapshot");
159 $task->{cleanup}->{remove_snapshot} = 1;
160
161 # reload config
162 my $conf = $self->{vmlist}->{$vmid} = PVE::LXC::load_config($vmid);
163 die "unable to read vzdump shanpshot config - internal error"
164 if !($conf->{snapshots} && $conf->{snapshots}->{vzdump});
165
166 # my $snapconf = $conf->{snapshots}->{vzdump};
167 # my $volid_list = PVE::LXC::get_vm_volumes($snapconf);
168 my $volid_list = [$diskinfo->{volid}];
169
22a91261 170 my $rootdir = $default_mount_point;
4ca61ce8
DM
171
172 my $mp = { volume => $diskinfo->{volid}, mp => "/" };
22a91261 173 PVE::LXC::mountpoint_mount($mp, $rootdir, $self->{storecfg}, 'vzdump');
4ca61ce8 174
22a91261 175 $diskinfo->{dir} = $diskinfo->{mountpoint} = $rootdir;
4ca61ce8
DM
176 $task->{snapdir} = $diskinfo->{dir};
177}
178
d14a9a1b
DM
179sub copy_data_phase1 {
180 my ($self, $task) = @_;
181
182 $self->$rsync_vm($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "first");
183}
184
185sub copy_data_phase2 {
186 my ($self, $task) = @_;
187
188 $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "final");
189}
190
191sub stop_vm {
192 my ($self, $task, $vmid) = @_;
193
194 $self->cmd("lxc-stop -n $vmid");
195}
196
197sub start_vm {
198 my ($self, $task, $vmid) = @_;
199
200 $self->cmd ("lxc-start -n $vmid");
201}
202
203sub suspend_vm {
204 my ($self, $task, $vmid) = @_;
205
206 $self->cmd ("lxc-freeze -n $vmid");
207}
208
209sub resume_vm {
210 my ($self, $task, $vmid) = @_;
211
212 $self->cmd ("lxc-unfreeze -n $vmid");
213}
214
215sub assemble {
216 my ($self, $task, $vmid) = @_;
217
cbd6753d 218 my $tmpdir = $task->{tmpdir};
d14a9a1b 219
cbd6753d 220 mkpath "$tmpdir/etc/vzdump/";
514b5f82
WL
221
222 my $conf = PVE::LXC::load_config($vmid);
223 delete $conf->{snapshots};
224 delete $conf->{'pve.parent'};
225
cbd6753d 226 PVE::Tools::file_set_contents("$tmpdir/etc/vzdump/pct.conf", PVE::LXC::write_pct_config("/lxc/$vmid.conf", $conf));
d14a9a1b
DM
227}
228
229sub archive {
230 my ($self, $task, $vmid, $filename, $comp) = @_;
cbd6753d 231
459fd4d2 232 if ($task->{mode} eq 'stop') {
22a91261 233 my $rootdir = $default_mount_point;
459fd4d2
DM
234 my $diskinfo = $task->{diskinfo};
235
236 my $volid_list = [$diskinfo->{volid}];
237 my $mp = { volume => $diskinfo->{volid}, mp => "/" };
238
22a91261
WB
239 $self->loginfo("mounting container root at '$rootdir'");
240 PVE::LXC::mountpoint_mount($mp, $rootdir, $self->{storecfg});
459fd4d2 241
22a91261 242 $diskinfo->{dir} = $diskinfo->{mountpoint} = $rootdir;
459fd4d2
DM
243 $task->{snapdir} = $diskinfo->{dir};
244 }
245
d14a9a1b 246 my $findexcl = $self->{vzdump}->{findexcl};
cbd6753d
DM
247 push @$findexcl, "'('", '-path', "./etc/vzdump", "-prune", "')'", '-o';
248
d14a9a1b
DM
249 my $findargs = join (' ', @$findexcl) . ' -print0';
250 my $opts = $self->{vzdump}->{opts};
251
252 my $srcdir = $task->{diskinfo}->{dir};
253 my $snapdir = $task->{snapdir};
cbd6753d 254 my $tmpdir = $task->{tmpdir};
d14a9a1b
DM
255
256 my $taropts = "--totals --sparse --numeric-owner --no-recursion --xattrs --one-file-system";
257
258 # note: --remove-files does not work because we do not
259 # backup all files (filters). tar complains:
260 # Cannot rmdir: Directory not empty
261 # we we disable this optimization for now
262 #if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) {
263 # $taropts .= " --remove-files"; # try to save space
264 #}
265
266 my $cmd = "(";
267
268 $cmd .= "cd $snapdir;find . $findargs|sed 's/\\\\/\\\\\\\\/g'|";
cbd6753d
DM
269 $cmd .= "tar cpf - $taropts ";
270 # The directory parameter can give a alternative directory as source.
271 # the second parameter gives the structure in the tar.
272 $cmd .= "--directory=$tmpdir ./etc/vzdump/pct.conf ";
273 $cmd .= "--directory=$snapdir --null -T -";
274
d14a9a1b
DM
275 my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
276 $cmd .= "|cstream -t $bwl" if $opts->{bwlimit};
277 $cmd .= "|$comp" if $comp;
278
279 $cmd .= ")";
280
281 if ($opts->{stdout}) {
148d1cb4 282 $self->cmd ($cmd, output => ">&" . fileno($opts->{stdout}));
d14a9a1b
DM
283 } else {
284 $self->cmd ("$cmd >$filename");
285 }
286}
287
288sub cleanup {
289 my ($self, $task, $vmid) = @_;
290
4ca61ce8 291 my $diskinfo = $task->{diskinfo};
d14a9a1b 292
22a91261
WB
293 if (my $rootdir = $diskinfo->{mountpoint}) {
294 PVE::Tools::run_command(['umount', '-l', '-d', $rootdir]);
b739f640
DM
295 };
296
4ca61ce8
DM
297 if ($task->{cleanup}->{remove_snapshot}) {
298 $self->loginfo("remove vzdump snapshot");
299 PVE::LXC::snapshot_delete($vmid, 'vzdump', 0);
d14a9a1b 300 }
d14a9a1b
DM
301}
302
3031;