Store VM metadata as JSON
This commit is contained in:
parent
bbd57d8dd2
commit
7af04b7535
11 changed files with 188 additions and 178 deletions
21
README.md
21
README.md
|
|
@ -4,7 +4,7 @@ Minimal Firecracker launcher.
|
|||
|
||||
## Requirements
|
||||
- Linux host with KVM (`/dev/kvm` access)
|
||||
- `sudo`, `ip`, `curl`, `ssh`
|
||||
- `sudo`, `ip`, `curl`, `ssh`, `jq`
|
||||
- `dmsetup`, `losetup`, `blockdev` (device-mapper snapshot for rootfs)
|
||||
- `e2cp`, `e2rm` (writes hostname and resolv.conf into rootfs snapshot)
|
||||
|
||||
|
|
@ -63,22 +63,9 @@ reboot
|
|||
```
|
||||
|
||||
## VM Info File
|
||||
Each VM writes a metadata file at `state/vms/<id>/info` with the following fields:
|
||||
- `id`: unique identifier for the VM instance.
|
||||
- `name`: VM name.
|
||||
- `pid`: Firecracker process ID.
|
||||
- `created_at`: timestamp when the VM was launched.
|
||||
- `rootfs`: root filesystem image path used to launch the VM.
|
||||
- `kernel`: kernel image path used to launch the VM.
|
||||
- `guest_ip`: IP address assigned to the guest.
|
||||
- `tap`: host TAP interface name attached to the bridge.
|
||||
- `api_sock`: path to the Firecracker API socket (stored under `$XDG_RUNTIME_DIR/banger/` when available).
|
||||
- `log`: path to the Firecracker log file.
|
||||
- `base_loop`: loop device backing the base rootfs (if present).
|
||||
- `cow_file`: copy-on-write image file (if present).
|
||||
- `cow_loop`: loop device for the COW image (if present).
|
||||
- `dm_name`: device-mapper name for the merged rootfs (if present).
|
||||
- `dm_dev`: device-mapper device path for the merged rootfs (if present).
|
||||
Each VM writes `state/vms/<id>/vm.json` with:
|
||||
- `meta`: local metadata (id, name, pid, created_at, guest_ip, tap, api_sock, log, rootfs, kernel, snapshot info).
|
||||
- `config`: full `/vm/config` response from Firecracker.
|
||||
|
||||
## Log Notes
|
||||
- `PCI: Fatal: No config space access function found` and `MissingAddressRange` lines are expected with `pci=off` in `run.sh`.
|
||||
|
|
|
|||
43
customize.sh
43
customize.sh
|
|
@ -89,6 +89,10 @@ if ! command -v resize2fs >/dev/null 2>&1; then
|
|||
log "resize2fs required"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
log "jq required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "copying base rootfs to $OUT_ROOTFS"
|
||||
cp --reflink=auto "$BASE_ROOTFS" "$OUT_ROOTFS"
|
||||
|
|
@ -165,16 +169,22 @@ for _ in $(seq 1 200); do
|
|||
done
|
||||
[[ -S "$API_SOCK" ]] || { log "firecracker api socket not ready"; exit 1; }
|
||||
|
||||
cat > "$VM_DIR/info" <<EOF
|
||||
id=$VM_ID
|
||||
name=$VM_NAME
|
||||
pid=$FC_PID
|
||||
created_at=$(date -Iseconds)
|
||||
guest_ip=$GUEST_IP
|
||||
tap=$TAP_DEV
|
||||
api_sock=$API_SOCK
|
||||
log=$LOG_FILE
|
||||
EOF
|
||||
VM_CONFIG_JSON="$(sudo -E curl --unix-socket "$API_SOCK" -sS http://localhost/vm/config)"
|
||||
CREATED_AT="$(date -Iseconds)"
|
||||
jq -n \
|
||||
--arg id "$VM_ID" \
|
||||
--arg name "$VM_NAME" \
|
||||
--arg pid "$FC_PID" \
|
||||
--arg created_at "$CREATED_AT" \
|
||||
--arg guest_ip "$GUEST_IP" \
|
||||
--arg tap "$TAP_DEV" \
|
||||
--arg api_sock "$API_SOCK" \
|
||||
--arg log "$LOG_FILE" \
|
||||
--arg rootfs "$OUT_ROOTFS" \
|
||||
--arg kernel "$KERNEL" \
|
||||
--argjson config "$VM_CONFIG_JSON" \
|
||||
'{meta:{id:$id,name:$name,pid:$pid,created_at:$created_at,guest_ip:$guest_ip,tap:$tap,api_sock:$api_sock,log:$log,rootfs:$rootfs,kernel:$kernel},config:$config}' \
|
||||
> "$VM_DIR/vm.json"
|
||||
|
||||
log "configuring machine"
|
||||
sudo -E curl --unix-socket "$API_SOCK" -X PUT http://localhost/machine-config \
|
||||
|
|
@ -232,6 +242,19 @@ ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
|||
printf 'nameserver %s\n' \"$DNS_SERVER\" > /etc/resolv.conf
|
||||
echo \"$VM_NAME\" > /etc/hostname
|
||||
printf '127.0.0.1 localhost\n127.0.1.1 %s\n' \"$VM_NAME\" > /etc/hosts
|
||||
mkdir -p /home /var
|
||||
if ! grep -q '^/dev/vdb ' /etc/fstab; then
|
||||
echo '/dev/vdb /home ext4 defaults 0 2' >> /etc/fstab
|
||||
fi
|
||||
if ! grep -q '^/dev/vdc ' /etc/fstab; then
|
||||
echo '/dev/vdc /var ext4 defaults 0 2' >> /etc/fstab
|
||||
fi
|
||||
if ! grep -q '^tmpfs /run ' /etc/fstab; then
|
||||
echo 'tmpfs /run tmpfs defaults,nodev,nosuid,mode=0755 0 0' >> /etc/fstab
|
||||
fi
|
||||
if ! grep -q '^tmpfs /tmp ' /etc/fstab; then
|
||||
echo 'tmpfs /tmp tmpfs defaults,nodev,nosuid,mode=1777 0 0' >> /etc/fstab
|
||||
fi
|
||||
apt-get update
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y upgrade
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y install git less tree ca-certificates curl
|
||||
|
|
|
|||
30
kill.sh
30
kill.sh
|
|
@ -13,23 +13,17 @@ Sends a signal to the Firecracker process.
|
|||
EOF
|
||||
}
|
||||
|
||||
get_prop() {
|
||||
local info="$1"
|
||||
local key="$2"
|
||||
awk -F= -v k="$key" '$1==k {print $2}' "$info"
|
||||
}
|
||||
|
||||
find_vm_info() {
|
||||
find_vm_json() {
|
||||
local query="$1"
|
||||
local info match_count=0 match=""
|
||||
local vm_json match_count=0 match=""
|
||||
|
||||
for info in state/vms/*/info; do
|
||||
[[ -f "$info" ]] || continue
|
||||
for vm_json in state/vms/*/vm.json; do
|
||||
[[ -f "$vm_json" ]] || continue
|
||||
local id name
|
||||
id="$(get_prop "$info" "id")"
|
||||
name="$(get_prop "$info" "name")"
|
||||
id="$(jq -r '.meta.id // empty' "$vm_json")"
|
||||
name="$(jq -r '.meta.name // empty' "$vm_json")"
|
||||
if [[ "$id" == "$query"* || "$name" == "$query"* ]]; then
|
||||
match="$info"
|
||||
match="$vm_json"
|
||||
match_count=$((match_count + 1))
|
||||
fi
|
||||
done
|
||||
|
|
@ -75,15 +69,15 @@ if [[ -z "$QUERY" || "$QUERY" == "-h" || "$QUERY" == "--help" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
INFO_FILE="$(find_vm_info "$QUERY")"
|
||||
PID="$(get_prop "$INFO_FILE" "pid")"
|
||||
API_SOCK="$(get_prop "$INFO_FILE" "api_sock")"
|
||||
VM_JSON="$(find_vm_json "$QUERY")"
|
||||
PID="$(jq -r '.meta.pid // empty' "$VM_JSON")"
|
||||
API_SOCK="$(jq -r '.meta.api_sock // empty' "$VM_JSON")"
|
||||
if [[ -z "$PID" ]]; then
|
||||
log "pid not found in $INFO_FILE"
|
||||
log "pid not found in $VM_JSON"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "$API_SOCK" ]]; then
|
||||
log "api_sock not found in $INFO_FILE"
|
||||
log "api_sock not found in $VM_JSON"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
|||
55
list.sh
55
list.sh
|
|
@ -3,28 +3,51 @@ set -euo pipefail
|
|||
|
||||
shopt -s nullglob
|
||||
|
||||
get_prop() {
|
||||
local info="$1"
|
||||
local key="$2"
|
||||
awk -F= -v k="$key" '$1==k {print $2}' "$info"
|
||||
}
|
||||
statuses=()
|
||||
ids=()
|
||||
names=()
|
||||
ips=()
|
||||
created=()
|
||||
max_name=4
|
||||
max_ip=2
|
||||
GREEN='\033[32m'
|
||||
YELLOW='\033[33m'
|
||||
RESET='\033[0m'
|
||||
|
||||
for info in state/vms/*/info; do
|
||||
id="$(get_prop "$info" "id")"
|
||||
name="$(get_prop "$info" "name")"
|
||||
created_at="$(get_prop "$info" "created_at")"
|
||||
guest_ip="$(get_prop "$info" "guest_ip")"
|
||||
pid="$(get_prop "$info" "pid")"
|
||||
tap="$(get_prop "$info" "tap")"
|
||||
api_sock="$(get_prop "$info" "api_sock")"
|
||||
for vm_json in state/vms/*/vm.json; do
|
||||
id="$(jq -r '.meta.id // empty' "$vm_json")"
|
||||
name="$(jq -r '.meta.name // empty' "$vm_json")"
|
||||
created_at="$(jq -r '.meta.created_at // empty' "$vm_json")"
|
||||
guest_ip="$(jq -r '.meta.guest_ip // empty' "$vm_json")"
|
||||
pid="$(jq -r '.meta.pid // empty' "$vm_json")"
|
||||
api_sock="$(jq -r '.meta.api_sock // empty' "$vm_json")"
|
||||
|
||||
status="stale"
|
||||
status="stopped"
|
||||
if [[ -n "$pid" && -n "$api_sock" ]]; then
|
||||
if ps -p "$pid" -o comm=,args= 2>/dev/null | rg -q "firecracker.*--api-sock $api_sock"; then
|
||||
status="running"
|
||||
fi
|
||||
fi
|
||||
|
||||
printf 'status=%s id=%s name=%s created_at=%s guest_ip=%s pid=%s tap=%s\n' \
|
||||
"$status" "$id" "$name" "$created_at" "$guest_ip" "$pid" "$tap"
|
||||
short_id="${id:0:12}"
|
||||
statuses+=("$status")
|
||||
ids+=("$short_id")
|
||||
names+=("$name")
|
||||
ips+=("$guest_ip")
|
||||
created+=("$created_at")
|
||||
if (( ${#name} > max_name )); then
|
||||
max_name=${#name}
|
||||
fi
|
||||
if (( ${#guest_ip} > max_ip )); then
|
||||
max_ip=${#guest_ip}
|
||||
fi
|
||||
done
|
||||
|
||||
for i in "${!ids[@]}"; do
|
||||
color="$YELLOW"
|
||||
if [[ "${statuses[$i]}" == "running" ]]; then
|
||||
color="$GREEN"
|
||||
fi
|
||||
printf '%-10b %-12s %-*s %-*s %s\n' \
|
||||
"${color}[${statuses[$i]}]${RESET}" "${ids[$i]}" "$max_name" "${names[$i]}" "$max_ip" "${ips[$i]}" "${created[$i]}"
|
||||
done
|
||||
|
|
|
|||
24
logs.sh
24
logs.sh
|
|
@ -13,23 +13,17 @@ Prints the Firecracker log for a VM. Use --follow to tail -f.
|
|||
EOF
|
||||
}
|
||||
|
||||
get_prop() {
|
||||
local info="$1"
|
||||
local key="$2"
|
||||
awk -F= -v k="$key" '$1==k {print $2}' "$info"
|
||||
}
|
||||
|
||||
find_vm_info() {
|
||||
find_vm_json() {
|
||||
local query="$1"
|
||||
local info match_count=0 match=""
|
||||
local vm_json match_count=0 match=""
|
||||
|
||||
for info in state/vms/*/info; do
|
||||
[[ -f "$info" ]] || continue
|
||||
for vm_json in state/vms/*/vm.json; do
|
||||
[[ -f "$vm_json" ]] || continue
|
||||
local id name
|
||||
id="$(get_prop "$info" "id")"
|
||||
name="$(get_prop "$info" "name")"
|
||||
id="$(jq -r '.meta.id // empty' "$vm_json")"
|
||||
name="$(jq -r '.meta.name // empty' "$vm_json")"
|
||||
if [[ "$id" == "$query"* || "$name" == "$query"* ]]; then
|
||||
match="$info"
|
||||
match="$vm_json"
|
||||
match_count=$((match_count + 1))
|
||||
fi
|
||||
done
|
||||
|
|
@ -76,8 +70,8 @@ if [[ -z "$QUERY" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
INFO_FILE="$(find_vm_info "$QUERY")"
|
||||
LOG_FILE="$(get_prop "$INFO_FILE" "log")"
|
||||
VM_JSON="$(find_vm_json "$QUERY")"
|
||||
LOG_FILE="$(jq -r '.meta.log // empty' "$VM_JSON")"
|
||||
|
||||
if [[ -z "$LOG_FILE" || ! -f "$LOG_FILE" ]]; then
|
||||
log "log file not found: $LOG_FILE"
|
||||
|
|
|
|||
28
nat.sh
28
nat.sh
|
|
@ -13,23 +13,17 @@ Manage per-VM NAT rules for internet access.
|
|||
EOF
|
||||
}
|
||||
|
||||
get_prop() {
|
||||
local info="$1"
|
||||
local key="$2"
|
||||
awk -F= -v k="$key" '$1==k {print $2}' "$info"
|
||||
}
|
||||
|
||||
find_vm_info() {
|
||||
find_vm_json() {
|
||||
local query="$1"
|
||||
local info match_count=0 match=""
|
||||
local vm_json match_count=0 match=""
|
||||
|
||||
for info in state/vms/*/info; do
|
||||
[[ -f "$info" ]] || continue
|
||||
for vm_json in state/vms/*/vm.json; do
|
||||
[[ -f "$vm_json" ]] || continue
|
||||
local id name
|
||||
id="$(get_prop "$info" "id")"
|
||||
name="$(get_prop "$info" "name")"
|
||||
id="$(jq -r '.meta.id // empty' "$vm_json")"
|
||||
name="$(jq -r '.meta.name // empty' "$vm_json")"
|
||||
if [[ "$id" == "$query"* || "$name" == "$query"* ]]; then
|
||||
match="$info"
|
||||
match="$vm_json"
|
||||
match_count=$((match_count + 1))
|
||||
fi
|
||||
done
|
||||
|
|
@ -64,12 +58,12 @@ if [[ -z "$ACTION" || -z "$QUERY" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
INFO_FILE="$(find_vm_info "$QUERY")"
|
||||
GUEST_IP="$(get_prop "$INFO_FILE" "guest_ip")"
|
||||
TAP="$(get_prop "$INFO_FILE" "tap")"
|
||||
VM_JSON="$(find_vm_json "$QUERY")"
|
||||
GUEST_IP="$(jq -r '.meta.guest_ip // empty' "$VM_JSON")"
|
||||
TAP="$(jq -r '.meta.tap // empty' "$VM_JSON")"
|
||||
|
||||
if [[ -z "$GUEST_IP" || -z "$TAP" ]]; then
|
||||
log "missing guest_ip or tap in $INFO_FILE"
|
||||
log "missing guest_ip or tap in $VM_JSON"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
|||
22
ps.sh
22
ps.sh
|
|
@ -3,20 +3,14 @@ set -euo pipefail
|
|||
|
||||
shopt -s nullglob
|
||||
|
||||
get_prop() {
|
||||
local info="$1"
|
||||
local key="$2"
|
||||
awk -F= -v k="$key" '$1==k {print $2}' "$info"
|
||||
}
|
||||
|
||||
for info in state/vms/*/info; do
|
||||
id="$(get_prop "$info" "id")"
|
||||
name="$(get_prop "$info" "name")"
|
||||
created_at="$(get_prop "$info" "created_at")"
|
||||
guest_ip="$(get_prop "$info" "guest_ip")"
|
||||
pid="$(get_prop "$info" "pid")"
|
||||
tap="$(get_prop "$info" "tap")"
|
||||
api_sock="$(get_prop "$info" "api_sock")"
|
||||
for vm_json in state/vms/*/vm.json; do
|
||||
id="$(jq -r '.meta.id // empty' "$vm_json")"
|
||||
name="$(jq -r '.meta.name // empty' "$vm_json")"
|
||||
created_at="$(jq -r '.meta.created_at // empty' "$vm_json")"
|
||||
guest_ip="$(jq -r '.meta.guest_ip // empty' "$vm_json")"
|
||||
pid="$(jq -r '.meta.pid // empty' "$vm_json")"
|
||||
tap="$(jq -r '.meta.tap // empty' "$vm_json")"
|
||||
api_sock="$(jq -r '.meta.api_sock // empty' "$vm_json")"
|
||||
|
||||
if [[ -z "$pid" || -z "$api_sock" ]]; then
|
||||
continue
|
||||
|
|
|
|||
38
rm.sh
38
rm.sh
|
|
@ -13,23 +13,17 @@ Removes VM artifacts from state/ and cleans up TAP and mapped devices.
|
|||
EOF
|
||||
}
|
||||
|
||||
get_prop() {
|
||||
local info="$1"
|
||||
local key="$2"
|
||||
awk -F= -v k="$key" '$1==k {print $2}' "$info"
|
||||
}
|
||||
|
||||
find_vm_info() {
|
||||
find_vm_json() {
|
||||
local query="$1"
|
||||
local info match_count=0 match=""
|
||||
local vm_json match_count=0 match=""
|
||||
|
||||
for info in state/vms/*/info; do
|
||||
[[ -f "$info" ]] || continue
|
||||
for vm_json in state/vms/*/vm.json; do
|
||||
[[ -f "$vm_json" ]] || continue
|
||||
local id name
|
||||
id="$(get_prop "$info" "id")"
|
||||
name="$(get_prop "$info" "name")"
|
||||
id="$(jq -r '.meta.id // empty' "$vm_json")"
|
||||
name="$(jq -r '.meta.name // empty' "$vm_json")"
|
||||
if [[ "$id" == "$query"* || "$name" == "$query"* ]]; then
|
||||
match="$info"
|
||||
match="$vm_json"
|
||||
match_count=$((match_count + 1))
|
||||
fi
|
||||
done
|
||||
|
|
@ -52,15 +46,15 @@ if [[ -z "$QUERY" || "$QUERY" == "-h" || "$QUERY" == "--help" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
INFO_FILE="$(find_vm_info "$QUERY")"
|
||||
VM_DIR="${INFO_FILE%/info}"
|
||||
PID="$(get_prop "$INFO_FILE" "pid")"
|
||||
TAP="$(get_prop "$INFO_FILE" "tap")"
|
||||
API_SOCK="$(get_prop "$INFO_FILE" "api_sock")"
|
||||
BASE_LOOP="$(get_prop "$INFO_FILE" "base_loop")"
|
||||
COW_LOOP="$(get_prop "$INFO_FILE" "cow_loop")"
|
||||
DM_DEV="$(get_prop "$INFO_FILE" "dm_dev")"
|
||||
DM_NAME="$(get_prop "$INFO_FILE" "dm_name")"
|
||||
VM_JSON="$(find_vm_json "$QUERY")"
|
||||
VM_DIR="$(dirname "$VM_JSON")"
|
||||
PID="$(jq -r '.meta.pid // empty' "$VM_JSON")"
|
||||
TAP="$(jq -r '.meta.tap // empty' "$VM_JSON")"
|
||||
API_SOCK="$(jq -r '.meta.api_sock // empty' "$VM_JSON")"
|
||||
BASE_LOOP="$(jq -r '.meta.base_loop // empty' "$VM_JSON")"
|
||||
COW_LOOP="$(jq -r '.meta.cow_loop // empty' "$VM_JSON")"
|
||||
DM_DEV="$(jq -r '.meta.dm_dev // empty' "$VM_JSON")"
|
||||
DM_NAME="$(jq -r '.meta.dm_name // empty' "$VM_JSON")"
|
||||
|
||||
if [[ -n "$PID" ]]; then
|
||||
sudo kill "$PID" 2>/dev/null || true
|
||||
|
|
|
|||
51
run.sh
51
run.sh
|
|
@ -62,9 +62,9 @@ shopt -s nullglob
|
|||
|
||||
name_taken() {
|
||||
local candidate="$1"
|
||||
local info existing_name
|
||||
for info in "$VM_ROOT"/*/info; do
|
||||
existing_name="$(awk -F= '$1=="name"{print $2}' "$info")"
|
||||
local vm_json existing_name
|
||||
for vm_json in "$VM_ROOT"/*/vm.json; do
|
||||
existing_name="$(jq -r '.meta.name // empty' "$vm_json" 2>/dev/null)"
|
||||
if [[ "$existing_name" == "$candidate" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
|
@ -311,6 +311,10 @@ if ! command -v e2cp >/dev/null 2>&1 || ! command -v e2rm >/dev/null 2>&1; then
|
|||
log "e2cp and e2rm are required to set hostname and resolv.conf"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
log "jq is required to persist VM metadata"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COW_BYTES="$(parse_disk_size "$COW_SIZE")"
|
||||
if [[ -z "$COW_BYTES" ]]; then
|
||||
|
|
@ -469,24 +473,29 @@ log "starting virtual machine"
|
|||
-H "Content-Type: application/json" \
|
||||
-d '{ "action_type": "InstanceStart" }' >/dev/null
|
||||
VM_STARTED=1
|
||||
|
||||
cat > "$VM_DIR/info" <<EOF
|
||||
id=$VM_ID
|
||||
name=$VM_NAME
|
||||
pid=$FC_PID
|
||||
created_at=$(date -Iseconds)
|
||||
rootfs=$ROOTFS
|
||||
kernel=$KERNEL
|
||||
guest_ip=$GUEST_IP
|
||||
tap=$TAP_DEV
|
||||
api_sock=$API_SOCK
|
||||
log=$LOG_FILE
|
||||
base_loop=$BASE_LOOP
|
||||
cow_file=$COW_FILE
|
||||
cow_loop=$COW_LOOP
|
||||
dm_name=$DM_NAME
|
||||
dm_dev=$DM_DEV
|
||||
EOF
|
||||
VM_CONFIG_JSON="$("${CURL_CMD[@]}" --unix-socket "$API_SOCK" -sS http://localhost/vm/config)"
|
||||
CREATED_AT="$(date -Iseconds)"
|
||||
jq -n \
|
||||
--arg id "$VM_ID" \
|
||||
--arg name "$VM_NAME" \
|
||||
--arg pid "$FC_PID" \
|
||||
--arg created_at "$CREATED_AT" \
|
||||
--arg guest_ip "$GUEST_IP" \
|
||||
--arg tap "$TAP_DEV" \
|
||||
--arg api_sock "$API_SOCK" \
|
||||
--arg log "$LOG_FILE" \
|
||||
--arg rootfs "$ROOTFS" \
|
||||
--arg kernel "$KERNEL" \
|
||||
--arg home_path "$HOME_PATH" \
|
||||
--arg var_path "$VAR_PATH" \
|
||||
--arg base_loop "$BASE_LOOP" \
|
||||
--arg cow_file "$COW_FILE" \
|
||||
--arg cow_loop "$COW_LOOP" \
|
||||
--arg dm_name "$DM_NAME" \
|
||||
--arg dm_dev "$DM_DEV" \
|
||||
--argjson config "$VM_CONFIG_JSON" \
|
||||
'{meta:{id:$id,name:$name,pid:$pid,created_at:$created_at,guest_ip:$guest_ip,tap:$tap,api_sock:$api_sock,log:$log,rootfs:$rootfs,kernel:$kernel,home_path:$home_path,var_path:$var_path,base_loop:$base_loop,cow_file:$cow_file,cow_loop:$cow_loop,dm_name:$dm_name,dm_dev:$dm_dev},config:$config}' \
|
||||
> "$VM_DIR/vm.json"
|
||||
|
||||
log "vm started successfully"
|
||||
log "guest ip: $GUEST_IP"
|
||||
|
|
|
|||
24
stop.sh
24
stop.sh
|
|
@ -13,23 +13,17 @@ Sends Ctrl+Alt+Del to the guest via the Firecracker API socket.
|
|||
EOF
|
||||
}
|
||||
|
||||
get_prop() {
|
||||
local info="$1"
|
||||
local key="$2"
|
||||
awk -F= -v k="$key" '$1==k {print $2}' "$info"
|
||||
}
|
||||
|
||||
find_vm_info() {
|
||||
find_vm_json() {
|
||||
local query="$1"
|
||||
local info match_count=0 match=""
|
||||
local vm_json match_count=0 match=""
|
||||
|
||||
for info in state/vms/*/info; do
|
||||
[[ -f "$info" ]] || continue
|
||||
for vm_json in state/vms/*/vm.json; do
|
||||
[[ -f "$vm_json" ]] || continue
|
||||
local id name
|
||||
id="$(get_prop "$info" "id")"
|
||||
name="$(get_prop "$info" "name")"
|
||||
id="$(jq -r '.meta.id // empty' "$vm_json")"
|
||||
name="$(jq -r '.meta.name // empty' "$vm_json")"
|
||||
if [[ "$id" == "$query"* || "$name" == "$query"* ]]; then
|
||||
match="$info"
|
||||
match="$vm_json"
|
||||
match_count=$((match_count + 1))
|
||||
fi
|
||||
done
|
||||
|
|
@ -52,8 +46,8 @@ if [[ -z "$QUERY" || "$QUERY" == "-h" || "$QUERY" == "--help" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
INFO_FILE="$(find_vm_info "$QUERY")"
|
||||
API_SOCK="$(get_prop "$INFO_FILE" "api_sock")"
|
||||
VM_JSON="$(find_vm_json "$QUERY")"
|
||||
API_SOCK="$(jq -r '.meta.api_sock // empty' "$VM_JSON")"
|
||||
if [[ -z "$API_SOCK" || ! -S "$API_SOCK" ]]; then
|
||||
log "api socket not found: $API_SOCK"
|
||||
exit 1
|
||||
|
|
|
|||
30
verify.sh
30
verify.sh
|
|
@ -6,18 +6,19 @@ log() {
|
|||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ -z "${VM_INFO:-}" || ! -f "$VM_INFO" ]]; then
|
||||
if [[ -z "${VM_JSON:-}" || ! -f "$VM_JSON" ]]; then
|
||||
return
|
||||
fi
|
||||
# shellcheck disable=SC1090
|
||||
source "$VM_INFO"
|
||||
if [[ -n "${pid:-}" ]]; then
|
||||
pid="$(jq -r '.meta.pid // empty' "$VM_JSON")"
|
||||
tap="$(jq -r '.meta.tap // empty' "$VM_JSON")"
|
||||
vm_dir="$(dirname "$VM_JSON")"
|
||||
if [[ -n "$pid" ]]; then
|
||||
sudo kill "$pid" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -n "${tap:-}" ]]; then
|
||||
if [[ -n "$tap" ]]; then
|
||||
sudo ip link del "$tap" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -n "${vm_dir:-}" ]]; then
|
||||
if [[ -n "$vm_dir" ]]; then
|
||||
rm -rf "$vm_dir"
|
||||
fi
|
||||
}
|
||||
|
|
@ -35,18 +36,21 @@ if [[ -z "$VM_DIR" ]]; then
|
|||
log "no VM state directory found"
|
||||
exit 1
|
||||
fi
|
||||
VM_INFO="$VM_DIR/info"
|
||||
if [[ ! -f "$VM_INFO" ]]; then
|
||||
log "info file not found: $VM_INFO"
|
||||
VM_JSON="$VM_DIR/vm.json"
|
||||
if [[ ! -f "$VM_JSON" ]]; then
|
||||
log "vm.json not found: $VM_JSON"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
source "$VM_INFO"
|
||||
name="$(jq -r '.meta.name // empty' "$VM_JSON")"
|
||||
created_at="$(jq -r '.meta.created_at // empty' "$VM_JSON")"
|
||||
guest_ip="$(jq -r '.meta.guest_ip // empty' "$VM_JSON")"
|
||||
tap="$(jq -r '.meta.tap // empty' "$VM_JSON")"
|
||||
pid="$(jq -r '.meta.pid // empty' "$VM_JSON")"
|
||||
vm_dir="$VM_DIR"
|
||||
|
||||
if [[ -z "${name:-}" || -z "${created_at:-}" || -z "${guest_ip:-}" ]]; then
|
||||
log "missing name or created_at in info file"
|
||||
if [[ -z "$name" || -z "$created_at" || -z "$guest_ip" ]]; then
|
||||
log "missing name or created_at in vm.json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue