X-Git-Url: https://git.proxmox.com/?p=qemu-server.git;a=blobdiff_plain;f=qmrestore;h=506f2bdcebd0da3a7df17858bb47c9c35f83f8bc;hp=de3279910d348f731a06f1a645fef1f487039196;hb=eff36b2caa6a008da253c24f86ef573721b28726;hpb=1e3baf05f2b5e795f99f5c4bb7bdb118c72b8b50 diff --git a/qmrestore b/qmrestore index de32799..506f2bd 100755 --- a/qmrestore +++ b/qmrestore @@ -1,420 +1,8 @@ -#!/usr/bin/perl -w -# -# Copyright (C) 2007-2009 Proxmox Server Solutions GmbH -# -# Copyright: vzdump is under GNU GPL, the GNU General Public License. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 dated June, 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the -# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, -# MA 02110-1301, USA. -# -# Author: Dietmar Maurer -# +#!/usr/bin/perl use strict; -use Getopt::Long; -use Sys::Syslog; -use File::Path; -use PVE::VZDump; -use PVE::Storage; +use warnings; -$ENV{LANG} = "C"; # avoid locale related issues/warnings +use PVE::CLI::qmrestore; -openlog ('vzdump', 'cons,pid', 'daemon'); - -my @std_opts = ('extract', 'storage=s', 'info', 'prealloc', 'unique'); - -sub print_usage { - my $msg = shift; - - print STDERR "ERROR: $msg\n\n" if $msg; - - print STDERR "usage: $0 [OPTIONS] \n\n"; -} - -sub shellquote { - my $str = shift; - - return "''" if !defined ($str) || ($str eq ''); - - die "unable to quote string containing null (\\000) bytes" - if $str =~ m/\x00/; - - # from String::ShellQuote - if ($str =~ m|[^\w!%+,\-./:@^]|) { - - # ' -> '\'' - $str =~ s/'/'\\''/g; - - $str = "'$str'"; - $str =~ s/^''//; - $str =~ s/''$//; - } - - return $str; -} - -my $quoted_cmd = shellquote ($0); -foreach my $arg (@ARGV) { - $quoted_cmd .= " " . shellquote ($arg); -} - -my $opts = {}; -if (!GetOptions ($opts, @std_opts)) { - print_usage (); - exit (-1); -} - -if ($#ARGV != 1) { - print_usage (); - exit (-1); -} - -my $archive = shift; -my $vmid = PVE::VZDump::check_vmids ((shift))->[0]; - -$SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub { - die "interrupted by signal\n"; -}; - -sub debugmsg { PVE::VZDump::debugmsg (@_); } # just a shortcut - -sub run_command { PVE::VZDump::run_command (undef, @_); } # just a shortcut - -if ($opts->{extract}) { - - # NOTE: this is run as tar subprocess (--to-command) - - my $filename = $ENV{TAR_FILENAME}; - die "got strange environment - no TAR_FILENAME\n" if !$filename; - - my $filesize = $ENV{TAR_SIZE}; - die "got strange file size '$filesize'\n" if !$filesize; - - my $tmpdir = $ENV{VZDUMP_TMPDIR}; - die "got strange environment - no VZDUMP_TMPDIR\n" if !$tmpdir; - - my $filetype = $ENV{TAR_FILETYPE} || 'none'; - die "got strange filetype '$filetype'\n" if $filetype ne 'f'; - - my $conffile = "$tmpdir/qemu-server.conf"; - my $statfile = "$tmpdir/qmrestore.stat"; - - if ($opts->{info}) { - print STDERR "reading archive member '$filename'\n"; - } else { - print STDERR "extracting '$filename' from archive\n"; - } - - if ($filename eq 'qemu-server.conf') { - my $outfd = IO::File->new ($conffile, "w") || - die "unable to write file '$conffile'\n"; - - while (defined (my $line = <>)) { - print $outfd $line; - print STDERR "CONFIG: $line" if $opts->{info}; - } - - $outfd->close(); - - exit (0); - } - - if ($opts->{info}) { - exec 'dd', 'bs=256K', "of=/dev/null"; - die "couldn't exec dd: $!\n"; - } - - my $conffd = IO::File->new ($conffile, "r") || - die "unable to read file '$conffile'\n"; - - my $map; - while (defined (my $line = <$conffd>)) { - if ($line =~ m/^\#vzdump\#map:(\S+):(\S+):(\d+):(\S*):$/) { - $map->{$2} = { virtdev => $1, size => $3, storeid => $4 }; - } - } - close ($conffd); - - my $statfd = IO::File->new ($statfile, "a") || - die "unable to open file '$statfile'\n"; - - if ($filename !~ m/^.*\.([^\.]+)$/){ - die "got strange filename '$filename'\n"; - } - my $format = $1; - - my $path; - - if (!$map) { - print STDERR "restoring old style vzdump archive - " . - "no device map inside archive\n"; - die "can't restore old style archive to storage '$opts->{storage}'\n" - if $opts->{storage} && $opts->{storage} ne 'local'; - - my $dir = "/var/lib/vz/images/$vmid"; - mkpath $dir; - - $path = "$dir/$filename"; - - print $statfd "vzdump::$path\n"; - $statfd->close(); - - } else { - - my $info = $map->{$filename}; - die "no vzdump info for '$filename'\n" if !$info; - - if ($filename !~ m/^vm-disk-$info->{virtdev}\.([^\.]+)$/){ - die "got strange filename '$filename'\n"; - } - - if ($filesize != $info->{size}) { - die "detected size difference for '$filename' " . - "($filesize != $info->{size})\n"; - } - - my $storeid; - if ($opts->{storage}) { - $storeid = $opts->{storage}; - } else { - $storeid = $info->{storeid} || 'local'; - } - - my $cfg = PVE::Storage::load_config(); - my $scfg = PVE::Storage::storage_config ($cfg, $storeid); - - my $alloc_size = ($filesize + 1024 - 1)/1024; - if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') { - # hack: we just alloc a small file (32K) - we overwrite it anyways - $alloc_size = 32; - } else { - die "unable to restore '$filename' to storage '$storeid'\n" . - "storage type '$scfg->{type}' does not support format '$format\n" - if $format ne 'raw'; - } - - my $volid = PVE::Storage::vdisk_alloc ($cfg, $storeid, $vmid, - $format, undef, $alloc_size); - - print STDERR "new volume ID is '$volid'\n"; - - print $statfd "vzdump:$info->{virtdev}:$volid\n"; - $statfd->close(); - - $path = PVE::Storage::path ($cfg, $volid); - } - - print STDERR "restore data to '$path' ($filesize bytes)\n"; - - if ($opts->{prealloc} || $format ne 'raw' || (-b $path)) { - exec 'dd', 'bs=256K', "of=$path"; - die "couldn't exec dd: $!\n"; - } else { - exec '/usr/lib/qemu-server/sparsecp', $path; - die "couldn't exec sparsecp: $!\n"; - } -} - -sub restore_cleanup { - my $statfile = shift; - - return if $opts->{info}; - - debugmsg ('info', "starting cleanup"); - if (my $fd = IO::File->new ($statfile, "r")) { - while (defined (my $line = <$fd>)) { - if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) { - my $volid = $2; - eval { - if ($volid =~ m|^/|) { - unlink $volid || die 'unlink failed\n'; - } else { - my $cfg = PVE::Storage::load_config(); - PVE::Storage::vdisk_free ($cfg, $volid); - } - debugmsg ('info', "temporary volume '$volid' sucessfuly removed"); - }; - debugmsg ('err', "unable to cleanup '$volid' - $@") if $@; - } else { - debugmsg ('info', "unable to parse line in statfile - $line"); - } - } - $fd->close(); - } -} - -sub restore_qemu { - my ($archive, $vmid, $tmpdir) = @_; - - local $ENV{VZDUMP_TMPDIR} = $tmpdir; - - my $subcmd = shellquote ("--to-command=${quoted_cmd}\ --extract"); - my $cmd = "tar xf '$archive' $subcmd"; - run_command ($cmd); - - return if $opts->{info}; - - # reed new mapping - my $map = {}; - my $statfile = "$tmpdir/qmrestore.stat"; - if (my $fd = IO::File->new ($statfile, "r")) { - while (defined (my $line = <$fd>)) { - if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) { - $map->{$1} = $2 if $1; - } else { - debugmsg ('info', "unable to parse line in statfile - $line"); - } - } - $fd->close(); - } - - my $confsrc = "$tmpdir/qemu-server.conf"; - - my $srcfd = new IO::File ($confsrc, "r") || - die "unable to open file '$confsrc'\n"; - - my $conffile = PVE::QemuServer::config_file ($vmid); - my $tmpfn = "$conffile.$$.tmp"; - - my $outfd = new IO::File ($tmpfn, "w") || - die "unable to write config for VM $vmid\n"; - - eval { - while (defined (my $line = <$srcfd>)) { - next if $line =~ m/^\#vzdump\#/; - next if $line =~ m/^lock:/; - - if (($line =~ m/^((vlan)\d+):(.*)$/) && ($opts->{unique})) { - my ($id,$ethcfg) = ($1,$3); - $ethcfg =~ s/^\s+//; - my ($model, $mac) = split(/\=/,$ethcfg); - my $printvlan = PVE::QemuServer::print_vlan (PVE::QemuServer::parse_vlan ($model)); - print $outfd "$id: $printvlan\n"; - } elsif ($line =~ m/^((ide|scsi|virtio)\d+):(.*)$/) { - my $virtdev = $1; - my $value = $2; - if ($line =~ m/backup=no/) { - print $outfd "#$line"; - } elsif ($virtdev && $map->{$virtdev}) { - my $di = PVE::QemuServer::parse_drive ($virtdev, $value); - $di->{file} = $map->{$virtdev}; - $value = PVE::QemuServer::print_drive ($vmid, $di); - print $outfd "$virtdev: $value\n"; - } else { - print $outfd $line; - } - } else { - print $outfd $line; - } - } - }; - my $err = $@; - - $outfd->close(); - - if ($err) { - unlink $tmpfn; - die $err; - } else { - rename $tmpfn, $conffile; - } -} - -my $firstfile = PVE::VZDump::read_firstfile ($archive); -if ($firstfile ne 'qemu-server.conf') { - die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"; -} - -my $tmpdir = "/var/tmp/vzdumptmp$$"; - -PVE::QemuServer::lock_config ($vmid, sub { - - my $conffile = PVE::QemuServer::config_file ($vmid); - - die "unable to restore VM '$vmid' - VM already exists\n" - if -f $conffile; - - mkpath $tmpdir; - - eval { - debugmsg ('info', "restore QemuServer backup '$archive' " . - "using ID $vmid", undef, 1) if !$opts->{info}; - - restore_qemu ($archive, $vmid, $tmpdir); - - if ($opts->{info}) { - debugmsg ('info', "reading '$archive' successful"); - } else { - debugmsg ('info', "restore QemuServer backup '$archive' successful", - undef, 1); - } - }; - my $err = $@; - - if ($err) { - local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub { - debugmsg ('info', "got interrupt - ignored (cleanup phase)"); - }; - - restore_cleanup ("$tmpdir/qmrestore.stat") if $err; - } - - die $err if $err; -}); - -my $err = $@; - -rmtree $tmpdir; - -if ($err) { - if ($opts->{info}) { - debugmsg ('info', "reading '$archive' failed - $err"); - } else { - - debugmsg ('err', "restore QemuServer backup '$archive' failed - $err", - undef, 1); - } - exit (-1); -} - -exit (0); - -__END__ - -=head1 NAME - -qmrestore - restore QemuServer vzdump backups - -=head1 SYNOPSIS - -qmrestore [OPTIONS] - - --info read/verify archive and print relevant - information (test run) - - --unique assign a unique random ethernet address - - --storage restore to storage - - --prealloc never generate sparse files - -=head1 DESCRIPTION - -Restore the QemuServer vzdump backup to virtual machine -. Volumes are allocated on the original storage if there is no -C<--storage> specified. - -=head1 SEE ALSO - - vzdump(1) vzrestore(1) +PVE::CLI::qmrestore->run_cli_handler();