Merge ~powersj/cloud-init:add-xkvm into cloud-init:master
- Git
- lp:~powersj/cloud-init
- add-xkvm
- Merge into master
Proposed by
Joshua Powers
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 | |
cloud-init Commiters | Pending | ||
Review via email: mp+330536@code.launchpad.net |
Commit message
tools: Add xkvm script, wrapper around qemu-system
Description of the change
To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote : | # |
review:
Approve
(continuous-integration)
Revision history for this message
Chad Smith (chad.smith) : | # |
There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.
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:/