]> git.proxmox.com Git - qemu-server.git/blob - qmextract
implement qmrestore
[qemu-server.git] / qmextract
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Getopt::Long;
5 use File::Path;
6 use IO::File;
7 use PVE::JSONSchema;
8 use PVE::Tools;
9 use PVE::Cluster qw(cfs_read_file);
10 use PVE::QemuServer;
11
12 my @std_opts = ('storage=s', 'info', 'prealloc');
13
14 sub print_usage {
15 print STDERR "usage: $0 [--storage=<storeid>] [--info] [--prealloc] <archive> <vmid>\n\n";
16 }
17
18 my $opts = {};
19 if (!GetOptions ($opts, @std_opts)) {
20 print_usage ();
21 exit (-1);
22 }
23
24 sub extract_archive {
25 # NOTE: this is run as tar subprocess (--to-command)
26
27 $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
28 die "interrupted by signal\n";
29 };
30
31 my $filename = $ENV{TAR_FILENAME};
32 die "got strange environment - no TAR_FILENAME\n" if !$filename;
33
34 my $filesize = $ENV{TAR_SIZE};
35 die "got strange file size '$filesize'\n" if !$filesize;
36
37 my $tmpdir = $ENV{VZDUMP_TMPDIR};
38 die "got strange environment - no VZDUMP_TMPDIR\n" if !$tmpdir;
39
40 my $filetype = $ENV{TAR_FILETYPE} || 'none';
41 die "got strange filetype '$filetype'\n" if $filetype ne 'f';
42
43 my $vmid = $ENV{VZDUMP_VMID};
44 PVE::JSONSchema::pve_verify_vmid($vmid);
45
46 if ($opts->{info}) {
47 print STDERR "reading archive member '$filename'\n";
48 } else {
49 print STDERR "extracting '$filename' from archive\n";
50 }
51
52 my $conffile = "$tmpdir/qemu-server.conf";
53 my $statfile = "$tmpdir/qmrestore.stat";
54
55 if ($filename eq 'qemu-server.conf') {
56 my $outfd = IO::File->new($conffile, "w") ||
57 die "unable to write file '$conffile'\n";
58
59 while (defined(my $line = <>)) {
60 print $outfd $line;
61 print STDERR "CONFIG: $line" if $opts->{info};
62 }
63
64 $outfd->close();
65
66 exit(0);
67 }
68
69 if ($opts->{info}) {
70 exec 'dd', 'bs=256K', "of=/dev/null";
71 die "couldn't exec dd: $!\n";
72 }
73
74 my $conffd = IO::File->new($conffile, "r") ||
75 die "unable to read file '$conffile'\n";
76
77 my $map;
78 while (defined(my $line = <$conffd>)) {
79 if ($line =~ m/^\#vzdump\#map:(\S+):(\S+):(\d+):(\S*):$/) {
80 $map->{$2} = { virtdev => $1, size => $3, storeid => $4 };
81 }
82 }
83 close($conffd);
84
85 my $statfd = IO::File->new($statfile, "a") ||
86 die "unable to open file '$statfile'\n";
87
88 if ($filename !~ m/^.*\.([^\.]+)$/){
89 die "got strange filename '$filename'\n";
90 }
91 my $format = $1;
92
93 my $path;
94
95 if (!$map) {
96 print STDERR "restoring old style vzdump archive - " .
97 "no device map inside archive\n";
98 die "can't restore old style archive to storage '$opts->{storage}'\n"
99 if $opts->{storage} && $opts->{storage} ne 'local';
100
101 my $dir = "/var/lib/vz/images/$vmid";
102 mkpath $dir;
103
104 $path = "$dir/$filename";
105
106 print $statfd "vzdump::$path\n";
107 $statfd->close();
108
109 } else {
110
111 my $info = $map->{$filename};
112 die "no vzdump info for '$filename'\n" if !$info;
113
114 if ($filename !~ m/^vm-disk-$info->{virtdev}\.([^\.]+)$/){
115 die "got strange filename '$filename'\n";
116 }
117
118 if ($filesize != $info->{size}) {
119 die "detected size difference for '$filename' " .
120 "($filesize != $info->{size})\n";
121 }
122
123 my $storeid;
124 if ($opts->{storage}) {
125 $storeid = $opts->{storage};
126 } else {
127 $storeid = $info->{storeid} || 'local';
128 }
129
130 my $cfg = cfs_read_file('storage.cfg');
131 my $scfg = PVE::Storage::storage_config($cfg, $storeid);
132
133 my $alloc_size = ($filesize + 1024 - 1)/1024;
134 if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
135 # hack: we just alloc a small file (32K) - we overwrite it anyways
136 $alloc_size = 32;
137 } else {
138 die "unable to restore '$filename' to storage '$storeid'\n" .
139 "storage type '$scfg->{type}' does not support format '$format\n"
140 if $format ne 'raw';
141 }
142
143 my $volid = PVE::Storage::vdisk_alloc($cfg, $storeid, $vmid,
144 $format, undef, $alloc_size);
145
146 print STDERR "new volume ID is '$volid'\n";
147
148 print $statfd "vzdump:$info->{virtdev}:$volid\n";
149 $statfd->close();
150
151 $path = PVE::Storage::path($cfg, $volid);
152 }
153
154 print STDERR "restore data to '$path' ($filesize bytes)\n";
155
156 if ($opts->{prealloc} || $format ne 'raw' || (-b $path)) {
157 exec 'dd', 'ibs=256K', 'obs=256K', "of=$path";
158 die "couldn't exec dd: $!\n";
159 } else {
160 exec '/usr/lib/qemu-server/sparsecp', $path;
161 die "couldn't exec sparsecp: $!\n";
162 }
163 }
164
165
166 if (scalar(@ARGV) == 2) {
167 my $archive = shift;
168 my $vmid = shift;
169
170 # fixme: use API call
171 PVE::JSONSchema::pve_verify_vmid($vmid);
172
173 PVE::Cluster::check_cfs_quorum();
174
175 PVE::QemuServer::restore_archive($archive, $vmid, $opts);
176
177 } elsif (scalar(@ARGV) == 0 && $ENV{TAR_FILENAME}) {
178 extract_archive();
179 } else {
180 print_usage ();
181 exit(-1);
182 }
183
184 exit(0);
185
186