feat(vm): add smart recovery to legacy VM scripts (Phase 4-6)

Phase 4: haos-vm.sh
- Inline recovery in error_handler with RETRY_DOWNLOAD option (cache clearing)
- VM creation wrapped in create_vm() for bounded recursive retry
- Supports max 2 retries with whiptail recovery menu

Phase 5: debian-13-vm.sh, ubuntu2204/2404/2504-vm.sh
- Inline recovery with RETRY/KEEP/ABORT options
- debian-13-vm.sh: SKIP_CUSTOMIZE option to bypass virt-customize failures
- All scripts wrapped in create_vm() with phase flag pattern

Phase 6: openwrt-vm.sh
- Limited recovery for download/import phase
- Preserves set -Eeo pipefail, disables pipefail during recovery menu
- sendkey/network config phase included in create_vm but not specifically retryable

Architecture: Inline recovery pattern (no vm-core.func dependency)
- Consistent with docker-vm.sh's recursive retry via error_handler
- Bounded by VM_MAX_RETRIES=2
- ERR trap disabled during recovery menu, restored before retry
This commit is contained in:
CanbiZ (MickLesk)
2026-02-16 19:02:50 +01:00
parent 6b249d9533
commit c58a13a230
6 changed files with 342 additions and 8 deletions

View File

@@ -65,13 +65,62 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
# Smart recovery state
VM_CREATION_PHASE="no"
VM_RECOVERY_ATTEMPT=0
VM_MAX_RETRIES=2
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
post_update_to_api "failed" "${exit_code}"
echo -e "\n$error_message\n"
# During VM creation phase: offer recovery menu instead of immediate cleanup
if [[ "$VM_CREATION_PHASE" == "yes" && $VM_RECOVERY_ATTEMPT -lt $VM_MAX_RETRIES ]]; then
((VM_RECOVERY_ATTEMPT++))
trap - ERR
set +e
local choice
choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "VM CREATION FAILED" \
--radiolist "Exit code: ${exit_code} | Attempt: ${VM_RECOVERY_ATTEMPT}/${VM_MAX_RETRIES}\nFailed command: ${command}\n\nChoose a recovery action:" 16 72 4 \
"RETRY" "Retry VM creation" "ON" \
"SKIP_CUSTOMIZE" "Retry and skip image customization" "OFF" \
"KEEP" "Keep partial VM for debugging" "OFF" \
"ABORT" "Destroy VM and exit" "OFF" \
3>&1 1>&2 2>&3) || choice="ABORT"
case "$choice" in
RETRY | SKIP_CUSTOMIZE)
msg_info "Cleaning up failed VM ${VMID} for retry"
cleanup_vmid 2>/dev/null || true
[[ "$choice" == "SKIP_CUSTOMIZE" ]] && export SKIP_VIRT_CUSTOMIZE="yes"
msg_ok "Ready for retry (attempt $((VM_RECOVERY_ATTEMPT + 1))/${VM_MAX_RETRIES})"
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
create_vm
exit $?
;;
KEEP)
echo -e "\n${YW} Keeping partial VM ${VMID} for debugging${CL}"
echo -e " Inspect: qm config ${VMID}"
echo -e " Remove: qm destroy ${VMID} --destroy-unreferenced-disks --purge\n"
post_update_to_api "failed" "$exit_code"
exit "$exit_code"
;;
*)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
exit "$exit_code"
;;
esac
fi
# Default: no recovery (max retries exceeded or outside creation phase)
post_update_to_api "failed" "${exit_code}"
cleanup_vmid
}
@@ -485,6 +534,7 @@ fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
create_vm() {
# ==============================================================================
# PREREQUISITES
# ==============================================================================
@@ -511,11 +561,12 @@ msg_ok "Downloaded ${CL}${BL}${FILE}${CL}"
# ==============================================================================
# IMAGE CUSTOMIZATION
# ==============================================================================
msg_info "Customizing ${FILE} image"
WORK_FILE=$(mktemp --suffix=.qcow2)
cp "$FILE" "$WORK_FILE"
if [[ "${SKIP_VIRT_CUSTOMIZE:-}" != "yes" ]]; then
msg_info "Customizing ${FILE} image"
# Set hostname
virt-customize -q -a "$WORK_FILE" --hostname "${HN}" >/dev/null 2>&1
@@ -551,6 +602,9 @@ EOF' >/dev/null 2>&1 || true
fi
msg_ok "Customized image"
else
msg_ok "Skipped image customization (will configure on first boot)"
fi
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
case $STORAGE_TYPE in
@@ -650,3 +704,8 @@ fi
msg_ok "Completed successfully!\n"
echo "More Info at https://github.com/community-scripts/ProxmoxVE/discussions/836"
} # end create_vm
VM_CREATION_PHASE="yes"
create_vm
VM_CREATION_PHASE="no"

