KVM

From Noah.org
Revision as of 03:36, 24 February 2010 by Root (talk | contribs)
Jump to navigationJump to search


KVM

Here is a simple KVM start script I've been working on. Mostly all it does it pull out all the key parameters into one place at the top of the script and it also handles setup of the bridge and tap network devices. You may hardcode the boot disk image and boot device or you may pass the image file on the command line. The script will figure out if it is CDROM or drive image.

#!/bin/sh
#
# KVM guest launcher.
#
# Copyright (c) 2010, Noah Spurrier <noah@noah.org>
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Version 1
#

KVM_GUEST_ID=1 # This should be unique on your subnet.
HOST_PHYSICAL_IF="eth2"
CDROM_ISO="ubuntu-9.10-desktop-i386.iso"
DISK_IMAGE="wiki.noah.org.img"
# Use 'c' for main disk, or 'd' for CDROM.
BOOT_DEVICE="c"

KVM_GUEST_NAME=`basename ${DISK_IMAGE} .img`
KVM_GUEST_MAC_ADDR="52:54:00:00:00:${KVM_GUEST_ID}"
# cleanup the MAC address
KVM_GUEST_MAC_ADDR="$(printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${KVM_GUEST_MAC_ADDR//:/ 0x})"
BRIDGE_IF="kvmbrif${KVM_GUEST_ID}"
TAP_IF="kvmtapif${KVM_GUEST_ID}"
SERIAL_TCP_PORT="44${KVM_GUEST_ID}"

###########################################################
##
## Parse command-line arguments
##
###########################################################

