mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-10-08 09:07:36 -04:00
Compare commits
10 Commits
MickLesk-p
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
632bfa43dc | ||
|
|
3d7c129646 | ||
|
|
6be105b961 | ||
|
|
de430e8131 | ||
|
|
0de4640699 | ||
|
|
ba752598eb | ||
|
|
84d2c964e6 | ||
|
|
6ffb433d5d | ||
|
|
a2474baaf1 | ||
|
|
005cdcdb2b |
23
CHANGELOG.md
23
CHANGELOG.md
@@ -561,17 +561,32 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
|
|
||||||
## 2026-10-08
|
## 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
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
- Revert "Immich: Pin to v3.3.0" [@MickLesk](https://github.com/MickLesk) ([#17767](https://github.com/community-scripts/ProxmoxVE/pull/17767))
|
- Revert "Immich: Pin to v3.3.0" [@MickLesk](https://github.com/MickLesk) ([#17767](https://github.com/community-scripts/ProxmoxVE/pull/17767))
|
||||||
|
|
||||||
|
- #### ✨ 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: Ubuntu VM [@MickLesk](https://github.com/MickLesk) ([#17776](https://github.com/community-scripts/ProxmoxVE/pull/17776))
|
||||||
|
|
||||||
### 💾 Core
|
### 💾 Core
|
||||||
|
|
||||||
- VM's: run first-boot units without debconf dialogs [@MickLesk](https://github.com/MickLesk) ([core#121](https://github.com/community-scripts/core/pull/121))
|
- Tolerate a missing datacenter.cfg in vm_keyboard_default [@MickLesk](https://github.com/MickLesk) ([core#124](https://github.com/community-scripts/core/pull/124))
|
||||||
- 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))
|
- 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))
|
||||||
- Remember a kept cached image until the upstream changes [@MickLesk](https://github.com/MickLesk) ([core#119](https://github.com/community-scripts/core/pull/119))
|
|
||||||
- tools: forge truncated release list [@MickLesk](https://github.com/MickLesk) ([core#118](https://github.com/community-scripts/core/pull/118))
|
|
||||||
- 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))
|
- 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
|
## 2026-10-07
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,12 @@ function update_script() {
|
|||||||
|
|
||||||
$STD corepack prepare "pnpm@${PNPM_VERSION}" --activate
|
$STD corepack prepare "pnpm@${PNPM_VERSION}" --activate
|
||||||
$STD pnpm install --frozen-lockfile
|
$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
|
$STD pnpm --filter server run build
|
||||||
cp -r /opt/bookorbit/client/dist /opt/bookorbit/server/public
|
cp -r /opt/bookorbit/client/dist /opt/bookorbit/server/public
|
||||||
mkdir -p /opt/bookorbit/server/migrations
|
mkdir -p /opt/bookorbit/server/migrations
|
||||||
|
|||||||
35
ct/immich.sh
35
ct/immich.sh
@@ -77,7 +77,7 @@ EOF
|
|||||||
BASE_DIR=${STAGING_DIR}/base-images
|
BASE_DIR=${STAGING_DIR}/base-images
|
||||||
SOURCE_DIR=${STAGING_DIR}/image-source
|
SOURCE_DIR=${STAGING_DIR}/image-source
|
||||||
cd /tmp
|
cd /tmp
|
||||||
RELEASE="v3.2.4"
|
RELEASE="v3.3.0"
|
||||||
if [[ -f ~/.intel_version ]]; then
|
if [[ -f ~/.intel_version ]]; then
|
||||||
INTEL_SRC="https://raw.githubusercontent.com/immich-app/immich/${RELEASE}/machine-learning"
|
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 ||
|
curl -fsSL "${INTEL_SRC}/scripts/install-intel-runtime.sh" -o ./intel-runtime 2>/dev/null ||
|
||||||
@@ -274,12 +274,13 @@ EOF
|
|||||||
msg_info "Updating Intel OpenVINO machine-learning"
|
msg_info "Updating Intel OpenVINO machine-learning"
|
||||||
for attempt in $(seq 1 3); do
|
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
|
$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
|
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
|
||||||
|
msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
|
||||||
done
|
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"
|
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"
|
msg_ok "Updated Intel OpenVINO machine-learning"
|
||||||
else
|
else
|
||||||
ML_PYTHON="python3.11"
|
ML_PYTHON="python3.13"
|
||||||
msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning"
|
msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning"
|
||||||
for attempt in $(seq 1 3); do
|
for attempt in $(seq 1 3); do
|
||||||
$STD sudo --preserve-env=VIRTUAL_ENV -Pnu immich uv python install "${ML_PYTHON}" && break
|
$STD sudo --preserve-env=VIRTUAL_ENV -Pnu immich uv python install "${ML_PYTHON}" && break
|
||||||
@@ -289,7 +290,8 @@ EOF
|
|||||||
msg_info "Updating machine-learning"
|
msg_info "Updating machine-learning"
|
||||||
for attempt in $(seq 1 3); do
|
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
|
$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
|
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
|
||||||
|
msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
|
||||||
done
|
done
|
||||||
msg_ok "Updated machine-learning"
|
msg_ok "Updated machine-learning"
|
||||||
fi
|
fi
|
||||||
@@ -333,6 +335,10 @@ EOF
|
|||||||
sed -i -e '$a\' "$INSTALL_DIR"/.env
|
sed -i -e '$a\' "$INSTALL_DIR"/.env
|
||||||
echo "IMMICH_HELMET_FILE=true" >>"$INSTALL_DIR"/.env
|
echo "IMMICH_HELMET_FILE=true" >>"$INSTALL_DIR"/.env
|
||||||
fi
|
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
|
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 '/^EnvironmentFile=/d' /etc/systemd/system/immich-web.service
|
||||||
@@ -341,25 +347,12 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# MickLesk temporary patch for HEIC thumbnail gen
|
# MickLesk temporary patch for HEIC thumbnail gen
|
||||||
msg_info "Patching media.repository.js"
|
|
||||||
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
|
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
|
||||||
if [[ -f "$MEDIA_REPO_JS" ]]; then
|
if grep -qF "(0, sharp_1.default)(input).metadata()" "$MEDIA_REPO_JS" 2>/dev/null; then
|
||||||
python3 - <<'PY'
|
msg_info "Patching media.repository.js"
|
||||||
from pathlib import Path
|
sed -i '0,/(0, sharp_1\.default)(input)\.metadata()/s//(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()/' "$MEDIA_REPO_JS"
|
||||||
p = Path('/opt/immich/app/dist/repositories/media.repository.js')
|
msg_ok "Patched 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
|
|
||||||
fi
|
fi
|
||||||
msg_ok "Patched media.repository.js"
|
|
||||||
|
|
||||||
# chown excluding upload dir contents (may be a mount with restricted permissions)
|
# chown excluding upload dir contents (may be a mount with restricted permissions)
|
||||||
chown immich:immich "$INSTALL_DIR"
|
chown immich:immich "$INSTALL_DIR"
|
||||||
|
|||||||
@@ -33,7 +33,12 @@ PNPM_VERSION=$(jq -r '.packageManager | ltrimstr("pnpm@")' /opt/bookorbit/packag
|
|||||||
|
|
||||||
$STD corepack prepare "pnpm@${PNPM_VERSION}" --activate
|
$STD corepack prepare "pnpm@${PNPM_VERSION}" --activate
|
||||||
$STD pnpm install --frozen-lockfile
|
$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
|
$STD pnpm --filter server run build
|
||||||
cp -r /opt/bookorbit/client/dist /opt/bookorbit/server/public
|
cp -r /opt/bookorbit/client/dist /opt/bookorbit/server/public
|
||||||
mkdir -p /opt/bookorbit/server/migrations
|
mkdir -p /opt/bookorbit/server/migrations
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ setting_up_container
|
|||||||
network_check
|
network_check
|
||||||
update_os
|
update_os
|
||||||
|
|
||||||
RELEASE="v3.2.4"
|
RELEASE="v3.3.0"
|
||||||
if lscpu | grep -q 'GenuineIntel'; then
|
if lscpu | grep -q 'GenuineIntel'; then
|
||||||
echo ""
|
echo ""
|
||||||
echo ""
|
echo ""
|
||||||
@@ -442,12 +442,13 @@ if [[ -f ~/.openvino ]]; then
|
|||||||
msg_info "Installing Intel OpenVINO machine-learning"
|
msg_info "Installing Intel OpenVINO machine-learning"
|
||||||
for attempt in $(seq 1 3); do
|
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
|
$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
|
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
|
||||||
|
msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
|
||||||
done
|
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"
|
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"
|
msg_ok "Installed Intel OpenVINO machine-learning"
|
||||||
else
|
else
|
||||||
ML_PYTHON="python3.11"
|
ML_PYTHON="python3.13"
|
||||||
msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning"
|
msg_info "Pre-installing Python ${ML_PYTHON} for machine-learning"
|
||||||
for attempt in $(seq 1 3); do
|
for attempt in $(seq 1 3); do
|
||||||
$STD sudo --preserve-env=VIRTUAL_ENV -Pnu immich uv python install "${ML_PYTHON}" && break
|
$STD sudo --preserve-env=VIRTUAL_ENV -Pnu immich uv python install "${ML_PYTHON}" && break
|
||||||
@@ -457,7 +458,8 @@ else
|
|||||||
msg_info "Installing machine-learning"
|
msg_info "Installing machine-learning"
|
||||||
for attempt in $(seq 1 3); do
|
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
|
$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
|
[[ $attempt -eq 3 ]] && { msg_error "uv sync failed three times, the machine-learning environment was not built"; exit 1; }
|
||||||
|
msg_warn "uv sync attempt $attempt failed, retrying..." && sleep 10
|
||||||
done
|
done
|
||||||
msg_ok "Installed machine-learning"
|
msg_ok "Installed machine-learning"
|
||||||
fi
|
fi
|
||||||
@@ -488,25 +490,12 @@ ln -s "$GEO_DIR" "$APP_DIR"
|
|||||||
msg_ok "Installed GeoNames data"
|
msg_ok "Installed GeoNames data"
|
||||||
|
|
||||||
# MickLesk temporary patch for HEIC thumbnail gen
|
# MickLesk temporary patch for HEIC thumbnail gen
|
||||||
msg_info "Patching media.repository.js"
|
|
||||||
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
|
MEDIA_REPO_JS="/opt/immich/app/dist/repositories/media.repository.js"
|
||||||
if [[ -f "$MEDIA_REPO_JS" ]]; then
|
if grep -qF "(0, sharp_1.default)(input).metadata()" "$MEDIA_REPO_JS" 2>/dev/null; then
|
||||||
python3 - <<'PY'
|
msg_info "Patching media.repository.js"
|
||||||
from pathlib import Path
|
sed -i '0,/(0, sharp_1\.default)(input)\.metadata()/s//(0, sharp_1.default)(input, { unlimited: true, limitInputPixels: false }).metadata()/' "$MEDIA_REPO_JS"
|
||||||
p = Path('/opt/immich/app/dist/repositories/media.repository.js')
|
msg_ok "Patched 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
|
|
||||||
fi
|
fi
|
||||||
msg_ok "Patched media.repository.js"
|
|
||||||
|
|
||||||
mkdir -p /var/log/immich
|
mkdir -p /var/log/immich
|
||||||
touch /var/log/immich/{web.log,ml.log}
|
touch /var/log/immich/{web.log,ml.log}
|
||||||
@@ -537,6 +526,7 @@ MACHINE_LEARNING_CACHE_FOLDER=${INSTALL_DIR}/cache
|
|||||||
## - inference speed while reducing accuracy
|
## - inference speed while reducing accuracy
|
||||||
## - Default is FP32
|
## - Default is FP32
|
||||||
# MACHINE_LEARNING_OPENVINO_PRECISION=FP16
|
# MACHINE_LEARNING_OPENVINO_PRECISION=FP16
|
||||||
|
MACHINE_LEARNING_MODEL_REVISION=v2
|
||||||
|
|
||||||
IMMICH_MEDIA_LOCATION=${UPLOAD_DIR}
|
IMMICH_MEDIA_LOCATION=${UPLOAD_DIR}
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
436
vm/unifi-os-server-vm.sh
Normal file
436
vm/unifi-os-server-vm.sh
Normal file
@@ -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
vm/zimaos-vm.sh
Normal file
126
vm/zimaos-vm.sh
Normal file
@@ -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