View File

@@ -69,13 +69,65 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
# Smart recovery state
VM_CREATION_PHASE="no"
VM_RECOVERY_ATTEMPT=0
VM_MAX_RETRIES=2
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
post_update_to_api "failed" "${exit_code}"
echo -e "\n$error_message\n"
# During VM creation phase: offer recovery menu instead of immediate cleanup
if [[ "$VM_CREATION_PHASE" == "yes" && $VM_RECOVERY_ATTEMPT -lt $VM_MAX_RETRIES ]]; then
((VM_RECOVERY_ATTEMPT++))
trap - ERR
set +e
local choice
choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "VM CREATION FAILED" \
--radiolist "Exit code: ${exit_code} | Attempt: ${VM_RECOVERY_ATTEMPT}/${VM_MAX_RETRIES}\nFailed command: ${command}\n\nChoose a recovery action:" 16 72 4 \
"RETRY" "Retry VM creation" "ON" \
"RETRY_DOWNLOAD" "Retry with fresh download (clear cache)" "OFF" \
"KEEP" "Keep partial VM for debugging" "OFF" \
"ABORT" "Destroy VM and exit" "OFF" \
3>&1 1>&2 2>&3) || choice="ABORT"
case "$choice" in
RETRY | RETRY_DOWNLOAD)
msg_info "Cleaning up failed VM ${VMID} for retry"
cleanup_vmid 2>/dev/null || true
if [[ "$choice" == "RETRY_DOWNLOAD" && -n "${CACHE_FILE:-}" ]]; then
rm -f "$CACHE_FILE"
msg_ok "Cleared cached image"
fi
msg_ok "Ready for retry (attempt $((VM_RECOVERY_ATTEMPT + 1))/${VM_MAX_RETRIES})"
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
create_vm
exit $?
;;
KEEP)
echo -e "\n${YW} Keeping partial VM ${VMID} for debugging${CL}"
echo -e " Inspect: qm config ${VMID}"
echo -e " Remove: qm destroy ${VMID} --destroy-unreferenced-disks --purge\n"
post_update_to_api "failed" "$exit_code"
exit "$exit_code"
;;
*)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
exit "$exit_code"
;;
esac
fi
# Default: no recovery (max retries exceeded or outside creation phase)
post_update_to_api "failed" "${exit_code}"
cleanup_vmid
}
@@ -554,6 +606,7 @@ fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
create_vm() {
var_version="${BRANCH}"
msg_info "Retrieving the URL for Home Assistant ${BRANCH} Disk Image"
if [ "$BRANCH" == "$dev" ]; then
@@ -658,3 +711,8 @@ if [ "$START_VM" == "yes" ]; then
fi
post_update_to_api "done" "none"
msg_ok "Completed successfully!\n"
} # end create_vm
VM_CREATION_PHASE="yes"
create_vm
VM_CREATION_PHASE="no"

View File

