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