mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-10-09 04:05:26 +00:00
* Refactor: OpenWrt VM * Refactor comments in openwrt-vm.sh Removed redundant author line and cleaned up comments.
370 lines
12 KiB
Bash
370 lines
12 KiB
Bash
#!/usr/bin/env bash
|
|
# Copyright (c) 2021-2026 tteck
|
|
# Author: tteck (tteckster) | Jon Spriggs (jontheniceguy) | MickLesk (CanbiZ)
|
|
# License: MIT
|
|
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
|
|
|
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/DevScripts/main}"
|
|
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
|
load_functions
|
|
|
|
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
|
METHOD=""
|
|
APP="OpenWrt"
|
|
APP_TYPE="vm"
|
|
NSAPP="openwrt-vm"
|
|
var_os="openwrt"
|
|
var_version=" "
|
|
DISK_SIZE="1G"
|
|
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
|
|
GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
|
|
|
|
HA=$(echo "\033[1;34m")
|
|
|
|
header_info
|
|
echo -e "\n Loading..."
|
|
|
|
set -Eeo pipefail
|
|
shopt -s inherit_errexit
|
|
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
|
trap cleanup EXIT
|
|
trap 'post_update_to_api "failed" "130"' SIGINT
|
|
trap 'post_update_to_api "failed" "143"' SIGTERM
|
|
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
|
|
|
|
vm_require_arch amd64
|
|
|
|
TEMP_DIR=$(mktemp -d)
|
|
pushd "$TEMP_DIR" >/dev/null
|
|
|
|
vm_preflight
|
|
vm_require_tools gzip
|
|
|
|
function send_line_to_vm() {
|
|
echo -e "${DGN}Sending line: ${YW}$1${CL}"
|
|
for ((i = 0; i < ${#1}; i++)); do
|
|
character=${1:i:1}
|
|
case $character in
|
|
" ") character="spc" ;;
|
|
"-") character="minus" ;;
|
|
"=") character="equal" ;;
|
|
",") character="comma" ;;
|
|
".") character="dot" ;;
|
|
"/") character="slash" ;;
|
|
"'") character="apostrophe" ;;
|
|
";") character="semicolon" ;;
|
|
'\') character="backslash" ;;
|
|
'`') character="grave_accent" ;;
|
|
"[") character="bracket_left" ;;
|
|
"]") character="bracket_right" ;;
|
|
"_") character="shift-minus" ;;
|
|
"+") character="shift-equal" ;;
|
|
"?") character="shift-slash" ;;
|
|
"<") character="shift-comma" ;;
|
|
">") character="shift-dot" ;;
|
|
'"') character="shift-apostrophe" ;;
|
|
":") character="shift-semicolon" ;;
|
|
"|") character="shift-backslash" ;;
|
|
"~") character="shift-grave_accent" ;;
|
|
"{") character="shift-bracket_left" ;;
|
|
"}") character="shift-bracket_right" ;;
|
|
"A") character="shift-a" ;;
|
|
"B") character="shift-b" ;;
|
|
"C") character="shift-c" ;;
|
|
"D") character="shift-d" ;;
|
|
"E") character="shift-e" ;;
|
|
"F") character="shift-f" ;;
|
|
"G") character="shift-g" ;;
|
|
"H") character="shift-h" ;;
|
|
"I") character="shift-i" ;;
|
|
"J") character="shift-j" ;;
|
|
"K") character="shift-k" ;;
|
|
"L") character="shift-l" ;;
|
|
"M") character="shift-m" ;;
|
|
"N") character="shift-n" ;;
|
|
"O") character="shift-o" ;;
|
|
"P") character="shift-p" ;;
|
|
"Q") character="shift-q" ;;
|
|
"R") character="shift-r" ;;
|
|
"S") character="shift-s" ;;
|
|
"T") character="shift-t" ;;
|
|
"U") character="shift-u" ;;
|
|
"V") character="shift-v" ;;
|
|
"W") character="shift-w" ;;
|
|
"X") character="shift-x" ;;
|
|
"Y") character="shift-y" ;;
|
|
"Z") character="shift-z" ;;
|
|
"!") character="shift-1" ;;
|
|
"@") character="shift-2" ;;
|
|
"#") character="shift-3" ;;
|
|
'$') character="shift-4" ;;
|
|
"%") character="shift-5" ;;
|
|
"^") character="shift-6" ;;
|
|
"&") character="shift-7" ;;
|
|
"*") character="shift-8" ;;
|
|
"(") character="shift-9" ;;
|
|
")") character="shift-0" ;;
|
|
esac
|
|
qm sendkey $VMID "$character"
|
|
done
|
|
qm sendkey $VMID ret
|
|
}
|
|
|
|
function validate_ip_octets() {
|
|
local octet='(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])'
|
|
[[ "$1" =~ ^${octet}\.${octet}\.${octet}\.${octet}$ ]]
|
|
}
|
|
|
|
function prompt_router_input() {
|
|
local var_name="$1" title="$2" prompt="$3" default_value="$4" value
|
|
if vm_dialog inputbox "$title" "$prompt" 8 58 "$default_value" --cancel-button Exit-Script; then
|
|
value="$VM_DIALOG_RESULT"
|
|
[[ -n "$value" ]] || value="$default_value"
|
|
printf -v "$var_name" '%s' "$value"
|
|
else
|
|
exit_script
|
|
fi
|
|
}
|
|
|
|
function prompt_router_ip() {
|
|
local var_name="$1" title="$2" prompt="$3" default_value="$4" label="$5" value
|
|
prompt_router_input "$var_name" "$title" "$prompt" "$default_value"
|
|
value="${!var_name}"
|
|
if ! validate_ip_octets "$value"; then
|
|
msg_error "Invalid ${label} format. Needs to be 0.0.0.0, was $value"
|
|
exit 1
|
|
fi
|
|
echo -e "${DGN}Using ${label}: ${BGN}$value${CL}"
|
|
}
|
|
|
|
function prompt_router_mac() {
|
|
local var_name="$1" title="$2" prompt="$3" default_value="$4" label="$5" value
|
|
prompt_router_input "$var_name" "$title" "$prompt" "$default_value"
|
|
value="${!var_name}"
|
|
if ! validate_mac_address "$value"; then
|
|
msg_error "Invalid ${label}: $value"
|
|
exit 1
|
|
fi
|
|
echo -e "${DGN}Using ${label}: ${BGN}$value${CL}"
|
|
}
|
|
|
|
function prompt_router_vlan() {
|
|
local var_name="$1" label_var="$2" title="$3" prompt="$4" default_value="$5" input_value
|
|
while true; do
|
|
prompt_router_input "$label_var" "$title" "$prompt" "$default_value"
|
|
input_value="${!label_var}"
|
|
if [ -z "$input_value" ] || [ "$input_value" = "Default" ]; then
|
|
printf -v "$var_name" '%s' ""
|
|
printf -v "$label_var" '%s' "Default"
|
|
echo -e "${DGN}Using ${title}: ${BGN}Default${CL}"
|
|
break
|
|
fi
|
|
if validate_vlan_tag "$input_value"; then
|
|
printf -v "$var_name" '%s' ",tag=$input_value"
|
|
echo -e "${DGN}Using ${title}: ${BGN}$input_value${CL}"
|
|
break
|
|
fi
|
|
vm_dialog msgbox "INVALID INPUT" "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
|
|
done
|
|
}
|
|
|
|
function prompt_router_mtu() {
|
|
local input_value
|
|
while true; do
|
|
prompt_router_input "MTU_VALUE" "MTU SIZE" "Set Interface MTU Size (leave blank for default)" ""
|
|
input_value="$MTU_VALUE"
|
|
if [ -z "$input_value" ]; then
|
|
MTU=""
|
|
MTU_VALUE="Default"
|
|
echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}"
|
|
break
|
|
fi
|
|
if validate_mtu "$input_value"; then
|
|
MTU=",mtu=$input_value"
|
|
echo -e "${DGN}Using Interface MTU Size: ${BGN}$input_value${CL}"
|
|
break
|
|
fi
|
|
vm_dialog msgbox "INVALID INPUT" "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
|
|
done
|
|
}
|
|
|
|
function default_settings() {
|
|
VMID=$(get_valid_nextid)
|
|
vm_apply_machine_type "i440fx"
|
|
HN="openwrt"
|
|
CORE_COUNT="1"
|
|
RAM_SIZE="256"
|
|
CPU_TYPE=""
|
|
DISK_CACHE=""
|
|
BRG="vmbr0"
|
|
LAN_BRG="vmbr0"
|
|
MAC=$GEN_MAC
|
|
LAN_MAC=$GEN_MAC_LAN
|
|
VLAN=""
|
|
LAN_VLAN=""
|
|
LAN_IP_ADDR="192.168.1.1"
|
|
LAN_NETMASK="255.255.255.0"
|
|
MTU=""
|
|
START_VM="yes"
|
|
METHOD="default"
|
|
DISK_SIZE="1G"
|
|
echo -e "${CONTAINERID}${BOLD}${DGN}VMID: ${BGN}${VMID}${CL}"
|
|
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
|
|
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
|
echo -e "${RAMSIZE}${BOLD}${DGN}RAM: ${BGN}${RAM_SIZE}${CL}"
|
|
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
|
|
echo -e "${BRIDGE}${BOLD}${DGN}WAN Bridge: ${BGN}${BRG}${CL}"
|
|
echo -e "${BRIDGE}${BOLD}${DGN}LAN Bridge: ${BGN}${LAN_BRG}${CL}"
|
|
echo -e "${MACADDRESS}${BOLD}${DGN}WAN MAC: ${BGN}${MAC}${CL}"
|
|
echo -e "${MACADDRESS}${BOLD}${DGN}LAN MAC: ${BGN}${LAN_MAC}${CL}"
|
|
}
|
|
|
|
function advanced_settings() {
|
|
METHOD="advanced"
|
|
CPU_TYPE=""
|
|
DISK_CACHE=""
|
|
vm_apply_machine_type "i440fx"
|
|
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
|
vm_prompt_hostname "openwrt"
|
|
vm_prompt_cpu_cores "1"
|
|
vm_prompt_ram "256"
|
|
vm_prompt_disk_size "1G"
|
|
vm_prompt_keyboard
|
|
vm_prompt_verbose "no"
|
|
prompt_router_input "BRG" "WAN BRIDGE" "Set a WAN Bridge" "vmbr0"
|
|
echo -e "${DGN}Using WAN Bridge: ${BGN}$BRG${CL}"
|
|
prompt_router_input "LAN_BRG" "LAN BRIDGE" "Set a LAN Bridge" "vmbr0"
|
|
echo -e "${DGN}Using LAN Bridge: ${BGN}$LAN_BRG${CL}"
|
|
prompt_router_ip "LAN_IP_ADDR" "LAN IP ADDRESS" "Set a router IP" "${LAN_IP_ADDR:-192.168.1.1}" "LAN IP ADDRESS"
|
|
prompt_router_ip "LAN_NETMASK" "LAN NETMASK" "Set a router netmask" "${LAN_NETMASK:-255.255.255.0}" "LAN NETMASK"
|
|
prompt_router_mac "MAC" "WAN MAC ADDRESS" "Set a WAN MAC Address" "$GEN_MAC" "WAN MAC address"
|
|
prompt_router_mac "LAN_MAC" "LAN MAC ADDRESS" "Set a LAN MAC Address" "$GEN_MAC_LAN" "LAN MAC address"
|
|
prompt_router_vlan "VLAN" "VLAN1" "WAN VLAN" "Set a WAN Vlan (leave blank for default)" ""
|
|
prompt_router_vlan "LAN_VLAN" "VLAN2" "LAN VLAN" "Set a LAN Vlan" "999"
|
|
prompt_router_mtu
|
|
vm_prompt_start_vm "yes"
|
|
|
|
if vm_confirm_advanced_settings "Ready to create OpenWrt VM?"; then
|
|
echo -e "${RD}Creating a OpenWrt VM using the above advanced settings${CL}"
|
|
else
|
|
header_info
|
|
echo -e "${RD}Using Advanced Settings${CL}"
|
|
advanced_settings
|
|
fi
|
|
}
|
|
|
|
vm_start_script "Use Default Settings?\n\nDefaults:\n• 1 CPU Core\n• 256 MB RAM\n• 1 GB Disk" 13 58
|
|
post_to_api_vm
|
|
|
|
vm_select_storage "$HN"
|
|
msg_info "Getting URL for OpenWrt Disk Image"
|
|
|
|
vm_latest_from_index "https://openwrt.org" 'Current stable release - OpenWrt \K[0-9]+\.[0-9]+\.[0-9]+' || exit 115
|
|
var_version="$VM_INDEX_LATEST"
|
|
URL="https://downloads.openwrt.org/releases/$var_version/targets/x86/64/openwrt-$var_version-x86-64-generic-ext4-combined.img.gz"
|
|
|
|
msg_ok "${CL}${BL}${URL}${CL}"
|
|
# A mirror serving an error page returns 200, so size decides whether this
|
|
# is an image. Anything real here is far above 5 MB.
|
|
CACHE_FILE="$(vm_image_cache_path "$URL")"
|
|
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((5 * 1024 * 1024)) || exit 115
|
|
|
|
FILE="$TEMP_DIR/$(basename "${CACHE_FILE%.gz}")"
|
|
vm_extract_image "$CACHE_FILE" "$FILE" || exit 115
|
|
FILE="$VM_IMAGE_FILE"
|
|
|
|
vm_claim_vmid
|
|
msg_info "Creating OpenWrt VM"
|
|
qm create $VMID -cores $CORE_COUNT -memory $RAM_SIZE -name $HN \
|
|
-onboot 1 -ostype l26 -scsihw virtio-scsi-pci --tablet 0 >/dev/null
|
|
vm_mark_created
|
|
vm_import_disk "$VMID" "$FILE" "$STORAGE"
|
|
|
|
$STD qm set $VMID \
|
|
-scsi0 "${VM_IMPORTED_DISK}" \
|
|
-boot order=scsi0 \
|
|
-tags community-script
|
|
msg_ok "Attached disk"
|
|
|
|
msg_info "Resizing disk to ${DISK_SIZE}"
|
|
qm disk resize "$VMID" scsi0 "${DISK_SIZE}" >/dev/null
|
|
msg_ok "Resized disk to ${DISK_SIZE}"
|
|
|
|
set_description
|
|
msg_ok "Created OpenWrt VM ${CL}${BL}(${HN})"
|
|
|
|
msg_info "Booting OpenWrt to configure its network interfaces"
|
|
$STD qm start $VMID
|
|
sleep 15
|
|
VM_STATE=""
|
|
for i in {1..30}; do
|
|
if ! VM_STATE="$(qm status "$VMID" 2>&1)"; then
|
|
msg_error "VM $VMID no longer exists: ${VM_STATE}"
|
|
exit 226
|
|
fi
|
|
[[ "$VM_STATE" == *running* ]] && break
|
|
sleep 1
|
|
done
|
|
|
|
if [[ "$VM_STATE" != *running* ]]; then
|
|
msg_error "VM $VMID did not reach running state: ${VM_STATE}"
|
|
exit 226
|
|
fi
|
|
sleep 5
|
|
msg_ok "OpenWrt is running"
|
|
|
|
msg_info "Configuring network interfaces in OpenWrt"
|
|
$STD send_line_to_vm ""
|
|
$STD send_line_to_vm "uci delete network.@device[0]"
|
|
$STD send_line_to_vm "uci set network.wan=interface"
|
|
$STD send_line_to_vm "uci set network.wan.device=eth1"
|
|
$STD send_line_to_vm "uci set network.wan.proto=dhcp"
|
|
$STD send_line_to_vm "uci delete network.lan"
|
|
$STD send_line_to_vm "uci set network.lan=interface"
|
|
$STD send_line_to_vm "uci set network.lan.device=eth0"
|
|
$STD send_line_to_vm "uci set network.lan.proto=static"
|
|
$STD send_line_to_vm "uci set network.lan.ipaddr=${LAN_IP_ADDR}"
|
|
$STD send_line_to_vm "uci set network.lan.netmask=${LAN_NETMASK}"
|
|
$STD send_line_to_vm "uci commit"
|
|
$STD send_line_to_vm "poweroff"
|
|
msg_ok "Network interfaces configured in OpenWrt"
|
|
|
|
msg_info "Waiting for OpenWrt to shut down"
|
|
for i in {1..60}; do
|
|
VM_STATE="$(qm status "$VMID" 2>&1)" || break
|
|
[[ "$VM_STATE" == *stopped* ]] && break
|
|
sleep 2
|
|
done
|
|
if [[ "$VM_STATE" != *stopped* ]]; then
|
|
msg_error "OpenWrt did not shut down: ${VM_STATE}"
|
|
exit 226
|
|
fi
|
|
msg_ok "OpenWrt has shut down"
|
|
|
|
msg_info "Adding bridge interfaces on Proxmox side"
|
|
qm set $VMID \
|
|
-net0 virtio,bridge=${LAN_BRG},macaddr=${LAN_MAC}${LAN_VLAN}${MTU} \
|
|
-net1 virtio,bridge=${BRG},macaddr=${MAC}${VLAN}${MTU} >/dev/null
|
|
msg_ok "Bridge interfaces added"
|
|
|
|
if [ "$START_VM" = "yes" ]; then
|
|
msg_info "Starting OpenWrt VM"
|
|
$STD qm start $VMID
|
|
msg_ok "Started OpenWrt VM"
|
|
fi
|
|
|
|
VLAN_FINISH=""
|
|
if [ -z "$VLAN" ] && [ "${VLAN2:-999}" != "999" ]; then
|
|
VLAN_FINISH=" Please remember to adjust the VLAN tags to suit your network."
|
|
fi
|
|
vm_print_summary \
|
|
"LAN URL=http://${LAN_IP_ADDR}" \
|
|
"WAN Bridge=${BRG}" \
|
|
"LAN Bridge=${LAN_BRG}" \
|
|
"OpenWrt Version=${var_version}"
|
|
vm_next_steps \
|
|
"Open the LAN web UI at http://${LAN_IP_ADDR}." \
|
|
"Login as root with a blank initial password, then set a strong password immediately." \
|
|
"Keep WAN and LAN isolated appropriately; the defaults put both on vmbr0 until you adjust bridges/VLANs.${VLAN_FINISH}"
|
|
vm_finish
|