@@ -70,13 +70,61 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
# Smart recovery state
VM_CREATION_PHASE="no"
VM_RECOVERY_ATTEMPT=0
VM_MAX_RETRIES=2
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
post_update_to_api "failed" "$exit_code"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
echo -e "\n$error_message\n"
# During VM creation phase: offer recovery menu instead of immediate cleanup
if [[ "$VM_CREATION_PHASE" == "yes" && $VM_RECOVERY_ATTEMPT -lt $VM_MAX_RETRIES ]]; then
((VM_RECOVERY_ATTEMPT++))
trap - ERR
set +e
set +o pipefail
local choice
choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "VM CREATION FAILED" \
--radiolist "Exit code: ${exit_code} | Attempt: ${VM_RECOVERY_ATTEMPT}/${VM_MAX_RETRIES}\nFailed command: ${command}\n\nChoose a recovery action:" 16 72 3 \
"RETRY" "Retry VM creation" "ON" \
"KEEP" "Keep partial VM for debugging" "OFF" \
"ABORT" "Destroy VM and exit" "OFF" \
3>&1 1>&2 2>&3) || choice="ABORT"
case "$choice" in
RETRY)
msg_info "Cleaning up failed VM ${VMID} for retry"
cleanup_vmid 2>/dev/null || true
msg_ok "Ready for retry (attempt $((VM_RECOVERY_ATTEMPT + 1))/${VM_MAX_RETRIES})"
set -Eeo pipefail
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
create_vm
exit $?
;;
KEEP)
echo -e "\n${YW} Keeping partial VM ${VMID} for debugging${CL}"
echo -e " Inspect: qm config ${VMID}"
echo -e " Remove: qm destroy ${VMID} --destroy-unreferenced-disks --purge\n"
post_update_to_api "failed" "$exit_code"
exit "$exit_code"
;;
*)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
exit "$exit_code"
;;
esac
fi
# Default: no recovery (max retries exceeded or outside creation phase)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
}
@@ -520,6 +568,8 @@ else
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
create_vm() {
msg_info "Getting URL for OpenWrt Disk Image"
response=$(curl -fsSL https://openwrt.org)
@@ -661,3 +711,8 @@ if [ -z "$VLAN" ] && [ "$VLAN2" != "999" ]; then
fi
post_update_to_api "done" "none"
msg_ok "Completed Successfully!${VLAN_FINISH:+\n$VLAN_FINISH}"
} # end create_vm
VM_CREATION_PHASE="yes"
create_vm
VM_CREATION_PHASE="no"

View File

@@ -62,13 +62,60 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
# Smart recovery state
VM_CREATION_PHASE="no"
VM_RECOVERY_ATTEMPT=0
VM_MAX_RETRIES=2
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
post_update_to_api "failed" "$exit_code"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
echo -e "\n$error_message\n"
# During VM creation phase: offer recovery menu instead of immediate cleanup
if [[ "$VM_CREATION_PHASE" == "yes" && $VM_RECOVERY_ATTEMPT -lt $VM_MAX_RETRIES ]]; then
((VM_RECOVERY_ATTEMPT++))
trap - ERR
set +e
local choice
choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "VM CREATION FAILED" \
--radiolist "Exit code: ${exit_code} | Attempt: ${VM_RECOVERY_ATTEMPT}/${VM_MAX_RETRIES}\nFailed command: ${command}\n\nChoose a recovery action:" 16 72 3 \
"RETRY" "Retry VM creation" "ON" \
"KEEP" "Keep partial VM for debugging" "OFF" \
"ABORT" "Destroy VM and exit" "OFF" \
3>&1 1>&2 2>&3) || choice="ABORT"
case "$choice" in
RETRY)
msg_info "Cleaning up failed VM ${VMID} for retry"
cleanup_vmid 2>/dev/null || true
msg_ok "Ready for retry (attempt $((VM_RECOVERY_ATTEMPT + 1))/${VM_MAX_RETRIES})"
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
create_vm
exit $?
;;
KEEP)
echo -e "\n${YW} Keeping partial VM ${VMID} for debugging${CL}"
echo -e " Inspect: qm config ${VMID}"
echo -e " Remove: qm destroy ${VMID} --destroy-unreferenced-disks --purge\n"
post_update_to_api "failed" "$exit_code"
exit "$exit_code"
;;
*)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
exit "$exit_code"
;;
esac
fi
# Default: no recovery (max retries exceeded or outside creation phase)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
}
@@ -466,6 +513,8 @@ else
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
create_vm() {
msg_info "Retrieving the URL for the Ubuntu 22.04 Disk Image"
URL=https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
sleep 2
@@ -562,3 +611,8 @@ post_update_to_api "done" "none"
msg_ok "Completed successfully!\n"
echo -e "Setup Cloud-Init before starting \n
More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n"
} # end create_vm
VM_CREATION_PHASE="yes"
create_vm
VM_CREATION_PHASE="no"

View File

