]>
Commit | Line | Data |
---|---|---|
d14a9a1b DM |
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; | |
514b5f82 | 12 | use PVE::Tools; |
d14a9a1b DM |
13 | |
14 | use base qw (PVE::VZDump::Plugin); | |
15 | ||
4ca61ce8 DM |
16 | my $default_mount_point = "/mnt/vzsnap0"; |
17 | ||
d14a9a1b DM |
18 | my $rsync_vm = sub { |
19 | my ($self, $task, $from, $to, $text) = @_; | |
20 | ||
21 | $self->loginfo ("starting $text sync $from to $to"); | |
22 | ||
23 | my $starttime = time(); | |
24 | ||
25 | my $opts = $self->{vzdump}->{opts}; | |
26 | ||
27 | my $rsyncopts = "--stats -x -X --numeric-ids"; | |
28 | ||
29 | $rsyncopts .= " --bwlimit=$opts->{bwlimit}" if $opts->{bwlimit}; | |
30 | ||
31 | $self->cmd ("rsync $rsyncopts -aH --delete --no-whole-file --inplace '$from' '$to'"); | |
32 | ||
33 | my $delay = time () - $starttime; | |
34 | ||
35 | $self->loginfo ("$text sync finished ($delay seconds)"); | |
36 | }; | |
37 | ||
38 | sub new { | |
39 | my ($class, $vzdump) = @_; | |
40 | ||
41 | PVE::VZDump::check_bin('lxc-stop'); | |
42 | PVE::VZDump::check_bin('lxc-start'); | |
43 | PVE::VZDump::check_bin('lxc-freeze'); | |
44 | PVE::VZDump::check_bin('lxc-unfreeze'); | |
45 | ||
46 | my $self = bless {}; | |
47 | ||
48 | $self->{vzdump} = $vzdump; | |
49 | $self->{storecfg} = PVE::Storage::config(); | |
50 | ||
51 | $self->{vmlist} = PVE::LXC::config_list(); | |
52 | ||
53 | return $self; | |
54 | } | |
55 | ||
56 | sub type { | |
57 | return 'lxc'; | |
58 | } | |
59 | ||
60 | sub vm_status { | |
61 | my ($self, $vmid) = @_; | |
62 | ||
63 | my $running = PVE::LXC::check_running($vmid) ? 1 : 0; | |
64 | ||
65 | return wantarray ? ($running, $running ? 'running' : 'stopped') : $running; | |
66 | } | |
67 | ||
4ca61ce8 | 68 | my $check_mountpoint_empty = sub { |
b739f640 DM |
69 | my ($mountpoint) = @_; |
70 | ||
4ca61ce8 DM |
71 | die "mountpoint '$mountpoint' is not a directory\n" if ! -d $mountpoint; |
72 | ||
b739f640 DM |
73 | PVE::Tools::dir_glob_foreach($mountpoint, qr/.*/, sub { |
74 | my $entry = shift; | |
75 | return if $entry eq '.' || $entry eq '..'; | |
4ca61ce8 | 76 | die "mountpoint '$mountpoint' not empty\n"; |
b739f640 | 77 | }); |
d14a9a1b DM |
78 | }; |
79 | ||
80 | sub prepare { | |
81 | my ($self, $task, $vmid, $mode) = @_; | |
82 | ||
83 | my $conf = $self->{vmlist}->{$vmid} = PVE::LXC::load_config($vmid); | |
57ed5ed0 | 84 | my $storage_cfg = $self->{storecfg}; |
d14a9a1b | 85 | |
14731ec6 DM |
86 | PVE::LXC::foreach_mountpoint($conf, sub { |
87 | my ($ms, $mountpoint) = @_; | |
88 | ||
89 | return if $ms eq 'rootfs'; | |
90 | # TODO: implement support for mountpoints | |
91 | die "unable to backup mountpoint '$ms' - feature not implemented\n"; | |
92 | }); | |
93 | ||
d14a9a1b DM |
94 | my $running = PVE::LXC::check_running($vmid); |
95 | ||
4ca61ce8 | 96 | my $diskinfo = $task->{diskinfo} = {}; |
d14a9a1b | 97 | |
27916659 | 98 | $task->{hostname} = $conf->{'hostname'} || "CT$vmid"; |
d14a9a1b | 99 | |
27916659 | 100 | my $rootinfo = PVE::LXC::parse_ct_mountpoint($conf->{rootfs}); |
4ca61ce8 | 101 | $diskinfo->{volid} = $rootinfo->{volume}; |
d14a9a1b | 102 | |
4ca61ce8 | 103 | die "missing root volid (no volid)\n" if !$diskinfo->{volid}; |
c135fbf1 | 104 | |
27916659 | 105 | # fixme: when do we deactivate ?? |
57ed5ed0 | 106 | PVE::Storage::activate_volumes($storage_cfg, [$diskinfo->{volid}]); |
d14a9a1b | 107 | |
d14a9a1b | 108 | if ($mode eq 'snapshot') { |
57ed5ed0 | 109 | if (!PVE::LXC::has_feature('snapshot', $conf, $storage_cfg)) { |
4ca61ce8 DM |
110 | die "mode failure - some volumes does not support snapshots\n"; |
111 | } | |
d14a9a1b | 112 | |
4ca61ce8 DM |
113 | if ($conf->{snapshots} && $conf->{snapshots}->{vzdump}) { |
114 | $self->loginfo("found old vzdump snapshot (force removal)"); | |
115 | PVE::LXC::snapshot_delete($vmid, 'vzdump', 0); | |
116 | } | |
d14a9a1b | 117 | |
22a91261 WB |
118 | my $rootdir = $default_mount_point; |
119 | mkpath $rootdir; | |
120 | &$check_mountpoint_empty($rootdir); | |
d14a9a1b | 121 | |
4ca61ce8 DM |
122 | # set snapshot_count (freezes CT it snapshot_count > 1) |
123 | my $volid_list = PVE::LXC::get_vm_volumes($conf); | |
124 | $task->{snapshot_count} = scalar(@$volid_list); | |
f5313774 | 125 | } elsif ($mode eq 'stop') { |
22a91261 WB |
126 | my $rootdir = $default_mount_point; |
127 | mkpath $rootdir; | |
128 | &$check_mountpoint_empty($rootdir); | |
f5313774 | 129 | } elsif ($mode eq 'suspend') { |
632eca5a DM |
130 | my $pid = PVE::LXC::find_lxc_pid($vmid); |
131 | $diskinfo->{dir} = "/proc/$pid/root"; | |
f5313774 DM |
132 | $task->{snapdir} = $task->{tmpdir}; |
133 | } else { | |
134 | die "unknown mode '$mode'\n"; # should not happen | |
d14a9a1b | 135 | } |
d14a9a1b DM |
136 | } |
137 | ||
138 | sub lock_vm { | |
139 | my ($self, $vmid) = @_; | |
140 | ||
141 | PVE::LXC::lock_aquire($vmid); | |
142 | } | |
143 | ||
144 | sub unlock_vm { | |
145 | my ($self, $vmid) = @_; | |
146 | ||
147 | PVE::LXC::lock_release($vmid); | |
148 | } | |
149 | ||
4ca61ce8 DM |
150 | sub snapshot { |
151 | my ($self, $task, $vmid) = @_; | |
152 | ||
153 | my $diskinfo = $task->{diskinfo}; | |
154 | ||
155 | $self->loginfo("create storage snapshot snapshot"); | |
156 | ||
157 | # todo: freeze/unfreeze if we have more than one volid | |
158 | PVE::LXC::snapshot_create($vmid, 'vzdump', "vzdump backup snapshot"); | |
159 | $task->{cleanup}->{remove_snapshot} = 1; | |
160 | ||
161 | # reload config | |
162 | my $conf = $self->{vmlist}->{$vmid} = PVE::LXC::load_config($vmid); | |
163 | die "unable to read vzdump shanpshot config - internal error" | |
164 | if !($conf->{snapshots} && $conf->{snapshots}->{vzdump}); | |
165 | ||
166 | # my $snapconf = $conf->{snapshots}->{vzdump}; | |
167 | # my $volid_list = PVE::LXC::get_vm_volumes($snapconf); | |
168 | my $volid_list = [$diskinfo->{volid}]; | |
169 | ||
22a91261 | 170 | my $rootdir = $default_mount_point; |
4ca61ce8 DM |
171 | |
172 | my $mp = { volume => $diskinfo->{volid}, mp => "/" }; | |
22a91261 | 173 | PVE::LXC::mountpoint_mount($mp, $rootdir, $self->{storecfg}, 'vzdump'); |
4ca61ce8 | 174 | |
22a91261 | 175 | $diskinfo->{dir} = $diskinfo->{mountpoint} = $rootdir; |
4ca61ce8 DM |
176 | $task->{snapdir} = $diskinfo->{dir}; |
177 | } | |
178 | ||
d14a9a1b DM |
179 | sub copy_data_phase1 { |
180 | my ($self, $task) = @_; | |
181 | ||
182 | $self->$rsync_vm($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "first"); | |
183 | } | |
184 | ||
185 | sub copy_data_phase2 { | |
186 | my ($self, $task) = @_; | |
187 | ||
188 | $self->$rsync_vm ($task, "$task->{diskinfo}->{dir}/", $task->{snapdir}, "final"); | |
189 | } | |
190 | ||
191 | sub stop_vm { | |
192 | my ($self, $task, $vmid) = @_; | |
193 | ||
194 | $self->cmd("lxc-stop -n $vmid"); | |
195 | } | |
196 | ||
197 | sub start_vm { | |
198 | my ($self, $task, $vmid) = @_; | |
199 | ||
200 | $self->cmd ("lxc-start -n $vmid"); | |
201 | } | |
202 | ||
203 | sub suspend_vm { | |
204 | my ($self, $task, $vmid) = @_; | |
205 | ||
206 | $self->cmd ("lxc-freeze -n $vmid"); | |
207 | } | |
208 | ||
209 | sub resume_vm { | |
210 | my ($self, $task, $vmid) = @_; | |
211 | ||
212 | $self->cmd ("lxc-unfreeze -n $vmid"); | |
213 | } | |
214 | ||
215 | sub assemble { | |
216 | my ($self, $task, $vmid) = @_; | |
217 | ||
cbd6753d | 218 | my $tmpdir = $task->{tmpdir}; |
d14a9a1b | 219 | |
cbd6753d | 220 | mkpath "$tmpdir/etc/vzdump/"; |
514b5f82 WL |
221 | |
222 | my $conf = PVE::LXC::load_config($vmid); | |
223 | delete $conf->{snapshots}; | |
224 | delete $conf->{'pve.parent'}; | |
225 | ||
cbd6753d | 226 | PVE::Tools::file_set_contents("$tmpdir/etc/vzdump/pct.conf", PVE::LXC::write_pct_config("/lxc/$vmid.conf", $conf)); |
d14a9a1b DM |
227 | } |
228 | ||
229 | sub archive { | |
230 | my ($self, $task, $vmid, $filename, $comp) = @_; | |
cbd6753d | 231 | |
459fd4d2 | 232 | if ($task->{mode} eq 'stop') { |
22a91261 | 233 | my $rootdir = $default_mount_point; |
459fd4d2 DM |
234 | my $diskinfo = $task->{diskinfo}; |
235 | ||
236 | my $volid_list = [$diskinfo->{volid}]; | |
237 | my $mp = { volume => $diskinfo->{volid}, mp => "/" }; | |
238 | ||
22a91261 WB |
239 | $self->loginfo("mounting container root at '$rootdir'"); |
240 | PVE::LXC::mountpoint_mount($mp, $rootdir, $self->{storecfg}); | |
459fd4d2 | 241 | |
22a91261 | 242 | $diskinfo->{dir} = $diskinfo->{mountpoint} = $rootdir; |
459fd4d2 DM |
243 | $task->{snapdir} = $diskinfo->{dir}; |
244 | } | |
245 | ||
d14a9a1b | 246 | my $findexcl = $self->{vzdump}->{findexcl}; |
cbd6753d DM |
247 | push @$findexcl, "'('", '-path', "./etc/vzdump", "-prune", "')'", '-o'; |
248 | ||
d14a9a1b DM |
249 | my $findargs = join (' ', @$findexcl) . ' -print0'; |
250 | my $opts = $self->{vzdump}->{opts}; | |
251 | ||
252 | my $srcdir = $task->{diskinfo}->{dir}; | |
253 | my $snapdir = $task->{snapdir}; | |
cbd6753d | 254 | my $tmpdir = $task->{tmpdir}; |
d14a9a1b DM |
255 | |
256 | my $taropts = "--totals --sparse --numeric-owner --no-recursion --xattrs --one-file-system"; | |
257 | ||
258 | # note: --remove-files does not work because we do not | |
259 | # backup all files (filters). tar complains: | |
260 | # Cannot rmdir: Directory not empty | |
261 | # we we disable this optimization for now | |
262 | #if ($snapdir eq $task->{tmpdir} && $snapdir =~ m|^$opts->{dumpdir}/|) { | |
263 | # $taropts .= " --remove-files"; # try to save space | |
264 | #} | |
265 | ||
266 | my $cmd = "("; | |
267 | ||
268 | $cmd .= "cd $snapdir;find . $findargs|sed 's/\\\\/\\\\\\\\/g'|"; | |
cbd6753d DM |
269 | $cmd .= "tar cpf - $taropts "; |
270 | # The directory parameter can give a alternative directory as source. | |
271 | # the second parameter gives the structure in the tar. | |
272 | $cmd .= "--directory=$tmpdir ./etc/vzdump/pct.conf "; | |
273 | $cmd .= "--directory=$snapdir --null -T -"; | |
274 | ||
d14a9a1b DM |
275 | my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream |
276 | $cmd .= "|cstream -t $bwl" if $opts->{bwlimit}; | |
277 | $cmd .= "|$comp" if $comp; | |
278 | ||
279 | $cmd .= ")"; | |
280 | ||
281 | if ($opts->{stdout}) { | |
148d1cb4 | 282 | $self->cmd ($cmd, output => ">&" . fileno($opts->{stdout})); |
d14a9a1b DM |
283 | } else { |
284 | $self->cmd ("$cmd >$filename"); | |
285 | } | |
286 | } | |
287 | ||
288 | sub cleanup { | |
289 | my ($self, $task, $vmid) = @_; | |
290 | ||
4ca61ce8 | 291 | my $diskinfo = $task->{diskinfo}; |
d14a9a1b | 292 | |
22a91261 WB |
293 | if (my $rootdir = $diskinfo->{mountpoint}) { |
294 | PVE::Tools::run_command(['umount', '-l', '-d', $rootdir]); | |
b739f640 DM |
295 | }; |
296 | ||
4ca61ce8 DM |
297 | if ($task->{cleanup}->{remove_snapshot}) { |
298 | $self->loginfo("remove vzdump snapshot"); | |
299 | PVE::LXC::snapshot_delete($vmid, 'vzdump', 0); | |
d14a9a1b | 300 | } |
d14a9a1b DM |
301 | } |
302 | ||
303 | 1; |