Files
ergosteurandClaude Opus 5 3c7a8cff27 Retire the OVA/OVF importers to unused/
Proxmox imports OVA/OVF natively now through a storage with the "import"
content type, or qm importovf. The built-in path also handles per-storage disk
format conversion, which neither script did.

Recorded the known bugs in each header rather than fixing them, since nothing
will run these again: import-ova-pve.py imports every VMDK but only attaches
disk-0, leaving multi-disk appliances partly orphaned, and assumes a
vm-<vmid>-disk-N name that not every storage type uses;
import-ova-to-proxmox.sh passes --format qcow2 unconditionally, which fails on
ZFS and LVM-thin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 16:58:40 +00:00

172 lines
5.4 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
#
# RETIRED. Kept for reference only.
#
# Proxmox imports OVA/OVF natively now, via a storage with the "import" content
# type: upload the .ova there and use Create VM > Import, or `qm importovf`.
# The built-in path handles disk format conversion per storage type, which this
# script did not.
#
# Known bug, left unfixed since the script is retired: importdisk is called with
# --format qcow2 unconditionally, which fails on ZFS and LVM-thin storages that
# do not take a format argument.
set -euo pipefail
# === OVF/OVA Importer for Proxmox ===
# Supports: --force, OVF parsing, accurate storage check, multi-disk VMDK, CPU/RAM
FORCE=0
POSITIONAL=()
# === Parse args manually to allow --force anywhere ===
for arg in "$@"; do
case "$arg" in
--force)
FORCE=1
;;
*)
POSITIONAL+=("$arg")
;;
esac
done
set -- "${POSITIONAL[@]}"
if [[ $# -lt 3 ]]; then
echo "Usage: $0 <ova|ovf-file> <vmid> <storage> [vm-name] [--force]"
exit 1
fi
INPUT="$1"
VMID="$2"
STORAGE="$3"
VMNAME="${4:-}"
# === Auto-suggest VM name from filename if not given ===
if [[ -z "$VMNAME" || "$VMNAME" == "--force" ]]; then
BASENAME=$(basename "$INPUT")
BASENAME="${BASENAME%.*}"
VMNAME=$(echo "$BASENAME" | sed -E 's/[^a-zA-Z0-9]+/-/g' | sed -E 's/^-+|-+$//g' | tr '[:upper:]' '[:lower:]')
echo "==> No VM name provided."
echo "==> Suggested VM Name: $VMNAME"
echo "==> Continuing in 5 seconds... (Ctrl-C to cancel)"
sleep 5
fi
# === Validate VM ID ===
if qm status "$VMID" &>/dev/null; then
echo "❌ ERROR: VM ID $VMID already exists"
exit 1
else
echo "✅ VM ID $VMID is available"
fi
# === Validate storage ===
if ! pvesm status | awk '{print $1}' | grep -qx "$STORAGE"; then
echo "❌ ERROR: Storage '$STORAGE' not found!"
echo "👉 Available storages:"
pvesm status | awk 'NR>1 {print $1}'
exit 1
else
echo "✅ Storage '$STORAGE' exists"
fi
# === Get free space in bytes from pvesm (field 6 × 1024) ===
STORAGE_FREE_KB=$(pvesm status | awk -v storage="$STORAGE" '$1 == storage {print $6}')
if [[ -z "$STORAGE_FREE_KB" || "$STORAGE_FREE_KB" -eq 0 ]]; then
echo "❌ ERROR: Could not determine available space for '$STORAGE'"
exit 1
fi
STORAGE_FREE=$((STORAGE_FREE_KB * 1024))
# === Temp workspace ===
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
echo "==> Preparing to import VM $VMID from $INPUT (name: $VMNAME)"
# === Extract OVA or OVF ===
if [[ "$INPUT" == *.ova ]]; then
echo "==> Detected OVA bundle. Extracting..."
tar -xvf "$INPUT" -C "$TMPDIR"
elif [[ "$INPUT" == *.ovf ]]; then
echo "==> Detected OVF file. Copying OVF and VMDKs..."
cp "$INPUT" "$TMPDIR"
for vmdk in "${INPUT%.ovf}"*.vmdk; do cp "$vmdk" "$TMPDIR"; done
else
echo "❌ ERROR: Input must be .ova or .ovf"
exit 1
fi
cd "$TMPDIR"
OVF=$(find . -name "*.ovf" | head -n 1)
[[ -f "$OVF" ]] || { echo "❌ ERROR: OVF file not found!"; exit 1; }
# === Get total declared capacity from OVF ===
REQUIRED_CAPACITY=$(grep -oP 'ovf:capacity="\K[0-9]+' "$OVF" | awk '{sum+=$1} END{print sum}')
REQUIRED_FMT=$(numfmt --to=iec "$REQUIRED_CAPACITY" 2>/dev/null || echo "$REQUIRED_CAPACITY B")
STORAGE_FMT=$(numfmt --to=iec "$STORAGE_FREE" 2>/dev/null || echo "$STORAGE_FREE B")
echo "🧮 Storage free space: $STORAGE_FMT"
echo "📦 Total VMDK declared size: $REQUIRED_FMT"
if [[ "$FORCE" -eq 1 ]]; then
echo "⚠️ --force enabled, skipping free space check"
elif [[ -z "$REQUIRED_CAPACITY" || -z "$STORAGE_FREE" ]]; then
echo "❌ ERROR: Capacity values missing"
exit 1
elif ! [[ "$REQUIRED_CAPACITY" =~ ^[0-9]+$ && "$STORAGE_FREE" =~ ^[0-9]+$ ]]; then
echo "❌ ERROR: Capacity values not integers"
exit 1
elif (( STORAGE_FREE < REQUIRED_CAPACITY )); then
echo "❌ Not enough free space on storage '$STORAGE'"
exit 1
else
echo "✅ Storage has enough capacity"
fi
# === Parse RAM and CPU ===
OVF_RAM=$(xmllint --xpath 'string(//*[local-name()="Item"][.//*[local-name()="ResourceType"]="4"]/*[local-name()="VirtualQuantity"])' "$OVF" 2>/dev/null || echo "2048")
OVF_CPU=$(xmllint --xpath 'string(//*[local-name()="Item"][.//*[local-name()="ResourceType"]="3"]/*[local-name()="VirtualQuantity"])' "$OVF" 2>/dev/null || echo "2")
[[ "$OVF_RAM" =~ ^[0-9]+$ ]] || OVF_RAM=2048
[[ "$OVF_CPU" =~ ^[0-9]+$ ]] || OVF_CPU=2
echo "🧠 Parsed RAM from OVF: ${OVF_RAM} MB"
echo "🧮 Parsed CPU count from OVF: $OVF_CPU"
# === Create VM ===
echo "==> Creating VM $VMID..."
qm create "$VMID" --name "$VMNAME" --memory "$OVF_RAM" --cores "$OVF_CPU" --net0 virtio,bridge=vmbr0 --ostype l26
# === Parse VMDK files reliably ===
mapfile -t VMDK_FILES < <(xmllint --xpath "//*[local-name()='File']/@*" "$OVF" 2>/dev/null \
| grep -oE 'href="[^"]+\.vmdk"' | sed -E 's/^href="|"$//g')
if [[ ${#VMDK_FILES[@]} -eq 0 ]]; then
echo "❌ ERROR: No VMDK files found in OVF"
exit 1
fi
echo "📄 VMDK files to import:"
for f in "${VMDK_FILES[@]}"; do echo " - $f"; done
i=0
for VMDK in "${VMDK_FILES[@]}"; do
VMDK_FILE=$(basename "$VMDK")
[[ -f "$VMDK_FILE" ]] || { echo "❌ Missing VMDK file: $VMDK_FILE"; exit 1; }
echo "💾 Importing $VMDK_FILE to $STORAGE..."
qm importdisk "$VMID" "$VMDK_FILE" "$STORAGE" --format qcow2
echo "🔗 Attaching disk as scsi$i..."
qm set "$VMID" --scsi$i "$STORAGE:vm-$VMID-disk-$i"
((i++))
done
# === Finalize configuration ===
qm set "$VMID" --scsihw virtio-scsi-pci --boot c --bootdisk scsi0
qm set "$VMID" --vga std
echo "✅ VM $VMID ('$VMNAME') successfully imported and ready to start!"