mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-10-09 12:15:21 +00:00
Compare commits
66
Commits
@@ -559,6 +559,105 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
</details>
|
||||
|
||||
## 2026-10-08
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
- Unifi-OS-Server VM ([#17752](https://github.com/community-scripts/ProxmoxVE/pull/17752))
|
||||
- ZimaOS VM ([#17778](https://github.com/community-scripts/ProxmoxVE/pull/17778))
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- Revert "Immich: Pin to v3.3.0" [@MickLesk](https://github.com/MickLesk) ([#17767](https://github.com/community-scripts/ProxmoxVE/pull/17767))
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- Homarr: replace the musl better-sqlite3 that v2.3.0 ships for Debian [@MickLesk](https://github.com/MickLesk) ([#17789](https://github.com/community-scripts/ProxmoxVE/pull/17789))
|
||||
- CLIProxyAPI: keep plugins and data across updates [@MickLesk](https://github.com/MickLesk) ([#17790](https://github.com/community-scripts/ProxmoxVE/pull/17790))
|
||||
- Kima-Hub: install the Python services into a uv venv [@MickLesk](https://github.com/MickLesk) ([#17791](https://github.com/community-scripts/ProxmoxVE/pull/17791))
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- Immich: Pin to 3.3.0 / Update ML Python to 3.13 [@MickLesk](https://github.com/MickLesk) ([#17770](https://github.com/community-scripts/ProxmoxVE/pull/17770))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- Refactor: OPNsense VM [@MickLesk](https://github.com/MickLesk) ([#17785](https://github.com/community-scripts/ProxmoxVE/pull/17785))
|
||||
- Refactor: Nextcloud VM (Turnkey) [@MickLesk](https://github.com/MickLesk) ([#17787](https://github.com/community-scripts/ProxmoxVE/pull/17787))
|
||||
- Refactor: OpenWrt VM [@MickLesk](https://github.com/MickLesk) ([#17774](https://github.com/community-scripts/ProxmoxVE/pull/17774))
|
||||
- Refactor: Ubuntu VM [@MickLesk](https://github.com/MickLesk) ([#17776](https://github.com/community-scripts/ProxmoxVE/pull/17776))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- Tolerate a missing datacenter.cfg in vm_keyboard_default [@MickLesk](https://github.com/MickLesk) ([core#124](https://github.com/community-scripts/core/pull/124))
|
||||
- Take the ISO storage from the disk pool when it can hold ISOs [@MickLesk](https://github.com/MickLesk) ([core#122](https://github.com/community-scripts/core/pull/122))
|
||||
- Shared VM helpers: disk import, releases, checksums, first boot, IP by MAC [@MickLesk](https://github.com/MickLesk) ([core#117](https://github.com/community-scripts/core/pull/117))
|
||||
- tools: forge truncated release list [@MickLesk](https://github.com/MickLesk) ([core#118](https://github.com/community-scripts/core/pull/118))
|
||||
- Remember a kept cached image until the upstream changes [@MickLesk](https://github.com/MickLesk) ([core#119](https://github.com/community-scripts/core/pull/119))
|
||||
- Choose the VM keyboard layout and carry the host's timezone into prepared images [@MickLesk](https://github.com/MickLesk) ([core#120](https://github.com/community-scripts/core/pull/120))
|
||||
- VM's: run first-boot units without debconf dialogs [@MickLesk](https://github.com/MickLesk) ([core#121](https://github.com/community-scripts/core/pull/121))
|
||||
|
||||
## 2026-10-07
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
- Fedora VM ([#17747](https://github.com/community-scripts/ProxmoxVE/pull/17747))
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- AudioMuse-AI: replace the venv on update without asking [@MickLesk](https://github.com/MickLesk) ([#17738](https://github.com/community-scripts/ProxmoxVE/pull/17738))
|
||||
- paperclip: run as a dedicated non-root user so Claude Code can skip permissions [@samvandenbossche](https://github.com/samvandenbossche) ([#17707](https://github.com/community-scripts/ProxmoxVE/pull/17707))
|
||||
- Thingsboard: update Java version from 17 to 25 [@CerberusStyle](https://github.com/CerberusStyle) ([#17733](https://github.com/community-scripts/ProxmoxVE/pull/17733))
|
||||
|
||||
- #### ✨ New Features
|
||||
|
||||
- Immich: Pin to v3.3.0 [@vhsdream](https://github.com/vhsdream) ([#17759](https://github.com/community-scripts/ProxmoxVE/pull/17759))
|
||||
|
||||
- #### 💥 Breaking Changes
|
||||
|
||||
- OpenCloud: bump to v8.1.0, rebuild search index on upgrade [@SimKaiLong](https://github.com/SimKaiLong) ([#17710](https://github.com/community-scripts/ProxmoxVE/pull/17710))
|
||||
|
||||
- #### 🔧 Refactor
|
||||
|
||||
- Pangolin: Unpin Release, stable enough, Bump to latest [@MickLesk](https://github.com/MickLesk) ([#17740](https://github.com/community-scripts/ProxmoxVE/pull/17740))
|
||||
- Refactor: Archlinux-VM [@MickLesk](https://github.com/MickLesk) ([#17741](https://github.com/community-scripts/ProxmoxVE/pull/17741))
|
||||
- Refactor: MikroTik RouterOS [@MickLesk](https://github.com/MickLesk) ([#17748](https://github.com/community-scripts/ProxmoxVE/pull/17748))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- Fix Fedora and BLS cloud image console setup [@MickLesk](https://github.com/MickLesk) ([core#116](https://github.com/community-scripts/core/pull/116))
|
||||
- Keep setup_nodejs alive when the caller's directory is gone [@MickLesk](https://github.com/MickLesk) ([core#114](https://github.com/community-scripts/core/pull/114))
|
||||
|
||||
## 2026-10-06
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
- Pricebuddy ([#17708](https://github.com/community-scripts/ProxmoxVE/pull/17708))
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- Webtrees: stop serving data/ and the source folders directly [@MickLesk](https://github.com/MickLesk) ([#17724](https://github.com/community-scripts/ProxmoxVE/pull/17724))
|
||||
|
||||
### 💾 Core
|
||||
|
||||
- Incus: check the architecture on Incus hosts too [@MickLesk](https://github.com/MickLesk) ([core#113](https://github.com/community-scripts/core/pull/113))
|
||||
- Incus: Pass the app's var_* settings into Incus containers [@MickLesk](https://github.com/MickLesk) ([core#112](https://github.com/community-scripts/core/pull/112))
|
||||
- Incus: stub storage_content_check on Incus [@MickLesk](https://github.com/MickLesk) ([core#104](https://github.com/community-scripts/core/pull/104))
|
||||
- Incus: map Proxmox template versions to Incus image names [@MickLesk](https://github.com/MickLesk) ([core#111](https://github.com/community-scripts/core/pull/111))
|
||||
- Incus: read storage names from .vars files [@MickLesk](https://github.com/MickLesk) ([core#110](https://github.com/community-scripts/core/pull/110))
|
||||
- Incus: show the spinner inside containers again [@MickLesk](https://github.com/MickLesk) ([core#109](https://github.com/community-scripts/core/pull/109))
|
||||
- Incus: set the hostname with sh, so it works before bash is installed [@MickLesk](https://github.com/MickLesk) ([core#102](https://github.com/community-scripts/core/pull/102))
|
||||
- Incus: reserve a plain address on Incus, leave the gateway to the network [@MickLesk](https://github.com/MickLesk) ([core#101](https://github.com/community-scripts/core/pull/101))
|
||||
- Incus: Wait for the network on Alpine OS too [@MickLesk](https://github.com/MickLesk) ([core#105](https://github.com/community-scripts/core/pull/105))
|
||||
- Incus: do not fail the build when hostname -I is missing [@MickLesk](https://github.com/MickLesk) ([core#103](https://github.com/community-scripts/core/pull/103))
|
||||
- Incus: Fuse always in unprivileged Containers, attach TUN only when container lacks it [@MickLesk](https://github.com/MickLesk) ([core#108](https://github.com/community-scripts/core/pull/108))
|
||||
- Incus: accept mode=generated [@MickLesk](https://github.com/MickLesk) ([core#106](https://github.com/community-scripts/core/pull/106))
|
||||
- Incus: pass the full install environment into Incus containers (quoted) [@MickLesk](https://github.com/MickLesk) ([core#107](https://github.com/community-scripts/core/pull/107))
|
||||
|
||||
## 2026-10-05
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ function update_script() {
|
||||
|
||||
msg_info "Updating Python Environment (${AUDIOMUSE_BACKEND})"
|
||||
cd /opt/audiomuse-ai
|
||||
$STD uv venv --seed --python "$PY_VERSION" /opt/audiomuse-ai/.venv
|
||||
$STD uv venv --clear --seed --python "$PY_VERSION" /opt/audiomuse-ai/.venv
|
||||
$STD uv pip install --python /opt/audiomuse-ai/.venv \
|
||||
-r "/opt/audiomuse-ai/requirements/${REQ_COMMON}" \
|
||||
-r "/opt/audiomuse-ai/requirements/${REQ_ACCEL}"
|
||||
|
||||
+6
-1
@@ -48,7 +48,12 @@ function update_script() {
|
||||
|
||||
$STD corepack prepare "pnpm@${PNPM_VERSION}" --activate
|
||||
$STD pnpm install --frozen-lockfile
|
||||
$STD pnpm --filter client run build-only
|
||||
# vite's bundler (rolldown) crashes at random on some builds, rolldown#10860.
|
||||
for attempt in 1 2 3; do
|
||||
$STD pnpm --filter client run build-only && break
|
||||
[[ $attempt -eq 3 ]] && { msg_error "Client build failed three times"; exit 1; }
|
||||
msg_warn "Client build failed (attempt $attempt), retrying"
|
||||
done
|
||||
$STD pnpm --filter server run build
|
||||
cp -r /opt/bookorbit/client/dist /opt/bookorbit/server/public
|
||||
mkdir -p /opt/bookorbit/server/migrations
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ function update_script() {
|
||||
|
||||
create_backup /opt/cliproxyapi/config.yaml
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "cliproxyapi" "router-for-me/CLIProxyAPI" "prebuild" "latest" "/opt/cliproxyapi" "CLIProxyAPI_*_linux_$(arch_resolve "amd64" "aarch64").tar.gz"
|
||||
CLEAN_INSTALL=1 CLEAN_INSTALL_KEEP="plugins data" fetch_and_deploy_gh_release "cliproxyapi" "router-for-me/CLIProxyAPI" "prebuild" "latest" "/opt/cliproxyapi" "CLIProxyAPI_*_linux_$(arch_resolve "amd64" "aarch64").tar.gz"
|
||||
|
||||
restore_backup
|
||||
|
||||
|
||||
@@ -85,6 +85,18 @@ EOF
|
||||
msg_ok "Started Services"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
# v2.3.0's Debian build ships a musl better-sqlite3 (homarr-labs/homarr#7097).
|
||||
mapfile -t MUSL_MODULES < <(find /opt/homarr -name better_sqlite3.node -exec grep -aqE "ld-musl|libc\.musl" {} \; -print)
|
||||
if ((${#MUSL_MODULES[@]})); then
|
||||
msg_info "Replacing musl better-sqlite3 build"
|
||||
BSQ_VERSION=$(jq -r .version /opt/homarr/node_modules/better-sqlite3/package.json)
|
||||
curl_with_retry "https://github.com/WiseLibs/better-sqlite3/releases/download/v${BSQ_VERSION}/better-sqlite3-v${BSQ_VERSION}-node-v$(node -p process.versions.modules)-linux-$(arch_resolve "x64" "arm64").tar.gz" /tmp/better-sqlite3.tar.gz
|
||||
tar -xzf /tmp/better-sqlite3.tar.gz -C /tmp build/Release/better_sqlite3.node
|
||||
for module in "${MUSL_MODULES[@]}"; do cp /tmp/build/Release/better_sqlite3.node "$module"; done
|
||||
rm -rf /tmp/better-sqlite3.tar.gz /tmp/build
|
||||
systemctl restart homarr
|
||||
msg_ok "Replaced musl better-sqlite3 build"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
|
||||
+41
-27
@@ -77,7 +77,7 @@ EOF
|
||||
BASE_DIR=${STAGING_DIR}/base-images
|
||||
SOURCE_DIR=${STAGING_DIR}/image-source
|
||||
cd /tmp
|
||||
RELEASE="v3.2.4"
|
||||
RELEASE="v3.3.0"
|
||||
if [[ -f ~/.intel_version ]]; then
|
||||
INTEL_SRC="https://raw.githubusercontent.com/immich-app/immich/${RELEASE}/machine-learning"
|
||||
curl -fsSL "${INTEL_SRC}/scripts/install-intel-runtime.sh" -o ./intel-runtime 2>/dev/null ||
|
||||
@@ -115,7 +115,10 @@ EOF
|
||||
fi
|
||||
|
||||
if check_for_gh_release "Immich" "immich-app/immich" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then
|
||||
if [[ $(cat ~/.immich) > "2.5.1" ]]; then
|
||||
msg_warn "The update takes 5-15 minutes, do not interrupt it"
|
||||
PREV_IMMICH="$(cat ~/.immich)"
|
||||
# An interrupted update leaves app/ empty, so there may be no immich-admin to call.
|
||||
if [[ "$PREV_IMMICH" > "2.5.1" && -x /opt/immich/app/bin/immich-admin ]]; then
|
||||
msg_info "Enabling Maintenance Mode"
|
||||
cd /opt/immich/app/bin
|
||||
$STD ./immich-admin enable-maintenance-mode || true
|
||||
@@ -273,13 +276,16 @@ EOF
|
||||
msg_ok "Pre-installed Python ${ML_PYTHON}"
|
||||
msg_info "Updating Intel OpenVINO machine-learning"
|
||||
for attempt in $(seq 1 3); do
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -Pnu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
|
||||
[[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT,UV_CONCURRENT_DOWNLOADS,UV_CONCURRENT_BUILDS,UV_CONCURRENT_INSTALLS -Pnu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
|
||||
[[ $attempt -eq 3 ]] && { ML_FAILED=1; break; }
|
||||
# Parallel fetches can trip DNS rate limits (AdGuard, Pi-hole); retry one at a time.
|
||||
export UV_CONCURRENT_DOWNLOADS=1 UV_CONCURRENT_BUILDS=1 UV_CONCURRENT_INSTALLS=1
|
||||
msg_warn "uv sync attempt $attempt failed, retrying one download at a time..." && sleep 10
|
||||
done
|
||||
patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-$(arch_resolve "x86_64" "aarch64")-linux-gnu.so"
|
||||
msg_ok "Updated Intel OpenVINO machine-learning"
|
||||
[[ "${ML_FAILED:-0}" == 1 ]] || patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-$(arch_resolve "x86_64" "aarch64")-linux-gnu.so"
|
||||
[[ "${ML_FAILED:-0}" == 1 ]] || msg_ok "Updated Intel OpenVINO machine-learning"
|
||||
else
|
||||
ML_PYTHON="python3.11"
|
||||
ML_PYTHON="python3.13"
|
||||
msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning"
|
||||
for attempt in $(seq 1 3); do
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV -Pnu immich uv python install "${ML_PYTHON}" && break
|
||||
@@ -288,11 +294,15 @@ EOF
|
||||
msg_ok "Pre-installed Python ${ML_PYTHON}"
|
||||
msg_info "Updating machine-learning"
|
||||
for attempt in $(seq 1 3); do
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -Pnu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
|
||||
[[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT,UV_CONCURRENT_DOWNLOADS,UV_CONCURRENT_BUILDS,UV_CONCURRENT_INSTALLS -Pnu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
|
||||
[[ $attempt -eq 3 ]] && { ML_FAILED=1; break; }
|
||||
# Parallel fetches can trip DNS rate limits (AdGuard, Pi-hole); retry one at a time.
|
||||
export UV_CONCURRENT_DOWNLOADS=1 UV_CONCURRENT_BUILDS=1 UV_CONCURRENT_INSTALLS=1
|
||||
msg_warn "uv sync attempt $attempt failed, retrying one download at a time..." && sleep 10
|
||||
done
|
||||
msg_ok "Updated machine-learning"
|
||||
[[ "${ML_FAILED:-0}" == 1 ]] || msg_ok "Updated machine-learning"
|
||||
fi
|
||||
[[ "${ML_FAILED:-0}" == 1 ]] && msg_warn "uv sync failed three times, machine learning was not updated"
|
||||
cd "$SRC_DIR"
|
||||
cp -a machine-learning/{ann,immich_ml} "$ML_DIR"
|
||||
[[ -f "$INSTALL_DIR"/ml_start.sh ]] && mv "$INSTALL_DIR"/ml_start.sh "$ML_DIR"
|
||||
@@ -333,33 +343,28 @@ EOF
|
||||
sed -i -e '$a\' "$INSTALL_DIR"/.env
|
||||
echo "IMMICH_HELMET_FILE=true" >>"$INSTALL_DIR"/.env
|
||||
fi
|
||||
if ! grep -q 'MODEL_REVISION' "$INSTALL_DIR"/.env; then
|
||||
sed -i -e '$a\' "$INSTALL_DIR"/.env
|
||||
echo "MACHINE_LEARNING_MODEL_REVISION=v2" >>"$INSTALL_DIR"/.env
|
||||
fi
|
||||
|
||||
if grep -q 'ExecStart=/usr/bin/node' /etc/systemd/system/immich-web.service; then
|
||||
sed -i '/^EnvironmentFile=/d' /etc/systemd/system/immich-web.service
|
||||
sed -i "s|^ExecStart=.*|ExecStart=${APP_DIR}/bin/start.sh|" /etc/systemd/system/immich-web.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
if grep -q "^ExecStart=${INSTALL_DIR}/ml_start.sh" /etc/systemd/system/immich-ml.service; then
|
||||
sed -i "s|^ExecStart=.*|ExecStart=${ML_DIR}/ml_start.sh|" /etc/systemd/system/immich-ml.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
# MickLesk temporary patch for HEIC thumbnail gen
|
||||
msg_info "Patching media.repository.js"
|
||||
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
|
||||
if [[ -f "$MEDIA_REPO_JS" ]]; then
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
p = Path('/opt/immich/app/dist/repositories/media.repository.js')
|
||||
s = p.read_text()
|
||||
old = "(0, sharp_1.default)(input).metadata()"
|
||||
new = "(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()"
|
||||
if new in s:
|
||||
print('hotfix already there')
|
||||
elif old in s:
|
||||
p.write_text(s.replace(old, new, 1))
|
||||
print('hotfix applied')
|
||||
else:
|
||||
print('pattern not found, skipped')
|
||||
PY
|
||||
if grep -qF "(0, sharp_1.default)(input).metadata()" "$MEDIA_REPO_JS" 2>/dev/null; then
|
||||
msg_info "Patching media.repository.js"
|
||||
sed -i '0,/(0, sharp_1\.default)(input)\.metadata()/s//(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()/' "$MEDIA_REPO_JS"
|
||||
msg_ok "Patched media.repository.js"
|
||||
fi
|
||||
msg_ok "Patched media.repository.js"
|
||||
|
||||
# chown excluding upload dir contents (may be a mount with restricted permissions)
|
||||
chown immich:immich "$INSTALL_DIR"
|
||||
@@ -373,6 +378,15 @@ PY
|
||||
$STD cd -
|
||||
msg_ok "Disabled Maintenance Mode"
|
||||
fi
|
||||
# A crash loop before the update leaves the units rate-limited, and restart would refuse.
|
||||
systemctl reset-failed immich-ml immich-web 2>/dev/null || true
|
||||
if [[ "${ML_FAILED:-0}" == 1 ]]; then
|
||||
systemctl restart immich-web || true
|
||||
[[ -f /etc/systemd/system/immich-proxy.service ]] && systemctl restart immich-proxy
|
||||
echo "$PREV_IMMICH" >~/.immich
|
||||
msg_error "Immich runs, but without machine learning (see the uv output above). Run update again to retry."
|
||||
exit 1
|
||||
fi
|
||||
systemctl restart immich-ml immich-web
|
||||
[[ -f /etc/systemd/system/immich-proxy.service ]] && systemctl restart immich-proxy
|
||||
msg_ok "Updated successfully!"
|
||||
|
||||
+30
-1
@@ -31,8 +31,9 @@ function update_script() {
|
||||
exit
|
||||
fi
|
||||
|
||||
RELEASE="v7.4.0"
|
||||
RELEASE="v8.1.0"
|
||||
if check_for_gh_release "OpenCloud" "opencloud-eu/opencloud" "${RELEASE}" "each release is tested individually before the version is updated. Please do not open issues for this"; then
|
||||
OLD_VERSION="$(cat ~/.opencloud 2>/dev/null)"
|
||||
msg_info "Stopping services"
|
||||
systemctl stop opencloud opencloud-wopi
|
||||
msg_ok "Stopped services"
|
||||
@@ -70,6 +71,34 @@ function update_script() {
|
||||
msg_info "Starting services"
|
||||
systemctl start opencloud opencloud-wopi
|
||||
msg_ok "Started services"
|
||||
|
||||
if [[ -z "$OLD_VERSION" || "${OLD_VERSION#v}" =~ ^[0-7]\. ]]; then
|
||||
# The index CLI cancels the rebuild when interrupted, so it runs as its own unit and the
|
||||
# update only waits for it. Restart covers a search service that is still starting.
|
||||
msg_info "Rebuilding search index (safe to interrupt, it continues in the background)"
|
||||
REINDEX_START="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
systemctl reset-failed opencloud-reindex &>/dev/null || true
|
||||
$STD systemd-run --unit=opencloud-reindex --uid=opencloud --gid=opencloud \
|
||||
-p EnvironmentFile=/etc/opencloud/opencloud.env -p Restart=on-failure -p RestartSec=15 \
|
||||
-p StartLimitIntervalSec=900 -p StartLimitBurst=20 \
|
||||
/usr/bin/opencloud search index --all-spaces --force-rescan --insecure
|
||||
while [[ "$(systemctl show -p ActiveState --value opencloud-reindex)" =~ ^(active|activating)$ ]]; do
|
||||
sleep 10
|
||||
done
|
||||
if [[ "$(systemctl show -p ActiveState --value opencloud-reindex)" == "failed" ]]; then
|
||||
msg_ok "Stopped waiting for the search index rebuild"
|
||||
msg_warn "The rebuild did not complete, see: journalctl -u opencloud-reindex"
|
||||
msg_warn "Retry with: systemctl reset-failed opencloud-reindex; systemd-run --unit=opencloud-reindex \
|
||||
--uid=opencloud --gid=opencloud -p EnvironmentFile=/etc/opencloud/opencloud.env \
|
||||
/usr/bin/opencloud search index --all-spaces --force-rescan --insecure"
|
||||
else
|
||||
msg_ok "Rebuilt search index"
|
||||
if journalctl -u opencloud-reindex --since "$REINDEX_START" --no-pager | grep -qE '/[0-9]+ ERROR'; then
|
||||
msg_warn "Some spaces could not be indexed, see: journalctl -u opencloud-reindex"
|
||||
fi
|
||||
msg_warn "Once search finds older files, remove the old index: rm -rf /var/lib/opencloud/search/bleve"
|
||||
fi
|
||||
fi
|
||||
msg_ok "Updated successfully"
|
||||
fi
|
||||
exit
|
||||
|
||||
+3
-4
@@ -7,7 +7,6 @@ source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_
|
||||
# Source: https://pangolin.net/ | Github: https://github.com/fosrl/pangolin
|
||||
|
||||
APP="Pangolin"
|
||||
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.23.0}"
|
||||
var_tags="${var_tags:-proxy}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-4096}"
|
||||
@@ -38,7 +37,7 @@ function update_script() {
|
||||
ensure_dependencies build-essential python3
|
||||
|
||||
if ! command -v psql &>/dev/null; then
|
||||
msg_error "This installation uses SQLite and cannot be upgraded to Pangolin ${PANGOLIN_VERSION}."
|
||||
msg_error "This installation uses SQLite and cannot be upgraded to latest Pangolin."
|
||||
echo -e "${INFO}${YW}Starting with Pangolin 1.20.0, PostgreSQL is required as the database backend.${CL}"
|
||||
echo -e "${INFO}${YW}An automatic migration of your existing SQLite data is not supported.${CL}"
|
||||
echo -e "${INFO}${YW}Please create a new LXC with the Pangolin install script, which sets up PostgreSQL automatically.${CL}"
|
||||
@@ -48,7 +47,7 @@ function update_script() {
|
||||
|
||||
NODE_VERSION="24" setup_nodejs
|
||||
|
||||
if check_for_gh_release "pangolin" "fosrl/pangolin" "$PANGOLIN_VERSION" "Pinned to a tested release because Pangolin's schema changes have repeatedly broken unattended updates. To try a newer version at your own risk, run: 'export PANGOLIN_VERSION=<tag>' and re-run update. If it breaks, please open an issue at https://github.com/community-scripts/ProxmoxVE/issues with the error log."; then
|
||||
if check_for_gh_release "pangolin" "fosrl/pangolin"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop pangolin
|
||||
systemctl stop gerbil
|
||||
@@ -57,7 +56,7 @@ function update_script() {
|
||||
DB_URL=$(sed -n 's/.*connection_string: "\(.*\)".*/\1/p' /opt/pangolin/config/config.yml)
|
||||
create_backup /opt/pangolin/config
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION"
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball"
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
|
||||
|
||||
msg_info "Updating Pangolin"
|
||||
|
||||
+29
-1
@@ -15,6 +15,8 @@ var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_arm64="${var_arm64:-yes}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
export var_paperclip_user="${var_paperclip_user:-}"
|
||||
export var_paperclip_pass="${var_paperclip_pass:-}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
@@ -59,9 +61,35 @@ function update_script() {
|
||||
@openai/codex@latest
|
||||
msg_ok "Updated Agent CLIs"
|
||||
|
||||
# Claude Code refuses --dangerously-skip-permissions as root; migrate existing installs to a dedicated user
|
||||
PAPERCLIP_USER=$(sed -n 's/^User=//p' /etc/systemd/system/paperclip.service)
|
||||
if [[ -z "$PAPERCLIP_USER" || "$PAPERCLIP_USER" == "root" ]]; then
|
||||
PAPERCLIP_USER="${var_paperclip_user:-paperclip}"
|
||||
if [[ "$PAPERCLIP_USER" == "root" || ! "$PAPERCLIP_USER" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]; then
|
||||
msg_error "Invalid var_paperclip_user '${PAPERCLIP_USER}' (must be a non-root lowercase Linux username)"
|
||||
exit 1
|
||||
fi
|
||||
PAPERCLIP_USER_HOME="/home/${PAPERCLIP_USER}"
|
||||
id -u "$PAPERCLIP_USER" &>/dev/null || useradd -m -d "$PAPERCLIP_USER_HOME" -s /bin/bash "$PAPERCLIP_USER"
|
||||
passwd -S "$PAPERCLIP_USER" 2>/dev/null | grep -q " P " || passwd -l "$PAPERCLIP_USER" &>/dev/null
|
||||
mkdir -p "${PAPERCLIP_USER_HOME}/.claude" "${PAPERCLIP_USER_HOME}/.codex"
|
||||
[[ -d /root/.claude ]] && cp -a /root/.claude/. "${PAPERCLIP_USER_HOME}/.claude/"
|
||||
[[ -d /root/.codex ]] && cp -a /root/.codex/. "${PAPERCLIP_USER_HOME}/.codex/"
|
||||
sed -i \
|
||||
-e "s|^User=.*|User=${PAPERCLIP_USER}\nGroup=${PAPERCLIP_USER}|" \
|
||||
-e "s|^Environment=HOME=.*|Environment=HOME=${PAPERCLIP_USER_HOME}|" \
|
||||
-e "s|^Environment=CODEX_HOME=.*|Environment=CODEX_HOME=${PAPERCLIP_USER_HOME}/.codex|" \
|
||||
-e "s|/root/.local/bin|${PAPERCLIP_USER_HOME}/.local/bin|" \
|
||||
/etc/systemd/system/paperclip.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
PAPERCLIP_USER_HOME=$(getent passwd "$PAPERCLIP_USER" | cut -d: -f6)
|
||||
chmod 600 /opt/paperclip-ai/.env
|
||||
chown -R "${PAPERCLIP_USER}:${PAPERCLIP_USER}" /opt/paperclip-ai /opt/paperclip-data "$PAPERCLIP_USER_HOME"
|
||||
|
||||
msg_info "Running Database Migrations"
|
||||
set -a && source /opt/paperclip-ai/.env && set +a
|
||||
$STD pnpm db:migrate
|
||||
$STD runuser -u "$PAPERCLIP_USER" -- env HOME="$PAPERCLIP_USER_HOME" bash -c 'cd /opt/paperclip-ai && pnpm db:migrate'
|
||||
msg_ok "Ran Database Migrations"
|
||||
|
||||
msg_info "Starting Service"
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env bash
|
||||
_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func"
|
||||
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/jez500/pricebuddy
|
||||
|
||||
APP="PriceBuddy"
|
||||
var_tags="${var_tags:-shopping;price-tracker}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-3072}"
|
||||
var_disk="${var_disk:-10}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_arm64="${var_arm64:-no}" # the bundled scraper runs Google Chrome, which is amd64-only
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/pricebuddy ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "pricebuddy" "jez500/pricebuddy"; then
|
||||
msg_info "Stopping PriceBuddy Worker"
|
||||
systemctl stop pricebuddy-worker
|
||||
msg_ok "Stopped PriceBuddy Worker"
|
||||
|
||||
setup_composer
|
||||
NODE_VERSION="22" setup_nodejs
|
||||
|
||||
create_backup /opt/pricebuddy/.env /opt/pricebuddy/storage
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "pricebuddy" "jez500/pricebuddy" "tarball"
|
||||
|
||||
restore_backup
|
||||
|
||||
msg_info "Updating PriceBuddy"
|
||||
cd /opt/pricebuddy
|
||||
sed -i "s/^APP_VERSION=.*/APP_VERSION=$(cat ~/.pricebuddy)/" .env
|
||||
$STD composer install --no-dev --optimize-autoloader --no-interaction
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
$STD php artisan storage:link
|
||||
$STD php artisan migrate --force
|
||||
$STD php artisan optimize
|
||||
$STD php artisan icons:cache
|
||||
$STD php artisan buddy:regenerate-price-cache
|
||||
chown -R www-data:www-data /opt/pricebuddy
|
||||
msg_ok "Updated PriceBuddy"
|
||||
|
||||
msg_info "Starting PriceBuddy Worker"
|
||||
systemctl start pricebuddy-worker
|
||||
msg_ok "Started PriceBuddy Worker"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
|
||||
if check_for_gh_release "seleniumbase-scrapper" "jez500/seleniumbase-scrapper"; then
|
||||
msg_info "Stopping SeleniumBase Scrapper"
|
||||
systemctl stop seleniumbase-scrapper
|
||||
msg_ok "Stopped SeleniumBase Scrapper"
|
||||
|
||||
msg_info "Updating Google Chrome"
|
||||
apt_update_safe
|
||||
upgrade_packages_with_retry google-chrome-stable
|
||||
msg_ok "Updated Google Chrome"
|
||||
|
||||
PYTHON_VERSION="3.12" setup_uv
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "seleniumbase-scrapper" "jez500/seleniumbase-scrapper" "tarball"
|
||||
|
||||
msg_info "Updating SeleniumBase Scrapper"
|
||||
$STD uv venv --python 3.12 /opt/seleniumbase-scrapper/.venv
|
||||
$STD uv pip install --python /opt/seleniumbase-scrapper/.venv/bin/python \
|
||||
"seleniumbase==$(sed -n 's/^ARG SELENIUMBASE_VERSION=v//p' /opt/seleniumbase-scrapper/Dockerfile)" \
|
||||
flask \
|
||||
beautifulsoup4
|
||||
$STD /opt/seleniumbase-scrapper/.venv/bin/seleniumbase get chromedriver
|
||||
msg_ok "Updated SeleniumBase Scrapper"
|
||||
|
||||
msg_info "Starting SeleniumBase Scrapper"
|
||||
systemctl start seleniumbase-scrapper
|
||||
msg_ok "Started SeleniumBase Scrapper"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed Successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW}Access it using the following URL:${CL}"
|
||||
echo -e "${GATEWAY}${BGN}http://${IP}${CL}"
|
||||
echo -e "${INFO}${YW}Log in as admin@example.com - the password is APP_USER_PASSWORD in /opt/pricebuddy/.env${CL}"
|
||||
@@ -30,6 +30,7 @@ function update_script() {
|
||||
exit
|
||||
fi
|
||||
|
||||
JAVA_VERSION="25" setup_java
|
||||
if check_for_gh_release "thingsboard" "thingsboard/thingsboard"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop thingsboard
|
||||
|
||||
@@ -31,6 +31,20 @@ function update_script() {
|
||||
exit
|
||||
fi
|
||||
|
||||
if ! grep -q "@private" /etc/caddy/Caddyfile; then
|
||||
msg_info "Blocking direct access to webtrees data"
|
||||
cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak
|
||||
sed -i '\|root \* /opt/webtrees|a\ @private path /app/* /data/* /modules_v4/* /resources/* /vendor/* /.*\n respond @private 403' /etc/caddy/Caddyfile
|
||||
if grep -q "@private" /etc/caddy/Caddyfile && caddy validate --config /etc/caddy/Caddyfile &>/dev/null; then
|
||||
rm -f /etc/caddy/Caddyfile.bak
|
||||
systemctl reload-or-restart caddy
|
||||
msg_ok "Blocked direct access to webtrees data"
|
||||
else
|
||||
mv /etc/caddy/Caddyfile.bak /etc/caddy/Caddyfile
|
||||
msg_warn "Could not patch /etc/caddy/Caddyfile - deny /data/ there by hand"
|
||||
fi
|
||||
fi
|
||||
|
||||
if check_for_gh_release "webtrees" "fisharebest/webtrees"; then
|
||||
msg_info "Stopping Service"
|
||||
PHP_VER=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
|
||||
|
||||
@@ -33,7 +33,12 @@ PNPM_VERSION=$(jq -r '.packageManager | ltrimstr("pnpm@")' /opt/bookorbit/packag
|
||||
|
||||
$STD corepack prepare "pnpm@${PNPM_VERSION}" --activate
|
||||
$STD pnpm install --frozen-lockfile
|
||||
$STD pnpm --filter client run build-only
|
||||
# vite's bundler (rolldown) crashes at random on some builds, rolldown#10860.
|
||||
for attempt in 1 2 3; do
|
||||
$STD pnpm --filter client run build-only && break
|
||||
[[ $attempt -eq 3 ]] && { msg_error "Client build failed three times"; exit 1; }
|
||||
msg_warn "Client build failed (attempt $attempt), retrying"
|
||||
done
|
||||
$STD pnpm --filter server run build
|
||||
cp -r /opt/bookorbit/client/dist /opt/bookorbit/server/public
|
||||
mkdir -p /opt/bookorbit/server/migrations
|
||||
|
||||
@@ -25,6 +25,18 @@ NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/
|
||||
setup_nodejs
|
||||
fetch_and_deploy_gh_release "homarr" "homarr-labs/homarr" "prebuild" "latest" "/opt/homarr" "build-debian-$(arch_resolve).tar.gz"
|
||||
|
||||
# v2.3.0's Debian build ships a musl better-sqlite3 (homarr-labs/homarr#7097).
|
||||
mapfile -t MUSL_MODULES < <(find /opt/homarr -name better_sqlite3.node -exec grep -aqE "ld-musl|libc\.musl" {} \; -print)
|
||||
if ((${#MUSL_MODULES[@]})); then
|
||||
msg_info "Replacing musl better-sqlite3 build"
|
||||
BSQ_VERSION=$(jq -r .version /opt/homarr/node_modules/better-sqlite3/package.json)
|
||||
curl_with_retry "https://github.com/WiseLibs/better-sqlite3/releases/download/v${BSQ_VERSION}/better-sqlite3-v${BSQ_VERSION}-node-v$(node -p process.versions.modules)-linux-$(arch_resolve "x64" "arm64").tar.gz" /tmp/better-sqlite3.tar.gz
|
||||
tar -xzf /tmp/better-sqlite3.tar.gz -C /tmp build/Release/better_sqlite3.node
|
||||
for module in "${MUSL_MODULES[@]}"; do cp /tmp/build/Release/better_sqlite3.node "$module"; done
|
||||
rm -rf /tmp/better-sqlite3.tar.gz /tmp/build
|
||||
msg_ok "Replaced musl better-sqlite3 build"
|
||||
fi
|
||||
|
||||
msg_info "Installing Homarr"
|
||||
mkdir -p /opt/homarr_db
|
||||
touch /opt/homarr_db/db.sqlite
|
||||
|
||||
+17
-23
@@ -13,7 +13,7 @@ setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
RELEASE="v3.2.4"
|
||||
RELEASE="v3.3.0"
|
||||
if lscpu | grep -q 'GenuineIntel'; then
|
||||
echo ""
|
||||
echo ""
|
||||
@@ -441,13 +441,16 @@ if [[ -f ~/.openvino ]]; then
|
||||
msg_ok "Pre-installed Python ${ML_PYTHON}"
|
||||
msg_info "Installing Intel OpenVINO machine-learning"
|
||||
for attempt in $(seq 1 3); do
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -Pnu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
|
||||
[[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT,UV_CONCURRENT_DOWNLOADS,UV_CONCURRENT_BUILDS,UV_CONCURRENT_INSTALLS -Pnu immich uv sync --extra openvino --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
|
||||
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
|
||||
# Parallel fetches can trip DNS rate limits (AdGuard, Pi-hole); retry one at a time.
|
||||
export UV_CONCURRENT_DOWNLOADS=1 UV_CONCURRENT_BUILDS=1 UV_CONCURRENT_INSTALLS=1
|
||||
msg_warn "uv sync attempt $attempt failed, retrying one download at a time..." && sleep 10
|
||||
done
|
||||
patchelf --clear-execstack "${VIRTUAL_ENV}/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_pybind11_state.cpython-313-$(arch_resolve "x86_64" "aarch64")-linux-gnu.so"
|
||||
msg_ok "Installed Intel OpenVINO machine-learning"
|
||||
else
|
||||
ML_PYTHON="python3.11"
|
||||
ML_PYTHON="python3.13"
|
||||
msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning"
|
||||
for attempt in $(seq 1 3); do
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV -Pnu immich uv python install "${ML_PYTHON}" && break
|
||||
@@ -456,8 +459,11 @@ else
|
||||
msg_ok "Pre-installed Python ${ML_PYTHON}"
|
||||
msg_info "Installing machine-learning"
|
||||
for attempt in $(seq 1 3); do
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT -Pnu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
|
||||
[[ $attempt -lt 3 ]] && msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
|
||||
$STD sudo --preserve-env=VIRTUAL_ENV,UV_HTTP_TIMEOUT,UV_CONCURRENT_DOWNLOADS,UV_CONCURRENT_BUILDS,UV_CONCURRENT_INSTALLS -Pnu immich uv sync --extra cpu --no-dev --active --link-mode copy -n -p "${ML_PYTHON}" --managed-python && break
|
||||
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
|
||||
# Parallel fetches can trip DNS rate limits (AdGuard, Pi-hole); retry one at a time.
|
||||
export UV_CONCURRENT_DOWNLOADS=1 UV_CONCURRENT_BUILDS=1 UV_CONCURRENT_INSTALLS=1
|
||||
msg_warn "uv sync attempt $attempt failed, retrying one download at a time..." && sleep 10
|
||||
done
|
||||
msg_ok "Installed machine-learning"
|
||||
fi
|
||||
@@ -488,25 +494,12 @@ ln -s "$GEO_DIR" "$APP_DIR"
|
||||
msg_ok "Installed GeoNames data"
|
||||
|
||||
# MickLesk temporary patch for HEIC thumbnail gen
|
||||
msg_info "Patching media.repository.js"
|
||||
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
|
||||
if [[ -f "$MEDIA_REPO_JS" ]]; then
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
p = Path('/opt/immich/app/dist/repositories/media.repository.js')
|
||||
s = p.read_text()
|
||||
old = "(0, sharp_1.default)(input).metadata()"
|
||||
new = "(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()"
|
||||
if new in s:
|
||||
print('hotfix already there')
|
||||
elif old in s:
|
||||
p.write_text(s.replace(old, new, 1))
|
||||
print('hotfix applied')
|
||||
else:
|
||||
print('pattern not found, skipped')
|
||||
PY
|
||||
if grep -qF "(0, sharp_1.default)(input).metadata()" "$MEDIA_REPO_JS" 2>/dev/null; then
|
||||
msg_info "Patching media.repository.js"
|
||||
sed -i '0,/(0, sharp_1\.default)(input)\.metadata()/s//(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()/' "$MEDIA_REPO_JS"
|
||||
msg_ok "Patched media.repository.js"
|
||||
fi
|
||||
msg_ok "Patched media.repository.js"
|
||||
|
||||
mkdir -p /var/log/immich
|
||||
touch /var/log/immich/{web.log,ml.log}
|
||||
@@ -537,6 +530,7 @@ MACHINE_LEARNING_CACHE_FOLDER=${INSTALL_DIR}/cache
|
||||
## - inference speed while reducing accuracy
|
||||
## - Default is FP32
|
||||
# MACHINE_LEARNING_OPENVINO_PRECISION=FP16
|
||||
MACHINE_LEARNING_MODEL_REVISION=v2
|
||||
|
||||
IMMICH_MEDIA_LOCATION=${UPLOAD_DIR}
|
||||
EOF
|
||||
|
||||
@@ -20,15 +20,14 @@ $STD apt install -y \
|
||||
openssl \
|
||||
ffmpeg \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-dev \
|
||||
python3-numpy \
|
||||
redis-server
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
PG_VERSION="16" PG_MODULES="pgvector" setup_postgresql
|
||||
PG_DB_NAME="kima" PG_DB_USER="kima" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
|
||||
NODE_VERSION="24" setup_nodejs
|
||||
PYTHON_VERSION="3.13" setup_uv
|
||||
|
||||
msg_info "Configuring Redis"
|
||||
systemctl enable -q --now redis-server
|
||||
@@ -37,8 +36,8 @@ msg_ok "Configured Redis"
|
||||
fetch_and_deploy_gh_release "kima-hub" "Chevron7Locked/kima-hub" "tarball"
|
||||
|
||||
msg_info "Installing Python Dependencies"
|
||||
export PIP_BREAK_SYSTEM_PACKAGES=1
|
||||
$STD pip3 install --no-cache-dir \
|
||||
$STD uv venv --python 3.13 /opt/kima-hub/venv
|
||||
$STD uv pip install --no-cache --python /opt/kima-hub/venv \
|
||||
tensorflow \
|
||||
essentia-tensorflow \
|
||||
redis \
|
||||
@@ -174,7 +173,7 @@ Environment=BATCH_SIZE=10
|
||||
Environment=SLEEP_INTERVAL=5
|
||||
Environment=NUM_WORKERS=2
|
||||
Environment=THREADS_PER_WORKER=1
|
||||
ExecStart=/usr/bin/python3 /opt/kima-hub/services/audio-analyzer/analyzer.py
|
||||
ExecStart=/opt/kima-hub/venv/bin/python /opt/kima-hub/services/audio-analyzer/analyzer.py
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
@@ -197,7 +196,7 @@ Environment=BACKEND_URL=http://localhost:3006
|
||||
Environment=MUSIC_PATH=/music
|
||||
Environment=SLEEP_INTERVAL=5
|
||||
Environment=NUM_WORKERS=1
|
||||
ExecStart=/usr/bin/python3 /opt/kima-hub/services/audio-analyzer-clap/analyzer.py
|
||||
ExecStart=/opt/kima-hub/venv/bin/python /opt/kima-hub/services/audio-analyzer-clap/analyzer.py
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ $STD sudo -u cool coolconfig set-admin-password --user=admin --password="$COOLPA
|
||||
echo "$COOLPASS" >~/.coolpass
|
||||
msg_ok "Installed Collabora Online"
|
||||
|
||||
fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "v7.4.0" "/usr/bin" "opencloud-*-linux-$(arch_resolve)"
|
||||
fetch_and_deploy_gh_release "OpenCloud" "opencloud-eu/opencloud" "singlefile" "v8.1.0" "/usr/bin" "opencloud-*-linux-$(arch_resolve)"
|
||||
mv /usr/bin/OpenCloud /usr/bin/opencloud
|
||||
|
||||
msg_info "Configuring OpenCloud"
|
||||
|
||||
@@ -22,13 +22,10 @@ msg_ok "Installed Dependencies"
|
||||
NODE_VERSION="24" setup_nodejs
|
||||
PG_VERSION="17" setup_postgresql
|
||||
PG_DB_NAME="pangolin" PG_DB_USER="pangolin" setup_postgresql_db
|
||||
PANGOLIN_VERSION="${PANGOLIN_VERSION:-1.23.0}"
|
||||
fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" "$PANGOLIN_VERSION"
|
||||
fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball"
|
||||
fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_$(arch_resolve)"
|
||||
fetch_and_deploy_gh_release "traefik" "traefik/traefik" "prebuild" "latest" "/usr/bin" "traefik_v*_linux_$(arch_resolve).tar.gz"
|
||||
|
||||
# Read the variable first and prompt only when it is unset, so the install can
|
||||
# be supplied up front. Same convention as install/forgejo-runner-install.sh.
|
||||
pango_url="${var_pangolin_url:-}"
|
||||
if [[ -z "$pango_url" ]]; then
|
||||
read -rp "${TAB3}Enter your Pangolin URL (ex: https://pangolin.example.com): " pango_url
|
||||
|
||||
@@ -46,8 +46,21 @@ msg_info "Configuring Paperclip"
|
||||
PAPERCLIP_HOME="/opt/paperclip-data"
|
||||
PAPERCLIP_CONFIG="${PAPERCLIP_HOME}/instances/default/config.json"
|
||||
|
||||
mkdir -p /opt/paperclip-data
|
||||
mkdir -p /root/.claude /root/.codex
|
||||
PAPERCLIP_USER="${var_paperclip_user:-paperclip}"
|
||||
# Claude Code refuses --dangerously-skip-permissions as root, so run as a dedicated user
|
||||
if [[ "$PAPERCLIP_USER" == "root" || ! "$PAPERCLIP_USER" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]; then
|
||||
msg_error "Invalid var_paperclip_user '${PAPERCLIP_USER}' (must be a non-root lowercase Linux username)"
|
||||
exit 1
|
||||
fi
|
||||
PAPERCLIP_USER_HOME="/home/${PAPERCLIP_USER}"
|
||||
id -u "$PAPERCLIP_USER" &>/dev/null || useradd -m -d "$PAPERCLIP_USER_HOME" -s /bin/bash "$PAPERCLIP_USER"
|
||||
if [[ -n "${var_paperclip_pass:-}" ]]; then
|
||||
printf '%s:%s\n' "$PAPERCLIP_USER" "$var_paperclip_pass" | chpasswd
|
||||
else
|
||||
passwd -l "$PAPERCLIP_USER" &>/dev/null
|
||||
fi
|
||||
unset var_paperclip_pass
|
||||
mkdir -p /opt/paperclip-data "${PAPERCLIP_USER_HOME}/.claude" "${PAPERCLIP_USER_HOME}/.codex"
|
||||
BETTER_AUTH_SECRET=$(openssl rand -hex 32)
|
||||
cat <<EOF >/opt/paperclip-ai/.env
|
||||
DATABASE_URL=postgresql://${PG_DB_USER}:${PG_DB_PASS}@127.0.0.1:5432/${PG_DB_NAME}
|
||||
@@ -62,11 +75,13 @@ PAPERCLIP_DEPLOYMENT_EXPOSURE=private
|
||||
PAPERCLIP_PUBLIC_URL=http://${LOCAL_IP}:3100
|
||||
BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET}
|
||||
EOF
|
||||
chmod 600 /opt/paperclip-ai/.env
|
||||
chown -R "${PAPERCLIP_USER}:${PAPERCLIP_USER}" /opt/paperclip-ai /opt/paperclip-data "$PAPERCLIP_USER_HOME"
|
||||
msg_ok "Configured Paperclip"
|
||||
|
||||
msg_info "Running Database Migrations"
|
||||
set -a && source /opt/paperclip-ai/.env && set +a
|
||||
$STD pnpm db:migrate
|
||||
$STD runuser -u "$PAPERCLIP_USER" -- env HOME="$PAPERCLIP_USER_HOME" pnpm db:migrate
|
||||
msg_ok "Ran Database Migrations"
|
||||
|
||||
msg_info "Bootstrapping Paperclip"
|
||||
@@ -77,7 +92,8 @@ for PAPERCLIP_ONBOARD_CMD in \
|
||||
"pnpm paperclipai onboard --yes --bind lan" \
|
||||
"pnpm paperclipai onboard --yes"; do
|
||||
rm -f "$PAPERCLIP_ONBOARD_LOG"
|
||||
setsid env \
|
||||
setsid runuser -u "$PAPERCLIP_USER" -- env \
|
||||
HOME="$PAPERCLIP_USER_HOME" \
|
||||
PAPERCLIP_HOME="$PAPERCLIP_HOME" \
|
||||
PAPERCLIP_CONFIG="$PAPERCLIP_CONFIG" \
|
||||
bash -c 'cd /opt/paperclip-ai && exec "$@"' _ $PAPERCLIP_ONBOARD_CMD \
|
||||
@@ -109,7 +125,8 @@ if [[ ! -f "$PAPERCLIP_CONFIG" ]]; then
|
||||
fi
|
||||
|
||||
if grep -q 'authenticated' $PAPERCLIP_CONFIG; then
|
||||
pnpm paperclipai auth bootstrap-ceo >"$PAPERCLIP_BOOTSTRAP_LOG" 2>&1 || true
|
||||
chown "${PAPERCLIP_USER}:${PAPERCLIP_USER}" /opt/paperclip-ai
|
||||
runuser -u "$PAPERCLIP_USER" -- env HOME="$PAPERCLIP_USER_HOME" bash -c 'cd /opt/paperclip-ai && pnpm paperclipai auth bootstrap-ceo' >"$PAPERCLIP_BOOTSTRAP_LOG" 2>&1 || true
|
||||
PAPERCLIP_INVITE_URL=$(awk -F'Invite URL: ' '/Invite URL:/ {print $2; exit}' "$PAPERCLIP_BOOTSTRAP_LOG")
|
||||
PAPERCLIP_INVITE_EXPIRY=$(awk -F'Expires: ' '/Expires:/ {print $2; exit}' "$PAPERCLIP_BOOTSTRAP_LOG")
|
||||
if [[ -n "$PAPERCLIP_INVITE_URL" ]]; then
|
||||
@@ -131,6 +148,7 @@ else
|
||||
fi
|
||||
rm -f "$PAPERCLIP_ONBOARD_LOG" "$PAPERCLIP_BOOTSTRAP_LOG"
|
||||
msg_ok "Bootstrapped Paperclip"
|
||||
echo -e "${INFO}${YW} Authenticate Claude Code and Codex as the service user: ${BGN}su - ${PAPERCLIP_USER}${CL}"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/paperclip.service
|
||||
@@ -141,12 +159,13 @@ Requires=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
User=${PAPERCLIP_USER}
|
||||
Group=${PAPERCLIP_USER}
|
||||
WorkingDirectory=/opt/paperclip-ai
|
||||
EnvironmentFile=/opt/paperclip-ai/.env
|
||||
Environment=HOME=/root
|
||||
Environment=CODEX_HOME=/root/.codex
|
||||
Environment=PATH=/root/.local/bin:/usr/local/bin:/usr/bin:/bin
|
||||
Environment=HOME=${PAPERCLIP_USER_HOME}
|
||||
Environment=CODEX_HOME=${PAPERCLIP_USER_HOME}/.codex
|
||||
Environment=PATH=${PAPERCLIP_USER_HOME}/.local/bin:/usr/local/bin:/usr/bin:/bin
|
||||
Environment=DISABLE_AUTOUPDATER=1
|
||||
ExecStart=/usr/bin/env pnpm paperclipai run
|
||||
Restart=on-failure
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/jez500/pricebuddy
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
nginx \
|
||||
cron \
|
||||
xvfb
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
PHP_VERSION="8.4" PHP_FPM="YES" PHP_MEMORY_LIMIT="2048M" setup_php
|
||||
setup_composer
|
||||
NODE_VERSION="22" setup_nodejs
|
||||
setup_mariadb
|
||||
MARIADB_DB_NAME="pricebuddy" MARIADB_DB_USER="pricebuddy" setup_mariadb_db
|
||||
PYTHON_VERSION="3.12" setup_uv
|
||||
|
||||
msg_info "Installing Google Chrome"
|
||||
setup_deb822_repo \
|
||||
"google-chrome" \
|
||||
"https://dl.google.com/linux/linux_signing_key.pub" \
|
||||
"https://dl.google.com/linux/chrome/deb/" \
|
||||
"stable"
|
||||
$STD apt install -y google-chrome-stable
|
||||
# the package adds its own .list for the same repo, which apt rejects next to the deb822 source
|
||||
rm -f /etc/apt/sources.list.d/google-chrome.list
|
||||
msg_ok "Installed Google Chrome"
|
||||
|
||||
fetch_and_deploy_gh_release "seleniumbase-scrapper" "jez500/seleniumbase-scrapper" "tarball"
|
||||
|
||||
msg_info "Setting up SeleniumBase Scrapper"
|
||||
$STD uv venv --python 3.12 /opt/seleniumbase-scrapper/.venv
|
||||
# the SeleniumBase release upstream builds and tests its image against
|
||||
$STD uv pip install --python /opt/seleniumbase-scrapper/.venv/bin/python \
|
||||
"seleniumbase==$(sed -n 's/^ARG SELENIUMBASE_VERSION=v//p' /opt/seleniumbase-scrapper/Dockerfile)" \
|
||||
flask \
|
||||
beautifulsoup4
|
||||
$STD /opt/seleniumbase-scrapper/.venv/bin/seleniumbase get chromedriver
|
||||
msg_ok "Set up SeleniumBase Scrapper"
|
||||
|
||||
fetch_and_deploy_gh_release "pricebuddy" "jez500/pricebuddy" "tarball"
|
||||
|
||||
msg_info "Setting up PriceBuddy"
|
||||
cd /opt/pricebuddy
|
||||
cat <<EOF >/opt/pricebuddy/.env
|
||||
APP_NAME=PriceBuddy
|
||||
APP_ENV=production
|
||||
APP_KEY=base64:$(openssl rand -base64 32)
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://${LOCAL_IP}
|
||||
APP_VERSION=$(cat ~/.pricebuddy)
|
||||
|
||||
DB_CONNECTION=mariadb
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=pricebuddy
|
||||
DB_USERNAME=pricebuddy
|
||||
DB_PASSWORD=${MARIADB_DB_PASS}
|
||||
|
||||
SCRAPER_BASE_URL=http://127.0.0.1:3000
|
||||
|
||||
APP_USER_EMAIL=admin@example.com
|
||||
APP_USER_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16)
|
||||
EOF
|
||||
$STD composer install --no-dev --optimize-autoloader --no-interaction
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
$STD php artisan storage:link
|
||||
# reads APP_USER_* for the admin via env(), so it has to run before optimize caches the config
|
||||
$STD php artisan buddy:init-db
|
||||
$STD php artisan optimize
|
||||
$STD php artisan icons:cache
|
||||
chown -R www-data:www-data /opt/pricebuddy
|
||||
chmod 600 /opt/pricebuddy/.env
|
||||
msg_ok "Set up PriceBuddy"
|
||||
|
||||
msg_info "Configuring Nginx"
|
||||
PHP_SOCK=$(get_php_fpm_socket)
|
||||
cat <<EOF >/etc/nginx/sites-available/pricebuddy
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /opt/pricebuddy/public;
|
||||
|
||||
index index.php;
|
||||
charset utf-8;
|
||||
|
||||
location / {
|
||||
try_files \$uri \$uri/ /index.php?\$query_string;
|
||||
}
|
||||
|
||||
location = /favicon.ico { access_log off; log_not_found off; }
|
||||
location = /robots.txt { access_log off; log_not_found off; }
|
||||
|
||||
error_page 404 /index.php;
|
||||
|
||||
location ~ \.php\$ {
|
||||
fastcgi_pass unix:${PHP_SOCK};
|
||||
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
fastcgi_read_timeout 300;
|
||||
}
|
||||
|
||||
location ~ /\.(?!well-known).* {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
nginx_enable_site "pricebuddy"
|
||||
msg_ok "Configured Nginx"
|
||||
|
||||
msg_info "Creating Services"
|
||||
cat <<EOF >/etc/systemd/system/seleniumbase-scrapper.service
|
||||
[Unit]
|
||||
Description=SeleniumBase Scrapper for PriceBuddy
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/seleniumbase-scrapper/api
|
||||
Environment=API_HOST=127.0.0.1
|
||||
Environment=API_PORT=3000
|
||||
ExecStart=/opt/seleniumbase-scrapper/.venv/bin/python /opt/seleniumbase-scrapper/api/server.py
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
cat <<EOF >/etc/systemd/system/pricebuddy-worker.service
|
||||
[Unit]
|
||||
Description=PriceBuddy Queue Worker
|
||||
After=network.target mariadb.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
Group=www-data
|
||||
WorkingDirectory=/opt/pricebuddy
|
||||
ExecStart=/usr/bin/php /opt/pricebuddy/artisan queue:work
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
cat <<EOF >/etc/cron.d/pricebuddy
|
||||
* * * * * www-data cd /opt/pricebuddy && php artisan schedule:run >/dev/null 2>&1
|
||||
EOF
|
||||
systemctl enable -q --now seleniumbase-scrapper pricebuddy-worker
|
||||
msg_ok "Created Services"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -20,7 +20,7 @@ $STD apt install -y \
|
||||
fonts-dejavu-core
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
JAVA_VERSION="17" setup_java
|
||||
JAVA_VERSION="25" setup_java
|
||||
PG_VERSION="16" setup_postgresql
|
||||
PG_DB_NAME="thingsboard_db" PG_DB_USER="thingsboard" setup_postgresql_db
|
||||
fetch_and_deploy_gh_release "thingsboard" "thingsboard/thingsboard" "binary" "latest" "/tmp" "thingsboard-*.deb"
|
||||
|
||||
@@ -36,6 +36,9 @@ PHP_SOCK=$(get_php_fpm_socket)
|
||||
cat <<EOF >/etc/caddy/Caddyfile
|
||||
:80 {
|
||||
root * /opt/webtrees
|
||||
# Caddy ignores data/.htaccess; media must go through webtrees so its privacy rules apply.
|
||||
@private path /app/* /data/* /modules_v4/* /resources/* /vendor/* /.*
|
||||
respond @private 403
|
||||
php_fastcgi unix/${PHP_SOCK}
|
||||
file_server
|
||||
encode gzip
|
||||
|
||||
+55
-522
@@ -1,218 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
|
||||
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
|
||||
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
||||
load_functions
|
||||
|
||||
function header_info {
|
||||
clear
|
||||
cat <<"EOF"
|
||||
___ __ __ _ _ ____ ___
|
||||
/ | __________/ /_ / / (_)___ __ ___ __ | | / / |/ /
|
||||
/ /| | / ___/ ___/ __ \ / / / / __ \/ / / / |/_/ | | / / /|_/ /
|
||||
/ ___ |/ / / /__/ / / / / /___/ / / / / /_/ /> < | |/ / / / /
|
||||
/_/ |_/_/ \___/_/ /_/ /_____/_/_/ /_/\__,_/_/|_| |___/_/ /_/
|
||||
|
||||
EOF
|
||||
}
|
||||
header_info
|
||||
echo -e "\n Loading..."
|
||||
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
|
||||
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
METHOD=""
|
||||
APP="Arch Linux"
|
||||
APP_TYPE="vm"
|
||||
NSAPP="archlinux-vm"
|
||||
var_os="arch-linux"
|
||||
var_version="n.d."
|
||||
|
||||
YW=$(echo "\033[33m")
|
||||
BL=$(echo "\033[36m")
|
||||
RD=$(echo "\033[01;31m")
|
||||
BGN=$(echo "\033[4;92m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
DGN=$(echo "\033[32m")
|
||||
CL=$(echo "\033[m")
|
||||
|
||||
CL=$(echo "\033[m")
|
||||
BOLD=$(echo "\033[1m")
|
||||
BFR="\\r\\033[K"
|
||||
HOLD=" "
|
||||
TAB=" "
|
||||
|
||||
CM="${TAB}✔️${TAB}${CL}"
|
||||
CROSS="${TAB}✖️${TAB}${CL}"
|
||||
INFO="${TAB}💡${TAB}${CL}"
|
||||
OS="${TAB}🖥️${TAB}${CL}"
|
||||
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
|
||||
DISKSIZE="${TAB}💾${TAB}${CL}"
|
||||
CPUCORE="${TAB}🧠${TAB}${CL}"
|
||||
RAMSIZE="${TAB}🛠️${TAB}${CL}"
|
||||
CONTAINERID="${TAB}🆔${TAB}${CL}"
|
||||
HOSTNAME="${TAB}🏠${TAB}${CL}"
|
||||
BRIDGE="${TAB}🌉${TAB}${CL}"
|
||||
GATEWAY="${TAB}🌐${TAB}${CL}"
|
||||
DEFAULT="${TAB}⚙️${TAB}${CL}"
|
||||
MACADDRESS="${TAB}🔗${TAB}${CL}"
|
||||
VLANTAG="${TAB}🏷️${TAB}${CL}"
|
||||
CREATING="${TAB}🚀${TAB}${CL}"
|
||||
ADVANCED="${TAB}🧩${TAB}${CL}"
|
||||
CLOUD="${TAB}☁️${TAB}${CL}"
|
||||
|
||||
THIN="discard=on,ssd=1,"
|
||||
|
||||
header_info
|
||||
echo -e "\n Loading..."
|
||||
set -e
|
||||
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
|
||||
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"
|
||||
cleanup_vmid
|
||||
}
|
||||
|
||||
function get_valid_nextid() {
|
||||
local try_id
|
||||
try_id=$(pvesh get /cluster/nextid)
|
||||
while true; do
|
||||
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
echo "$try_id"
|
||||
}
|
||||
|
||||
function cleanup_vmid() {
|
||||
if qm status $VMID &>/dev/null; then
|
||||
qm stop $VMID &>/dev/null
|
||||
qm destroy $VMID &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
vm_require_arch amd64
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
pushd $TEMP_DIR >/dev/null
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Arch Linux VM" --yesno "This will create a New Arch Linux VM. Proceed?" 10 58; then
|
||||
:
|
||||
else
|
||||
header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
|
||||
fi
|
||||
|
||||
function msg_info() {
|
||||
local msg="$1"
|
||||
echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}"
|
||||
}
|
||||
|
||||
function msg_ok() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR}${CM}${GN}${msg}${CL}"
|
||||
}
|
||||
|
||||
function msg_error() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR}${CROSS}${RD}${msg}${CL}"
|
||||
}
|
||||
|
||||
function check_root() {
|
||||
if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then
|
||||
clear
|
||||
msg_error "Please run this script as root."
|
||||
echo -e "\nExiting..."
|
||||
sleep 2
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
|
||||
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
|
||||
pve_check() {
|
||||
local PVE_VER
|
||||
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
|
||||
|
||||
# Check for Proxmox VE 8.x: allow 8.0–8.9
|
||||
if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 9)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 8.0 – 8.9"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
|
||||
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 2)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# All other unsupported versions
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.2"
|
||||
exit 105
|
||||
}
|
||||
|
||||
function arch_check() {
|
||||
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
|
||||
echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n"
|
||||
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n"
|
||||
echo -e "Exiting..."
|
||||
sleep 2
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
function ssh_check() {
|
||||
if command -v pveversion >/dev/null 2>&1; then
|
||||
if [ -n "${SSH_CLIENT:+x}" ]; then
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then
|
||||
echo "you've been warned"
|
||||
else
|
||||
clear
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function exit-script() {
|
||||
clear
|
||||
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
|
||||
exit
|
||||
}
|
||||
|
||||
function default_settings() {
|
||||
vm_apply_machine_type "i440fx"
|
||||
VMID=$(get_valid_nextid)
|
||||
MACHINE=""
|
||||
DISK_SIZE="4G"
|
||||
DISK_SIZE="10G"
|
||||
DISK_CACHE=""
|
||||
HN="arch-linux"
|
||||
CPU_TYPE=""
|
||||
@@ -224,219 +48,28 @@ function default_settings() {
|
||||
MTU=""
|
||||
START_VM="yes"
|
||||
METHOD="default"
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}"
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}"
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}"
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}"
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a Arch Linux VM using the above default settings${CL}"
|
||||
vm_echo_default_settings
|
||||
}
|
||||
|
||||
function advanced_settings() {
|
||||
METHOD="advanced"
|
||||
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
|
||||
while true; do
|
||||
if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 "$VMID" --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VMID" ]; then
|
||||
VMID=$(get_valid_nextid)
|
||||
fi
|
||||
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
|
||||
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
|
||||
sleep 2
|
||||
continue
|
||||
fi
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
|
||||
break
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
||||
vm_prompt_machine_type "i440fx"
|
||||
vm_prompt_disk_size "10G"
|
||||
vm_prompt_disk_cache "none"
|
||||
vm_prompt_hostname "arch-linux"
|
||||
vm_prompt_cpu_model "kvm64"
|
||||
vm_prompt_cpu_cores "1"
|
||||
vm_prompt_ram "1024"
|
||||
vm_prompt_bridge "vmbr0"
|
||||
vm_prompt_mac "$GEN_MAC"
|
||||
vm_prompt_vlan
|
||||
vm_prompt_mtu
|
||||
|
||||
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
|
||||
"i440fx" "Machine i440fx" ON \
|
||||
"q35" "Machine q35" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ "$MACH" = q35 ]; then
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
|
||||
MACHINE=" -machine q35"
|
||||
else
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
|
||||
MACHINE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
vm_prompt_verbose "no"
|
||||
vm_prompt_start_vm "yes"
|
||||
|
||||
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ')
|
||||
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
|
||||
DISK_SIZE="${DISK_SIZE}G"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
|
||||
elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
|
||||
else
|
||||
echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}"
|
||||
exit-script
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "None (Default)" ON \
|
||||
"1" "Write Through" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ "$DISK_CACHE" = "1" ]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}"
|
||||
DISK_CACHE="cache=writethrough,"
|
||||
else
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
||||
DISK_CACHE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 arch-linux --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VM_NAME" ]; then
|
||||
HN="arch-linux"
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
|
||||
if [ "$HN" != "${VM_NAME,,}" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
|
||||
fi
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "KVM64 (Default)" ON \
|
||||
"1" "Host" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ "$CPU_TYPE1" = "1" ]; then
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
|
||||
CPU_TYPE=" -cpu host"
|
||||
else
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
|
||||
CPU_TYPE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi
|
||||
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi
|
||||
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$BRG" ]; then
|
||||
BRG="vmbr0"
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 "$GEN_MAC" --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MAC1" ]; then
|
||||
MAC="$GEN_MAC"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then
|
||||
MAC="$MAC1"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VLAN1" ]; then
|
||||
VLAN1="Default"
|
||||
VLAN=""
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then
|
||||
VLAN=",tag=$VLAN1"
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MTU1" ]; then
|
||||
MTU1="Default"
|
||||
MTU=""
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then
|
||||
MTU=",mtu=$MTU1"
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||
START_VM="yes"
|
||||
else
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}"
|
||||
START_VM="no"
|
||||
fi
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Arch Linux VM?" --no-button Do-Over 10 58); then
|
||||
if vm_confirm_advanced_settings "Ready to create a Arch Linux VM?"; then
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a Arch Linux VM using the above advanced settings${CL}"
|
||||
else
|
||||
header_info
|
||||
@@ -445,154 +78,54 @@ function advanced_settings() {
|
||||
fi
|
||||
}
|
||||
|
||||
function start_script() {
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
|
||||
header_info
|
||||
echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}"
|
||||
default_settings
|
||||
else
|
||||
header_info
|
||||
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
}
|
||||
|
||||
check_root
|
||||
arch_check
|
||||
pve_check
|
||||
ssh_check
|
||||
start_script
|
||||
vm_preflight
|
||||
vm_start_script "Use Default Settings?\n\nDefaults:\n• 1 CPU Core\n• 1 GB RAM\n• 10 GB Disk" 13 58
|
||||
post_to_api_vm
|
||||
|
||||
msg_info "Validating Storage"
|
||||
while read -r line; do
|
||||
TAG=$(echo "$line" | awk '{print $1}')
|
||||
TYPE=$(echo "$line" | awk '{printf "%-10s", $2}')
|
||||
FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
|
||||
ITEM=" Type: $TYPE Free: $FREE "
|
||||
OFFSET=2
|
||||
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
|
||||
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
|
||||
fi
|
||||
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
|
||||
done < <(pvesm status -content images | awk 'NR>1')
|
||||
VALID=$(pvesm status -content images | awk 'NR>1')
|
||||
if [ -z "$VALID" ]; then
|
||||
msg_error "Unable to detect a valid storage location."
|
||||
exit
|
||||
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
|
||||
STORAGE=${STORAGE_MENU[0]}
|
||||
else
|
||||
while [ -z "${STORAGE:+x}" ]; do
|
||||
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
"Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \
|
||||
16 $(($MSG_MAX_LENGTH + 23)) 6 \
|
||||
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
|
||||
done
|
||||
fi
|
||||
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
||||
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
||||
vm_select_storage "$HN"
|
||||
msg_info "Retrieving the URL for the Arch Linux .iso File"
|
||||
URL=https://geo.mirror.pkgbuild.com/iso/latest/archlinux-x86_64.iso
|
||||
FILENAME="archlinux-x86_64.iso"
|
||||
vm_select_iso_storage "$FILENAME" "$HN"
|
||||
CACHE_FILE="$ISO_PATH"
|
||||
sleep 2
|
||||
msg_ok "${CL}${BL}${URL}${CL}"
|
||||
curl -f#SL -o "$(basename "$URL")" "$URL"
|
||||
echo -en "\e[1A\e[0K"
|
||||
FILE=$(basename $URL)
|
||||
msg_ok "Downloaded ${CL}${BL}${FILE}${CL}"
|
||||
|
||||
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
|
||||
case $STORAGE_TYPE in
|
||||
nfs | dir | cifs)
|
||||
DISK_EXT=".qcow2"
|
||||
DISK_REF="$VMID/"
|
||||
DISK_IMPORT="--format qcow2"
|
||||
THIN=""
|
||||
;;
|
||||
btrfs)
|
||||
DISK_EXT=".raw"
|
||||
DISK_REF="$VMID/"
|
||||
DISK_IMPORT="--format raw"
|
||||
THIN=""
|
||||
;;
|
||||
*)
|
||||
DISK_EXT=""
|
||||
DISK_REF=""
|
||||
DISK_IMPORT="--format raw"
|
||||
;;
|
||||
esac
|
||||
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115
|
||||
|
||||
msg_info "Creating a Arch Linux VM"
|
||||
qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
|
||||
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
|
||||
|
||||
if qm disk import --help >/dev/null 2>&1; then
|
||||
IMPORT_CMD=(qm disk import)
|
||||
else
|
||||
IMPORT_CMD=(qm importdisk)
|
||||
fi
|
||||
|
||||
IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "${FILE}" "$STORAGE" ${DISK_IMPORT:-} 2>&1 || true)"
|
||||
DISK_REF_IMPORTED="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")"
|
||||
[[ -z "$DISK_REF_IMPORTED" ]] && DISK_REF_IMPORTED="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)"
|
||||
[[ -z "$DISK_REF_IMPORTED" ]] && {
|
||||
msg_error "Unable to determine imported disk reference."
|
||||
echo "$IMPORT_OUT"
|
||||
exit 226
|
||||
}
|
||||
msg_ok "Imported disk (${CL}${BL}${DISK_REF_IMPORTED}${CL})"
|
||||
|
||||
# The ISO is the installer, not the system: an empty disk takes scsi0 and wins
|
||||
# the boot order as soon as something is on it.
|
||||
qm set $VMID \
|
||||
-efidisk0 ${STORAGE}:0,efitype=4m \
|
||||
-scsi0 ${DISK_REF_IMPORTED},${DISK_CACHE}${THIN%,} \
|
||||
-ide2 ${STORAGE}:cloudinit \
|
||||
-boot order=scsi0 \
|
||||
-scsi0 ${STORAGE}:${DISK_SIZE%G},${DISK_CACHE}${THIN%,} \
|
||||
-cdrom "$ISO_VOLUME" \
|
||||
-boot order='scsi0;ide2' \
|
||||
-serial0 socket >/dev/null
|
||||
DESCRIPTION=$(
|
||||
cat <<EOF
|
||||
<div align='center'>
|
||||
<a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
|
||||
</a>
|
||||
|
||||
<h2 style='font-size: 24px; margin: 20px 0;'>Arch Linux VM</h2>
|
||||
|
||||
<p style='margin: 16px 0;'>
|
||||
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
|
||||
</span>
|
||||
</div>
|
||||
EOF
|
||||
)
|
||||
qm set $VMID -description "$DESCRIPTION" >/dev/null
|
||||
if [ -n "$DISK_SIZE" ]; then
|
||||
msg_info "Resizing disk to $DISK_SIZE GB"
|
||||
qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null
|
||||
else
|
||||
msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB"
|
||||
qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null
|
||||
fi
|
||||
set_description
|
||||
|
||||
msg_ok "Created a Arch Linux VM ${CL}${BL}(${HN})"
|
||||
if [ "$START_VM" == "yes" ]; then
|
||||
msg_info "Starting Arch Linux VM"
|
||||
qm start $VMID
|
||||
$STD qm start $VMID
|
||||
msg_ok "Started Arch Linux VM"
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
|
||||
echo -e "\n${INFO}${BOLD}${GN}Arch Linux VM Configuration Summary:${CL}"
|
||||
echo -e "${TAB}${DGN}VM ID: ${BGN}${VMID}${CL}"
|
||||
echo -e "${TAB}${DGN}Hostname: ${BGN}${HN}${CL}"
|
||||
echo -e "${TAB}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
|
||||
|
||||
echo -e "\n${INFO}${BOLD}${YW}Next Steps:${CL}"
|
||||
echo -e "${TAB}1. Open the VM Console in Proxmox - the live ISO logs in as root by itself"
|
||||
echo -e "${TAB}2. Run ${BL}archinstall${CL}, or install by hand, onto ${BL}/dev/sda${CL}"
|
||||
echo -e "${TAB}3. Detach the ISO afterwards (${BL}qm set ${VMID} --ide2 none${CL}) -- the boot"
|
||||
echo -e "${TAB} order already prefers the disk"
|
||||
echo -e "${TAB}4. The ISO carries no Cloud-Init and no guest agent. Install"
|
||||
echo -e "${TAB} ${BL}qemu-guest-agent${CL} in the guest for Proxmox to show its IP"
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://fedoraproject.org/cloud/
|
||||
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
|
||||
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
||||
load_functions
|
||||
|
||||
APP="Fedora"
|
||||
APP_TYPE="vm"
|
||||
NSAPP="fedora-vm"
|
||||
var_os="fedora"
|
||||
var_version=" "
|
||||
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
|
||||
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
METHOD=""
|
||||
THIN="discard=on,ssd=1,"
|
||||
|
||||
header_info
|
||||
echo -e "\n Loading..."
|
||||
|
||||
set -e
|
||||
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
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
pushd "$TEMP_DIR" >/dev/null
|
||||
|
||||
vm_preflight
|
||||
|
||||
# Fedora Cloud Base sets no password and has no console login, so the only
|
||||
# question is which credentials.
|
||||
CLOUDINIT_REQUIRED=1
|
||||
vm_prompt_cloud_init "fedora"
|
||||
|
||||
function default_settings() {
|
||||
VMID=$(get_valid_nextid)
|
||||
vm_apply_machine_type "q35"
|
||||
DISK_SIZE="20G"
|
||||
DISK_CACHE=""
|
||||
HN="fedora"
|
||||
CPU_TYPE=" -cpu host"
|
||||
CORE_COUNT="2"
|
||||
RAM_SIZE="2048"
|
||||
BRG="vmbr0"
|
||||
MAC="$GEN_MAC"
|
||||
VLAN=""
|
||||
MTU=""
|
||||
START_VM="yes"
|
||||
METHOD="default"
|
||||
echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}"
|
||||
vm_echo_default_settings
|
||||
}
|
||||
|
||||
function advanced_settings() {
|
||||
METHOD="advanced"
|
||||
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
||||
vm_prompt_machine_type "q35"
|
||||
vm_prompt_disk_size "20G" "Set Disk Size in GiB"
|
||||
vm_prompt_disk_cache "none"
|
||||
vm_prompt_hostname "fedora"
|
||||
vm_prompt_cpu_model "host"
|
||||
vm_prompt_cpu_cores "2"
|
||||
vm_prompt_ram "2048"
|
||||
vm_prompt_bridge "vmbr0"
|
||||
vm_prompt_mac "$GEN_MAC"
|
||||
vm_prompt_vlan
|
||||
vm_prompt_mtu
|
||||
vm_prompt_verbose "no"
|
||||
vm_prompt_start_vm "yes"
|
||||
|
||||
if vm_confirm_advanced_settings "Ready to create a Fedora VM?"; then
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a Fedora VM using the above advanced settings${CL}"
|
||||
else
|
||||
header_info
|
||||
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
}
|
||||
|
||||
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 2 GB RAM\n• 20 GB Disk\n• Cloud-Init enabled" 14 58
|
||||
post_to_api_vm
|
||||
|
||||
vm_select_storage "$HN"
|
||||
|
||||
if ! command -v virt-customize &>/dev/null; then
|
||||
msg_info "Installing libguestfs-tools"
|
||||
$STD apt-get update
|
||||
$STD apt-get install -y libguestfs-tools
|
||||
msg_ok "Installed libguestfs-tools"
|
||||
fi
|
||||
|
||||
msg_info "Retrieving the URL for the Fedora Cloud Base image"
|
||||
MIRROR="https://dl.fedoraproject.org/pub/fedora/linux/releases"
|
||||
FEDORA_RELEASE=$(curl -fsSL "$MIRROR/" 2>/dev/null | grep -oP 'href="\K[0-9]+(?=/")' | sort -rn | head -1)
|
||||
[[ -z "$FEDORA_RELEASE" ]] && FEDORA_RELEASE="44"
|
||||
|
||||
FEDORA_ARCH="$(vm_arch_resolve x86_64 aarch64)"
|
||||
IMAGE_DIR="${MIRROR}/${FEDORA_RELEASE}/Cloud/${FEDORA_ARCH}/images"
|
||||
FILE=$(curl -fsSL "${IMAGE_DIR}/" 2>/dev/null | grep -oP 'href="\KFedora-Cloud-Base-Generic-[^"]+\.'"${FEDORA_ARCH}"'\.qcow2(?=")' | sort -V | tail -1)
|
||||
if [[ -z "$FILE" ]]; then
|
||||
msg_error "Could not determine the current Fedora Cloud image"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg_ok "Fedora ${CL}${BL}${FEDORA_RELEASE}${CL} ${GN}(${FILE})"
|
||||
|
||||
URL="${IMAGE_DIR}/${FILE}"
|
||||
CACHE_FILE="$(vm_image_cache_path "$URL")"
|
||||
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115
|
||||
|
||||
WORK_FILE=$(mktemp --suffix=.qcow2)
|
||||
cp "$CACHE_FILE" "$WORK_FILE"
|
||||
popd >/dev/null
|
||||
rm -rf "$TEMP_DIR"
|
||||
vm_prepare_cloud_image "$WORK_FILE" "$HN" || true
|
||||
|
||||
msg_info "Customizing ${FILE}"
|
||||
CUSTOMIZE_FAILURES_BEFORE=${#_VM_PREPARE_FAILED[@]}
|
||||
_vm_customize "Fedora serial console" "$WORK_FILE" --run-command "systemctl enable serial-getty@ttyS0.service"
|
||||
_vm_customize "Fedora SELinux relabel" "$WORK_FILE" --selinux-relabel
|
||||
if ((${#_VM_PREPARE_FAILED[@]} == CUSTOMIZE_FAILURES_BEFORE)); then
|
||||
msg_ok "Customized image"
|
||||
else
|
||||
msg_warn "Fedora image customization incomplete; see warnings above"
|
||||
fi
|
||||
|
||||
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
|
||||
vm_apply_storage_layout "$STORAGE_TYPE"
|
||||
vm_define_disk_references 3
|
||||
|
||||
if [[ "$DISK_IMPORT_FORMAT" == "raw" ]]; then
|
||||
msg_info "Converting image to raw format"
|
||||
RAW_FILE=$(mktemp --suffix=.raw)
|
||||
qemu-img convert -f qcow2 -O raw "$WORK_FILE" "$RAW_FILE"
|
||||
rm -f "$WORK_FILE"
|
||||
WORK_FILE="$RAW_FILE"
|
||||
msg_ok "Converted image to raw format"
|
||||
fi
|
||||
|
||||
msg_info "Creating a Fedora VM"
|
||||
qm create "$VMID" -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores "$CORE_COUNT" -memory "$RAM_SIZE" \
|
||||
-name "$HN" -tags community-script -net0 virtio,bridge="$BRG",macaddr="$MAC""$VLAN""$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
|
||||
vm_alloc_efi_disk "$DISK0"
|
||||
pvesm alloc "$STORAGE" "$VMID" "$DISK2" 4M 1>&/dev/null
|
||||
qm importdisk "$VMID" "$WORK_FILE" "$STORAGE" -format "$DISK_IMPORT_FORMAT" 1>&/dev/null
|
||||
qm set "$VMID" \
|
||||
-efidisk0 "${DISK0_REF}${FORMAT:-}" \
|
||||
-scsi0 "${DISK1_REF}",${DISK_CACHE}${THIN}size="${DISK_SIZE}" \
|
||||
-tpmstate0 "${DISK2_REF}",version=v2.0 \
|
||||
-boot order=scsi0 \
|
||||
-serial0 socket >/dev/null
|
||||
set_description
|
||||
rm -f "$WORK_FILE"
|
||||
msg_ok "Created a Fedora VM ${CL}${BL}(${HN})"
|
||||
|
||||
vm_resize_disk
|
||||
|
||||
vm_provision "$VMID" || true
|
||||
|
||||
if [ "$START_VM" == "yes" ]; then
|
||||
msg_info "Starting Fedora VM"
|
||||
$STD qm start "$VMID"
|
||||
msg_ok "Started Fedora VM"
|
||||
fi
|
||||
|
||||
post_update_to_api "done" "none"
|
||||
|
||||
echo -e "\n${INFO}${BOLD}${GN}Fedora VM Configuration Summary:${CL}"
|
||||
echo -e "${TAB}${DGN}VM ID: ${BGN}${VMID}${CL}"
|
||||
echo -e "${TAB}${DGN}Hostname: ${BGN}${HN}${CL}"
|
||||
echo -e "${TAB}${DGN}Release: ${BGN}Fedora ${FEDORA_RELEASE} (${FEDORA_ARCH})${CL}"
|
||||
if [ -n "${CLOUDINIT_CRED_FILE:-}" ]; then
|
||||
echo -e "${TAB}${DGN}Cloud-Init credentials: ${BGN}${CLOUDINIT_CRED_FILE}${CL}"
|
||||
fi
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
+42
-488
@@ -1,219 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT
|
||||
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
|
||||
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
|
||||
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
||||
load_functions
|
||||
|
||||
function header_info {
|
||||
clear
|
||||
cat <<"EOF"
|
||||
__ ____ __ __ _ __ ____ __ ____ _____ ________ ______
|
||||
/ |/ (_) /___________ / /_(_) /__ / __ \____ __ __/ /____ _____/ __ \/ ___/ / ____/ / / / __ \
|
||||
/ /|_/ / / //_/ ___/ __ \/ __/ / //_/ / /_/ / __ \/ / / / __/ _ \/ ___/ / / /\__ \ / / / /_/ / /_/ /
|
||||
/ / / / / ,< / / / /_/ / /_/ / ,< / _, _/ /_/ / /_/ / /_/ __/ / / /_/ /___/ / / /___/ __ / _, _/
|
||||
/_/ /_/_/_/|_/_/ \____/\__/_/_/|_| /_/ |_|\____/\__,_/\__/\___/_/ \____//____/ \____/_/ /_/_/ |_|
|
||||
|
||||
EOF
|
||||
}
|
||||
header_info
|
||||
echo -e "Loading..."
|
||||
GEN_MAC=$(echo '00 60 2f'$(od -An -N3 -t xC /dev/urandom) | sed -e 's/ /:/g' | tr '[:lower:]' '[:upper:]')
|
||||
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
METHOD=""
|
||||
APP="MikroTik RouterOS"
|
||||
APP_TYPE="vm"
|
||||
NSAPP="mikrotik-routeros"
|
||||
var_os="mikrotik"
|
||||
var_version=" "
|
||||
DISK_SIZE="1G"
|
||||
YW=$(echo "\033[33m")
|
||||
BL=$(echo "\033[36m")
|
||||
RD=$(echo "\033[01;31m")
|
||||
BGN=$(echo "\033[4;92m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
DGN=$(echo "\033[32m")
|
||||
CL=$(echo "\033[m")
|
||||
|
||||
CL=$(echo "\033[m")
|
||||
BOLD=$(echo "\033[1m")
|
||||
BFR="\\r\\033[K"
|
||||
HOLD=" "
|
||||
TAB=" "
|
||||
|
||||
CM="${TAB}✔️${TAB}${CL}"
|
||||
CROSS="${TAB}✖️${TAB}${CL}"
|
||||
INFO="${TAB}💡${TAB}${CL}"
|
||||
OS="${TAB}🖥️${TAB}${CL}"
|
||||
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
|
||||
DISKSIZE="${TAB}💾${TAB}${CL}"
|
||||
CPUCORE="${TAB}🧠${TAB}${CL}"
|
||||
RAMSIZE="${TAB}🛠️${TAB}${CL}"
|
||||
CONTAINERID="${TAB}🆔${TAB}${CL}"
|
||||
HOSTNAME="${TAB}🏠${TAB}${CL}"
|
||||
BRIDGE="${TAB}🌉${TAB}${CL}"
|
||||
GATEWAY="${TAB}🌐${TAB}${CL}"
|
||||
DEFAULT="${TAB}⚙️${TAB}${CL}"
|
||||
MACADDRESS="${TAB}🔗${TAB}${CL}"
|
||||
VLANTAG="${TAB}🏷️${TAB}${CL}"
|
||||
CREATING="${TAB}🚀${TAB}${CL}"
|
||||
ADVANCED="${TAB}🧩${TAB}${CL}"
|
||||
CLOUD="${TAB}☁️${TAB}${CL}"
|
||||
|
||||
THIN="discard=on,ssd=1,"
|
||||
|
||||
header_info
|
||||
echo -e "Loading..."
|
||||
set -e
|
||||
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
|
||||
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"
|
||||
cleanup_vmid
|
||||
}
|
||||
|
||||
function get_valid_nextid() {
|
||||
local try_id
|
||||
try_id=$(pvesh get /cluster/nextid)
|
||||
while true; do
|
||||
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
echo "$try_id"
|
||||
}
|
||||
|
||||
function cleanup_vmid() {
|
||||
if qm status $VMID &>/dev/null; then
|
||||
qm stop $VMID &>/dev/null
|
||||
qm destroy $VMID &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
vm_require_arch amd64
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
pushd $TEMP_DIR >/dev/null
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Mikrotik RouterOS CHR VM" --yesno "This will create a Mikrotik RouterOS CHR VM. Proceed?" 10 58; then
|
||||
:
|
||||
else
|
||||
header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
|
||||
fi
|
||||
|
||||
function msg_info() {
|
||||
local msg="$1"
|
||||
echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}"
|
||||
}
|
||||
|
||||
function msg_ok() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR}${CM}${GN}${msg}${CL}"
|
||||
}
|
||||
|
||||
function msg_error() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR}${CROSS}${RD}${msg}${CL}"
|
||||
}
|
||||
|
||||
function check_root() {
|
||||
if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then
|
||||
clear
|
||||
msg_error "Please run this script as root."
|
||||
echo -e "\nExiting..."
|
||||
sleep 2
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
|
||||
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
|
||||
pve_check() {
|
||||
local PVE_VER
|
||||
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
|
||||
|
||||
# Check for Proxmox VE 8.x: allow 8.0–8.9
|
||||
if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 9)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 8.0 – 8.9"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
|
||||
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 2)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# All other unsupported versions
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.2"
|
||||
exit 105
|
||||
}
|
||||
|
||||
function arch_check() {
|
||||
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
|
||||
echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n"
|
||||
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n"
|
||||
echo -e "Exiting..."
|
||||
sleep 2
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
function ssh_check() {
|
||||
if command -v pveversion >/dev/null 2>&1; then
|
||||
if [ -n "${SSH_CLIENT:+x}" ]; then
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then
|
||||
echo "you've been warned"
|
||||
else
|
||||
clear
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function exit-script() {
|
||||
clear
|
||||
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
|
||||
exit
|
||||
}
|
||||
|
||||
function default_settings() {
|
||||
vm_apply_machine_type "i440fx"
|
||||
VMID=$(get_valid_nextid)
|
||||
FORMAT=",efitype=4m"
|
||||
MACHINE=""
|
||||
DISK_SIZE="8G"
|
||||
DISK_CACHE=""
|
||||
HN="mikrotik-routeros-chr"
|
||||
@@ -227,20 +51,7 @@ function default_settings() {
|
||||
START_VM="yes"
|
||||
CLOUD_INIT="no"
|
||||
METHOD="default"
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}"
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}"
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}"
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}"
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS VM using the above default settings${CL}"
|
||||
vm_echo_default_settings
|
||||
}
|
||||
|
||||
function get_mikrotik_version() {
|
||||
@@ -307,204 +118,23 @@ function get_mikrotik_version() {
|
||||
|
||||
function advanced_settings() {
|
||||
METHOD="advanced"
|
||||
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
|
||||
while true; do
|
||||
if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VMID" ]; then
|
||||
VMID=$(get_valid_nextid)
|
||||
fi
|
||||
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
|
||||
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
|
||||
sleep 2
|
||||
continue
|
||||
fi
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
|
||||
break
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
||||
vm_prompt_machine_type "i440fx"
|
||||
vm_prompt_disk_size "8G"
|
||||
vm_prompt_disk_cache "none"
|
||||
vm_prompt_hostname "mikrotik-routeros-chr"
|
||||
vm_prompt_cpu_model "kvm64"
|
||||
vm_prompt_cpu_cores "2"
|
||||
vm_prompt_ram "512"
|
||||
vm_prompt_bridge "vmbr0"
|
||||
vm_prompt_mac "$GEN_MAC"
|
||||
vm_prompt_vlan
|
||||
vm_prompt_mtu
|
||||
vm_prompt_verbose "no"
|
||||
vm_prompt_start_vm "yes"
|
||||
|
||||
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
|
||||
"i440fx" "Machine i440fx" ON \
|
||||
"q35" "Machine q35" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $MACH = q35 ]; then
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
|
||||
FORMAT=""
|
||||
MACHINE=" -machine q35"
|
||||
else
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
|
||||
FORMAT=",efitype=4m"
|
||||
MACHINE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ')
|
||||
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
|
||||
DISK_SIZE="${DISK_SIZE}G"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
|
||||
elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
|
||||
else
|
||||
echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}"
|
||||
exit-script
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "None (Default)" ON \
|
||||
"1" "Write Through" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $DISK_CACHE = "1" ]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}"
|
||||
DISK_CACHE="cache=writethrough,"
|
||||
else
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
||||
DISK_CACHE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 mikrotik-routeros-chr --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $VM_NAME ]; then
|
||||
HN="mikrotik-routeros-chr"
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
|
||||
if [ "$HN" != "${VM_NAME,,}" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
|
||||
fi
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "KVM64 (Default)" ON \
|
||||
"1" "Host" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $CPU_TYPE1 = "1" ]; then
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
|
||||
CPU_TYPE=" -cpu host"
|
||||
else
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
|
||||
CPU_TYPE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi
|
||||
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi
|
||||
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $BRG ]; then
|
||||
BRG="vmbr0"
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MAC1" ]; then
|
||||
MAC="$GEN_MAC"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then
|
||||
MAC="$MAC1"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VLAN1" ]; then
|
||||
VLAN1="Default"
|
||||
VLAN=""
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then
|
||||
VLAN=",tag=$VLAN1"
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MTU1" ]; then
|
||||
MTU1="Default"
|
||||
MTU=""
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then
|
||||
MTU=",mtu=$MTU1"
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||
START_VM="yes"
|
||||
else
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}"
|
||||
START_VM="no"
|
||||
fi
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Mikrotik RouterOS CHR VM?" --no-button Do-Over 10 58); then
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a Mikrotik RouterOS CHR VM using the above advanced settings${CL}"
|
||||
if vm_confirm_advanced_settings "Ready to create a MikroTik RouterOS VM?"; then
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a MikroTik RouterOS VM using the above advanced settings${CL}"
|
||||
else
|
||||
header_info
|
||||
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
|
||||
@@ -512,54 +142,12 @@ function advanced_settings() {
|
||||
fi
|
||||
}
|
||||
|
||||
function start_script() {
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
|
||||
header_info
|
||||
echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}"
|
||||
default_settings
|
||||
else
|
||||
header_info
|
||||
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
}
|
||||
|
||||
check_root
|
||||
arch_check
|
||||
pve_check
|
||||
ssh_check
|
||||
start_script
|
||||
vm_preflight
|
||||
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 512 MB RAM\n• 8 GB Disk" 13 58
|
||||
|
||||
post_to_api_vm
|
||||
msg_info "Validating Storage"
|
||||
while read -r line; do
|
||||
TAG=$(echo $line | awk '{print $1}')
|
||||
TYPE=$(echo $line | awk '{printf "%-10s", $2}')
|
||||
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
|
||||
ITEM=" Type: $TYPE Free: $FREE "
|
||||
OFFSET=2
|
||||
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
|
||||
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
|
||||
fi
|
||||
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
|
||||
done < <(pvesm status -content images | awk 'NR>1')
|
||||
VALID=$(pvesm status -content images | awk 'NR>1')
|
||||
if [ -z "$VALID" ]; then
|
||||
echo -e "\n${RD}⚠ Unable to detect a valid storage location.${CL}"
|
||||
echo -e "Exiting..."
|
||||
exit
|
||||
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
|
||||
STORAGE=${STORAGE_MENU[0]}
|
||||
else
|
||||
while [ -z "${STORAGE:+x}" ]; do
|
||||
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
"Which storage pool would you like to use for the Mikrotik RouterOS CHR VM?\n\n" \
|
||||
16 $(($MSG_MAX_LENGTH + 23)) 6 \
|
||||
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
|
||||
done
|
||||
fi
|
||||
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
||||
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
||||
vm_select_storage "$HN"
|
||||
msg_info "Getting URL for Latest Mikrotik RouterOS CHR Disk Image"
|
||||
|
||||
MIK_VER=$(get_mikrotik_version s)
|
||||
@@ -576,12 +164,14 @@ URL=https://download.mikrotik.com/routeros/$MIK_VER/chr-$MIK_VER.img.zip
|
||||
|
||||
sleep 2
|
||||
msg_ok "Downloading from URL: ${CL}${BL}${URL}${CL}"
|
||||
curl -f#SL -o "$(basename "$URL")" "$URL"
|
||||
echo -en "\e[1A\e[0K"
|
||||
FILE=$(basename $URL)
|
||||
msg_ok "Downloaded ${CL}${BL}$FILE${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
|
||||
msg_info "Extracting Mikrotik RouterOS CHR Disk Image"
|
||||
gunzip -f -S .zip $FILE
|
||||
# Decompress out of the cache rather than over it, gunzip eats its input.
|
||||
FILE="$(basename "${CACHE_FILE%.zip}")"
|
||||
gunzip -c -S .zip "$CACHE_FILE" >"$FILE"
|
||||
STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}')
|
||||
case $STORAGE_TYPE in
|
||||
nfs | dir)
|
||||
@@ -614,54 +204,18 @@ msg_info "Creating Mikrotik RouterOS CHR VM"
|
||||
qm create $VMID -tablet 0 -localtime 1 -cores $CORE_COUNT -memory $RAM_SIZE -name $HN \
|
||||
-tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU \
|
||||
-onboot 1 -ostype l26 -scsihw virtio-scsi-pci
|
||||
qm importdisk $VMID ${FILE%.*} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
|
||||
qm importdisk $VMID "$FILE" $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
|
||||
qm set $VMID \
|
||||
-scsi0 "$DISK_REF" \
|
||||
-boot order=scsi0 >/dev/null
|
||||
|
||||
DESCRIPTION=$(
|
||||
cat <<EOF
|
||||
<div align='center'>
|
||||
<a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
|
||||
</a>
|
||||
|
||||
<h2 style='font-size: 24px; margin: 20px 0;'>Mikrotik RouterOS CHR</h2>
|
||||
|
||||
<p style='margin: 16px 0;'>
|
||||
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
|
||||
</span>
|
||||
</div>
|
||||
EOF
|
||||
)
|
||||
qm set $VMID -description "$DESCRIPTION" >/dev/null
|
||||
if [ -n "$DISK_SIZE" ]; then
|
||||
msg_info "Resizing disk to $DISK_SIZE GB"
|
||||
qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null
|
||||
else
|
||||
msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB"
|
||||
qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null
|
||||
fi
|
||||
set_description
|
||||
vm_resize_disk
|
||||
|
||||
msg_ok "Mikrotik RouterOS CHR VM ${CL}${BL}(${HN})"
|
||||
if [ "$START_VM" == "yes" ]; then
|
||||
msg_info "Starting Mikrotik RouterOS CHR VM"
|
||||
qm start $VMID
|
||||
$STD qm start $VMID
|
||||
msg_ok "Started Mikrotik RouterOS CHR VM"
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
|
||||
+58
-537
@@ -1,218 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# Author: tteck (tteckster) | MickLesk (CanbiZ)
|
||||
# License: MIT
|
||||
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
|
||||
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
|
||||
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
||||
load_functions
|
||||
|
||||
function header_info {
|
||||
clear
|
||||
cat <<"EOF"
|
||||
______ __ __ _ __ __ __ __ _ ____ ___
|
||||
/_ __/_ _________ / //_/__ __ __ / |/ /____ __/ /_____/ /__ __ _____/ / | | / / |/ /
|
||||
/ / / // / __/ _ \/ ,< / -_) // / / / -_) \ / __/ __/ / _ \/ // / _ / | |/ / /|_/ /
|
||||
/_/ \_,_/_/ /_//_/_/|_|\__/\_, / /_/|_/\__/_\_\\__/\__/_/\___/\_,_/\_,_/ |___/_/ /_/
|
||||
/___/
|
||||
EOF
|
||||
}
|
||||
header_info
|
||||
echo -e "\n Loading..."
|
||||
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
|
||||
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
METHOD=""
|
||||
APP="Nextcloud"
|
||||
APP_TYPE="vm"
|
||||
NSAPP="nextcloud-vm"
|
||||
var_os="turnkey-nextcloud"
|
||||
var_version="n.d."
|
||||
|
||||
YW=$(echo "\033[33m")
|
||||
BL=$(echo "\033[36m")
|
||||
RD=$(echo "\033[01;31m")
|
||||
BGN=$(echo "\033[4;92m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
DGN=$(echo "\033[32m")
|
||||
CL=$(echo "\033[m")
|
||||
|
||||
CL=$(echo "\033[m")
|
||||
BOLD=$(echo "\033[1m")
|
||||
BFR="\\r\\033[K"
|
||||
HOLD=" "
|
||||
TAB=" "
|
||||
|
||||
CM="${TAB}✔️${TAB}${CL}"
|
||||
CROSS="${TAB}✖️${TAB}${CL}"
|
||||
INFO="${TAB}💡${TAB}${CL}"
|
||||
OS="${TAB}🖥️${TAB}${CL}"
|
||||
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
|
||||
DISKSIZE="${TAB}💾${TAB}${CL}"
|
||||
CPUCORE="${TAB}🧠${TAB}${CL}"
|
||||
RAMSIZE="${TAB}🛠️${TAB}${CL}"
|
||||
CONTAINERID="${TAB}🆔${TAB}${CL}"
|
||||
HOSTNAME="${TAB}🏠${TAB}${CL}"
|
||||
BRIDGE="${TAB}🌉${TAB}${CL}"
|
||||
GATEWAY="${TAB}🌐${TAB}${CL}"
|
||||
DEFAULT="${TAB}⚙️${TAB}${CL}"
|
||||
MACADDRESS="${TAB}🔗${TAB}${CL}"
|
||||
VLANTAG="${TAB}🏷️${TAB}${CL}"
|
||||
CREATING="${TAB}🚀${TAB}${CL}"
|
||||
ADVANCED="${TAB}🧩${TAB}${CL}"
|
||||
CLOUD="${TAB}☁️${TAB}${CL}"
|
||||
var_version="19.0"
|
||||
|
||||
THIN="discard=on,ssd=1,"
|
||||
set -e
|
||||
|
||||
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
|
||||
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"
|
||||
cleanup_vmid
|
||||
}
|
||||
|
||||
function get_valid_nextid() {
|
||||
local try_id
|
||||
try_id=$(pvesh get /cluster/nextid)
|
||||
while true; do
|
||||
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
echo "$try_id"
|
||||
}
|
||||
|
||||
function cleanup_vmid() {
|
||||
if qm status $VMID &>/dev/null; then
|
||||
qm stop $VMID &>/dev/null
|
||||
qm destroy $VMID &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
vm_require_arch amd64
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
pushd $TEMP_DIR >/dev/null
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Nextcloud VM" --yesno "This will create a New Nextcloud VM. Proceed?" 10 58; then
|
||||
:
|
||||
else
|
||||
header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
|
||||
fi
|
||||
|
||||
function msg_info() {
|
||||
local msg="$1"
|
||||
echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}"
|
||||
}
|
||||
|
||||
function msg_ok() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR}${CM}${GN}${msg}${CL}"
|
||||
}
|
||||
|
||||
function msg_error() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR}${CROSS}${RD}${msg}${CL}"
|
||||
}
|
||||
|
||||
function check_root() {
|
||||
if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then
|
||||
clear
|
||||
msg_error "Please run this script as root."
|
||||
echo -e "\nExiting..."
|
||||
sleep 2
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
|
||||
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
|
||||
pve_check() {
|
||||
local PVE_VER
|
||||
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
|
||||
|
||||
# Check for Proxmox VE 8.x: allow 8.0–8.9
|
||||
if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 9)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 8.0 – 8.9"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
|
||||
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 2)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# All other unsupported versions
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.2"
|
||||
exit 105
|
||||
}
|
||||
|
||||
function arch_check() {
|
||||
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
|
||||
echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n"
|
||||
echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n"
|
||||
echo -e "Exiting..."
|
||||
sleep 2
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
function ssh_check() {
|
||||
if command -v pveversion >/dev/null 2>&1; then
|
||||
if [ -n "${SSH_CLIENT:+x}" ]; then
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then
|
||||
echo "you've been warned"
|
||||
else
|
||||
clear
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function exit-script() {
|
||||
clear
|
||||
echo -e "\n${CROSS}${RD}User exited script${CL}\n"
|
||||
exit
|
||||
}
|
||||
pushd "$TEMP_DIR" >/dev/null
|
||||
|
||||
function default_settings() {
|
||||
vm_apply_machine_type "i440fx"
|
||||
VMID=$(get_valid_nextid)
|
||||
FORMAT=",efitype=4m"
|
||||
MACHINE=""
|
||||
DISK_SIZE="10G"
|
||||
DISK_CACHE=""
|
||||
HN="nextcloud-vm"
|
||||
@@ -225,221 +49,28 @@ function default_settings() {
|
||||
MTU=""
|
||||
START_VM="yes"
|
||||
METHOD="default"
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}"
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}"
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}"
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}"
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}"
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above default settings${CL}"
|
||||
vm_echo_default_settings
|
||||
}
|
||||
|
||||
function advanced_settings() {
|
||||
METHOD="advanced"
|
||||
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
|
||||
while true; do
|
||||
if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VMID" ]; then
|
||||
VMID=$(get_valid_nextid)
|
||||
fi
|
||||
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
|
||||
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
|
||||
sleep 2
|
||||
continue
|
||||
fi
|
||||
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
|
||||
break
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
||||
vm_prompt_machine_type "i440fx"
|
||||
vm_prompt_disk_size "10G"
|
||||
vm_prompt_disk_cache "none"
|
||||
vm_prompt_hostname "nextcloud-vm"
|
||||
vm_prompt_cpu_model "kvm64"
|
||||
vm_prompt_cpu_cores "2"
|
||||
vm_prompt_ram "2048"
|
||||
vm_prompt_bridge "vmbr0"
|
||||
vm_prompt_mac "$GEN_MAC"
|
||||
vm_prompt_vlan
|
||||
vm_prompt_mtu
|
||||
vm_prompt_keyboard
|
||||
vm_prompt_verbose "no"
|
||||
vm_prompt_start_vm "yes"
|
||||
|
||||
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
|
||||
"i440fx" "Machine i440fx" ON \
|
||||
"q35" "Machine q35" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $MACH = q35 ]; then
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
|
||||
FORMAT=""
|
||||
MACHINE=" -machine q35"
|
||||
else
|
||||
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}"
|
||||
FORMAT=",efitype=4m"
|
||||
MACHINE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ')
|
||||
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
|
||||
DISK_SIZE="${DISK_SIZE}G"
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
|
||||
elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
|
||||
else
|
||||
echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}"
|
||||
exit-script
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "None (Default)" ON \
|
||||
"1" "Write Through" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $DISK_CACHE = "1" ]; then
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}"
|
||||
DISK_CACHE="cache=writethrough,"
|
||||
else
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
||||
DISK_CACHE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 nextcloud-vm --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $VM_NAME ]; then
|
||||
HN="nextcloud-vm"
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
|
||||
if [ "$HN" != "${VM_NAME,,}" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
|
||||
fi
|
||||
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "KVM64 (Default)" ON \
|
||||
"1" "Host" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $CPU_TYPE1 = "1" ]; then
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
|
||||
CPU_TYPE=" -cpu host"
|
||||
else
|
||||
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
|
||||
CPU_TYPE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CORE_COUNT" ]; then CORE_COUNT="2"; fi
|
||||
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$RAM_SIZE" ]; then RAM_SIZE="2048"; fi
|
||||
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $BRG ]; then
|
||||
BRG="vmbr0"
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MAC1" ]; then
|
||||
MAC="$GEN_MAC"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$MAC1" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then
|
||||
MAC="$MAC1"
|
||||
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VLAN1" ]; then
|
||||
VLAN1="Default"
|
||||
VLAN=""
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then
|
||||
VLAN=",tag=$VLAN1"
|
||||
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MTU1" ]; then
|
||||
MTU1="Default"
|
||||
MTU=""
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then
|
||||
MTU=",mtu=$MTU1"
|
||||
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||
START_VM="yes"
|
||||
else
|
||||
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}"
|
||||
START_VM="no"
|
||||
fi
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Nextcloud VM?" --no-button Do-Over 10 58); then
|
||||
if vm_confirm_advanced_settings "Ready to create a Nextcloud VM?"; then
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a Nextcloud VM using the above advanced settings${CL}"
|
||||
else
|
||||
header_info
|
||||
@@ -448,145 +79,35 @@ function advanced_settings() {
|
||||
fi
|
||||
}
|
||||
|
||||
function start_script() {
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
|
||||
header_info
|
||||
echo -e "${BL}Using Default Settings${CL}"
|
||||
default_settings
|
||||
else
|
||||
header_info
|
||||
echo -e "${RD}Using Advanced Settings${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
}
|
||||
|
||||
check_root
|
||||
arch_check
|
||||
pve_check
|
||||
ssh_check
|
||||
start_script
|
||||
vm_preflight
|
||||
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 2 GB RAM\n• 10 GB Disk" 13 58
|
||||
|
||||
post_to_api_vm
|
||||
|
||||
msg_info "Validating Storage"
|
||||
while read -r line; do
|
||||
TAG=$(echo $line | awk '{print $1}')
|
||||
TYPE=$(echo $line | awk '{printf "%-10s", $2}')
|
||||
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
|
||||
ITEM=" Type: $TYPE Free: $FREE "
|
||||
OFFSET=2
|
||||
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
|
||||
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
|
||||
fi
|
||||
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
|
||||
done < <(pvesm status -content images | awk 'NR>1')
|
||||
VALID=$(pvesm status -content images | awk 'NR>1')
|
||||
if [ -z "$VALID" ]; then
|
||||
msg_error "Unable to detect a valid storage location."
|
||||
exit
|
||||
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
|
||||
STORAGE=${STORAGE_MENU[0]}
|
||||
else
|
||||
while [ -z "${STORAGE:+x}" ]; do
|
||||
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
"Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \
|
||||
16 $(($MSG_MAX_LENGTH + 23)) 6 \
|
||||
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
|
||||
done
|
||||
fi
|
||||
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
||||
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
||||
msg_info "Retrieving the URL for the $NAME Disk Image"
|
||||
URL=http://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-nextcloud-18.1-bookworm-amd64.iso
|
||||
vm_select_storage "$HN"
|
||||
msg_info "Retrieving the ${APP} installer ISO"
|
||||
URL=https://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-nextcloud-19.0-trixie-amd64.iso
|
||||
sleep 2
|
||||
msg_ok "${CL}${BL}${URL}${CL}"
|
||||
curl -f#SL -o "$(basename "$URL")" "$URL"
|
||||
echo -en "\e[1A\e[0K"
|
||||
FILE=$(basename $URL)
|
||||
msg_ok "Downloaded ${CL}${BL}${FILE}${CL}"
|
||||
vm_select_iso_storage "$(basename "$URL")" "$HN"
|
||||
vm_fetch_image "$URL" "$ISO_PATH" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115
|
||||
|
||||
STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}')
|
||||
case $STORAGE_TYPE in
|
||||
nfs | dir)
|
||||
DISK_EXT=".raw"
|
||||
DISK_REF="$VMID/"
|
||||
DISK_IMPORT="-format raw"
|
||||
THIN=""
|
||||
;;
|
||||
btrfs)
|
||||
DISK_EXT=".raw"
|
||||
DISK_REF="$VMID/"
|
||||
DISK_IMPORT="-format raw"
|
||||
FORMAT=",efitype=4m"
|
||||
THIN=""
|
||||
;;
|
||||
*)
|
||||
DISK_EXT=""
|
||||
DISK_REF=""
|
||||
DISK_IMPORT="-format raw"
|
||||
;;
|
||||
esac
|
||||
for i in {0,1,2}; do
|
||||
disk="DISK$i"
|
||||
eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-}
|
||||
eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk}
|
||||
done
|
||||
vm_claim_vmid
|
||||
msg_info "Creating a ${APP}"
|
||||
qm create $VMID -agent 1${MACHINE} -tablet 0 -bios seabios${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
|
||||
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci \
|
||||
-scsi0 "${STORAGE}:${DISK_SIZE%G},${DISK_CACHE}${THIN%,}" \
|
||||
-cdrom "$ISO_VOLUME" -boot order='scsi0;ide2' >/dev/null
|
||||
vm_mark_created
|
||||
set_description
|
||||
|
||||
msg_info "Creating a $NAME"
|
||||
qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios seabios${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
|
||||
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
|
||||
pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null
|
||||
pvesm alloc $STORAGE $VMID $DISK1 12G 1>&/dev/null
|
||||
qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
|
||||
qm set $VMID \
|
||||
-efidisk0 ${DISK0_REF}${FORMAT} \
|
||||
-scsi0 ${DISK1_REF},${DISK_CACHE}${THIN} \
|
||||
-scsi1 ${DISK2_REF},${DISK_CACHE}${THIN} \
|
||||
-boot order='scsi1;scsi0' >/dev/null
|
||||
DESCRIPTION=$(
|
||||
cat <<EOF
|
||||
<div align='center'>
|
||||
<a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
|
||||
</a>
|
||||
|
||||
<h2 style='font-size: 24px; margin: 20px 0;'>Nextcloud VM</h2>
|
||||
|
||||
<p style='margin: 16px 0;'>
|
||||
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
|
||||
</span>
|
||||
</div>
|
||||
EOF
|
||||
)
|
||||
qm set $VMID -description "$DESCRIPTION" >/dev/null
|
||||
if [ -n "$DISK_SIZE" ]; then
|
||||
msg_info "Resizing disk to $DISK_SIZE GB"
|
||||
qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null
|
||||
else
|
||||
msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB"
|
||||
qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null
|
||||
fi
|
||||
|
||||
msg_ok "Created a $NAME ${CL}${BL}(${HN})"
|
||||
if [ "$START_VM" == "yes" ]; then
|
||||
msg_info "Starting $NAME"
|
||||
qm start $VMID
|
||||
msg_ok "Started $NAME"
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
msg_ok "Completed successfully!\n"
|
||||
msg_ok "Created a ${APP} ${CL}${BL}(${HN})"
|
||||
vm_start_vm "$APP"
|
||||
vm_print_summary "Version=TurnKey Nextcloud ${var_version}" "Installer=$(basename "$URL")" "Web UI=https://<VM-IP>/" "Webmin=https://<VM-IP>:12321/"
|
||||
vm_next_steps \
|
||||
"Open the VM Console in Proxmox and complete the TurnKey installer." \
|
||||
"Set the root, Nextcloud admin, and database credentials when prompted." \
|
||||
"After installation, detach the ISO: qm set ${VMID} --ide2 none." \
|
||||
"Start or reboot the VM, then open https://<VM-IP>/ for Nextcloud." \
|
||||
"Manage the appliance through Webmin at https://<VM-IP>:12321/."
|
||||
vm_finish "VM created; complete the TurnKey Nextcloud installation in the Proxmox console."
|
||||
|
||||
+175
-502
@@ -1,30 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# Jon Spriggs (jontheniceguy)
|
||||
# Author: tteck (tteckster) | Jon Spriggs (jontheniceguy) | MickLesk (CanbiZ)
|
||||
# License: MIT
|
||||
# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Based on work from https://i12bretro.github.io/tutorials/0405.html
|
||||
|
||||
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
|
||||
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
|
||||
|
||||
function header_info {
|
||||
clear
|
||||
cat <<"EOF"
|
||||
____ _ __ __
|
||||
/ __ \____ ___ ____| | / /____/ /_
|
||||
/ / / / __ \/ _ \/ __ \ | /| / / ___/ __/
|
||||
/ /_/ / /_/ / __/ / / / |/ |/ / / / /_
|
||||
\____/ .___/\___/_/ /_/|__/|__/_/ \__/
|
||||
/_/ W I R E L E S S F R E E D O M
|
||||
|
||||
EOF
|
||||
}
|
||||
header_info
|
||||
echo -e "\n Loading..."
|
||||
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
METHOD=""
|
||||
APP="OpenWrt"
|
||||
APP_TYPE="vm"
|
||||
NSAPP="openwrt-vm"
|
||||
var_os="openwrt"
|
||||
var_version=" "
|
||||
@@ -32,94 +19,27 @@ 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/.$//')
|
||||
|
||||
YW=$(echo "\033[33m")
|
||||
BL=$(echo "\033[36m")
|
||||
HA=$(echo "\033[1;34m")
|
||||
RD=$(echo "\033[01;31m")
|
||||
BGN=$(echo "\033[4;92m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
DGN=$(echo "\033[32m")
|
||||
CL=$(echo "\033[m")
|
||||
|
||||
BOLD=$(echo "\033[1m")
|
||||
BFR="\\r\\033[K"
|
||||
HOLD=" "
|
||||
TAB=" "
|
||||
|
||||
CM="${TAB}✔️${TAB}${CL}"
|
||||
CROSS="${TAB}✖️${TAB}${CL}"
|
||||
INFO="${TAB}💡${TAB}${CL}"
|
||||
OS="${TAB}🖥️${TAB}${CL}"
|
||||
CONTAINERTYPE="${TAB}📦${TAB}${CL}"
|
||||
DISKSIZE="${TAB}💾${TAB}${CL}"
|
||||
CPUCORE="${TAB}🧠${TAB}${CL}"
|
||||
RAMSIZE="${TAB}🛠️${TAB}${CL}"
|
||||
CONTAINERID="${TAB}🆔${TAB}${CL}"
|
||||
HOSTNAME="${TAB}🏠${TAB}${CL}"
|
||||
BRIDGE="${TAB}🌉${TAB}${CL}"
|
||||
GATEWAY="${TAB}🌐${TAB}${CL}"
|
||||
DEFAULT="${TAB}⚙️${TAB}${CL}"
|
||||
MACADDRESS="${TAB}🔗${TAB}${CL}"
|
||||
VLANTAG="${TAB}🏷️${TAB}${CL}"
|
||||
CREATING="${TAB}🚀${TAB}${CL}"
|
||||
ADVANCED="${TAB}🧩${TAB}${CL}"
|
||||
CLOUD="${TAB}☁️${TAB}${CL}"
|
||||
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
|
||||
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"
|
||||
cleanup_vmid
|
||||
}
|
||||
|
||||
function get_valid_nextid() {
|
||||
local try_id
|
||||
try_id=$(pvesh get /cluster/nextid)
|
||||
while true; do
|
||||
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
echo "$try_id"
|
||||
}
|
||||
|
||||
function cleanup_vmid() {
|
||||
if qm status $VMID &>/dev/null; then
|
||||
qm stop $VMID &>/dev/null
|
||||
qm destroy $VMID &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
vm_require_arch amd64
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
pushd $TEMP_DIR >/dev/null
|
||||
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
|
||||
@@ -171,7 +91,7 @@ function send_line_to_vm() {
|
||||
"U") character="shift-u" ;;
|
||||
"V") character="shift-v" ;;
|
||||
"W") character="shift-w" ;;
|
||||
"X") character="shift=x" ;;
|
||||
"X") character="shift-x" ;;
|
||||
"Y") character="shift-y" ;;
|
||||
"Z") character="shift-z" ;;
|
||||
"!") character="shift-1" ;;
|
||||
@@ -190,97 +110,92 @@ function send_line_to_vm() {
|
||||
qm sendkey $VMID ret
|
||||
}
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
pushd $TEMP_DIR >/dev/null
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "OpenWrt VM" --yesno "This will create a New OpenWrt VM. Proceed?" 10 58); then
|
||||
:
|
||||
else
|
||||
header_info && echo -e "⚠ User exited script \n" && exit
|
||||
fi
|
||||
|
||||
function msg_info() {
|
||||
local msg="$1"
|
||||
echo -ne " ${HOLD} ${YW}${msg}..."
|
||||
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 msg_ok() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR} ${CM} ${GN}${msg}${CL}"
|
||||
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 msg_error() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}"
|
||||
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}"
|
||||
}
|
||||
|
||||
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
|
||||
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
|
||||
pve_check() {
|
||||
local PVE_VER
|
||||
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
|
||||
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}"
|
||||
}
|
||||
|
||||
# Check for Proxmox VE 8.x: allow 8.0–8.9
|
||||
if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 9)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 8.0 – 8.9"
|
||||
exit 105
|
||||
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
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
|
||||
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 2)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
|
||||
exit 105
|
||||
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
|
||||
return 0
|
||||
fi
|
||||
|
||||
# All other unsupported versions
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.2"
|
||||
exit 105
|
||||
vm_dialog msgbox "INVALID INPUT" "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
|
||||
done
|
||||
}
|
||||
|
||||
function arch_check() {
|
||||
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
|
||||
echo -e "\n ${CROSS} This script will not work with PiMox! \n"
|
||||
echo -e "Exiting..."
|
||||
sleep 2
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
function ssh_check() {
|
||||
if command -v pveversion >/dev/null 2>&1; then
|
||||
if [ -n "${SSH_CLIENT:+x}" ]; then
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then
|
||||
echo "you've been warned"
|
||||
else
|
||||
clear
|
||||
exit
|
||||
fi
|
||||
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
|
||||
fi
|
||||
}
|
||||
|
||||
function exit-script() {
|
||||
clear
|
||||
echo -e "⚠ User exited script \n"
|
||||
exit
|
||||
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
|
||||
@@ -306,198 +221,30 @@ function default_settings() {
|
||||
|
||||
function advanced_settings() {
|
||||
METHOD="advanced"
|
||||
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
|
||||
while true; do
|
||||
if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VMID" ]; then
|
||||
VMID=$(get_valid_nextid)
|
||||
fi
|
||||
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
|
||||
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
|
||||
sleep 2
|
||||
continue
|
||||
fi
|
||||
echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
|
||||
break
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
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_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 openwrt --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $VM_NAME ]; then
|
||||
HN="openwrt"
|
||||
else
|
||||
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
|
||||
if [ "$HN" != "${VM_NAME,,}" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
|
||||
fi
|
||||
fi
|
||||
echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 1 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CORE_COUNT" ]; then CORE_COUNT="1"; fi
|
||||
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 1)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 256 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$RAM_SIZE" ]; then RAM_SIZE="256"; fi
|
||||
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 256)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \
|
||||
--inputbox "Set Disk Size in GiB (e.g., 1, 2, 4)" 8 58 "1" \
|
||||
--title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
|
||||
DISK_SIZE="${DISK_SIZE}G"
|
||||
fi
|
||||
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN Bridge" 8 58 vmbr0 --title "WAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $BRG ]; then
|
||||
BRG="vmbr0"
|
||||
fi
|
||||
echo -e "${DGN}Using WAN Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if LAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Bridge" 8 58 vmbr0 --title "LAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $LAN_BRG ]; then
|
||||
LAN_BRG="vmbr0"
|
||||
fi
|
||||
echo -e "${DGN}Using LAN Bridge: ${BGN}$LAN_BRG${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if LAN_IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a router IP" 8 58 $LAN_IP_ADDR --title "LAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $LAN_IP_ADDR ]; then
|
||||
LAN_IP_ADDR="192.168.1.1"
|
||||
fi
|
||||
echo -e "${DGN}Using LAN IP ADDRESS: ${BGN}$LAN_IP_ADDR${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if LAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a router netmask" 8 58 $LAN_NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $LAN_NETMASK ]; then
|
||||
LAN_NETMASK="255.255.255.0"
|
||||
fi
|
||||
echo -e "${DGN}Using LAN NETMASK: ${BGN}$LAN_NETMASK${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $MAC1 ]; then
|
||||
MAC="$GEN_MAC"
|
||||
else
|
||||
MAC="$MAC1"
|
||||
fi
|
||||
echo -e "${DGN}Using WAN MAC Address: ${BGN}$MAC${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC_LAN --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $MAC2 ]; then
|
||||
LAN_MAC="$GEN_MAC_LAN"
|
||||
else
|
||||
LAN_MAC="$MAC2"
|
||||
fi
|
||||
echo -e "${DGN}Using LAN MAC Address: ${BGN}$LAN_MAC${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN Vlan (leave blank for default)" 8 58 --title "WAN VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VLAN1" ]; then
|
||||
VLAN1="Default"
|
||||
VLAN=""
|
||||
echo -e "${DGN}Using WAN Vlan: ${BGN}$VLAN1${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$VLAN1" =~ ^[0-9]+$ ]] && [ "$VLAN1" -ge 1 ] && [ "$VLAN1" -le 4094 ]; then
|
||||
VLAN=",tag=$VLAN1"
|
||||
echo -e "${DGN}Using WAN Vlan: ${BGN}$VLAN1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if VLAN2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Vlan" 8 58 999 --title "LAN VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VLAN2" ]; then
|
||||
VLAN2="Default"
|
||||
LAN_VLAN=""
|
||||
echo -e "${DGN}Using LAN Vlan: ${BGN}$VLAN2${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$VLAN2" =~ ^[0-9]+$ ]] && [ "$VLAN2" -ge 1 ] && [ "$VLAN2" -le 4094 ]; then
|
||||
LAN_VLAN=",tag=$VLAN2"
|
||||
echo -e "${DGN}Using LAN Vlan: ${BGN}$VLAN2${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$MTU1" ]; then
|
||||
MTU1="Default"
|
||||
MTU=""
|
||||
echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
break
|
||||
fi
|
||||
if [[ "$MTU1" =~ ^[0-9]+$ ]] && [ "$MTU1" -ge 576 ] && [ "$MTU1" -le 65520 ]; then
|
||||
MTU=",mtu=$MTU1"
|
||||
echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then
|
||||
START_VM="yes"
|
||||
else
|
||||
START_VM="no"
|
||||
fi
|
||||
echo -e "${DGN}Start VM when completed: ${BGN}$START_VM${CL}"
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create OpenWrt VM?" --no-button Do-Over 10 58); then
|
||||
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
|
||||
@@ -506,174 +253,92 @@ function advanced_settings() {
|
||||
fi
|
||||
}
|
||||
|
||||
function start_script() {
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
|
||||
header_info
|
||||
echo -e "${BL}Using Default Settings${CL}"
|
||||
default_settings
|
||||
else
|
||||
header_info
|
||||
echo -e "${RD}Using Advanced Settings${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
}
|
||||
|
||||
arch_check
|
||||
pve_check
|
||||
ssh_check
|
||||
start_script
|
||||
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
|
||||
|
||||
msg_info "Validating Storage"
|
||||
while read -r line; do
|
||||
TAG=$(echo $line | awk '{print $1}')
|
||||
TYPE=$(echo $line | awk '{printf "%-10s", $2}')
|
||||
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
|
||||
ITEM=" Type: $TYPE Free: $FREE "
|
||||
OFFSET=2
|
||||
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
|
||||
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
|
||||
fi
|
||||
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
|
||||
done < <(pvesm status -content images | awk 'NR>1')
|
||||
VALID=$(pvesm status -content images | awk 'NR>1')
|
||||
if [ -z "$VALID" ]; then
|
||||
echo -e "\n${RD}⚠ Unable to detect a valid storage location.${CL}"
|
||||
echo -e "Exiting..."
|
||||
exit
|
||||
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
|
||||
STORAGE=${STORAGE_MENU[0]}
|
||||
else
|
||||
while [ -z "${STORAGE:+x}" ]; do
|
||||
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
"Which storage pool would you like to use for the OpenWrt VM?\n\n" \
|
||||
16 $(($MSG_MAX_LENGTH + 23)) 6 \
|
||||
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
|
||||
done
|
||||
fi
|
||||
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
||||
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
||||
vm_select_storage "$HN"
|
||||
msg_info "Getting URL for OpenWrt Disk Image"
|
||||
|
||||
response=$(curl -fsSL https://openwrt.org)
|
||||
stableversion=$(echo "$response" | sed -n 's/.*Current stable release - OpenWrt \([0-9.]\+\).*/\1/p' | head -n 1)
|
||||
URL="https://downloads.openwrt.org/releases/$stableversion/targets/x86/64/openwrt-$stableversion-x86-64-generic-ext4-combined.img.gz"
|
||||
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}"
|
||||
curl -f#SL -o "$(basename "$URL")" "$URL"
|
||||
FILE=$(basename "$URL")
|
||||
msg_ok "Downloaded ${CL}${BL}$FILE${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
|
||||
|
||||
gunzip -f "$FILE" >/dev/null 2>&1 || true
|
||||
FILE="${FILE%.*}"
|
||||
msg_ok "Extracted OpenWrt Disk Image ${CL}${BL}$FILE${CL}"
|
||||
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
|
||||
if [[ "$(pvesm status | awk -v s=$STORAGE '$1==s {print $2}')" == "dir" ]]; then
|
||||
qm set $VMID -efidisk0 ${STORAGE}:0,efitype=4m,size=4M
|
||||
else
|
||||
pvesm alloc $STORAGE $VMID vm-$VMID-disk-0 4M >/dev/null
|
||||
qm set $VMID -efidisk0 ${STORAGE}:vm-$VMID-disk-0,efitype=4m,size=4M
|
||||
fi
|
||||
-onboot 1 -ostype l26 -scsihw virtio-scsi-pci --tablet 0 >/dev/null
|
||||
vm_mark_created
|
||||
vm_import_disk "$VMID" "$FILE" "$STORAGE"
|
||||
|
||||
IMPORT_OUT="$(qm importdisk $VMID $FILE $STORAGE --format raw 2>&1 || true)"
|
||||
DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p")"
|
||||
|
||||
if [[ -z "$DISK_REF" ]]; then
|
||||
DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$1 ~ ("vm-"id"-disk-") {print $1}' | sort | tail -n1)"
|
||||
fi
|
||||
|
||||
if [[ -z "$DISK_REF" ]]; then
|
||||
msg_error "Unable to determine imported disk reference."
|
||||
echo "$IMPORT_OUT"
|
||||
exit 226
|
||||
fi
|
||||
|
||||
qm set $VMID \
|
||||
-efidisk0 ${STORAGE}:0,efitype=4m,size=4M \
|
||||
-scsi0 ${DISK_REF} \
|
||||
$STD qm set $VMID \
|
||||
-scsi0 "${VM_IMPORTED_DISK}" \
|
||||
-boot order=scsi0 \
|
||||
-tags community-script >/dev/null
|
||||
-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}"
|
||||
|
||||
DESCRIPTION=$(
|
||||
cat <<EOF
|
||||
<div align='center'>
|
||||
<a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
|
||||
</a>
|
||||
|
||||
<h2 style='font-size: 24px; margin: 20px 0;'>OpenWrt VM</h2>
|
||||
|
||||
<p style='margin: 16px 0;'>
|
||||
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
|
||||
</span>
|
||||
</div>
|
||||
EOF
|
||||
)
|
||||
qm set $VMID -description "$DESCRIPTION" >/dev/null
|
||||
|
||||
set_description
|
||||
msg_ok "Created OpenWrt VM ${CL}${BL}(${HN})"
|
||||
msg_info "OpenWrt is being started in order to configure the network interfaces."
|
||||
qm start $VMID
|
||||
|
||||
msg_info "Booting OpenWrt to configure its network interfaces"
|
||||
$STD qm start $VMID
|
||||
sleep 15
|
||||
msg_info "Waiting for OpenWrt to boot..."
|
||||
VM_STATE=""
|
||||
for i in {1..30}; do
|
||||
if qm status "$VMID" | grep -q "running"; then
|
||||
sleep 5
|
||||
msg_ok "OpenWrt is running"
|
||||
break
|
||||
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
|
||||
|
||||
msg_ok "Network interfaces are being configured as OpenWrt initiates."
|
||||
|
||||
if qm status "$VMID" | grep -q "running"; then
|
||||
send_line_to_vm ""
|
||||
send_line_to_vm "uci delete network.@device[0]"
|
||||
send_line_to_vm "uci set network.wan=interface"
|
||||
send_line_to_vm "uci set network.wan.device=eth1"
|
||||
send_line_to_vm "uci set network.wan.proto=dhcp"
|
||||
send_line_to_vm "uci delete network.lan"
|
||||
send_line_to_vm "uci set network.lan=interface"
|
||||
send_line_to_vm "uci set network.lan.device=eth0"
|
||||
send_line_to_vm "uci set network.lan.proto=static"
|
||||
send_line_to_vm "uci set network.lan.ipaddr=${LAN_IP_ADDR}"
|
||||
send_line_to_vm "uci set network.lan.netmask=${LAN_NETMASK}"
|
||||
send_line_to_vm "uci commit"
|
||||
send_line_to_vm "poweroff"
|
||||
msg_ok "Network interfaces configured in OpenWrt"
|
||||
else
|
||||
msg_error "VM is not running"
|
||||
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 "Waiting for OpenWrt to shut down..."
|
||||
until qm status "$VMID" | grep -q "stopped"; do
|
||||
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"
|
||||
@@ -684,13 +349,21 @@ msg_ok "Bridge interfaces added"
|
||||
|
||||
if [ "$START_VM" = "yes" ]; then
|
||||
msg_info "Starting OpenWrt VM"
|
||||
qm start $VMID
|
||||
$STD qm start $VMID
|
||||
msg_ok "Started OpenWrt VM"
|
||||
fi
|
||||
|
||||
VLAN_FINISH=""
|
||||
if [ -z "$VLAN" ] && [ "$VLAN2" != "999" ]; then
|
||||
if [ -z "$VLAN" ] && [ "${VLAN2:-999}" != "999" ]; then
|
||||
VLAN_FINISH=" Please remember to adjust the VLAN tags to suit your network."
|
||||
fi
|
||||
post_update_to_api "done" "none"
|
||||
msg_ok "Completed Successfully!${VLAN_FINISH:+\n$VLAN_FINISH}"
|
||||
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
|
||||
|
||||
+256
-604
@@ -1,27 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: michelroegl-brunner
|
||||
# Author: michelroegl-brunner | MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
|
||||
source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
|
||||
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
||||
load_functions
|
||||
|
||||
function header_info {
|
||||
clear
|
||||
cat <<"EOF"
|
||||
____ ____ _ __
|
||||
/ __ \/ __ \/ | / /_______ ____ ________
|
||||
/ / / / /_/ / |/ / ___/ _ \/ __ \/ ___/ _ \
|
||||
/ /_/ / ____/ /| (__ ) __/ / / (__ ) __/
|
||||
\____/_/ /_/ |_/____/\___/_/ /_/____/\___/
|
||||
|
||||
EOF
|
||||
}
|
||||
header_info
|
||||
echo -e "Loading..."
|
||||
#API VARIABLES
|
||||
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
METHOD=""
|
||||
APP="OPNsense"
|
||||
APP_TYPE="vm"
|
||||
NSAPP="opnsense-vm"
|
||||
var_os="opnsense"
|
||||
var_version="26.7"
|
||||
@@ -30,70 +20,20 @@ FREEBSD_MAJOR="15"
|
||||
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/.$//')
|
||||
|
||||
YW=$(echo "\033[33m")
|
||||
BL=$(echo "\033[36m")
|
||||
HA=$(echo "\033[1;34m")
|
||||
RD=$(echo "\033[01;31m")
|
||||
BGN=$(echo "\033[4;92m")
|
||||
GN=$(echo "\033[1;92m")
|
||||
DGN=$(echo "\033[32m")
|
||||
CL=$(echo "\033[m")
|
||||
BFR="\\r\\033[K"
|
||||
HOLD="-"
|
||||
CM="${GN}✓${CL}"
|
||||
CROSS="${RD}✗${CL}"
|
||||
THIN="discard=on,ssd=1,"
|
||||
|
||||
header_info
|
||||
echo -e "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
|
||||
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"
|
||||
cleanup_vmid
|
||||
}
|
||||
|
||||
function get_valid_nextid() {
|
||||
local try_id
|
||||
try_id=$(pvesh get /cluster/nextid)
|
||||
while true; do
|
||||
if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then
|
||||
try_id=$((try_id + 1))
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
echo "$try_id"
|
||||
}
|
||||
|
||||
function cleanup_vmid() {
|
||||
if qm status $VMID &>/dev/null; then
|
||||
qm stop $VMID &>/dev/null
|
||||
qm destroy $VMID &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
local exit_code=$?
|
||||
popd >/dev/null
|
||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
post_update_to_api "done" "none"
|
||||
else
|
||||
post_update_to_api "failed" "$exit_code"
|
||||
fi
|
||||
fi
|
||||
rm -rf $TEMP_DIR
|
||||
}
|
||||
vm_require_arch amd64
|
||||
|
||||
function check_disk_space() {
|
||||
local path="$1"
|
||||
@@ -106,16 +46,16 @@ function check_disk_space() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# Use disk-backed temp directory to avoid tmpfs/RAM size limits in /tmp
|
||||
if [ -d "/var/tmp" ] && check_disk_space "/var/tmp" 20; then
|
||||
TEMP_DIR=$(mktemp -d /var/tmp/opnsense-vm.XXXXXX)
|
||||
elif [ -d "/tmp" ] && check_disk_space "/tmp" 20; then
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
else
|
||||
# Fallback: try /var/tmp anyway, disk space check will catch it later
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
if ! check_disk_space "$TEMP_DIR" 20 && [ -d "/var/tmp" ] && check_disk_space "/var/tmp" 20; then
|
||||
rm -rf "$TEMP_DIR"
|
||||
TEMP_DIR=$(mktemp -d /var/tmp/opnsense-vm.XXXXXX)
|
||||
fi
|
||||
pushd $TEMP_DIR >/dev/null
|
||||
pushd "$TEMP_DIR" >/dev/null
|
||||
|
||||
vm_preflight
|
||||
vm_require_tools xz
|
||||
|
||||
function send_line_to_vm() {
|
||||
echo -e "${DGN}Sending line: ${YW}$1${CL}"
|
||||
for ((i = 0; i < ${#1}; i++)); do
|
||||
@@ -186,95 +126,139 @@ function send_line_to_vm() {
|
||||
qm sendkey $VMID ret
|
||||
}
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "OPNsense VM" --yesno "This will create a New OPNsense VM. Proceed?" 10 58); then
|
||||
:
|
||||
else
|
||||
header_info && echo -e "⚠ User exited script \n" && exit
|
||||
fi
|
||||
|
||||
function msg_info() {
|
||||
local msg="$1"
|
||||
echo -ne " ${HOLD} ${YW}${msg}..."
|
||||
}
|
||||
|
||||
function msg_ok() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR} ${CM} ${GN}${msg}${CL}"
|
||||
}
|
||||
|
||||
function msg_error() {
|
||||
local msg="$1"
|
||||
echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}"
|
||||
}
|
||||
|
||||
# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported.
|
||||
# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.2
|
||||
pve_check() {
|
||||
local PVE_VER
|
||||
PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')"
|
||||
|
||||
# Check for Proxmox VE 8.x: allow 8.0–8.9
|
||||
if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 9)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 8.0 – 8.9"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check for Proxmox VE 9.x: allow 9.0 and 9.2
|
||||
if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then
|
||||
local MINOR="${BASH_REMATCH[1]}"
|
||||
if ((MINOR < 0 || MINOR > 2)); then
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported: Proxmox VE version 9.0 – 9.2"
|
||||
exit 105
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# All other unsupported versions
|
||||
msg_error "This version of Proxmox VE is not supported."
|
||||
msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.2"
|
||||
exit 105
|
||||
}
|
||||
|
||||
function arch_check() {
|
||||
if [ "$(dpkg --print-architecture)" != "amd64" ]; then
|
||||
echo -e "\n ${CROSS} This script will not work with PiMox! \n"
|
||||
echo -e "Exiting..."
|
||||
sleep 2
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
function ssh_check() {
|
||||
if command -v pveversion >/dev/null 2>&1; then
|
||||
if [ -n "${SSH_CLIENT:+x}" ]; then
|
||||
if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then
|
||||
echo "you've been warned"
|
||||
else
|
||||
clear
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function exit-script() {
|
||||
clear
|
||||
echo -e "⚠ User exited script \n"
|
||||
exit
|
||||
}
|
||||
|
||||
function get_available_bridges() {
|
||||
ip -o link show type bridge 2>/dev/null | awk -F': ' '{print $2}' | sort
|
||||
}
|
||||
|
||||
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"
|
||||
printf -v "$var_name" '%s' "$value"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
}
|
||||
|
||||
function prompt_optional_static_ip() {
|
||||
local ip_var="$1" gw_var="$2" mask_var="$3" prefix="$4" ip_value gw_value mask_value
|
||||
prompt_router_input "$ip_var" "${prefix} IP ADDRESS" "Set a ${prefix} IP" "${!ip_var:-}"
|
||||
ip_value="${!ip_var}"
|
||||
if [ -z "$ip_value" ]; then
|
||||
echo -e "${DGN}Using DHCP AS ${prefix} IP ADDRESS${CL}"
|
||||
return 0
|
||||
fi
|
||||
if ! validate_ip_octets "$ip_value"; then
|
||||
msg_error "Invalid IP Address format for ${prefix} IP. Needs to be 0.0.0.0, was $ip_value"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${DGN}Using ${prefix} IP ADDRESS: ${BGN}$ip_value${CL}"
|
||||
|
||||
prompt_router_input "$gw_var" "${prefix} GATEWAY IP ADDRESS" "Set a ${prefix} GATEWAY IP" "${!gw_var:-}"
|
||||
gw_value="${!gw_var}"
|
||||
if [ -z "$gw_value" ]; then
|
||||
msg_error "${prefix} gateway is required for a static IP."
|
||||
exit 1
|
||||
fi
|
||||
if ! validate_ip_octets "$gw_value"; then
|
||||
msg_error "Invalid IP Address format for ${prefix} Gateway. Needs to be 0.0.0.0, was $gw_value"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${DGN}Using ${prefix} GATEWAY ADDRESS: ${BGN}$gw_value${CL}"
|
||||
|
||||
prompt_router_input "$mask_var" "${prefix} NETMASK" "Set a ${prefix} netmask (24 for example)" "${!mask_var:-}"
|
||||
mask_value="${!mask_var}"
|
||||
if [ -z "$mask_value" ]; then
|
||||
msg_error "${prefix} netmask is required for a static IP."
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "$mask_value" =~ ^[0-9]+$ || "$mask_value" -lt 1 || "$mask_value" -gt 32 ]]; then
|
||||
msg_error "Invalid ${prefix} NETMASK format. Needs to be 1-32, was $mask_value"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${DGN}Using ${prefix} NETMASK: ${BGN}$mask_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}"
|
||||
[[ -n "$value" ]] || value="$default_value"
|
||||
printf -v "$var_name" '%s' "$value"
|
||||
if ! validate_mac_address "$value"; then
|
||||
msg_error "Invalid ${label}: $value"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${DGN}Using ${label}: ${BGN}$value${CL}"
|
||||
}
|
||||
|
||||
function select_network_mode() {
|
||||
local available_bridges bridge_count default_wan_brg
|
||||
available_bridges=$(get_available_bridges)
|
||||
bridge_count=$(echo "$available_bridges" | wc -l)
|
||||
default_wan_brg=$(echo "$available_bridges" | grep -v "^${BRG}$" | head -n1 || true)
|
||||
|
||||
if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
|
||||
WAN_BRG="${VM_WAN_BRIDGE:-}"
|
||||
elif [ "$bridge_count" -ge 2 ]; then
|
||||
if vm_dialog radiolist "NETWORK CONFIGURATION" --cancel-button Exit-Script \
|
||||
"Choose network setup mode for OPNsense:\n" 14 70 2 \
|
||||
"dual" "Dual Interface (Firewall/Router) - uses ${default_wan_brg}" ON \
|
||||
"single" "Single Interface (Proxy/VPN/IDS Server)" OFF; then
|
||||
if [ "$VM_DIALOG_RESULT" = "dual" ]; then
|
||||
WAN_BRG="$default_wan_brg"
|
||||
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
|
||||
echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}"
|
||||
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
|
||||
else
|
||||
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
||||
WAN_BRG=""
|
||||
fi
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
else
|
||||
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
||||
echo -e "${YW} (Only one bridge detected, dual interface requires a second bridge)${CL}"
|
||||
WAN_BRG=""
|
||||
fi
|
||||
}
|
||||
|
||||
function prompt_wan_bridge() {
|
||||
local wan_bridges wan_menu=() first=true brg
|
||||
wan_bridges=$(get_available_bridges | grep -v "^${BRG}$" || true)
|
||||
if [ -z "$wan_bridges" ]; then
|
||||
WAN_BRG=""
|
||||
msg_warn "Only one bridge is available; using single-interface mode."
|
||||
return 0
|
||||
fi
|
||||
while IFS= read -r brg; do
|
||||
if $first; then
|
||||
wan_menu+=("$brg" "" "ON")
|
||||
first=false
|
||||
else
|
||||
wan_menu+=("$brg" "" "OFF")
|
||||
fi
|
||||
done <<<"$wan_bridges"
|
||||
|
||||
if vm_dialog radiolist "WAN BRIDGE" "Select WAN Bridge" 14 58 6 "${wan_menu[@]}"; then
|
||||
WAN_BRG="$VM_DIALOG_RESULT"
|
||||
[[ -n "$WAN_BRG" ]] || WAN_BRG=$(echo "$wan_bridges" | head -n1)
|
||||
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
}
|
||||
|
||||
function default_settings() {
|
||||
vm_apply_machine_type "i440fx"
|
||||
VMID=$(get_valid_nextid)
|
||||
DISK_SIZE="20G"
|
||||
FORMAT=",efitype=4m"
|
||||
MACHINE=""
|
||||
DISK_CACHE=""
|
||||
@@ -297,12 +281,6 @@ function default_settings() {
|
||||
START_VM="yes"
|
||||
METHOD="default"
|
||||
|
||||
# Detect available bridges
|
||||
local AVAILABLE_BRIDGES
|
||||
AVAILABLE_BRIDGES=$(get_available_bridges)
|
||||
local BRIDGE_COUNT
|
||||
BRIDGE_COUNT=$(echo "$AVAILABLE_BRIDGES" | wc -l)
|
||||
|
||||
echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}"
|
||||
echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}"
|
||||
echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
@@ -315,289 +293,55 @@ function default_settings() {
|
||||
fi
|
||||
echo -e "${DGN}Using LAN VLAN: ${BGN}Default${CL}"
|
||||
echo -e "${DGN}Using LAN MAC Address: ${BGN}${MAC}${CL}"
|
||||
|
||||
# Determine available network modes based on bridge count
|
||||
local DEFAULT_WAN_BRG
|
||||
DEFAULT_WAN_BRG=$(echo "$AVAILABLE_BRIDGES" | grep -v "^${BRG}$" | head -n1 || true)
|
||||
|
||||
if [ "$BRIDGE_COUNT" -ge 2 ]; then
|
||||
# Multiple bridges available - offer dual or single mode
|
||||
if NETWORK_MODE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK CONFIGURATION" --radiolist --cancel-button Exit-Script \
|
||||
"Choose network setup mode for OPNsense:\n" 14 70 2 \
|
||||
"dual" "Dual Interface (Firewall/Router) - uses ${DEFAULT_WAN_BRG}" ON \
|
||||
"single" "Single Interface (Proxy/VPN/IDS Server)" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ "$NETWORK_MODE" = "dual" ]; then
|
||||
WAN_BRG="$DEFAULT_WAN_BRG"
|
||||
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
|
||||
echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}"
|
||||
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
|
||||
else
|
||||
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
||||
WAN_BRG=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
else
|
||||
# Only one bridge available - single interface mode only
|
||||
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
||||
echo -e "${YW} (Only one bridge detected, dual interface requires a second bridge)${CL}"
|
||||
WAN_BRG=""
|
||||
fi
|
||||
select_network_mode
|
||||
echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}"
|
||||
echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||
echo -e "${BL}Creating a OPNsense VM using the above default settings${CL}"
|
||||
}
|
||||
|
||||
function advanced_settings() {
|
||||
local ip_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$'
|
||||
METHOD="advanced"
|
||||
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
|
||||
while true; do
|
||||
if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VMID" ]; then
|
||||
VMID=$(get_valid_nextid)
|
||||
fi
|
||||
if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
|
||||
echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
|
||||
sleep 2
|
||||
continue
|
||||
fi
|
||||
echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
|
||||
break
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
IP_ADDR=""
|
||||
WAN_IP_ADDR=""
|
||||
LAN_GW=""
|
||||
WAN_GW=""
|
||||
NETMASK=""
|
||||
WAN_NETMASK=""
|
||||
VLAN=""
|
||||
MTU=""
|
||||
vm_prompt_disk_size "20G"
|
||||
vm_prompt_keyboard
|
||||
vm_prompt_verbose "no"
|
||||
vm_prompt_start_vm "yes"
|
||||
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
||||
vm_prompt_machine_type "i440fx"
|
||||
vm_prompt_cpu_model "kvm64"
|
||||
vm_prompt_disk_cache "none"
|
||||
vm_prompt_hostname "opnsense"
|
||||
vm_prompt_cpu_cores "4"
|
||||
vm_prompt_ram "8192"
|
||||
|
||||
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
|
||||
"i440fx" "Machine i440fx" ON \
|
||||
"q35" "Machine q35" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $MACH = q35 ]; then
|
||||
echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}"
|
||||
FORMAT=""
|
||||
MACHINE=" -machine q35"
|
||||
else
|
||||
echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}"
|
||||
FORMAT=",efitype=4m"
|
||||
MACHINE=""
|
||||
fi
|
||||
prompt_router_input "BRG" "LAN BRIDGE" "Set a LAN Bridge" "vmbr0"
|
||||
[[ -n "$BRG" ]] || BRG="vmbr0"
|
||||
if ! ip link show "${BRG}" &>/dev/null; then
|
||||
msg_error "Bridge '${BRG}' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${DGN}Using LAN Bridge: ${BGN}$BRG${CL}"
|
||||
|
||||
prompt_optional_static_ip "IP_ADDR" "LAN_GW" "NETMASK" "LAN"
|
||||
prompt_wan_bridge
|
||||
if [[ -n "$WAN_BRG" ]]; then
|
||||
prompt_optional_static_ip "WAN_IP_ADDR" "WAN_GW" "WAN_NETMASK" "WAN"
|
||||
fi
|
||||
prompt_router_mac "MAC" "LAN MAC ADDRESS" "Set a LAN MAC Address" "$GEN_MAC" "LAN MAC address"
|
||||
if [[ -n "$WAN_BRG" ]]; then
|
||||
prompt_router_mac "WAN_MAC" "WAN MAC ADDRESS" "Set a WAN MAC Address" "$GEN_MAC_LAN" "WAN MAC address"
|
||||
else
|
||||
exit-script
|
||||
WAN_MAC="$GEN_MAC_LAN"
|
||||
fi
|
||||
|
||||
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "KVM64 (Default)" ON \
|
||||
"1" "Host" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $CPU_TYPE1 = "1" ]; then
|
||||
echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}"
|
||||
CPU_TYPE=" -cpu host"
|
||||
else
|
||||
echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}"
|
||||
CPU_TYPE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
||||
"0" "None (Default)" ON \
|
||||
"1" "Write Through" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ $DISK_CACHE = "1" ]; then
|
||||
echo -e "${DGN}Using Disk Cache: ${BGN}Write Through${CL}"
|
||||
DISK_CACHE="cache=writethrough,"
|
||||
else
|
||||
echo -e "${DGN}Using Disk Cache: ${BGN}None${CL}"
|
||||
DISK_CACHE=""
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 OPNsense --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$VM_NAME" ]; then
|
||||
HN="OPNsense"
|
||||
else
|
||||
HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//')
|
||||
if [ "$HN" != "${VM_NAME,,}" ]; then
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58
|
||||
fi
|
||||
fi
|
||||
echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 4 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$CORE_COUNT" ]; then CORE_COUNT="4"; fi
|
||||
if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 4)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 8192 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$RAM_SIZE" ]; then RAM_SIZE="8192"; fi
|
||||
if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}"
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 8192)." 8 58
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
done
|
||||
|
||||
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN Bridge" 8 58 vmbr0 --title "LAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $BRG ]; then
|
||||
BRG="vmbr0"
|
||||
fi
|
||||
if ! ip link show "${BRG}" &>/dev/null; then
|
||||
msg_error "Bridge '${BRG}' does not exist"
|
||||
exit
|
||||
fi
|
||||
echo -e "${DGN}Using LAN Bridge: ${BGN}$BRG${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN IP" 8 58 $IP_ADDR --title "LAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $IP_ADDR ]; then
|
||||
echo -e "${DGN}Using DHCP AS LAN IP ADDRESS${CL}"
|
||||
else
|
||||
if [[ -n "$IP_ADDR" && ! "$IP_ADDR" =~ $ip_regex ]]; then
|
||||
msg_error "Invalid IP Address format for LAN IP. Needs to be 0.0.0.0, was $IP_ADDR"
|
||||
exit
|
||||
fi
|
||||
echo -e "${DGN}Using LAN IP ADDRESS: ${BGN}$IP_ADDR${CL}"
|
||||
if LAN_GW=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN GATEWAY IP" 8 58 $LAN_GW --title "LAN GATEWAY IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $LAN_GW ]; then
|
||||
echo -e "${DGN}Gateway needs to be set if ip is not dhcp${CL}"
|
||||
exit-script
|
||||
fi
|
||||
if [[ -n "$LAN_GW" && ! "$LAN_GW" =~ $ip_regex ]]; then
|
||||
msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was $LAN_GW"
|
||||
exit
|
||||
fi
|
||||
echo -e "${DGN}Using LAN GATEWAY ADDRESS: ${BGN}$LAN_GW${CL}"
|
||||
fi
|
||||
if NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN netmask (24 for example)" 8 58 $NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $NETMASK ]; then
|
||||
echo -e "${DGN}Netmask needs to be set if ip is not dhcp${CL}"
|
||||
fi
|
||||
if [[ -n "$NETMASK" && ! ("$NETMASK" =~ ^[0-9]+$ && "$NETMASK" -ge 1 && "$NETMASK" -le 32) ]]; then
|
||||
msg_error "Invalid LAN NETMASK format. Needs to be 1-32, was $NETMASK"
|
||||
exit
|
||||
fi
|
||||
echo -e "${DGN}Using LAN NETMASK: ${BGN}$NETMASK${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
# Build WAN bridge selection from available bridges (excluding LAN bridge)
|
||||
local WAN_BRIDGES
|
||||
WAN_BRIDGES=$(get_available_bridges | grep -v "^${BRG}$" || true)
|
||||
if [ -z "$WAN_BRIDGES" ]; then
|
||||
msg_error "No additional bridge available for WAN. Only '${BRG}' exists."
|
||||
msg_error "Create a second bridge (e.g. vmbr1) in Proxmox network config first."
|
||||
exit
|
||||
fi
|
||||
local WAN_MENU=()
|
||||
local first=true
|
||||
while IFS= read -r brg; do
|
||||
if $first; then
|
||||
WAN_MENU+=("$brg" "" "ON")
|
||||
first=false
|
||||
else
|
||||
WAN_MENU+=("$brg" "" "OFF")
|
||||
fi
|
||||
done <<<"$WAN_BRIDGES"
|
||||
|
||||
if WAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "WAN BRIDGE" --radiolist "Select WAN Bridge" 14 58 6 \
|
||||
"${WAN_MENU[@]}" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$WAN_BRG" ]; then
|
||||
WAN_BRG=$(echo "$WAN_BRIDGES" | head -n1)
|
||||
fi
|
||||
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if WAN_IP_ADDR=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN IP" 8 58 $WAN_IP_ADDR --title "WAN IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $WAN_IP_ADDR ]; then
|
||||
echo -e "${DGN}Using DHCP AS WAN IP ADDRESS${CL}"
|
||||
else
|
||||
if [[ -n "$WAN_IP_ADDR" && ! "$WAN_IP_ADDR" =~ $ip_regex ]]; then
|
||||
msg_error "Invalid IP Address format for WAN IP. Needs to be 0.0.0.0, was $WAN_IP_ADDR"
|
||||
exit
|
||||
fi
|
||||
echo -e "${DGN}Using WAN IP ADDRESS: ${BGN}$WAN_IP_ADDR${CL}"
|
||||
if WAN_GW=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN GATEWAY IP" 8 58 $WAN_GW --title "WAN GATEWAY IP ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $WAN_GW ]; then
|
||||
echo -e "${DGN}Gateway needs to be set if ip is not dhcp${CL}"
|
||||
exit-script
|
||||
fi
|
||||
if [[ -n "$WAN_GW" && ! "$WAN_GW" =~ $ip_regex ]]; then
|
||||
msg_error "Invalid IP Address format for WAN Gateway. Needs to be 0.0.0.0, was $WAN_GW"
|
||||
exit
|
||||
fi
|
||||
echo -e "${DGN}Using WAN GATEWAY ADDRESS: ${BGN}$WAN_GW${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
if WAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN netmask (24 for example)" 8 58 $WAN_NETMASK --title "WAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $WAN_NETMASK ]; then
|
||||
echo -e "${DGN}WAN Netmask needs to be set if ip is not dhcp${CL}"
|
||||
fi
|
||||
if [[ -n "$WAN_NETMASK" && ! ("$WAN_NETMASK" =~ ^[0-9]+$ && "$WAN_NETMASK" -ge 1 && "$WAN_NETMASK" -le 32) ]]; then
|
||||
msg_error "Invalid WAN NETMASK format. Needs to be 1-32, was $WAN_NETMASK"
|
||||
exit
|
||||
fi
|
||||
echo -e "${DGN}Using WAN NETMASK: ${BGN}$WAN_NETMASK${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $MAC1 ]; then
|
||||
MAC="$GEN_MAC"
|
||||
else
|
||||
MAC="$MAC1"
|
||||
fi
|
||||
echo -e "${DGN}Using LAN MAC Address: ${BGN}$MAC${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC_LAN --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||
if [ -z $MAC2 ]; then
|
||||
WAN_MAC="$GEN_MAC_LAN"
|
||||
else
|
||||
WAN_MAC="$MAC2"
|
||||
fi
|
||||
echo -e "${DGN}Using WAN MAC Address: ${BGN}$WAN_MAC${CL}"
|
||||
else
|
||||
exit-script
|
||||
fi
|
||||
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create OPNsense VM?" --no-button Do-Over 10 58); then
|
||||
if vm_confirm_advanced_settings "Ready to create OPNsense VM?"; then
|
||||
echo -e "${RD}Creating a OPNsense VM using the above advanced settings${CL}"
|
||||
else
|
||||
header_info
|
||||
@@ -606,75 +350,18 @@ function advanced_settings() {
|
||||
fi
|
||||
}
|
||||
|
||||
function start_script() {
|
||||
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
|
||||
header_info
|
||||
echo -e "${BL}Using Default Settings${CL}"
|
||||
default_settings
|
||||
else
|
||||
header_info
|
||||
echo -e "${RD}Using Advanced Settings${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
}
|
||||
|
||||
arch_check
|
||||
pve_check
|
||||
ssh_check
|
||||
start_script
|
||||
vm_start_script "Use Default Settings?\n\nDefaults:\n• 4 CPU Cores\n• 8 GB RAM\n• 20 GB Disk" 13 58
|
||||
post_to_api_vm
|
||||
|
||||
msg_info "Validating Storage"
|
||||
while read -r line; do
|
||||
TAG=$(echo $line | awk '{print $1}')
|
||||
TYPE=$(echo $line | awk '{printf "%-10s", $2}')
|
||||
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
|
||||
ITEM=" Type: $TYPE Free: $FREE "
|
||||
OFFSET=2
|
||||
if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
|
||||
MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
|
||||
fi
|
||||
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
|
||||
done < <(pvesm status -content images | awk 'NR>1')
|
||||
VALID=$(pvesm status -content images | awk 'NR>1')
|
||||
if [ -z "$VALID" ]; then
|
||||
msg_error "Unable to detect a valid storage location."
|
||||
exit
|
||||
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
|
||||
STORAGE=${STORAGE_MENU[0]}
|
||||
else
|
||||
while [ -z "${STORAGE:+x}" ]; do
|
||||
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
||||
"Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \
|
||||
16 $(($MSG_MAX_LENGTH + 23)) 6 \
|
||||
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
|
||||
done
|
||||
fi
|
||||
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
||||
vm_select_storage "$HN"
|
||||
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
||||
msg_info "Retrieving the URL for the OPNsense Qcow2 Disk Image"
|
||||
# Use latest stable FreeBSD amd64 qcow2 VM image matching FREEBSD_MAJOR
|
||||
RELEASE_LIST="$(curl -s https://download.freebsd.org/releases/VM-IMAGES/ |
|
||||
grep -Eo "${FREEBSD_MAJOR}\.[0-9]+-RELEASE" |
|
||||
sort -Vr |
|
||||
uniq)"
|
||||
URL=""
|
||||
FREEBSD_VER=""
|
||||
for ver in $RELEASE_LIST; do
|
||||
# FreeBSD 15+ publishes separate -ufs/-zfs images instead of a generic one
|
||||
for variant in "" "-ufs" "-zfs"; do
|
||||
candidate="https://download.freebsd.org/releases/VM-IMAGES/${ver}/amd64/Latest/FreeBSD-${ver}-amd64${variant}.qcow2.xz"
|
||||
if curl -fsI "$candidate" >/dev/null 2>&1; then
|
||||
FREEBSD_VER="$ver"
|
||||
URL="$candidate"
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
if [ -z "$URL" ]; then
|
||||
msg_error "Could not find a FreeBSD ${FREEBSD_MAJOR}.x amd64 qcow2 image."
|
||||
exit 115
|
||||
fi
|
||||
vm_latest_from_index "https://download.freebsd.org/releases/VM-IMAGES/" "${FREEBSD_MAJOR}\.[0-9]+-RELEASE" \
|
||||
--probe "https://download.freebsd.org/releases/VM-IMAGES/{}/amd64/Latest/FreeBSD-{}-amd64.qcow2.xz" \
|
||||
--probe "https://download.freebsd.org/releases/VM-IMAGES/{}/amd64/Latest/FreeBSD-{}-amd64-ufs.qcow2.xz" \
|
||||
--probe "https://download.freebsd.org/releases/VM-IMAGES/{}/amd64/Latest/FreeBSD-{}-amd64-zfs.qcow2.xz" || exit 115
|
||||
FREEBSD_VER="$VM_INDEX_LATEST"
|
||||
URL="$VM_INDEX_URL"
|
||||
msg_ok "Download URL: ${CL}${BL}${URL}${CL}"
|
||||
|
||||
# Check available disk space (require at least 20GB for safety)
|
||||
@@ -687,9 +374,12 @@ if ! check_disk_space "$TEMP_DIR" 20; then
|
||||
fi
|
||||
|
||||
msg_info "Downloading FreeBSD Image"
|
||||
curl -f#SL -o "$(basename "$URL")" "$URL"
|
||||
# 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 --verify-xz --min-bytes $((5 * 1024 * 1024)) || exit 1
|
||||
echo -en "\e[1A\e[0K"
|
||||
msg_ok "Downloaded ${CL}${BL}$(basename "$URL")${CL}"
|
||||
msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}"
|
||||
|
||||
# Check disk space again before decompression
|
||||
if ! check_disk_space "$TEMP_DIR" 15; then
|
||||
@@ -699,49 +389,19 @@ if ! check_disk_space "$TEMP_DIR" 15; then
|
||||
exit 214
|
||||
fi
|
||||
|
||||
msg_info "Decompressing FreeBSD Image (this may take a few minutes)"
|
||||
FILE=FreeBSD.qcow2
|
||||
if ! unxz -cv $(basename $URL) >${FILE}; then
|
||||
msg_error "Failed to decompress FreeBSD image."
|
||||
msg_error "This is usually caused by insufficient disk space."
|
||||
df -h "$TEMP_DIR"
|
||||
exit 115
|
||||
fi
|
||||
FILE="$TEMP_DIR/FreeBSD.qcow2"
|
||||
vm_extract_image "$CACHE_FILE" "$FILE" || exit 115
|
||||
FILE="$VM_IMAGE_FILE"
|
||||
|
||||
# Remove the compressed file to save space
|
||||
rm -f "$(basename "$URL")"
|
||||
msg_ok "Decompressed ${CL}${BL}${FILE}${CL}"
|
||||
|
||||
STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}')
|
||||
case $STORAGE_TYPE in
|
||||
nfs | dir)
|
||||
DISK_EXT=".qcow2"
|
||||
DISK_REF="$VMID/"
|
||||
DISK_IMPORT="-format qcow2"
|
||||
THIN=""
|
||||
;;
|
||||
btrfs)
|
||||
DISK_EXT=".raw"
|
||||
DISK_REF="$VMID/"
|
||||
DISK_IMPORT="-format raw"
|
||||
FORMAT=",efitype=4m"
|
||||
THIN=""
|
||||
;;
|
||||
*)
|
||||
DISK_EXT=""
|
||||
DISK_REF=""
|
||||
DISK_IMPORT="-format raw"
|
||||
;;
|
||||
esac
|
||||
for i in {0,1}; do
|
||||
disk="DISK$i"
|
||||
eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-}
|
||||
eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk}
|
||||
done
|
||||
vm_define_disk_references 1
|
||||
|
||||
vm_claim_vmid
|
||||
msg_info "Creating a OPNsense VM"
|
||||
qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
|
||||
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
|
||||
# A firewall VM filters itself: Proxmox's per-NIC firewall off, and virtio
|
||||
# multiqueue matched to the cores so routing scales past one vCPU.
|
||||
qm create $VMID -agent 1${MACHINE} -tablet 0 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE -balloon 0 \
|
||||
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC,firewall=0,queues=$CORE_COUNT$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
|
||||
vm_mark_created
|
||||
|
||||
# Retry pvesm alloc on transient zfs_request "got timeout" errors (#14127)
|
||||
alloc_attempt=1
|
||||
@@ -760,66 +420,34 @@ while :; do
|
||||
echo -e "$alloc_err" >&2
|
||||
exit 220
|
||||
done
|
||||
qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} &>/dev/null
|
||||
vm_import_disk "$VMID" "$FILE" "$STORAGE"
|
||||
qm set $VMID \
|
||||
-efidisk0 ${DISK0_REF}${FORMAT} \
|
||||
-scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=2G \
|
||||
-scsi0 "${VM_IMPORTED_DISK}",${DISK_CACHE}${THIN}size=2G \
|
||||
-boot order=scsi0 \
|
||||
-serial0 socket \
|
||||
-tags community-script >/dev/null
|
||||
qm resize $VMID scsi0 20G >/dev/null
|
||||
DESCRIPTION=$(
|
||||
cat <<EOF
|
||||
<div align='center'>
|
||||
<a href='https://community-scripts.org' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/images/logo-81x112.png' alt='Logo' style='width:81px;height:112px;'/>
|
||||
</a>
|
||||
|
||||
<h2 style='font-size: 24px; margin: 20px 0;'>OPNsense VM</h2>
|
||||
|
||||
<p style='margin: 16px 0;'>
|
||||
<a href='https://ko-fi.com/community_scripts' target='_blank' rel='noopener noreferrer'>
|
||||
<img src='https://img.shields.io/badge/☕-Buy us a coffee-blue' alt='spend Coffee' />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-github fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>GitHub</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-comments fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/discussions' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Discussions</a>
|
||||
</span>
|
||||
<span style='margin: 0 10px;'>
|
||||
<i class="fa fa-exclamation-circle fa-fw" style="color: #f5f5f5;"></i>
|
||||
<a href='https://github.com/community-scripts/ProxmoxVE/issues' target='_blank' rel='noopener noreferrer' style='text-decoration: none; color: #00617f;'>Issues</a>
|
||||
</span>
|
||||
</div>
|
||||
EOF
|
||||
)
|
||||
qm set $VMID -description "$DESCRIPTION" >/dev/null
|
||||
vm_resize_disk
|
||||
set_description
|
||||
|
||||
msg_info "Bridge interfaces are being added."
|
||||
qm set $VMID \
|
||||
-net0 virtio,bridge=${BRG},macaddr=${MAC}${VLAN}${MTU} 2>/dev/null
|
||||
-net0 virtio,bridge=${BRG},macaddr=${MAC},firewall=0,queues=${CORE_COUNT}${VLAN}${MTU} 2>/dev/null
|
||||
if [ -n "$WAN_BRG" ]; then
|
||||
qm set $VMID \
|
||||
-net1 virtio,bridge=${WAN_BRG},macaddr=${WAN_MAC},firewall=0,queues=${CORE_COUNT} 2>/dev/null
|
||||
fi
|
||||
msg_ok "Bridge interfaces have been successfully added."
|
||||
|
||||
msg_ok "Created a OPNsense VM ${CL}${BL}(${HN})"
|
||||
msg_ok "Starting OPNsense VM (Patience this takes 20-30 minutes)"
|
||||
qm start $VMID
|
||||
msg_ok "Starting OPNsense VM (the bootstrap takes 10-30 minutes)"
|
||||
$STD qm start $VMID
|
||||
sleep 90
|
||||
send_line_to_vm "root"
|
||||
sleep 2
|
||||
send_line_to_vm ""
|
||||
send_line_to_vm "fetch https://raw.githubusercontent.com/opnsense/update/master/src/bootstrap/opnsense-bootstrap.sh.in"
|
||||
if [ -n "$WAN_BRG" ]; then
|
||||
msg_info "Adding WAN interface"
|
||||
qm set $VMID \
|
||||
-net1 virtio,bridge=${WAN_BRG},macaddr=${WAN_MAC} &>/dev/null
|
||||
msg_ok "WAN interface added"
|
||||
sleep 5 # Brief pause after adding network interface
|
||||
fi
|
||||
send_line_to_vm "for i in \$(seq 1 60); do fetch -q https://raw.githubusercontent.com/opnsense/update/master/src/bootstrap/opnsense-bootstrap.sh.in && break; sleep 5; done"
|
||||
sleep 5
|
||||
# FreeBSD 15+ VM images ship the base system as pkgbase packages; the bootstrap's
|
||||
# "delete all packages" step would remove the running base system (/bin/rm etc.)
|
||||
# and brick the VM. Deregister them from the pkg db first - the files stay in
|
||||
@@ -877,13 +505,22 @@ while [ $build_stable -lt 6 ] && [ $build_elapsed -lt 2400 ]; do
|
||||
fi
|
||||
# No working screendump after several attempts: fixed wait instead
|
||||
if [ $screen_ok -eq 0 ] && [ $build_elapsed -ge 480 ]; then
|
||||
msg_error "Console screendump not available on this system - falling back to a fixed wait (12 minutes)."
|
||||
msg_warn "Console screendump not available on this system - falling back to a fixed wait (12 minutes)."
|
||||
sleep 720
|
||||
build_elapsed=$((build_elapsed + 720))
|
||||
break
|
||||
fi
|
||||
done
|
||||
msg_ok "OPNsense build finished after $((build_elapsed / 60)) minutes"
|
||||
if [ $build_stable -ge 6 ]; then
|
||||
msg_ok "OPNsense console settled after $((build_elapsed / 60)) minutes"
|
||||
else
|
||||
msg_warn "The console did not settle within $((build_elapsed / 60)) minutes; continuing, verify the OPNsense console afterwards"
|
||||
fi
|
||||
# The answers below are matched to OPNsense's console dialog (setaddr.php).
|
||||
# One prompt ("via WAN tracking?") only appears when WAN has DHCP6, so a spare
|
||||
# "n" is sent for it; where it is absent, OPNsense re-asks the IPv6 address and
|
||||
# the following ENTER lands there. Invalid answers are re-asked, empty ones take
|
||||
# the default, which keeps the sequence safe in both dialog variants.
|
||||
send_line_to_vm "root"
|
||||
send_line_to_vm "opnsense"
|
||||
send_line_to_vm "2"
|
||||
@@ -933,13 +570,28 @@ if [ -n "$WAN_BRG" ] && [ "$WAN_IP_ADDR" != "" ]; then
|
||||
fi
|
||||
sleep 10
|
||||
send_line_to_vm "0"
|
||||
msg_ok "Started OPNsense VM"
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
if [ "$IP_ADDR" != "" ]; then
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP_ADDR}${CL}"
|
||||
if [[ "$START_VM" == "no" ]]; then
|
||||
msg_info "Shutting down OPNsense as requested"
|
||||
$STD qm shutdown "$VMID" --timeout 120
|
||||
msg_ok "OPNsense VM shut down"
|
||||
else
|
||||
echo -e "${INFO}${YW} LAN IP was DHCP.${CL}"
|
||||
echo -e "${INFO}${BGN}To find the IP login to the VM shell${CL}"
|
||||
msg_ok "OPNsense VM is running"
|
||||
fi
|
||||
|
||||
if [ "$IP_ADDR" != "" ]; then
|
||||
LAN_URL="http://${IP_ADDR}"
|
||||
else
|
||||
LAN_URL="DHCP - check the OPNsense console or your leases"
|
||||
fi
|
||||
|
||||
vm_print_summary \
|
||||
"LAN URL=${LAN_URL}" \
|
||||
"LAN Bridge=${BRG}" \
|
||||
"WAN Bridge=${WAN_BRG:-single-interface mode}" \
|
||||
"OPNsense Version=${var_version}" \
|
||||
"FreeBSD Base=${FREEBSD_VER}"
|
||||
vm_next_steps \
|
||||
"Verify the guest bootstrap and network configuration in the OPNsense console." \
|
||||
"Login as root with password opnsense after successful bootstrap, then change the password immediately." \
|
||||
"Keep WAN and LAN isolated appropriately; single-interface mode is intended for proxy, VPN, or IDS use cases."
|
||||
vm_finish "VM creation completed; verify the guest bootstrap and network configuration in the console."
|
||||
|
||||
+3
-3
@@ -149,8 +149,8 @@ fi
|
||||
|
||||
FULL_URL="https://download.truenas.com/${SELECTED_ISO#/}"
|
||||
ISO_NAME=$(basename "$FULL_URL")
|
||||
CACHE_DIR="/var/lib/vz/template/iso"
|
||||
CACHE_FILE="$CACHE_DIR/$ISO_NAME"
|
||||
vm_select_iso_storage "$ISO_NAME" "$HN"
|
||||
CACHE_FILE="$ISO_PATH"
|
||||
|
||||
msg_info "Retrieving the ISO for the TrueNAS Disk Image"
|
||||
MIN_ISO_BYTES=$((500 * 1024 * 1024))
|
||||
@@ -162,7 +162,7 @@ qm create "$VMID"${MACHINE} -bios ovmf -agent enabled=1 -tablet 0 -localtime 1${
|
||||
-cores "$CORE_COUNT" -memory "$RAM_SIZE" -balloon 0 -name "$HN" -tags community-script \
|
||||
-net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 \
|
||||
-efidisk0 $STORAGE:1,efitype=4m,pre-enrolled-keys=0 -sata0 ${STORAGE}:${DISK_SIZE%G},ssd=1 \
|
||||
-scsihw virtio-scsi-single -cdrom local:iso/$ISO_NAME -boot order='sata0;ide2' -vga virtio >/dev/null
|
||||
-scsihw virtio-scsi-single -cdrom "$ISO_VOLUME" -boot order='sata0;ide2' -vga virtio >/dev/null
|
||||
msg_ok "Created VM shell"
|
||||
|
||||
if [ "$IMPORT_DISKS" == "yes" ]; then
|
||||
|
||||
+66
-63
@@ -1,10 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main}"
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
|
||||
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
||||
load_functions
|
||||
|
||||
@@ -19,55 +18,51 @@ var_version="26.04"
|
||||
THIN="discard=on,ssd=1,"
|
||||
USE_CLOUD_INIT="no"
|
||||
|
||||
|
||||
set -e
|
||||
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_preflight
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
pushd "$TEMP_DIR" >/dev/null
|
||||
|
||||
if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
|
||||
var_version="${VM_OS_VERSION:-$var_version}"
|
||||
elif vm_dialog radiolist "UBUNTU VERSION" "Choose the Ubuntu release to install" --cancel-button Exit-Script 13 60 4 \
|
||||
"26.04" "Ubuntu 26.04 LTS (Resolute)" ON \
|
||||
"25.04" "Ubuntu 25.04 (Plucky)" OFF \
|
||||
"24.04" "Ubuntu 24.04 LTS (Noble)" OFF \
|
||||
"22.04" "Ubuntu 22.04 LTS (Jammy)" OFF; then
|
||||
var_version="$VM_DIALOG_RESULT"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
vm_preflight
|
||||
vm_require_tools virt-customize
|
||||
|
||||
case "$var_version" in
|
||||
26.04) UBUNTU_CODENAME="resolute" ;;
|
||||
25.04) UBUNTU_CODENAME="plucky" ;;
|
||||
24.04) UBUNTU_CODENAME="noble" ;;
|
||||
22.04) UBUNTU_CODENAME="jammy" ;;
|
||||
*)
|
||||
msg_error "Unsupported Ubuntu version '${var_version}'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
APP="Ubuntu ${var_version} VM"
|
||||
function select_os() {
|
||||
if [[ -n "${1:-}" ]]; then
|
||||
var_version="$1"
|
||||
elif [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
|
||||
var_version="${VM_OS_VERSION:-$var_version}"
|
||||
elif vm_dialog radiolist "UBUNTU VERSION" "Choose the Ubuntu release to install" --cancel-button Exit-Script 13 60 4 \
|
||||
"26.04" "Ubuntu 26.04 LTS (Resolute)" ON \
|
||||
"24.04" "Ubuntu 24.04 LTS (Noble)" OFF \
|
||||
"22.04" "Ubuntu 22.04 LTS (Jammy)" OFF; then
|
||||
var_version="$VM_DIALOG_RESULT"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
|
||||
case "$var_version" in
|
||||
26.04) UBUNTU_CODENAME="resolute" ;;
|
||||
24.04) UBUNTU_CODENAME="noble" ;;
|
||||
22.04) UBUNTU_CODENAME="jammy" ;;
|
||||
*)
|
||||
msg_error "Unsupported Ubuntu version '${var_version}'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
APP="Ubuntu ${var_version}"
|
||||
}
|
||||
|
||||
header_info
|
||||
echo -e "\n Loading..."
|
||||
|
||||
# Ubuntu cloud images configure netplan from cloud-init only. Without it the
|
||||
# guest boots with an interface that never gets an address.
|
||||
VM_CLOUD_INIT="${VM_CLOUD_INIT:-yes}"
|
||||
vm_prompt_cloud_init "ubuntu"
|
||||
if [ "$USE_CLOUD_INIT" != "yes" ]; then
|
||||
msg_warn "Without Cloud-Init this Ubuntu image gets no network configuration - configure it in the guest yourself."
|
||||
fi
|
||||
|
||||
function default_settings() {
|
||||
select_os "${VM_OS_VERSION:-}"
|
||||
VMID=$(get_valid_nextid)
|
||||
vm_apply_machine_type "q35"
|
||||
DISK_SIZE="7G"
|
||||
@@ -102,6 +97,7 @@ function default_settings() {
|
||||
|
||||
function advanced_settings() {
|
||||
METHOD="advanced"
|
||||
select_os
|
||||
echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}"
|
||||
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
||||
vm_prompt_machine_type "q35"
|
||||
@@ -115,6 +111,7 @@ function advanced_settings() {
|
||||
vm_prompt_mac "$GEN_MAC"
|
||||
vm_prompt_vlan
|
||||
vm_prompt_mtu
|
||||
vm_prompt_keyboard
|
||||
vm_prompt_verbose "no"
|
||||
vm_prompt_start_vm "yes"
|
||||
|
||||
@@ -128,11 +125,14 @@ function advanced_settings() {
|
||||
}
|
||||
|
||||
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 2 GB RAM\n• 7 GB Disk" 13 58
|
||||
VM_CLOUD_INIT="${VM_CLOUD_INIT:-yes}"
|
||||
vm_prompt_cloud_init "ubuntu"
|
||||
if [ "$USE_CLOUD_INIT" != "yes" ]; then
|
||||
msg_warn "Without Cloud-Init this Ubuntu image gets no network configuration or credentials; configure them in the guest yourself."
|
||||
fi
|
||||
post_to_api_vm
|
||||
|
||||
vm_select_storage "$HN"
|
||||
vm_define_disk_references 2
|
||||
DISK_IMPORT="-format ${DISK_IMPORT_FORMAT}"
|
||||
|
||||
msg_info "Retrieving the URL for the ${APP} Disk Image"
|
||||
UBUNTU_ARCH="$(vm_arch_resolve amd64 arm64)"
|
||||
@@ -147,33 +147,31 @@ FILE="$(basename "$CACHE_FILE")"
|
||||
cp -f "$CACHE_FILE" "$FILE"
|
||||
|
||||
vm_prepare_cloud_image "$FILE" "$HN" || true
|
||||
if [[ "$USE_CLOUD_INIT" != "yes" ]]; then
|
||||
vm_expand_image "$FILE" "$DISK_SIZE"
|
||||
fi
|
||||
|
||||
vm_claim_vmid
|
||||
msg_info "Creating a ${APP}"
|
||||
qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
|
||||
qm create $VMID -agent 1${MACHINE} -tablet 0 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
|
||||
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
|
||||
vm_mark_created
|
||||
vm_alloc_efi_disk "$DISK0"
|
||||
qm importdisk $VMID $FILE $STORAGE ${DISK_IMPORT:-} 1>&/dev/null
|
||||
vm_import_disk "$VMID" "$FILE" "$STORAGE" "$DISK_IMPORT_FORMAT"
|
||||
qm set $VMID \
|
||||
-efidisk0 ${DISK0_REF}${FORMAT} \
|
||||
-scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \
|
||||
-scsi0 ${VM_IMPORTED_DISK},${DISK_CACHE}${THIN}size=${DISK_SIZE} \
|
||||
-boot order=scsi0 \
|
||||
-serial0 socket >/dev/null
|
||||
set_description
|
||||
|
||||
vm_resize_disk
|
||||
|
||||
if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f setup_cloud_init >/dev/null 2>&1; then
|
||||
setup_cloud_init \
|
||||
"$VMID" \
|
||||
"$STORAGE" \
|
||||
"$HN" \
|
||||
"yes" \
|
||||
"${CLOUDINIT_USER:-ubuntu}" \
|
||||
"${CLOUDINIT_NETWORK_MODE:-dhcp}" \
|
||||
"${CLOUDINIT_IP:-}" \
|
||||
"${CLOUDINIT_GW:-}" \
|
||||
"${CLOUDINIT_DNS:-${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}}"
|
||||
|
||||
vm_provision "$VMID"
|
||||
if [[ "$USE_CLOUD_INIT" == "yes" && -n "${CLOUDINIT_SSH_KEYS:-}" ]]; then
|
||||
$STD qm set "$VMID" --sshkeys "$CLOUDINIT_SSH_KEYS"
|
||||
fi
|
||||
if [ "$USE_CLOUD_INIT" = "yes" ]; then
|
||||
if [[ "${CLOUDINIT_NETWORK_MODE:-dhcp}" == "static" ]]; then
|
||||
setup_cloud_init_network_no_rename \
|
||||
"$VMID" \
|
||||
@@ -186,16 +184,21 @@ if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f setup_cloud_init >/dev/null 2>&1;
|
||||
fi
|
||||
|
||||
msg_ok "Created a ${APP} ${CL}${BL}(${HN})"
|
||||
if [ "$START_VM" = "yes" ]; then
|
||||
msg_info "Starting ${APP}"
|
||||
$STD qm start $VMID
|
||||
msg_ok "Started ${APP}"
|
||||
fi
|
||||
vm_start_vm "$APP"
|
||||
vm_wait_for_ip 120 || true
|
||||
|
||||
post_update_to_api "done" "none"
|
||||
msg_ok "Completed successfully!\n"
|
||||
if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f display_cloud_init_info >/dev/null 2>&1; then
|
||||
display_cloud_init_info "$VMID" "$HN"
|
||||
else
|
||||
echo -e "Cloud-Init is disabled. The VM disk was resized on the Proxmox side only.\nIf the guest does not auto-expand its root filesystem after first boot, expand it manually inside the VM.\n\nMore info at https://github.com/community-scripts/ProxmoxVED/discussions/272 \n"
|
||||
fi
|
||||
|
||||
vm_print_summary \
|
||||
"Ubuntu Release=${var_version} (${UBUNTU_CODENAME})" \
|
||||
"Cloud-Init=${USE_CLOUD_INIT}"
|
||||
|
||||
if [ "$USE_CLOUD_INIT" != "yes" ]; then
|
||||
vm_next_steps \
|
||||
"Configure guest networking and credentials manually from the VM console." \
|
||||
"The root filesystem was expanded offline where host tools allowed it."
|
||||
fi
|
||||
|
||||
vm_finish
|
||||
|
||||
+3
-4
@@ -96,9 +96,8 @@ URL="https://download.umbrel.com/release/${UMBREL_RELEASE}/umbrelos-amd64-usb-in
|
||||
# The upstream file name is the same for every release, so the version goes into
|
||||
# the cached name -- otherwise the cache serves 1.7.4 to someone asking for 2.0.
|
||||
ISO_NAME="umbrelos-${UMBREL_RELEASE}-amd64-usb-installer.iso"
|
||||
CACHE_DIR="/var/lib/vz/template/iso"
|
||||
CACHE_FILE="${CACHE_DIR}/${ISO_NAME}"
|
||||
mkdir -p "$CACHE_DIR"
|
||||
vm_select_iso_storage "$ISO_NAME" "$HN"
|
||||
CACHE_FILE="$ISO_PATH"
|
||||
msg_ok "${CL}${BL}${URL}${CL}"
|
||||
|
||||
# download.umbrel.com answers 307 for any name at all, so a redirect proves
|
||||
@@ -114,7 +113,7 @@ qm create "$VMID"${MACHINE} -bios ovmf -agent enabled=1 -tablet 0 -localtime 1 $
|
||||
-net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci \
|
||||
-efidisk0 "${STORAGE}:1,efitype=4m,pre-enrolled-keys=0" \
|
||||
-scsi0 "${STORAGE}:${DISK_SIZE%G},${DISK_CACHE:-}${THIN%,}" \
|
||||
-cdrom "local:iso/${ISO_NAME}" -boot order='scsi0;ide2' >/dev/null
|
||||
-cdrom "$ISO_VOLUME" -boot order='scsi0;ide2' >/dev/null
|
||||
|
||||
set_description
|
||||
msg_ok "Created a Umbrel OS VM ${CL}${BL}(${HN})"
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: 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/ProxmoxVE/main}"
|
||||
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
||||
load_functions
|
||||
|
||||
APP="UniFi OS Server"
|
||||
APP_TYPE="vm"
|
||||
NSAPP="unifi-os-server-vm"
|
||||
var_os="debian"
|
||||
var_version="13"
|
||||
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
|
||||
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
METHOD=""
|
||||
CLOUDINIT_REQUIRED=1
|
||||
OS_TYPE=""
|
||||
OS_VERSION=""
|
||||
OS_CODENAME=""
|
||||
OS_DISPLAY=""
|
||||
THIN="discard=on,ssd=1,"
|
||||
|
||||
header_info
|
||||
echo -e "\n Loading..."
|
||||
|
||||
set -Eeuo 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 curl jq virt-customize
|
||||
|
||||
function select_os() {
|
||||
if [[ -n "${1:-}" ]]; then
|
||||
OS_CHOICE="$1"
|
||||
elif [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
|
||||
OS_CHOICE="${VM_OS_VERSION:-debian13}"
|
||||
elif vm_dialog radiolist "SELECT OS" "Choose Operating System for UniFi OS VM" 12 68 2 \
|
||||
"debian13" "Debian 13 (Trixie) - Latest" ON \
|
||||
"ubuntu2404" "Ubuntu 24.04 LTS (Noble)" OFF; then
|
||||
OS_CHOICE="$VM_DIALOG_RESULT"
|
||||
else
|
||||
exit_script
|
||||
fi
|
||||
|
||||
case $OS_CHOICE in
|
||||
debian13)
|
||||
OS_TYPE="debian"
|
||||
OS_VERSION="13"
|
||||
OS_CODENAME="trixie"
|
||||
OS_DISPLAY="Debian 13 (Trixie)"
|
||||
var_os="debian"
|
||||
var_version="13"
|
||||
;;
|
||||
ubuntu2404)
|
||||
OS_TYPE="ubuntu"
|
||||
OS_VERSION="24.04"
|
||||
OS_CODENAME="noble"
|
||||
OS_DISPLAY="Ubuntu 24.04 LTS"
|
||||
var_os="ubuntu"
|
||||
var_version="24.04"
|
||||
;;
|
||||
*)
|
||||
msg_error "Unsupported OS '${OS_CHOICE}' (expected debian13 or ubuntu2404)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
function get_image_url() {
|
||||
local arch
|
||||
arch=$(vm_arch_resolve amd64 arm64)
|
||||
case $OS_TYPE in
|
||||
debian)
|
||||
# Always use Cloud-Init variant for UniFi OS
|
||||
echo "https://cloud.debian.org/images/cloud/${OS_CODENAME}/latest/debian-${OS_VERSION}-generic-${arch}.qcow2"
|
||||
;;
|
||||
ubuntu)
|
||||
# Ubuntu only has cloudimg variant (always with Cloud-Init support)
|
||||
echo "https://cloud-images.ubuntu.com/${OS_CODENAME}/current/${OS_CODENAME}-server-cloudimg-${arch}.img"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
function default_settings() {
|
||||
vm_apply_machine_type "q35"
|
||||
select_os "${VM_OS_VERSION:-}"
|
||||
|
||||
# Set defaults for other settings
|
||||
VMID=$(get_valid_nextid)
|
||||
DISK_CACHE=""
|
||||
DISK_SIZE="32G"
|
||||
HN="unifi-server-os"
|
||||
CPU_TYPE=" -cpu host"
|
||||
CORE_COUNT="2"
|
||||
RAM_SIZE="6144"
|
||||
BRG="vmbr0"
|
||||
MAC="$GEN_MAC"
|
||||
VLAN=""
|
||||
MTU=""
|
||||
START_VM="yes"
|
||||
METHOD="default"
|
||||
vm_echo_default_settings
|
||||
}
|
||||
|
||||
function advanced_settings() {
|
||||
METHOD="advanced"
|
||||
select_os
|
||||
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
||||
vm_prompt_machine_type "q35"
|
||||
vm_prompt_disk_size "32G"
|
||||
vm_prompt_disk_cache "none"
|
||||
vm_prompt_hostname "unifi-server-os"
|
||||
vm_prompt_cpu_model "host"
|
||||
vm_prompt_cpu_cores "2"
|
||||
vm_prompt_ram "6144"
|
||||
vm_prompt_bridge "vmbr0"
|
||||
vm_prompt_mac "$GEN_MAC"
|
||||
vm_prompt_vlan
|
||||
vm_prompt_mtu
|
||||
vm_prompt_keyboard
|
||||
vm_prompt_verbose "no"
|
||||
vm_prompt_start_vm "yes"
|
||||
|
||||
if vm_confirm_advanced_settings "Ready to create a UniFi OS Server VM?"; then
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a UniFi OS Server VM using the above advanced settings${CL}"
|
||||
else
|
||||
header_info
|
||||
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
}
|
||||
|
||||
vm_start_script "Use Default Settings?\n\nDefaults:\n• 2 CPU Cores\n• 6 GB RAM\n• 32 GB Disk\n• Cloud-Init enabled" 14 58
|
||||
|
||||
if [[ "${VM_UNATTENDED:-0}" == "1" ]]; then
|
||||
CLOUDINIT_PASSWORD="${CLOUDINIT_PASSWORD:-${VM_ROOT_PASSWORD:-}}"
|
||||
fi
|
||||
vm_prompt_cloud_init "root"
|
||||
if [[ "${USE_CLOUD_INIT:-no}" != "yes" ]]; then
|
||||
msg_error "UniFi OS Server requires Cloud-Init"
|
||||
exit 1
|
||||
fi
|
||||
if ! declare -f setup_cloud_init >/dev/null; then
|
||||
msg_error "Required Cloud-Init helpers are unavailable"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${VM_UNATTENDED:-0}" == "1" && -n "${VM_SSH_KEYS:-}" && -z "${CLOUDINIT_SSH_KEYS:-}" ]]; then
|
||||
if [[ -f "$VM_SSH_KEYS" ]]; then
|
||||
cp "$VM_SSH_KEYS" "$TEMP_DIR/ssh-keys-input"
|
||||
else
|
||||
printf '%s\n' "$VM_SSH_KEYS" >"$TEMP_DIR/ssh-keys-input"
|
||||
fi
|
||||
CLOUDINIT_SSH_KEYS="$TEMP_DIR/ssh-keys.pub"
|
||||
_ci_ssh_extract_keys_from_file "$TEMP_DIR/ssh-keys-input" >"$CLOUDINIT_SSH_KEYS"
|
||||
if [[ ! -s "$CLOUDINIT_SSH_KEYS" ]] || ! ssh-keygen -lf "$CLOUDINIT_SSH_KEYS" >/dev/null; then
|
||||
msg_error "VM_SSH_KEYS must contain valid SSH public keys or name a public-key file"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
post_to_api_vm
|
||||
|
||||
msg_info "Checking system resources"
|
||||
SYSTEM_RAM_GB=$(grep MemTotal /proc/meminfo | awk '{printf "%.0f", $2 / 1024 / 1024}')
|
||||
SYSTEM_SWAP_GB=$(grep SwapTotal /proc/meminfo | awk '{printf "%.0f", $2 / 1024 / 1024}')
|
||||
SYSTEM_FREE_DISK_GB=$(df -BG / | awk 'NR==2 {print $4}' | sed 's/G//')
|
||||
if [[ ${SYSTEM_RAM_GB} -lt 4 ]]; then
|
||||
msg_error "Warning: Less than 4GB RAM detected (${SYSTEM_RAM_GB}GB). Install may be slow."
|
||||
sleep 3
|
||||
fi
|
||||
if [[ ${SYSTEM_FREE_DISK_GB} -lt 10 ]]; then
|
||||
msg_error "Warning: Less than 10GB free disk detected. Install may fail."
|
||||
sleep 3
|
||||
fi
|
||||
msg_ok "System resources: ${SYSTEM_RAM_GB}GB RAM, ${SYSTEM_FREE_DISK_GB}GB free disk"
|
||||
|
||||
if command -v ufw &>/dev/null; then
|
||||
if ufw status verbose | grep -q "Status: active"; then
|
||||
msg_info "Setting up firewall rules for UniFi OS Server ports"
|
||||
ufw allow 11443/tcp 2>/dev/null
|
||||
ufw allow 8080/tcp 2>/dev/null
|
||||
ufw allow 3478/tcp 2>/dev/null
|
||||
ufw allow 3478/udp 2>/dev/null
|
||||
msg_ok "Firewall rules configured"
|
||||
fi
|
||||
fi
|
||||
|
||||
vm_select_storage "$HN"
|
||||
|
||||
# Fetch latest UniFi OS Server version and download URL
|
||||
msg_info "Fetching latest UniFi OS Server version"
|
||||
|
||||
# Download firmware list from Ubiquiti API
|
||||
API_URL="https://fw-update.ui.com/api/firmware-latest"
|
||||
TEMP_JSON=$(mktemp)
|
||||
|
||||
if ! curl -fsSL "$API_URL" -o "$TEMP_JSON"; then
|
||||
rm -f "$TEMP_JSON"
|
||||
msg_error "Failed to fetch data from Ubiquiti API"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse JSON to find latest unifi-os-server linux-x64 version
|
||||
LATEST=$(jq -r '
|
||||
._embedded.firmware
|
||||
| map(select(.product == "unifi-os-server"))
|
||||
| map(select(.platform == "linux-x64"))
|
||||
| sort_by(.version_major, .version_minor, .version_patch)
|
||||
| last
|
||||
' "$TEMP_JSON")
|
||||
|
||||
UOS_VERSION=$(echo "$LATEST" | jq -r '.version' | sed 's/^v//')
|
||||
UOS_URL=$(echo "$LATEST" | jq -r '._links.data.href')
|
||||
|
||||
# Cleanup temp file
|
||||
rm -f "$TEMP_JSON"
|
||||
|
||||
if [[ -z "$UOS_URL" || "$UOS_URL" == "null" || -z "$UOS_VERSION" || "$UOS_VERSION" == "null" ]]; then
|
||||
msg_error "Failed to parse UniFi OS Server version or download URL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin"
|
||||
msg_ok "Found UniFi OS Server ${UOS_VERSION}"
|
||||
|
||||
# --- Download Cloud Image ---
|
||||
msg_info "Downloading ${OS_DISPLAY} Cloud Image"
|
||||
URL=$(get_image_url)
|
||||
sleep 2
|
||||
msg_ok "${CL}${BL}${URL}${CL}"
|
||||
CACHE_FILE="$(vm_image_cache_path "$URL")"
|
||||
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes $((100 * 1024 * 1024)) || exit 115
|
||||
FILE="$(basename "$CACHE_FILE")"
|
||||
# Work on a copy: image preparation rewrites the image, which would poison the cache for every later VM.
|
||||
cp -f "$CACHE_FILE" "$FILE"
|
||||
|
||||
# Resize the imported disk with vm_resize_disk; Cloud-Init grows the actual root
|
||||
# filesystem before the first-boot installer runs, without another offline copy.
|
||||
|
||||
# --- Download UniFi OS installer on the host ---
|
||||
msg_info "Downloading UniFi OS Server ${UOS_VERSION} installer"
|
||||
curl -fsSL "${UOS_URL}" -o "unifi-os-server.bin"
|
||||
chmod +x "unifi-os-server.bin"
|
||||
msg_ok "Downloaded UniFi OS Server installer"
|
||||
|
||||
# --- Pre-install packages and setup first-boot installer ---
|
||||
msg_info "Customizing disk image (installing packages, staging installer)"
|
||||
|
||||
# Create the first-boot installer script
|
||||
FIRSTBOOT_SCRIPT="$TEMP_DIR/unifi-os-firstboot.sh"
|
||||
cat >"$FIRSTBOOT_SCRIPT" <<'FBEOF'
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
LOG="/var/log/unifi-os-install.log"
|
||||
exec > >(tee -a "$LOG") 2>&1
|
||||
echo "[$(date)] Starting UniFi OS Server first-boot setup..."
|
||||
trap 'echo "[$(date)] ERROR: First-boot setup failed at line $LINENO"' ERR
|
||||
if ! systemctl start qemu-guest-agent; then
|
||||
echo "[$(date)] WARNING: Preinstalled guest agent could not start; retrying after package installation"
|
||||
fi
|
||||
|
||||
# Sync clock before apt (fresh VMs have clock skew that breaks GPG signature validation)
|
||||
echo "[$(date)] Syncing system clock..."
|
||||
if ! timedatectl set-ntp true; then
|
||||
echo "[$(date)] WARNING: Could not enable NTP; checking existing clock synchronization"
|
||||
fi
|
||||
# Try NTP first
|
||||
for attempt in {1..6}; do
|
||||
if timedatectl show -p NTPSynchronized --value 2>/dev/null | grep -q "yes"; then
|
||||
echo "[$(date)] Clock synchronized via NTP"
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
# Fallback: sync from HTTP header if NTP didn't work
|
||||
if ! timedatectl show -p NTPSynchronized --value 2>/dev/null | grep -q "yes"; then
|
||||
if HTTP_DATE=$(curl -fsSI --max-time 10 https://deb.debian.org | sed -n 's/^[Dd]ate: //p' | tr -d '\r') &&
|
||||
[ -n "$HTTP_DATE" ] && date -s "$HTTP_DATE" >/dev/null; then
|
||||
echo "[$(date)] Clock synchronized via HTTP"
|
||||
else
|
||||
echo "[$(date)] WARNING: Clock synchronization unavailable"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install required packages
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
echo "[$(date)] Installing packages..."
|
||||
for attempt in {1..3}; do
|
||||
if apt-get update -qq 2>&1; then
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
echo "[$(date)] apt-get update failed after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
echo "[$(date)] apt-get update failed (attempt $attempt/3), retrying in 10s..."
|
||||
sleep 10
|
||||
done
|
||||
for attempt in {1..3}; do
|
||||
if apt-get install -y -qq qemu-guest-agent podman uidmap slirp4netns curl wget; then
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
echo "[$(date)] apt-get install failed after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
echo "[$(date)] apt-get install failed (attempt $attempt/3), retrying in 10s..."
|
||||
sleep 10
|
||||
done
|
||||
systemctl enable --now qemu-guest-agent
|
||||
echo "[$(date)] Packages installed"
|
||||
|
||||
# Setup swap (2GB)
|
||||
if [ ! -f /swapfile ]; then
|
||||
fallocate -l 2G /swapfile
|
||||
chmod 600 /swapfile
|
||||
mkswap /swapfile
|
||||
swapon /swapfile
|
||||
echo '/swapfile none swap sw 0 0' >> /etc/fstab
|
||||
echo "[$(date)] Swap file created"
|
||||
fi
|
||||
|
||||
# Run UniFi OS installer
|
||||
if [ -f /opt/unifi-os-server.bin ]; then
|
||||
cd /opt
|
||||
./unifi-os-server.bin <<<'y'
|
||||
rm -f /opt/unifi-os-server.bin
|
||||
echo "[$(date)] UniFi OS Server installed successfully"
|
||||
else
|
||||
echo "[$(date)] ERROR: /opt/unifi-os-server.bin not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[$(date)] First-boot setup complete"
|
||||
FBEOF
|
||||
|
||||
vm_prepare_cloud_image "$FILE" "$HN"
|
||||
|
||||
vm_customize "UniFi OS installer" "$FILE" \
|
||||
--upload "unifi-os-server.bin:/opt/unifi-os-server.bin" \
|
||||
--chmod 0755:/opt/unifi-os-server.bin \
|
||||
--run-command "systemctl enable ssh" || exit 1
|
||||
|
||||
vm_firstboot_unit "$FILE" "unifi-os-firstboot" "$FIRSTBOOT_SCRIPT" \
|
||||
--description "UniFi OS Server First Boot Installer" \
|
||||
--after qemu-guest-agent.service \
|
||||
--requires-path /opt/unifi-os-server.bin \
|
||||
--cloud-init yes || exit 1
|
||||
|
||||
rm -f "$FIRSTBOOT_SCRIPT" "unifi-os-server.bin"
|
||||
msg_ok "Disk image customized (UniFi OS ${UOS_VERSION} staged for first-boot install)"
|
||||
|
||||
vm_claim_vmid
|
||||
msg_info "Creating UniFi OS VM"
|
||||
qm create "$VMID" -agent 1${MACHINE} -tablet 0 -bios ovmf \
|
||||
${CPU_TYPE} -cores "$CORE_COUNT" -memory "$RAM_SIZE" \
|
||||
-name "$HN" -tags community-script \
|
||||
-net0 virtio,bridge="$BRG",macaddr="$MAC""$VLAN""$MTU" \
|
||||
-onboot 1 -ostype l26 -scsihw virtio-scsi-pci
|
||||
vm_mark_created
|
||||
vm_import_disk "$VMID" "$FILE" "$STORAGE" "$DISK_IMPORT_FORMAT"
|
||||
|
||||
qm set "$VMID" \
|
||||
-efidisk0 "${STORAGE}:0,efitype=4m" \
|
||||
-scsi0 "${VM_IMPORTED_DISK},${DISK_CACHE}size=${DISK_SIZE}" \
|
||||
-boot order=scsi0 -serial0 socket >/dev/null
|
||||
vm_resize_disk
|
||||
qm set "$VMID" --agent enabled=1 >/dev/null
|
||||
|
||||
vm_provision "$VMID"
|
||||
# Core currently suppresses SSH-key write errors; keep them fatal for this VM.
|
||||
if [[ -n "${CLOUDINIT_SSH_KEYS:-}" ]]; then
|
||||
qm set "$VMID" --sshkeys "$CLOUDINIT_SSH_KEYS" >/dev/null
|
||||
fi
|
||||
|
||||
set_description
|
||||
|
||||
msg_ok "Created a UniFi OS VM ${CL}${BL}(${HN})"
|
||||
|
||||
VM_IP=""
|
||||
UNIFI_READY=""
|
||||
FIRSTBOOT_DONE=""
|
||||
if [ "$START_VM" == "yes" ]; then
|
||||
vm_start_vm "UniFi OS VM"
|
||||
vm_wait_for_ip 360 || true
|
||||
|
||||
if [[ -n "${VM_FIRSTBOOT_MARKER:-}" ]] && vm_guest_exec "$VMID" 10 test -f "$VM_FIRSTBOOT_MARKER" >/dev/null; then
|
||||
FIRSTBOOT_DONE="yes"
|
||||
else
|
||||
msg_warn "UniFi OS first-boot installation has not completed yet"
|
||||
fi
|
||||
|
||||
if [[ -n "${VM_IP:-}" ]] && vm_wait_http "https://${VM_IP}:11443" 300 --insecure; then
|
||||
UNIFI_READY="yes"
|
||||
msg_ok "UniFi OS is up at https://${VM_IP}:11443"
|
||||
elif [[ -n "${VM_IP:-}" ]]; then
|
||||
msg_warn "UniFi OS is not ready; first-boot installation may still be running or may have failed"
|
||||
fi
|
||||
else
|
||||
msg_info "Start VM ${VMID} to run the UniFi OS first-boot installation"
|
||||
fi
|
||||
|
||||
vm_print_summary \
|
||||
"Operating System=${OS_DISPLAY}" \
|
||||
"UniFi OS Version=${UOS_VERSION}" \
|
||||
"Web Interface=https://${VM_IP:-<VM-IP>}:11443" \
|
||||
"Console Login=${CLOUDINIT_USER:-root}"
|
||||
|
||||
if [[ "$UNIFI_READY" == "yes" ]]; then
|
||||
vm_next_steps \
|
||||
"Open https://${VM_IP}:11443 and complete UniFi OS setup."
|
||||
FINISH_MESSAGE="UniFi OS Server VM is ready."
|
||||
else
|
||||
vm_next_steps \
|
||||
"Wait for first-boot installation to complete in the VM: journalctl -u unifi-os-firstboot.service" \
|
||||
"Follow progress in /var/log/unifi-os-install.log inside the VM." \
|
||||
"Open https://${VM_IP:-<VM-IP>}:11443 after the installer finishes."
|
||||
if [[ "$FIRSTBOOT_DONE" == "yes" ]]; then
|
||||
FINISH_MESSAGE="VM provisioning completed; UniFi OS is installed, but web readiness was not confirmed yet."
|
||||
else
|
||||
FINISH_MESSAGE="VM provisioning completed; UniFi OS installation continues in the VM on first boot."
|
||||
fi
|
||||
fi
|
||||
vm_finish "$FINISH_MESSAGE"
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/IceWhaleTech/ZimaOS
|
||||
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main}"
|
||||
source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/pve/vm-core.func")
|
||||
load_functions
|
||||
|
||||
APP="ZimaOS"
|
||||
APP_TYPE="vm"
|
||||
NSAPP="zimaos-vm"
|
||||
var_os="zimaos"
|
||||
var_version=" "
|
||||
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
|
||||
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
METHOD=""
|
||||
THIN="discard=on,ssd=1,"
|
||||
|
||||
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
|
||||
|
||||
function default_settings() {
|
||||
VMID=$(get_valid_nextid)
|
||||
vm_apply_machine_type "q35"
|
||||
DISK_SIZE="64G"
|
||||
DISK_CACHE=""
|
||||
HN="zimaos"
|
||||
CPU_TYPE=" -cpu host"
|
||||
CORE_COUNT="4"
|
||||
RAM_SIZE="4096"
|
||||
BRG="vmbr0"
|
||||
MAC="$GEN_MAC"
|
||||
VLAN=""
|
||||
MTU=""
|
||||
START_VM="yes"
|
||||
METHOD="default"
|
||||
vm_echo_default_settings
|
||||
}
|
||||
|
||||
function advanced_settings() {
|
||||
METHOD="advanced"
|
||||
vm_prompt_vmid "${VMID:-$(get_valid_nextid)}"
|
||||
vm_prompt_machine_type "q35"
|
||||
vm_prompt_disk_size "64G" "Set Disk Size in GiB (Recommended: 64+ for apps)"
|
||||
vm_prompt_disk_cache "none"
|
||||
vm_prompt_hostname "zimaos"
|
||||
vm_prompt_cpu_model "host"
|
||||
vm_prompt_cpu_cores "4"
|
||||
vm_prompt_ram "4096"
|
||||
vm_prompt_bridge "vmbr0"
|
||||
vm_prompt_mac "$GEN_MAC"
|
||||
vm_prompt_vlan
|
||||
vm_prompt_mtu
|
||||
vm_prompt_keyboard
|
||||
vm_prompt_verbose "no"
|
||||
vm_prompt_start_vm "yes"
|
||||
|
||||
if vm_confirm_advanced_settings "Ready to create a ZimaOS VM?"; then
|
||||
echo -e "${CREATING}${BOLD}${DGN}Creating a ZimaOS VM using the above advanced settings${CL}"
|
||||
else
|
||||
header_info
|
||||
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
|
||||
advanced_settings
|
||||
fi
|
||||
}
|
||||
|
||||
vm_start_script "Use Default Settings?\n\nDefaults:\n• 4 CPU Cores (Host model)\n• 4 GB RAM\n• 64 GB Disk\n• Q35 Machine Type" 14 58
|
||||
post_to_api_vm
|
||||
|
||||
vm_select_storage "$HN"
|
||||
|
||||
msg_info "Retrieving the URL for the ZimaOS installer"
|
||||
if ! vm_release_asset github IceWhaleTech/ZimaOS 'zimaos-x86_64-.*_installer\.iso$'; then
|
||||
exit 1
|
||||
fi
|
||||
URL="$VM_RELEASE_URL"
|
||||
FILENAME="$VM_RELEASE_ASSET"
|
||||
ZIMAOS_VERSION="$VM_RELEASE_VERSION"
|
||||
var_version="$ZIMAOS_VERSION"
|
||||
vm_select_iso_storage "$FILENAME" "$HN"
|
||||
CACHE_FILE="$ISO_PATH"
|
||||
msg_ok "ZimaOS ${CL}${BL}${ZIMAOS_VERSION}${CL}"
|
||||
|
||||
[[ -s "$CACHE_FILE" ]] || msg_warn "Downloading the ZimaOS installer (approximately 2 GB, this may take a while)"
|
||||
# Official installers such as 1.8.0-beta2 are smaller than 2 GiB.
|
||||
MIN_ISO_BYTES=$((1024 * 1024 * 1024))
|
||||
vm_fetch_image "$URL" "$CACHE_FILE" --cache --min-bytes "$MIN_ISO_BYTES" --sha256 "$VM_RELEASE_SHA256" || exit 115
|
||||
|
||||
vm_claim_vmid
|
||||
msg_info "Creating a ZimaOS VM"
|
||||
$STD qm create $VMID -agent 1${MACHINE} -tablet 0 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
|
||||
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-single \
|
||||
-efidisk0 ${STORAGE}:1,efitype=4m,pre-enrolled-keys=0 -scsi0 ${STORAGE}:${DISK_SIZE%G},${DISK_CACHE}${THIN%,} \
|
||||
-cdrom "$ISO_VOLUME" -boot order='scsi0;ide2' -vga std -serial0 socket
|
||||
vm_mark_created
|
||||
set_description
|
||||
msg_ok "Created a ZimaOS VM ${CL}${BL}(${HN})"
|
||||
|
||||
vm_start_vm "ZimaOS VM"
|
||||
vm_print_summary "Version=${ZIMAOS_VERSION}" "ISO=${FILENAME}" "Discovery=https://find.zimaspace.com"
|
||||
vm_next_steps \
|
||||
"Open the VM Console in Proxmox." \
|
||||
"Follow the installer and select scsi0 as the target disk." \
|
||||
"When prompted to remove the disk and reboot, reboot; the boot order prefers the disk." \
|
||||
"Detach the ISO afterwards (Hardware -> CD/DVD -> Remove)." \
|
||||
"Read the IP from the Proxmox summary once the guest agent is up, or use https://find.zimaspace.com." \
|
||||
"For NAS use, add a second disk in Proxmox and let ZimaOS manage it."
|
||||
vm_finish "VM created; complete the ZimaOS installation in the Proxmox console."
|
||||
Reference in New Issue
Block a user