Created
November 19, 2012 12:18
-
-
Save wrouesnel/4110362 to your computer and use it in GitHub Desktop.
A script to automate the setup of Debian-like LXC containers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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