lxc-ubuntu-cloud.in revision 9313e1e628160ca64f9e7fcec6500056c9a0725f
816bf6f8088b162b681101d93fd450127a0e586fJulian Kornberger#!/bin/bash
a847d9812b328c048773e705606b10875a929034Eugen Kuksa
5e2a6a8992b4e9e77be3a94fc87af4dc14b0c8c4henning mueller# template script for generating ubuntu container for LXC based on released cloud
a4344d37747b6733bbd0d8df738b614cb385316cTim Reddehase# images
d1f0cb74e8bc61e9185488a431b86816cb1cc7edSascha Graef#
c273fbbb7863507673f695bc85709c0cb715011fTim Reddehase# Copyright © 2012 Serge Hallyn <serge.hallyn@canonical.com>
c273fbbb7863507673f695bc85709c0cb715011fTim Reddehase#
5ec1c6ff5da427d945d7fb708567ce3526c4c741Tim Reddehase# This program is free software; you can redistribute it and/or modify
5ec1c6ff5da427d945d7fb708567ce3526c4c741Tim Reddehase# it under the terms of the GNU General Public License version 2, as
98ba1c38b1cce99ecc61117259f2ae05ffe98469Tim Reddehase# published by the Free Software Foundation.
5ec1c6ff5da427d945d7fb708567ce3526c4c741Tim Reddehase
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehase# This program is distributed in the hope that it will be useful,
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase# but WITHOUT ANY WARRANTY; without even the implied warranty of
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase# GNU General Public License for more details.
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase# You should have received a copy of the GNU General Public License along
6f84d8dbc4edca35b7e69c8296a257ea17e3b74fTim Reddehase# with this program; if not, write to the Free Software Foundation, Inc.,
6f84d8dbc4edca35b7e69c8296a257ea17e3b74fTim Reddehase# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase#
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehaseset -e
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehaseif [ -r /etc/default/lxc ]; then
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase . /etc/default/lxc
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehasefi
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehase
10be32b36246121321399ec3ab26d4d723d550e3Tim Reddehasecopy_configuration()
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehase{
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehase path=$1
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehase rootfs=$2
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehase name=$3
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase arch=$4
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase release=$5
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase if [ $arch = "i386" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase arch="i686"
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase fi
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase # if there is exactly one veth network entry, make sure it has an
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase # associated hwaddr.
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehase nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l`
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase if [ $nics -eq 1 ]; then
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase 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
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase fi
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase cat <<EOF >> $path/config
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.mount = $path/fstab
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehaselxc.pivotdir = lxc_putold
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.devttydir =$ttydir
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.tty = 4
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.pts = 1024
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.utsname = $name
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.arch = $arch
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cap.drop = sys_module mac_admin mac_override sys_time
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase# When using LXC with apparmor, uncomment the next line to run unconfined:
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase#lxc.aa_profile = unconfined
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase# To support container nesting on an Ubuntu host, uncomment next two lines:
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase#lxc.aa_profile = lxc-container-default-with-nesting
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase#lxc.hook.mount = /usr/share/lxc/hooks/mountcgroups
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.deny = a
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase# Allow any mknod (but not using the node)
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c *:* m
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = b *:* m
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase# /dev/null and zero
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c 1:3 rwm
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c 1:5 rwm
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase# consoles
4949048bda09e116ee3627383e831455954cbe41Tim Reddehaselxc.cgroup.devices.allow = c 5:1 rwm
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehaselxc.cgroup.devices.allow = c 5:0 rwm
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase# /dev/{,u}random
4949048bda09e116ee3627383e831455954cbe41Tim Reddehaselxc.cgroup.devices.allow = c 1:9 rwm
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c 1:8 rwm
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c 136:* rwm
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehaselxc.cgroup.devices.allow = c 5:2 rwm
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase# rtc
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c 254:0 rm
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase# fuse
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c 10:229 rwm
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase# tun
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c 10:200 rwm
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase# full
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaselxc.cgroup.devices.allow = c 1:7 rwm
4949048bda09e116ee3627383e831455954cbe41Tim Reddehase# hpet
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksalxc.cgroup.devices.allow = c 10:228 rwm
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa# kvm
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksalxc.cgroup.devices.allow = c 10:232 rwm
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen KuksaEOF
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa cat <<EOF > $path/fstab
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksaproc proc proc nodev,noexec,nosuid 0 0
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksasysfs sys sysfs defaults 0 0
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen KuksaEOF
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa # rmdir /dev/shm for containers that have /run/shm
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa # I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa # get bind mounted to the host's /run/shm. So try to rmdir
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa # it, and in case that fails move it out of the way.
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa if [ ! -L $rootfs/dev/shm ] && [ -d $rootfs/run/shm ] && [ -e $rootfs/dev/shm ]; then
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa mv $rootfs/dev/shm $rootfs/dev/shm.bak
273b30f33fe1a753893887c8eb94f8078cc96928Eugen Kuksa ln -s /run/shm $rootfs/dev/shm
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa fi
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa return 0
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa}
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksausage()
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa{
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa cat <<EOF
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen KuksaLXC Container configuration for Ubuntu Cloud images.
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen KuksaGeneric Options
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa[ -r | --release <release> ]: Release name of container, defaults to host
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa[ --rootfs <path> ]: Path in which rootfs will be placed
9ade3006d75c25cfa77d51526e4a6cdd2370be5cEugen Kuksa[ -a | --arch ]: Arhcitecture of container, defaults to host arcitecture
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa[ -C | --cloud ]: Configure container for use with meta-data service, defaults to no
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa[ -T | --tarball ]: Location of tarball
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa[ -d | --debug ]: Run with 'set -x' to debug errors
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa[ -s | --stream]: Use specified stream rather than 'released'
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen KuksaOptions, mutually exclusive of "-C" and "--cloud":
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa [ -i | --hostid ]: HostID for cloud-init, defaults to random string
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa [ -u | --userdata ]: Cloud-init user-data file to configure container on start
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa [ -S | --auth-key ]: SSH Public key file to inject into container
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa [ -L | --nolocales ]: Do not copy host's locales into container
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen KuksaEOF
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa return 0
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa}
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehaseoptions=$(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: -- "$@")
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehaseif [ $? -ne 0 ]; then
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase usage $(basename $0)
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase exit 1
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehasefi
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehaseeval set -- "$options"
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehaserelease=lucid
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksaif [ -f /etc/lsb-release ]; then
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa . /etc/lsb-release
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase case "$DISTRIB_CODENAME" in
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa lucid|natty|oneiric|precise|quantal)
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehase release=$DISTRIB_CODENAME
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehase ;;
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehase esac
d11de492e18415f91e383e1567beb4e1385d5938Tim Reddehasefi
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehase
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehase# Code taken from debootstrap
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehaseif [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase arch=`/usr/bin/dpkg --print-architecture`
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseelif type udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase arch=`/usr/bin/udpkg --print-architecture`
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseelse
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase arch=$(uname -m)
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase if [ "$arch" = "i686" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase arch="i386"
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase elif [ "$arch" = "x86_64" ]; then
7c0f71e3e6e26af2cc91112fdbe3905879bfd893Tim Reddehase arch="amd64"
cc3ee169b3cbf177eaf03e9a590ff9e30c338bd2Tim Reddehase elif [ "$arch" = "armv7l" ]; then
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase # note: arm images don't exist before oneiric; are called armhf in
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase # precise and later; and are not supported by the query, so we don't actually
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase # support them yet (see check later on). When Query2 is available,
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase # we'll use that to enable arm images.
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase arch="armhf"
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase fi
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehasefi
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehase
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehasedebug=0
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehasehostarch=$arch
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehasecloud=0
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehaselocales=1
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehaseflushcache=0
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehasestream="released"
503ccb65bd740225668eb51be966d60fb4ae29e1Tim Reddehasewhile true
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksado
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa case "$1" in
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -h|--help) usage $0 && exit 0;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -p|--path) path=$2; shift 2;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -n|--name) name=$2; shift 2;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -F|--flush-cache) flushcache=1; shift 1;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -r|--release) release=$2; shift 2;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -a|--arch) arch=$2; shift 2;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -i|--hostid) host_id=$2; shift 2;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -u|--userdata) userdata=$2; shift 2;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -C|--cloud) cloud=1; shift 1;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -S|--auth-key) auth_key=$2; shift 2;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -L|--no_locales) locales=0; shift 1;;
575f7ed30e509f1a650d73927f607e7b5b11a6dbEugen Kuksa -T|--tarball) tarball=$2; shift 2;;
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehase -d|--debug) debug=1; shift 1;;
81a6387a4ab56a24194ecbabd6609c6bcca568b7Tim Reddehase -s|--stream) stream=$2; shift 2;;
98ba1c38b1cce99ecc61117259f2ae05ffe98469Tim Reddehase --) shift 1; break ;;
98ba1c38b1cce99ecc61117259f2ae05ffe98469Tim Reddehase *) break ;;
98ba1c38b1cce99ecc61117259f2ae05ffe98469Tim Reddehase esac
d51cee8257d50d1f2d7c0818d968b32096f6b795Tim Reddehasedone
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseif [ $debug -eq 1 ]; then
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase set -x
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehasefi
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseif [ "$arch" == "i686" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase arch=i386
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehasefi
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseif [ $arch != "i386" -a $arch != "amd64" -a $arch != "armhf" -a $arch != "armel" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase echo "Only i386, amd64, armel and armhf are supported by the ubuntu cloud template."
5ec1c6ff5da427d945d7fb708567ce3526c4c741Tim Reddehase exit 1
7dadc1a5aa3845c2ce19ad1daa2c63dfd7b59979Tim Reddehasefi
7dadc1a5aa3845c2ce19ad1daa2c63dfd7b59979Tim Reddehase
7dadc1a5aa3845c2ce19ad1daa2c63dfd7b59979Tim Reddehaseif [ $hostarch != "i386" -a $hostarch != "amd64" -a $hostarch != "armhf" -a $hostarch != "armel" ]; then
7dadc1a5aa3845c2ce19ad1daa2c63dfd7b59979Tim Reddehase echo "Only i386, amd64, armel and armhf are supported as host."
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase exit 1
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehasefi
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseif [ $hostarch = "amd64" -a $arch != "amd64" -a $arch != "i386" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase echo "can't create $arch container on $hostarch"
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase exit 1
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehasefi
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseif [ $hostarch = "i386" -a $arch != "i386" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase echo "can't create $arch container on $hostarch"
7dadc1a5aa3845c2ce19ad1daa2c63dfd7b59979Tim Reddehase exit 1
d51cee8257d50d1f2d7c0818d968b32096f6b795Tim Reddehasefi
d51cee8257d50d1f2d7c0818d968b32096f6b795Tim Reddehase
d51cee8257d50d1f2d7c0818d968b32096f6b795Tim Reddehaseif [ $hostarch = "armhf" -o $hostarch = "armel" ] && \
772288cf17d57e84a4799c56949263f7ff098773Tim Reddehase [ $arch != "armhf" -a $arch != "armel" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase echo "can't create $arch container on $hostarch"
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase exit 1
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehasefi
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseif [ "$stream" != "daily" -a "$stream" != "released" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase echo "Only 'daily' and 'released' streams are supported"
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase exit 1
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehasefi
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehaseif [ -n "$userdata" ]; then
23f0ab4a423943eb59109df37074ef0c330d07c0Tim Reddehase if [ ! -f "$userdata" ]; then
653f8d7c88abc38a2c42f12b80b3e3efd882e039Tim Reddehase echo "Userdata ($userdata) does not exist"
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa exit 1
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa else
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa userdata=`readlink -f $userdata`
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa fi
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksafi
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksaif [ -n "$auth_key" ]; then
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa if [ ! -f "$auth_key" ]; then
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa echo "--auth-key=${auth_key} must reference a file"
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa exit 1
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa fi
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa auth_key=$(readlink -f "${auth_key}") ||
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa { echo "failed to get full path for auth_key"; exit 1; }
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksafi
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksaif [ -z "$path" ]; then
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa echo "'path' parameter is required"
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa exit 1
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksafi
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksaif [ "$(id -u)" != "0" ]; then
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa echo "This script should be run as 'root'"
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa exit 1
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksafi
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa# detect rootfs
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksaconfig="$path/config"
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksaif [ -z "$rootfs" ]; then
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa if grep -q '^lxc.rootfs' $config 2>/dev/null ; then
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'`
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa else
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa rootfs=$path/rootfs
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksa fi
dfc7323cf206c044e128ffc866be9d3c777be8b7Eugen Kuksafi
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksatype ubuntu-cloudimg-query
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksatype wget
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa# determine the url, tarball, and directory names
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa# download if needed
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksacache="@LOCALSTATEDIR@/cache/lxc/cloud-$release"
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksamkdir -p $cache
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksaif [ -n "$tarball" ]; then
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa url2="$tarball"
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksaelse
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa url1=`ubuntu-cloudimg-query $release $stream $arch --format "%{url}\n"`
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksa url2=`echo $url1 | sed -e 's/.tar.gz/-root\0/'`
c67b096189612bb816b4306ef88080b795b5cf41Eugen Kuksafi
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksafilename=`basename $url2`
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksa
a3bd54a5482d637ac89bbddbc27796d303544d4dEugen Kuksawgetcleanup()
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa{
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa rm -f $filename
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa}
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksabuildcleanup()
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa{
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa cd $rootfs
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa umount -l $cache/$xdir || true
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa rm -rf $cache
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa}
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa# if the release doesn't have a *-rootfs.tar.gz, then create one from the
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa# cloudimg.tar.gz by extracting the .img, mounting it loopback, and creating
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa# a tarball from the mounted image.
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksabuild_root_tgz()
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa{
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa url=$1
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa filename=$2
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa
1d39ed6dd0248b5b0c969926bae5f6f66ff4d082Eugen Kuksa xdir=`mktemp -d -p .`
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa tarname=`basename $url`
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa imgname="$release-*-cloudimg-$arch.img"
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa trap buildcleanup EXIT SIGHUP SIGINT SIGTERM
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa if [ $flushcache -eq 1 -o ! -f $cache/$tarname ]; then
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa rm -f $tarname
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa echo "Downloading cloud image from $url"
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa wget $url || { echo "Couldn't find cloud image $url."; exit 1; }
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa fi
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa echo "Creating new cached cloud image rootfs"
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa tar --wildcards -zxf $tarname $imgname
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa mount -o loop $imgname $xdir
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa (cd $xdir; tar zcf ../$filename .)
0af42668e3f55c0feaf10dc656fb423c4adaba80Eugen Kuksa umount $xdir
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa rm -f $tarname $imgname
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa rmdir $xdir
1f3a52128a4c9a5830936e25b071ce6a81fec1beEugen Kuksa echo "New cloud image cache created"
273b30f33fe1a753893887c8eb94f8078cc96928Eugen Kuksa trap EXIT
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa trap SIGHUP
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa trap SIGINT
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa trap SIGTERM
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa}
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksamkdir -p @LOCALSTATEDIR@/lock/subsys/
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa(
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa flock -x 200
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa cd $cache
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa if [ $flushcache -eq 1 ]; then
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa echo "Clearing the cached images"
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa rm -f $filename
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa fi
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa trap wgetcleanup EXIT SIGHUP SIGINT SIGTERM
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa if [ ! -f $filename ]; then
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa wget $url2 || build_root_tgz $url1 $filename
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa fi
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa trap EXIT
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa trap SIGHUP
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa trap SIGINT
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa trap SIGTERM
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa echo "Extracting container rootfs"
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa mkdir -p $rootfs
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa cd $rootfs
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa tar -zxf $cache/$filename
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa if [ $cloud -eq 0 ]; then
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa echo "Configuring for running outside of a cloud environment"
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa echo "If you want to configure for a cloud evironment, please use '-- -C' to create the container"
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa seed_d=$rootfs/var/lib/cloud/seed/nocloud-net
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa rhostid=$(uuidgen | cut -c -8)
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa host_id=${hostid:-$rhostid}
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa mkdir -p $seed_d
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksa cat > "$seed_d/meta-data" <<EOF
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen Kuksainstance-id: lxc-$host_id
b7fe8f3179594e2ebccf6ee9f562e9c2e17aab96Eugen KuksaEOF
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa if [ -n "$auth_key" ]; then
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa {
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa echo "public-keys:" &&
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa sed -e '/^$/d' -e 's,^,- ,' "$auth_key" "$auth_key"
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa } >> "$seed_d/meta-data"
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa [ $? -eq 0 ] ||
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa { echo "failed to write public keys to metadata"; exit 1; }
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa fi
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa rm $rootfs/etc/hostname
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa if [ $locales -eq 1 ]; then
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa cp /usr/lib/locale/locale-archive $rootfs/usr/lib/locale/locale-archive
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa fi
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa if [ -f "$userdata" ]; then
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa echo "Using custom user-data"
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa cp $userdata $seed_d/user-data
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa else
41a28e74548dbac9c1ee8f936906904fb91dda0cEugen Kuksa
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksa if [ -z "$MIRROR" ]; then
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksa MIRROR="http://archive.ubuntu.com/ubuntu"
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksa fi
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksa
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksa cat > "$seed_d/user-data" <<EOF
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksa#cloud-config
7dd850622b51de5132e9383683cd28370a7c4b1aEugen Kuksaoutput: {all: '| tee -a /var/log/cloud-init-output.log'}
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksaapt_mirror: $MIRROR
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksamanage_etc_hosts: localhost
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksalocale: $(/usr/bin/locale | awk -F= '/LANG=/ {print$NF}')
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksapassword: ubuntu
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksachpasswd: { expire: False }
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen KuksaEOF
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksa fi
9f4ecaa71ceb2eec7be34a2687cb8c3cb3441b05Eugen Kuksa else
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase echo "Configured for running in a cloud environment."
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase echo "If you do not have a meta-data service, this container will likely be useless."
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase fi
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase) 200>@LOCALSTATEDIR@/lock/subsys/lxc-ubuntu-cloud
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehasecopy_configuration $path $rootfs $name $arch $release
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehaseecho "Container $name created."
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehaseexit 0
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase# vi: ts=4 expandtab
db8aaeeeb3b24f487a5d02c60d18e96e55f6ba85Tim Reddehase