@@ -65,13 +65,60 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
# Smart recovery state
VM_CREATION_PHASE="no"
VM_RECOVERY_ATTEMPT=0
VM_MAX_RETRIES=2
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
post_update_to_api "failed" "$exit_code"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
echo -e "\n$error_message\n"
# During VM creation phase: offer recovery menu instead of immediate cleanup
if [[ "$VM_CREATION_PHASE" == "yes" && $VM_RECOVERY_ATTEMPT -lt $VM_MAX_RETRIES ]]; then
((VM_RECOVERY_ATTEMPT++))
trap - ERR
set +e
local choice
choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "VM CREATION FAILED" \
--radiolist "Exit code: ${exit_code} | Attempt: ${VM_RECOVERY_ATTEMPT}/${VM_MAX_RETRIES}\nFailed command: ${command}\n\nChoose a recovery action:" 16 72 3 \
"RETRY" "Retry VM creation" "ON" \
"KEEP" "Keep partial VM for debugging" "OFF" \
"ABORT" "Destroy VM and exit" "OFF" \
3>&1 1>&2 2>&3) || choice="ABORT"
case "$choice" in
RETRY)
msg_info "Cleaning up failed VM ${VMID} for retry"
cleanup_vmid 2>/dev/null || true
msg_ok "Ready for retry (attempt $((VM_RECOVERY_ATTEMPT + 1))/${VM_MAX_RETRIES})"
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
create_vm
exit $?
;;
KEEP)
echo -e "\n${YW} Keeping partial VM ${VMID} for debugging${CL}"
echo -e " Inspect: qm config ${VMID}"
echo -e " Remove: qm destroy ${VMID} --destroy-unreferenced-disks --purge\n"
post_update_to_api "failed" "$exit_code"
exit "$exit_code"
;;
*)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
exit "$exit_code"
;;
esac
fi
# Default: no recovery (max retries exceeded or outside creation phase)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
}
@@ -468,6 +515,8 @@ else
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
create_vm() {
msg_info "Retrieving the URL for the Ubuntu 24.04 Disk Image"
URL=https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
sleep 2
@@ -564,3 +613,8 @@ post_update_to_api "done" "none"
msg_ok "Completed successfully!\n"
echo -e "Setup Cloud-Init before starting \n
More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n"
} # end create_vm
VM_CREATION_PHASE="yes"
create_vm
VM_CREATION_PHASE="no"

View File

@@ -64,13 +64,60 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT
trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM
# Smart recovery state
VM_CREATION_PHASE="no"
VM_RECOVERY_ATTEMPT=0
VM_MAX_RETRIES=2
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
post_update_to_api "failed" "$exit_code"
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
echo -e "\n$error_message\n"
# During VM creation phase: offer recovery menu instead of immediate cleanup
if [[ "$VM_CREATION_PHASE" == "yes" && $VM_RECOVERY_ATTEMPT -lt $VM_MAX_RETRIES ]]; then
((VM_RECOVERY_ATTEMPT++))
trap - ERR
set +e
local choice
choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "VM CREATION FAILED" \
--radiolist "Exit code: ${exit_code} | Attempt: ${VM_RECOVERY_ATTEMPT}/${VM_MAX_RETRIES}\nFailed command: ${command}\n\nChoose a recovery action:" 16 72 3 \
"RETRY" "Retry VM creation" "ON" \
"KEEP" "Keep partial VM for debugging" "OFF" \
"ABORT" "Destroy VM and exit" "OFF" \
3>&1 1>&2 2>&3) || choice="ABORT"
case "$choice" in
RETRY)
msg_info "Cleaning up failed VM ${VMID} for retry"
cleanup_vmid 2>/dev/null || true
msg_ok "Ready for retry (attempt $((VM_RECOVERY_ATTEMPT + 1))/${VM_MAX_RETRIES})"
set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
create_vm
exit $?
;;
KEEP)
echo -e "\n${YW} Keeping partial VM ${VMID} for debugging${CL}"
echo -e " Inspect: qm config ${VMID}"
echo -e " Remove: qm destroy ${VMID} --destroy-unreferenced-disks --purge\n"
post_update_to_api "failed" "$exit_code"
exit "$exit_code"
;;
*)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
exit "$exit_code"
;;
esac
fi
# Default: no recovery (max retries exceeded or outside creation phase)
post_update_to_api "failed" "$exit_code"
cleanup_vmid
}
@@ -467,6 +514,8 @@ else
fi
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
create_vm() {
msg_info "Retrieving the URL for the Ubuntu 25.04 Disk Image"
URL=https://cloud-images.ubuntu.com/plucky/current/plucky-server-cloudimg-amd64.img
sleep 2
@@ -563,3 +612,8 @@ post_update_to_api "done" "none"
msg_ok "Completed successfully!\n"
echo -e "Setup Cloud-Init before starting \n
More info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n"
} # end create_vm
VM_CREATION_PHASE="yes"
create_vm
VM_CREATION_PHASE="no"