#!/bin/sh # Define and install a libvirt domain # Copyright © 2013 Guilhem Moulin # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set -ue PATH=/usr/bin:/bin root=$(dirname "$0") usage() { cat >&2 <<-EOF Usage: $0 install [] Install the given domain, using the given image (created if necessary) as a disk. $0 view Open a connection to a running domain using the Spice protocol EOF exit 1 } getXMLattr() { local id="$1" object="$2" attribute="$3" what="${4:-domain}" local cmd case "$what" in domain) cmd=;; network) cmd=net;; esac virsh "${cmd:+$cmd-}dumpxml" "$id" \ | xmlstarlet sel -t -m "/$what/$object" -n -v @"$attribute" \ | sed '/^\s*$/d' } add_net_section() { local section="$1" xml="$2" net="${3:-$network}" virsh net-update "$net" add "$section" --xml "$xml" --live --config } doesExist() { virsh -r list --all --name | grep -qxF "$1" } isActive() { virsh -r list --name | grep -qxF "$1" } install() { [ $# -eq 1 -o $# -eq 2 ] || usage local name=$1 local disk=${2:-$root/images/${name}.qcow2} local darch case "${ARCH:=$(/bin/uname -m)}" in i386|i686) darch=i386;; x86_64) darch=amd64;; *) darch=$ARCH;; esac local version=$(sed -n '/^VERSION\s*=\s*/ {s///p;q}' $root/../preseed/Makefile) local cdrom="$root/../preseed/dist/$darch/debian-$version-$darch-netinst-preseeded.iso" if doesExist "$name"; then echo "Error: Domain $name already exists. If you want to replace the domain, run" >&2 echo >&2 isActive "$name" && echo " virsh destroy $name" >&2 echo " virsh undefine $name" >&2 exit 1 fi if ! [ -f "$cdrom" -a -r "$cdrom" ]; then cat >&2 <<- EOF Error: '$cdrom' does not exist or is not readable. To create the preseeded image, run cd $root/../preseed ARCH=$darch make iso-preseed EOF exit 1 fi grep -q '^kvm\s' /proc/modules || echo 'WARN: KVM not available!' >&2 if [ ! -f "$disk" ]; then local size=12G echo "Creating (sparse) $size disk image '$disk'" >&2 qemu-img create -f "${disk##*.}" -o size="$size",preallocation=metadata "$disk" fi # TODO: the bus should be chosen at random among sata,ide,scsi,usb # TODO: test EFI: http://www.linux-kvm.org/page/OVMF local network=default ip mac virt-install -q \ --name "$name" \ --arch "$ARCH" \ --ram 128 \ --cdrom "$cdrom" \ --disk "$disk",cache=writeback,bus=sata \ --network network="$network" \ --graphics spice # Attribute the next available IP. You may also want to add a domain # section, e.g., , and prepend # the DNS IP to /etc/resolv.conf. # Cf. http://libvirt.org/formatnetwork.html mac=$( getXMLattr $name devices/interface/mac address ) ip=$( getXMLattr $network ip/dhcp/host ip network | sort -n | tail -1 ) [ "$ip" ] || \ ip=$( getXMLattr $network ip address network ) ip=${ip%.*}.$(( 1 + ${ip##*.} )) add_net_section ip-dhcp-host "" add_net_section dns-host "$name" cat >&2 <<- EOF Domain '$name' has been created, and has been given IP $ip. As you can see in virsh list it should now be up and running. To open a graphical console, run $0 view $name EOF } view(){ [ $# -eq 1 ] || usage local domain="$1" if ! doesExist "$domain"; then cat >&2 <<- EOF Error: Domain $domain does not exist. To create it, run ./virt install $domain EOF exit 1 elif ! isActive "$domain"; then echo "Error: Domain $domain is not active" >&2 exit 1 fi local type=$(getXMLattr "$domain" devices/graphics type) local port=$(getXMLattr "$domain" devices/graphics port) || true if ! [ "$type" = spice -a "$port" ]; then echo "Error: Could not find a valid Spice server on domain '$domain'." >&2 exit 1 fi spicec -h 127.0.0.1 -p $port } [ $# -gt 0 ] || usage command="$1" shift; case "$command" in install|view) $command "$@";; *) usage esac