]> git.proxmox.com Git - pve-manager.git/blob - PVE/VZDump/OpenVZ.pm
adopt vzdump to pve 2
[pve-manager.git] / PVE / VZDump / OpenVZ.pm
1 package PVE::VZDump::OpenVZ;
2
3 # Copyright (C) 2007-2009 Proxmox Server Solutions GmbH
4 #
5 # Copyright: vzdump is under GNU GPL, the GNU General Public License.
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; version 2 dated June, 1991.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the
18 # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 #
21 # Author: Dietmar Maurer <dietmar@proxmox.com>
22
23 use strict;
24 use warnings;
25 use File::Path;
26 use File::Basename;
27 use PVE::INotify;
28 use PVE::VZDump;
29 use PVE::OpenVZ;
30
31 use base qw (PVE::VZDump::Plugin);
32
33 use constant SCRIPT_EXT => qw (start stop mount umount);
34
35 my $load_vz_conf = sub {
36 my ($self, $vmid) = @_;
37
38 my $conf = PVE::OpenVZ::load_config($vmid);
39
40 my $dir = $self->{privatedir};
41 if ($conf->{ve_private} && $conf->{ve_private}->{value}) {
42 $dir = $conf->{ve_private}->{value};
43 }
44 $dir =~ s/\$VEID/$vmid/;
45 $self->{vmlist}->{$vmid}->{dir} = $dir;
46
47 my $hostname = "CT $vmid";
48 if ($conf->{hostname} && $conf->{hostname}->{value}) {
49 $hostname = $conf->{hostname}->{value};
50 }
51 $self->{vmlist}->{$vmid}->{hostname} = $hostname;
52 };
53
54 my $rsync_vm = sub {
55 my ($self, $task, $from, $to, $text) = @_;
56
57 $self->loginfo ("starting $text sync $from to $to");
58
59 my $starttime = time();
60
61 my $opts = $self->{vzdump}->{opts};
62
63 my $rsyncopts = "--stats -x --numeric-ids";
64
65 $rsyncopts .= " --bwlimit=$opts->{bwlimit}" if $opts->{bwlimit};
66
67 $self->cmd ("rsync $rsyncopts -aH --delete --no-whole-file --inplace '$from' '$to'");
68
69 my $delay = time () - $starttime;
70
71 $self->loginfo ("$text sync finished ($delay seconds)");
72 };
73
74 sub new {
75 my ($class, $vzdump) = @_;
76
77 PVE::VZDump::check_bin ('vzctl');
78
79 my $self = bless PVE::OpenVZ::read_global_vz_config ();
80
81 $self->{vzdump} = $vzdump;
82
83 $self->{vmlist} = PVE::OpenVZ::config_list();
84
85 return $self;
86 };
87
88 sub type {
89 return 'openvz';
90 }
91
92 sub vm_status {
93 my ($self, $vmid) = @_;
94
95 my $status_text = $self->cmd ("vzctl status $vmid", returnstdout => 1);
96 chomp $status_text;
97
98 my $running = $status_text =~ m/running/ ? 1 : 0;
99
100 return wantarray ? ($running, $running ? 'running' : 'stopped') : $running;
101 }
102
103 sub prepare {
104 my ($self, $task, $vmid, $mode) = @_;
105
106 $self->$load_vz_conf ($vmid);
107
108 my $dir = $self->{vmlist}->{$vmid}->{dir};
109
110 my $diskinfo = { dir => $dir };
111
112 $task->{hostname} = $self->{vmlist}->{$vmid}->{hostname};
113
114 $task->{diskinfo} = $diskinfo;
115
116 my $hostname = PVE::INotify::nodename();
117
118 if ($mode eq 'snapshot') {
119
120 my $lvmmap = PVE::VZDump::get_lvm_mapping();
121 my ($srcdev, $lvmpath, $lvmvg, $lvmlv, $fstype) =
122 PVE::VZDump::get_lvm_device ($dir, $lvmmap);
123
124 my $targetdev = PVE::VZDump::get_lvm_device ($task->{dumpdir}, $lvmmap);
125
126 die ("mode failure - unable to detect lvm volume group\n") if !$lvmvg;
127 die ("mode failure - wrong lvm mount point '$lvmpath'\n") if $dir !~ m|/?$lvmpath/?|;
128 die ("mode failure - unable to dump into snapshot (use option --dumpdir)\n")
129 if $targetdev eq $srcdev;
130
131 $diskinfo->{snapname} = "vzsnap-$hostname-0";
132 $diskinfo->{snapdev} = "/dev/$lvmvg/$diskinfo->{snapname}";
133 $diskinfo->{srcdev} = $srcdev;
134 $diskinfo->{lvmvg} = $lvmvg;
135 $diskinfo->{lvmlv} = $lvmlv;
136 $diskinfo->{fstype} = $fstype;
137 $diskinfo->{lvmpath} = $lvmpath;
138 $diskinfo->{mountpoint} = "/mnt/vzsnap0";
139
140 $task->{snapdir} = $dir;
141 $task->{snapdir} =~ s|/?$lvmpath/?|$diskinfo->{mountpoint}/|;
142
143 } elsif ($mode eq 'suspend') {
144 $task->{snapdir} = $task->{tmpdir};
145 } else {
146 $task->{snapdir} = $dir;
147 }
148 }
149
150 sub lock_vm {
151 my ($self, $vmid) = @_;
152
153 my $filename = "$self->{lockdir}/103.lck";
154
155 my $lockmgr = PVE::OpenVZ::create_lock_manager();
156
157 $self->{lock} = $lockmgr->lock($filename) || die "can't lock VM $vmid\n";
158 }
159
160 sub unlock_vm {
161 my ($self, $vmid) = @_;
162
163 $self->{lock}->release();
164 }
165
166 sub copy_data_phase1 {
167 my ($self, $task) = @_;
168
169 $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "first");
170 }
171
172 # we use --skiplock for vzctl because we have already locked the VM
173 # by calling lock_vm()
174
175 sub stop_vm {
176 my ($self, $task, $vmid) = @_;
177
178 $self->cmd ("vzctl --skiplock stop $vmid");
179 }
180
181 sub start_vm {
182 my ($self, $task, $vmid) = @_;
183
184 $self->cmd ("vzctl --skiplock start $vmid");
185 }
186
187 sub suspend_vm {
188 my ($self, $task, $vmid) = @_;
189
190 $self->cmd ("vzctl --skiplock chkpnt $vmid --suspend");
191 }
192
193 sub snapshot {
194 my ($self, $task) = @_;
195
196 my $opts = $self->{vzdump}->{opts};
197
198 my $di = $task->{diskinfo};
199
200 mkpath $di->{mountpoint}; # create mount point for lvm snapshot
201
202 if (-b $di->{snapdev}) {
203 $self->loginfo ("trying to remove stale snapshot '$di->{snapdev}'");
204
205 $self->cmd_noerr ("umount $di->{mountpoint}");
206
207 $self->cmd_noerr ("lvremove -f $di->{snapdev}");
208 }
209
210 $self->loginfo ("creating lvm snapshot of $di->{srcdev} ('$di->{snapdev}')");
211
212 $task->{cleanup}->{lvm_snapshot} = 1;
213
214 $self->cmd ("lvcreate --size $opts->{size}M --snapshot" .
215 " --name $di->{snapname} /dev/$di->{lvmvg}/$di->{lvmlv}");
216
217 my $mopts = $di->{fstype} eq 'xfs' ? "-o nouuid" : '';
218
219 $task->{cleanup}->{snapshot_mount} = 1;
220
221 $self->cmd ("mount -t $di->{fstype} $mopts $di->{snapdev} $di->{mountpoint}");
222 }
223
224 sub copy_data_phase2 {
225 my ($self, $task) = @_;
226
227 $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "final");
228 }
229
230 sub resume_vm {
231 my ($self, $task, $vmid) = @_;
232
233 $self->cmd ("vzctl --skiplock chkpnt $vmid --resume");
234 }
235
236 sub assemble {
237 my ($self, $task, $vmid) = @_;
238
239 my $conffile = PVE::OpenVZ::config_file($vmid);
240
241 my $dir = $task->{snapdir};
242
243 $task->{cleanup}->{etc_vzdump} = 1;
244
245 mkpath "$dir/etc/vzdump/";
246 $self->cmd ("cp '$conffile' '$dir/etc/vzdump/vps.conf'");
247 my $cfgdir = dirname ($conffile);
248 foreach my $s (SCRIPT_EXT) {
249 my $fn = "$cfgdir/$vmid.$s";
250 $self->cmd ("cp '$fn' '$dir/etc/vzdump/vps.$s'") if -f $fn;
251 }
252 }
253
254 sub archive {
255 my ($self, $task, $vmid, $filename) = @_;
256
257 my $findexcl = $self->{vzdump}->{findexcl};
258 my $findargs = join (' ', @$findexcl) . ' -print0';
259 my $opts = $self->{vzdump}->{opts};
260
261 my $srcdir = $self->{vmlist}->{$vmid}->{dir};
262 my $snapdir = $task->{snapdir};
263
264 my $zflag = $opts->{compress} ? 'z' : '';
265
266 my $taropts = "--totals --sparse --numeric-owner --no-recursion --ignore-failed-read --one-file-system";
267
268 # note: --remove-files does not work because we do not
269 # backup all files (filters). tar complains:
270 # Cannot rmdir: Directory not empty
271 # we we disable this optimization for now
272 #if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) {
273 # $taropts .= " --remove-files"; # try to save space
274 #}
275
276 my $cmd = "(";
277 $cmd .= "cd $snapdir;find . $findargs|sed 's/\\\\/\\\\\\\\/g'|";
278 $cmd .= "tar c${zflag}pf - $taropts --null -T -";
279
280 if ($opts->{bwlimit}) {
281 my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
282 $cmd .= "|cstream -t $bwl";
283 }
284
285 $cmd .= ")";
286
287 if ($opts->{stdout}) {
288 $self->cmd ($cmd, output => ">&=" . fileno($opts->{stdout}));
289 } else {
290 $self->cmd ("$cmd >$filename");
291 }
292 }
293
294 sub cleanup {
295 my ($self, $task, $vmid) = @_;
296
297 my $di = $task->{diskinfo};
298
299 if ($task->{cleanup}->{snapshot_mount}) {
300 $self->cmd_noerr ("umount $di->{mountpoint}");
301 }
302
303 if ($task->{cleanup}->{lvm_snapshot}) {
304 $self->cmd_noerr ("lvremove -f $di->{snapdev}") if -b $di->{snapdev};
305 }
306
307 if ($task->{cleanup}->{etc_vzdump}) {
308 my $dir = "$task->{snapdir}/etc/vzdump";
309 eval { rmtree $dir if -d $dir; };
310 $self->logerr ($@) if $@;
311 }
312
313 }
314
315 1;