lxc-ubuntu.in revision 3e9c97c17a86ff52897bdb965182c36248cdb97a
0N/A#!/bin/bash
0N/A
0N/A#
0N/A# template script for generating ubuntu container for LXC
0N/A#
0N/A# This script consolidates and extends the existing lxc ubuntu scripts
0N/A#
0N/A
0N/A# XXX todo: add -lvm option
0N/A
0N/A# Copyright � 2011 Serge Hallyn <serge.hallyn@canonical.com>
0N/A# Copyright � 2010 Wilhelm Meier
0N/A# Author: Wilhelm Meier <wilhelm.meier@fh-kl.de>
0N/A#
0N/A# This program is free software; you can redistribute it and/or modify
0N/A# it under the terms of the GNU General Public License version 2, as
0N/A# published by the Free Software Foundation.
0N/A
928N/A# This program is distributed in the hope that it will be useful,
0N/A# but WITHOUT ANY WARRANTY; without even the implied warranty of
0N/A# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0N/A# GNU General Public License for more details.
0N/A
0N/A# You should have received a copy of the GNU General Public License along
0N/A# with this program; if not, write to the Free Software Foundation, Inc.,
0N/A# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A#
0N/A
58N/Aif [ -r /etc/default/lxc ]; then
0N/A . /etc/default/lxc
0N/Afi
0N/A
0N/Aconfigure_ubuntu()
207N/A{
0N/A rootfs=$1
0N/A hostname=$2
0N/A
58N/A # configure the network using the dhcp
99N/A cat <<EOF > $rootfs/etc/network/interfaces
99N/Aauto lo
928N/Aiface lo inet loopback
928N/A
99N/Aauto eth0
0N/Aiface eth0 inet dhcp
0N/AEOF
0N/A
0N/A # so you can 'ssh $hostname.' or 'ssh $hostname.local'
928N/A if [ -f $rootfs/etc/dhcp/dhclient.conf ]; then
0N/A sed -i "s/<hostname>/$hostname/" $rootfs/etc/dhcp/dhclient.conf
0N/A elif [ -f $rootfs/etc/dhcp3/dhclient.conf ]; then
0N/A sed -i "s/<hostname>/$hostname/" $rootfs/etc/dhcp3/dhclient.conf
0N/A fi
0N/A
0N/A # set the hostname
0N/A cat <<EOF > $rootfs/etc/hostname
0N/A$hostname
58N/AEOF
207N/A # set minimal hosts
98N/A cat <<EOF > $rootfs/etc/hosts
98N/A127.0.0.1 localhost $hostname
98N/AEOF
98N/A
819N/A # suppress log level output for udev
928N/A sed -i "s/=\"err\"/=0/" $rootfs/etc/udev/udev.conf
98N/A
98N/A # remove jobs for consoles 5 and 6 since we only create 4 consoles in
98N/A # this template
98N/A rm -f $rootfs/etc/init/tty{5,6}.conf
98N/A
98N/A echo "Please change root-password !"
819N/A echo "root:root" | chroot $rootfs chpasswd
830N/A
98N/A return 0
98N/A}
98N/A
98N/Adownload_ubuntu()
98N/A{
98N/A cache=$1
98N/A arch=$2
98N/A release=$3
98N/A
98N/A if [ $release = "lucid" ]; then
98N/A packages=dialog,apt,apt-utils,resolvconf,iproute,inetutils-ping,vim,dhcp3-client,ssh,lsb-release,gnupg
98N/A elif [ $release = "maverick" ]; then
819N/A packages=dialog,apt,apt-utils,resolvconf,iproute,inetutils-ping,vim,dhcp3-client,ssh,lsb-release,gnupg,netbase
819N/A elif [ $release = "natty" ]; then
98N/A packages=dialog,apt,apt-utils,resolvconf,iproute,inetutils-ping,vim,isc-dhcp-client,isc-dhcp-common,ssh,lsb-release,gnupg,netbase
98N/A else
98N/A packages=dialog,apt,apt-utils,resolvconf,iproute,inetutils-ping,vim,isc-dhcp-client,isc-dhcp-common,ssh,lsb-release,gnupg,netbase,ubuntu-keyring
98N/A fi
98N/A echo "installing packages: $packages"
830N/A
58N/A # check the mini ubuntu was not already downloaded
830N/A mkdir -p "$cache/partial-$arch"
0N/A if [ $? -ne 0 ]; then
0N/A echo "Failed to create '$cache/partial-$arch' directory"
0N/A return 1
0N/A fi
0N/A
0N/A # download a mini ubuntu into a cache
0N/A echo "Downloading ubuntu $release minimal ..."
830N/A debootstrap --verbose --variant=minbase --components=main,universe --arch=$arch --include=$packages $release $cache/partial-$arch $MIRROR
0N/A if [ $? -ne 0 ]; then
0N/A echo "Failed to download the rootfs, aborting."
0N/A return 1
928N/A fi
819N/A
0N/A mv "$1/partial-$arch" "$1/rootfs-$arch"
0N/A echo "Download complete."
819N/A
819N/A return 0
0N/A}
0N/A
830N/Acopy_ubuntu()
830N/A{
830N/A cache=$1
819N/A arch=$2
819N/A rootfs=$3
819N/A
928N/A # make a local copy of the miniubuntu
819N/A echo -n "Copying rootfs to $rootfs ..."
0N/A cp -a $cache/rootfs-$arch $rootfs || return 1
0N/A return 0
0N/A}
0N/A
0N/Ainstall_ubuntu()
0N/A{
58N/A rootfs=$1
0N/A release=$2
0N/A cache="/var/cache/lxc/$release"
0N/A mkdir -p /var/lock/subsys/
0N/A (
0N/A flock -n -x 200
0N/A if [ $? -ne 0 ]; then
0N/A echo "Cache repository is busy."
0N/A return 1
0N/A fi
819N/A
0N/A
0N/A echo "Checking cache download in $cache/rootfs-$arch ... "
0N/A if [ ! -e "$cache/rootfs-$arch" ]; then
0N/A download_ubuntu $cache $arch $release
0N/A if [ $? -ne 0 ]; then
0N/A echo "Failed to download 'ubuntu $release base'"
226N/A return 1
58N/A fi
928N/A fi
99N/A
125N/A echo "Copy $cache/rootfs-$arch to $rootfs ... "
125N/A copy_ubuntu $cache $arch $rootfs
0N/A if [ $? -ne 0 ]; then
830N/A echo "Failed to copy rootfs"
207N/A return 1
207N/A fi
207N/A
207N/A return 0
207N/A
207N/A ) 200>/var/lock/subsys/lxc
207N/A
830N/A return $?
830N/A}
830N/A
830N/Acopy_configuration()
830N/A{
830N/A path=$1
928N/A rootfs=$2
830N/A name=$3
830N/A arch=$4
830N/A
830N/A if [ $arch = "i386" ]; then
830N/A arch="i686"
830N/A fi
830N/A
830N/A cat <<EOF >> $path/config
830N/Alxc.utsname = $name
928N/A
830N/Alxc.tty = 4
830N/Alxc.pts = 1024
207N/Alxc.rootfs = $rootfs
830N/Alxc.mount = $path/fstab
928N/Alxc.arch = $arch
830N/A
830N/Alxc.cgroup.devices.deny = a
207N/A# /dev/null and zero
830N/Alxc.cgroup.devices.allow = c 1:3 rwm
830N/Alxc.cgroup.devices.allow = c 1:5 rwm
928N/A# consoles
819N/Alxc.cgroup.devices.allow = c 5:1 rwm
928N/Alxc.cgroup.devices.allow = c 5:0 rwm
819N/A#lxc.cgroup.devices.allow = c 4:0 rwm
819N/A#lxc.cgroup.devices.allow = c 4:1 rwm
819N/A# /dev/{,u}random
819N/Alxc.cgroup.devices.allow = c 1:9 rwm
819N/Alxc.cgroup.devices.allow = c 1:8 rwm
819N/Alxc.cgroup.devices.allow = c 136:* rwm
819N/Alxc.cgroup.devices.allow = c 5:2 rwm
819N/A# rtc
928N/Alxc.cgroup.devices.allow = c 254:0 rwm
819N/A#fuse
819N/Alxc.cgroup.devices.allow = c 10:229 rwm
819N/AEOF
819N/A
819N/A cat <<EOF > $path/fstab
819N/Aproc $rootfs/proc proc nodev,noexec,nosuid 0 0
819N/Asysfs $rootfs/sys sysfs defaults 0 0
819N/AEOF
819N/A
819N/A if [ $? -ne 0 ]; then
819N/A echo "Failed to add configuration"
928N/A return 1
819N/A fi
819N/A
819N/A return 0
819N/A}
819N/A
207N/Atrim()
819N/A{
819N/A rootfs=$1
819N/A release=$2
819N/A
819N/A # provide the lxc service
819N/A cat <<EOF > $rootfs/etc/init/lxc.conf
819N/A# fake some events needed for correct startup other services
207N/A
0N/Adescription "Container Upstart"
0N/A
0N/Astart on startup
510N/A
510N/Ascript
510N/A rm -rf /var/run/*.pid
849N/A rm -rf /var/run/network/*
510N/A /sbin/initctl emit stopped JOB=udevtrigger --no-wait
510N/A /sbin/initctl emit started JOB=udev --no-wait
0N/Aend script
0N/AEOF
0N/A
345N/A # fix buggus runlevel with sshd
0N/A cat <<EOF > $rootfs/etc/init/ssh.conf
850N/A# ssh - OpenBSD Secure Shell server
942N/A#
942N/A# The OpenSSH server provides secure shell access to the system.
942N/A
942N/Adescription "OpenSSH server"
942N/A
942N/Astart on filesystem
107N/Astop on runlevel [!2345]
107N/A
942N/Aexpect fork
942N/Arespawn
942N/Arespawn limit 10 5
819N/Aumask 022
158N/A# replaces SSHD_OOM_ADJUST in /etc/default/ssh
151N/Aoom never
158N/A
152N/Apre-start script
158N/A test -x /usr/sbin/sshd || { stop; exit 0; }
152N/A test -e /etc/ssh/sshd_not_to_be_run && { stop; exit 0; }
152N/A test -c /dev/null || { stop; exit 0; }
0N/A
144N/A mkdir -p -m0755 /var/run/sshd
147N/Aend script
147N/A
144N/A# if you used to set SSHD_OPTS in /etc/default/ssh, you can change the
0N/A# 'exec' line here instead
0N/Aexec /usr/sbin/sshd
144N/AEOF
0N/A
0N/A cat <<EOF > $rootfs/etc/init/console.conf
850N/A# console - getty
98N/A#
853N/A# This service maintains a console on tty1 from the point the system is
850N/A# started until it is shut down again.
850N/A
850N/Astart on stopped rc RUNLEVEL=[2345]
850N/Astop on runlevel [!2345]
850N/A
850N/Arespawn
866N/Aexec /sbin/getty -8 38400 /dev/console
866N/AEOF
866N/A
866N/A cat <<EOF > $rootfs/lib/init/fstab
866N/A# /lib/init/fstab: cleared out for bare-bones lxc
850N/AEOF
98N/A
850N/A # reconfigure some services
850N/A if [ -z "$LANG" ]; then
850N/A chroot $rootfs locale-gen en_US.UTF-8
98N/A chroot $rootfs update-locale LANG=en_US.UTF-8
98N/A else
850N/A chroot $rootfs locale-gen $LANG
819N/A chroot $rootfs update-locale LANG=$LANG
850N/A fi
819N/A
850N/A # remove pointless services in a container
98N/A chroot $rootfs /usr/sbin/update-rc.d -f ondemand remove
850N/A
98N/A chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls u*.conf); do mv $f $f.orig; done'
98N/A chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls tty[2-9].conf); do mv $f $f.orig; done'
98N/A chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls plymouth*.conf); do mv $f $f.orig; done'
98N/A chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls hwclock*.conf); do mv $f $f.orig; done'
98N/A chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls module*.conf); do mv $f $f.orig; done'
0N/A
0N/A # if this isn't lucid, then we need to twiddle the network upstart bits :(
0N/A if [ $release != "lucid" ]; then
830N/A sed -i 's/^.*emission handled.*$/echo Emitting lo/' $rootfs/etc/network/if-up.d/upstart
0N/A fi
0N/A}
819N/A
99N/Apost_process()
830N/A{
207N/A rootfs=$1
207N/A release=$2
830N/A trim_container=$3
830N/A
830N/A if [ $trim_container -eq 1 ]; then
830N/A trim $rootfs $release
830N/A else
830N/A # for lucid and maverick, if not trimming, then add the ubuntu-virt
830N/A # ppa and install lxcguest
830N/A if [ $release = "lucid" -o $release = "maverick" ]; then
830N/A chroot $rootfs apt-get install --force-yes -y python-software-properties
830N/A chroot $rootfs add-apt-repository ppa:ubuntu-virt/ppa
207N/A chroot $rootfs apt-get update
830N/A fi
830N/A chroot $rootfs apt-get install --force-yes -y lxcguest
830N/A fi
830N/A}
830N/A
830N/Ado_bindhome()
830N/A{
830N/A rootfs=$1
830N/A user=$2
928N/A
99N/A # bind-mount the user's path into the container's /home
99N/A h=`getent passwd $user | cut -d: -f 6`
0N/A mkdir -p $rootfs/$h
603N/A echo "$h $rootfs/$h none bind 0 0" >> $path/fstab
0N/A
0N/A # copy /etc/passwd, /etc/shadow, and /etc/group entries into container
0N/A pwd=`getent passwd $user`
0N/A if [ $? -ne 0 ]; then
0N/A echo 'Warning: failed to copy password entry for $user'
0N/A else
830N/A echo $pwd >> $rootfs/etc/passwd
0N/A fi
603N/A shad=`getent shadow $user`
830N/A echo $shad >> $rootfs/etc/shadow
603N/A}
603N/A
603N/Aclean()
0N/A{
0N/A release=$1
0N/A cache="/var/cache/lxc/$release"
0N/A
0N/A if [ ! -e $cache ]; then
0N/A exit 0
0N/A fi
0N/A
0N/A # lock, so we won't purge while someone is creating a repository
0N/A (
830N/A flock -n -x 200
0N/A if [ $? != 0 ]; then
603N/A echo "Cache repository is busy."
830N/A exit 1
603N/A fi
603N/A
830N/A echo -n "Purging the download cache..."
0N/A rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
0N/A exit 0
0N/A
0N/A ) 200>/var/lock/subsys/lxc
0N/A}
830N/A
0N/Ausage()
0N/A{
0N/A cat <<EOF
0N/A$1 -h|--help -p|--path=<path> --clean [-a|--arch] [-b|--bindhome <user>] [--trim] [-r|--release]
0N/Arelease: lucid | maverick | natty | oneiric
830N/Atrim: make a minimal (faster, but not upgrade-safe) container
0N/Abindhome: bind <user>'s home into the container
603N/Aarch: amd64 or i386: defaults to host arch
830N/AEOF
603N/A return 0
603N/A}
830N/A
0N/Aoptions=$(getopt -o a:b:hp:r:xn:c -l arch:,bindhome:,help,path:,release:,trim,name:,clean -- "$@")
0N/Aif [ $? -ne 0 ]; then
0N/A usage $(basename $0)
0N/A exit 1
0N/Afi
603N/Aeval set -- "$options"
603N/A
603N/Arelease=lucid
99N/Aif [ -f /etc/lsb-release ]; then
830N/A . /etc/lsb-release
830N/A case "$DISTRIB_CODENAME" in
0N/A lucid|maverick|natty|oneiric)
830N/A release=$DISTRIB_CODENAME
0N/A ;;
0N/A esac
0N/Afi
0N/A
0N/Abindhome=
0N/Aarch=$(arch)
0N/A
0N/A# Code taken from debootstrap
0N/Aif [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then
0N/A arch=`/usr/bin/dpkg --print-architecture`
820N/Aelif type udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then
820N/A arch=`/usr/bin/udpkg --print-architecture`
0N/Aelse
0N/A arch=$(arch)
820N/A if [ "$arch" = "i686" ]; then
0N/A arch="i386"
850N/A elif [ "$arch" = "x86_64" ]; then
0N/A arch="amd64"
0N/A elif [ "$arch" = "armv7l" ]; then
0N/A arch="armel"
97N/A fi
830N/Afi
850N/A
850N/Atrim_container=0
850N/Ahostarch=$arch
850N/Awhile true
850N/Ado
850N/A case "$1" in
0N/A -h|--help) usage $0 && exit 0;;
0N/A -p|--path) path=$2; shift 2;;
0N/A -n|--name) name=$2; shift 2;;
0N/A -c|--clean) clean=$2; shift 2;;
0N/A -r|--release) release=$2; shift 2;;
0N/A -b|--bindhome) bindhome=$2; shift 2;;
0N/A -a|--arch) arch=$2; shift 2;;
0N/A -x|--trim) trim_container=1; shift 1;;
0N/A --) shift 1; break ;;
0N/A *) break ;;
0N/A esac
0N/Adone
819N/A
0N/Aif [ "$arch" == "i686" ]; then
0N/A arch=i386
0N/Afi
0N/A
0N/Aif [ ! -z "$clean" -a -z "$path" ]; then
819N/A clean || exit 1
0N/A exit 0
0N/Afi
0N/A
0N/Aif [ $hostarch = "i386" -a $arch = "amd64" ]; then
850N/A echo "can't create amd64 container on i386"
0N/A exit 1
0N/Afi
0N/A
0N/Atype debootstrap
0N/Aif [ $? -ne 0 ]; then
819N/A echo "'debootstrap' command is missing"
0N/A exit 1
0N/Afi
819N/A
0N/Aif [ -z "$path" ]; then
0N/A echo "'path' parameter is required"
0N/A exit 1
0N/Afi
0N/A
0N/Aif [ "$(id -u)" != "0" ]; then
0N/A echo "This script should be run as 'root'"
0N/A exit 1
0N/Afi
0N/A
0N/Arootfs=$path/rootfs
0N/A
0N/Ainstall_ubuntu $rootfs $release
0N/Aif [ $? -ne 0 ]; then
0N/A echo "failed to install ubuntu $release"
0N/A exit 1
0N/Afi
0N/A
0N/Aconfigure_ubuntu $rootfs $name
0N/Aif [ $? -ne 0 ]; then
0N/A echo "failed to configure ubuntu $release for a container"
0N/A exit 1
58N/Afi
0N/A
0N/Acopy_configuration $path $rootfs $name $arch
830N/Aif [ $? -ne 0 ]; then
819N/A echo "failed write configuration file"
0N/A exit 1
0N/Afi
0N/A
0N/Apost_process $rootfs $release $trim_container
58N/Aif [ ! -z $bindhome ]; then
58N/A do_bindhome $rootfs $bindhome
0N/Afi
0N/A
0N/Aif [ ! -z $clean ]; then
0N/A clean $release || exit 1
0N/A exit 0
0N/Afi
0N/A