]> git.proxmox.com Git - pve-manager.git/blame - PVE/VZDump/OpenVZ.pm
vzdump: remove temporary directories
[pve-manager.git] / PVE / VZDump / OpenVZ.pm
CommitLineData
aaeeeebe
DM
1package PVE::VZDump::OpenVZ;
2
aaeeeebe
DM
3use strict;
4use warnings;
5use File::Path;
6use File::Basename;
4a4051d8 7use PVE::INotify;
aaeeeebe 8use PVE::VZDump;
4a4051d8 9use PVE::OpenVZ;
aaeeeebe
DM
10
11use base qw (PVE::VZDump::Plugin);
12
aaeeeebe
DM
13my $load_vz_conf = sub {
14 my ($self, $vmid) = @_;
15
4a4051d8 16 my $conf = PVE::OpenVZ::load_config($vmid);
aaeeeebe 17
4a4051d8
DM
18 my $dir = $self->{privatedir};
19 if ($conf->{ve_private} && $conf->{ve_private}->{value}) {
20 $dir = $conf->{ve_private}->{value};
aaeeeebe
DM
21 }
22 $dir =~ s/\$VEID/$vmid/;
23 $self->{vmlist}->{$vmid}->{dir} = $dir;
24
4a4051d8
DM
25 my $hostname = "CT $vmid";
26 if ($conf->{hostname} && $conf->{hostname}->{value}) {
27 $hostname = $conf->{hostname}->{value};
aaeeeebe 28 }
4a4051d8 29 $self->{vmlist}->{$vmid}->{hostname} = $hostname;
aaeeeebe
DM
30};
31
aaeeeebe
DM
32my $rsync_vm = sub {
33 my ($self, $task, $from, $to, $text) = @_;
34
35 $self->loginfo ("starting $text sync $from to $to");
36
37 my $starttime = time();
38
39 my $opts = $self->{vzdump}->{opts};
40
41 my $rsyncopts = "--stats -x --numeric-ids";
42
43 $rsyncopts .= " --bwlimit=$opts->{bwlimit}" if $opts->{bwlimit};
44
45 $self->cmd ("rsync $rsyncopts -aH --delete --no-whole-file --inplace '$from' '$to'");
46
47 my $delay = time () - $starttime;
48
49 $self->loginfo ("$text sync finished ($delay seconds)");
50};
51
52sub new {
53 my ($class, $vzdump) = @_;
54
55 PVE::VZDump::check_bin ('vzctl');
56
4a4051d8 57 my $self = bless PVE::OpenVZ::read_global_vz_config ();
aaeeeebe
DM
58
59 $self->{vzdump} = $vzdump;
60
4a4051d8 61 $self->{vmlist} = PVE::OpenVZ::config_list();
aaeeeebe
DM
62
63 return $self;
64};
65
66sub type {
67 return 'openvz';
68}
69
70sub vm_status {
71 my ($self, $vmid) = @_;
72
7f910306
DM
73 my $status_text = '';
74 $self->cmd ("vzctl status $vmid", outfunc => sub {$status_text .= shift; });
aaeeeebe
DM
75 chomp $status_text;
76
77 my $running = $status_text =~ m/running/ ? 1 : 0;
78
4a4051d8 79 return wantarray ? ($running, $running ? 'running' : 'stopped') : $running;
aaeeeebe
DM
80}
81
82sub prepare {
83 my ($self, $task, $vmid, $mode) = @_;
84
85 $self->$load_vz_conf ($vmid);
86
87 my $dir = $self->{vmlist}->{$vmid}->{dir};
88
89 my $diskinfo = { dir => $dir };
90
91 $task->{hostname} = $self->{vmlist}->{$vmid}->{hostname};
92
93 $task->{diskinfo} = $diskinfo;
94
4a4051d8 95 my $hostname = PVE::INotify::nodename();
aaeeeebe
DM
96
97 if ($mode eq 'snapshot') {
98
99 my $lvmmap = PVE::VZDump::get_lvm_mapping();
100 my ($srcdev, $lvmpath, $lvmvg, $lvmlv, $fstype) =
101 PVE::VZDump::get_lvm_device ($dir, $lvmmap);
102
103 my $targetdev = PVE::VZDump::get_lvm_device ($task->{dumpdir}, $lvmmap);
104
105 die ("mode failure - unable to detect lvm volume group\n") if !$lvmvg;
106 die ("mode failure - wrong lvm mount point '$lvmpath'\n") if $dir !~ m|/?$lvmpath/?|;
107 die ("mode failure - unable to dump into snapshot (use option --dumpdir)\n")
108 if $targetdev eq $srcdev;
109
110 $diskinfo->{snapname} = "vzsnap-$hostname-0";
111 $diskinfo->{snapdev} = "/dev/$lvmvg/$diskinfo->{snapname}";
112 $diskinfo->{srcdev} = $srcdev;
113 $diskinfo->{lvmvg} = $lvmvg;
114 $diskinfo->{lvmlv} = $lvmlv;
115 $diskinfo->{fstype} = $fstype;
116 $diskinfo->{lvmpath} = $lvmpath;
117 $diskinfo->{mountpoint} = "/mnt/vzsnap0";
118
119 $task->{snapdir} = $dir;
120 $task->{snapdir} =~ s|/?$lvmpath/?|$diskinfo->{mountpoint}/|;
121
122 } elsif ($mode eq 'suspend') {
123 $task->{snapdir} = $task->{tmpdir};
124 } else {
125 $task->{snapdir} = $dir;
126 }
127}
128
129sub lock_vm {
130 my ($self, $vmid) = @_;
131
132 my $filename = "$self->{lockdir}/103.lck";
133
4a4051d8 134 my $lockmgr = PVE::OpenVZ::create_lock_manager();
aaeeeebe
DM
135
136 $self->{lock} = $lockmgr->lock($filename) || die "can't lock VM $vmid\n";
137}
138
139sub unlock_vm {
140 my ($self, $vmid) = @_;
141
142 $self->{lock}->release();
143}
144
145sub copy_data_phase1 {
146 my ($self, $task) = @_;
147
148 $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "first");
149}
150
151# we use --skiplock for vzctl because we have already locked the VM
152# by calling lock_vm()
153
154sub stop_vm {
155 my ($self, $task, $vmid) = @_;
156
157 $self->cmd ("vzctl --skiplock stop $vmid");
158}
159
160sub start_vm {
161 my ($self, $task, $vmid) = @_;
162
163 $self->cmd ("vzctl --skiplock start $vmid");
164}
165
166sub suspend_vm {
167 my ($self, $task, $vmid) = @_;
168
169 $self->cmd ("vzctl --skiplock chkpnt $vmid --suspend");
170}
171
172sub snapshot {
173 my ($self, $task) = @_;
174
175 my $opts = $self->{vzdump}->{opts};
176
177 my $di = $task->{diskinfo};
178
179 mkpath $di->{mountpoint}; # create mount point for lvm snapshot
180
181 if (-b $di->{snapdev}) {
182 $self->loginfo ("trying to remove stale snapshot '$di->{snapdev}'");
183
184 $self->cmd_noerr ("umount $di->{mountpoint}");
185
186 $self->cmd_noerr ("lvremove -f $di->{snapdev}");
187 }
188
189 $self->loginfo ("creating lvm snapshot of $di->{srcdev} ('$di->{snapdev}')");
190
191 $task->{cleanup}->{lvm_snapshot} = 1;
192
193 $self->cmd ("lvcreate --size $opts->{size}M --snapshot" .
194 " --name $di->{snapname} /dev/$di->{lvmvg}/$di->{lvmlv}");
195
196 my $mopts = $di->{fstype} eq 'xfs' ? "-o nouuid" : '';
197
198 $task->{cleanup}->{snapshot_mount} = 1;
199
425bbb26 200 $self->cmd ("mount -n -t $di->{fstype} $mopts $di->{snapdev} $di->{mountpoint}");
aaeeeebe
DM
201}
202
203sub copy_data_phase2 {
204 my ($self, $task) = @_;
205
206 $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "final");
207}
208
209sub resume_vm {
210 my ($self, $task, $vmid) = @_;
211
212 $self->cmd ("vzctl --skiplock chkpnt $vmid --resume");
213}
214
215sub assemble {
216 my ($self, $task, $vmid) = @_;
217
4a4051d8 218 my $conffile = PVE::OpenVZ::config_file($vmid);
aaeeeebe
DM
219
220 my $dir = $task->{snapdir};
221
222 $task->{cleanup}->{etc_vzdump} = 1;
223
224 mkpath "$dir/etc/vzdump/";
225 $self->cmd ("cp '$conffile' '$dir/etc/vzdump/vps.conf'");
226 my $cfgdir = dirname ($conffile);
aabf3add 227 foreach my $s (PVE::OpenVZ::SCRIPT_EXT) {
aaeeeebe
DM
228 my $fn = "$cfgdir/$vmid.$s";
229 $self->cmd ("cp '$fn' '$dir/etc/vzdump/vps.$s'") if -f $fn;
230 }
231}
232
233sub archive {
d7550e09 234 my ($self, $task, $vmid, $filename, $comp) = @_;
aaeeeebe
DM
235
236 my $findexcl = $self->{vzdump}->{findexcl};
237 my $findargs = join (' ', @$findexcl) . ' -print0';
238 my $opts = $self->{vzdump}->{opts};
239
240 my $srcdir = $self->{vmlist}->{$vmid}->{dir};
241 my $snapdir = $task->{snapdir};
242
042b3736 243 my $taropts = "--totals --sparse --numeric-owner --no-recursion --one-file-system";
aaeeeebe 244
4a4051d8
DM
245 # note: --remove-files does not work because we do not
246 # backup all files (filters). tar complains:
247 # Cannot rmdir: Directory not empty
248 # we we disable this optimization for now
249 #if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) {
250 # $taropts .= " --remove-files"; # try to save space
251 #}
aaeeeebe
DM
252
253 my $cmd = "(";
aaeeeebe 254
d7550e09
DM
255 $cmd .= "cd $snapdir;find . $findargs|sed 's/\\\\/\\\\\\\\/g'|";
256 $cmd .= "tar cpf - $taropts --null -T -";
257 my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
258 $cmd .= "|cstream -t $bwl" if $opts->{bwlimit};
259 $cmd .= "|$comp" if $comp;
aaeeeebe
DM
260
261 $cmd .= ")";
262
263 if ($opts->{stdout}) {
264 $self->cmd ($cmd, output => ">&=" . fileno($opts->{stdout}));
265 } else {
266 $self->cmd ("$cmd >$filename");
267 }
268}
269
270sub cleanup {
271 my ($self, $task, $vmid) = @_;
272
273 my $di = $task->{diskinfo};
274
275 if ($task->{cleanup}->{snapshot_mount}) {
56f57985
DM
276 # Note: sleep to avoid 'device is busy' message.
277 # Seems Kernel need some time to cleanup open file list,
278 # fir example when we stop the tar with kill (stop task)
279 sleep(1);
aaeeeebe
DM
280 $self->cmd_noerr ("umount $di->{mountpoint}");
281 }
282
283 if ($task->{cleanup}->{lvm_snapshot}) {
5dd46cbd 284 # loop, because we often get 'LV in use: not deactivating'
5bbea954 285 # we use run_command() because we do not want to log errors here
5dd46cbd
DM
286 my $wait = 1;
287 while(-b $di->{snapdev}) {
5bbea954
DM
288 eval {
289 my $cmd = ['lvremove', '-f', $di->{snapdev}];
5b9ae5b7 290 PVE::Tools::run_command($cmd, outfunc => sub {}, errfunc => sub {});
5bbea954 291 };
5dd46cbd
DM
292 last if !$@;
293 if ($wait >= 64) {
294 $self->logerr($@);
295 last;
296 }
5bbea954 297 $self->loginfo("lvremove failed - trying again in $wait seconds") if $wait >= 8;
5dd46cbd
DM
298 sleep($wait);
299 $wait = $wait*2;
300 }
301
aaeeeebe
DM
302 }
303
304 if ($task->{cleanup}->{etc_vzdump}) {
305 my $dir = "$task->{snapdir}/etc/vzdump";
fe6643b6
DM
306 eval { rmtree $dir if -d $dir; };
307 $self->logerr ($@) if $@;
aaeeeebe
DM
308 }
309
310}
311
3121;