Skip to content

Instantly share code, notes, and snippets.

@wrouesnel
Created November 19, 2012 12:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wrouesnel/4110362 to your computer and use it in GitHub Desktop.
Save wrouesnel/4110362 to your computer and use it in GitHub Desktop.
A script to automate the setup of Debian-like LXC containers.
#!/bin/bash
# LXC test environment builder for libvirt.
# Libvirt uses a different environment to the Ubuntu implementation, since
# cgroups are a rather general purpose protocol.
# The virsh manipulation stuff is pretty brute-force and inelegant. This whole
# thing really demands a GUI of some such, but it's a script which got out of
# hand really.
# Usage: make-libvirt-lxc [ROOT] <HOSTNAME>
trap `echo interrupted; [ ! -z $TMPXML ] && [ -e $TMPXML ] && rm $TMPXML ; exit` INT
# List of ports to poll for active apt-proxys
APTPROXYPORTS=( 3142 9999 )
# Non-stdout echo command
echoerr ()
{
echo $@ 1>&2
}
usage ()
{
cat << EOF 1>&2
usage: $(basename $0) [options] <path to create container in>
The container creation folder must exist and be empty.
-i <network name> libvirt network name to assign to host
note: specify none to indicate no network should be
configured and none prompted for.
-n <hostname> hostname of LXC container
-p apt-proxy address to assign (should be of form http://proxy:port)
-h show this help.
EOF
#do
# Fluff to avoid breaking syntax highlighting
exit 1
}
# Fatal error that should terminate the script
# Usage: fatal <exit code> <error string>
fatal ()
{
echo "${@:2}" 2>&1
exit $1
}
# Check script dependencies
check_dependencies ()
{
# Check for virsh
if [ -z $(which virsh) ]; then
fatal 1 "Could not locate virsh executable."
fi
# Check for nc
if [ -z $(which nc) ]; then
fatal 1 "Could not locate nc executable."
fi
}
# Sets the LXC root.
# Usage: set_lxcroot <optional path>
set_lxcroot ()
{
# Remaining option is hostname
if [ -z $1 ]; then
LXCROOT=$(pwd)
else
if [[ "$1" = /* ]]
then
# Absolute path
LXCROOT=$1
else
# Relative path
LXCROOT=$(pwd)/$1
fi
fi
# Sanity check the new root location
# Do not accept a blank location
if [ -z $LXCROOT ]; then
fatal 1 "Location is blank."
fi
# Check it exists
if [ ! -d $LXCROOT ]; then
fatal 1 "$LXCROOT does not appear to be a valid directory."
fi
# Check if location is empty (will prevent like 99% of screw ups)
if [ "$(ls -A $LXCROOT)" ]; then
fatal 1 "$LXCROOT is not empty."
fi
# Also check location does not evaluate to root
if [ "/" -ef "$LXCROOT" ]; then
fatal 1 "$LXCROOT is /. Refusing to run on /."
fi
echoerr "Location checks successful."
echoerr "Container will be installed to: $LXCROOT"
}
# Optional hostname parameter. If left blank then default to the basename of
# the directory we're running in. Could cause conflicts, but nothing serious
# and its user error if it does.
set_lxchostname ()
{
if [ -z $LXCHOSTNAME ]; then
LXCHOSTNAME=$(basename $LXCROOT)
echoerr "Hostname defaulting: $LXCHOSTNAME"
else
echoerr "Hostname specified: $LXCHOSTNAME"
fi
}
acquire_localaptproxy ()
{
# Acquire the current apt proxy setting, if any.
export http_proxy=$(apt-config dump | grep Acquire::http::proxy | cut -d'"' -f2)
if [ ! -z http_proxy ]; then
echo "Found a proxy to use for APT: $http_proxy"
fi
}
# Cleans a debootstrap image so it will boot successfully in LXC.
lxcroot_clean ()
{
# Clear out /dev (managed by libvirt)
echoerr "Clearing out redundant $LXCROOT/dev"
rm -r "$LXCROOT/dev"
mkdir $LXCROOT/dev
# Fix up securetty so we can login.
echo "Fixing securetty..." 1>&2
cat << EOF >> $LXCROOT/etc/securetty
# libVirt LXC consoles
pts/0
pts/1
pts/2
pts/3
EOF
}
lxcroot_sethostname ()
{
# Set hostname
echoerr "Setting hostname..."
echo $LXCHOSTNAME > $LXCROOT/etc/hostname
}
lxcroot_setpasswd ()
{
# Copy root password from /etc/shadow to the new container
echoerr "Adding root password to $LXCROOT/etc/shadow"
cat $LOCATION/etc/shadow | awk -v r=$(cat /etc/shadow | grep root) -F: '{if(tolower($0) ~ /root/) $0 = r; print $0}' > $LXCROOT/etc/shadow.new && mv -f $LXCROOT/etc/shadow.new $LXCROOT/etc/shadow
}
# Adds DHCP to eth0 so the domain will pickup network configuration automatically.
# Only invoked if the domain is connected to a network to start with.
lxcroot_configurenetwork ()
{
# Check if network should be configured.
if [ -z $LXCNETNAME ]; then
echoerr "No network specified for guest."
return
fi
# Set network defaults
echoerr "Configuring network..."
cat << EOF >> $LXCROOT/etc/network/interfaces
# Default virt-io adapter
auto eth0
iface eth0 inet dhcp
EOF
}
lxcroot_getdist ()
{
# Set the distribution parameter.
local DIST=$(lsb_release --short --codename)
# Distribution name remapping
local ORIGINALDIST=$DIST
case "$DIST" in
maya) # Linux Mint - use ubuntu components
local DIST=precise
;;
*)
echoerr "Distribution Name Remapping not needed: $DIST"
;;
esac
if [[ "$ORIGINALDIST" != "$DIST" ]]; then
echoerr "Distribution Name Remapped: $ORIGINALDIST -> $DIST"
fi
# This is a return code.
echo $DIST
}
# Sets the apt proxy for the root.
lxcroot_setaptproxy ()
{
if [ ! -z $LXCAPTPROXY ]; then
echoerr "Configuring apt proxy for container..."
cat << EOF > $LXCROOT/etc/apt/apt.conf.d/30proxy
Acquire::http::proxy "$LXCAPTPROXY";
EOF
fi
}
# Prompts for the network to connect the host too
virsh_getnetwork ()
{
# No network.
if [[ $LXCNETNAME == "none" ]]; then
LXCNETNAME=
return
fi
# Check if network is valid.
while ! virsh net-info $LXCNETNAME 1> /dev/null 2>&1 ; do
virsh net-list --all 1>&2
echoerr -n "Enter the name of network to connect the host to, or type $n \
'none' to avoid configuring a network at this time: "
read LXCNETNAME
if [[ $LXCNETNAME == "none" ]]; then
echoerr "No network will be configured."
LXCNETNAME=
break
fi
done
}
virsh_checkaptproxy ()
{
# Check if we're configuring a network
if [ -z $LXCNETNAME ]; then
return
fi
# Check if we need to do this
if [ ! -z $LXCAPTPROXY ]; then
return
fi
# Get the gateway of our virtual network
local NETGATEWAY=$(virsh net-dumpxml $LXCNETNAME | grep "\<ip address" | cut -d"'" -f2)
for PORT in ${APTPROXYPORTS[@]}
do
# Poll it to see if there's an http proxy there
if nc -z $NETGATEWAY $PORT; then
LXCAPTPROXY="http://$NETGATEWAY:$PORT"
echoerr "Apt-Proxy for container found: $LXCAPTPROXY"
return
fi
done
echoerr "No apt-proxy found on guest network interface."
}
virsh_createdomain ()
{
# Check if the domain already exists and abort creation if it does.
virsh --connect lxc:/// dominfo $1 1> /dev/null 2>&1
if [[ $? == 0 ]]; then
fatal 2 "$1 already exists. Aborting attempt to recreate."
fi
# Try and get a temporary file for XML.
local TMPXML=$(mktemp)
if [[ $? != 0 ]]; then
fatal 2 "Couldn't make a temporary file. Aborting domain creation."
fi
# Create the virtual-machine in Virsh
# Note this assumes there's a network name'
echoerr "Configuring libvirt domain..."
cat << EOF > $TMPXML
<domain type='lxc'>
<name>$1</name>
<memory>2097152</memory>
<currentMemory>2097152</currentMemory>
<vcpu>1</vcpu>
<os>
<type arch='x86_64'>exe</type>
<init>/sbin/init</init>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>
<emulator>/usr/lib/libvirt/libvirt_lxc</emulator>
<filesystem type='mount' accessmode='passthrough'>
<source dir='$LXCROOT'/>
<target dir='/'/>
</filesystem>
EOF
# Check if we're configuring a network
if [ ! -z $LXCNETNAME ]; then
cat << EOF >> $TMPXML
<interface type='network'>
<source network='$LXCNETNAME'/>
</interface>
EOF
fi
# Finish up the rest of the configuration
cat << EOF >> $TMPXML
<console type='pty'>
<target type='lxc' port='0'/>
</console>
</devices>
</domain>
EOF
# Define the VM
virsh --connect lxc:/// define $TMPXML
local VIRSHRETURN=$?
# Clear temp file
rm $TMPXML
return $VIRSHRETURN
}
################
# Actual Script
# Enforce root execution for script.
if [[ $EUID -ne 0 ]]; then
#sudo "$0 $@"
echoerr "Must run as root!"
exit $?
fi
check_dependencies
while getopts ":n:i:h" option; do
case $option in
h) usage ;;
n) LXCHOSTNAME="$OPTARG" ;;
p) LXCAPTPROXY="$OPTARG" ;;
i) # provide for a "none", which suppresses name prompting
if [[ $OPTARG != "none" ]]; then
LXCNETNAME="$OPTARG"
fi
;;
:) fatal 1 "Error: -$option requires an argument." ;;
?) fatal 1 "Error: -$option unknown option." ;;
esac
done
# Access remaining options (should be just 1)
shift $(($OPTIND - 1))
if [[ $# > 1 ]]; then
fatal 1 "Too many arguments. Should be 1."
fi
# Set variables for building the root
set_lxcroot $1
set_lxchostname
acquire_localaptproxy
# Get container configurations
virsh_getnetwork
virsh_checkaptproxy
# Create the domain
virsh_createdomain $LXCHOSTNAME
if [[ $? != 0 ]]; then
fatal 1 "Domain creation failed. Aborting."
fi
## Build the root:
debootstrap --arch $(dpkg --print-architecture) $(lxcroot_getdist) "$LXCROOT"
if [[ $? != 0 ]]; then
# Uncreate the domain before terminating.
echoerr "Debootstrap failed. Removing libvirt domain."
virsh destroy $LXCHOSTNAME
fatal 1 "Debootstrap failed."
fi
## Configure the root
lxcroot_clean
lxcroot_sethostname
lxcroot_setpasswd
lxcroot_configurenetwork
lxcroot_setaptproxy
# We should be all done here.
echoerr "Successfully created a libvirt LXC domain!"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment