Restore is a special case of create_vm.
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}
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/
.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 {} ';'
name => 'create_vm',
path => '',
method => 'POST',
- description => "Create new virtual machine.",
+ description => "Create or restore a virtual machine.",
protected => 1,
proxyto => 'node',
parameters => {
{
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 => {
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 {
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();
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({
}
sub create_disks {
- my ($storecfg, $vmid, $settings, $conf) = @_;
+ my ($storecfg, $vmid, $settings, $conf, $default_storage) = @_;
my $vollist = [];
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;
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;
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;
--- /dev/null
+#!/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);
+
+
#!/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