-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathlinux
executable file
·249 lines (196 loc) · 8.14 KB
/
linux
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#!/bin/bash
# For use in shebang of ./vm.* wrapper-scripts, e.g.: #!./vm.linux -i 123 -m 4G -d vm.img
[[ "$1" = *\ * && "$2" = ./vm.* ]] && {
cmd=( "$0" $1 ); shift; shift; cmd+=( "$@" ); exec "${cmd[@]}"; }
script_info='Generic linux distro VM with grub and partitioned disk img.'
ins=1 smp=4 mem=4G nets=1 root_opts_file=()
img=() dev_ro=() dev=() efi= iso=
read -r -d '' script_usage <<EOF
Usage: $0 [-r|--root] [ {-i|--instance} $ins ]
[ {-t|--smp} $smp ] [ {-m|--mem} $mem ] [ {-o|--opts} file.opts ]
[ {-q|--img} file.qcow2 +] [ {-s|--dev-ro} disk.img +] [ {-d|--dev} /dev/xyz +]
[ {-e|--efi} [copy of /usr/share/ovmf/x64/OVMF_VARS.fd].bin ]
[ --iso image.iso ] [-c|--console] [-g|--gtk] [-nX|--nets=X]
[ { d | dump } [path] | { r | restore } [-i] [path] | { m | monitor } | { n | net-only } ]
(note: ordering of options matters)
EOF
script_usage=${script_usage// / }
read -r -d '' script_info_ext <<EOF
$script_info
Cheatsheet for qemu-img (to use with -q/--img option):
qemu-img create -f qcow2 install.qcow2 10G
qemu-img create -b install.qcow2 -f qcow2 install.qcow2.inc
qemu-img commit install.qcow2.inc && rm install.qcow2.inc
Cheatsheet to init new cloudimg:
qemu-nbd -c /dev/nbd0 install.qcow2.inc && fsck -a /dev/nbd0p1 && mount /dev/nbd0p1 /mnt/nbd
&& chmod a+r /mnt/nbd/{vmlinuz,initrd.img}
&& cat ~/.ssh/authorized_keys > /mnt/nbd/root/.ssh/authorized_keys
&& cat ~/.ssh/authorized_keys > /mnt/nbd/home/ubuntu/.ssh/authorized_keys
&& touch /mnt/nbd/etc/cloud/cloud-init.disabled
&& rm -rf /mnt/nbd/etc/systemd/system/cloud-init.target.wants
&& umount /mnt/nbd && qemu-nbd -d /dev/nbd0
EOF
script_info_ext=${script_info_ext// / }
[[ "$1" = -r || "$1" = --root || "$UID" -ne 0 ]] || {
echo >&2 "ERROR: running as uid=0 but no -r/--root specified"; exit 1; }
[[ "$1" = -i || "$1" = --instance ]] && { ins=$2; shift; shift; }
video=( -vga std ) video_vhost=
root=$(dirname "$BASH_SOURCE")
dst="$root"/live.dump
telnet_port=$(( 8200 + $ins ))
vde_mac_tail=54:00:12:34:$(( 60 + $ins )) # missing first (even) octet
sock_path="$XDG_RUNTIME_DIR"/vm
dump_inc= restore= net_only=
[[ "$1" = -t || "$1" = --smp ]] && { smp=$2; shift; shift; }
[[ "$1" = -m || "$1" = --mem ]] && { mem=$2; shift; shift; }
[[ "$1" = -o || "$1" = --opts ]] && { declare -a root_opts_file="($(<"$2"))"; shift; shift; }
while [[ "$1" = -q || "$1" = --img ]]; do img+=( "$(realpath "$2")" ); shift; shift; done
while [[ "$1" = -s || "$1" = --dev-ro ]]; do dev_ro+=( "$(realpath "$2")" ); shift; shift; done
while [[ "$1" = -d || "$1" = --dev ]]; do dev+=( "$(realpath "$2")" ); shift; shift; done
[[ "$1" = -e || "$1" = --efi ]] && { efi=$(realpath "$2"); shift; shift; }
[[ "$1" = --iso ]] && { iso=$(realpath "$2"); shift; shift; }
[[ "$1" = -c || "$1" = --console ]] && { video=( -vga none -nographic ); shift; }
[[ "$1" = -g || "$1" = --gtk ]] && {
# https://www.kraxel.org/blog/2019/09/display-devices-in-qemu/
video=( -display gtk,gl=on -device virtio-gpu-gl ); shift; }
## vhost-user-gpu-pci - not sure if this might only be for vfio
# video_vhost="$sock_path"/gpu.sock video=(
# -display gtk,gl=on,zoom-to-fit=on
# -chardev socket,id=vgpu,path="$video_vhost"
# -device vhost-user-gpu-pci,chardev=vgpu ); shift; }
[[ "$1" = -n2 || "$1" = --nets=2 ]] && { nets=2; shift; }
[[ "$1" = -n3 || "$1" = --nets=3 ]] && { nets=3; shift; }
bsleep() { read -rt "$1" <> <(:) || :; } # built-in sleep alternative
### Management and one-off commands
case "$1" in
d|dump)
[[ "$2" = -i ]] && { dump_inc=-i; shift; }
[[ -n "$2" ]] && {
touch "$2" || exit 1
dst=$(realpath "$2")
}
echo "Dumping to: $dst"
ncat -t 127.0.0.1 "$telnet_port" <<EOF
stop
migrate_set_speed 4g
migrate -d $dump_inc "exec:lzop -c > $dst"
EOF
echo
while :; do
echo 'info migrate' | ncat -t 127.0.0.1 "$telnet_port" |
grep -aq '^Migration status:[[:space:]]*completed[[:space:]]*$'
pipe=( "${PIPESTATUS[@]}" )
[[ ${pipe[0]} -ne 0 || ${pipe[1]} -ne 0 ]] && exit 1
[[ ${pipe[2]} -eq 0 ]] && {
echo 'quit' | ncat -t 127.0.0.1 "$telnet_port"
echo "Done"
ls -lah "$dst"
exit 0
}
bsleep 1 || exit 1
done ;;
r|restore)
[[ -n "$2" ]] && dst=$2
echo "Restoring from: $dst"
[[ -f "$dst" ]] || { echo >&2 "FIle not found: $dst"; exit 1; }
restore=t ;;
m|monitor) exec rlwrap ncat -t 127.0.0.1 "$telnet_port" ;;
n|net|net-only) net_only=t ;;
'') ;;
-h|--help-ext) echo "$script_usage";echo; echo "$script_info_ext"; exit 0 ;;
*)
echo >&2 "$script_usage"
echo >&2
echo >&2 "$script_info"
echo >&2 "Use -h/--help-ext option for various qemu/option/setup cheatsheets."
exit 1 ;;
esac
cd "$root"
### Too slow otherwise
err=0
modprobe kvm || exit 1
modprobe kvm-intel 2>/dev/null || (( err += 1 ))
modprobe kvm-amd 2>/dev/null || (( err += 1 ))
[[ "$err" -lt 2 ]] || { echo >&2 "ERROR: failed to probe intel/amd kvm modules"; exit 1; }
### VDE helpers
# Tend to break terminal for some reason, hence &>/dev/null on start
pid1=$$ pid2=$$
mkdir -pm700 "$sock_path"
cgrc -rcu vde && {
rm -rf "$sock_path"/link.sock
cgrc -ru vde vm -- vde_switch \
-s "$sock_path"/link.sock -M "$sock_path"/mgmt.sock &>/dev/null &
pid1=$!; }
for n in {0..50}; do [[ -e "$sock_path"/link.sock ]] && break; bsleep 0.1; done
[[ $n -lt 50 ]] || { echo >&2 "ERROR: vde switch startup failed"; exit 1; }
cgrc -rcu vde-tap && {
cgrc -ru vde-tap vm -- \
sudo -n vde_plug2tap -s "$sock_path"/link.sock vde &>/dev/null &
pid2=$!; }
# Add a little delay if vde stuff is just starting
for n in {0..5}; do
kill -0 "$pid1" "$pid2" || { echo >&2 "ERROR: vde pids failed to start"; exit 1; }
err=t; ip link show vde &>/dev/null && { err=; break; }
bsleep 0.1
done
[[ -z "$err" ]] || { echo >&2 "ERROR: vde failed to start (no iface)"; exit 1; }
[[ -n "$net_only" ]] && exit 0
### Use separate vhost-user-gpu helper process
[[ -z "$video_vhost" ]] || {
cgrc -rcu vhost-gpu && cgrc -ru vhost-gpu vm -- \
/usr/lib/qemu/vhost-user-gpu --virgl -s "$video_vhost" &
for n in {0..50}; do [[ -e "$video_vhost" ]] && break; bsleep 0.1; done
[[ $n -lt 50 ]] || cgrc -rcu vhost-gpu || {
echo >&2 "ERROR: vhost-user-gpu startup failed"; exit 1; } }
### img/iso options
## For scsi/nvme opts, see: -device help, -device virtio-blk,help
# -device virtio-scsi,id=scsi0,num_queues=4
# -device scsi-hd,drive=drive0,bus=scsi0.0,channel=0,scsi-id=0,lun=0
# -drive file="$img1",if=none,id=drive0
# -device scsi-hd,drive=drive1,bus=scsi0.0,channel=0,scsi-id=0,lun=1
# -drive file="$img2",if=none,id=drive1
n=0 root_opts=()
add_disk() {
[[ "$2" = /dev/* ]] && cache=none || cache=writethrough
root_opts+=(
-drive file="$1",id=vio-disk.$n,if=none,cache="$cache",aio=io_uring,"$2"
-device virtio-blk,drive=vio-disk.$n,logical_block_size=512,physical_block_size=4096 )
((n+=1)); }
[[ "${#root_opts_file[@]}" -eq 0 ]] || root_opts+=( "${root_opts_file[@]}" )
[[ "${#img[@]}" -eq 0 ]] || for p in "${img[@]}"; do add_disk "$p" format=qcow2; done
[[ "${#dev_ro[@]}" -eq 0 ]] || for p in "${dev_ro[@]}"; do add_disk "$p" format=raw,readonly=on; done
[[ "${#dev[@]}" -eq 0 ]] || for p in "${dev[@]}"; do add_disk "$p" format=raw; done
[[ -z "$iso" ]] || root_opts+=( -cdrom "$iso" )
[[ -z "$efi" ]] || root_opts+=(
-drive if=pflash,format=raw,readonly,file=/usr/share/ovmf/x64/OVMF_CODE.fd
-drive if=pflash,format=raw,file="$efi" )
### net options
mac_head=52 net_opts=()
while [[ "$nets" -gt 0 ]]; do
net_opts+=(
-device virtio-net-pci,mac=${mac_head}:${vde_mac_tail},netdev=vde_${mac_head}
-netdev vde,sock="$sock_path"/link.sock,id=vde_${mac_head} )
(( nets -= 1 )); (( mac_head += 2 ))
done
### Run qemu
qemu_opts=(
-name "$(basename "$0").$ins"
-rtc base=utc,clock=host
-monitor telnet:127.0.0.1:${telnet_port},server,nowait
-usb -device usb-kbd -device usb-mouse
-k en-us
-enable-kvm
-cpu host
-smp "$smp"
-m "$mem"
-device virtio-balloon,deflate-on-oom=on
"${video[@]}"
"${net_opts[@]}"
"${root_opts[@]}"
# -device usb-ehci,id=usb,bus=pci.0,addr=0x4
# -device usb-host,hostbus=1,hostaddr=3
)
ulimit -c 0 -s 1048576 -m 18874368 -v 25165824 # STACK=1G RSZ=18G VSZ=24G
export QEMU_AUDIO_DRV=none NO_AT_BRIDGE=1
[[ -n "$restore" ]] && qemu_opts+=( -incoming "exec:bsdcat $dst" )
exec cgrc -ru "qemu-linux@$ins" vm -- qemu-system-x86_64 "${qemu_opts[@]}"