Merge ~powersj/cloud-init:add-xkvm into cloud-init:master
- Git
- lp:~powersj/cloud-init
- add-xkvm
- Merge into master
Proposed by
Joshua Powers
on 2017-09-11
| Status: | Merged |
|---|---|
| Merged at revision: | a4c1d578070145023ae88a9f79f8517e36b52559 |
| Proposed branch: | ~powersj/cloud-init:add-xkvm |
| Merge into: | cloud-init:master |
| Diff against target: |
670 lines (+664/-0) 1 file modified
tools/xkvm (+664/-0) |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Server Team CI bot | continuous-integration | Approve on 2017-09-11 | |
| cloud-init commiters | 2017-09-11 | Pending | |
|
Review via email:
|
|||
Commit Message
tools: Add xkvm script, wrapper around qemu-system
Description of the Change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | diff --git a/tools/xkvm b/tools/xkvm |
| 2 | new file mode 100755 |
| 3 | index 0000000..a30ba91 |
| 4 | --- /dev/null |
| 5 | +++ b/tools/xkvm |
| 6 | @@ -0,0 +1,664 @@ |
| 7 | +#!/bin/bash |
| 8 | + |
| 9 | +set -f |
| 10 | + |
| 11 | +VERBOSITY=0 |
| 12 | +KVM_PID="" |
| 13 | +DRY_RUN=false |
| 14 | +TEMP_D="" |
| 15 | +DEF_BRIDGE="virbr0" |
| 16 | +TAPDEVS=( ) |
| 17 | +# OVS_CLEANUP gets populated with bridge:devname pairs used with ovs |
| 18 | +OVS_CLEANUP=( ) |
| 19 | +MAC_PREFIX="52:54:00:12:34" |
| 20 | +KVM="kvm" |
| 21 | +declare -A KVM_DEVOPTS |
| 22 | + |
| 23 | +error() { echo "$@" 1>&2; } |
| 24 | +fail() { [ $# -eq 0 ] || error "$@"; exit 1; } |
| 25 | + |
| 26 | +bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; } |
| 27 | +randmac() { |
| 28 | + # return random mac addr within final 3 tokens |
| 29 | + local random="" |
| 30 | + random=$(printf "%02x:%02x:%02x" \ |
| 31 | + "$((${RANDOM}%256))" "$((${RANDOM}%256))" "$((${RANDOM}%256))") |
| 32 | + padmac "$random" |
| 33 | +} |
| 34 | + |
| 35 | +cleanup() { |
| 36 | + [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}" |
| 37 | + [ -z "${KVM_PID}" ] || kill "$KVM_PID" |
| 38 | + if [ ${#TAPDEVS[@]} -ne 0 ]; then |
| 39 | + local name item |
| 40 | + for item in "${TAPDEVS[@]}"; do |
| 41 | + [ "${item}" = "skip" ] && continue |
| 42 | + debug 1 "removing" "$item" |
| 43 | + name="${item%:*}" |
| 44 | + if $DRY_RUN; then |
| 45 | + error ip tuntap del mode tap "$name" |
| 46 | + else |
| 47 | + ip tuntap del mode tap "$name" |
| 48 | + fi |
| 49 | + [ $? -eq 0 ] || error "failed removal of $name" |
| 50 | + done |
| 51 | + if [ ${#OVS_CLEANUP[@]} -ne 0 ]; then |
| 52 | + # with linux bridges, there seems to be no harm in just deleting |
| 53 | + # the device (not detaching from the bridge). However, with |
| 54 | + # ovs, you have to remove them from the bridge, or later it |
| 55 | + # will refuse to add the same name. |
| 56 | + error "cleaning up ovs ports: ${OVS_CLEANUP[@]}" |
| 57 | + if ${DRY_RUN}; then |
| 58 | + error sudo "$0" tap-control ovs-cleanup "${OVS_CLEANUP[@]}" |
| 59 | + else |
| 60 | + sudo "$0" tap-control ovs-cleanup "${OVS_CLEANUP[@]}" |
| 61 | + fi |
| 62 | + fi |
| 63 | + fi |
| 64 | +} |
| 65 | + |
| 66 | +debug() { |
| 67 | + local level=${1}; shift; |
| 68 | + [ "${level}" -gt "${VERBOSITY}" ] && return |
| 69 | + error "${@}" |
| 70 | +} |
| 71 | + |
| 72 | +Usage() { |
| 73 | + cat <<EOF |
| 74 | +Usage: ${0##*/} [ options ] -- kvm-args [ ... ] |
| 75 | + |
| 76 | + run kvm with a tap interface. |
| 77 | + |
| 78 | + options: |
| 79 | + -n | --netdev NETDEV netdev can be 'user' or a bridge. |
| 80 | + default is to bridge to $DEF_BRIDGE |
| 81 | + -d | --disk DISK.img attach DISK.img as a disk (via virtio) |
| 82 | + --dry-run only report what would be done |
| 83 | + |
| 84 | + --uefi boot with efi |
| 85 | + --uefi-nvram=FILE boot with efi, using nvram settings in FILE |
| 86 | + if FILE not present, copy from defaults. |
| 87 | + |
| 88 | + NETDEV: |
| 89 | + Above, 'NETDEV' is a comma delimited string |
| 90 | + The first field must be |
| 91 | + * bridge name: (br0 or virbr0): attach a device to this bridge |
| 92 | + * literal 'user': use qemu user networking |
| 93 | + |
| 94 | + Additional fields are optional, and can be anything that is acceptable |
| 95 | + to kvm either as: |
| 96 | + * '-device virtio-net-pci' option (see 'kvm -device virtio-net-pci,?') |
| 97 | + * '-net [user|tap]' option |
| 98 | + |
| 99 | + Example: |
| 100 | + * xkvm --netdev br0,macaddr=:05 -- -drive file=disk.img,if=virtio -curses |
| 101 | + attach a tap device to bridge 'br0' with mac address |
| 102 | + '${MAC_PREFIX}:05' |
| 103 | + |
| 104 | + * xkvm --netdev user,mac=random --netdev br1,model=e1000,mac=auto -- -curses |
| 105 | + attach virtio user networking nic with random mac address |
| 106 | + attach tap device to br1 bridge as e1000 with unspecified mac |
| 107 | + |
| 108 | + * xkvm --disk disk1.img |
| 109 | +EOF |
| 110 | +} |
| 111 | + |
| 112 | +isdevopt() { |
| 113 | + local model="$1" input="${2%%=*}" |
| 114 | + local out="" opt="" opts=() |
| 115 | + if [ -z "${KVM_DEVOPTS[$model]}" ]; then |
| 116 | + out=$($KVM -device "$model,?" 2>&1) && |
| 117 | + out=$(echo "$out" | sed -e "s,[^.]*[.],," -e 's,=.*,,') && |
| 118 | + KVM_DEVOPTS[$model]="$out" || |
| 119 | + { error "bad device model $model?"; exit 1; } |
| 120 | + fi |
| 121 | + opts=( ${KVM_DEVOPTS[$model]} ) |
| 122 | + for opt in "${opts[@]}"; do |
| 123 | + [ "$input" = "$opt" ] && return 0 |
| 124 | + done |
| 125 | + return 1 |
| 126 | +} |
| 127 | + |
| 128 | +padmac() { |
| 129 | + # return a full mac, given a subset. |
| 130 | + # assume whatever is input is the last portion to be |
| 131 | + # returned, and fill it out with entries from MAC_PREFIX |
| 132 | + local mac="$1" num="$2" prefix="${3:-$MAC_PREFIX}" itoks="" ptoks="" |
| 133 | + # if input is empty set to :$num |
| 134 | + [ -n "$mac" ] || mac=$(printf "%02x" "$num") || return |
| 135 | + itoks=( ${mac//:/ } ) |
| 136 | + ptoks=( ${prefix//:/ } ) |
| 137 | + rtoks=( ) |
| 138 | + for r in ${ptoks[@]:0:6-${#itoks[@]}} ${itoks[@]}; do |
| 139 | + rtoks[${#rtoks[@]}]="0x$r" |
| 140 | + done |
| 141 | + _RET=$(printf "%02x:%02x:%02x:%02x:%02x:%02x" "${rtoks[@]}") |
| 142 | +} |
| 143 | + |
| 144 | +make_nics_Usage() { |
| 145 | + cat <<EOF |
| 146 | +Usage: ${0##*/} tap-control make-nics [options] bridge [bridge [..]] |
| 147 | + |
| 148 | + make a tap device on each of bridges requested |
| 149 | + outputs: 'tapname:type' for each input, or 'skip' if nothing needed. |
| 150 | + |
| 151 | + type is one of 'brctl' or 'ovs' |
| 152 | +EOF |
| 153 | +} |
| 154 | + |
| 155 | +make_nics() { |
| 156 | + # takes input of list of bridges to create a tap device on |
| 157 | + # and echos either 'skip' or |
| 158 | + # <tapname>:<type> for each tap created |
| 159 | + # type is one of "ovs" or "brctl" |
| 160 | + local short_opts="v" |
| 161 | + local long_opts="--verbose" |
| 162 | + local getopt_out="" |
| 163 | + getopt_out=$(getopt --name "${0##*/} make-nics" \ |
| 164 | + --options "${short_opts}" --long "${long_opts}" -- "$@") && |
| 165 | + eval set -- "${getopt_out}" || { make_nics_Usage 1>&2; return 1; } |
| 166 | + |
| 167 | + local cur="" next="" |
| 168 | + while [ $# -ne 0 ]; do |
| 169 | + cur=${1}; next=${2}; |
| 170 | + case "$cur" in |
| 171 | + -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; |
| 172 | + --) shift; break;; |
| 173 | + esac |
| 174 | + shift; |
| 175 | + done |
| 176 | + |
| 177 | + [ $# -ne 0 ] || { |
| 178 | + make_nics_Usage 1>&2; error "must give bridge"; |
| 179 | + return 1; |
| 180 | + } |
| 181 | + |
| 182 | + local owner="" ovsbrs="" tap="" tapnum="0" brtype="" bridge="" |
| 183 | + [ "$(id -u)" = "0" ] || { error "must be root for make-nics"; return 1; } |
| 184 | + owner="${SUDO_USER:-root}" |
| 185 | + ovsbrs="" |
| 186 | + if command -v ovs-vsctl >/dev/null 2>&1; then |
| 187 | + out=$(ovs-vsctl list-br) |
| 188 | + out=$(echo "$out" | sed "s/\n/,/") |
| 189 | + ovsbrs=",$out," |
| 190 | + fi |
| 191 | + for bridge in "$@"; do |
| 192 | + [ "$bridge" = "user" ] && echo skip && continue |
| 193 | + [ "${ovsbrs#*,${bridge},}" != "$ovsbrs" ] && |
| 194 | + btype="ovs" || btype="brctl" |
| 195 | + tapnum=0; |
| 196 | + while [ -e /sys/class/net/tapvm$tapnum ]; do tapnum=$(($tapnum+1)); done |
| 197 | + tap="tapvm$tapnum" |
| 198 | + debug 1 "creating $tap:$btype on $bridge" 1>&2 |
| 199 | + ip tuntap add mode tap user "$owner" "$tap" || |
| 200 | + { error "failed to create tap '$tap' for '$owner'"; return 1; } |
| 201 | + ip link set "$tap" up 1>&2 || { |
| 202 | + error "failed to bring up $tap"; |
| 203 | + ip tuntap del mode tap "$tap"; |
| 204 | + return 1; |
| 205 | + } |
| 206 | + if [ "$btype" = "ovs" ]; then |
| 207 | + ovs-vsctl add-port "$bridge" "$tap" 1>&2 || { |
| 208 | + error "failed: ovs-vsctl add-port $bridge $tap"; |
| 209 | + ovs-vsctl del-port "$bridge" "$tap" |
| 210 | + return 1; |
| 211 | + } |
| 212 | + else |
| 213 | + ip link set "$tap" master "$bridge" 1>&2 || { |
| 214 | + error "failed to add tap '$tap' to '$bridge'" |
| 215 | + ip tuntap del mode tap "$tap"; |
| 216 | + return 1 |
| 217 | + } |
| 218 | + fi |
| 219 | + echo "$tap:$btype" |
| 220 | + done |
| 221 | +} |
| 222 | + |
| 223 | +ovs_cleanup() { |
| 224 | + [ "$(id -u)" = "0" ] || |
| 225 | + { error "must be root for ovs-cleanup"; return 1; } |
| 226 | + local item="" errors=0 |
| 227 | + # TODO: if get owner (SUDO_USERNAME) and if that isn't |
| 228 | + # the owner, then do not delete. |
| 229 | + for item in "$@"; do |
| 230 | + name=${item#*:} |
| 231 | + bridge=${item%:*} |
| 232 | + ovs-vsctl del-port "$bridge" "$name" || errors=$((errors+1)) |
| 233 | + done |
| 234 | + return $errors |
| 235 | +} |
| 236 | + |
| 237 | +quote_cmd() { |
| 238 | + local quote='"' x="" vline="" |
| 239 | + for x in "$@"; do |
| 240 | + if [ "${x#* }" != "${x}" ]; then |
| 241 | + if [ "${x#*$quote}" = "${x}" ]; then |
| 242 | + x="\"$x\"" |
| 243 | + else |
| 244 | + x="'$x'" |
| 245 | + fi |
| 246 | + fi |
| 247 | + vline="${vline} $x" |
| 248 | + done |
| 249 | + echo "$vline" |
| 250 | +} |
| 251 | + |
| 252 | +get_bios_opts() { |
| 253 | + # get_bios_opts(bios, uefi, nvram) |
| 254 | + # bios is a explicit bios to boot. |
| 255 | + # uefi is boolean indicating uefi |
| 256 | + # nvram is optional and indicates that ovmf vars should be copied |
| 257 | + # to that file if it does not exist. if it exists, use it. |
| 258 | + local bios="$1" uefi="${2:-false}" nvram="$3" |
| 259 | + local ovmf_dir="/usr/share/OVMF" |
| 260 | + local bios_opts="" pflash_common="if=pflash,format=raw" |
| 261 | + unset _RET |
| 262 | + _RET=( ) |
| 263 | + if [ -n "$bios" ]; then |
| 264 | + _RET=( -drive "${pflash_common},file=$bios" ) |
| 265 | + return 0 |
| 266 | + elif ! $uefi; then |
| 267 | + return 0 |
| 268 | + fi |
| 269 | + |
| 270 | + # ovmf in older releases (14.04) shipped only a single file |
| 271 | + # /usr/share/ovmf/OVMF.fd |
| 272 | + # newer ovmf ships split files |
| 273 | + # /usr/share/OVMF/OVMF_CODE.fd |
| 274 | + # /usr/share/OVMF/OVMF_VARS.fd |
| 275 | + # with single file, pass only one file and read-write |
| 276 | + # with split, pass code as readonly and vars as read-write |
| 277 | + local joined="/usr/share/ovmf/OVMF.fd" |
| 278 | + local code="/usr/share/OVMF/OVMF_CODE.fd" |
| 279 | + local vars="/usr/share/OVMF/OVMF_VARS.fd" |
| 280 | + local split="" nvram_src="" |
| 281 | + if [ -e "$code" -o -e "$vars" ]; then |
| 282 | + split=true |
| 283 | + nvram_src="$vars" |
| 284 | + elif [ -e "$joined" ]; then |
| 285 | + split=false |
| 286 | + nvram_src="$joined" |
| 287 | + elif [ -n "$nvram" -a -e "$nvram" ]; then |
| 288 | + error "WARN: nvram given, but did not find expected ovmf files." |
| 289 | + error " assuming this is code and vars (OVMF.fd)" |
| 290 | + split=false |
| 291 | + else |
| 292 | + error "uefi support requires ovmf bios: apt-get install -qy ovmf" |
| 293 | + return 1 |
| 294 | + fi |
| 295 | + |
| 296 | + if [ -n "$nvram" ]; then |
| 297 | + if [ ! -f "$nvram" ]; then |
| 298 | + cp "$nvram_src" "$nvram" || |
| 299 | + { error "failed copy $nvram_src to $nvram"; return 1; } |
| 300 | + debug 1 "copied $nvram_src to $nvram" |
| 301 | + fi |
| 302 | + else |
| 303 | + debug 1 "uefi without --uefi-nvram storage." \ |
| 304 | + "nvram settings likely will not persist." |
| 305 | + nvram="${nvram_src}" |
| 306 | + fi |
| 307 | + |
| 308 | + if [ ! -w "$nvram" ]; then |
| 309 | + debug 1 "nvram file ${nvram} is readonly" |
| 310 | + nvram_ro="readonly" |
| 311 | + fi |
| 312 | + |
| 313 | + if $split; then |
| 314 | + # to ensure bootability firmware must be first, then variables |
| 315 | + _RET=( -drive "${pflash_common},file=$code,readonly" ) |
| 316 | + fi |
| 317 | + _RET=( "${_RET[@]}" |
| 318 | + -drive "${pflash_common},file=$nvram${nvram_ro:+,${nvram_ro}}" ) |
| 319 | +} |
| 320 | + |
| 321 | +main() { |
| 322 | + local short_opts="hd:n:v" |
| 323 | + local long_opts="bios:,help,dowait,disk:,dry-run,kvm:,no-dowait,netdev:,uefi,uefi-nvram:,verbose" |
| 324 | + local getopt_out="" |
| 325 | + getopt_out=$(getopt --name "${0##*/}" \ |
| 326 | + --options "${short_opts}" --long "${long_opts}" -- "$@") && |
| 327 | + eval set -- "${getopt_out}" || { bad_Usage; return 1; } |
| 328 | + |
| 329 | + local bridge="$DEF_BRIDGE" oifs="$IFS" |
| 330 | + local netdevs="" need_tap="" ret="" p="" i="" pt="" cur="" conn="" |
| 331 | + local kvm="" kvmcmd="" archopts="" |
| 332 | + local def_disk_driver=${DEF_DISK_DRIVER:-"virtio-blk"} |
| 333 | + local def_netmodel=${DEF_NETMODEL:-"virtio-net-pci"} |
| 334 | + local bios="" uefi=false uefi_nvram="" |
| 335 | + |
| 336 | + archopts=( ) |
| 337 | + kvmcmd=( ) |
| 338 | + netdevs=( ) |
| 339 | + addargs=( ) |
| 340 | + diskdevs=( ) |
| 341 | + diskargs=( ) |
| 342 | + |
| 343 | + # dowait: run qemu-system with a '&' and then 'wait' on the pid. |
| 344 | + # the reason to do this or not do this has to do with interactivity |
| 345 | + # if detached with &, then user input will not go to xkvm. |
| 346 | + # if *not* detached, then signal handling is blocked until |
| 347 | + # the foreground subprocess returns. which means we can't handle |
| 348 | + # a sigterm and kill the qemu-system process. |
| 349 | + # We default to dowait=false if input and output are a terminal |
| 350 | + local dowait="" |
| 351 | + [ -t 0 -a -t 1 ] && dowait=false || dowait=true |
| 352 | + while [ $# -ne 0 ]; do |
| 353 | + cur=${1}; next=${2}; |
| 354 | + case "$cur" in |
| 355 | + -h|--help) Usage; exit 0;; |
| 356 | + -d|--disk) |
| 357 | + diskdevs[${#diskdevs[@]}]="$next"; shift;; |
| 358 | + --dry-run) DRY_RUN=true;; |
| 359 | + --kvm) kvm="$next"; shift;; |
| 360 | + -n|--netdev) |
| 361 | + netdevs[${#netdevs[@]}]=$next; shift;; |
| 362 | + -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; |
| 363 | + --dowait) dowait=true;; |
| 364 | + --no-dowait) dowait=false;; |
| 365 | + --bios) bios="$next"; shift;; |
| 366 | + --uefi) uefi=true;; |
| 367 | + --uefi-nvram) uefi=true; uefi_nvram="$next"; shift;; |
| 368 | + --) shift; break;; |
| 369 | + esac |
| 370 | + shift; |
| 371 | + done |
| 372 | + |
| 373 | + [ ${#netdevs[@]} -eq 0 ] && netdevs=( "${DEF_BRIDGE}" ) |
| 374 | + pt=( "$@" ) |
| 375 | + |
| 376 | + local kvm_pkg="" virtio_scsi_bus="virtio-scsi-pci" |
| 377 | + [ -n "$kvm" ] && kvm_pkg="none" |
| 378 | + case $(uname -m) in |
| 379 | + i?86) |
| 380 | + [ -n "$kvm" ] || |
| 381 | + { kvm="qemu-system-i386"; kvm_pkg="qemu-system-x86"; } |
| 382 | + ;; |
| 383 | + x86_64) |
| 384 | + [ -n "$kvm" ] || |
| 385 | + { kvm="qemu-system-x86_64"; kvm_pkg="qemu-system-x86"; } |
| 386 | + ;; |
| 387 | + s390x) |
| 388 | + [ -n "$kvm" ] || |
| 389 | + { kvm="qemu-system-s390x"; kvm_pkg="qemu-system-misc"; } |
| 390 | + def_netmodel=${DEF_NETMODEL:-"virtio-net-ccw"} |
| 391 | + virtio_scsi_bus="virtio-scsi-ccw" |
| 392 | + ;; |
| 393 | + ppc64*) |
| 394 | + [ -n "$kvm" ] || |
| 395 | + { kvm="qemu-system-ppc64"; kvm_pkg="qemu-system-ppc"; } |
| 396 | + def_netmodel="virtio-net-pci" |
| 397 | + # virtio seems functional on in 14.10, but might want scsi here |
| 398 | + #def_diskif="scsi" |
| 399 | + archopts=( "${archopts[@]}" -machine pseries,usb=off ) |
| 400 | + archopts=( "${archopts[@]}" -device spapr-vscsi ) |
| 401 | + ;; |
| 402 | + *) kvm=qemu-system-$(uname -m);; |
| 403 | + esac |
| 404 | + KVM="$kvm" |
| 405 | + kvmcmd=( $kvm -enable-kvm ) |
| 406 | + |
| 407 | + local bios_opts="" |
| 408 | + if [ -n "$bios" ] && $uefi; then |
| 409 | + error "--uefi (or --uefi-nvram) is incompatible with --bios" |
| 410 | + return 1 |
| 411 | + fi |
| 412 | + get_bios_opts "$bios" "$uefi" "$uefi_nvram" || |
| 413 | + { error "failed to get bios opts"; return 1; } |
| 414 | + bios_opts=( "${_RET[@]}" ) |
| 415 | + |
| 416 | + local out="" fmt="" bus="" unit="" index="" serial="" driver="" devopts="" |
| 417 | + local busorindex="" driveopts="" cur="" val="" file="" |
| 418 | + for((i=0;i<${#diskdevs[@]};i++)); do |
| 419 | + cur=${diskdevs[$i]} |
| 420 | + IFS=","; set -- $cur; IFS="$oifs" |
| 421 | + driver="" |
| 422 | + id=$(printf "disk%02d" "$i") |
| 423 | + file="" |
| 424 | + fmt="" |
| 425 | + bus="" |
| 426 | + unit="" |
| 427 | + index="" |
| 428 | + serial="" |
| 429 | + for tok in "$@"; do |
| 430 | + [ "${tok#*=}" = "${tok}" -a -f "${tok}" -a -z "$file" ] && file="$tok" |
| 431 | + val=${tok#*=} |
| 432 | + case "$tok" in |
| 433 | + driver=*) driver=$val;; |
| 434 | + if=virtio) driver=virtio-blk;; |
| 435 | + if=scsi) driver=scsi-hd;; |
| 436 | + if=pflash) driver=;; |
| 437 | + if=sd|if=mtd|floppy) fail "do not know what to do with $tok on $cur";; |
| 438 | + id=*) id=$val;; |
| 439 | + file=*) file=$val;; |
| 440 | + fmt=*|format=*) fmt=$val;; |
| 441 | + serial=*) serial=$val;; |
| 442 | + bus=*) bus=$val;; |
| 443 | + unit=*) unit=$val;; |
| 444 | + index=*) index=$val;; |
| 445 | + esac |
| 446 | + done |
| 447 | + [ -z "$file" ] && fail "did not read a file from $cur" |
| 448 | + if [ -f "$file" -a -z "$fmt" ]; then |
| 449 | + out=$(LANG=C qemu-img info "$file") && |
| 450 | + fmt=$(echo "$out" | awk '$0 ~ /^file format:/ { print $3 }') || |
| 451 | + { error "failed to determine format of $file"; return 1; } |
| 452 | + else |
| 453 | + fmt=raw |
| 454 | + fi |
| 455 | + if [ -z "$driver" ]; then |
| 456 | + driver="$def_disk_driver" |
| 457 | + fi |
| 458 | + if [ -z "$serial" ]; then |
| 459 | + serial="${file##*/}" |
| 460 | + fi |
| 461 | + |
| 462 | + # make sure we add either bus= or index= |
| 463 | + if [ -n "$bus" -o "$unit" ] && [ -n "$index" ]; then |
| 464 | + fail "bus and index cant be specified together: $cur" |
| 465 | + elif [ -z "$bus" -a -z "$unit" -a -z "$index" ]; then |
| 466 | + index=$i |
| 467 | + elif [ -n "$bus" -a -z "$unit" ]; then |
| 468 | + unit=$i |
| 469 | + fi |
| 470 | + |
| 471 | + busorindex="${bus:+bus=$bus,unit=$unit}${index:+index=${index}}" |
| 472 | + diskopts="file=${file},id=$id,if=none,format=$fmt,$busorindex" |
| 473 | + devopts="$driver,drive=$id${serial:+,serial=${serial}}" |
| 474 | + for tok in "$@"; do |
| 475 | + case "$tok" in |
| 476 | + id=*|if=*|driver=*|$file|file=*) continue;; |
| 477 | + fmt=*|format=*) continue;; |
| 478 | + serial=*|bus=*|unit=*|index=*) continue;; |
| 479 | + esac |
| 480 | + isdevopt "$driver" "$tok" && devopts="${devopts},$tok" || |
| 481 | + diskopts="${diskopts},${tok}" |
| 482 | + done |
| 483 | + |
| 484 | + diskargs=( "${diskargs[@]}" -drive "$diskopts" -device "$devopts" ) |
| 485 | + done |
| 486 | + |
| 487 | + local mnics_vflag="" |
| 488 | + for((i=0;i<${VERBOSITY}-1;i++)); do mnics_vflag="${mnics_vflag}v"; done |
| 489 | + [ -n "$mnics_vflag" ] && mnics_vflag="-${mnics_vflag}" |
| 490 | + |
| 491 | + # now go through and split out options |
| 492 | + # -device virtio-net-pci,netdev=virtnet0,mac=52:54:31:15:63:02 |
| 493 | + # -netdev type=tap,id=virtnet0,vhost=on,script=/etc/kvm/kvm-ifup.br0,downscript=no |
| 494 | + local netopts="" devopts="" id="" need_taps=0 model="" |
| 495 | + local device_args netdev_args |
| 496 | + device_args=( ) |
| 497 | + netdev_args=( ) |
| 498 | + connections=( ) |
| 499 | + for((i=0;i<${#netdevs[@]};i++)); do |
| 500 | + id=$(printf "net%02d" "$i") |
| 501 | + netopts=""; |
| 502 | + devopts="" |
| 503 | + # mac=auto is 'unspecified' (let qemu assign one) |
| 504 | + mac="auto" |
| 505 | + #vhost="off" |
| 506 | + |
| 507 | + IFS=","; set -- ${netdevs[$i]}; IFS="$oifs" |
| 508 | + bridge=$1; shift; |
| 509 | + if [ "$bridge" = "user" ]; then |
| 510 | + netopts="type=user" |
| 511 | + ntype="user" |
| 512 | + connections[$i]="user" |
| 513 | + else |
| 514 | + need_taps=1 |
| 515 | + ntype="tap" |
| 516 | + netopts="type=tap" |
| 517 | + connections[$i]="$bridge" |
| 518 | + fi |
| 519 | + netopts="${netopts},id=$id" |
| 520 | + [ "$ntype" = "tap" ] && netopts="${netopts},script=no,downscript=no" |
| 521 | + |
| 522 | + model="${def_netmodel}" |
| 523 | + for tok in "$@"; do |
| 524 | + [ "${tok#model=}" = "${tok}" ] && continue |
| 525 | + case "${tok#model=}" in |
| 526 | + virtio) model=virtio-net-pci;; |
| 527 | + *) model=${tok#model=};; |
| 528 | + esac |
| 529 | + done |
| 530 | + |
| 531 | + for tok in "$@"; do |
| 532 | + case "$tok" in |
| 533 | + mac=*) mac="${tok#mac=}"; continue;; |
| 534 | + macaddr=*) mac=${tok#macaddr=}; continue;; |
| 535 | + model=*) continue;; |
| 536 | + esac |
| 537 | + |
| 538 | + isdevopt "$model" "$tok" && devopts="${devopts},$tok" || |
| 539 | + netopts="${netopts},${tok}" |
| 540 | + done |
| 541 | + devopts=${devopts#,} |
| 542 | + netopts=${netopts#,} |
| 543 | + |
| 544 | + if [ "$mac" != "auto" ]; then |
| 545 | + [ "$mac" = "random" ] && randmac && mac="$_RET" |
| 546 | + padmac "$mac" "$i" |
| 547 | + devopts="${devopts:+${devopts},}mac=$_RET" |
| 548 | + fi |
| 549 | + devopts="$model,netdev=$id${devopts:+,${devopts}}" |
| 550 | + #netopts="${netopts},vhost=${vhost}" |
| 551 | + |
| 552 | + device_args[$i]="$devopts" |
| 553 | + netdev_args[$i]="$netopts" |
| 554 | + done |
| 555 | + |
| 556 | + trap cleanup EXIT |
| 557 | + |
| 558 | + reqs=( "$kvm" ) |
| 559 | + pkgs=( "$kvm_pkg" ) |
| 560 | + for((i=0;i<${#reqs[@]};i++)); do |
| 561 | + req=${reqs[$i]} |
| 562 | + pkg=${pkgs[$i]} |
| 563 | + [ "$pkg" = "none" ] && continue |
| 564 | + command -v "$req" >/dev/null || { |
| 565 | + missing="${missing:+${missing} }${req}" |
| 566 | + missing_pkgs="${missing_pkgs:+${missing_pkgs} }$pkg" |
| 567 | + } |
| 568 | + done |
| 569 | + if [ -n "$missing" ]; then |
| 570 | + local reply cmd="" |
| 571 | + cmd=( sudo apt-get --quiet install ${missing_pkgs} ) |
| 572 | + error "missing prereqs: $missing"; |
| 573 | + error "install them now with the following?: ${cmd[*]}" |
| 574 | + read reply && [ "$reply" = "y" -o "$reply" = "Y" ] || |
| 575 | + { error "run: apt-get install ${missing_pkgs}"; return 1; } |
| 576 | + "${cmd[@]}" || { error "failed to install packages"; return 1; } |
| 577 | + fi |
| 578 | + |
| 579 | + if [ $need_taps -ne 0 ]; then |
| 580 | + local missing="" missing_pkgs="" reqs="" req="" pkgs="" pkg="" |
| 581 | + for i in "${connections[@]}"; do |
| 582 | + [ "$i" = "user" -o -e "/sys/class/net/$i" ] || |
| 583 | + missing="${missing} $i" |
| 584 | + done |
| 585 | + [ -z "$missing" ] || { |
| 586 | + error "cannot create connection on: ${missing# }." |
| 587 | + error "bridges do not exist."; |
| 588 | + return 1; |
| 589 | + } |
| 590 | + error "creating tap devices: ${connections[*]}" |
| 591 | + if $DRY_RUN; then |
| 592 | + error "sudo $0 tap-control make-nics" \ |
| 593 | + $mnics_vflag "${connections[@]}" |
| 594 | + taps="" |
| 595 | + for((i=0;i<${#connections[@]};i++)); do |
| 596 | + if [ "${connections[$i]}" = "user" ]; then |
| 597 | + taps="${taps} skip" |
| 598 | + else |
| 599 | + taps="${taps} dryruntap$i:brctl" |
| 600 | + fi |
| 601 | + done |
| 602 | + else |
| 603 | + taps=$(sudo "$0" tap-control make-nics \ |
| 604 | + ${mnics_vflag} "${connections[@]}") || |
| 605 | + { error "$failed to make-nics ${connections[*]}"; return 1; } |
| 606 | + fi |
| 607 | + TAPDEVS=( ${taps} ) |
| 608 | + for((i=0;i<${#TAPDEVS[@]};i++)); do |
| 609 | + cur=${TAPDEVS[$i]} |
| 610 | + [ "${cur#*:}" = "ovs" ] || continue |
| 611 | + conn=${connections[$i]} |
| 612 | + OVS_CLEANUP[${#OVS_CLEANUP[@]}]="${conn}:${cur%:*}" |
| 613 | + done |
| 614 | + |
| 615 | + debug 2 "tapdevs='${TAPDEVS[@]}'" |
| 616 | + [ ${#OVS_CLEANUP[@]} -eq 0 ] || error "OVS_CLEANUP='${OVS_CLEANUP[*]}'" |
| 617 | + |
| 618 | + for((i=0;i<${#TAPDEVS[@]};i++)); do |
| 619 | + cur=${TAPDEVS[$i]} |
| 620 | + [ "$cur" = "skip" ] && continue |
| 621 | + netdev_args[$i]="${netdev_args[$i]},ifname=${cur%:*}"; |
| 622 | + done |
| 623 | + fi |
| 624 | + |
| 625 | + netargs=() |
| 626 | + for((i=0;i<${#device_args[@]};i++)); do |
| 627 | + netargs=( "${netargs[@]}" -device "${device_args[$i]}" |
| 628 | + -netdev "${netdev_args[$i]}") |
| 629 | + done |
| 630 | + |
| 631 | + local bus_devices |
| 632 | + bus_devices=( -device "$virtio_scsi_bus,id=virtio-scsi-xkvm" ) |
| 633 | + cmd=( "${kvmcmd[@]}" "${archopts[@]}" |
| 634 | + "${bios_opts[@]}" |
| 635 | + "${bus_devices[@]}" |
| 636 | + "${netargs[@]}" |
| 637 | + "${diskargs[@]}" "${pt[@]}" ) |
| 638 | + local pcmd=$(quote_cmd "${cmd[@]}") |
| 639 | + error "$pcmd" |
| 640 | + ${DRY_RUN} && return 0 |
| 641 | + |
| 642 | + if $dowait; then |
| 643 | + "${cmd[@]}" & |
| 644 | + KVM_PID=$! |
| 645 | + debug 1 "kvm pid=$KVM_PID. my pid=$$" |
| 646 | + wait |
| 647 | + ret=$? |
| 648 | + KVM_PID="" |
| 649 | + else |
| 650 | + "${cmd[@]}" |
| 651 | + ret=$? |
| 652 | + fi |
| 653 | + return $ret |
| 654 | +} |
| 655 | + |
| 656 | + |
| 657 | +if [ "$1" = "tap-control" ]; then |
| 658 | + shift |
| 659 | + mode=$1 |
| 660 | + shift || fail "must give mode to tap-control" |
| 661 | + case "$mode" in |
| 662 | + make-nics) make_nics "$@";; |
| 663 | + ovs-cleanup) ovs_cleanup "$@";; |
| 664 | + *) fail "tap mode must be either make-nics or ovs-cleanup";; |
| 665 | + esac |
| 666 | +else |
| 667 | + main "$@" |
| 668 | +fi |
| 669 | + |
| 670 | +# vi: ts=4 expandtab |


PASSED: Continuous integration, rev:210024716c2 173249539ff9847 cbca36e89555c7 /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 282/
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 282/rebuild
https:/