]> git.proxmox.com Git - pve-container.git/blob - src/PVE/VZDump/LXC.pm
use ./ prefix for all files inside tar archive
[pve-container.git] / src / PVE / VZDump / LXC.pm
1 package PVE::VZDump::LXC;
2
3 use strict;
4 use warnings;
5 use File::Path;
6 use File::Basename;
7 use PVE::INotify;
8 use PVE::Cluster qw(cfs_read_file);
9 use PVE::Storage;
10 use PVE::VZDump;
11 use PVE::LXC;
12
13 use base qw (PVE::VZDump::Plugin);
14
15 my $rsync_vm = sub {
16 my ($self, $task, $from, $to, $text) = @_;
17
18 $self->loginfo ("starting $text sync $from to $to");
19
20 my $starttime = time();
21
22 my $opts = $self->{vzdump}->{opts};
23
24 my $rsyncopts = "--stats -x -X --numeric-ids";
25
26 $rsyncopts .= " --bwlimit=$opts->{bwlimit}" if $opts->{bwlimit};
27
28 $self->cmd ("rsync $rsyncopts -aH --delete --no-whole-file --inplace '$from' '$to'");
29
30 my $delay = time () - $starttime;
31
32 $self->loginfo ("$text sync finished ($delay seconds)");
33 };
34
35 sub new {
36 my ($class, $vzdump) = @_;
37
38 PVE::VZDump::check_bin('lxc-stop');
39 PVE::VZDump::check_bin('lxc-start');
40 PVE::VZDump::check_bin('lxc-freeze');
41 PVE::VZDump::check_bin('lxc-unfreeze');
42
43 my $self = bless {};
44
45 $self->{vzdump} = $vzdump;
46 $self->{storecfg} = PVE::Storage::config();
47
48 $self->{vmlist} = PVE::LXC::config_list();
49
50 return $self;
51 }
52
53 sub type {
54 return 'lxc';
55 }
56
57 sub vm_status {
58 my ($self, $vmid) = @_;
59
60 my $running = PVE::LXC::check_running($vmid) ? 1 : 0;
61
62 return wantarray ? ($running, $running ? 'running' : 'stopped') : $running;
63 }
64
65 my $loop_mount_image = sub {
66 my ($image_path, $mountpoint) = @_;
67
68 my $loopdev;
69 my $mounted;
70 eval {
71 my $parser = sub {
72 my $line = shift;
73 $loopdev = $line if $line =~m|^/dev/loop\d+$|;
74 };
75 PVE::Tools::run_command(['losetup', '--find', '--show', $image_path], outfunc => $parser);
76
77 File::Path::mkpath($mountpoint);
78 PVE::Tools::run_command(['mount', '-t', 'ext4', $loopdev, $mountpoint]);
79 $mounted = 1;
80 };
81 if (my $err = $@) {
82 if ($mounted) {
83 eval { PVE::Tools::run_command(['umount', '-d', $mountpoint]) };
84 warn $@ if $@;
85 } else {
86 eval { PVE::Tools::run_command(['losetup', '-d', $loopdev]) if $loopdev; };
87 warn $@ if $@;
88 }
89 die $err;
90 }
91 };
92
93 sub prepare {
94 my ($self, $task, $vmid, $mode) = @_;
95
96 my $conf = $self->{vmlist}->{$vmid} = PVE::LXC::load_config($vmid);
97
98 my $running = PVE::LXC::check_running($vmid);
99
100 my $diskinfo = {};
101 $task->{diskinfo} = $diskinfo;
102
103 $task->{hostname} = $conf->{'lxc.utsname'} || "CT$vmid";
104
105 my $volid = $conf->{'pve.volid'};
106
107 # fixme: whe do we deactivate ??
108 PVE::Storage::activate_volumes($self->{storecfg}, [$volid]) if $volid;
109
110 my $rootfs = $conf->{'lxc.rootfs'};
111
112 if ($mode eq 'snapshot') {
113
114 die "mode failure - storage does not support snapshots (no volid)\n"
115 if !$volid;
116
117 die "mode failure - storage does not support snapshots\n"
118 if !PVE::Storage::volume_has_feature($self->{storecfg}, 'snapshot', $volid);
119
120 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
121
122 my $scfg = PVE::Storage::storage_config($self->{storecfg}, $sid);
123
124 # we only handle well known types for now, because the storage
125 # library dos not handle mount/unmount of snapshots
126
127 if ($scfg->{type} ne 'zfs') {
128 $diskinfo->{mountpoint} = "/mnt/vzsnap0";
129 } else {
130 die "mode failure - storage does not support snapshot mount\n"
131 }
132
133 PVE::Storage::volume_snapshot($self->{storecfg}, $volid, '__vzdump__');
134 $task->{cleanup}->{snap_volid} = $volid;
135
136 # $diskinfo->{dir} = $rootfs;
137 die "implement me";
138
139 } else {
140
141 if ($rootfs =~ m!^/! && -d $rootfs) {
142 $diskinfo->{dir} = $rootfs;
143 } else {
144 if ($mode eq 'stop') {
145 my $mountpoint = "/mnt/vzsnap0";
146 my $path = PVE::Storage::path($self->{storecfg}, $volid);
147 &$loop_mount_image($path, $mountpoint);
148 $task->{cleanup}->{snapshot_mount} = 1;
149 $diskinfo->{dir} = $diskinfo->{mountpoint} = $mountpoint;
150 } elsif ($mode eq 'suspend') {
151 my $tasks_fn = "/sys/fs/cgroup/cpu/lxc/$vmid/tasks";
152 my $init_pid = PVE::Tools::file_read_firstline($tasks_fn);
153 if ($init_pid =~ m/^(\d+)$/) {
154 $diskinfo->{dir} = "/proc/$1/root";
155 } else {
156 die "unable to find container init task\n";
157 }
158 } else {
159 die "unknown mode '$mode'\n"; # should not happen
160 }
161 }
162
163
164 if ($mode eq 'suspend') {
165 $task->{snapdir} = $task->{tmpdir};
166 } else {
167 $task->{snapdir} = $diskinfo->{dir};
168 }
169 }
170
171 }
172
173 sub lock_vm {
174 my ($self, $vmid) = @_;
175
176 PVE::LXC::lock_aquire($vmid);
177 }
178
179 sub unlock_vm {
180 my ($self, $vmid) = @_;
181
182 PVE::LXC::lock_release($vmid);
183 }
184
185 sub copy_data_phase1 {
186 my ($self, $task) = @_;
187
188 $self->$rsync_vm($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "first");
189 }
190
191 sub copy_data_phase2 {
192 my ($self, $task) = @_;
193
194 $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "final");
195 }
196
197 sub stop_vm {
198 my ($self, $task, $vmid) = @_;
199
200 $self->cmd("lxc-stop -n $vmid");
201 }
202
203 sub start_vm {
204 my ($self, $task, $vmid) = @_;
205
206 $self->cmd ("lxc-start -n $vmid");
207 }
208
209 sub suspend_vm {
210 my ($self, $task, $vmid) = @_;
211
212 $self->cmd ("lxc-freeze -n $vmid");
213 }
214
215 sub resume_vm {
216 my ($self, $task, $vmid) = @_;
217
218 $self->cmd ("lxc-unfreeze -n $vmid");
219 }
220
221 sub assemble {
222 my ($self, $task, $vmid) = @_;
223
224 my $conffile = PVE::LXC::config_file($vmid);
225
226 my $dir = $task->{snapdir};
227
228 $task->{cleanup}->{etc_vzdump} = 1;
229
230 mkpath "$dir/etc/vzdump/";
231 $self->cmd ("cp '$conffile' '$dir/etc/vzdump/lxc.conf'");
232 }
233
234 sub archive {
235 my ($self, $task, $vmid, $filename, $comp) = @_;
236
237 my $findexcl = $self->{vzdump}->{findexcl};
238 my $findargs = join (' ', @$findexcl) . ' -print0';
239 my $opts = $self->{vzdump}->{opts};
240
241 my $srcdir = $task->{diskinfo}->{dir};
242 my $snapdir = $task->{snapdir};
243
244 my $taropts = "--totals --sparse --numeric-owner --no-recursion --xattrs --one-file-system";
245
246 # note: --remove-files does not work because we do not
247 # backup all files (filters). tar complains:
248 # Cannot rmdir: Directory not empty
249 # we we disable this optimization for now
250 #if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) {
251 # $taropts .= " --remove-files"; # try to save space
252 #}
253
254 my $cmd = "(";
255
256 $cmd .= "cd $snapdir;find . $findargs|sed 's/\\\\/\\\\\\\\/g'|";
257 $cmd .= "tar cpf - $taropts ./etc/vzdump/lxc.conf --null -T -";
258 my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
259 $cmd .= "|cstream -t $bwl" if $opts->{bwlimit};
260 $cmd .= "|$comp" if $comp;
261
262 $cmd .= ")";
263
264 if ($opts->{stdout}) {
265 $self->cmd ($cmd, output => ">&" . fileno($opts->{stdout}));
266 } else {
267 $self->cmd ("$cmd >$filename");
268 }
269 }
270
271 sub cleanup {
272 my ($self, $task, $vmid) = @_;
273
274 my $di = $task->{diskinfo};
275
276 if ($task->{cleanup}->{snapshot_mount}) {
277 # Note: sleep to avoid 'device is busy' message.
278 # Seems Kernel need some time to cleanup open file list,
279 # for example when we stop the tar with kill (stop task)
280 # We use -d to automatically free used loop devices
281 sleep(1);
282 $self->cmd_noerr("umount -d $di->{mountpoint}");
283 }
284
285 if (my $volid = $task->{cleanup}->{snap_volid}) {
286 eval { PVE::Storage::volume_snapshot_delete($self->{storecfg}, $volid, '__vzdump__'); };
287 warn $@ if $@;
288 }
289
290 if ($task->{cleanup}->{etc_vzdump}) {
291 my $dir = "$task->{snapdir}/etc/vzdump";
292 eval { rmtree $dir if -d $dir; };
293 $self->logerr ($@) if $@;
294 }
295
296 }
297
298 1;