]> git.proxmox.com Git - qemu-server.git/commitdiff
implement qmrestore
authorDietmar Maurer <dietmar@proxmox.com>
Mon, 17 Oct 2011 11:49:48 +0000 (13:49 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Mon, 17 Oct 2011 11:51:05 +0000 (13:51 +0200)
Restore is a special case of create_vm.

Makefile
PVE/API2/Qemu.pm
PVE/QemuServer.pm
PVE/VZDump/QemuServer.pm
qmextract [new file with mode: 0755]
qmrestore

index 6217a8e6f953dd900b4ef02a005614895ef8c9dc..1638f1d2b00040107d59b04d9c469410fba4f652 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -54,10 +54,13 @@ sparsecp: sparsecp.c utils.c
 qm.1.pod: qm PVE/QemuServer.pm
        perl -I. ./qm printmanpod >$@
 
+qmrestore.1.pod: qmrestore
+       perl -I. ./qmrestore printmanpod >$@
+
 vm.conf.5.pod: gen-vmconf-pod.pl PVE/QemuServer.pm 
        perl -I. ./gen-vmconf-pod.pl >$@
 
-PKGSOURCES=qm qm.1.gz qm.1.pod qmrestore qmrestore.1.gz sparsecp vmtar qemu.init.d qmupdate control vm.conf.5.pod vm.conf.5.gz
+PKGSOURCES=qm qm.1.gz qm.1.pod qmrestore qmrestore.1.pod qmrestore.1.gz qmextract sparsecp vmtar qemu.init.d qmupdate control vm.conf.5.pod vm.conf.5.gz
 
 .PHONY: install
 install: ${PKGSOURCES}
@@ -78,9 +81,11 @@ install: ${PKGSOURCES}
        install -m 0755 pve-bridge ${DESTDIR}${VARLIBDIR}/pve-bridge
        install -s -m 0755 vmtar ${DESTDIR}${LIBDIR}
        install -s -m 0755 sparsecp ${DESTDIR}${LIBDIR}
+       install -m 0755 qmextract ${DESTDIR}${LIBDIR}
        install -m 0644 qm.1.gz ${DESTDIR}/usr/share/man/man1/
        install -m 0644 qm.1.pod ${DESTDIR}/${PODDIR}
        install -m 0644 qmrestore.1.gz ${DESTDIR}/usr/share/man/man1/
+       install -m 0644 qmrestore.1.pod ${DESTDIR}/${PODDIR}
        install -m 0644 vm.conf.5.pod ${DESTDIR}/${PODDIR}
        install -m 0644 vm.conf.5.gz ${DESTDIR}/usr/share/man/man5/
 
@@ -114,7 +119,7 @@ upload:
 
 .PHONY: clean
 clean:         
-       rm -rf build *.deb qm.1.gz control vzsyscalls.ph _h2ph_pre.ph ${PACKAGE}-*.tar.gz dist *.1,gz *.pod vmtar sparsecp
+       rm -rf build *.deb control vzsyscalls.ph _h2ph_pre.ph ${PACKAGE}-*.tar.gz dist *.1.gz *.pod vmtar sparsecp
        find . -name '*~' -exec rm {} ';'
 
 
index c6e8ea0c8606307331cff475eb390cfbe730aa83..aedfb89b41d71baf43398508083c25745ae6e82e 100644 (file)
@@ -66,7 +66,7 @@ __PACKAGE__->register_method({
     name => 'create_vm', 
     path => '', 
     method => 'POST',
-    description => "Create new virtual machine.",
+    description => "Create or restore a virtual machine.",
     protected => 1,
     proxyto => 'node',
     parameters => {
@@ -75,6 +75,21 @@ __PACKAGE__->register_method({
            {
                node => get_standard_option('pve-node'),
                vmid => get_standard_option('pve-vmid'),
+               archive => {
+                   description => "The backup file.",
+                   type => 'string',
+                   optional => 1,
+                   maxLength => 255,
+               },
+               storage => get_standard_option('pve-storage-id', {
+                   description => "Default storage.",
+                   optional => 1,
+               }),
+               force => {
+                   optional => 1, 
+                   type => 'boolean',
+                   description => "Allow to overwrite existing VM.",
+               },
            }),
     },
     returns => { 
@@ -91,26 +106,50 @@ __PACKAGE__->register_method({
 
        my $vmid = extract_param($param, 'vmid');
 
+       my $archive = extract_param($param, 'archive');
+
+       my $storage = extract_param($param, 'storage');
+
        my $filename = PVE::QemuServer::config_file($vmid);
-       # first test (befor locking)
-       die "unable to create vm $vmid: config file already exists\n" 
-           if -f $filename;
        
        my $storecfg = PVE::Storage::config(); 
 
-       &$resolve_cdrom_alias($param);
+       PVE::Cluster::check_cfs_quorum();
 
-       foreach my $opt (keys %$param) {
-           if (PVE::QemuServer::valid_drivename($opt)) {
-               my $drive = PVE::QemuServer::parse_drive($opt, $param->{$opt});
-               raise_param_exc({ $opt => "unable to parse drive options" }) if !$drive;
+       if (!$archive) { 
+           &$resolve_cdrom_alias($param);
 
-               PVE::QemuServer::cleanup_drive_path($opt, $storecfg, $drive);
-               $param->{$opt} = PVE::QemuServer::print_drive($vmid, $drive);
+           foreach my $opt (keys %$param) {
+               if (PVE::QemuServer::valid_drivename($opt)) {
+                   my $drive = PVE::QemuServer::parse_drive($opt, $param->{$opt});
+                   raise_param_exc({ $opt => "unable to parse drive options" }) if !$drive;
+                   
+                   PVE::QemuServer::cleanup_drive_path($opt, $storecfg, $drive);
+                   $param->{$opt} = PVE::QemuServer::print_drive($vmid, $drive);
+               }
            }
+
+           PVE::QemuServer::add_random_macs($param);
        }
 
-       PVE::QemuServer::add_random_macs($param);
+       # fixme: archive eq '-' (read from stdin)
+
+       my $restorefn = sub {
+
+           if (-f $filename) {
+               die "unable to restore vm $vmid: config file already exists\n" 
+                   if !$param->{force};
+
+               die "unable to restore vm $vmid: vm is running\n" 
+                   if PVE::QemuServer::check_running($vmid);
+           }
+
+           my $realcmd = sub {
+               PVE::QemuServer::restore_archive($archive, $vmid, { storage => $storage});
+           };
+
+           return $rpcenv->fork_worker('qmrestore', $vmid, $user, $realcmd);
+       };
 
        my $createfn = sub {
 
@@ -123,7 +162,7 @@ __PACKAGE__->register_method({
                my $vollist = [];
 
                eval {
-                   $vollist = PVE::QemuServer::create_disks($storecfg, $vmid, $param);
+                   $vollist = PVE::QemuServer::create_disks($storecfg, $vmid, $param, $storage);
 
                    # try to be smart about bootdisk
                    my @disks = PVE::QemuServer::disknames();
@@ -155,7 +194,7 @@ __PACKAGE__->register_method({
            return $rpcenv->fork_worker('qmcreate', $vmid, $user, $realcmd);
        };
 
-       return PVE::QemuServer::lock_config($vmid, $createfn);
+       return PVE::QemuServer::lock_config($vmid, $archive ? $restorefn : $createfn);
     }});
 
 __PACKAGE__->register_method({
index 4773ec94911b006735ab609ab500c5f4ee74c0c0..48c6cba6dfcaebf70193c56d37d1cb13d91ed1be 100644 (file)
@@ -1265,7 +1265,7 @@ sub touch_config {
 }
 
 sub create_disks {
-    my ($storecfg, $vmid, $settings, $conf) = @_;
+    my ($storecfg, $vmid, $settings, $conf, $default_storage) = @_;
 
     my $vollist = [];
 
@@ -1278,7 +1278,7 @@ sub create_disks {
            my $file = $disk->{file};
 
            if ($file =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
-               my $storeid = $2 || 'local';
+               my $storeid = $2 || $default_storage;
                my $size = $3;
                my $defformat = PVE::Storage::storage_default_format($storecfg, $storeid);
                my $fmt = $disk->{format} || $defformat;
@@ -2918,4 +2918,161 @@ sub vm_balloonset {
     vm_monitor_command($vmid, "balloon $value", 1);
 }
 
+# vzdump restore implementaion
+
+sub archive_read_firstfile {
+    my $archive = shift;
+    
+    die "ERROR: file '$archive' does not exist\n" if ! -f $archive;
+
+    # try to detect archive type first
+    my $pid = open (TMP, "tar tf '$archive'|") ||
+       die "unable to open file '$archive'\n";
+    my $firstfile = <TMP>;
+    kill 15, $pid;
+    close TMP;
+
+    die "ERROR: archive contaions no data\n" if !$firstfile;
+    chomp $firstfile;
+
+    return $firstfile;
+}
+
+sub restore_cleanup {
+    my $statfile = shift;
+
+    print STDERR "starting cleanup\n";
+
+    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 = cfs_read_file('storage.cfg');
+                       PVE::Storage::vdisk_free($cfg, $volid);
+                   }
+                   print STDERR "temporary volume '$volid' sucessfuly removed\n";  
+               };
+               print STDERR "unable to cleanup '$volid' - $@" if $@;
+           } else {
+               print STDERR "unable to parse line in statfile - $line";
+           }   
+       }
+       $fd->close();
+    }
+}
+
+sub restore_archive {
+    my ($archive, $vmid, $opts) = @_;
+
+    my $firstfile = archive_read_firstfile($archive);
+    die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"
+       if $firstfile ne 'qemu-server.conf';
+
+    my $tocmd = "/usr/lib/qemu-server/qmextract";
+
+    $tocmd .= " --storage $opts->{storage}" if $opts->{storage};
+    $tocmd .= ' --prealloc' if $opts->{prealloc};
+    $tocmd .= ' --info' if $opts->{info};
+
+    my $cmd = ['tar', 'xf', $archive, '--to-command', $tocmd];
+
+    my $tmpdir = "/var/tmp/vzdumptmp$$";
+    mkpath $tmpdir;
+
+    local $ENV{VZDUMP_TMPDIR} = $tmpdir;
+    local $ENV{VZDUMP_VMID} = $vmid;
+
+    my $conffile = PVE::QemuServer::config_file($vmid);
+    my $tmpfn = "$conffile.$$.tmp";
+
+    # disable interrupts (always do cleanups)
+    local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
+       print STDERR "got interrupt - ignored\n";
+    };
+
+    eval { 
+       # enable interrupts
+       local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
+           die "interrupted by signal\n";
+       };
+
+       PVE::Tools::run_command($cmd); 
+
+       return if $opts->{info};
+
+       # read 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 {
+                   print STDERR "unable to parse line in statfile - $line\n";
+               }
+           }
+           $fd->close();
+       }
+
+       my $confsrc = "$tmpdir/qemu-server.conf";
+
+       my $srcfd = new IO::File($confsrc, "r") ||
+           die "unable to open file '$confsrc'\n";
+
+       my $outfd = new IO::File ($tmpfn, "w") ||
+           die "unable to write config for VM $vmid\n";
+
+       while (defined (my $line = <$srcfd>)) {
+           next if $line =~ m/^\#vzdump\#/;
+           next if $line =~ m/^lock:/;
+           next if $line =~ m/^unused\d+:/;
+
+           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;
+           }
+       }
+
+       $srcfd->close();
+       $outfd->close();
+    };
+    my $err = $@;
+
+    if ($err) {        
+
+       unlink $tmpfn;
+
+       restore_cleanup("$tmpdir/qmrestore.stat") if !$opts->{info};
+       
+       die $err;
+    } 
+
+    rmtree $tmpdir;
+
+    rename $tmpfn, $conffile ||
+       die "unable to commit configuration file '$conffile'\n";
+};
+
 1;
index d2eed8cdb94d0e1e373d723fe075ecbc41fd1531..3f26ef81f4806da3d4e5c5187960e95ccddf38f4 100644 (file)
@@ -6,7 +6,8 @@ use File::Path;
 use File::Basename;
 use PVE::INotify;
 use PVE::VZDump;
-use PVE::Cluster;
+use PVE::Cluster qw(cfs_read_file);
+use PVE::Tools;
 use PVE::Storage;
 use PVE::QemuServer;
 use IO::File;
diff --git a/qmextract b/qmextract
new file mode 100755 (executable)
index 0000000..b5d3db2
--- /dev/null
+++ b/qmextract
@@ -0,0 +1,186 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Long;
+use File::Path;
+use IO::File;
+use PVE::JSONSchema;
+use PVE::Tools;
+use PVE::Cluster qw(cfs_read_file);
+use PVE::QemuServer;
+
+my @std_opts = ('storage=s', 'info', 'prealloc');
+
+sub print_usage {
+    print STDERR "usage: $0 [--storage=<storeid>] [--info] [--prealloc] <archive> <vmid>\n\n";
+}
+
+my $opts = {};
+if (!GetOptions ($opts, @std_opts)) {
+    print_usage ();
+    exit (-1);
+}
+
+sub extract_archive {
+    # NOTE: this is run as tar subprocess (--to-command)
+
+    $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
+       die "interrupted by signal\n";
+    };
+
+    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 $vmid = $ENV{VZDUMP_VMID};
+    PVE::JSONSchema::pve_verify_vmid($vmid);
+
+    if ($opts->{info}) {
+       print STDERR "reading archive member '$filename'\n";
+    } else {
+       print STDERR "extracting '$filename' from archive\n";
+    }
+
+    my $conffile = "$tmpdir/qemu-server.conf";
+    my $statfile = "$tmpdir/qmrestore.stat";
+
+    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 = cfs_read_file('storage.cfg');
+       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', 'ibs=256K', 'obs=256K', "of=$path";
+       die "couldn't exec dd: $!\n";
+    } else {
+       exec '/usr/lib/qemu-server/sparsecp', $path;
+       die "couldn't exec sparsecp: $!\n";
+    }
+}
+
+
+if (scalar(@ARGV) == 2) {
+    my $archive = shift;
+    my $vmid = shift;
+
+    # fixme: use API call
+    PVE::JSONSchema::pve_verify_vmid($vmid);
+
+    PVE::Cluster::check_cfs_quorum();
+
+    PVE::QemuServer::restore_archive($archive, $vmid, $opts);
+} elsif (scalar(@ARGV) == 0 && $ENV{TAR_FILENAME}) {
+    extract_archive();    
+} else {
+    print_usage ();
+    exit(-1);
+} 
+
+exit(0);
+
+
index a27571fd84b7f63dbe4e42d5c5d4821dfe4bbbca..f256b39fa109f752ea55c27e19bd703650123541 100755 (executable)
--- a/qmrestore
+++ b/qmrestore
 #!/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 <dietmar@proxmox.com>
-#
 
 use strict;
-use Getopt::Long;
-use Sys::Syslog;
-use File::Path;
-use PVE::VZDump;
-use PVE::Storage;
-
-$ENV{LANG} = "C"; # avoid locale related issues/warnings
-
-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] <ARCHIVE> <VMID>\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', 'ibs=256K', 'obs=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);
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::INotify;
+use PVE::RPCEnvironment;
+use PVE::CLIHandler;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::API2::Qemu;
+
+use Data::Dumper; # fixme: remove
+
+use base qw(PVE::CLIHandler);
+
+$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
+
+initlog('qmrestore');
+
+die "please run as root\n" if $> != 0;
+
+PVE::INotify::inotify_init();
+
+my $rpcenv = PVE::RPCEnvironment->init('cli');
+
+$rpcenv->init_request();
+$rpcenv->set_language($ENV{LANG});
+$rpcenv->set_user('root@pam'); 
+
+__PACKAGE__->register_method({
+    name => 'qmrestore', 
+    path => 'qmrestore', 
+    method => 'POST',
+    description => "Restore QemuServer vzdump backups.",
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           vmid => get_standard_option('pve-vmid'),
+           archive => {
+               description => "The backup file.",
+               type => 'string', 
+               maxLength => 255,
+           },
+           storage => get_standard_option('pve-storage-id', {
+               description => "Default storage.",
+               optional => 1,
+           }),
+           force => {
+               optional => 1, 
+               type => 'boolean',
+               description => "Allow to overwrite existing VM.",
+           },
+       },
+    },
+    returns => { 
+       type => 'string',
+    },
+    code => sub {
+       my ($param) = @_;
+
+       $param->{node} = PVE::INotify::nodename();
+
+       return PVE::API2::Qemu->create_vm($param);
+    }});    
+
+my $cmddef = [ __PACKAGE__, 'qmrestore', ['archive', 'vmid'], undef, 
+              sub {
+                  my $upid = shift;
+                  my $status = PVE::Tools::upid_read_status($upid);
+                  exit($status eq 'OK' ? 0 : -1);
+              }];
+
+push @ARGV, 'help' if !scalar(@ARGV);
+
+PVE::CLIHandler::handle_simple_cmd($cmddef, \@ARGV, undef, $0);
+
+exit 0;
 
 __END__
 
 =head1 NAME
-                                          
+
 qmrestore - restore QemuServer vzdump backups
 
 =head1 SYNOPSIS
 
-qmrestore [OPTIONS] <archive> <VMID>
-
- --info                    read/verify archive and print relevant 
-                           information (test run)
-
- --unique                 assign a unique random ethernet address
-
- --storage <STORAGE_ID>    restore to storage <STORAGE_ID>
-
- --prealloc                never generate sparse files
+=include synopsis
 
 =head1 DESCRIPTION
 
-Restore the QemuServer vzdump backup <archive> to virtual machine
-<VMID>. Volumes are allocated on the original storage if there is no
-C<--storage> specified.
+Restore the QemuServer vzdump backup C<archive> to virtual machine
+C<vmid>. Volumes are allocated on the original storage if there is no
+C<storage> specified.
 
 =head1 SEE ALSO
 
-    vzdump(1) vzrestore(1)
+vzdump(1) vzrestore(1)
+
+=include pve_copyright