-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmicrovm.sh
executable file
·256 lines (241 loc) · 7.09 KB
/
microvm.sh
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
250
251
252
253
254
255
256
#! /bin/sh
##
## microvm.sh --
##
## Help script for https://github.com/uablrek/microvm
##
## Commands;
##
prg=$(basename $0)
dir=$(dirname $0); dir=$(readlink -f $dir)
tmp=/tmp/${prg}_$$
die() {
echo "ERROR: $*" >&2
rm -rf $tmp
exit 1
}
help() {
grep '^##' $0 | cut -c3-
rm -rf $tmp
exit 0
}
test -n "$1" || help
echo "$1" | grep -qi "^help\|-h" && help
log() {
echo "$*" >&2
}
findf() {
f=$ARCHIVE/$1
test -r $f && return 0
f=$HOME/Downloads/$1
test -r $f
}
## env
## Print environment
cmd_env() {
test "$envread" = "yes" && return 0
envread=yes
test -n "$MICROVM_WORKSPACE" || export MICROVM_WORKSPACE=$HOME/tmp/microvm
test -n "$ARCHIVE" || export ARCHIVE=$HOME/Downloads
test -n "$__kver" || __kver=linux-6.9.1
test -n "$__kdir" || __kdir=$MICROVM_WORKSPACE/$__kver
test -n "$__kcfg" || __kcfg=$dir/config/$__kver
test -n "$__kernel" || __kernel=$MICROVM_WORKSPACE/bzImage-$__kver
test -n "$__kobj" || __kobj=$MICROVM_WORKSPACE/obj-$__kver
test -n "$__fcver" || __fcver=v1.7.0
fc=$MICROVM_WORKSPACE/release-$__fcver-x86_64/firecracker-$__fcver-x86_64
test -n "$__fccfg" || __fccfg=$dir/config/vm_config.json
test -n "$__rootfsar" || __rootfsar=alpine-minirootfs-3.19.1-x86_64.tar.gz
if test -z "$DISKIM"; then
DISKIM=$(find $MICROVM_WORKSPACE -name diskim.sh)
test -n "$DISKIM" || log "WARNING: diskim not installed"
fi
if test "$cmd" = "env"; then
local opts="kver|kcfg|kobj|kdir|fcver|kernel|ksetup|fccfg|rootfsar"
set | grep -E "^(__($opts)|MICROVM_.*|ARCHIVE|DISKIM|fc)=" | sort
exit 0
fi
test -d "$MICROVM_WORKSPACE" || mkdir -p "$MICROVM_WORKSPACE"
}
## setup
## The $MICROVM_WORKSPACE dir is used which defaults to
## $HOME/tmp/microvm. This should be executed when diskim or the kernel
## is updated, or on initial setup
cmd_setup() {
local ar
if test -n "$DISKIM"; then
log "Already installed [$DISKIM]"
else
local diskim_ver=1.0.0
log "Installing diskim $diskim_ver ..."
ar=diskim-$diskim_ver.tar.xz
if ! findf $ar; then
curl -L -o $ARCHIVE/$ar https://github.com/lgekman/diskim/releases/download/$diskim_ver/$ar || die "FAILED: Download diskim"
findf $ar || die "FAILED: diskim not found"
fi
tar -C $MICROVM_WORKSPACE -xf $f || die "FAILED: tar -xf $f"
DISKIM=$(find $MICROVM_WORKSPACE -name diskim.sh)
test -x $DISKIM || die "Not executable [$DISKIM]"
fi
if test -d $__kdir; then
log "Already installed [$__kdir]"
else
export __kver __kdir
$DISKIM kernel_download || die "FAILED"
$DISKIM kernel_unpack || die "FAILED"
fi
if test -x $fc; then
log "Already installed [$fc]"
else
ar=firecracker-$__fcver-x86_64.tgz
log "Installing $ar ..."
if ! findf $ar; then
curl -L -o $ARCHIVE/$ar https://github.com/firecracker-microvm/firecracker/releases/download/$__fcver/$ar || die "FAILED: Download firecracker"
findf $ar || die "FAILED: firecracker not found"
fi
tar -C $MICROVM_WORKSPACE -xf $f || die "FAILED: tar -xf $f"
test -x $fc || die "FAILED: Install firecracker"
fi
if findf $__rootfsar; then
log "Rootfs archive found [$__rootfsar]"
else
die "Not found [$__rootfsar]. Download from https://dl-cdn.alpinelinux.org/alpine"
fi
}
## kernel_build [--menuconfig] [--tinyconfig]
## Build the microvm kernel
cmd_kernel_build() {
if test "$__tinyconfig" = "yes"; then
rm -r $__kobj
mkdir -p $__kobj $(dirname $__kcfg)
make -C $__kdir O=$__kobj tinyconfig
cp $__kobj/.config $__kcfg
__menuconfig=yes
fi
export __kver __kdir __kcfg __kobj __kernel
$DISKIM kernel_build --menuconfig=$__menuconfig
}
## mkimage [--size=2G] [--rootfsar=] <output-file> [ovls...]
## Create a disk image from a rootfs archive and optional overlays
cmd_mkimage() {
test -n "$1" || die "Parameter missing"
local image=$1
shift
findf $__rootfsar || die "Can's find rootfs archive [$__rootfsar]"
unset __kernel
$DISKIM mkimage --size=$__size --format=raw --image=$image $f $@ || die FAILED
}
## mktap [--bridge=] [--adr=] [--user=$USER] <tap>
## Create a network tun/tap device. The tun/tap device can
## optionally be attached to a bridge. If "sudo" is required
## you must specify "--user=$USER"
cmd_mktap() {
test -n "$1" || die "Parameter missing"
if ip link show dev $1 > /dev/null 2>&1; then
log "Device exists [$1]"
return 0
fi
if test -n "$__bridge"; then
ip link show dev $__bridge > /dev/null 2>&1 \
|| die "Bridge does not exist [$__bridge]"
fi
test -n "$__user" || __user=$USER
ip tuntap add $1 mode tap user $__user || die "Create tap"
ip link set up $1
if test -n "$__bridge"; then
ip link set dev $1 master $__bridge || die "Attach to bridge"
elif test -n "$__adr"; then
local opt
echo "$__adr" | grep -q : && opt=-6
ip $opt addr add $__adr dev $1 || die "Set address [$__adr]"
fi
}
## run_microvm [--init=/init] [--mem=128] [--tap=] <image>
## Run a qemu microvm
cmd_run_microvm() {
test -n "$1" || die "Parameter missing"
test -r "$1" || die "Not readable [$1]"
local image=$1
shift
test -n "$__init" || __init=/init
test -n "$__mem" || __mem=128
local opt="-smp 2 -k sv -m $__mem"
opt="$opt -drive file=$image,if=none,id=drive0,format=raw"
opt="$opt -device virtio-blk-device,drive=drive0"
if test -n "$__tap"; then
setmac
opt="$opt -netdev tap,id=$__tap,script=no,ifname=$__tap"
opt="$opt -device virtio-net-device,netdev=$__tap,mac=$mac"
fi
exec qemu-system-x86_64-microvm -enable-kvm \
-M microvm,acpi=off,x-option-roms=off,pit=on,pic=off,rtc=off \
-cpu host -nodefaults -no-user-config -nographic -no-reboot \
-serial stdio -kernel $__kernel $opt \
-append "console=ttyS0 root=/dev/vda init=$__init rw reboot=t $__append"
}
setmac() {
local b0=$(echo $__tap | tr -dc '[0-9]')
if test -n "$b0"; then
b0=$(printf "%02d" $b0)
else
b0=00
fi
mac=00:00:00:01:00:$b0
}
## run_fc [--init=/init] [--mem=128] [--tap=] <image>
## Run a firecracker vm
cmd_run_fc() {
test -n "$1" || die "Parameter missing"
test -r "$1" || die "Not readable [$1]"
local image=$1
test -n "$__init" || __init=/init
test -n "$__mem" || __mem=128
mkdir -p $tmp
local kernel=$__kobj/vmlinux
sed -e "s,vmlinux.bin,$kernel," -e "s,bionic.rootfs.ext4,$image," \
-e "s,/init,$__init," \
-e "s,\"mem_size_mib\": 1024,\"mem_size_mib\": $__mem," \
< $__fccfg > $tmp/fc-config.json
if test -n "$__tap"; then
setmac
cat > $tmp/addnet <<EOF
."network-interfaces" += [{
"iface_id": "eth0",
"guest_mac": "$mac",
"host_dev_name": "$__tap"
}]
EOF
jq -f $tmp/addnet $tmp/fc-config.json > $tmp/fc-config-net.json
mv -f $tmp/fc-config-net.json $tmp/fc-config.json
fi
$fc --no-api --config-file $tmp/fc-config.json
}
##
# Get the command
cmd=$1
shift
grep -q "^cmd_$cmd()" $0 $hook || die "Invalid command [$cmd]"
while echo "$1" | grep -q '^--'; do
if echo $1 | grep -q =; then
o=$(echo "$1" | cut -d= -f1 | sed -e 's,-,_,g')
v=$(echo "$1" | cut -d= -f2-)
eval "$o=\"$v\""
else
if test "$1" = "--"; then
shift
break
fi
o=$(echo "$1" | sed -e 's,-,_,g')
eval "$o=yes"
fi
shift
done
unset o v
long_opts=`set | grep '^__' | cut -d= -f1`
# Execute command
trap "die Interrupted" INT TERM
cmd_env
cmd_$cmd "$@"
status=$?
rm -rf $tmp
exit $status