aboutsummaryrefslogtreecommitdiff
path: root/kvm-lab-create
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukasz.kasprzak@pm.me>2026-01-29 01:27:17 +0100
committerLukasz Kasprzak <lukasz.kasprzak@pm.me>2026-01-29 01:27:17 +0100
commit70e6d80c2d367480478eacf46363ee88a0e994ff (patch)
tree3585660271002a776a8756994c92f8e546f98044 /kvm-lab-create
parent646bb0d8df0e720c966dc2992dcf7fe1154d17cf (diff)
downloadbin-70e6d80c2d367480478eacf46363ee88a0e994ff.tar.gz
bin-70e6d80c2d367480478eacf46363ee88a0e994ff.zip
added lots of new dmenu scripts
Diffstat (limited to 'kvm-lab-create')
-rwxr-xr-xkvm-lab-create148
1 files changed, 71 insertions, 77 deletions
diff --git a/kvm-lab-create b/kvm-lab-create
index 43d2926..e574a4c 100755
--- a/kvm-lab-create
+++ b/kvm-lab-create
@@ -11,14 +11,20 @@ TEMPLATE_DOMAIN="debian13-template"
TEMPLATE_BASE="$BASEDIR/debian13-template.qcow2"
usage() {
- echo "Usage: kvm-lab-create <lab-name> [--base template|work01|/abs/path.qcow2] [--start]"
- echo "Examples:"
- echo " kvm-lab-create lab10 --base template --start"
- echo " kvm-lab-create lab11 --base work01 --start"
- echo " kvm-lab-create lab12 --base /var/lib/libvirt/images/base/custom.qcow2 --start"
+ cat <<'EOF'
+Usage: kvm-lab-create <lab-name> [--base template|<prefix>|<domain>|<filename-in-base>|/abs/path.qcow2] [--start]
+
+Base resolution order:
+ 1) template -> /var/lib/libvirt/images/base/debian13-template.qcow2
+ 2) /abs/path.qcow2 -> that file
+ 3) <filename in /var/lib/libvirt/images/base> -> /var/lib/libvirt/images/base/<filename>
+ 4) <domain name> -> vda disk path of that domain (virsh domblklist)
+ 5) <prefix> -> newest /var/lib/libvirt/images/base/<prefix>-base-*.qcow2
+EOF
}
-# --- Parse args ---
+die() { echo "ERROR: $*" >&2; exit 1; }
+
NAME="${1:-}"
shift || true
@@ -27,97 +33,76 @@ START="no"
while [[ $# -gt 0 ]]; do
case "$1" in
- --base)
- BASESEL="${2:-}"
- shift 2
- ;;
- --start)
- START="yes"
- shift
- ;;
- -h|--help)
- usage
- exit 0
- ;;
- *)
- echo "ERROR: Unknown argument: $1"
- usage
- exit 1
- ;;
+ --base) BASESEL="${2:-}"; shift 2 ;;
+ --start) START="yes"; shift ;;
+ -h|--help) usage; exit 0 ;;
+ *) die "Unknown argument: $1" ;;
esac
done
-if [[ -z "$NAME" ]]; then
- usage
- exit 1
-fi
+[[ -n "$NAME" ]] || { usage; exit 1; }
DISK="${LABDIR}/${NAME}.qcow2"
XML_TMP="$(mktemp "/tmp/${NAME}.xml.XXXXXX")"
-cleanup() { rm -f "$XML_TMP"; }
+
+defined="no"
+cleanup() {
+ rm -f "$XML_TMP"
+ if [[ "$defined" != "yes" && -e "$DISK" ]]; then
+ echo "WARN: Removing orphan disk: $DISK"
+ sudo rm -f "$DISK" || true
+ fi
+}
trap cleanup EXIT
-# --- Resolve base selection ---
resolve_base() {
local sel="$1"
+
if [[ "$sel" == "template" ]]; then
- echo "$TEMPLATE_BASE"
- return 0
+ echo "$TEMPLATE_BASE"; return 0
fi
- if [[ "$sel" == "work01" ]]; then
- # Pick newest promoted work01 base (must exist first)
- local latest
- latest="$(ls -1t "$BASEDIR"/work01-base-*.qcow2 2>/dev/null | head -n 1 || true)"
- if [[ -z "$latest" ]]; then
- echo "ERROR: No work01 base found in $BASEDIR (expected work01-base-*.qcow2)."
- echo "Action: run your promote script to create one."
- return 1
- fi
- echo "$latest"
- return 0
+ if [[ "$sel" == /* ]]; then
+ echo "$sel"; return 0
fi
- # Absolute path base
- if [[ "$sel" == /* ]]; then
- echo "$sel"
- return 0
+ if [[ -f "$BASEDIR/$sel" ]]; then
+ echo "$BASEDIR/$sel"; return 0
fi
- echo "ERROR: Invalid --base '$sel' (use template|work01|/abs/path.qcow2)"
- return 1
-}
+ if virsh -c "$URI" dominfo "$sel" &>/dev/null; then
+ local src
+ src="$(virsh -c "$URI" domblklist "$sel" --details \
+ | awk '$2=="disk" && $3=="vda" {print $4; exit}')"
+ [[ -n "$src" ]] || die "Domain '$sel' found but could not resolve vda disk path."
+ echo "$src"; return 0
+ fi
-BASE="$(resolve_base "$BASESEL")"
+ local latest
+ latest="$(ls -1t "$BASEDIR"/"${sel}"-base-*.qcow2 2>/dev/null | head -n 1 || true)"
+ [[ -n "$latest" ]] || die "Invalid --base '$sel' (no matching domain/file/promoted base in $BASEDIR)."
+ echo "$latest"
+}
-# --- Preconditions (read-only checks) ---
-if [[ ! -d "$LABDIR" ]]; then
- echo "ERROR: Lab directory missing: $LABDIR"
- exit 1
+# Preconditions
+[[ -d "$LABDIR" ]] || die "Lab directory missing: $LABDIR"
+[[ -d "$BASEDIR" ]] || die "Base directory missing: $BASEDIR"
+if ! virsh -c "$URI" dominfo "$TEMPLATE_DOMAIN" &>/dev/null; then
+ die "Template domain not found in $URI: $TEMPLATE_DOMAIN"
fi
-if ! sudo test -r "$BASE"; then
- echo "ERROR: Base image not readable (via sudo): $BASE"
- exit 1
-fi
+BASE="$(resolve_base "$BASESEL")"
+echo "Base selection: $BASESEL -> $BASE"
-if [[ -e "$DISK" ]]; then
- echo "ERROR: Disk already exists: $DISK"
- exit 1
-fi
+[[ -f "$BASE" ]] || die "Base image not found: $BASE"
+sudo test -r "$BASE" || die "Base image not readable (via sudo): $BASE"
+[[ ! -e "$DISK" ]] || die "Disk already exists: $DISK"
if virsh -c "$URI" dominfo "$NAME" &>/dev/null; then
- echo "ERROR: Domain already exists: $NAME"
- exit 1
+ die "Domain already exists: $NAME"
fi
-if ! virsh -c "$URI" dominfo "$TEMPLATE_DOMAIN" &>/dev/null; then
- echo "ERROR: Template domain not found in $URI: $TEMPLATE_DOMAIN"
- exit 1
-fi
-
-# --- Create overlay disk ---
-echo "Using base: $BASE"
+# Create overlay
echo "Creating overlay disk: $DISK"
sudo qemu-img create -f qcow2 -F qcow2 -b "$BASE" "$DISK" >/dev/null
sudo chown libvirt-qemu:libvirt "$DISK"
@@ -126,28 +111,37 @@ sudo chmod 0660 "$DISK"
echo "Validating backing file..."
sudo qemu-img info "$DISK" | grep -E "file format:|virtual size:|backing file:" || true
-# --- Clone domain XML ---
+# Clone template XML and adjust
echo "Cloning domain XML from: $TEMPLATE_DOMAIN"
virsh -c "$URI" dumpxml "$TEMPLATE_DOMAIN" > "$XML_TMP"
-# Set name, disk path, and force NAT-only network "default"
+# Update: name, disk source, network default
+# - replace <name>...</name> once
+# - replace template disk path with overlay path (global)
+# - force network='default'
sed -i \
-e "s|<name>${TEMPLATE_DOMAIN}</name>|<name>${NAME}</name>|" \
-e "s|${TEMPLATE_BASE}|${DISK}|g" \
-e "s|<source network='[^']*'|<source network='default'|g" \
"$XML_TMP"
-# Remove UUID to avoid collision with template
+# Remove UUID and MAC addresses to avoid collisions
sed -i -E '/<uuid>[^<]*<\/uuid>/d' "$XML_TMP"
+sed -i -E "s/<mac address='[^']*'\/>//g" "$XML_TMP"
-# Remove any host filesystem passthrough (if present)
+# Remove filesystem passthrough (if any)
perl -0777 -i -pe 's/<filesystem[\s\S]*?<\/filesystem>\n?//g' "$XML_TMP"
-# --- Define domain ---
+# Validate substitutions happened
+grep -q "<name>${NAME}</name>" "$XML_TMP" || die "XML name substitution failed"
+grep -qF "$DISK" "$XML_TMP" || die "XML disk substitution failed"
+
+# Define domain
echo "Defining domain: $NAME"
virsh -c "$URI" define "$XML_TMP" >/dev/null
+defined="yes"
-# --- Optional start ---
+# Optional start
if [[ "$START" == "yes" ]]; then
echo "Starting domain: $NAME"
virsh -c "$URI" start "$NAME" >/dev/null