Blame tools/xkvm

Packit bc9a3a
#!/bin/bash
Packit bc9a3a
# This file is part of cloud-init.
Packit bc9a3a
# See LICENSE file for copyright and license info.
Packit bc9a3a
Packit bc9a3a
set -f
Packit bc9a3a
Packit bc9a3a
VERBOSITY=0
Packit bc9a3a
KVM_PID=""
Packit bc9a3a
DRY_RUN=false
Packit bc9a3a
TEMP_D=""
Packit bc9a3a
DEF_BRIDGE="virbr0"
Packit bc9a3a
TAPDEVS=( )
Packit bc9a3a
# OVS_CLEANUP gets populated with bridge:devname pairs used with ovs
Packit bc9a3a
OVS_CLEANUP=( )
Packit bc9a3a
MAC_PREFIX="52:54:00:12:34"
Packit bc9a3a
# allow this to be set externally.
Packit bc9a3a
_QEMU_SUPPORTS_FILE_LOCKING="${_QEMU_SUPPORTS_FILE_LOCKING}"
Packit bc9a3a
KVM="kvm"
Packit bc9a3a
declare -A KVM_DEVOPTS
Packit bc9a3a
Packit bc9a3a
error() { echo "$@" 1>&2; }
Packit bc9a3a
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
Packit bc9a3a
Packit bc9a3a
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; }
Packit bc9a3a
randmac() {
Packit bc9a3a
    # return random mac addr within final 3 tokens
Packit bc9a3a
    local random=""
Packit bc9a3a
    random=$(printf "%02x:%02x:%02x" \
Packit bc9a3a
        "$((${RANDOM}%256))" "$((${RANDOM}%256))" "$((${RANDOM}%256))")
Packit bc9a3a
    padmac "$random"
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
cleanup() {
Packit bc9a3a
    [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
Packit bc9a3a
    [ -z "${KVM_PID}" ] || kill "$KVM_PID"
Packit bc9a3a
    if [ ${#TAPDEVS[@]} -ne 0 ]; then
Packit bc9a3a
        local name item
Packit bc9a3a
        for item in "${TAPDEVS[@]}"; do
Packit bc9a3a
            [ "${item}" = "skip" ] && continue
Packit bc9a3a
            debug 1 "removing" "$item"
Packit bc9a3a
            name="${item%:*}"
Packit bc9a3a
            if $DRY_RUN; then
Packit bc9a3a
                error ip tuntap del mode tap "$name"
Packit bc9a3a
            else
Packit bc9a3a
                ip tuntap del mode tap "$name"
Packit bc9a3a
            fi
Packit bc9a3a
            [ $? -eq 0 ] || error "failed removal of $name"
Packit bc9a3a
        done
Packit bc9a3a
        if [ ${#OVS_CLEANUP[@]} -ne 0 ]; then
Packit bc9a3a
            # with linux bridges, there seems to be no harm in just deleting
Packit bc9a3a
            # the device (not detaching from the bridge).  However, with
Packit bc9a3a
            # ovs, you have to remove them from the bridge, or later it
Packit bc9a3a
            # will refuse to add the same name.
Packit bc9a3a
            error "cleaning up ovs ports: ${OVS_CLEANUP[@]}"
Packit bc9a3a
            if ${DRY_RUN}; then
Packit bc9a3a
                error sudo "$0" tap-control ovs-cleanup "${OVS_CLEANUP[@]}"
Packit bc9a3a
            else
Packit bc9a3a
                sudo "$0" tap-control ovs-cleanup "${OVS_CLEANUP[@]}"
Packit bc9a3a
            fi
Packit bc9a3a
        fi
Packit bc9a3a
    fi
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
debug() {
Packit bc9a3a
    local level=${1}; shift;
Packit bc9a3a
    [ "${level}" -gt "${VERBOSITY}" ] && return
Packit bc9a3a
    error "${@}"
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
Usage() {
Packit bc9a3a
    cat <
Packit bc9a3a
Usage: ${0##*/} [ options ] -- kvm-args [ ... ]
Packit bc9a3a
Packit bc9a3a
   run kvm with a tap interface.
Packit bc9a3a
Packit bc9a3a
   options:
Packit bc9a3a
      -n | --netdev NETDEV    netdev can be 'user' or a bridge.
Packit bc9a3a
                              default is to bridge to $DEF_BRIDGE
Packit bc9a3a
      -d | --disk  DISK.img   attach DISK.img as a disk (via virtio)
Packit bc9a3a
           --dry-run          only report what would be done
Packit bc9a3a
Packit bc9a3a
           --uefi             boot with efi
Packit bc9a3a
           --uefi-nvram=FILE  boot with efi, using nvram settings in FILE
Packit bc9a3a
                              if FILE not present, copy from defaults.
Packit bc9a3a
Packit bc9a3a
   NETDEV:
Packit bc9a3a
    Above, 'NETDEV' is a comma delimited string
Packit bc9a3a
    The first field must be
Packit bc9a3a
     * bridge name: (br0 or virbr0): attach a device to this bridge
Packit bc9a3a
     * literal 'user': use qemu user networking
Packit bc9a3a
Packit bc9a3a
    Additional fields are optional, and can be anything that is acceptable
Packit bc9a3a
    to kvm either as:
Packit bc9a3a
      * '-device virtio-net-pci' option (see 'kvm -device virtio-net-pci,?')
Packit bc9a3a
      * '-net [user|tap]' option
Packit bc9a3a
Packit bc9a3a
   Example:
Packit bc9a3a
     * xkvm --netdev br0,macaddr=:05 -- -drive file=disk.img,if=virtio -curses
Packit bc9a3a
       attach a tap device to bridge 'br0' with mac address
Packit bc9a3a
         '${MAC_PREFIX}:05'
Packit bc9a3a
Packit bc9a3a
     * xkvm --netdev user,mac=random --netdev br1,model=e1000,mac=auto -- -curses
Packit bc9a3a
       attach virtio user networking nic with random mac address
Packit bc9a3a
       attach tap device to br1 bridge as e1000 with unspecified mac
Packit bc9a3a
Packit bc9a3a
     * xkvm --disk disk1.img
Packit bc9a3a
EOF
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
isdevopt() {
Packit bc9a3a
    local model="$1" input="${2%%=*}"
Packit bc9a3a
    local out="" opt="" opts=()
Packit bc9a3a
    if [ -z "${KVM_DEVOPTS[$model]}" ]; then
Packit bc9a3a
        out=$($KVM -device "$model,?" 2>&1) &&
Packit bc9a3a
            out=$(echo "$out" | sed -e "s,[^.]*[.],," -e 's,=.*,,') &&
Packit bc9a3a
            KVM_DEVOPTS[$model]="$out" ||
Packit bc9a3a
            { error "bad device model $model?"; exit 1; }
Packit bc9a3a
    fi
Packit bc9a3a
    opts=( ${KVM_DEVOPTS[$model]} )
Packit bc9a3a
    for opt in "${opts[@]}"; do
Packit bc9a3a
        [ "$input" = "$opt" ] && return 0
Packit bc9a3a
    done
Packit bc9a3a
    return 1
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
qemu_supports_file_locking() {
Packit bc9a3a
    # hackily check if qemu has file.locking in -drive params (LP: #1716028)
Packit bc9a3a
    if [ -z "$_QEMU_SUPPORTS_FILE_LOCKING" ]; then
Packit bc9a3a
        # The only way we could find to check presense of file.locking is
Packit bc9a3a
        # qmp (query-qmp-schema).  Simply checking if the virtio-blk driver
Packit bc9a3a
        # supports 'share-rw' is expected to be equivalent and simpler.
Packit bc9a3a
        isdevopt virtio-blk share-rw &&
Packit bc9a3a
            _QEMU_SUPPORTS_FILE_LOCKING=true ||
Packit bc9a3a
            _QEMU_SUPPORTS_FILE_LOCKING=false
Packit bc9a3a
        debug 1 "qemu supports file locking = ${_QEMU_SUPPORTS_FILE_LOCKING}"
Packit bc9a3a
    fi
Packit bc9a3a
    [ "$_QEMU_SUPPORTS_FILE_LOCKING" = "true" ]
Packit bc9a3a
    return
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
padmac() {
Packit bc9a3a
    # return a full mac, given a subset.
Packit bc9a3a
    # assume whatever is input is the last portion to be
Packit bc9a3a
    # returned, and fill it out with entries from MAC_PREFIX
Packit bc9a3a
    local mac="$1" num="$2" prefix="${3:-$MAC_PREFIX}" itoks="" ptoks=""
Packit bc9a3a
    # if input is empty set to :$num
Packit bc9a3a
    [ -n "$mac" ] || mac=$(printf "%02x" "$num") || return
Packit bc9a3a
    itoks=( ${mac//:/ } )
Packit bc9a3a
    ptoks=( ${prefix//:/ } )
Packit bc9a3a
    rtoks=( )
Packit bc9a3a
    for r in ${ptoks[@]:0:6-${#itoks[@]}} ${itoks[@]}; do
Packit bc9a3a
        rtoks[${#rtoks[@]}]="0x$r"
Packit bc9a3a
    done
Packit bc9a3a
    _RET=$(printf "%02x:%02x:%02x:%02x:%02x:%02x" "${rtoks[@]}")
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
make_nics_Usage() {
Packit bc9a3a
    cat <
Packit bc9a3a
Usage: ${0##*/} tap-control make-nics [options] bridge [bridge [..]]
Packit bc9a3a
Packit bc9a3a
   make a tap device on each of bridges requested
Packit bc9a3a
   outputs: 'tapname:type' for each input, or 'skip' if nothing needed.
Packit bc9a3a
Packit bc9a3a
   type is one of 'brctl' or 'ovs'
Packit bc9a3a
EOF
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
make_nics() {
Packit bc9a3a
    # takes input of list of bridges to create a tap device on
Packit bc9a3a
    # and echos either 'skip' or
Packit bc9a3a
    # <tapname>:<type> for each tap created
Packit bc9a3a
    # type is one of "ovs" or "brctl"
Packit bc9a3a
    local short_opts="v"
Packit bc9a3a
    local long_opts="--verbose"
Packit bc9a3a
    local getopt_out=""
Packit bc9a3a
    getopt_out=$(getopt --name "${0##*/} make-nics" \
Packit bc9a3a
        --options "${short_opts}" --long "${long_opts}" -- "$@") &&
Packit bc9a3a
        eval set -- "${getopt_out}" || { make_nics_Usage 1>&2; return 1; }
Packit bc9a3a
Packit bc9a3a
    local cur="" next=""
Packit bc9a3a
    while [ $# -ne 0 ]; do
Packit bc9a3a
        cur=${1}; next=${2};
Packit bc9a3a
        case "$cur" in
Packit bc9a3a
            -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
Packit bc9a3a
            --) shift; break;;
Packit bc9a3a
        esac
Packit bc9a3a
        shift;
Packit bc9a3a
    done
Packit bc9a3a
Packit bc9a3a
    [ $# -ne 0 ] || {
Packit bc9a3a
        make_nics_Usage 1>&2; error "must give bridge";
Packit bc9a3a
        return 1;
Packit bc9a3a
    }
Packit bc9a3a
Packit bc9a3a
    local owner="" ovsbrs="" tap="" tapnum="0" brtype="" bridge=""
Packit bc9a3a
    [ "$(id -u)" = "0" ] || { error "must be root for make-nics"; return 1; }
Packit bc9a3a
    owner="${SUDO_USER:-root}"
Packit bc9a3a
    ovsbrs=""
Packit bc9a3a
    if command -v ovs-vsctl >/dev/null 2>&1; then
Packit bc9a3a
        out=$(ovs-vsctl list-br)
Packit bc9a3a
        out=$(echo "$out" | sed "s/\n/,/")
Packit bc9a3a
        ovsbrs=",$out,"
Packit bc9a3a
    fi
Packit bc9a3a
    for bridge in "$@"; do
Packit bc9a3a
        [ "$bridge" = "user" ] && echo skip && continue
Packit bc9a3a
        [ "${ovsbrs#*,${bridge},}" != "$ovsbrs" ] &&
Packit bc9a3a
            btype="ovs" || btype="brctl"
Packit bc9a3a
        tapnum=0;
Packit bc9a3a
        while [ -e /sys/class/net/tapvm$tapnum ]; do tapnum=$(($tapnum+1)); done
Packit bc9a3a
        tap="tapvm$tapnum"
Packit bc9a3a
        debug 1 "creating $tap:$btype on $bridge" 1>&2
Packit bc9a3a
        ip tuntap add mode tap user "$owner" "$tap" ||
Packit bc9a3a
            { error "failed to create tap '$tap' for '$owner'"; return 1; }
Packit bc9a3a
        ip link set "$tap" up 1>&2 || {
Packit bc9a3a
            error "failed to bring up $tap";
Packit bc9a3a
            ip tuntap del mode tap "$tap";
Packit bc9a3a
            return 1;
Packit bc9a3a
        }
Packit bc9a3a
        if [ "$btype" = "ovs" ]; then
Packit bc9a3a
            ovs-vsctl add-port "$bridge" "$tap" 1>&2 || {
Packit bc9a3a
                error "failed: ovs-vsctl add-port $bridge $tap";
Packit bc9a3a
                ovs-vsctl del-port "$bridge" "$tap"
Packit bc9a3a
                return 1;
Packit bc9a3a
            }
Packit bc9a3a
        else
Packit bc9a3a
            ip link set "$tap" master "$bridge" 1>&2 || {
Packit bc9a3a
                error "failed to add tap '$tap' to '$bridge'"
Packit bc9a3a
                ip tuntap del mode tap "$tap";
Packit bc9a3a
                return 1
Packit bc9a3a
            }
Packit bc9a3a
        fi
Packit bc9a3a
        echo "$tap:$btype"
Packit bc9a3a
    done
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
ovs_cleanup() {
Packit bc9a3a
    [ "$(id -u)" = "0" ] ||
Packit bc9a3a
        { error "must be root for ovs-cleanup"; return 1; }
Packit bc9a3a
    local item="" errors=0
Packit bc9a3a
    # TODO: if get owner (SUDO_USERNAME) and if that isn't
Packit bc9a3a
    # the owner, then do not delete.
Packit bc9a3a
    for item in "$@"; do
Packit bc9a3a
        name=${item#*:}
Packit bc9a3a
        bridge=${item%:*}
Packit bc9a3a
        ovs-vsctl del-port "$bridge" "$name" || errors=$((errors+1))
Packit bc9a3a
    done
Packit bc9a3a
    return $errors
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
quote_cmd() {
Packit bc9a3a
    local quote='"' x="" vline=""
Packit bc9a3a
    for x in "$@"; do
Packit bc9a3a
        if [ "${x#* }" != "${x}" ]; then
Packit bc9a3a
            if [ "${x#*$quote}" = "${x}" ]; then
Packit bc9a3a
                x="\"$x\""
Packit bc9a3a
            else
Packit bc9a3a
                x="'$x'"
Packit bc9a3a
            fi
Packit bc9a3a
        fi
Packit bc9a3a
        vline="${vline} $x"
Packit bc9a3a
    done
Packit bc9a3a
    echo "$vline"
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
get_bios_opts() {
Packit bc9a3a
    # get_bios_opts(bios, uefi, nvram)
Packit bc9a3a
    # bios is a explicit bios to boot.
Packit bc9a3a
    # uefi is boolean indicating uefi
Packit bc9a3a
    # nvram is optional and indicates that ovmf vars should be copied
Packit bc9a3a
    # to that file if it does not exist. if it exists, use it.
Packit bc9a3a
    local bios="$1" uefi="${2:-false}" nvram="$3"
Packit bc9a3a
    local ovmf_dir="/usr/share/OVMF"
Packit bc9a3a
    local bios_opts="" pflash_common="if=pflash,format=raw"
Packit bc9a3a
    unset _RET
Packit bc9a3a
    _RET=( )
Packit bc9a3a
    if [ -n "$bios" ]; then
Packit bc9a3a
        _RET=( -drive "${pflash_common},file=$bios" )
Packit bc9a3a
        return 0
Packit bc9a3a
    elif ! $uefi; then
Packit bc9a3a
        return 0
Packit bc9a3a
    fi
Packit bc9a3a
Packit bc9a3a
    # ovmf in older releases (14.04) shipped only a single file
Packit bc9a3a
    #   /usr/share/ovmf/OVMF.fd
Packit bc9a3a
    # newer ovmf ships split files
Packit bc9a3a
    #   /usr/share/OVMF/OVMF_CODE.fd
Packit bc9a3a
    #   /usr/share/OVMF/OVMF_VARS.fd
Packit bc9a3a
    # with single file, pass only one file and read-write
Packit bc9a3a
    # with split, pass code as readonly and vars as read-write
Packit bc9a3a
    local joined="/usr/share/ovmf/OVMF.fd"
Packit bc9a3a
    local code="/usr/share/OVMF/OVMF_CODE.fd"
Packit bc9a3a
    local vars="/usr/share/OVMF/OVMF_VARS.fd"
Packit bc9a3a
    local split="" nvram_src=""
Packit bc9a3a
    if [ -e "$code" -o -e "$vars" ]; then
Packit bc9a3a
        split=true
Packit bc9a3a
        nvram_src="$vars"
Packit bc9a3a
    elif [ -e "$joined" ]; then
Packit bc9a3a
        split=false
Packit bc9a3a
        nvram_src="$joined"
Packit bc9a3a
    elif [ -n "$nvram" -a -e "$nvram" ]; then
Packit bc9a3a
        error "WARN: nvram given, but did not find expected ovmf files."
Packit bc9a3a
        error "      assuming this is code and vars (OVMF.fd)"
Packit bc9a3a
        split=false
Packit bc9a3a
    else
Packit bc9a3a
        error "uefi support requires ovmf bios: apt-get install -qy ovmf"
Packit bc9a3a
        return 1
Packit bc9a3a
    fi
Packit bc9a3a
Packit bc9a3a
    if [ -n "$nvram" ]; then
Packit bc9a3a
        if [ ! -f "$nvram" ]; then
Packit bc9a3a
            cp "$nvram_src" "$nvram" || 
Packit bc9a3a
                { error "failed copy $nvram_src to $nvram"; return 1; }
Packit bc9a3a
            debug 1 "copied $nvram_src to $nvram"
Packit bc9a3a
        fi
Packit bc9a3a
    else
Packit bc9a3a
        debug 1 "uefi without --uefi-nvram storage." \
Packit bc9a3a
            "nvram settings likely will not persist."
Packit bc9a3a
        nvram="${nvram_src}"
Packit bc9a3a
    fi
Packit bc9a3a
Packit bc9a3a
    if [ ! -w "$nvram" ]; then
Packit bc9a3a
        debug 1 "nvram file ${nvram} is readonly"
Packit bc9a3a
        nvram_ro="readonly"
Packit bc9a3a
    fi
Packit bc9a3a
Packit bc9a3a
    if $split; then
Packit bc9a3a
        # to ensure bootability firmware must be first, then variables
Packit bc9a3a
        _RET=( -drive "${pflash_common},file=$code,readonly" )
Packit bc9a3a
    fi
Packit bc9a3a
    _RET=( "${_RET[@]}"
Packit bc9a3a
           -drive "${pflash_common},file=$nvram${nvram_ro:+,${nvram_ro}}" )
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
main() {
Packit bc9a3a
    local short_opts="hd:n:v"
Packit bc9a3a
    local long_opts="bios:,help,dowait,disk:,dry-run,kvm:,no-dowait,netdev:,uefi,uefi-nvram:,verbose"
Packit bc9a3a
    local getopt_out=""
Packit bc9a3a
    getopt_out=$(getopt --name "${0##*/}" \
Packit bc9a3a
        --options "${short_opts}" --long "${long_opts}" -- "$@") &&
Packit bc9a3a
        eval set -- "${getopt_out}" || { bad_Usage; return 1; }
Packit bc9a3a
Packit bc9a3a
    local bridge="$DEF_BRIDGE" oifs="$IFS"
Packit bc9a3a
    local netdevs="" need_tap="" ret="" p="" i="" pt="" cur="" conn=""
Packit bc9a3a
    local kvm="" kvmcmd="" archopts=""
Packit bc9a3a
    local def_disk_driver=${DEF_DISK_DRIVER:-"virtio-blk"}
Packit bc9a3a
    local def_netmodel=${DEF_NETMODEL:-"virtio-net-pci"}
Packit bc9a3a
    local bios="" uefi=false uefi_nvram=""
Packit bc9a3a
Packit bc9a3a
    archopts=( )
Packit bc9a3a
    kvmcmd=( )
Packit bc9a3a
    netdevs=( )
Packit bc9a3a
    addargs=( )
Packit bc9a3a
    diskdevs=( )
Packit bc9a3a
    diskargs=( )
Packit bc9a3a
Packit bc9a3a
    # dowait: run qemu-system with a '&' and then 'wait' on the pid.
Packit bc9a3a
    #  the reason to do this or not do this has to do with interactivity
Packit bc9a3a
    #  if detached with &, then user input will not go to xkvm.
Packit bc9a3a
    #  if *not* detached, then signal handling is blocked until
Packit bc9a3a
    #  the foreground subprocess returns. which means we can't handle
Packit bc9a3a
    #  a sigterm and kill the qemu-system process.
Packit bc9a3a
    #  We default to dowait=false if input and output are a terminal
Packit bc9a3a
    local dowait=""
Packit bc9a3a
    [ -t 0 -a -t 1 ] && dowait=false || dowait=true
Packit bc9a3a
    while [ $# -ne 0 ]; do
Packit bc9a3a
        cur=${1}; next=${2};
Packit bc9a3a
        case "$cur" in
Packit bc9a3a
            -h|--help) Usage; exit 0;;
Packit bc9a3a
            -d|--disk)
Packit bc9a3a
                diskdevs[${#diskdevs[@]}]="$next"; shift;;
Packit bc9a3a
            --dry-run) DRY_RUN=true;;
Packit bc9a3a
            --kvm) kvm="$next"; shift;;
Packit bc9a3a
            -n|--netdev)
Packit bc9a3a
                netdevs[${#netdevs[@]}]=$next; shift;;
Packit bc9a3a
            -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
Packit bc9a3a
            --dowait) dowait=true;;
Packit bc9a3a
            --no-dowait) dowait=false;;
Packit bc9a3a
            --bios) bios="$next"; shift;;
Packit bc9a3a
            --uefi) uefi=true;;
Packit bc9a3a
            --uefi-nvram) uefi=true; uefi_nvram="$next"; shift;;
Packit bc9a3a
            --) shift; break;;
Packit bc9a3a
        esac
Packit bc9a3a
        shift;
Packit bc9a3a
    done
Packit bc9a3a
Packit bc9a3a
    [ ${#netdevs[@]} -eq 0 ] && netdevs=( "${DEF_BRIDGE}" )
Packit bc9a3a
    pt=( "$@" )
Packit bc9a3a
Packit bc9a3a
    local kvm_pkg="" virtio_scsi_bus="virtio-scsi-pci" virtio_rng_device="virtio-rng-pci"
Packit bc9a3a
    [ -n "$kvm" ] && kvm_pkg="none"
Packit bc9a3a
    case $(uname -m) in
Packit bc9a3a
        i?86)
Packit bc9a3a
            [ -n "$kvm" ] ||
Packit bc9a3a
                { kvm="qemu-system-i386"; kvm_pkg="qemu-system-x86"; }
Packit bc9a3a
            ;;
Packit bc9a3a
        x86_64)
Packit bc9a3a
            [ -n "$kvm" ] ||
Packit bc9a3a
                { kvm="qemu-system-x86_64"; kvm_pkg="qemu-system-x86"; }
Packit bc9a3a
            ;;
Packit bc9a3a
        s390x)
Packit bc9a3a
            [ -n "$kvm" ] ||
Packit bc9a3a
                { kvm="qemu-system-s390x"; kvm_pkg="qemu-system-misc"; }
Packit bc9a3a
            def_netmodel=${DEF_NETMODEL:-"virtio-net-ccw"}
Packit bc9a3a
            # disable virtio-scsi-bus
Packit bc9a3a
            virtio_scsi_bus="virtio-scsi-ccw"
Packit bc9a3a
            virtio_blk_bus="virtio-blk-ccw"
Packit bc9a3a
            virtio_rng_device="virtio-rng-ccw"
Packit bc9a3a
            ;;
Packit bc9a3a
        ppc64*)
Packit bc9a3a
            [ -n "$kvm" ] ||
Packit bc9a3a
                { kvm="qemu-system-ppc64"; kvm_pkg="qemu-system-ppc"; }
Packit bc9a3a
            def_netmodel="virtio-net-pci"
Packit bc9a3a
            # virtio seems functional on in 14.10, but might want scsi here
Packit bc9a3a
            #def_diskif="scsi"
Packit bc9a3a
            archopts=( "${archopts[@]}" -machine pseries,usb=off )
Packit bc9a3a
            archopts=( "${archopts[@]}" -device spapr-vscsi )
Packit bc9a3a
            ;;
Packit bc9a3a
        *) kvm=qemu-system-$(uname -m);;
Packit bc9a3a
    esac
Packit bc9a3a
    KVM="$kvm"
Packit bc9a3a
    kvmcmd=( $kvm -enable-kvm )
Packit bc9a3a
Packit bc9a3a
    local bios_opts=""
Packit bc9a3a
    if [ -n "$bios" ] && $uefi; then
Packit bc9a3a
        error "--uefi (or --uefi-nvram) is incompatible with --bios"
Packit bc9a3a
        return 1
Packit bc9a3a
    fi
Packit bc9a3a
    get_bios_opts "$bios" "$uefi" "$uefi_nvram" ||
Packit bc9a3a
        { error "failed to get bios opts"; return 1; }
Packit bc9a3a
    bios_opts=( "${_RET[@]}" )
Packit bc9a3a
Packit bc9a3a
    local out="" fmt="" bus="" unit="" index="" serial="" driver="" devopts=""
Packit bc9a3a
    local busorindex="" driveopts="" cur="" val="" file="" wwn=""
Packit bc9a3a
    for((i=0;i<${#diskdevs[@]};i++)); do
Packit bc9a3a
        cur=${diskdevs[$i]}
Packit bc9a3a
        IFS=","; set -- $cur; IFS="$oifs"
Packit bc9a3a
        driver=""
Packit bc9a3a
        id=$(printf "disk%02d" "$i")
Packit bc9a3a
        file=""
Packit bc9a3a
        fmt=""
Packit bc9a3a
        bus=""
Packit bc9a3a
        unit=""
Packit bc9a3a
        index=""
Packit bc9a3a
        serial=""
Packit bc9a3a
        wwn=""
Packit bc9a3a
        for tok in "$@"; do
Packit bc9a3a
            [ "${tok#*=}" = "${tok}" -a -f "${tok}" -a -z "$file" ] && file="$tok"
Packit bc9a3a
            val=${tok#*=}
Packit bc9a3a
            case "$tok" in
Packit bc9a3a
                driver=*) driver=$val;;
Packit bc9a3a
                if=virtio) driver=virtio-blk;;
Packit bc9a3a
                if=scsi) driver=scsi-hd;;
Packit bc9a3a
                if=pflash) driver=;;
Packit bc9a3a
                if=sd|if=mtd|floppy) fail "do not know what to do with $tok on $cur";;
Packit bc9a3a
                id=*) id=$val;;
Packit bc9a3a
                file=*) file=$val;;
Packit bc9a3a
                fmt=*|format=*) fmt=$val;;
Packit bc9a3a
                serial=*) serial=$val;;
Packit bc9a3a
                wwn=*) wwn=$val;;
Packit bc9a3a
                bus=*) bus=$val;;
Packit bc9a3a
                unit=*) unit=$val;;
Packit bc9a3a
                index=*) index=$val;;
Packit bc9a3a
            esac
Packit bc9a3a
        done
Packit bc9a3a
        [ -z "$file" ] && fail "did not read a file from $cur"
Packit bc9a3a
        if [ -f "$file" -a -z "$fmt" ]; then
Packit bc9a3a
            out=$(LANG=C qemu-img info "$file") &&
Packit bc9a3a
                fmt=$(echo "$out" | awk '$0 ~ /^file format:/ { print $3 }') ||
Packit bc9a3a
                { error "failed to determine format of $file"; return 1; }
Packit bc9a3a
        elif [ -z "$fmt" ]; then
Packit bc9a3a
            fmt=raw
Packit bc9a3a
        fi
Packit bc9a3a
        if [ -z "$driver" ]; then
Packit bc9a3a
            driver="$def_disk_driver"
Packit bc9a3a
        fi
Packit bc9a3a
        if [ -z "$serial" ]; then
Packit bc9a3a
            # use filename as serial if not provided a wwn
Packit bc9a3a
            if [ -n "$wwn" ]; then
Packit bc9a3a
                serial="$wwn"
Packit bc9a3a
            else
Packit bc9a3a
                serial="${file##*/}"
Packit bc9a3a
            fi
Packit bc9a3a
        fi
Packit bc9a3a
Packit bc9a3a
        # make sure we add either bus= or index=
Packit bc9a3a
        if [ -n "$bus" -o "$unit" ] && [ -n "$index" ]; then
Packit bc9a3a
            fail "bus and index cant be specified together: $cur"
Packit bc9a3a
        elif [ -z "$bus" -a -z "$unit" -a -z "$index" ]; then
Packit bc9a3a
            index=$i
Packit bc9a3a
        elif [ -n "$bus" -a -z "$unit" ]; then
Packit bc9a3a
            unit=$i
Packit bc9a3a
        fi
Packit bc9a3a
Packit bc9a3a
        busorindex="${bus:+bus=$bus,unit=$unit}${index:+index=${index}}"
Packit bc9a3a
        diskopts="file=${file},id=$id,if=none,format=$fmt,$busorindex"
Packit bc9a3a
        devopts="$driver,drive=$id${serial:+,serial=${serial}}"
Packit bc9a3a
        for tok in "$@"; do
Packit bc9a3a
            case "$tok" in
Packit bc9a3a
                id=*|if=*|driver=*|$file|file=*) continue;;
Packit bc9a3a
                fmt=*|format=*) continue;;
Packit bc9a3a
                serial=*|bus=*|unit=*|index=*) continue;;
Packit bc9a3a
                file.locking=*)
Packit bc9a3a
                    qemu_supports_file_locking || {
Packit bc9a3a
                        debug 2 "qemu has no file locking." \
Packit bc9a3a
                            "Dropping '$tok' from: $cur"
Packit bc9a3a
                        continue
Packit bc9a3a
                    };;
Packit bc9a3a
            esac
Packit bc9a3a
            isdevopt "$driver" "$tok" && devopts="${devopts},$tok" ||
Packit bc9a3a
                diskopts="${diskopts},${tok}"
Packit bc9a3a
        done
Packit bc9a3a
        case $driver in
Packit bc9a3a
            virtio-blk-ccw)
Packit bc9a3a
                # disable scsi when using virtio-blk-ccw
Packit bc9a3a
                devopts="${devopts},scsi=off";;
Packit bc9a3a
        esac
Packit bc9a3a
        diskargs=( "${diskargs[@]}" -drive "$diskopts" -device "$devopts" )
Packit bc9a3a
    done
Packit bc9a3a
Packit bc9a3a
    local mnics_vflag=""
Packit bc9a3a
    for((i=0;i<${VERBOSITY}-1;i++)); do mnics_vflag="${mnics_vflag}v"; done
Packit bc9a3a
    [ -n "$mnics_vflag" ] && mnics_vflag="-${mnics_vflag}"
Packit bc9a3a
Packit bc9a3a
    # now go through and split out options
Packit bc9a3a
    # -device virtio-net-pci,netdev=virtnet0,mac=52:54:31:15:63:02
Packit bc9a3a
    # -netdev type=tap,id=virtnet0,vhost=on,script=/etc/kvm/kvm-ifup.br0,downscript=no
Packit bc9a3a
    local netopts="" devopts="" id="" need_taps=0 model=""
Packit bc9a3a
    local device_args netdev_args
Packit bc9a3a
    device_args=( )
Packit bc9a3a
    netdev_args=( )
Packit bc9a3a
    connections=( )
Packit bc9a3a
    for((i=0;i<${#netdevs[@]};i++)); do
Packit bc9a3a
        id=$(printf "net%02d" "$i")
Packit bc9a3a
        netopts="";
Packit bc9a3a
        devopts=""
Packit bc9a3a
        # mac=auto is 'unspecified' (let qemu assign one)
Packit bc9a3a
        mac="auto"
Packit bc9a3a
        #vhost="off"
Packit bc9a3a
Packit bc9a3a
        IFS=","; set -- ${netdevs[$i]}; IFS="$oifs"
Packit bc9a3a
        bridge=$1; shift;
Packit bc9a3a
        if [ "$bridge" = "user" ]; then
Packit bc9a3a
            netopts="type=user"
Packit bc9a3a
            ntype="user"
Packit bc9a3a
            connections[$i]="user"
Packit bc9a3a
        else
Packit bc9a3a
            need_taps=1
Packit bc9a3a
            ntype="tap"
Packit bc9a3a
            netopts="type=tap"
Packit bc9a3a
            connections[$i]="$bridge"
Packit bc9a3a
        fi
Packit bc9a3a
        netopts="${netopts},id=$id"
Packit bc9a3a
        [ "$ntype" = "tap" ] && netopts="${netopts},script=no,downscript=no"
Packit bc9a3a
Packit bc9a3a
        model="${def_netmodel}"
Packit bc9a3a
        for tok in "$@"; do
Packit bc9a3a
            [ "${tok#model=}" = "${tok}" ] && continue
Packit bc9a3a
            case "${tok#model=}" in
Packit bc9a3a
                virtio) model=virtio-net-pci;;
Packit bc9a3a
                *) model=${tok#model=};;
Packit bc9a3a
            esac
Packit bc9a3a
        done
Packit bc9a3a
Packit bc9a3a
        for tok in "$@"; do
Packit bc9a3a
            case "$tok" in
Packit bc9a3a
                mac=*) mac="${tok#mac=}"; continue;;
Packit bc9a3a
                macaddr=*) mac=${tok#macaddr=}; continue;;
Packit bc9a3a
                model=*) continue;;
Packit bc9a3a
            esac
Packit bc9a3a
Packit bc9a3a
            isdevopt "$model" "$tok" && devopts="${devopts},$tok" ||
Packit bc9a3a
                netopts="${netopts},${tok}"
Packit bc9a3a
        done
Packit bc9a3a
        devopts=${devopts#,}
Packit bc9a3a
        netopts=${netopts#,}
Packit bc9a3a
Packit bc9a3a
        if [ "$mac" != "auto" ]; then
Packit bc9a3a
            [ "$mac" = "random" ] && randmac && mac="$_RET"
Packit bc9a3a
            padmac "$mac" "$i"
Packit bc9a3a
            devopts="${devopts:+${devopts},}mac=$_RET"
Packit bc9a3a
        fi
Packit bc9a3a
        devopts="$model,netdev=$id${devopts:+,${devopts}}"
Packit bc9a3a
        #netopts="${netopts},vhost=${vhost}"
Packit bc9a3a
Packit bc9a3a
        device_args[$i]="$devopts"
Packit bc9a3a
        netdev_args[$i]="$netopts"
Packit bc9a3a
    done
Packit bc9a3a
Packit bc9a3a
    trap cleanup EXIT
Packit bc9a3a
Packit bc9a3a
    reqs=( "$kvm" )
Packit bc9a3a
    pkgs=( "$kvm_pkg" )
Packit bc9a3a
    for((i=0;i<${#reqs[@]};i++)); do
Packit bc9a3a
        req=${reqs[$i]}
Packit bc9a3a
        pkg=${pkgs[$i]}
Packit bc9a3a
        [ "$pkg" = "none" ] && continue
Packit bc9a3a
        command -v "$req" >/dev/null || {
Packit bc9a3a
            missing="${missing:+${missing} }${req}"
Packit bc9a3a
            missing_pkgs="${missing_pkgs:+${missing_pkgs} }$pkg"
Packit bc9a3a
        }
Packit bc9a3a
    done
Packit bc9a3a
    if [ -n "$missing" ]; then
Packit bc9a3a
        local reply cmd=""
Packit bc9a3a
        cmd=( sudo apt-get --quiet install ${missing_pkgs} )
Packit bc9a3a
        error "missing prereqs: $missing";
Packit bc9a3a
        error "install them now with the following?: ${cmd[*]}"
Packit bc9a3a
        read reply && [ "$reply" = "y" -o "$reply" = "Y" ] ||
Packit bc9a3a
            { error "run: apt-get install ${missing_pkgs}"; return 1; }
Packit bc9a3a
        "${cmd[@]}" || { error "failed to install packages"; return 1; }
Packit bc9a3a
    fi
Packit bc9a3a
Packit bc9a3a
    if [ $need_taps -ne 0 ]; then
Packit bc9a3a
        local missing="" missing_pkgs="" reqs="" req="" pkgs="" pkg=""
Packit bc9a3a
        for i in "${connections[@]}"; do
Packit bc9a3a
            [ "$i" = "user" -o -e "/sys/class/net/$i" ] ||
Packit bc9a3a
                missing="${missing} $i"
Packit bc9a3a
        done
Packit bc9a3a
        [ -z "$missing" ] || {
Packit bc9a3a
            error "cannot create connection on: ${missing# }."
Packit bc9a3a
            error "bridges do not exist.";
Packit bc9a3a
            return 1;
Packit bc9a3a
        }
Packit bc9a3a
        error "creating tap devices: ${connections[*]}"
Packit bc9a3a
        if $DRY_RUN; then
Packit bc9a3a
            error "sudo $0 tap-control make-nics" \
Packit bc9a3a
                $mnics_vflag "${connections[@]}"
Packit bc9a3a
            taps=""
Packit bc9a3a
            for((i=0;i<${#connections[@]};i++)); do
Packit bc9a3a
                if [ "${connections[$i]}" = "user" ]; then
Packit bc9a3a
                    taps="${taps} skip"
Packit bc9a3a
                else
Packit bc9a3a
                    taps="${taps} dryruntap$i:brctl"
Packit bc9a3a
                fi
Packit bc9a3a
            done
Packit bc9a3a
        else
Packit bc9a3a
            taps=$(sudo "$0" tap-control make-nics \
Packit bc9a3a
                   ${mnics_vflag} "${connections[@]}") ||
Packit bc9a3a
                { error "$failed to make-nics ${connections[*]}"; return 1; }
Packit bc9a3a
        fi
Packit bc9a3a
        TAPDEVS=( ${taps} )
Packit bc9a3a
        for((i=0;i<${#TAPDEVS[@]};i++)); do
Packit bc9a3a
            cur=${TAPDEVS[$i]}
Packit bc9a3a
            [ "${cur#*:}" = "ovs" ] || continue
Packit bc9a3a
            conn=${connections[$i]}
Packit bc9a3a
            OVS_CLEANUP[${#OVS_CLEANUP[@]}]="${conn}:${cur%:*}"
Packit bc9a3a
        done
Packit bc9a3a
Packit bc9a3a
        debug 2 "tapdevs='${TAPDEVS[@]}'"
Packit bc9a3a
        [ ${#OVS_CLEANUP[@]} -eq 0 ] || error "OVS_CLEANUP='${OVS_CLEANUP[*]}'"
Packit bc9a3a
Packit bc9a3a
        for((i=0;i<${#TAPDEVS[@]};i++)); do
Packit bc9a3a
            cur=${TAPDEVS[$i]}
Packit bc9a3a
            [ "$cur" = "skip" ] && continue
Packit bc9a3a
            netdev_args[$i]="${netdev_args[$i]},ifname=${cur%:*}";
Packit bc9a3a
        done
Packit bc9a3a
    fi
Packit bc9a3a
Packit bc9a3a
    netargs=()
Packit bc9a3a
    for((i=0;i<${#device_args[@]};i++)); do
Packit bc9a3a
        netargs=( "${netargs[@]}" -device "${device_args[$i]}"
Packit bc9a3a
                  -netdev "${netdev_args[$i]}")
Packit bc9a3a
    done
Packit bc9a3a
Packit bc9a3a
    local bus_devices
Packit bc9a3a
    if [ -n "${virtio_scsi_bus}" ]; then
Packit bc9a3a
        bus_devices=( -device "$virtio_scsi_bus,id=virtio-scsi-xkvm" )
Packit bc9a3a
    fi
Packit bc9a3a
    local rng_devices
Packit bc9a3a
    rng_devices=( -object "rng-random,filename=/dev/urandom,id=objrng0"
Packit bc9a3a
                  -device "$virtio_rng_device,rng=objrng0,id=rng0" )
Packit bc9a3a
    cmd=( "${kvmcmd[@]}" "${archopts[@]}"
Packit bc9a3a
          "${bios_opts[@]}"
Packit bc9a3a
          "${bus_devices[@]}"
Packit bc9a3a
          "${rng_devices[@]}"
Packit bc9a3a
          "${netargs[@]}"
Packit bc9a3a
          "${diskargs[@]}" "${pt[@]}" )
Packit bc9a3a
    local pcmd=$(quote_cmd "${cmd[@]}")
Packit bc9a3a
    error "$pcmd"
Packit bc9a3a
    ${DRY_RUN} && return 0
Packit bc9a3a
Packit bc9a3a
    if $dowait; then
Packit bc9a3a
        "${cmd[@]}" &
Packit bc9a3a
        KVM_PID=$!
Packit bc9a3a
        debug 1 "kvm pid=$KVM_PID. my pid=$$"
Packit bc9a3a
        wait
Packit bc9a3a
        ret=$?
Packit bc9a3a
        KVM_PID=""
Packit bc9a3a
    else
Packit bc9a3a
        "${cmd[@]}"
Packit bc9a3a
        ret=$?
Packit bc9a3a
    fi
Packit bc9a3a
    return $ret
Packit bc9a3a
}
Packit bc9a3a
Packit bc9a3a
Packit bc9a3a
if [ "$1" = "tap-control" ]; then
Packit bc9a3a
    shift
Packit bc9a3a
    mode=$1
Packit bc9a3a
    shift || fail "must give mode to tap-control"
Packit bc9a3a
    case "$mode" in
Packit bc9a3a
        make-nics) make_nics "$@";;
Packit bc9a3a
        ovs-cleanup) ovs_cleanup "$@";;
Packit bc9a3a
        *) fail "tap mode must be either make-nics or ovs-cleanup";;
Packit bc9a3a
    esac
Packit bc9a3a
else
Packit bc9a3a
    main "$@"
Packit bc9a3a
fi
Packit bc9a3a
Packit bc9a3a
# vi: ts=4 expandtab syntax=sh