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