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