[core]: unify misc/*.func scripts with centralized logic from core.func (#5316)

* Core Changes

* Formatting

* add create_lxc patches

* Update install.func

* remove dev header
This commit is contained in:
CanbiZ
2025-06-20 13:28:06 +02:00
committed by GitHub
parent 4e46fd739d
commit 1f7d85ac60
7 changed files with 404 additions and 700 deletions

View File

@ -1,52 +1,13 @@
# Copyright (c) 2021-2025 tteck # Copyright (c) 2021-2025 community-scripts ORG
# Author: tteck (tteckster) # Author: tteck (tteckster)
# Co-Author: MickLesk # Co-Author: MickLesk
# License: MIT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# This function sets color variables for formatting output in the terminal if ! command -v curl >/dev/null 2>&1; then
color() { apk update && apk add curl >/dev/null 2>&1
# Colors fi
YW=$(echo "\033[33m") source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
BL=$(echo "\033[36m") load_functions
RD=$(echo "\033[01;31m")
GN=$(echo "\033[1;92m")
# Formatting
CL=$(echo "\033[m")
BFR="\\r\\033[K"
BOLD=$(echo "\033[1m")
TAB=" "
TAB3=" "
# System
RETRY_NUM=10
RETRY_EVERY=3
i=$RETRY_NUM
# Icons
CM="${TAB}✔️${TAB}${CL}"
CROSS="${TAB}✖️${TAB}${CL}"
INFO="${TAB}💡${TAB}${CL}"
NETWORK="${TAB}📡${TAB}${CL}"
OS="${TAB}🖥️${TAB}${CL}"
HOSTNAME="${TAB}🏠${TAB}${CL}"
GATEWAY="${TAB}🌐${TAB}${CL}"
}
# Function to set STD mode based on verbosity
set_std_mode() {
if [ "$VERBOSE" = "yes" ]; then
STD=""
else
STD="silent"
fi
}
# Silent execution function
silent() {
"$@" >/dev/null 2>&1
}
# This function enables IPv6 if it's not disabled and sets verbose mode # This function enables IPv6 if it's not disabled and sets verbose mode
verb_ip6() { verb_ip6() {
@ -74,33 +35,15 @@ error_handler() {
echo -e "\n$error_message\n" echo -e "\n$error_message\n"
} }
# This function displays an informational message with a yellow color.
msg_info() {
local msg="$1"
echo -ne " ${TAB}${YW}${msg}"
}
# This function displays a success message with a green color.
msg_ok() {
local msg="$1"
echo -e "${BFR}${CM}${GN}${msg}${CL}"
}
# This function displays a error message with a red color.
msg_error() {
local msg="$1"
echo -e "${BFR}${CROSS}${RD}${msg}${CL}"
}
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
setting_up_container() { setting_up_container() {
msg_info "Setting up Container OS" msg_info "Setting up Container OS"
while [ "$i" -gt 0 ]; do while [ $i -gt 0 ]; do
if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then
break break
fi fi
echo 1>&2 -en "${CROSS}${RD} No Network! " echo 1>&2 -en "${CROSS}${RD} No Network! "
sleep "$RETRY_EVERY" sleep $RETRY_EVERY
i=$((i - 1)) i=$((i - 1))
done done
@ -149,10 +92,9 @@ update_os() {
# This function modifies the message of the day (motd) and SSH settings # This function modifies the message of the day (motd) and SSH settings
motd_ssh() { motd_ssh() {
# Set terminal to 256-color mode
echo "export TERM='xterm-256color'" >>/root/.bashrc echo "export TERM='xterm-256color'" >>/root/.bashrc
IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1)
# Get OS information
if [ -f "/etc/os-release" ]; then if [ -f "/etc/os-release" ]; then
OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"')
OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"')
@ -213,6 +155,6 @@ EOF
msg_ok "Customized Container" msg_ok "Customized Container"
fi fi
echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVE/raw/main/ct/${app}.sh)\"" >/usr/bin/update echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update
chmod +x /usr/bin/update chmod +x /usr/bin/update
} }

View File

@ -4,7 +4,7 @@
post_to_api() { post_to_api() {
if ! command -v curl &> /dev/null; then if ! command -v curl &>/dev/null; then
return return
fi fi
@ -20,7 +20,8 @@ post_to_api() {
local pve_version="not found" local pve_version="not found"
pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') pve_version=$(pveversion | awk -F'[/ ]' '{print $2}')
JSON_PAYLOAD=$(cat <<EOF JSON_PAYLOAD=$(
cat <<EOF
{ {
"ct_type": $CT_TYPE, "ct_type": $CT_TYPE,
"type":"lxc", "type":"lxc",
@ -37,12 +38,12 @@ post_to_api() {
"random_id": "$RANDOM_UUID" "random_id": "$RANDOM_UUID"
} }
EOF EOF
) )
if [[ "$DIAGNOSTICS" == "yes" ]]; then if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \ RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "$JSON_PAYLOAD") || true -d "$JSON_PAYLOAD") || true
fi fi
} }
@ -52,7 +53,7 @@ post_to_api_vm() {
return return
fi fi
DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics | awk -F'=' '{print $2}') DIAGNOSTICS=$(grep -i "^DIAGNOSTICS=" /usr/local/community-scripts/diagnostics | awk -F'=' '{print $2}')
if ! command -v curl &> /dev/null; then if ! command -v curl &>/dev/null; then
return return
fi fi
@ -70,7 +71,8 @@ post_to_api_vm() {
DISK_SIZE_API=${DISK_SIZE%G} DISK_SIZE_API=${DISK_SIZE%G}
JSON_PAYLOAD=$(cat <<EOF JSON_PAYLOAD=$(
cat <<EOF
{ {
"ct_type": 2, "ct_type": 2,
"type":"vm", "type":"vm",
@ -87,42 +89,43 @@ post_to_api_vm() {
"random_id": "$RANDOM_UUID" "random_id": "$RANDOM_UUID"
} }
EOF EOF
) )
if [[ "$DIAGNOSTICS" == "yes" ]]; then if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \ RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "$JSON_PAYLOAD") || true -d "$JSON_PAYLOAD") || true
fi fi
} }
POST_UPDATE_DONE=false POST_UPDATE_DONE=false
post_update_to_api() { post_update_to_api() {
if ! command -v curl &> /dev/null; then if ! command -v curl &>/dev/null; then
return return
fi fi
if [ "$POST_UPDATE_DONE" = true ]; then if [ "$POST_UPDATE_DONE" = true ]; then
return 0 return 0
fi fi
local API_URL="http://api.community-scripts.org/upload/updatestatus" local API_URL="http://api.community-scripts.org/upload/updatestatus"
local status="${1:-failed}" local status="${1:-failed}"
local error="${2:-No error message}" local error="${2:-No error message}"
JSON_PAYLOAD=$(cat <<EOF JSON_PAYLOAD=$(
cat <<EOF
{ {
"status": "$status", "status": "$status",
"error": "$error", "error": "$error",
"random_id": "$RANDOM_UUID" "random_id": "$RANDOM_UUID"
} }
EOF EOF
) )
if [[ "$DIAGNOSTICS" == "yes" ]]; then if [[ "$DIAGNOSTICS" == "yes" ]]; then
RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \ RESPONSE=$(curl -s -w "%{http_code}" -L -X POST "$API_URL" --post301 --post302 \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "$JSON_PAYLOAD") || true -d "$JSON_PAYLOAD") || true
fi fi
POST_UPDATE_DONE=true POST_UPDATE_DONE=true

View File

@ -12,56 +12,20 @@ variables() {
DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call.
METHOD="default" # sets the METHOD variable to "default", used for the API call. METHOD="default" # sets the METHOD variable to "default", used for the API call.
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable.
CT_TYPE=${var_unprivileged:-$CT_TYPE}
} }
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
# This function sets various color variables using ANSI escape codes for formatting text in the terminal. if command -v curl >/dev/null 2>&1; then
color() { source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
# Colors load_functions
YW=$(echo "\033[33m") #echo "(build.func) Loaded core.func via curl"
YWB=$(echo "\033[93m") elif command -v wget >/dev/null 2>&1; then
BL=$(echo "\033[36m") source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
RD=$(echo "\033[01;31m") load_functions
BGN=$(echo "\033[4;92m") #echo "(build.func) Loaded core.func via wget"
GN=$(echo "\033[1;92m") fi
DGN=$(echo "\033[32m")
# Formatting
CL=$(echo "\033[m")
BOLD=$(echo "\033[1m")
HOLD=" "
TAB=" "
TAB3=" "
# Icons
CM="${TAB}✔️${TAB}"
CROSS="${TAB}✖️${TAB}"
INFO="${TAB}💡${TAB}${CL}"
OS="${TAB}🖥️${TAB}${CL}"
OSVERSION="${TAB}🌟${TAB}${CL}"
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
DISKSIZE="${TAB}💾${TAB}${CL}"
CPUCORE="${TAB}🧠${TAB}${CL}"
RAMSIZE="${TAB}🛠️${TAB}${CL}"
SEARCH="${TAB}🔍${TAB}${CL}"
VERBOSE_CROPPED="🔍${TAB}"
VERIFYPW="${TAB}🔐${TAB}${CL}"
CONTAINERID="${TAB}🆔${TAB}${CL}"
HOSTNAME="${TAB}🏠${TAB}${CL}"
BRIDGE="${TAB}🌉${TAB}${CL}"
NETWORK="${TAB}📡${TAB}${CL}"
GATEWAY="${TAB}🌐${TAB}${CL}"
DISABLEIPV6="${TAB}🚫${TAB}${CL}"
DEFAULT="${TAB}⚙️${TAB}${CL}"
MACADDRESS="${TAB}🔗${TAB}${CL}"
VLANTAG="${TAB}🏷️${TAB}${CL}"
ROOTSSH="${TAB}🔑${TAB}${CL}"
CREATING="${TAB}🚀${TAB}${CL}"
ADVANCED="${TAB}🧩${TAB}${CL}"
FUSE="${TAB}🗂️${TAB}${CL}"
}
# This function enables error handling in the script by setting options and defining a trap for the ERR signal. # This function enables error handling in the script by setting options and defining a trap for the ERR signal.
catch_errors() { catch_errors() {
set -Eeuo pipefail set -Eeuo pipefail
@ -71,7 +35,6 @@ catch_errors() {
# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message.
error_handler() { error_handler() {
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi
printf "\e[?25h" printf "\e[?25h"
local exit_code="$?" local exit_code="$?"
local line_number="$1" local line_number="$1"
@ -81,78 +44,6 @@ error_handler() {
echo -e "\n$error_message\n" echo -e "\n$error_message\n"
} }
# This function displays an informational message with logging support.
declare -A MSG_INFO_SHOWN
SPINNER_ACTIVE=0
SPINNER_PID=""
SPINNER_MSG=""
trap 'stop_spinner' EXIT INT TERM HUP
start_spinner() {
local msg="$1"
local frames=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏)
local spin_i=0
local interval=0.1
SPINNER_MSG="$msg"
printf "\r\e[2K" >&2
{
while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do
printf "\r\e[2K%s %b" "${frames[spin_i]}" "${YW}${SPINNER_MSG}${CL}" >&2
spin_i=$(((spin_i + 1) % ${#frames[@]}))
sleep "$interval"
done
} &
SPINNER_PID=$!
disown "$SPINNER_PID"
}
stop_spinner() {
if [[ ${SPINNER_PID+v} && -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then
kill "$SPINNER_PID" 2>/dev/null
sleep 0.1
kill -0 "$SPINNER_PID" 2>/dev/null && kill -9 "$SPINNER_PID" 2>/dev/null
wait "$SPINNER_PID" 2>/dev/null || true
fi
SPINNER_ACTIVE=0
unset SPINNER_PID
}
spinner_guard() {
if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "$SPINNER_PID" ]]; then
kill "$SPINNER_PID" 2>/dev/null
wait "$SPINNER_PID" 2>/dev/null || true
SPINNER_ACTIVE=0
unset SPINNER_PID
fi
}
msg_info() {
local msg="$1"
[[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return
MSG_INFO_SHOWN["$msg"]=1
spinner_guard
SPINNER_ACTIVE=1
start_spinner "$msg"
}
msg_ok() {
local msg="$1"
stop_spinner
printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2
unset MSG_INFO_SHOWN["$msg"]
}
msg_error() {
stop_spinner
local msg="$1"
printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2
}
# Check if the shell is using bash # Check if the shell is using bash
shell_check() { shell_check() {
if [[ "$(basename "$SHELL")" != "bash" ]]; then if [[ "$(basename "$SHELL")" != "bash" ]]; then
@ -273,42 +164,6 @@ update_motd_ip() {
fi fi
} }
# Function to download & save header files
get_header() {
local app_name=$(echo "${APP,,}" | tr -d ' ')
local header_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/headers/${app_name}"
local local_header_path="/usr/local/community-scripts/headers/${app_name}"
mkdir -p "$(dirname "$local_header_path")"
if [ ! -s "$local_header_path" ]; then
if ! curl -fsSL "$header_url" -o "$local_header_path"; then
echo -e "Failed to download header for ${app_name}. No header will be displayed."
return 1
fi
fi
cat "$local_header_path"
}
# This function sets the APP-Name into an ASCII Header in Slant, figlet needed on proxmox main node.
header_info() {
local app_name=$(echo "${APP,,}" | tr -d ' ')
local header_content
# Download & save Header-File locally
header_content=$(get_header "$app_name")
if [ $? -ne 0 ]; then
# Fallback: Doesn't show Header
return 0
fi
# Show ASCII-Header
term_width=$(tput cols 2>/dev/null || echo 120)
clear
echo "$header_content"
}
# This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. # This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit.
ssh_check() { ssh_check() {
if [ -n "${SSH_CLIENT:+x}" ]; then if [ -n "${SSH_CLIENT:+x}" ]; then
@ -346,8 +201,8 @@ base_settings() {
SSH="no" SSH="no"
SSH_AUTHORIZED_KEY="" SSH_AUTHORIZED_KEY=""
TAGS="community-script;" TAGS="community-script;"
ENABLE_FUSE="no" ENABLE_FUSE="${1:-no}"
ENABLE_TUN="no" ENABLE_TUN="${1:-no}"
# Override default settings with variables from ct script # Override default settings with variables from ct script
CT_TYPE=${var_unprivileged:-$CT_TYPE} CT_TYPE=${var_unprivileged:-$CT_TYPE}
@ -516,32 +371,40 @@ advanced_settings() {
fi fi
done done
while true; do while true; do
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then
if [[ ! -z "$PW1" ]]; then # Empty = Autologin
if [[ "$PW1" == *" "* ]]; then if [[ -z "$PW1" ]]; then
whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58
elif [ ${#PW1} -lt 5 ]; then
whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58
else
if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then
if [[ "$PW1" == "$PW2" ]]; then
PW="-password $PW1"
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
break
else
whiptail --msgbox "Passwords do not match. Please try again." 8 58
fi
else
exit_script
fi
fi
else
PW1="Automatic Login"
PW="" PW=""
PW1="Automatic Login"
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}"
break break
fi fi
# Invalid: contains spaces
if [[ "$PW1" == *" "* ]]; then
whiptail --msgbox "Password cannot contain spaces." 8 58
continue
fi
# Invalid: too short
if ((${#PW1} < 5)); then
whiptail --msgbox "Password must be at least 5 characters." 8 58
continue
fi
# Confirm password
if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then
if [[ "$PW1" == "$PW2" ]]; then
PW="-password $PW1"
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
break
else
whiptail --msgbox "Passwords do not match. Please try again." 8 58
fi
else
exit_script
fi
else else
exit_script exit_script
fi fi
@ -565,7 +428,7 @@ advanced_settings() {
else else
HN=$(echo "${CT_NAME,,}" | tr -d ' ') HN=$(echo "${CT_NAME,,}" | tr -d ' ')
fi fi
# Hostname validate (RFC 1123)
if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
break break
@ -824,11 +687,11 @@ advanced_settings() {
echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}"
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then
VERB="yes" VERBOSE="yes"
else else
VERB="no" VERBOSE="no"
fi fi
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERB${CL}" echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}"
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then
echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}"
@ -944,18 +807,18 @@ install_script() {
1) 1)
header_info header_info
echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}"
VERB="no" VERBOSE="no"
METHOD="default" METHOD="default"
base_settings "$VERB" base_settings "$VERBOSE"
echo_default echo_default
break break
;; ;;
2) 2)
header_info header_info
echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}"
VERB="yes" VERBOSE="yes"
METHOD="default" METHOD="default"
base_settings "$VERB" base_settings "$VERBOSE"
echo_default echo_default
break break
;; ;;
@ -1044,18 +907,9 @@ check_container_storage() {
} }
start() { start() {
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then
if command -v pveversion >/dev/null 2>&1; then
if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC" --yesno "This will create a New ${APP} LXC. Proceed?" 10 58); then
clear
exit_script
exit
fi
SPINNER_PID=""
install_script install_script
fi else
if ! command -v pveversion >/dev/null 2>&1; then
CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \
"Support/Update functions for ${APP} LXC. Choose an option:" \ "Support/Update functions for ${APP} LXC. Choose an option:" \
12 60 3 \ 12 60 3 \
@ -1065,11 +919,11 @@ start() {
case "$CHOICE" in case "$CHOICE" in
1) 1)
VERB="no" VERBOSE="no"
set_std_mode set_std_mode
;; ;;
2) 2)
VERB="yes" VERBOSE="yes"
set_std_mode set_std_mode
;; ;;
3) 3)
@ -1078,15 +932,13 @@ start() {
exit exit
;; ;;
esac esac
SPINNER_PID=""
update_script update_script
fi fi
} }
# This function collects user settings and integrates all the collected information. # This function collects user settings and integrates all the collected information.
build_container() { build_container() {
# if [ "$VERB" == "yes" ]; then set -x; fi # if [ "$VERBOSE" == "yes" ]; then set -x; fi
if [ "$CT_TYPE" == "1" ]; then if [ "$CT_TYPE" == "1" ]; then
FEATURES="keyctl=1,nesting=1" FEATURES="keyctl=1,nesting=1"
@ -1119,7 +971,7 @@ build_container() {
export APPLICATION="$APP" export APPLICATION="$APP"
export app="$NSAPP" export app="$NSAPP"
export PASSWORD="$PW" export PASSWORD="$PW"
export VERBOSE="$VERB" export VERBOSE="$VERBOSE"
export SSH_ROOT="${SSH}" export SSH_ROOT="${SSH}"
export SSH_AUTHORIZED_KEY export SSH_AUTHORIZED_KEY
export CTID="$CT_ID" export CTID="$CT_ID"
@ -1200,20 +1052,34 @@ lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
EOF EOF
fi fi
# This starts the container and executes <app>-install.sh # This starts the container and executes <app>-install.sh
msg_info "Starting LXC Container" msg_info "Starting LXC Container"
pct start "$CTID" pct start "$CTID"
msg_ok "Started LXC Container" msg_ok "Started LXC Container"
msg_info "Customizing LXC Container"
if [ "$var_os" == "alpine" ]; then if [ "$var_os" == "alpine" ]; then
sleep 3 sleep 3
pct exec "$CTID" -- /bin/sh -c 'cat <<EOF >/etc/apk/repositories pct exec "$CTID" -- /bin/sh -c 'cat <<EOF >/etc/apk/repositories
http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/main
http://dl-cdn.alpinelinux.org/alpine/latest-stable/community http://dl-cdn.alpinelinux.org/alpine/latest-stable/community
EOF' EOF'
pct exec "$CTID" -- ash -c "apk add bash >/dev/null" pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null"
fi else
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/"$var_install".sh)" $? sleep 3
# Set locale and timezone before update
pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen"
pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \
echo LANG=\$locale_line >/etc/default/locale && \
locale-gen >/dev/null && \
export LANG=\$locale_line"
pct exec "$CTID" -- bash -c "echo $tz >/etc/timezone && ln -sf /usr/share/zoneinfo/$tz /etc/localtime"
# Install curl
pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null"
fi
msg_ok "Customized LXC Container"
lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/"$var_install".sh)" $?
} }
# This function sets the description of the container. # This function sets the description of the container.
@ -1262,23 +1128,6 @@ EOF
post_update_to_api "done" "none" post_update_to_api "done" "none"
} }
set_std_mode() {
if [ "$VERB" = "yes" ]; then
STD=""
else
STD="silent"
fi
}
# Silent execution function
silent() {
if [ "$VERB" = "no" ]; then
"$@" >/dev/null 2>&1 || return 1
else
"$@" || return 1
fi
}
api_exit_script() { api_exit_script() {
exit_code=$? # Capture the exit status of the last executed command exit_code=$? # Capture the exit status of the last executed command
#200 exit codes indicate error in create_lxc.sh #200 exit codes indicate error in create_lxc.sh

View File

@ -53,15 +53,15 @@ config_file() {
fi fi
else else
if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then
if [ -z "$CT_ID" ]; then if [ -z "$CT_ID" ]; then
CT_ID="$NEXTID" CT_ID="$NEXTID"
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
else
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}"
fi
else else
echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" exit_script
fi fi
else
exit_script
fi
fi fi
if [[ -n "${CT_TYPE-}" ]]; then if [[ -n "${CT_TYPE-}" ]]; then
@ -75,23 +75,22 @@ config_file() {
fi fi
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
else else
if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \
"1" "Unprivileged" ON \ "1" "Unprivileged" ON \
"0" "Privileged" OFF \ "0" "Privileged" OFF \
3>&1 1>&2 2>&3); then 3>&1 1>&2 2>&3); then
if [ -n "$CT_TYPE" ]; then if [ -n "$CT_TYPE" ]; then
CT_TYPE_DESC="Unprivileged" CT_TYPE_DESC="Unprivileged"
if [ "$CT_TYPE" -eq 0 ]; then if [ "$CT_TYPE" -eq 0 ]; then
CT_TYPE_DESC="Privileged" CT_TYPE_DESC="Privileged"
fi
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
fi fi
else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}"
exit_script
fi fi
else
exit_script
fi
fi fi
if [[ -n "${PW-}" ]]; then if [[ -n "${PW-}" ]]; then
if [[ "$PW" == "none" ]]; then if [[ "$PW" == "none" ]]; then
PW="" PW=""
@ -109,50 +108,50 @@ config_file() {
fi fi
else else
while true; do while true; do
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then
if [[ -n "$PW1" ]]; then if [[ -n "$PW1" ]]; then
if [[ "$PW1" == *" "* ]]; then if [[ "$PW1" == *" "* ]]; then
whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58
elif [ ${#PW1} -lt 5 ]; then elif [ ${#PW1} -lt 5 ]; then
whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58
else
if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then
if [[ "$PW1" == "$PW2" ]]; then
PW="-password $PW1"
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
break
else
whiptail --msgbox "Passwords do not match. Please try again." 8 58
fi
else else
exit_script if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then
if [[ "$PW1" == "$PW2" ]]; then
PW="-password $PW1"
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}"
break
else
whiptail --msgbox "Passwords do not match. Please try again." 8 58
fi
else
exit_script
fi
fi fi
else
PW1="Automatic Login"
PW=""
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}"
break
fi fi
else else
PW1="Automatic Login" exit_script
PW=""
echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}"
break
fi fi
else done
exit_script
fi
done
fi fi
if [[ -n "${HN-}" ]]; then if [[ -n "${HN-}" ]]; then
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else else
if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then
if [ -z "$CT_NAME" ]; then if [ -z "$CT_NAME" ]; then
HN="$NSAPP" HN="$NSAPP"
else
HN=$(echo "${CT_NAME,,}" | tr -d ' ')
fi
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else else
HN=$(echo "${CT_NAME,,}" | tr -d ' ') exit_script
fi fi
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
else
exit_script
fi
fi fi
if [[ -n "${DISK_SIZE-}" ]]; then if [[ -n "${DISK_SIZE-}" ]]; then
@ -164,19 +163,19 @@ config_file() {
fi fi
else else
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then
if [ -z "$DISK_SIZE" ]; then if [ -z "$DISK_SIZE" ]; then
DISK_SIZE="$var_disk" DISK_SIZE="$var_disk"
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
else else
if ! [[ $DISK_SIZE =~ $INTEGER ]]; then if ! [[ $DISK_SIZE =~ $INTEGER ]]; then
echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}"
advanced_settings advanced_settings
fi
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}"
fi fi
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" else
exit_script
fi fi
else
exit_script
fi
fi fi
if [[ -n "${CORE_COUNT-}" ]]; then if [[ -n "${CORE_COUNT-}" ]]; then
@ -188,15 +187,15 @@ config_file() {
fi fi
else else
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then
if [ -z "$CORE_COUNT" ]; then if [ -z "$CORE_COUNT" ]; then
CORE_COUNT="$var_cpu" CORE_COUNT="$var_cpu"
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
else
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
fi
else else
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" exit_script
fi fi
else
exit_script
fi
fi fi
if [[ -n "${RAM_SIZE-}" ]]; then if [[ -n "${RAM_SIZE-}" ]]; then
@ -208,15 +207,15 @@ config_file() {
fi fi
else else
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then
if [ -z "$RAM_SIZE" ]; then if [ -z "$RAM_SIZE" ]; then
RAM_SIZE="$var_ram" RAM_SIZE="$var_ram"
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
else
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}"
fi
else else
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" exit_script
fi fi
else
exit_script
fi
fi fi
IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f)
@ -227,20 +226,20 @@ config_file() {
for iface_filepath in ${IFACE_FILEPATH_LIST}; do for iface_filepath in ${IFACE_FILEPATH_LIST}; do
iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX')
( grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1 ) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' > "${iface_indexes_tmpfile}" || true (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true
if [ -f "${iface_indexes_tmpfile}" ]; then if [ -f "${iface_indexes_tmpfile}" ]; then
while read -r pair; do while read -r pair; do
start=$(echo "${pair}" | cut -d':' -f1) start=$(echo "${pair}" | cut -d':' -f1)
end=$(echo "${pair}" | cut -d':' -f2) end=$(echo "${pair}" | cut -d':' -f2)
if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then
iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}')
BRIDGES="${iface_name}"$'\n'"${BRIDGES}" BRIDGES="${iface_name}"$'\n'"${BRIDGES}"
fi fi
done < "${iface_indexes_tmpfile}" done <"${iface_indexes_tmpfile}"
rm -f "${iface_indexes_tmpfile}" rm -f "${iface_indexes_tmpfile}"
fi fi
done done
@ -296,7 +295,7 @@ config_file() {
done done
fi fi
elif [[ "$NET" == *-* ]]; then elif [[ "$NET" == *-* ]]; then
IFS="-" read -r ip_start ip_end <<< "$NET" IFS="-" read -r ip_start ip_end <<<"$NET"
if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then if [[ ! "$ip_start" =~ $ip_cidr_regex ]] || [[ ! "$ip_end" =~ $ip_cidr_regex ]]; then
msg_error "Invalid IP range format, was $NET should be 0.0.0.0/0-0.0.0.0/0" msg_error "Invalid IP range format, was $NET should be 0.0.0.0/0-0.0.0.0/0"
@ -309,21 +308,21 @@ config_file() {
ip_to_int() { ip_to_int() {
local IFS=. local IFS=.
read -r i1 i2 i3 i4 <<< "$1" read -r i1 i2 i3 i4 <<<"$1"
echo $(( (i1 << 24) + (i2 << 16) + (i3 << 8) + i4 )) echo $(((i1 << 24) + (i2 << 16) + (i3 << 8) + i4))
} }
int_to_ip() { int_to_ip() {
local ip=$1 local ip=$1
echo "$(( (ip >> 24) & 0xFF )).$(( (ip >> 16) & 0xFF )).$(( (ip >> 8) & 0xFF )).$(( ip & 0xFF ))" echo "$(((ip >> 24) & 0xFF)).$(((ip >> 16) & 0xFF)).$(((ip >> 8) & 0xFF)).$((ip & 0xFF))"
} }
start_int=$(ip_to_int "$ip1") start_int=$(ip_to_int "$ip1")
end_int=$(ip_to_int "$ip2") end_int=$(ip_to_int "$ip2")
for ((ip_int=start_int; ip_int<=end_int; ip_int++)); do for ((ip_int = start_int; ip_int <= end_int; ip_int++)); do
ip=$(int_to_ip $ip_int) ip=$(int_to_ip $ip_int)
msg_info "Checking IP: $ip" msg_info "Checking IP: $ip"
if ! ping -c 2 -W 1 "$ip" >/dev/null 2>&1; then if ! ping -c 2 -W 1 "$ip" >/dev/null 2>&1; then
NET="$ip/$cidr" NET="$ip/$cidr"
msg_ok "Using free IP Address: ${BGN}$NET${CL}" msg_ok "Using free IP Address: ${BGN}$NET${CL}"
@ -363,37 +362,37 @@ config_file() {
fi fi
else else
while true; do while true; do
NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3)
exit_status=$? exit_status=$?
if [ $exit_status -eq 0 ]; then if [ $exit_status -eq 0 ]; then
if [ "$NET" = "dhcp" ]; then if [ "$NET" = "dhcp" ]; then
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
break
else
if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
break break
else else
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then
echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}"
break
else
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58
fi
fi fi
else
exit_script
fi fi
else
exit_script
fi
done done
if [ "$NET" != "dhcp" ]; then if [ "$NET" != "dhcp" ]; then
while true; do while true; do
GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3)
if [ -z "$GATE1" ]; then if [ -z "$GATE1" ]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58
elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58
else else
GATE=",gw=$GATE1" GATE=",gw=$GATE1"
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}"
break break
fi fi
done done
else else
GATE="" GATE=""
echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}"
@ -406,8 +405,8 @@ config_file() {
else else
if [[ -n "${APT_CACHER_IP-}" ]]; then if [[ -n "${APT_CACHER_IP-}" ]]; then
if [[ ! $APT_CACHER_IP == "none" ]]; then if [[ ! $APT_CACHER_IP == "none" ]]; then
APT_CACHER="yes" APT_CACHER="yes"
echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}"
else else
APT_CACHER="" APT_CACHER=""
echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}No${CL}" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}No${CL}"
@ -416,7 +415,7 @@ config_file() {
if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then
APT_CACHER="${APT_CACHER_IP:+yes}" APT_CACHER="${APT_CACHER_IP:+yes}"
echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}"
if [[ -n $APT_CACHER_IP ]]; then if [[ -n $APT_CACHER_IP ]]; then
APT_CACHER_IP="none" APT_CACHER_IP="none"
fi fi
else else
@ -506,7 +505,7 @@ config_file() {
fi fi
echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}"
else else
exit_script exit_script
fi fi
fi fi
@ -533,7 +532,7 @@ config_file() {
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
fi fi
else else
exit_script exit_script
fi fi
fi fi
@ -582,7 +581,7 @@ config_file() {
fi fi
echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}"
else else
exit_script exit_script
fi fi
fi fi
@ -646,7 +645,7 @@ config_file() {
exit exit
fi fi
else else
if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then
VERBOSE="yes" VERBOSE="yes"
else else
VERBOSE="no" VERBOSE="no"

View File

@ -46,15 +46,26 @@ load_functions() {
on_error() { on_error() {
local exit_code="$1" local exit_code="$1"
local lineno="$2" local lineno="$2"
msg_error "Script failed at line $lineno with exit code $exit_code"
# Optionally log to your API or file here stop_spinner
case "$exit_code" in
1) msg_error "Generic error occurred (line $lineno)" ;;
2) msg_error "Shell misuse (line $lineno)" ;;
126) msg_error "Command cannot execute (line $lineno)" ;;
127) msg_error "Command not found (line $lineno)" ;;
128) msg_error "Invalid exit argument (line $lineno)" ;;
130) msg_error "Script aborted by user (CTRL+C)" ;;
143) msg_error "Script terminated by SIGTERM" ;;
*) msg_error "Script failed at line $lineno with exit code $exit_code" ;;
esac
exit "$exit_code" exit "$exit_code"
} }
on_exit() { on_exit() {
# Always called on script exit, success or failure cleanup_spinner || true
cleanup_temp_files || true [[ "${VERBOSE:-no}" == "yes" ]] && msg_info "Script exited"
msg_info "Script exited"
} }
on_interrupt() { on_interrupt() {
@ -429,8 +440,14 @@ msg_info() {
local msg="$1" local msg="$1"
[[ -z "$msg" || -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return [[ -z "$msg" || -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return
MSG_INFO_SHOWN["$msg"]=1 MSG_INFO_SHOWN["$msg"]=1
stop_spinner stop_spinner
start_spinner "$msg"
if [[ "${VERBOSE:-no}" == "no" && -t 2 ]]; then
start_spinner "$msg"
else
printf "\r\e[2K%s %b"${TAB}"" "⏳" "${YW}${msg}${CL}" >&2
fi
} }
msg_ok() { msg_ok() {

View File

@ -3,32 +3,20 @@
# Copyright (c) 2021-2025 tteck # Copyright (c) 2021-2025 tteck
# Author: tteck (tteckster) # Author: tteck (tteckster)
# Co-Author: MickLesk # Co-Author: MickLesk
# License: MIT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# This sets verbose mode if the global variable is set to "yes" # This sets verbose mode if the global variable is set to "yes"
# if [ "$VERBOSE" == "yes" ]; then set -x; fi # if [ "$VERBOSE" == "yes" ]; then set -x; fi
# This function sets color variables for formatting output in the terminal if command -v curl >/dev/null 2>&1; then
# Colors source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
YW=$(echo "\033[33m") load_functions
YWB=$(echo "\033[93m") #echo "(create-lxc.sh) Loaded core.func via curl"
BL=$(echo "\033[36m") elif command -v wget >/dev/null 2>&1; then
RD=$(echo "\033[01;31m") source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
GN=$(echo "\033[1;92m") load_functions
#echo "(create-lxc.sh) Loaded core.func via wget"
# Formatting fi
CL=$(echo "\033[m")
UL=$(echo "\033[4m")
BOLD=$(echo "\033[1m")
BFR="\\r\\033[K"
HOLD=" "
TAB=" "
# Icons
CM="${TAB}✔️${TAB}${CL}"
CROSS="${TAB}✖️${TAB}${CL}"
INFO="${TAB}💡${TAB}${CL}"
# This sets error handling options and defines the error_handler function to handle errors # This sets error handling options and defines the error_handler function to handle errors
set -Eeuo pipefail set -Eeuo pipefail
@ -36,7 +24,6 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
# This function handles errors # This function handles errors
function error_handler() { function error_handler() {
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
printf "\e[?25h" printf "\e[?25h"
local exit_code="$?" local exit_code="$?"
local line_number="$1" local line_number="$1"
@ -46,53 +33,6 @@ function error_handler() {
exit 200 exit 200
} }
# This function displays a spinner.
function spinner() {
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
local spin_i=0
local interval=0.1
printf "\e[?25l"
local color="${YWB}"
while true; do
printf "\r ${color}%s${CL}" "${frames[spin_i]}"
spin_i=$(((spin_i + 1) % ${#frames[@]}))
sleep "$interval"
done
}
# This function displays an informational message with a yellow color.
function msg_info() {
local msg="$1"
echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}"
spinner &
SPINNER_PID=$!
}
function msg_warn() {
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
printf "\e[?25h"
local msg="$1"
echo -e "${BFR}${INFO}${YWB}${msg}${CL}"
}
# This function displays a success message with a green color.
function msg_ok() {
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
printf "\e[?25h"
local msg="$1"
echo -e "${BFR}${CM}${GN}${msg}${CL}"
}
# This function displays a error message with a red color.
function msg_error() {
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
printf "\e[?25h"
local msg="$1"
echo -e "${BFR}${CROSS}${RD}${msg}${CL}"
}
# This checks for the presence of valid Container Storage and Template Storage locations # This checks for the presence of valid Container Storage and Template Storage locations
msg_info "Validating Storage" msg_info "Validating Storage"
VALIDCT=$(pvesm status -content rootdir | awk 'NR>1') VALIDCT=$(pvesm status -content rootdir | awk 'NR>1')
@ -126,40 +66,44 @@ function select_storage() {
} ;; } ;;
esac esac
# This Queries all storage locations # Collect storage options
local -a MENU local -a MENU
while read -r line; do local MSG_MAX_LENGTH=0
local TAG=$(echo $line | awk '{print $1}')
local TYPE=$(echo $line | awk '{printf "%-10s", $2}')
local FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
local ITEM="Type: $TYPE Free: $FREE "
local OFFSET=2
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
local MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
fi
MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content $CONTENT | awk 'NR>1')
# Select storage location while read -r TAG TYPE _ _ _ FREE _; do
if [ $((${#MENU[@]} / 3)) -eq 1 ]; then local TYPE_PADDED
printf ${MENU[0]} local FREE_FMT
else
local STORAGE TYPE_PADDED=$(printf "%-10s" "$TYPE")
while [ -z "${STORAGE:+x}" ]; do FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.2f <<<"$FREE")B
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ local ITEM="Type: $TYPE_PADDED Free: $FREE_FMT"
"Which storage pool would you like to use for the ${CONTENT_LABEL,,}?\nTo make a selection, use the Spacebar.\n" \
16 $(($MSG_MAX_LENGTH + 23)) 6 \ ((${#ITEM} + 2 > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + 2))
"${MENU[@]}" 3>&1 1>&2 2>&3) || {
msg_error "Menu aborted." MENU+=("$TAG" "$ITEM" "OFF")
exit 202 done < <(pvesm status -content "$CONTENT" | awk 'NR>1')
}
if [ $? -ne 0 ]; then local OPTION_COUNT=$((${#MENU[@]} / 3))
echo -e "${CROSS}${RD} Menu aborted by user.${CL}"
exit 0 # Auto-select if only one option available
fi if [[ "$OPTION_COUNT" -eq 1 ]]; then
done echo "${MENU[0]}"
printf "%s" "$STORAGE" return 0
fi fi
# Display selection menu
local STORAGE
while [[ -z "${STORAGE:+x}" ]]; do
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
"Select the storage pool to use for the ${CONTENT_LABEL,,}.\nUse the spacebar to make a selection.\n" \
16 $((MSG_MAX_LENGTH + 23)) 6 \
"${MENU[@]}" 3>&1 1>&2 2>&3) || {
msg_error "Storage selection cancelled."
exit 202
}
done
echo "$STORAGE"
} }
# Test if required variables are set # Test if required variables are set
[[ "${CTID:-}" ]] || { [[ "${CTID:-}" ]] || {
@ -177,20 +121,6 @@ function select_storage() {
exit 205 exit 205
} }
# Check for network connectivity (IPv4 & IPv6)
#function check_network() {
# local CHECK_URLS=("8.8.8.8" "1.1.1.1" "9.9.9.9" "2606:4700:4700::1111" "2001:4860:4860::8888" "2620:fe::fe")
#
# for url in "${CHECK_URLS[@]}"; do
# if ping -c 1 -W 2 "$url" &>/dev/null; then
# return 0 # Success: At least one connection works
# fi
# done
#
# msg_error "No network connection detected. Check your internet connection."
# exit 101
#}
# Test if ID is in use # Test if ID is in use
if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then
echo -e "ID '$CTID' is already in use." echo -e "ID '$CTID' is already in use."
@ -207,6 +137,13 @@ msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage."
CONTAINER_STORAGE=$(select_storage container) CONTAINER_STORAGE=$(select_storage container)
msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage." msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage."
# Check free space on selected container storage
STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }')
REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024))
if [ "$STORAGE_FREE" -lt "$REQUIRED_KB" ]; then
msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G."
exit 214
fi
# Check Cluster Quorum if in Cluster # Check Cluster Quorum if in Cluster
if [ -f /etc/pve/corosync.conf ]; then if [ -f /etc/pve/corosync.conf ]; then
msg_info "Checking Proxmox cluster quorum status" msg_info "Checking Proxmox cluster quorum status"
@ -220,44 +157,48 @@ fi
# Update LXC template list # Update LXC template list
msg_info "Updating LXC Template List" msg_info "Updating LXC Template List"
#check_network
pveam update >/dev/null if ! timeout 10 pveam update >/dev/null 2>&1; then
msg_ok "Updated LXC Template List" msg_error "Failed to update LXC template list. Please check your Proxmox host's internet connection and DNS resolution."
exit 201
fi
$STD msg_ok "LXC Template List Updated"
# Get LXC template string # Get LXC template string
TEMPLATE_SEARCH=${PCT_OSTYPE}-${PCT_OSVERSION:-} TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}"
mapfile -t TEMPLATES < <(pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V) mapfile -t TEMPLATES < <(pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V)
[ ${#TEMPLATES[@]} -gt 0 ] || {
msg_error "Unable to find a template when searching for '$TEMPLATE_SEARCH'."
exit 207
}
TEMPLATE="${TEMPLATES[-1]}"
TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE)"
# Without NAS/Mount: TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE"
# Check if template exists, if corrupt remove and redownload
if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE" || ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then
msg_warn "Template $TEMPLATE not found in storage or seems to be corrupted. Redownloading."
[[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH"
# Download with 3 attempts if [ ${#TEMPLATES[@]} -eq 0 ]; then
msg_error "No matching LXC template found for '${TEMPLATE_SEARCH}'. Make sure your host can reach the Proxmox template repository."
exit 207
fi
TEMPLATE="${TEMPLATES[-1]}"
TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")"
# Check if template exists and is valid
if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE" || ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then
msg_warn "Template $TEMPLATE not found or appears to be corrupted. Re-downloading."
[[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH"
for attempt in {1..3}; do for attempt in {1..3}; do
msg_info "Attempt $attempt: Downloading LXC template..." msg_info "Attempt $attempt: Downloading LXC template..."
if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then
msg_ok "Template download successful." msg_ok "Template download successful."
break break
fi fi
if [ $attempt -eq 3 ]; then if [ $attempt -eq 3 ]; then
msg_error "Three failed attempts. Aborting." msg_error "Failed after 3 attempts. Please check your Proxmox hosts internet access or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE"
exit 208 exit 208
fi fi
sleep $((attempt * 5)) sleep $((attempt * 5))
done done
fi fi
msg_ok "LXC Template is ready to use."
msg_ok "LXC Template '$TEMPLATE' is ready to use."
# Check and fix subuid/subgid # Check and fix subuid/subgid
grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid
grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid
@ -266,28 +207,55 @@ grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgi
PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}})
[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") [[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}")
# Secure creation of the LXC container with lock and template check
lockfile="/tmp/template.${TEMPLATE}.lock"
exec 9>"$lockfile"
flock -w 60 9 || {
msg_error "Timeout while waiting for template lock"
exit 211
}
msg_info "Creating LXC Container" msg_info "Creating LXC Container"
if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then
msg_error "Container creation failed. Checking if template is corrupted." msg_error "Container creation failed. Checking if template is corrupted or incomplete."
if ! zstdcat "$TEMPLATE_PATH" | tar -tf - >/dev/null 2>&1; then if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then
msg_error "Template appears to be corrupted. Removing and re-downloading." msg_error "Template file too small or missing re-downloading."
rm -f "$TEMPLATE_PATH"
elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then
msg_error "Template appears to be corrupted re-downloading."
rm -f "$TEMPLATE_PATH" rm -f "$TEMPLATE_PATH"
if ! timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then
msg_error "Failed to re-download template."
exit 208
fi
msg_ok "Re-downloaded LXC Template"
if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then
msg_error "Container creation failed after re-downloading template."
exit 200
fi
else else
msg_error "Container creation failed, but template is not corrupted." msg_error "Template is valid, but container creation still failed."
exit 209 exit 209
fi fi
# Retry download
for attempt in {1..3}; do
msg_info "Attempt $attempt: Re-downloading template..."
if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then
msg_ok "Template re-download successful."
break
fi
if [ "$attempt" -eq 3 ]; then
msg_error "Three failed attempts. Aborting."
exit 208
fi
sleep $((attempt * 5))
done
sleep 1 # I/O-Sync-Delay
msg_ok "Re-downloaded LXC Template"
if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then
msg_error "Container creation failed after re-downloading template."
exit 200
fi
fi fi
if ! pct status "$CTID" &>/dev/null; then
msg_error "Container not found after pct create assuming failure."
exit 210
fi
msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created."

View File

@ -4,53 +4,13 @@
# License: MIT # License: MIT
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# This function sets color variables for formatting output in the terminal if ! command -v curl >/dev/null 2>&1; then
color() { printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
# Colors apt-get update >/dev/null 2>&1
YW=$(echo "\033[33m") apt-get install -y curl >/dev/null 2>&1
YWB=$(echo "\033[93m") fi
BL=$(echo "\033[36m") source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
RD=$(echo "\033[01;31m") load_functions
GN=$(echo "\033[1;92m")
# Formatting
CL=$(echo "\033[m")
BFR="\\r\\033[K"
BOLD=$(echo "\033[1m")
HOLD=" "
TAB=" "
TAB3=" "
# System
RETRY_NUM=10
RETRY_EVERY=3
# Icons
CM="${TAB}✔️${TAB}${CL}"
CROSS="${TAB}✖️${TAB}${CL}"
INFO="${TAB}💡${TAB}${CL}"
NETWORK="${TAB}📡${TAB}${CL}"
OS="${TAB}🖥️${TAB}${CL}"
OSVERSION="${TAB}🌟${TAB}${CL}"
HOSTNAME="${TAB}🏠${TAB}${CL}"
GATEWAY="${TAB}🌐${TAB}${CL}"
DEFAULT="${TAB}⚙️${TAB}${CL}"
}
# Function to set STD mode based on verbosity
set_std_mode() {
if [ "$VERBOSE" = "yes" ]; then
STD=""
else
STD="silent"
fi
}
# Silent execution function
silent() {
"$@" >/dev/null 2>&1
}
# This function enables IPv6 if it's not disabled and sets verbose mode # This function enables IPv6 if it's not disabled and sets verbose mode
verb_ip6() { verb_ip6() {
set_std_mode # Set STD mode based on VERBOSE set_std_mode # Set STD mode based on VERBOSE
@ -70,7 +30,6 @@ catch_errors() {
# This function handles errors # This function handles errors
error_handler() { error_handler() {
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi
printf "\e[?25h" printf "\e[?25h"
local exit_code="$?" local exit_code="$?"
local line_number="$1" local line_number="$1"
@ -85,62 +44,15 @@ error_handler() {
fi fi
} }
# This function displays a spinner.
spinner() {
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
local spin_i=0
local interval=0.1
printf "\e[?25l"
local color="${YWB}"
while true; do
printf "\r ${color}%s${CL}" "${frames[spin_i]}"
spin_i=$(((spin_i + 1) % ${#frames[@]}))
sleep "$interval"
done
}
# This function displays an informational message with a yellow color.
msg_info() {
local msg="$1"
echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}"
spinner &
SPINNER_PID=$!
}
# This function displays a success message with a green color.
msg_ok() {
if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi
printf "\e[?25h"
local msg="$1"
echo -e "${BFR}${CM}${GN}${msg}${CL}"
}
# This function displays a error message with a red color.
msg_error() {
if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi
printf "\e[?25h"
local msg="$1"
echo -e "${BFR}${CROSS}${RD}${msg}${CL}"
}
# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection
setting_up_container() { setting_up_container() {
msg_info "Setting up Container OS" msg_info "Setting up Container OS"
sed -i "/$LANG/ s/\(^# \)//" /etc/locale.gen
locale_line=$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print $1}' | head -n 1)
echo "LANG=${locale_line}" >/etc/default/locale
locale-gen >/dev/null
export LANG=${locale_line}
echo "$tz" >/etc/timezone
ln -sf /usr/share/zoneinfo/"$tz" /etc/localtime
for ((i = RETRY_NUM; i > 0; i--)); do for ((i = RETRY_NUM; i > 0; i--)); do
if [ "$(hostname -I)" != "" ]; then if [ "$(hostname -I)" != "" ]; then
break break
fi fi
echo 1>&2 -en "${CROSS}${RD} No Network! " echo 1>&2 -en "${CROSS}${RD} No Network! "
sleep "$RETRY_EVERY" sleep $RETRY_EVERY
done done
if [ "$(hostname -I)" = "" ]; then if [ "$(hostname -I)" = "" ]; then
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
@ -150,7 +62,7 @@ setting_up_container() {
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
systemctl disable -q --now systemd-networkd-wait-online.service systemctl disable -q --now systemd-networkd-wait-online.service
msg_ok "Set up Container OS" msg_ok "Set up Container OS"
msg_ok "Network Connected: ${BL}$(hostname -I)" msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)"
} }
# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected # This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected
@ -178,7 +90,7 @@ network_check() {
# If both IPv4 and IPv6 checks fail, prompt the user # If both IPv4 and IPv6 checks fail, prompt the user
if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then
read -r -p "No Internet detected,would you like to continue anyway? <y/N> " prompt read -r -p "No Internet detected, would you like to continue anyway? <y/N> " prompt
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" echo -e "${INFO}${RD}Expect Issues Without Internet${CL}"
else else
@ -187,8 +99,26 @@ network_check() {
fi fi
fi fi
RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6)
if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi GITHUB_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com")
GITHUB_STATUS="GitHub DNS:"
DNS_FAILED=false
for HOST in "${GITHUB_HOSTS[@]}"; do
RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1)
if [[ -z "$RESOLVEDIP" ]]; then
GITHUB_STATUS+="$HOST:($DNSFAIL)"
DNS_FAILED=true
else
GITHUB_STATUS+=" $HOST:($DNSOK)"
fi
done
if [[ "$DNS_FAILED" == true ]]; then
fatal "$GITHUB_STATUS"
else
msg_ok "$GITHUB_STATUS"
fi
set -e set -e
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
} }
@ -213,11 +143,7 @@ EOF
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
msg_ok "Updated Container OS" msg_ok "Updated Container OS"
msg_info "Installing core dependencies"
$STD apt-get update
$STD apt-get install -y sudo curl mc gnupg2
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func)
msg_ok "Core dependencies installed"
} }
# This function modifies the message of the day (motd) and SSH settings # This function modifies the message of the day (motd) and SSH settings