if [ -f "$1" -a $# -eq 1 ]
then
    if file $1 | grep -q "ISO 9660"
    then
        CDROM_ISO=$1
        BOOT_DEVICE="d"
    else
        # Also look for "Qemu Image" or "boot sector".
        DISK_IMAGE=$1
        BOOT_DEVICE="c"
    fi  
else
        echo "File not found: $1"
        echo "Expected a CD ISO, a drive image, or nothing."
        exit 1
fi

#        BRIDGE_IF
#           /   \
#          /     \
#  PHYSICAL_IF   TAP_IF

###########################################################
##
## Functions
##
###########################################################

create_tap ()
{
    #sudo tunctl -u kvm -t ${TAP_IF}
    sudo tunctl -t ${TAP_IF}
    sudo brctl addif ${BRIDGE_IF} ${TAP_IF}
    sudo ifconfig ${TAP_IF} up
}

destroy_tap ()
{
    sleep 1
    echo "step 1"
    sudo ifconfig ${TAP_IF} down
    sleep 1
    echo "step 2"
    sudo brctl delif ${BRIDGE_IF} ${TAP_IF}
    sleep 1
    echo "step 3"
    sudo tunctl -d ${TAP_IF}
    sleep 1
}

create_bridge ()
{
    if ! brctl show | grep -q ${BRIDGE_IF}; then
        echo "Bridge interface, ${BRIDGE_IF}, does not exit."
        echo "Attempting to create ${BRIDGE_IF} and bridge it to ${PHYSICAL_IF}."
        ifconfig ${PHYSICAL_IF} up
        sudo brctl addbr ${BRIDGE_IF}
#        sudo brctl stp ${BRIDGE_IF} on
        sudo brctl addif ${BRIDGE_IF} ${PHYSICAL_IF}
    fi
    sleep 1
    echo "Bringing up ${BRIDGE_IF}."
    sudo ifconfig ${BRIDGE_IF} up
    sleep 1
}

destroy_bridge ()
{
    if ! brctl show | grep -q ${BRIDGE_IF}; then
        echo "Bridge interface, ${BRIDGE_IF}, does not exit."
        echo "...will not attempt to destroy it."
        return 0
    fi
    echo "Attempting to destroy ${BRIDGE_IF} and bridge to ${PHYSICAL_IF}."
    sudo ifconfig ${BRIDGE_IF} down
    #sudo brctl delif ${BRIDGE_IF} ${PHYSICAL_IF}
    #sleep 1
    sudo brctl delbr ${BRIDGE_IF}
    sleep 1
}

###########################################################
##
## MAIN
##
###########################################################

date --rfc-3339=seconds

echo "Bringing up ${KVM_GUEST_NAME}"

echo "Creating bridge"
create_bridge

if ifconfig -s -a | grep -q ${TAP_IF}; then
    echo "Old TAP interface found, ${TAP_IF}. Attempting to destroy it."
    destroy_tap
fi

echo "Creating TAP device"
create_tap

echo "Starting KVM..."
kvm_cmd="sudo kvm \
    -name ${KVM_GUEST_NAME}
    -cdrom ${CDROM_ISO} \
    -drive file=${DISK_IMAGE},if=ide,index=0,cache=none,format=raw \
    -m 512 \
    -smp 1 \
    -serial stdio \
    -vga std \
    -vnc :${KVM_GUEST_ID} \
    -net nic,macaddr=${KVM_GUEST_MAC_ADDR} \
    -net tap,ifname=${TAP_IF},script=no \
    -boot ${BOOT_DEVICE}"
#    -serial tcp:127.0.0.1:${SERIAL_TCP_PORT},server,nowait \
#    -daemonize

echo "Command-line being used:"
echo "    "${kvm_cmd}
${kvm_cmd}

# FIXME
# After this point it doesn't work sp perfect. It's harmless, but 
# KVM itself apparently tries to take down the TAP interface.

if ! echo ${kvm_cmd} | grep -q daemonize; then
    echo "Destroying TAP device"
    destroy_tap
fi
destroy_bridge

date --rfc-3339=seconds

KVM Networking

Edit the file /etc/network/interfaces:

auto eth0
iface eth0 inet static
# Don't need to set eth0 since it will get bridged.
auto br0
iface br0 inet dhcp
bridge_ports eth0
bridge_fd 9
bridge_stp off
bridge_maxage 12
bridge_hello 2

edit libvirt guest XML definition

Edit the file for your given VM. In this example the guest name is model1. In my system the full path is /etc/libvirt/qemu/model1.xml. Look for the interface section and change it to a bridge something like the following:

    <interface type='bridge'>
        <mac address='54:52:00:1e:67:94'/>
        <source bridge='br0'/>
    </interface>

Restart the host's networking

You will notice a momentary interruption of the connection to the machine.

/etc/init.d/networking restart

Restart libvirtd

/etc/init.d/libvirt-bin restart

Start your libvirt client and test the connection

I use `virt-manager` or `virsh`.


KVM network console port

Add the following to /etc/rc.local, where 00:E0:81:2B:0C:C1 is the MAC address of the destination nc listener and 12345 is the listening port:

/sbin/modprobe netconsole netconsole=@/eth0,12345@10.0.0.1/00:E0:81:2B:0C:C1

The log client is just the NetCat command:

nc -dul 12345

It helps to disable sync logging. Edit /etc/syslog.conf:

 /var/log/messages

to

 -/var/log/messages

Add the following to /etc/rc.local:

echo 9 > /proc/sysrq-trigger

KVM Remote via VNC

This works quite well.

libvirtd

Libvirt does not do the actual virtualization itself. It is just a client-server interface to virtual machines. This allows you to manage the guests remotely through a simple GUI interface. I have not decided if this tool is worth the trouble or not. It's simple to just SSH into a server and manage it from the command-line. Libvirt is still a little rough. It's very fragile. You will find that you have to restart it often:

/etc/init.d/libvirt-bin restart

virsh

The XML config files are stored here: /etc/libvirt/qemu/*.xml.

virsh -c qemu:///system

Common commands. Note that domain is the guest name. Also note that list should really be called list_only_running; you want the --all option to make it actually list all the domains no matter what state they are in.

help
list --all
domstate [domain]
start [domain]
destroy [domain]

localhost

Multiple VNC screen sessions can run on the same machine. By default each session starts at port 5900+session_number. So if you are running just one VNC screem it will usually be mapped to port 5900; the next one will be mapped to 5901; and the next one 5902 and so on.

ssh -N -f -L 5900:192.168.1.13:5900 username@gateway.example.com
vncviewer AutoSelect=0 FullColor=1 localhost

Or like this to view VNC session 0 on the machine 192.168.1.13.

vncviewer AutoSelect=0 FullColor=1 -via username@gateway.example.com 192.168.1.13:0

session 1 on the machine 192.168.1.13:

vncviewer AutoSelect=0 FullColor=1 -via username@gateway.example.com 192.168.1.13:1

Note that when you use via the names and addresses are from the point of view of the gateway machine. So machine 192.168.1.13 on the LAN visible to the gateway machine. You could also just say localhost if you want to connect to a VNC session running on the gateway machine itself.

vncviewer AutoSelect=0 FullColor=1 -via username@gateway.example.com localhost

So why use via at all in that case? Why not just do this?

vncviewer AutoSelect=0 FullColor=1 username@gateway.example.com

Remember that we may still be going through a firewall. Firewalls rarely allow the VNC ports 5900 through. The via option says to go through SSH (port 22), which most firewalls will allow -- plus this adds SSH encryption to the session.

remote KVM server

sudo virt-install --connect qemu:///system --name ubuntu910 --force --ram 512 --file ubuntu910.qcow2 --file-size 12 --cdrom ubuntu-9.10-desktop-i386.iso --vnc --noautoconsole --accelerate --os-type linux --os-variant generic26

Error: Rect too big

This is a bug in the vncviewer. Add these options to the command-line:

AutoSelect=0 FullColor=1
vncviewer AutoSelect=0 FullColor=1 -via username@gateway.example.com localhost:0