]>
git.proxmox.com Git - qemu-server.git/blob - qmrestore
3 # Copyright (C) 2007-2009 Proxmox Server Solutions GmbH
5 # Copyright: vzdump is under GNU GPL, the GNU General Public License.
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.
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.
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,
21 # Author: Dietmar Maurer <dietmar@proxmox.com>
31 $ENV{LANG
} = "C"; # avoid locale related issues/warnings
33 openlog
('vzdump', 'cons,pid', 'daemon');
35 my @std_opts = ('extract', 'storage=s', 'info', 'prealloc', 'unique');
40 print STDERR
"ERROR: $msg\n\n" if $msg;
42 print STDERR
"usage: $0 [OPTIONS] <ARCHIVE> <VMID>\n\n";
48 return "''" if !defined ($str) || ($str eq '');
50 die "unable to quote string containing null (\\000) bytes"
53 # from String::ShellQuote
54 if ($str =~ m
|[^\w
!%+,\-./:@^]|) {
67 my $quoted_cmd = shellquote
($0);
68 foreach my $arg (@ARGV) {
69 $quoted_cmd .= " " . shellquote
($arg);
73 if (!GetOptions
($opts, @std_opts)) {
84 my $vmid = PVE
::VZDump
::check_vmids
((shift))->[0];
86 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = $SIG{HUP
} = $SIG{PIPE
} = sub {
87 die "interrupted by signal\n";
90 sub debugmsg
{ PVE
::VZDump
::debugmsg
(@_); } # just a shortcut
92 sub run_command
{ PVE
::VZDump
::run_command
(undef, @_); } # just a shortcut
94 if ($opts->{extract
}) {
96 # NOTE: this is run as tar subprocess (--to-command)
98 my $filename = $ENV{TAR_FILENAME
};
99 die "got strange environment - no TAR_FILENAME\n" if !$filename;
101 my $filesize = $ENV{TAR_SIZE
};
102 die "got strange file size '$filesize'\n" if !$filesize;
104 my $tmpdir = $ENV{VZDUMP_TMPDIR
};
105 die "got strange environment - no VZDUMP_TMPDIR\n" if !$tmpdir;
107 my $filetype = $ENV{TAR_FILETYPE
} || 'none';
108 die "got strange filetype '$filetype'\n" if $filetype ne 'f';
110 my $conffile = "$tmpdir/qemu-server.conf";
111 my $statfile = "$tmpdir/qmrestore.stat";
114 print STDERR
"reading archive member '$filename'\n";
116 print STDERR
"extracting '$filename' from archive\n";
119 if ($filename eq 'qemu-server.conf') {
120 my $outfd = IO
::File-
>new ($conffile, "w") ||
121 die "unable to write file '$conffile'\n";
123 while (defined (my $line = <>)) {
125 print STDERR
"CONFIG: $line" if $opts->{info
};
134 exec 'dd', 'bs=256K', "of=/dev/null";
135 die "couldn't exec dd: $!\n";
138 my $conffd = IO
::File-
>new ($conffile, "r") ||
139 die "unable to read file '$conffile'\n";
142 while (defined (my $line = <$conffd>)) {
143 if ($line =~ m/^\#vzdump\#map:(\S+):(\S+):(\d+):(\S*):$/) {
144 $map->{$2} = { virtdev
=> $1, size
=> $3, storeid
=> $4 };
149 my $statfd = IO
::File-
>new ($statfile, "a") ||
150 die "unable to open file '$statfile'\n";
152 if ($filename !~ m/^.*\.([^\.]+)$/){
153 die "got strange filename '$filename'\n";
160 print STDERR
"restoring old style vzdump archive - " .
161 "no device map inside archive\n";
162 die "can't restore old style archive to storage '$opts->{storage}'\n"
163 if $opts->{storage
} && $opts->{storage
} ne 'local';
165 my $dir = "/var/lib/vz/images/$vmid";
168 $path = "$dir/$filename";
170 print $statfd "vzdump::$path\n";
175 my $info = $map->{$filename};
176 die "no vzdump info for '$filename'\n" if !$info;
178 if ($filename !~ m/^vm-disk-$info->{virtdev}\.([^\.]+)$/){
179 die "got strange filename '$filename'\n";
182 if ($filesize != $info->{size
}) {
183 die "detected size difference for '$filename' " .
184 "($filesize != $info->{size})\n";
188 if ($opts->{storage
}) {
189 $storeid = $opts->{storage
};
191 $storeid = $info->{storeid
} || 'local';
194 my $cfg = PVE
::Storage
::load_config
();
195 my $scfg = PVE
::Storage
::storage_config
($cfg, $storeid);
197 my $alloc_size = ($filesize + 1024 - 1)/1024;
198 if ($scfg->{type
} eq 'dir' || $scfg->{type
} eq 'nfs') {
199 # hack: we just alloc a small file (32K) - we overwrite it anyways
202 die "unable to restore '$filename' to storage '$storeid'\n" .
203 "storage type '$scfg->{type}' does not support format '$format\n"
207 my $volid = PVE
::Storage
::vdisk_alloc
($cfg, $storeid, $vmid,
208 $format, undef, $alloc_size);
210 print STDERR
"new volume ID is '$volid'\n";
212 print $statfd "vzdump:$info->{virtdev}:$volid\n";
215 $path = PVE
::Storage
::path
($cfg, $volid);
218 print STDERR
"restore data to '$path' ($filesize bytes)\n";
220 if ($opts->{prealloc
} || $format ne 'raw' || (-b
$path)) {
221 exec 'dd', 'ibs=256K', 'obs=256K', "of=$path";
222 die "couldn't exec dd: $!\n";
224 exec '/usr/lib/qemu-server/sparsecp', $path;
225 die "couldn't exec sparsecp: $!\n";
229 sub restore_cleanup
{
230 my $statfile = shift;
232 return if $opts->{info
};
234 debugmsg
('info', "starting cleanup");
235 if (my $fd = IO
::File-
>new ($statfile, "r")) {
236 while (defined (my $line = <$fd>)) {
237 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
240 if ($volid =~ m
|^/|) {
241 unlink $volid || die 'unlink failed\n';
243 my $cfg = PVE
::Storage
::load_config
();
244 PVE
::Storage
::vdisk_free
($cfg, $volid);
246 debugmsg
('info', "temporary volume '$volid' sucessfuly removed");
248 debugmsg
('err', "unable to cleanup '$volid' - $@") if $@;
250 debugmsg
('info', "unable to parse line in statfile - $line");
258 my ($archive, $vmid, $tmpdir) = @_;
260 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
262 my $subcmd = shellquote
("--to-command=${quoted_cmd}\ --extract");
263 my $cmd = "tar xf '$archive' $subcmd";
266 return if $opts->{info
};
270 my $statfile = "$tmpdir/qmrestore.stat";
271 if (my $fd = IO
::File-
>new ($statfile, "r")) {
272 while (defined (my $line = <$fd>)) {
273 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
274 $map->{$1} = $2 if $1;
276 debugmsg
('info', "unable to parse line in statfile - $line");
282 my $confsrc = "$tmpdir/qemu-server.conf";
284 my $srcfd = new IO
::File
($confsrc, "r") ||
285 die "unable to open file '$confsrc'\n";
287 my $conffile = PVE
::QemuServer
::config_file
($vmid);
288 my $tmpfn = "$conffile.$$.tmp";
290 my $outfd = new IO
::File
($tmpfn, "w") ||
291 die "unable to write config for VM $vmid\n";
294 while (defined (my $line = <$srcfd>)) {
295 next if $line =~ m/^\#vzdump\#/;
296 next if $line =~ m/^lock:/;
298 if (($line =~ m/^((vlan)\d+):(.*)$/) && ($opts->{unique
})) {
299 my ($id,$ethcfg) = ($1,$3);
301 my ($model, $mac) = split(/\=/,$ethcfg);
302 my $printvlan = PVE
::QemuServer
::print_vlan
(PVE
::QemuServer
::parse_vlan
($model));
303 print $outfd "$id: $printvlan\n";
304 } elsif ($line =~ m/^((ide|scsi|virtio)\d+):(.*)$/) {
307 if ($line =~ m/backup=no/) {
308 print $outfd "#$line";
309 } elsif ($virtdev && $map->{$virtdev}) {
310 my $di = PVE
::QemuServer
::parse_drive
($virtdev, $value);
311 $di->{file
} = $map->{$virtdev};
312 $value = PVE
::QemuServer
::print_drive
($vmid, $di);
313 print $outfd "$virtdev: $value\n";
330 rename $tmpfn, $conffile;
334 my $firstfile = PVE
::VZDump
::read_firstfile
($archive);
335 if ($firstfile ne 'qemu-server.conf') {
336 die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n";
339 my $tmpdir = "/var/tmp/vzdumptmp$$";
341 PVE
::QemuServer
::lock_config
($vmid, sub {
343 my $conffile = PVE
::QemuServer
::config_file
($vmid);
345 die "unable to restore VM '$vmid' - VM already exists\n"
351 debugmsg
('info', "restore QemuServer backup '$archive' " .
352 "using ID $vmid", undef, 1) if !$opts->{info
};
354 restore_qemu
($archive, $vmid, $tmpdir);
357 debugmsg
('info', "reading '$archive' successful");
359 debugmsg
('info', "restore QemuServer backup '$archive' successful",
366 local $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = $SIG{HUP
} = sub {
367 debugmsg
('info', "got interrupt - ignored (cleanup phase)");
370 restore_cleanup
("$tmpdir/qmrestore.stat") if $err;
382 debugmsg
('info', "reading '$archive' failed - $err");
385 debugmsg
('err', "restore QemuServer backup '$archive' failed - $err",
397 qmrestore - restore QemuServer vzdump backups
401 qmrestore [OPTIONS] <archive> <VMID>
403 --info read/verify archive and print relevant
404 information (test run)
406 --unique assign a unique random ethernet address
408 --storage <STORAGE_ID> restore to storage <STORAGE_ID>
410 --prealloc never generate sparse files
414 Restore the QemuServer vzdump backup <archive> to virtual machine
415 <VMID>. Volumes are allocated on the original storage if there is no
416 C<--storage> specified.
420 vzdump(1) vzrestore(1)