This commit is contained in:
2026-07-25 18:45:18 -05:00
parent 808546846c
commit 6bed7068c7
4 changed files with 428 additions and 38 deletions
+7
View File
@@ -50,6 +50,7 @@ restart Restart core local Proxmox management services
cpu Detect and optionally apply a migration-safe CPU model cpu Detect and optionally apply a migration-safe CPU model
maintenance Toggle local HA maintenance mode maintenance Toggle local HA maintenance mode
keepalived Deploy Keepalived across the cluster keepalived Deploy Keepalived across the cluster
iso-nfs Create an LXC NFS server and cluster-wide shared ISO storage
``` ```
## Companion files ## Companion files
@@ -71,6 +72,12 @@ portal without changing ProxMenu.
The Keepalived deployment additionally requires a healthy, quorate Proxmox The Keepalived deployment additionally requires a healthy, quorate Proxmox
cluster and passwordless root SSH between cluster nodes. cluster and passwordless root SSH between cluster nodes.
The shared ISO storage wizard creates a privileged Debian LXC with a dedicated
secondary volume, restricts its NFS export to a supplied client CIDR, and adds
the export to Proxmox's cluster-wide storage configuration. The container host
and network path to the container must remain available for nodes to use the
ISO repository.
The legacy Dell OMSA installer is limited to supported PowerEdge x30/x40 The legacy Dell OMSA installer is limited to supported PowerEdge x30/x40
systems running Proxmox VE 9 on Debian 13 (Trixie), amd64. systems running Proxmox VE 9 on Debian 13 (Trixie), amd64.
+1 -9
View File
@@ -3,7 +3,7 @@
action="${1:-}" action="${1:-}"
FOLDER='/opt/idssys/ta-proxmenu' FOLDER='/opt/idssys/ta-proxmenu'
VERS='2026.7.25-35' VERS='2026.7.25-37'
noupdate=' ' noupdate=' '
@@ -20,14 +20,6 @@ TAPM_BROKER_URL='https://tapm.scity.us'
S1_BROKER_PACKAGE='sentinelone-linux' S1_BROKER_PACKAGE='sentinelone-linux'
S1_PACKAGE='tapm-sentinelone.deb' S1_PACKAGE='tapm-sentinelone.deb'
if [[ -d /mnt/pve/PVE-Shared-Storage/template/iso ]]; then
DLDIR='/mnt/pve/PVE-Shared-Storage/template/iso'
else
DLDIR='/var/lib/vz/template/iso'
fi
# if [ -f /etc/apt/sources.list.d/gyptazy.list ]; then # if [ -f /etc/apt/sources.list.d/gyptazy.list ]; then
# rm -f /etc/apt/sources.list.d/gyptazy.list /etc/apt/keyrings/gyptazy.asc # rm -f /etc/apt/sources.list.d/gyptazy.list /etc/apt/keyrings/gyptazy.asc
# echo "deb https://repo.gyptazy.com/stable /" > /etc/apt/sources.list.d/proxlb.list # echo "deb https://repo.gyptazy.com/stable /" > /etc/apt/sources.list.d/proxlb.list
+263
View File
@@ -0,0 +1,263 @@
#!/usr/bin/env bash
# Deploy a dedicated LXC NFS server and register it as cluster ISO storage.
TAPM_ISO_NFS_VALID_ID() {
[[ "${1:-}" =~ ^[A-Za-z][A-Za-z0-9_-]{0,31}$ ]]
}
TAPM_ISO_NFS_VALID_CTID() {
[[ "${1:-}" =~ ^[1-9][0-9]{2,8}$ ]]
}
TAPM_ISO_NFS_VALID_IPV4_CIDR() {
local value="${1:-}"
local address prefix octet
local -a octets
[[ "$value" == */* ]] || return 1
address="${value%/*}"
prefix="${value#*/}"
[[ "$prefix" =~ ^[0-9]+$ ]] && (( prefix <= 32 )) || return 1
IFS=. read -r -a octets <<<"$address"
(( ${#octets[@]} == 4 )) || return 1
for octet in "${octets[@]}"; do
[[ "$octet" =~ ^[0-9]+$ ]] && (( 10#$octet <= 255 )) || return 1
done
}
TAPM_ISO_NFS_PROMPT() {
local variable="$1"
local label="$2"
local default_value="${3:-}"
local value
if [[ -n "$default_value" ]]; then
read -r -p " ${label} [${default_value}]: " value
printf -v "$variable" '%s' "${value:-$default_value}"
else
read -r -p " ${label}: " value
printf -v "$variable" '%s' "$value"
fi
}
TAPM_ISO_NFS_FAIL() {
echo -e "\n${idsCL[LightRed]}$1${idsCL[Default]}"
return 1
}
TAPM_ISO_NFS_SELECT_STORAGE() {
local variable="$1"
local label="$2"
local default_value="$3"
local value
echo
echo " Active storages that support LXC volumes:"
pvesm status --content rootdir 2>/dev/null |
awk 'NR == 1 || $3 == "active" { printf " %s\n", $1 }'
TAPM_ISO_NFS_PROMPT value "$label" "$default_value"
if ! pvesm status --content rootdir 2>/dev/null |
awk 'NR > 1 && $3 == "active" { print $1 }' |
grep -Fxq -- "$value"; then
TAPM_ISO_NFS_FAIL "Storage '${value}' is not active here or does not support LXC volumes."
return 1
fi
printf -v "$variable" '%s' "$value"
}
TAPM_DEPLOY_ISO_NFS_LXC() {
local ctid default_ctid hostname address_cidr server_ip gateway bridge
local client_cidr root_storage data_storage root_size data_size
local pve_storage_id template_storage template_name template_path
local default_root_storage default_template_storage choice test_file
local container_config
echo
echo -e "${idsCL[LightCyan]}Shared ISO storage using an LXC NFS server${idsCL[Default]}"
echo
echo " This creates a privileged Debian LXC with an unconfined AppArmor profile,"
echo " allocates a dedicated mp0 data volume, exports it to the cluster, and"
echo " registers it in storage.cfg. This reduced isolation is required for the"
echo " kernel NFS service; do not run unrelated or untrusted software in this LXC."
echo " The container's host must be online for ISO storage to remain available."
echo
[[ $EUID -eq 0 ]] ||
{ TAPM_ISO_NFS_FAIL "Run this action as root on a Proxmox VE host."; return 1; }
for command in pct pvesm pveam pvesh; do
command -v "$command" >/dev/null 2>&1 ||
{ TAPM_ISO_NFS_FAIL "Required Proxmox command '${command}' was not found."; return 1; }
done
default_ctid="$(pvesh get /cluster/nextid 2>/dev/null || true)"
TAPM_ISO_NFS_PROMPT ctid "Container ID" "$default_ctid"
TAPM_ISO_NFS_VALID_CTID "$ctid" ||
{ TAPM_ISO_NFS_FAIL "The container ID is invalid."; return 1; }
if pct status "$ctid" >/dev/null 2>&1; then
TAPM_ISO_NFS_FAIL "Container ${ctid} already exists; no changes were made."
return 1
fi
TAPM_ISO_NFS_PROMPT hostname "Container hostname" "iso-nfs"
[[ "$hostname" =~ ^[A-Za-z0-9][A-Za-z0-9.-]{0,62}$ ]] ||
{ TAPM_ISO_NFS_FAIL "The hostname is invalid."; return 1; }
TAPM_ISO_NFS_PROMPT address_cidr "Static IPv4 address with prefix (example: 10.20.30.10/24)"
TAPM_ISO_NFS_VALID_IPV4_CIDR "$address_cidr" ||
{ TAPM_ISO_NFS_FAIL "A valid static IPv4 address and prefix are required."; return 1; }
server_ip="${address_cidr%/*}"
TAPM_ISO_NFS_PROMPT gateway "IPv4 gateway"
TAPM_ISO_NFS_VALID_IPV4_CIDR "${gateway}/32" ||
{ TAPM_ISO_NFS_FAIL "A valid IPv4 gateway is required."; return 1; }
TAPM_ISO_NFS_PROMPT bridge "Proxmox bridge" "vmbr0"
ip link show "$bridge" >/dev/null 2>&1 ||
{ TAPM_ISO_NFS_FAIL "Bridge '${bridge}' does not exist on this host."; return 1; }
client_cidr="$(
python3 -c 'import ipaddress,sys; print(ipaddress.ip_interface(sys.argv[1]).network)' \
"$address_cidr" 2>/dev/null
)" || client_cidr=''
TAPM_ISO_NFS_PROMPT client_cidr "CIDR allowed to mount the export" "$client_cidr"
TAPM_ISO_NFS_VALID_IPV4_CIDR "$client_cidr" ||
{ TAPM_ISO_NFS_FAIL "The allowed client CIDR is invalid."; return 1; }
default_root_storage="$(
pvesm status --content rootdir 2>/dev/null |
awk 'NR > 1 && $3 == "active" { print $1; exit }'
)"
[[ -n "$default_root_storage" ]] ||
{ TAPM_ISO_NFS_FAIL "No active storage supports LXC volumes."; return 1; }
TAPM_ISO_NFS_SELECT_STORAGE root_storage "Root filesystem storage" "$default_root_storage" || return 1
TAPM_ISO_NFS_SELECT_STORAGE data_storage "Dedicated ISO volume storage" "$root_storage" || return 1
TAPM_ISO_NFS_PROMPT root_size "Root filesystem size in GiB" "8"
[[ "$root_size" =~ ^[1-9][0-9]*$ ]] ||
{ TAPM_ISO_NFS_FAIL "The root filesystem size must be a positive integer."; return 1; }
TAPM_ISO_NFS_PROMPT data_size "ISO volume size in GiB" "250"
[[ "$data_size" =~ ^[1-9][0-9]*$ ]] ||
{ TAPM_ISO_NFS_FAIL "The ISO volume size must be a positive integer."; return 1; }
TAPM_ISO_NFS_PROMPT pve_storage_id "Proxmox cluster storage ID" "PVE-Shared-Storage"
TAPM_ISO_NFS_VALID_ID "$pve_storage_id" ||
{ TAPM_ISO_NFS_FAIL "The Proxmox storage ID is invalid."; return 1; }
if pvesm status 2>/dev/null | awk 'NR > 1 { print $1 }' | grep -Fxq -- "$pve_storage_id"; then
TAPM_ISO_NFS_FAIL "Storage ID '${pve_storage_id}' already exists; no changes were made."
return 1
fi
default_template_storage="$(
pvesm status --content vztmpl 2>/dev/null |
awk 'NR > 1 && $3 == "active" { print $1; exit }'
)"
[[ -n "$default_template_storage" ]] ||
{ TAPM_ISO_NFS_FAIL "No active storage supports container templates."; return 1; }
template_storage="$default_template_storage"
echo
echo " Deployment summary"
echo " LXC: ${ctid} (${hostname}), privileged"
echo " Network: ${address_cidr} via ${gateway} on ${bridge}"
echo " Root volume: ${root_storage}:${root_size} GiB"
echo " ISO volume mp0: ${data_storage}:${data_size} GiB -> /srv/iso"
echo " NFS clients: ${client_cidr}"
echo " Cluster storage: ${pve_storage_id}"
echo
read -r -p " Create this container and storage (type YES to continue)? " choice
[[ "$choice" == "YES" ]] || {
echo " Cancelled; no changes were made."
return 0
}
echo -e "\n${idsCL[LightCyan]}Locating a Debian container template...${idsCL[Default]}"
pveam update || { TAPM_ISO_NFS_FAIL "Could not refresh the template catalog."; return 1; }
template_name="$(
pveam available --section system |
awk '$2 ~ /^debian-(13|12)-standard_/ { print $2 }' |
sort -V |
tail -1
)"
[[ -n "$template_name" ]] ||
{ TAPM_ISO_NFS_FAIL "No supported Debian 12/13 standard template was found."; return 1; }
template_path="${template_storage}:vztmpl/${template_name}"
if ! pveam list "$template_storage" 2>/dev/null |
awk 'NR > 1 { print $1 }' |
grep -Fxq -- "$template_path"; then
pveam download "$template_storage" "$template_name" ||
{ TAPM_ISO_NFS_FAIL "The Debian template download failed."; return 1; }
fi
echo -e "\n${idsCL[LightCyan]}Creating LXC ${ctid}...${idsCL[Default]}"
if ! pct create "$ctid" "$template_path" \
--hostname "$hostname" \
--ostype debian \
--unprivileged 0 \
--features nesting=1 \
--cores 2 \
--memory 1024 \
--swap 512 \
--rootfs "${root_storage}:${root_size}" \
--mp0 "${data_storage}:${data_size},mp=/srv/iso,backup=1" \
--net0 "name=eth0,bridge=${bridge},ip=${address_cidr},gw=${gateway},type=veth" \
--onboot 1 \
--startup order=1; then
TAPM_ISO_NFS_FAIL "Container creation failed."
return 1
fi
container_config="/etc/pve/lxc/${ctid}.conf"
if ! grep -q '^lxc\.apparmor\.profile:' "$container_config"; then
printf 'lxc.apparmor.profile: unconfined\n' >>"$container_config" ||
{ TAPM_ISO_NFS_FAIL "Container ${ctid} was created, but its NFS AppArmor setting could not be applied."; return 1; }
fi
pct start "$ctid" ||
{ TAPM_ISO_NFS_FAIL "Container ${ctid} was created but could not be started."; return 1; }
if ! timeout 60 bash -c \
"until pct exec '$ctid' -- test -d /run/systemd/system >/dev/null 2>&1; do sleep 2; done"; then
TAPM_ISO_NFS_FAIL "Container ${ctid} did not become ready within 60 seconds."
return 1
fi
echo -e "\n${idsCL[LightCyan]}Installing and configuring NFS...${idsCL[Default]}"
pct exec "$ctid" -- apt-get update ||
{ TAPM_ISO_NFS_FAIL "Package index refresh failed inside container ${ctid}."; return 1; }
pct exec "$ctid" -- env DEBIAN_FRONTEND=noninteractive \
apt-get install -y nfs-kernel-server ||
{ TAPM_ISO_NFS_FAIL "NFS package installation failed inside container ${ctid}."; return 1; }
pct exec "$ctid" -- install -d -m 0775 /srv/iso/template/iso ||
{ TAPM_ISO_NFS_FAIL "Could not initialize the ISO directory."; return 1; }
printf '/srv/iso %s(rw,sync,no_subtree_check,no_root_squash)\n' "$client_cidr" |
pct exec "$ctid" -- tee /etc/exports.d/proxmox-isos.exports >/dev/null ||
{ TAPM_ISO_NFS_FAIL "Could not write the NFS export configuration."; return 1; }
pct exec "$ctid" -- exportfs -ra ||
{ TAPM_ISO_NFS_FAIL "The NFS export configuration was rejected."; return 1; }
pct exec "$ctid" -- systemctl enable --now nfs-server ||
{ TAPM_ISO_NFS_FAIL "The NFS server could not be started."; return 1; }
pct exec "$ctid" -- exportfs -v | grep -Fq "/srv/iso" ||
{ TAPM_ISO_NFS_FAIL "The expected NFS export is not active."; return 1; }
echo -e "\n${idsCL[LightCyan]}Registering cluster storage...${idsCL[Default]}"
if ! pvesm add nfs "$pve_storage_id" \
--server "$server_ip" \
--export /srv/iso \
--content iso \
--options vers=3; then
TAPM_ISO_NFS_FAIL "The LXC is running, but Proxmox could not add the NFS storage."
return 1
fi
if ! timeout 30 pvesm status --storage "$pve_storage_id" |
awk -v id="$pve_storage_id" 'NR > 1 && $1 == id && $3 == "active" { found=1 } END { exit !found }'; then
pvesm remove "$pve_storage_id" >/dev/null 2>&1 || true
TAPM_ISO_NFS_FAIL "The NFS storage did not become active; its cluster entry was removed."
return 1
fi
test_file="/mnt/pve/${pve_storage_id}/template/iso/.tapm-write-test"
if ! touch "$test_file" || ! rm -f -- "$test_file"; then
pvesm remove "$pve_storage_id" >/dev/null 2>&1 || true
TAPM_ISO_NFS_FAIL "The NFS mount was not writable; its cluster entry was removed."
return 1
fi
echo
echo -e "${idsCL[Green]}Shared ISO storage '${pve_storage_id}' is active.${idsCL[Default]}"
echo " LXC ${ctid} serves ${data_storage}:${data_size} GiB from ${server_ip}:/srv/iso."
echo " Because Proxmox storage configuration is cluster-wide, every cluster node"
echo " can use it when that node can reach ${server_ip} and is allowed by ${client_cidr}."
return 0
}
+157 -29
View File
@@ -6,6 +6,7 @@
source /opt/idssys/defaults/default.inc source /opt/idssys/defaults/default.inc
source /opt/idssys/ta-proxmenu/defaults.inc source /opt/idssys/ta-proxmenu/defaults.inc
source /opt/idssys/ta-proxmenu/inc/git-update.inc source /opt/idssys/ta-proxmenu/inc/git-update.inc
source /opt/idssys/ta-proxmenu/inc/deploy-iso-nfs-lxc.sh
ACTION_REQUESTED=0 ACTION_REQUESTED=0
[[ -n "${action:-}" ]] && ACTION_REQUESTED=1 [[ -n "${action:-}" ]] && ACTION_REQUESTED=1
@@ -174,8 +175,9 @@ TAPM_AUTHORIZE() {
host_fingerprint="$(sha256sum /etc/machine-id | cut -d' ' -f1)" host_fingerprint="$(sha256sum /etc/machine-id | cut -d' ' -f1)"
exchange_response="$( exchange_response="$(
TAPM_CODE="$deploycode" TAPM_FINGERPRINT="$host_fingerprint" TAPM_HOSTNAME="$(hostname)" \ TAPM_CODE="$deploycode" TAPM_FINGERPRINT="$host_fingerprint" TAPM_HOSTNAME="$(hostname)" \
TAPM_LAN_IP="${RNIP:-}" \
TAPM_REQUESTED_ACTION="$required_action" TAPM_REQUESTED_PACKAGE="$required_package" \ TAPM_REQUESTED_ACTION="$required_action" TAPM_REQUESTED_PACKAGE="$required_package" \
python3 -c 'import json, os, sys; json.dump({"code": os.environ["TAPM_CODE"], "host_fingerprint": os.environ["TAPM_FINGERPRINT"], "hostname": os.environ["TAPM_HOSTNAME"], "requested_action": os.environ["TAPM_REQUESTED_ACTION"], "requested_package": os.environ["TAPM_REQUESTED_PACKAGE"]}, sys.stdout)' | python3 -c 'import json, os, sys; json.dump({"code": os.environ["TAPM_CODE"], "host_fingerprint": os.environ["TAPM_FINGERPRINT"], "hostname": os.environ["TAPM_HOSTNAME"], "lan_ip": os.environ["TAPM_LAN_IP"], "requested_action": os.environ["TAPM_REQUESTED_ACTION"], "requested_package": os.environ["TAPM_REQUESTED_PACKAGE"]}, sys.stdout)' |
curl --fail --silent --show-error \ curl --fail --silent --show-error \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--data-binary @- "${TAPM_BROKER_URL}/api/v1/exchange" --data-binary @- "${TAPM_BROKER_URL}/api/v1/exchange"
@@ -783,6 +785,11 @@ VIRTIO_STABLE_CHECKED=0
VIRTIO_STABLE_STATUS='unknown' VIRTIO_STABLE_STATUS='unknown'
VIRTIO_STABLE_FILE='' VIRTIO_STABLE_FILE=''
VIRTIO_LAST_FILE='' VIRTIO_LAST_FILE=''
DLDIR=''
VIRTIO_SELECTED_STORAGE=''
declare -a VIRTIO_STORAGE_IDS=()
declare -a VIRTIO_STORAGE_TYPES=()
declare -a VIRTIO_STORAGE_DIRS=()
TAPM_VIRTIO_FILENAME_FROM_URL() { TAPM_VIRTIO_FILENAME_FROM_URL() {
local url="${1%%\?*}" local url="${1%%\?*}"
@@ -792,12 +799,103 @@ TAPM_VIRTIO_FILENAME_FROM_URL() {
printf '%s\n' "$filename" printf '%s\n' "$filename"
} }
TAPM_VIRTIO_LABELED_FILENAME() {
local source_filename="$1"
local label="$2"
local version=''
[[ "$label" =~ ^[a-z0-9-]+$ ]] || return 1
if [[ "$source_filename" =~ ^virtio-win-(0\.1\.[0-9]+)\.iso$ ]]; then
version="${BASH_REMATCH[1]}"
elif [[ "$source_filename" != 'virtio-win.iso' ]]; then
return 1
fi
printf 'virtio-win-%s%s.iso\n' "$label" "${version:+-${version}}"
}
TAPM_REFRESH_ISO_STORAGES() {
local storage
local type
local status
local probe_path
VIRTIO_STORAGE_IDS=()
VIRTIO_STORAGE_TYPES=()
VIRTIO_STORAGE_DIRS=()
command -v pvesm >/dev/null 2>&1 || return 1
while read -r storage type status _; do
[[ "$storage" != 'Name' && "$status" == 'active' ]] || continue
probe_path="$(
pvesm path "${storage}:iso/tapm-path-probe.iso" 2>/dev/null
)" || continue
[[ "$probe_path" == /*/* ]] || continue
VIRTIO_STORAGE_IDS+=("$storage")
VIRTIO_STORAGE_TYPES+=("$type")
VIRTIO_STORAGE_DIRS+=("${probe_path%/*}")
done < <(pvesm status --content iso --enabled 1 2>/dev/null)
(( ${#VIRTIO_STORAGE_IDS[@]} > 0 ))
}
TAPM_SELECT_ISO_STORAGE() {
local index
local -a labels=()
local -a values=()
if ! TAPM_REFRESH_ISO_STORAGES; then
echo -e "${idsCL[LightRed]}No active, enabled Proxmox storage supporting ISO images was found.${idsCL[Default]}"
ENTER2CONTINUE
return 1
fi
if (( ${#VIRTIO_STORAGE_IDS[@]} == 1 )); then
index=0
else
for index in "${!VIRTIO_STORAGE_IDS[@]}"; do
labels+=("${VIRTIO_STORAGE_IDS[$index]} (${VIRTIO_STORAGE_TYPES[$index]}) — ${VIRTIO_STORAGE_DIRS[$index]}")
values+=("storage:${index}")
done
SELECT_MENU "Select ISO Storage" labels values
case "$MENU_SELECTION" in
storage:*) index="${MENU_SELECTION#storage:}";;
quit) EXIT1; exit 0;;
*) return 1;;
esac
fi
VIRTIO_SELECTED_STORAGE="${VIRTIO_STORAGE_IDS[$index]}"
DLDIR="${VIRTIO_STORAGE_DIRS[$index]}"
echo -e "\n${idsCL[LightCyan]}ISO storage: ${VIRTIO_SELECTED_STORAGE} (${DLDIR})${idsCL[Default]}"
}
TAPM_VIRTIO_FILE_EXISTS() {
local filename="$1"
local directory
for directory in "${VIRTIO_STORAGE_DIRS[@]}"; do
[[ -f "${directory}/${filename}" ]] && return 0
done
return 1
}
TAPM_ANY_LOCAL_VIRTIO_ISOS() {
local directory
for directory in "${VIRTIO_STORAGE_DIRS[@]}"; do
compgen -G "${directory}/virtio-win*.iso" >/dev/null && return 0
done
return 1
}
TAPM_REFRESH_VIRTIO_LOCAL_STATUS() { TAPM_REFRESH_VIRTIO_LOCAL_STATUS() {
[[ -n "$VIRTIO_STABLE_FILE" ]] || return [[ -n "$VIRTIO_STABLE_FILE" ]] || return
if [[ -f "${DLDIR}/${VIRTIO_STABLE_FILE}" ]]; then if TAPM_VIRTIO_FILE_EXISTS "$VIRTIO_STABLE_FILE"; then
VIRTIO_STABLE_STATUS='current' VIRTIO_STABLE_STATUS='current'
elif compgen -G "${DLDIR}/virtio-win*.iso" >/dev/null; then elif TAPM_ANY_LOCAL_VIRTIO_ISOS; then
VIRTIO_STABLE_STATUS='update' VIRTIO_STABLE_STATUS='update'
else else
VIRTIO_STABLE_STATUS='available' VIRTIO_STABLE_STATUS='available'
@@ -807,6 +905,7 @@ TAPM_REFRESH_VIRTIO_LOCAL_STATUS() {
TAPM_CHECK_VIRTIO_STABLE() { TAPM_CHECK_VIRTIO_STABLE() {
local force="${1:-0}" local force="${1:-0}"
local effective_url local effective_url
local source_filename
if (( VIRTIO_STABLE_CHECKED == 1 && force == 0 )); then if (( VIRTIO_STABLE_CHECKED == 1 && force == 0 )); then
return return
@@ -823,7 +922,9 @@ TAPM_CHECK_VIRTIO_STABLE() {
--output /dev/null --write-out '%{url_effective}' \ --output /dev/null --write-out '%{url_effective}' \
"$VIRTIO_STABLE_URL" 2>/dev/null "$VIRTIO_STABLE_URL" 2>/dev/null
)" || return 1 )" || return 1
VIRTIO_STABLE_FILE="$(TAPM_VIRTIO_FILENAME_FROM_URL "$effective_url")" || source_filename="$(TAPM_VIRTIO_FILENAME_FROM_URL "$effective_url")" ||
return 1
VIRTIO_STABLE_FILE="$(TAPM_VIRTIO_LABELED_FILENAME "$source_filename" latest)" ||
return 1 return 1
TAPM_REFRESH_VIRTIO_LOCAL_STATUS TAPM_REFRESH_VIRTIO_LOCAL_STATUS
} }
@@ -844,7 +945,8 @@ TAPM_VALID_VIRTIO_ISO() {
TAPM_DOWNLOAD_VIRTIO_ISO() { TAPM_DOWNLOAD_VIRTIO_ISO() {
local url="$1" local url="$1"
local expected_filename="${2:-}" local expected_filename="${2:-}"
local description="${3:-VirtIO driver ISO}" local filename_label="$3"
local description="${4:-VirtIO driver ISO}"
local effective_url local effective_url
local filename local filename
local final_file local final_file
@@ -896,6 +998,11 @@ TAPM_DOWNLOAD_VIRTIO_ISO() {
echo -e "${idsCL[LightRed]}The VirtIO archive returned ${filename}; expected ${expected_filename}.${idsCL[Default]}" echo -e "${idsCL[LightRed]}The VirtIO archive returned ${filename}; expected ${expected_filename}.${idsCL[Default]}"
return 1 return 1
fi fi
filename="$(TAPM_VIRTIO_LABELED_FILENAME "$filename" "$filename_label")" || {
rm -f -- "$partial_file"
echo -e "${idsCL[LightRed]}Could not create a safe local filename for the VirtIO ISO.${idsCL[Default]}"
return 1
}
if ! TAPM_VALID_VIRTIO_ISO "$partial_file"; then if ! TAPM_VALID_VIRTIO_ISO "$partial_file"; then
rm -f -- "$partial_file" rm -f -- "$partial_file"
@@ -916,11 +1023,13 @@ TAPM_DOWNLOAD_VIRTIO_ISO() {
echo -e "\n${idsCL[Green]}VirtIO ISO downloaded and validated.${idsCL[Default]}" echo -e "\n${idsCL[Green]}VirtIO ISO downloaded and validated.${idsCL[Default]}"
printf ' File: %s\n' "$filename" printf ' File: %s\n' "$filename"
printf ' Size: %s\n' "$(numfmt --to=iec-i --suffix=B "$size")" printf ' Size: %s\n' "$(numfmt --to=iec-i --suffix=B "$size")"
printf ' Storage: %s\n' "$VIRTIO_SELECTED_STORAGE"
printf ' Destination: %s\n' "$DLDIR" printf ' Destination: %s\n' "$DLDIR"
} }
DOWNLOAD_VIRTIO_STABLE() { DOWNLOAD_VIRTIO_STABLE() {
if ! TAPM_DOWNLOAD_VIRTIO_ISO "$VIRTIO_STABLE_URL" '' \ TAPM_SELECT_ISO_STORAGE || return
if ! TAPM_DOWNLOAD_VIRTIO_ISO "$VIRTIO_STABLE_URL" '' latest \
'current stable VirtIO drivers'; then 'current stable VirtIO drivers'; then
FINISH_FAILED_ACTION FINISH_FAILED_ACTION
return return
@@ -934,11 +1043,13 @@ DOWNLOAD_VIRTIO_STABLE() {
DOWNLOAD_VIRTIO_ARCHIVE() { DOWNLOAD_VIRTIO_ARCHIVE() {
local release="$1" local release="$1"
local description="$2" local description="$2"
local filename_label="${3:-archive}"
local iso_version="${release%-*}" local iso_version="${release%-*}"
local filename="virtio-win-${iso_version}.iso" local filename="virtio-win-${iso_version}.iso"
local url="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-${release}/${filename}" local url="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-${release}/${filename}"
if ! TAPM_DOWNLOAD_VIRTIO_ISO "$url" "$filename" "$description"; then TAPM_SELECT_ISO_STORAGE || return
if ! TAPM_DOWNLOAD_VIRTIO_ISO "$url" "$filename" "$filename_label" "$description"; then
FINISH_FAILED_ACTION FINISH_FAILED_ACTION
return return
fi fi
@@ -961,6 +1072,7 @@ DOWNLOAD_CUSTOM_VIRTIO_ARCHIVE() {
} }
SHOW_LOCAL_VIRTIO_ISOS() { SHOW_LOCAL_VIRTIO_ISOS() {
local index
local file local file
local found=0 local found=0
@@ -968,18 +1080,25 @@ SHOW_LOCAL_VIRTIO_ISOS() {
echo echo
echo -e " ${idsCL[LightCyan]}Downloaded VirtIO ISOs${idsCL[Default]}" echo -e " ${idsCL[LightCyan]}Downloaded VirtIO ISOs${idsCL[Default]}"
echo echo
echo " Destination: ${DLDIR}" if TAPM_REFRESH_ISO_STORAGES; then
echo for index in "${!VIRTIO_STORAGE_IDS[@]}"; do
while IFS= read -r -d '' file; do echo -e " ${idsCL[LightCyan]}${VIRTIO_STORAGE_IDS[$index]}${idsCL[Default]} (${VIRTIO_STORAGE_TYPES[$index]})"
found=1 echo " ${VIRTIO_STORAGE_DIRS[$index]}"
printf ' %-34s %10s %s\n' \ while IFS= read -r -d '' file; do
"${file##*/}" \ found=1
"$(numfmt --to=iec-i --suffix=B "$(stat -c '%s' "$file")")" \ printf ' %-42s %10s %s\n' \
"$(date --date="@$(stat -c '%Y' "$file")" '+%F %R')" "${file##*/}" \
done < <( "$(numfmt --to=iec-i --suffix=B "$(stat -c '%s' "$file")")" \
find "$DLDIR" -maxdepth 1 -type f -name 'virtio-win*.iso' \ "$(date --date="@$(stat -c '%Y' "$file")" '+%F %R')"
-print0 2>/dev/null | sort -z done < <(
) find "${VIRTIO_STORAGE_DIRS[$index]}" -maxdepth 1 -type f \
-name 'virtio-win*.iso' -print0 2>/dev/null | sort -z
)
echo
done
else
echo -e " ${idsCL[LightRed]}No active ISO-capable storage was found.${idsCL[Default]}"
fi
(( found == 1 )) || echo " No VirtIO ISOs are currently downloaded." (( found == 1 )) || echo " No VirtIO ISOs are currently downloaded."
echo echo
read -r -p " Press ENTER to return..." _ read -r -p " Press ENTER to return..." _
@@ -1000,10 +1119,12 @@ VIRTIO_MENU() {
) )
echo -en "\n${idsCL[LightCyan]}Checking the current stable VirtIO release...${idsCL[Default]} " echo -en "\n${idsCL[LightCyan]}Checking the current stable VirtIO release...${idsCL[Default]} "
TAPM_REFRESH_ISO_STORAGES || true
TAPM_CHECK_VIRTIO_STABLE 1 || true TAPM_CHECK_VIRTIO_STABLE 1 || true
echo echo
while true; do while true; do
TAPM_REFRESH_ISO_STORAGES || true
TAPM_REFRESH_VIRTIO_LOCAL_STATUS TAPM_REFRESH_VIRTIO_LOCAL_STATUS
case "$VIRTIO_STABLE_STATUS" in case "$VIRTIO_STABLE_STATUS" in
current) current)
@@ -1030,13 +1151,13 @@ VIRTIO_MENU() {
"View downloaded VirtIO ISOs" "View downloaded VirtIO ISOs"
"Refresh stable-version check" "Refresh stable-version check"
) )
[[ -f "${DLDIR}/virtio-win-0.1.240.iso" ]] && TAPM_VIRTIO_FILE_EXISTS 'virtio-win-server-2016-0.1.240.iso' &&
labels[1]+=" (downloaded)" labels[1]+=" (downloaded)"
[[ -f "${DLDIR}/virtio-win-0.1.189.iso" ]] && TAPM_VIRTIO_FILE_EXISTS 'virtio-win-server-2012r2-0.1.189.iso' &&
labels[2]+=" (downloaded)" labels[2]+=" (downloaded)"
[[ -f "${DLDIR}/virtio-win-0.1.172.iso" ]] && TAPM_VIRTIO_FILE_EXISTS 'virtio-win-server-2008r2-0.1.172.iso' &&
labels[3]+=" (downloaded)" labels[3]+=" (downloaded)"
[[ -f "${DLDIR}/virtio-win-0.1.141.iso" ]] && TAPM_VIRTIO_FILE_EXISTS 'virtio-win-server-2008-0.1.141.iso' &&
labels[4]+=" (downloaded)" labels[4]+=" (downloaded)"
SELECT_MENU "VirtIO Driver Downloads" labels values SELECT_MENU "VirtIO Driver Downloads" labels values
@@ -1044,19 +1165,19 @@ VIRTIO_MENU() {
stable) DOWNLOAD_VIRTIO_STABLE;; stable) DOWNLOAD_VIRTIO_STABLE;;
server2016) server2016)
DOWNLOAD_VIRTIO_ARCHIVE 0.1.240-1 \ DOWNLOAD_VIRTIO_ARCHIVE 0.1.240-1 \
"Windows Server 2016 compatibility drivers" "Windows Server 2016 compatibility drivers" server-2016
;; ;;
server2012) server2012)
DOWNLOAD_VIRTIO_ARCHIVE 0.1.189-1 \ DOWNLOAD_VIRTIO_ARCHIVE 0.1.189-1 \
"Windows Server 2012/R2 compatibility drivers" "Windows Server 2012/R2 compatibility drivers" server-2012r2
;; ;;
server2008r2) server2008r2)
DOWNLOAD_VIRTIO_ARCHIVE 0.1.172-1 \ DOWNLOAD_VIRTIO_ARCHIVE 0.1.172-1 \
"Windows Server 2008 R2 compatibility drivers" "Windows Server 2008 R2 compatibility drivers" server-2008r2
;; ;;
server2008) server2008)
DOWNLOAD_VIRTIO_ARCHIVE 0.1.141-1 \ DOWNLOAD_VIRTIO_ARCHIVE 0.1.141-1 \
"Windows Server 2008 compatibility drivers" "Windows Server 2008 compatibility drivers" server-2008
;; ;;
archive) DOWNLOAD_CUSTOM_VIRTIO_ARCHIVE;; archive) DOWNLOAD_CUSTOM_VIRTIO_ARCHIVE;;
local) SHOW_LOCAL_VIRTIO_ISOS;; local) SHOW_LOCAL_VIRTIO_ISOS;;
@@ -1736,7 +1857,8 @@ HOST_SETUP_MENU() {
labels+=("Detect CPU model for live migrations") labels+=("Detect CPU model for live migrations")
values+=("cpu") values+=("cpu")
if compgen -G "${DLDIR}/virtio-win*.iso" >/dev/null; then TAPM_REFRESH_ISO_STORAGES >/dev/null 2>&1 || true
if TAPM_ANY_LOCAL_VIRTIO_ISOS; then
labels+=("VirtIO driver downloads (local ISOs available)") labels+=("VirtIO driver downloads (local ISOs available)")
else else
labels+=("VirtIO driver downloads") labels+=("VirtIO driver downloads")
@@ -1810,7 +1932,7 @@ MONITORING_MENU() {
CLUSTER_MENU() { CLUSTER_MENU() {
local -a labels local -a labels
local -a values=("maintenance" "services" "keepalived") local -a values=("maintenance" "services" "keepalived" "iso_nfs")
while true; do while true; do
if ha-manager status | grep -F "$(hostname -s)" | grep -q "maintenance mode"; then if ha-manager status | grep -F "$(hostname -s)" | grep -q "maintenance mode"; then
@@ -1823,12 +1945,17 @@ CLUSTER_MENU() {
dpkg-query -W -f='${Status}' keepalived 2>/dev/null | grep -q "install ok installed" && dpkg-query -W -f='${Status}' keepalived 2>/dev/null | grep -q "install ok installed" &&
labels+=("Deploy/reconfigure Keepalived (installed locally)") || labels+=("Deploy/reconfigure Keepalived (installed locally)") ||
labels+=("Deploy Keepalived on all cluster hosts") labels+=("Deploy Keepalived on all cluster hosts")
labels+=("Create shared ISO storage using an LXC NFS server")
SELECT_MENU "Cluster & Maintenance" labels values SELECT_MENU "Cluster & Maintenance" labels values
case "$MENU_SELECTION" in case "$MENU_SELECTION" in
maintenance) MAINTENANCE_MODE;; maintenance) MAINTENANCE_MODE;;
services) SERVICE_RECOVERY_MENU;; services) SERVICE_RECOVERY_MENU;;
keepalived) INSTALL_KEEPALIVE;; keepalived) INSTALL_KEEPALIVE;;
iso_nfs)
TAPM_DEPLOY_ISO_NFS_LXC
FINISH_ACTION
;;
back) return;; back) return;;
quit) EXIT1; exit 0;; quit) EXIT1; exit 0;;
esac esac
@@ -2110,6 +2237,7 @@ if (( ACTION_REQUESTED == 1 )); then
cpu) DETECT_CPU;; cpu) DETECT_CPU;;
maintenance|mm) MAINTENANCE_MODE;; maintenance|mm) MAINTENANCE_MODE;;
keepalived) INSTALL_KEEPALIVE;; keepalived) INSTALL_KEEPALIVE;;
iso-nfs|iso_nfs) TAPM_DEPLOY_ISO_NFS_LXC;;
*) MAIN_MENU;; *) MAIN_MENU;;
esac esac
else else