]>
Commit | Line | Data |
---|---|---|
d1458ac8 SH |
1 | #!/bin/bash |
2 | ||
3eecde70 | 3 | # template script for generating ubuntu container for LXC based on released cloud |
d1458ac8 SH |
4 | # images |
5 | # | |
6 | # Copyright © 2012 Serge Hallyn <serge.hallyn@canonical.com> | |
7 | # | |
8 | # This program is free software; you can redistribute it and/or modify | |
9 | # it under the terms of the GNU General Public License version 2, as | |
10 | # published by the Free Software Foundation. | |
11 | ||
12 | # This program is distributed in the hope that it will be useful, | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | # GNU General Public License for more details. | |
16 | ||
17 | # You should have received a copy of the GNU General Public License along | |
18 | # with this program; if not, write to the Free Software Foundation, Inc., | |
19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
20 | # | |
21 | ||
22 | set -e | |
23 | ||
24 | if [ -r /etc/default/lxc ]; then | |
25 | . /etc/default/lxc | |
26 | fi | |
27 | ||
28 | copy_configuration() | |
29 | { | |
30 | path=$1 | |
31 | rootfs=$2 | |
32 | name=$3 | |
33 | arch=$4 | |
42ff5f0f | 34 | release=$5 |
d1458ac8 SH |
35 | |
36 | if [ $arch = "i386" ]; then | |
37 | arch="i686" | |
38 | fi | |
39 | ||
4759162d SH |
40 | # if there is exactly one veth network entry, make sure it has an |
41 | # associated hwaddr. | |
42 | nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l` | |
43 | if [ $nics -eq 1 ]; then | |
daaf41b3 | 44 | grep -q "^lxc.network.hwaddr" $path/config || sed -i -e "/^lxc\.network\.type[ \t]*=[ \t]*veth/a lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config |
4759162d SH |
45 | fi |
46 | ||
1881820a | 47 | grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config |
d1458ac8 | 48 | cat <<EOF >> $path/config |
daaf41b3 SG |
49 | lxc.mount = $path/fstab |
50 | lxc.pivotdir = lxc_putold | |
d1458ac8 | 51 | |
daaf41b3 | 52 | lxc.devttydir =$ttydir |
d1458ac8 SH |
53 | lxc.tty = 4 |
54 | lxc.pts = 1024 | |
daaf41b3 SG |
55 | |
56 | lxc.utsname = $name | |
d1458ac8 | 57 | lxc.arch = $arch |
eee3ba81 | 58 | lxc.cap.drop = sys_module mac_admin mac_override sys_time |
d1458ac8 | 59 | |
f02ce27d SG |
60 | # When using LXC with apparmor, uncomment the next line to run unconfined: |
61 | #lxc.aa_profile = unconfined | |
9313e1e6 | 62 | |
b85ab798 SH |
63 | # To support container nesting on an Ubuntu host, uncomment next two lines: |
64 | #lxc.aa_profile = lxc-container-default-with-nesting | |
65 | #lxc.hook.mount = /usr/share/lxc/hooks/mountcgroups | |
f02ce27d | 66 | |
d1458ac8 SH |
67 | lxc.cgroup.devices.deny = a |
68 | # Allow any mknod (but not using the node) | |
69 | lxc.cgroup.devices.allow = c *:* m | |
70 | lxc.cgroup.devices.allow = b *:* m | |
71 | # /dev/null and zero | |
72 | lxc.cgroup.devices.allow = c 1:3 rwm | |
73 | lxc.cgroup.devices.allow = c 1:5 rwm | |
74 | # consoles | |
75 | lxc.cgroup.devices.allow = c 5:1 rwm | |
76 | lxc.cgroup.devices.allow = c 5:0 rwm | |
d1458ac8 SH |
77 | # /dev/{,u}random |
78 | lxc.cgroup.devices.allow = c 1:9 rwm | |
79 | lxc.cgroup.devices.allow = c 1:8 rwm | |
80 | lxc.cgroup.devices.allow = c 136:* rwm | |
81 | lxc.cgroup.devices.allow = c 5:2 rwm | |
82 | # rtc | |
eee3ba81 | 83 | lxc.cgroup.devices.allow = c 254:0 rm |
a2abaa9e | 84 | # fuse |
d1458ac8 | 85 | lxc.cgroup.devices.allow = c 10:229 rwm |
a2abaa9e | 86 | # tun |
d1458ac8 | 87 | lxc.cgroup.devices.allow = c 10:200 rwm |
a2abaa9e | 88 | # full |
d1458ac8 | 89 | lxc.cgroup.devices.allow = c 1:7 rwm |
a2abaa9e | 90 | # hpet |
d1458ac8 | 91 | lxc.cgroup.devices.allow = c 10:228 rwm |
a2abaa9e | 92 | # kvm |
d1458ac8 SH |
93 | lxc.cgroup.devices.allow = c 10:232 rwm |
94 | EOF | |
95 | ||
96 | cat <<EOF > $path/fstab | |
80a881b2 SH |
97 | proc proc proc nodev,noexec,nosuid 0 0 |
98 | sysfs sys sysfs defaults 0 0 | |
bf7d76cf | 99 | EOF |
d1458ac8 | 100 | |
542939c3 | 101 | # rmdir /dev/shm for containers that have /run/shm |
42ff5f0f SH |
102 | # I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did |
103 | # get bind mounted to the host's /run/shm. So try to rmdir | |
104 | # it, and in case that fails move it out of the way. | |
542939c3 SG |
105 | if [ ! -L $rootfs/dev/shm ] && [ -d $rootfs/run/shm ] && [ -e $rootfs/dev/shm ]; then |
106 | mv $rootfs/dev/shm $rootfs/dev/shm.bak | |
42ff5f0f SH |
107 | ln -s /run/shm $rootfs/dev/shm |
108 | fi | |
109 | ||
d1458ac8 SH |
110 | return 0 |
111 | } | |
112 | ||
113 | usage() | |
114 | { | |
115 | cat <<EOF | |
4759162d SH |
116 | LXC Container configuration for Ubuntu Cloud images. |
117 | ||
118 | Generic Options | |
119 | [ -r | --release <release> ]: Release name of container, defaults to host | |
1897e3bc | 120 | [ --rootfs <path> ]: Path in which rootfs will be placed |
4759162d SH |
121 | [ -a | --arch ]: Arhcitecture of container, defaults to host arcitecture |
122 | [ -C | --cloud ]: Configure container for use with meta-data service, defaults to no | |
123 | [ -T | --tarball ]: Location of tarball | |
52c8f624 | 124 | [ -d | --debug ]: Run with 'set -x' to debug errors |
427bffc7 | 125 | [ -s | --stream]: Use specified stream rather than 'released' |
4759162d SH |
126 | |
127 | Options, mutually exclusive of "-C" and "--cloud": | |
128 | [ -i | --hostid ]: HostID for cloud-init, defaults to random string | |
129 | [ -u | --userdata ]: Cloud-init user-data file to configure container on start | |
52c8f624 | 130 | [ -S | --auth-key ]: SSH Public key file to inject into container |
4759162d SH |
131 | [ -L | --nolocales ]: Do not copy host's locales into container |
132 | ||
d1458ac8 SH |
133 | EOF |
134 | return 0 | |
135 | } | |
136 | ||
1897e3bc | 137 | options=$(getopt -o a:hp:r:n:Fi:CLS:T:ds:u: -l arch:,help,rootfs:,path:,release:,name:,flush-cache,hostid:,auth-key:,cloud,no_locales,tarball:,debug,stream:,userdata: -- "$@") |
d1458ac8 SH |
138 | if [ $? -ne 0 ]; then |
139 | usage $(basename $0) | |
140 | exit 1 | |
141 | fi | |
142 | eval set -- "$options" | |
143 | ||
144 | release=lucid | |
145 | if [ -f /etc/lsb-release ]; then | |
146 | . /etc/lsb-release | |
147 | case "$DISTRIB_CODENAME" in | |
f34ff296 | 148 | lucid|natty|oneiric|precise|quantal) |
d1458ac8 SH |
149 | release=$DISTRIB_CODENAME |
150 | ;; | |
151 | esac | |
152 | fi | |
153 | ||
d1458ac8 SH |
154 | # Code taken from debootstrap |
155 | if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then | |
156 | arch=`/usr/bin/dpkg --print-architecture` | |
157 | elif type udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then | |
158 | arch=`/usr/bin/udpkg --print-architecture` | |
159 | else | |
ed4616b1 | 160 | arch=$(uname -m) |
d1458ac8 SH |
161 | if [ "$arch" = "i686" ]; then |
162 | arch="i386" | |
163 | elif [ "$arch" = "x86_64" ]; then | |
164 | arch="amd64" | |
165 | elif [ "$arch" = "armv7l" ]; then | |
3eecde70 | 166 | # note: arm images don't exist before oneiric; are called armhf in |
b8bced69 | 167 | # precise and later; and are not supported by the query, so we don't actually |
3eecde70 SH |
168 | # support them yet (see check later on). When Query2 is available, |
169 | # we'll use that to enable arm images. | |
8a63c0a9 | 170 | arch="armhf" |
d1458ac8 SH |
171 | fi |
172 | fi | |
173 | ||
52c8f624 | 174 | debug=0 |
d1458ac8 SH |
175 | hostarch=$arch |
176 | cloud=0 | |
4759162d | 177 | locales=1 |
d1458ac8 | 178 | flushcache=0 |
427bffc7 | 179 | stream="released" |
d1458ac8 SH |
180 | while true |
181 | do | |
182 | case "$1" in | |
4759162d SH |
183 | -h|--help) usage $0 && exit 0;; |
184 | -p|--path) path=$2; shift 2;; | |
185 | -n|--name) name=$2; shift 2;; | |
186 | -F|--flush-cache) flushcache=1; shift 1;; | |
187 | -r|--release) release=$2; shift 2;; | |
188 | -a|--arch) arch=$2; shift 2;; | |
189 | -i|--hostid) host_id=$2; shift 2;; | |
190 | -u|--userdata) userdata=$2; shift 2;; | |
191 | -C|--cloud) cloud=1; shift 1;; | |
52c8f624 | 192 | -S|--auth-key) auth_key=$2; shift 2;; |
6b1a07ba | 193 | -L|--no_locales) locales=0; shift 1;; |
4759162d | 194 | -T|--tarball) tarball=$2; shift 2;; |
52c8f624 | 195 | -d|--debug) debug=1; shift 1;; |
427bffc7 | 196 | -s|--stream) stream=$2; shift 2;; |
868a70af | 197 | --rootfs) rootfs=$2; shift 2;; |
4759162d | 198 | --) shift 1; break ;; |
d1458ac8 SH |
199 | *) break ;; |
200 | esac | |
201 | done | |
202 | ||
3eecde70 | 203 | if [ $debug -eq 1 ]; then |
52c8f624 SH |
204 | set -x |
205 | fi | |
206 | ||
d1458ac8 SH |
207 | if [ "$arch" == "i686" ]; then |
208 | arch=i386 | |
209 | fi | |
210 | ||
8a63c0a9 SG |
211 | if [ $arch != "i386" -a $arch != "amd64" -a $arch != "armhf" -a $arch != "armel" ]; then |
212 | echo "Only i386, amd64, armel and armhf are supported by the ubuntu cloud template." | |
d1458ac8 SH |
213 | exit 1 |
214 | fi | |
215 | ||
8a63c0a9 SG |
216 | if [ $hostarch != "i386" -a $hostarch != "amd64" -a $hostarch != "armhf" -a $hostarch != "armel" ]; then |
217 | echo "Only i386, amd64, armel and armhf are supported as host." | |
218 | exit 1 | |
219 | fi | |
220 | ||
221 | if [ $hostarch = "amd64" -a $arch != "amd64" -a $arch != "i386" ]; then | |
222 | echo "can't create $arch container on $hostarch" | |
223 | exit 1 | |
224 | fi | |
225 | ||
226 | if [ $hostarch = "i386" -a $arch != "i386" ]; then | |
227 | echo "can't create $arch container on $hostarch" | |
228 | exit 1 | |
229 | fi | |
230 | ||
231 | if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \ | |
232 | [ $arch != "armhf" -a $arch != "armel" ]; then | |
233 | echo "can't create $arch container on $hostarch" | |
d1458ac8 SH |
234 | exit 1 |
235 | fi | |
236 | ||
427bffc7 SH |
237 | if [ "$stream" != "daily" -a "$stream" != "released" ]; then |
238 | echo "Only 'daily' and 'released' streams are supported" | |
239 | exit 1 | |
240 | fi | |
241 | ||
10f73bfa SH |
242 | if [ -n "$userdata" ]; then |
243 | if [ ! -f "$userdata" ]; then | |
244 | echo "Userdata ($userdata) does not exist" | |
245 | exit 1 | |
246 | else | |
247 | userdata=`readlink -f $userdata` | |
248 | fi | |
5a0f7f3c SH |
249 | fi |
250 | ||
b942e672 SM |
251 | if [ -n "$auth_key" ]; then |
252 | if [ ! -f "$auth_key" ]; then | |
253 | echo "--auth-key=${auth_key} must reference a file" | |
254 | exit 1 | |
255 | fi | |
256 | auth_key=$(readlink -f "${auth_key}") || | |
257 | { echo "failed to get full path for auth_key"; exit 1; } | |
258 | fi | |
259 | ||
d1458ac8 SH |
260 | if [ -z "$path" ]; then |
261 | echo "'path' parameter is required" | |
262 | exit 1 | |
263 | fi | |
264 | ||
265 | if [ "$(id -u)" != "0" ]; then | |
266 | echo "This script should be run as 'root'" | |
267 | exit 1 | |
268 | fi | |
269 | ||
1881820a SH |
270 | # detect rootfs |
271 | config="$path/config" | |
1897e3bc SH |
272 | if [ -z "$rootfs" ]; then |
273 | if grep -q '^lxc.rootfs' $config 2>/dev/null ; then | |
274 | rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'` | |
275 | else | |
276 | rootfs=$path/rootfs | |
277 | fi | |
1881820a | 278 | fi |
d1458ac8 SH |
279 | |
280 | type ubuntu-cloudimg-query | |
281 | type wget | |
282 | ||
283 | # determine the url, tarball, and directory names | |
284 | # download if needed | |
e29bf450 | 285 | cache="@LOCALSTATEDIR@/cache/lxc/cloud-$release" |
d1458ac8 SH |
286 | |
287 | mkdir -p $cache | |
288 | ||
4759162d | 289 | if [ -n "$tarball" ]; then |
b942e672 | 290 | url2="$tarball" |
4759162d | 291 | else |
b942e672 SM |
292 | url1=`ubuntu-cloudimg-query $release $stream $arch --format "%{url}\n"` |
293 | url2=`echo $url1 | sed -e 's/.tar.gz/-root\0/'` | |
4759162d SH |
294 | fi |
295 | ||
d1458ac8 SH |
296 | filename=`basename $url2` |
297 | ||
f1ccde27 SH |
298 | wgetcleanup() |
299 | { | |
b942e672 | 300 | rm -f $filename |
f1ccde27 SH |
301 | } |
302 | ||
3eecde70 SH |
303 | buildcleanup() |
304 | { | |
305 | cd $rootfs | |
306 | umount -l $cache/$xdir || true | |
307 | rm -rf $cache | |
308 | } | |
309 | ||
310 | # if the release doesn't have a *-rootfs.tar.gz, then create one from the | |
311 | # cloudimg.tar.gz by extracting the .img, mounting it loopback, and creating | |
312 | # a tarball from the mounted image. | |
313 | build_root_tgz() | |
314 | { | |
315 | url=$1 | |
316 | filename=$2 | |
317 | ||
318 | xdir=`mktemp -d -p .` | |
319 | tarname=`basename $url` | |
edd3810e | 320 | imgname="$release-*-cloudimg-$arch.img" |
f1ccde27 | 321 | trap buildcleanup EXIT SIGHUP SIGINT SIGTERM |
3eecde70 SH |
322 | if [ $flushcache -eq 1 -o ! -f $cache/$tarname ]; then |
323 | rm -f $tarname | |
324 | echo "Downloading cloud image from $url" | |
325 | wget $url || { echo "Couldn't find cloud image $url."; exit 1; } | |
326 | fi | |
327 | echo "Creating new cached cloud image rootfs" | |
edd3810e | 328 | tar --wildcards -zxf $tarname $imgname |
3eecde70 SH |
329 | mount -o loop $imgname $xdir |
330 | (cd $xdir; tar zcf ../$filename .) | |
331 | umount $xdir | |
332 | rm -f $tarname $imgname | |
333 | rmdir $xdir | |
334 | echo "New cloud image cache created" | |
335 | trap EXIT | |
f1ccde27 SH |
336 | trap SIGHUP |
337 | trap SIGINT | |
338 | trap SIGTERM | |
3eecde70 SH |
339 | } |
340 | ||
e29bf450 | 341 | mkdir -p @LOCALSTATEDIR@/lock/subsys/ |
d1458ac8 | 342 | ( |
add1d118 | 343 | flock -x 200 |
d1458ac8 SH |
344 | |
345 | cd $cache | |
346 | if [ $flushcache -eq 1 ]; then | |
347 | echo "Clearing the cached images" | |
348 | rm -f $filename | |
349 | fi | |
350 | ||
f1ccde27 | 351 | trap wgetcleanup EXIT SIGHUP SIGINT SIGTERM |
d1458ac8 | 352 | if [ ! -f $filename ]; then |
b942e672 | 353 | wget $url2 || build_root_tgz $url1 $filename |
d1458ac8 | 354 | fi |
f1ccde27 SH |
355 | trap EXIT |
356 | trap SIGHUP | |
357 | trap SIGINT | |
358 | trap SIGTERM | |
d1458ac8 | 359 | |
3eecde70 | 360 | echo "Extracting container rootfs" |
d1458ac8 SH |
361 | mkdir -p $rootfs |
362 | cd $rootfs | |
363 | tar -zxf $cache/$filename | |
364 | ||
365 | ||
366 | if [ $cloud -eq 0 ]; then | |
b942e672 SM |
367 | echo "Configuring for running outside of a cloud environment" |
368 | echo "If you want to configure for a cloud evironment, please use '-- -C' to create the container" | |
d1458ac8 | 369 | |
b942e672 SM |
370 | seed_d=$rootfs/var/lib/cloud/seed/nocloud-net |
371 | rhostid=$(uuidgen | cut -c -8) | |
372 | host_id=${hostid:-$rhostid} | |
373 | mkdir -p $seed_d | |
d1458ac8 | 374 | |
b942e672 SM |
375 | cat > "$seed_d/meta-data" <<EOF |
376 | instance-id: lxc-$host_id | |
d1458ac8 | 377 | EOF |
b942e672 SM |
378 | if [ -n "$auth_key" ]; then |
379 | { | |
380 | echo "public-keys:" && | |
381 | sed -e '/^$/d' -e 's,^,- ,' "$auth_key" "$auth_key" | |
382 | } >> "$seed_d/meta-data" | |
383 | [ $? -eq 0 ] || | |
384 | { echo "failed to write public keys to metadata"; exit 1; } | |
385 | fi | |
386 | ||
387 | rm $rootfs/etc/hostname | |
388 | ||
389 | if [ $locales -eq 1 ]; then | |
390 | cp /usr/lib/locale/locale-archive $rootfs/usr/lib/locale/locale-archive | |
391 | fi | |
392 | ||
393 | if [ -f "$userdata" ]; then | |
394 | echo "Using custom user-data" | |
395 | cp $userdata $seed_d/user-data | |
396 | else | |
397 | ||
398 | if [ -z "$MIRROR" ]; then | |
399 | MIRROR="http://archive.ubuntu.com/ubuntu" | |
400 | fi | |
401 | ||
402 | cat > "$seed_d/user-data" <<EOF | |
4759162d SH |
403 | #cloud-config |
404 | output: {all: '| tee -a /var/log/cloud-init-output.log'} | |
b942e672 | 405 | apt_mirror: $MIRROR |
4759162d SH |
406 | manage_etc_hosts: localhost |
407 | locale: $(/usr/bin/locale | awk -F= '/LANG=/ {print$NF}') | |
b942e672 SM |
408 | password: ubuntu |
409 | chpasswd: { expire: False } | |
4759162d | 410 | EOF |
b942e672 SM |
411 | fi |
412 | else | |
d1458ac8 | 413 | |
b942e672 SM |
414 | echo "Configured for running in a cloud environment." |
415 | echo "If you do not have a meta-data service, this container will likely be useless." | |
d1458ac8 | 416 | |
b942e672 | 417 | fi |
fe253caa | 418 | ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-ubuntu-cloud |
d1458ac8 | 419 | |
42ff5f0f | 420 | copy_configuration $path $rootfs $name $arch $release |
d1458ac8 SH |
421 | |
422 | echo "Container $name created." | |
423 | exit 0 | |
b942e672 SM |
424 | |
425 | # vi: ts=4 expandtab |