]>
Commit | Line | Data |
---|---|---|
5b4657d0 DM |
1 | package PVE::LXCCreate; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use File::Basename; | |
6 | use File::Path; | |
7 | use Data::Dumper; | |
8 | ||
9 | use PVE::Storage; | |
10 | use PVE::LXC; | |
11 | use PVE::LXCSetup; | |
12 | ||
6ed8c6dd DM |
13 | sub next_free_nbd_dev { |
14 | ||
15 | for(my $i = 0;;$i++) { | |
16 | my $dev = "/dev/nbd$i"; | |
17 | last if ! -b $dev; | |
18 | next if -f "/sys/block/nbd$i/pid"; # busy | |
19 | return $dev; | |
20 | } | |
21 | die "unable to find free nbd device\n"; | |
22 | } | |
23 | ||
5b4657d0 DM |
24 | sub restore_archive { |
25 | my ($archive, $rootdir, $conf) = @_; | |
26 | ||
27 | # we always use the same mapping: 'b:0:100000:65536' | |
28 | my $userns_cmd; | |
29 | ||
30 | if ($conf->{'lxc.id_map'}) { | |
31 | $userns_cmd = ['lxc-usernsexec', '-m', 'b:0:100000:65536', '--']; | |
32 | } else { | |
33 | $userns_cmd = []; | |
34 | } | |
35 | ||
36 | my $cmd; | |
37 | ||
38 | if ($conf->{'lxc.id_map'}) { | |
39 | PVE::Tools::run_command(['chown', '-R', '100000:100000', $rootdir]); | |
40 | } | |
41 | ||
42 | $cmd = [@$userns_cmd, 'tar', 'xpf', $archive, '--numeric-owner', '--totals', | |
43 | '--sparse', '-C', $rootdir]; | |
44 | ||
45 | push @$cmd, '--anchored'; | |
46 | push @$cmd, '--exclude' , './dev/*'; | |
47 | ||
6034ae50 DM |
48 | if ($archive eq '-') { |
49 | print "extracting archive from STDIN\n"; | |
50 | PVE::Tools::run_command($cmd, input => "<&STDIN"); | |
51 | } else { | |
52 | print "extracting archive '$archive'\n"; | |
53 | PVE::Tools::run_command($cmd); | |
54 | } | |
55 | ||
5b4657d0 DM |
56 | # is this really required? what for? |
57 | #$cmd = [@$userns_cmd, 'mkdir', '-p', "$rootdir/dev/pts"]; | |
58 | #PVE::Tools::run_command($cmd); | |
59 | ||
a9d131df TL |
60 | #determine file type of /usr/bin/file itself to get guests' architecture |
61 | $cmd = [@$userns_cmd, '/usr/bin/file', '-b', '-L', "$rootdir/usr/bin/file"]; | |
62 | PVE::Tools::run_command($cmd, outfunc => sub { | |
63 | shift =~ /^ELF (\d{2}-bit)/; # safely assumes x86 linux | |
64 | my $arch_str = $1; | |
65 | $conf->{'lxc.arch'} = 'amd64'; # defaults to 64bit | |
66 | if(defined($arch_str)) { | |
67 | $conf->{'lxc.arch'} = 'i386' if $arch_str =~ /32/; | |
68 | print "Detected container architecture: $conf->{'lxc.arch'}\n"; | |
69 | } else { | |
70 | print "CT architecture detection failed, falling back to amd64.\n". | |
71 | "Edit the config in /etc/pve/nodes/{node}/lxc/{vmid}/config". | |
72 | " to set another arch.\n"; | |
73 | } | |
74 | }); | |
5b4657d0 DM |
75 | } |
76 | ||
77 | sub restore_and_configure { | |
78 | my ($vmid, $archive, $rootdir, $conf, $password) = @_; | |
79 | ||
80 | restore_archive($archive, $rootdir, $conf); | |
81 | ||
82 | PVE::LXC::write_config($vmid, $conf); | |
83 | ||
84 | my $lxc_setup = PVE::LXCSetup->new($conf, $rootdir); # detect OS | |
85 | ||
86 | PVE::LXC::write_config($vmid, $conf); # safe config (after OS detection) | |
87 | ||
88 | $lxc_setup->post_create_hook($password); | |
89 | } | |
90 | ||
91 | # directly use a storage directory | |
92 | sub create_rootfs_dir { | |
10fc3ba5 | 93 | my ($cleanup, $storage_conf, $storage, $vmid, $conf, $archive, $password) = @_; |
5b4657d0 | 94 | |
10fc3ba5 | 95 | # note: there is no size limit |
148d1cb4 | 96 | $conf->{'pve.disksize'} = 0; |
5b4657d0 DM |
97 | |
98 | my $private = PVE::Storage::get_private_dir($storage_conf, $storage, $vmid); | |
99 | mkdir($private) || die "unable to create container private dir '$private' - $!\n"; | |
100 | ||
101 | push @{$cleanup->{files}}, $private; | |
102 | $conf->{'lxc.rootfs'} = $private; | |
103 | ||
104 | restore_and_configure($vmid, $archive, $private, $conf, $password); | |
105 | } | |
106 | ||
ad09aa15 DM |
107 | # use new subvolume API |
108 | sub create_rootfs_subvol { | |
109 | my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; | |
110 | ||
111 | my $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'subvol', | |
112 | "subvol-$vmid-rootfs", $size); | |
113 | push @{$cleanup->{volids}}, $volid; | |
114 | ||
115 | my $private = PVE::Storage::path($storage_conf, $volid); | |
116 | (-d $private) || die "unable to get container private dir '$private' - $!\n"; | |
117 | ||
118 | $conf->{'lxc.rootfs'} = $private; | |
119 | $conf->{'pve.volid'} = $volid; | |
120 | ||
121 | restore_and_configure($vmid, $archive, $private, $conf, $password); | |
122 | } | |
123 | ||
5b4657d0 DM |
124 | # create a raw file, then loop mount |
125 | sub create_rootfs_dir_loop { | |
126 | my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; | |
127 | ||
128 | my $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'raw', "vm-$vmid-rootfs.raw", $size); | |
148d1cb4 | 129 | $conf->{'pve.disksize'} = $size/(1024*1024); |
5b4657d0 DM |
130 | |
131 | push @{$cleanup->{volids}}, $volid; | |
132 | ||
133 | my $image_path = PVE::Storage::path($storage_conf, $volid); | |
134 | $conf->{'lxc.rootfs'} = "loop:${image_path}"; | |
135 | ||
136 | my $cmd = ['mkfs.ext4', $image_path]; | |
137 | PVE::Tools::run_command($cmd); | |
138 | ||
139 | print "allocated image: $image_path\n"; | |
140 | ||
141 | my $mountpoint; | |
142 | ||
143 | my $loopdev; | |
144 | eval { | |
145 | my $parser = sub { | |
146 | my $line = shift; | |
147 | $loopdev = $line if $line =~m|^/dev/loop\d+$|; | |
148 | }; | |
149 | PVE::Tools::run_command(['losetup', '--find', '--show', $image_path], outfunc => $parser); | |
150 | ||
151 | my $tmp = "/var/lib/lxc/$vmid/rootfs"; | |
152 | File::Path::mkpath($tmp); | |
153 | PVE::Tools::run_command(['mount', '-t', 'ext4', $loopdev, $tmp]); | |
154 | $mountpoint = $tmp; | |
155 | ||
611fe3aa | 156 | $conf->{'pve.volid'} = $volid; |
5b4657d0 DM |
157 | restore_and_configure($vmid, $archive, $mountpoint, $conf, $password); |
158 | }; | |
159 | if (my $err = $@) { | |
160 | if ($mountpoint) { | |
161 | eval { PVE::Tools::run_command(['umount', '-d', $mountpoint]) }; | |
162 | warn $@ if $@; | |
163 | } else { | |
164 | eval { PVE::Tools::run_command(['losetup', '-d', $loopdev]) if $loopdev; }; | |
165 | warn $@ if $@; | |
166 | } | |
167 | die $err; | |
168 | } | |
169 | ||
170 | PVE::Tools::run_command(['umount', '-l', '-d', $mountpoint]); | |
171 | } | |
172 | ||
6ed8c6dd DM |
173 | # create a file, then mount with qemu-nbd |
174 | sub create_rootfs_dir_qemu { | |
175 | my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; | |
176 | ||
177 | my $format = 'qcow2'; | |
178 | ||
179 | my $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, | |
180 | $format, "vm-$vmid-rootfs.$format", $size); | |
181 | ||
148d1cb4 DM |
182 | $conf->{'pve.disksize'} = $size/(1024*1024); |
183 | ||
6ed8c6dd DM |
184 | push @{$cleanup->{volids}}, $volid; |
185 | ||
186 | my $image_path = PVE::Storage::path($storage_conf, $volid); | |
187 | $conf->{'lxc.rootfs'} = "nbd:${image_path}"; | |
188 | ||
189 | print "allocated image: $image_path\n"; | |
190 | ||
191 | my $mountpoint; | |
192 | ||
193 | my $nbd_dev; | |
194 | eval { | |
195 | $nbd_dev = next_free_nbd_dev(); | |
196 | PVE::Tools::run_command(['qemu-nbd', '-c', $nbd_dev, $image_path]); | |
197 | ||
198 | my $cmd = ['mkfs.ext4', $nbd_dev]; | |
199 | PVE::Tools::run_command($cmd); | |
200 | ||
201 | ||
202 | my $tmp = "/var/lib/lxc/$vmid/rootfs"; | |
203 | File::Path::mkpath($tmp); | |
204 | PVE::Tools::run_command(['mount', '-t', 'ext4', $nbd_dev, $tmp]); | |
205 | $mountpoint = $tmp; | |
206 | ||
611fe3aa | 207 | $conf->{'pve.volid'} = $volid; |
6ed8c6dd DM |
208 | restore_and_configure($vmid, $archive, $mountpoint, $conf, $password); |
209 | }; | |
210 | if (my $err = $@) { | |
211 | if ($mountpoint) { | |
212 | eval { PVE::Tools::run_command(['umount', $mountpoint]); }; | |
213 | warn $@ if $@; | |
214 | } | |
215 | if ($nbd_dev) { | |
216 | eval { PVE::Tools::run_command(['qemu-nbd', '-d', $nbd_dev]); }; | |
217 | warn $@ if $@; | |
218 | } | |
219 | die $err; | |
220 | } | |
221 | ||
222 | PVE::Tools::run_command(['umount', $mountpoint]); | |
223 | PVE::Tools::run_command(['qemu-nbd', '-d', $nbd_dev]); | |
224 | } | |
225 | ||
5b4657d0 | 226 | sub create_rootfs { |
148d1cb4 DM |
227 | my ($storage_conf, $storage, $disk_size_gb, $vmid, $conf, $archive, $password, $restore) = @_; |
228 | ||
229 | my $config_fn = PVE::LXC::config_file($vmid); | |
230 | if (-f $config_fn) { | |
231 | die "container exists" if !$restore; # just to be sure | |
232 | ||
233 | my $old_conf = PVE::LXC::load_config($vmid); | |
234 | ||
235 | if (!defined($disk_size_gb) && defined($old_conf->{'pve.disksize'})) { | |
236 | $disk_size_gb = $old_conf->{'pve.disksize'}; | |
237 | } | |
f09ce711 DM |
238 | |
239 | # we only copy known settings to restored container | |
240 | my $pve_conf = PVE::LXC::lxc_conf_to_pve($vmid, $old_conf); | |
241 | foreach my $opt (qw(disk digest)) { | |
242 | delete $pve_conf->{$opt}; | |
243 | } | |
244 | update_lxc_config($vmid, $conf, 0, $pve_conf); | |
245 | ||
148d1cb4 DM |
246 | # destroy old container |
247 | PVE::LXC::destory_lxc_container($storage_conf, $vmid, $old_conf); | |
5b4657d0 | 248 | |
148d1cb4 | 249 | PVE::LXC::create_config($vmid, $conf); |
5b4657d0 | 250 | |
148d1cb4 DM |
251 | } else { |
252 | ||
253 | PVE::LXC::create_config($vmid, $conf); | |
254 | } | |
255 | ||
256 | my $size = 4*1024*1024; # defaults to 4G | |
257 | ||
258 | $size = int($disk_size_gb*1024) * 1024 if defined($disk_size_gb); | |
259 | ||
5b4657d0 DM |
260 | my $cleanup = { files => [], volids => [] }; |
261 | ||
262 | eval { | |
263 | my $scfg = PVE::Storage::storage_config($storage_conf, $storage); | |
264 | if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') { | |
10fc3ba5 | 265 | if ($size > 0) { |
e878090c | 266 | create_rootfs_dir_loop($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password); |
5b4657d0 | 267 | } else { |
10fc3ba5 | 268 | create_rootfs_dir($cleanup, $storage_conf, $storage, $vmid, $conf, $archive, $password); |
5b4657d0 | 269 | } |
ad09aa15 DM |
270 | } elsif ($scfg->{type} eq 'zfspool') { |
271 | ||
272 | create_rootfs_subvol($cleanup, $storage_conf, $storage, $size, | |
273 | $vmid, $conf, $archive, $password); | |
274 | ||
5b4657d0 | 275 | } else { |
ad09aa15 | 276 | |
5b4657d0 DM |
277 | die "unable to create containers on storage type '$scfg->{type}'\n"; |
278 | } | |
279 | }; | |
280 | if (my $err = $@) { | |
281 | # cleanup | |
282 | File::Path::rmtree($cleanup->{files}); | |
283 | foreach my $volid (@{$cleanup->{volids}}) { | |
284 | eval { PVE::Storage::vdisk_free($storage_conf, $volid); }; | |
285 | warn $@ if $@; | |
286 | } | |
287 | ||
288 | PVE::LXC::destroy_config($vmid); | |
289 | ||
290 | die $err; | |
291 | } | |
292 | } | |
293 | ||
294 | 1; |