Improve VM lifecycle tooling
Make spawned VMs easier to use and restore from the host. Add shared DNS and runtime helpers, publish <vm-name>.vm records through mapdns, and teach run/customize/interactive/restore to persist the metadata needed for SSH, DNS cleanup, and clean restores. Seed per-VM /home and /var disks from the rootfs snapshot so package state is present on first boot, add an interactive customization entrypoint plus ssh.sh and human-friendly list output, and let stop/kill/rm operate on multiple VM identifiers. Tear down stale TAP, dm, and loop state when VMs stop so restore can recreate them safely, and validate the updated scripts with bash -n plus targeted dry-run harnesses for teardown and restore paths.
This commit is contained in:
parent
a8078f2393
commit
9191b7e370
11 changed files with 966 additions and 144 deletions
54
run.sh
54
run.sh
|
|
@ -25,6 +25,7 @@ EOF
|
|||
log "starting"
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$DIR/dns.sh"
|
||||
STATE="$DIR/state"
|
||||
VM_ROOT="$STATE/vms"
|
||||
mkdir -p "$VM_ROOT"
|
||||
|
|
@ -254,6 +255,41 @@ BASE_LOOP=""
|
|||
COW_LOOP=""
|
||||
DM_NAME="fc-rootfs-$VM_TAG"
|
||||
DM_DEV=""
|
||||
SEED_ROOT_MNT="$VM_DIR/mnt-root"
|
||||
SEED_HOME_MNT="$VM_DIR/mnt-home"
|
||||
SEED_VAR_MNT="$VM_DIR/mnt-var"
|
||||
DNS_NAME=""
|
||||
|
||||
unmount_if_needed() {
|
||||
local mount_path="$1"
|
||||
[[ -n "$mount_path" ]] || return 0
|
||||
sudo umount "$mount_path" 2>/dev/null || true
|
||||
}
|
||||
|
||||
seed_volume_from_rootfs() {
|
||||
local source_dir="$1"
|
||||
local target_image="$2"
|
||||
local target_mount="$3"
|
||||
local label="$4"
|
||||
|
||||
if [[ ! -d "$source_dir" ]]; then
|
||||
log "source directory missing in rootfs snapshot: $label"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$target_mount"
|
||||
sudo mount "$target_image" "$target_mount"
|
||||
sudo cp -a "$source_dir/." "$target_mount/"
|
||||
sudo umount "$target_mount"
|
||||
}
|
||||
|
||||
populate_data_disks() {
|
||||
mkdir -p "$SEED_ROOT_MNT" "$SEED_HOME_MNT" "$SEED_VAR_MNT"
|
||||
sudo mount -o ro "$DM_DEV" "$SEED_ROOT_MNT"
|
||||
seed_volume_from_rootfs "$SEED_ROOT_MNT/home" "$HOME_PATH" "$SEED_HOME_MNT" "/home"
|
||||
seed_volume_from_rootfs "$SEED_ROOT_MNT/var" "$VAR_PATH" "$SEED_VAR_MNT" "/var"
|
||||
sudo umount "$SEED_ROOT_MNT"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
local exit_code=$?
|
||||
|
|
@ -268,6 +304,10 @@ cleanup() {
|
|||
sudo ip link del "$TAP_DEV" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "${API_SOCK:-}"
|
||||
unmount_if_needed "${SEED_VAR_MNT:-}"
|
||||
unmount_if_needed "${SEED_HOME_MNT:-}"
|
||||
unmount_if_needed "${SEED_ROOT_MNT:-}"
|
||||
banger_dns_remove_record_name "${DNS_NAME:-}"
|
||||
if [[ -n "${DM_NAME:-}" ]]; then
|
||||
sudo dmsetup remove "$DM_NAME" 2>/dev/null || true
|
||||
fi
|
||||
|
|
@ -328,6 +368,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 mount >/dev/null 2>&1 || ! command -v umount >/dev/null 2>&1; then
|
||||
log "mount and umount are required to populate home/var disks"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
log "jq is required to persist VM metadata"
|
||||
exit 1
|
||||
|
|
@ -368,6 +412,9 @@ sudo e2cp "$HOSTS_TMP" "$DM_DEV:/etc/hosts" >/dev/null 2>&1 || {
|
|||
exit 1
|
||||
}
|
||||
|
||||
log "populating /home and /var disks from rootfs snapshot"
|
||||
populate_data_disks
|
||||
|
||||
# Host bridge
|
||||
if ! ip link show "$BR_DEV" >/dev/null 2>&1; then
|
||||
log "creating host bridge $BR_DEV ($BR_IP/$CIDR)"
|
||||
|
|
@ -494,9 +541,10 @@ log "starting virtual machine"
|
|||
"${CURL_CMD[@]}" --unix-socket "$API_SOCK" -X PUT http://localhost/actions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "action_type": "InstanceStart" }' >/dev/null
|
||||
VM_STARTED=1
|
||||
VM_CONFIG_JSON="$("${CURL_CMD[@]}" --unix-socket "$API_SOCK" -sS http://localhost/vm/config)"
|
||||
CREATED_AT="$(date -Iseconds)"
|
||||
DNS_NAME="$(banger_dns_name "$VM_NAME")"
|
||||
banger_dns_write_record "$VM_NAME" "$GUEST_IP"
|
||||
jq -n \
|
||||
--arg id "$VM_ID" \
|
||||
--arg name "$VM_NAME" \
|
||||
|
|
@ -515,9 +563,11 @@ jq -n \
|
|||
--arg cow_loop "$COW_LOOP" \
|
||||
--arg dm_name "$DM_NAME" \
|
||||
--arg dm_dev "$DM_DEV" \
|
||||
--arg dns_name "$DNS_NAME" \
|
||||
--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}' \
|
||||
'{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,dns_name:$dns_name},config:$config}' \
|
||||
> "$VM_DIR/vm.json"
|
||||
VM_STARTED=1
|
||||
|
||||
log "vm started successfully"
|
||||
log "guest ip: $GUEST_IP"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue