]>
Commit | Line | Data |
---|---|---|
aaeeeebe DM |
1 | package PVE::VZDump::OpenVZ; |
2 | ||
aaeeeebe DM |
3 | use strict; |
4 | use warnings; | |
5 | use File::Path; | |
6 | use File::Basename; | |
4a4051d8 | 7 | use PVE::INotify; |
aaeeeebe | 8 | use PVE::VZDump; |
4a4051d8 | 9 | use PVE::OpenVZ; |
aaeeeebe DM |
10 | |
11 | use base qw (PVE::VZDump::Plugin); | |
12 | ||
aaeeeebe DM |
13 | my $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 |
32 | my $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 | ||
52 | sub 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 | ||
66 | sub type { | |
67 | return 'openvz'; | |
68 | } | |
69 | ||
70 | sub 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 | ||
82 | sub 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 | ||
129 | sub 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 | ||
139 | sub unlock_vm { | |
140 | my ($self, $vmid) = @_; | |
141 | ||
142 | $self->{lock}->release(); | |
143 | } | |
144 | ||
145 | sub 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 | ||
154 | sub stop_vm { | |
155 | my ($self, $task, $vmid) = @_; | |
156 | ||
157 | $self->cmd ("vzctl --skiplock stop $vmid"); | |
158 | } | |
159 | ||
160 | sub start_vm { | |
161 | my ($self, $task, $vmid) = @_; | |
162 | ||
163 | $self->cmd ("vzctl --skiplock start $vmid"); | |
164 | } | |
165 | ||
166 | sub suspend_vm { | |
167 | my ($self, $task, $vmid) = @_; | |
168 | ||
169 | $self->cmd ("vzctl --skiplock chkpnt $vmid --suspend"); | |
170 | } | |
171 | ||
172 | sub 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 | ||
203 | sub copy_data_phase2 { | |
204 | my ($self, $task) = @_; | |
205 | ||
206 | $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "final"); | |
207 | } | |
208 | ||
209 | sub resume_vm { | |
210 | my ($self, $task, $vmid) = @_; | |
211 | ||
212 | $self->cmd ("vzctl --skiplock chkpnt $vmid --resume"); | |
213 | } | |
214 | ||
215 | sub 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 | ||
233 | sub 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 | ||
270 | sub 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 | ||
312 | 1; |