]>
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 | ||
48 | print "extracting archive '$archive'\n"; | |
49 | PVE::Tools::run_command($cmd); | |
50 | ||
51 | # is this really required? what for? | |
52 | #$cmd = [@$userns_cmd, 'mkdir', '-p', "$rootdir/dev/pts"]; | |
53 | #PVE::Tools::run_command($cmd); | |
54 | ||
55 | # template/OS specific configuration | |
56 | $conf->{'lxc.arch'} = 'i386'; #fixme: || x86_64 | |
57 | } | |
58 | ||
59 | sub restore_and_configure { | |
60 | my ($vmid, $archive, $rootdir, $conf, $password) = @_; | |
61 | ||
62 | restore_archive($archive, $rootdir, $conf); | |
63 | ||
64 | PVE::LXC::write_config($vmid, $conf); | |
65 | ||
66 | my $lxc_setup = PVE::LXCSetup->new($conf, $rootdir); # detect OS | |
67 | ||
68 | PVE::LXC::write_config($vmid, $conf); # safe config (after OS detection) | |
69 | ||
70 | $lxc_setup->post_create_hook($password); | |
71 | } | |
72 | ||
73 | # directly use a storage directory | |
74 | sub create_rootfs_dir { | |
10fc3ba5 | 75 | my ($cleanup, $storage_conf, $storage, $vmid, $conf, $archive, $password) = @_; |
5b4657d0 | 76 | |
10fc3ba5 | 77 | # note: there is no size limit |
5b4657d0 DM |
78 | |
79 | my $private = PVE::Storage::get_private_dir($storage_conf, $storage, $vmid); | |
80 | mkdir($private) || die "unable to create container private dir '$private' - $!\n"; | |
81 | ||
82 | push @{$cleanup->{files}}, $private; | |
83 | $conf->{'lxc.rootfs'} = $private; | |
84 | ||
85 | restore_and_configure($vmid, $archive, $private, $conf, $password); | |
86 | } | |
87 | ||
88 | # create a raw file, then loop mount | |
89 | sub create_rootfs_dir_loop { | |
90 | my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; | |
91 | ||
92 | my $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, 'raw', "vm-$vmid-rootfs.raw", $size); | |
93 | ||
94 | push @{$cleanup->{volids}}, $volid; | |
95 | ||
96 | my $image_path = PVE::Storage::path($storage_conf, $volid); | |
97 | $conf->{'lxc.rootfs'} = "loop:${image_path}"; | |
98 | ||
99 | my $cmd = ['mkfs.ext4', $image_path]; | |
100 | PVE::Tools::run_command($cmd); | |
101 | ||
102 | print "allocated image: $image_path\n"; | |
103 | ||
104 | my $mountpoint; | |
105 | ||
106 | my $loopdev; | |
107 | eval { | |
108 | my $parser = sub { | |
109 | my $line = shift; | |
110 | $loopdev = $line if $line =~m|^/dev/loop\d+$|; | |
111 | }; | |
112 | PVE::Tools::run_command(['losetup', '--find', '--show', $image_path], outfunc => $parser); | |
113 | ||
114 | my $tmp = "/var/lib/lxc/$vmid/rootfs"; | |
115 | File::Path::mkpath($tmp); | |
116 | PVE::Tools::run_command(['mount', '-t', 'ext4', $loopdev, $tmp]); | |
117 | $mountpoint = $tmp; | |
118 | ||
611fe3aa | 119 | $conf->{'pve.volid'} = $volid; |
5b4657d0 DM |
120 | restore_and_configure($vmid, $archive, $mountpoint, $conf, $password); |
121 | }; | |
122 | if (my $err = $@) { | |
123 | if ($mountpoint) { | |
124 | eval { PVE::Tools::run_command(['umount', '-d', $mountpoint]) }; | |
125 | warn $@ if $@; | |
126 | } else { | |
127 | eval { PVE::Tools::run_command(['losetup', '-d', $loopdev]) if $loopdev; }; | |
128 | warn $@ if $@; | |
129 | } | |
130 | die $err; | |
131 | } | |
132 | ||
133 | PVE::Tools::run_command(['umount', '-l', '-d', $mountpoint]); | |
134 | } | |
135 | ||
6ed8c6dd DM |
136 | # create a file, then mount with qemu-nbd |
137 | sub create_rootfs_dir_qemu { | |
138 | my ($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; | |
139 | ||
140 | my $format = 'qcow2'; | |
141 | ||
142 | my $volid = PVE::Storage::vdisk_alloc($storage_conf, $storage, $vmid, | |
143 | $format, "vm-$vmid-rootfs.$format", $size); | |
144 | ||
145 | push @{$cleanup->{volids}}, $volid; | |
146 | ||
147 | my $image_path = PVE::Storage::path($storage_conf, $volid); | |
148 | $conf->{'lxc.rootfs'} = "nbd:${image_path}"; | |
149 | ||
150 | print "allocated image: $image_path\n"; | |
151 | ||
152 | my $mountpoint; | |
153 | ||
154 | my $nbd_dev; | |
155 | eval { | |
156 | $nbd_dev = next_free_nbd_dev(); | |
157 | PVE::Tools::run_command(['qemu-nbd', '-c', $nbd_dev, $image_path]); | |
158 | ||
159 | my $cmd = ['mkfs.ext4', $nbd_dev]; | |
160 | PVE::Tools::run_command($cmd); | |
161 | ||
162 | ||
163 | my $tmp = "/var/lib/lxc/$vmid/rootfs"; | |
164 | File::Path::mkpath($tmp); | |
165 | PVE::Tools::run_command(['mount', '-t', 'ext4', $nbd_dev, $tmp]); | |
166 | $mountpoint = $tmp; | |
167 | ||
611fe3aa | 168 | $conf->{'pve.volid'} = $volid; |
6ed8c6dd DM |
169 | restore_and_configure($vmid, $archive, $mountpoint, $conf, $password); |
170 | }; | |
171 | if (my $err = $@) { | |
172 | if ($mountpoint) { | |
173 | eval { PVE::Tools::run_command(['umount', $mountpoint]); }; | |
174 | warn $@ if $@; | |
175 | } | |
176 | if ($nbd_dev) { | |
177 | eval { PVE::Tools::run_command(['qemu-nbd', '-d', $nbd_dev]); }; | |
178 | warn $@ if $@; | |
179 | } | |
180 | die $err; | |
181 | } | |
182 | ||
183 | PVE::Tools::run_command(['umount', $mountpoint]); | |
184 | PVE::Tools::run_command(['qemu-nbd', '-d', $nbd_dev]); | |
185 | } | |
186 | ||
5b4657d0 DM |
187 | sub create_rootfs { |
188 | my ($storage_conf, $storage, $size, $vmid, $conf, $archive, $password) = @_; | |
189 | ||
190 | PVE::LXC::create_config($vmid, $conf); | |
191 | ||
192 | my $cleanup = { files => [], volids => [] }; | |
193 | ||
194 | eval { | |
195 | my $scfg = PVE::Storage::storage_config($storage_conf, $storage); | |
196 | if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') { | |
10fc3ba5 | 197 | if ($size > 0) { |
e878090c | 198 | create_rootfs_dir_loop($cleanup, $storage_conf, $storage, $size, $vmid, $conf, $archive, $password); |
5b4657d0 | 199 | } else { |
10fc3ba5 | 200 | create_rootfs_dir($cleanup, $storage_conf, $storage, $vmid, $conf, $archive, $password); |
5b4657d0 DM |
201 | } |
202 | } else { | |
203 | die "unable to create containers on storage type '$scfg->{type}'\n"; | |
204 | } | |
205 | }; | |
206 | if (my $err = $@) { | |
207 | # cleanup | |
208 | File::Path::rmtree($cleanup->{files}); | |
209 | foreach my $volid (@{$cleanup->{volids}}) { | |
210 | eval { PVE::Storage::vdisk_free($storage_conf, $volid); }; | |
211 | warn $@ if $@; | |
212 | } | |
213 | ||
214 | PVE::LXC::destroy_config($vmid); | |
215 | ||
216 | die $err; | |
217 | } | |
218 | } | |
219 | ||
220 | 1; |