KVM on Linux

I've been using some shell scripts to start and stop KVM based virtual machines on my Gentoo Linux box. Maybe someone finds this stuff useful.

Installation

  1. Create a directory hierarchy containing subdirectories named bin, disks, lib, and tmp, e.g:
    $ mkdir -p ~/kvm/{bin,disks,lib,tmp}
    
  2. Download kvmlib.sh and put it into ~/kvm/lib/
  3. Download taputil, put it into ~/kvm/bin/, make it executable, and create /etc/sudoers.d/kvm (assuming that your user is member of the kvm group on your system):
    %kvm	ALL=(ALL) NOPASSWD: YOUR_HOME_DIRECTORY/kvm/bin/taputil
    
    taputil is called by kvmlib.sh right before launching qemu and after stopping a virtual machine. It creates interfaces named brXX and tapXXYY with XX being the XX-nth network card, while YY denotes the number of your VM. For example, if you have two virtual machines each with one network card, and a third one with two network cards, you will end up having these bridges and tap interfaces:
    $ brctl show
    bridge name	bridge id		STP enabled	interfaces
    br00		8000.00000000000	no		bond0		# physical card(s) in your host
    							tap0000		# 1st nic of 1st vm
    							tap0001		# 1st nic of 2nd vm
    							tap0002		# 1st nic of 3rd vm
    br01		8000.000000000000	no		
    							tap0102		# 2nd nic of 3rd vm
    
    On Gentoo put something like this to /etc/conf.d/net:
    # bundle ethernet and wireless card and create a failover trunk
    slaves_bond0="eno2 wlo1"
    mode_bond0="active-backup"
    miimon_bond0="500"
    config_bond0="null"
    
    # physically enable ethernet card
    config_eno2="null"
    
    # activate wireless card via wpa_supplicant
    modules="wpa_supplicant"
    config_wlo1="null"
    
    # bond0 becomes first member of bridge br00
    # each first nic of any VM will be connected to br00 resulting in "bridged networking"
    bridge_br00="bond0"
    config_br00="192.168.23.42/24"
    routes_br00="default via 192.168.23.1 initrwnd 32 initcwnd 32"
    
    # bridge br01 gets any secondary nic of all VMs, and serves as host-only networking
    bridge_br01=""
    config_br01="null"
    
    # lower timeout for IPv6 duplicate address detection to 3 seconds
    dad_timeout=3
    
  4. Create a harddisk sparse image for your virtual machine, e.g. a 10 GB image (the trailing .raw is important):
    $ truncate -s 10G ~/kvm/disks/some.host.name.raw
    
    Or:
    $ dd if=/dev/zero of=~/kvm/disks/some.host.name.raw bs=1 count=0 seek=10G
    
    Alternatively, download my shell script mkdisk, put it into ~/kvm/bin/, make it executable, and call
    $ ~/kvm/bin/mkdisk some.host.name 10
    
    If you prefer qcow, then you have to use qemu-img:
    $ qemu-img create -f qcow2 some.host.name.qcow2 10G
    
    In this case, also add VM_FILE_FORMAT0="qcow2" to shell script of this VM (see next step).
  5. Start that virtual machine:
    $ ~/kvm/bin/0some.host.name.sh start
    
    To stop it:
    $ ~/kvm/bin/0some.host.name.sh stop
    
    Any additional parameters are directly passed to qemu-kvm:
    $ ~/kvm/bin/0some.host.name.sh start -cdrom /data/isos/freebsd.iso
    
  6. To connect to that virtual machine's console:
    $ ~/kvm/bin/0some.host.name.sh vnc
    
    Or call vncviewer directly:
    $ vncviewer :0
    
  7. To change a cdrom:
    $ telnet localhost 6666
    (qemu) change ide1-cd0 /data/isos/netbsd.iso
    
    To eject it:
    (qemu) eject ide1-cd0
    
  8. To start and stop virtual machines during system boot or shutdown, save the following script as ~/kvm/bin/kvm:
    #!/bin/sh
    
    kvm_stop ()
    {
      local vm
      vm="$1"
      [ -z "$vm" ] && return
      [ ! -f "$vm" ] && return
      shift
      kvm_stop "$@"
      sh "./$vm" "stop" &
    }
    
    myself=`readlink "$0"`
    if [ -z "$myself" ]; then
      myself="$0"
    fi
    
    cd "`dirname "$myself"`/../bin" || exit 1
    
    action="$1"
    
    case "$action" in
      start|stop)
        ;;
      *)
        action="${0##*.}"
        ;;
    esac
    
    case "$action" in
      start)
        for vm in *.sh; do
          [ -x "$vm" ] && "./$vm" "$action" &
        done
        ;;
      stop)
        kvm_stop *.sh
        ;;
      *)
        echo "usage: $0 <start | stop>"
        exit 1
        ;;
    esac
    
    wait
    
    Then symlink the above script as /etc/local.d/kvm.start and /etc/local.d/kvm.stop. That way, any running virtual machine will be shut down when your host system reboots.
  9. If your virtual machine experiences problems with IPv6 or multicast, then disable multicast snooping on all bridge interfaces, e.g. either add the following line to /etc/udev/rules.d/99-local.rules:
    SUBSYSTEM=="net",ACTION=="add",KERNEL=="br*",ATTR{bridge/multicast_snooping}="0"
    
    and do a udevadm trigger (or reboot your host). Or create /etc/local.d/nobrsnoop.start (don't forget to make it executable):
    #!/bin/sh
    for f in /sys/class/net/*/bridge/multicast_snooping; do
      [ -f "$f" ] && echo 0 > "$f"
    done
    
    Then run it once or reboot your host.
  10. Optionally, download and compile zeroes.cs on your Windows VM:
    C:\Users\dude>\Windows\Microsoft.NET\Framework\<latest .net version>\csc.exe zeroes.cs
    
    Then, from time to time, simply start zeroes.exe. It will write zeroes (ASCII 0) to a file called _ZEREOS.BIN in the current directory. This give qemu a chance to reclaim disk space.

Files

kvmlib.sh

taputil

zeroes