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