262 lines
7.0 KiB
Bash
Executable File
262 lines
7.0 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -x
|
|
|
|
echo "VER-0.1.0-1.2"
|
|
|
|
# Returns the integer representation of an IP arg, passed in ascii
|
|
# dotted-decimal notation (x.x.x.x)
|
|
atoi() {
|
|
IP=$1; IPNUM=0
|
|
for (( i=0 ; i<4 ; ++i )); do
|
|
((IPNUM+=${IP%%.*}*$((256**$((3-${i}))))))
|
|
IP=${IP#*.}
|
|
done
|
|
echo $IPNUM
|
|
}
|
|
|
|
# Returns the dotted-decimal ascii form of an IP arg passed in integer
|
|
# format
|
|
itoa() {
|
|
echo -n $(($(($(($((${1}/256))/256))/256))%256)).
|
|
echo -n $(($(($((${1}/256))/256))%256)).
|
|
echo -n $(($((${1}/256))%256)).
|
|
echo $((${1}%256))
|
|
}
|
|
|
|
generate_cloud_drive() {
|
|
metadata=/metadata
|
|
if [ ! -f $metadata ]; then
|
|
metadata=""
|
|
fi
|
|
|
|
userdata=/userdata
|
|
if [ ! -f $userdata ]; then
|
|
userdata=""
|
|
fi
|
|
|
|
if [ "$metadata" == "" -a "$userdata" == "" ]; then
|
|
return
|
|
fi
|
|
|
|
TMPDIR=`mktemp -d -t aicvm.XXXXXX`
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "Fail to create temporaily directory"
|
|
exit 1
|
|
fi
|
|
|
|
# create form of config drive
|
|
mkdir -p ${TMPDIR}/openstack/2012-08-10
|
|
OLD_PWD=$PWD
|
|
cd ${TMPDIR}/openstack
|
|
ln -s 2012-08-10 latest
|
|
cd $OLD_PWD
|
|
|
|
if [ -f $metadata ]; then
|
|
cp $metadata ${TMPDIR}/openstack/2012-08-10/meta_data.json
|
|
fi
|
|
if [ -f $userdata ]; then
|
|
cp $userdata ${TMPDIR}/openstack/2012-08-10/user_data
|
|
fi
|
|
|
|
iso="cloud-drive.iso"
|
|
mkisofs -R -V config-2 -o $iso ${TMPDIR}
|
|
if [ $? -ne 0 ]; then
|
|
echo Fail to create cloud-drive ISO image for cloud-init
|
|
exit 1
|
|
fi
|
|
echo $iso
|
|
}
|
|
|
|
# Generate random new MAC address
|
|
hexchars="0123456789ABCDEF"
|
|
end=$( for i in {1..8} ; do echo -n ${hexchars:$(( $RANDOM % 16 )):1} ; done | sed -e 's/\(..\)/:\1/g' )
|
|
NEWMAC=`echo 06:FE$end`
|
|
|
|
# These two variables can be overwritten
|
|
: ${KVM_BLK_OPTS:="-drive file=\$KVM_IMAGE,if=none,id=drive-disk0,format=qcow2 \
|
|
-device virtio-blk-pci,scsi=off,drive=drive-disk0,id=virtio-disk0,bootindex=1"}
|
|
: ${KVM_RAW_BLK_OPTS:="-drive file=\$KVM_IMAGE,if=none,id=drive-disk0,format=raw \
|
|
-device virtio-blk-pci,scsi=off,drive=drive-disk0,id=virtio-disk0,bootindex=1"}
|
|
: ${KVM_NET_OPTS:="-netdev bridge,br=\$BRIDGE_IFACE,id=net0 \
|
|
-device virtio-net-pci,netdev=net0,mac=\$NEWMAC"}
|
|
|
|
# define some valeus for the VM side of the networking but
|
|
# allow them to be overriden by the operator
|
|
: ${VM_IP:="192.168.254.2"}
|
|
: ${VM_GW:="192.168.254.1"}
|
|
|
|
# the netmask is not definable, as we leverage
|
|
# /30 elsewhere
|
|
VM_NETMASK="255.255.255.252"
|
|
|
|
# For debugging
|
|
if [ "$1" = "bash" ]; then
|
|
exec bash
|
|
fi
|
|
|
|
# Pass Docker command args to kvm
|
|
KVM_ARGS=$@
|
|
|
|
# Create the qcow disk image on the Docker volume named /image, using
|
|
# the compressed qcow image that came with Docker image as the base.
|
|
# Docker volumes typically perform better than the file system for
|
|
# Docker images (no need for overlay fs etc.)
|
|
|
|
if [ -e /dev/vm/root ]; then
|
|
KVM_BLK_OPTS="$KVM_RAW_BLK_OPTS"
|
|
KVM_IMAGE=/dev/vm/root
|
|
else
|
|
|
|
if [ -e "${IMG_TARGET}" ]; then
|
|
BASE=${IMG_TARGET}
|
|
else
|
|
|
|
if [ ! -d "/image" ]; then
|
|
echo "/image directory does not exist, failed to mount volume?"
|
|
exit 2
|
|
fi
|
|
|
|
if [ ! -e "/image/${IMG_TARGET}" ]; then
|
|
echo "Fetching missing image target"
|
|
curl ${IMG_SOURCE} > /image/${IMG_TARGET}
|
|
fi
|
|
|
|
BASE=/image/${IMG_TARGET}
|
|
fi
|
|
|
|
if [ ! -d "/image" ]; then
|
|
echo "/image directory does not exist, failed to mount volume /image?"
|
|
exit 2
|
|
fi
|
|
|
|
if [ -z "${HOSTNAME}" ]; then
|
|
echo "Could not find HOSTNAME var. Did you specify a HOSTNAME environment variable?"
|
|
fi
|
|
|
|
KVM_IMAGE=/image/${HOSTNAME}.qcow2
|
|
|
|
if [ -e "${KVM_IMAGE}" ]; then
|
|
echo "Image ${KVM_IMAGE} already exists. Not recreating"
|
|
else
|
|
qemu-img create -f qcow2 -b ${BASE} \
|
|
$KVM_IMAGE > /dev/null
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "Failed to create qcow2 image"
|
|
exit 3
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
VOLUMES_DIR="/volumes/"
|
|
VOLUMES_LIST=`find $VOLUMES_DIR -name "*.img" | sort -d`
|
|
extra_kvm_blk_opts=""
|
|
for volume in $VOLUMES_LIST /dev/vm/disk* ; do
|
|
if [ -e $volume ]; then
|
|
extra_kvm_blk_opts=$extra_kvm_blk_opts" -drive file=$volume,if=virtio,format=raw"
|
|
fi
|
|
done
|
|
KVM_BLK_OPTS=$KVM_BLK_OPTS$extra_kvm_blk_opts
|
|
|
|
# Network setup:
|
|
#
|
|
# 1. Create a bridge named br0
|
|
# 2. Remove IP from eth0, save eth0 MAC, give eth0 a random MAC
|
|
|
|
IFACE=eth0
|
|
BRIDGE_IFACE=br0
|
|
|
|
cidr2mask() {
|
|
local i mask=""
|
|
local full_octets=$(($1/8))
|
|
local partial_octet=$(($1%8))
|
|
|
|
for ((i=0;i<4;i+=1)); do
|
|
if [ $i -lt $full_octets ]; then
|
|
mask+=255
|
|
elif [ $i -eq $full_octets ]; then
|
|
mask+=$((256 - 2**(8-$partial_octet)))
|
|
else
|
|
mask+=0
|
|
fi
|
|
test $i -lt 3 && mask+=.
|
|
done
|
|
|
|
echo $mask
|
|
}
|
|
|
|
setup_bridge_networking() {
|
|
|
|
MAC=`ip addr show $IFACE | grep ether | sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g' | cut -f2 -d ' '`
|
|
HOST_IP=`ip addr show dev $IFACE | grep "inet $IP" | awk '{print $2}' | cut -f1 -d/`
|
|
HOST_CIDR=`ip addr show dev $IFACE | grep "inet $IP" | awk '{print $2}' | cut -f2 -d/`
|
|
HOST_NETMASK=`cidr2mask $HOST_CIDR`
|
|
HOST_GATEWAY=`ip route get 8.8.8.8 | grep via | cut -f3 -d ' '`
|
|
NAMESERVER=( `grep nameserver /etc/resolv.conf | grep -v "#" | cut -f2 -d ' '` )
|
|
NAMESERVERS=`echo ${NAMESERVER[*]} | sed "s/ /,/"`
|
|
SEARCH=( `grep -E ^search /etc/resolv.conf | grep -v "#" | cut -f2- -d ' ' | tr ' ' ','` )
|
|
|
|
# we must enable forwarding inside the container
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
|
|
# we support exposing port 5900 on the container but leave
|
|
# it up to the operator on whether to expose this - they can
|
|
# specify NO_VNC as an environment variable to disable this
|
|
# functionality
|
|
if [ -z $NO_VNC ]; then
|
|
iptables -t nat -A PREROUTING -p tcp \! --dport 5900 -d $HOST_IP -j DNAT --to-destination $VM_IP
|
|
iptables -t nat -A POSTROUTING -s $VM_IP -j SNAT --to-source $HOST_IP
|
|
else
|
|
iptables -t nat -A PREROUTING -d $HOST_IP -j DNAT --to-destination $VM_IP
|
|
iptables -t nat -A POSTROUTING -s $VM_IP -j SNAT --to-source $HOST_IP
|
|
fi
|
|
|
|
# generate VM specifics
|
|
cat > /etc/dnsmasq.conf << EOF
|
|
user=root
|
|
dhcp-range=$VM_IP,$VM_IP
|
|
dhcp-host=$NEWMAC,$HOSTNAME,$VM_IP,infinite
|
|
dhcp-option=option:router,$VM_GW
|
|
dhcp-option=option:netmask,$VM_NETMASK
|
|
dhcp-option=option:dns-server,$NAMESERVERS
|
|
dhcp-option=119,$SEARCH
|
|
EOF
|
|
|
|
if [ -z $NO_DHCP ]; then
|
|
dnsmasq
|
|
fi
|
|
|
|
brctl addbr $BRIDGE_IFACE
|
|
ip link set dev $BRIDGE_IFACE up
|
|
ip addr add $VM_GW/30 dev $BRIDGE_IFACE
|
|
|
|
# alanmeadows(NOTE) in many implementations with out of
|
|
# subnet gateways the dhcp approach does not work
|
|
# if [ -z $NO_DHCP ]; then
|
|
# ip addr add $NEWIP/$NEWCIDR dev $BRIDGE_IFACE
|
|
# fi
|
|
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "Failed to bring up network bridge"
|
|
exit 4
|
|
fi
|
|
|
|
# Exec kvm as PID 1
|
|
mkdir -p /etc/qemu
|
|
echo allow $BRIDGE_IFACE > /etc/qemu/bridge.conf
|
|
}
|
|
|
|
# need to wait until network is ready
|
|
ISO=`generate_cloud_drive`
|
|
if [[ $ISO ]]; then
|
|
KVM_BLK_OPTS=$KVM_BLK_OPTS" -cdrom $ISO"
|
|
fi
|
|
|
|
setup_bridge_networking
|
|
|
|
HOST_IP=`ip addr show dev $IFACE | grep "inet $IP" | awk '{print $2}' | cut -f1 -d/`
|
|
VNC="-vnc $HOST_IP:0"
|
|
|
|
exec $LAUNCHER qemu-system-x86_64 -enable-kvm $VNC `eval echo $KVM_BLK_OPTS` `eval echo $KVM_NET_OPTS` -usbdevice tablet -nographic $KVM_ARGS
|