Fix detection: PVE 9 deb822 repos, ZFS partition matching, WireGuard state
Tested against live aether-lab host. Fixes: 1. Repo detection: PVE 9 uses .sources (deb822) format, not .list files. Now checks both formats, reads Enabled: field for deb822 enterprise repos, and greps for pve-no-subscription across all formats. 2. Available disk detection: zpool status shows partition names (nvme0n1p3), not whole-disk names. Now strips partition suffix to get parent disk, correctly excluding system pool disks. Also detects existing partition tables (linux_raid_member, etc.) on available disks and warns about them. 3. WireGuard detection: empty output from "wg show" piped through "head -1" produced empty string (not "none"), which was parsed as active. Now explicitly checks for empty output. 4. Storage apply: detects and warns about disks with existing partitions, offers to wipe before ZFS pool creation. Default pool name changed to "vm-storage" (since "local-zfs" is the system pool). 5. Repo apply: handles both .list and .sources formats for disabling enterprise repos. Generates correct deb822 format for PVE 9 when adding no-subscription repo. 6. Fixed invalid syntax: "for f in ... 2>/dev/null; do" is not valid bash -- removed the redirect, file existence is checked with -f. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a44458ae0c
commit
a7eb09e248
|
|
@ -392,11 +392,43 @@ zpool list -H 2>/dev/null || true
|
|||
echo '---PVE_STORAGE---'
|
||||
pvesm status 2>/dev/null || true
|
||||
echo '---ENTERPRISE_REPOS---'
|
||||
grep -rl '^deb.*enterprise' /etc/apt/sources.list.d/*.list 2>/dev/null || echo 'none'
|
||||
# Check both .list (PVE 8) and .sources/deb822 (PVE 9) formats
|
||||
enterprise_active='no'
|
||||
# Old .list format: uncommented deb lines with enterprise
|
||||
if grep -rl '^deb.*enterprise' /etc/apt/sources.list.d/*.list 2>/dev/null | head -1 | grep -q .; then
|
||||
enterprise_active='yes'
|
||||
fi
|
||||
# New .sources/deb822 format: check for enterprise files that are NOT disabled
|
||||
for f in /etc/apt/sources.list.d/*enterprise*.sources /etc/apt/sources.list.d/*enterprise*.list; do
|
||||
[ -f "$f" ] || continue
|
||||
# deb822: Enabled: false means disabled
|
||||
if grep -qi '^Enabled:.*false' "$f" 2>/dev/null; then
|
||||
continue
|
||||
fi
|
||||
# deb822: if it has enterprise in URIs and no Enabled: false, it's active
|
||||
if grep -qi 'enterprise' "$f" 2>/dev/null; then
|
||||
enterprise_active='yes'
|
||||
fi
|
||||
done
|
||||
echo "$enterprise_active"
|
||||
echo '---NOSUB_REPO---'
|
||||
test -f /etc/apt/sources.list.d/pve-no-subscription.list && echo 'yes' || echo 'no'
|
||||
# Check both .list and .sources formats for no-subscription repo
|
||||
nosub='no'
|
||||
if grep -rq 'pve-no-subscription' /etc/apt/sources.list.d/ 2>/dev/null; then
|
||||
nosub='yes'
|
||||
fi
|
||||
echo "$nosub"
|
||||
echo '---NAG_PATCHED---'
|
||||
grep -q 'void({' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js 2>/dev/null && echo 'yes' || echo 'no'
|
||||
# Check if the subscription nag popup has been patched
|
||||
# PVE 8: void({ replaces Ext.Msg.show
|
||||
# PVE 9: same pattern or similar
|
||||
if grep -q 'void({' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js 2>/dev/null; then
|
||||
echo 'yes'
|
||||
elif ! grep -q "No valid subscription" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js 2>/dev/null; then
|
||||
echo 'yes' # nag text removed entirely
|
||||
else
|
||||
echo 'no'
|
||||
fi
|
||||
echo '---UPGRADABLE---'
|
||||
apt list --upgradable 2>/dev/null | grep -c upgradable || true
|
||||
echo '---VMBR1---'
|
||||
|
|
@ -408,11 +440,14 @@ iptables -t nat -L POSTROUTING -n 2>/dev/null | grep -q MASQUERADE && echo 'yes'
|
|||
echo '---WG_INSTALLED---'
|
||||
command -v wg >/dev/null 2>&1 && echo 'yes' || echo 'no'
|
||||
echo '---WG_ACTIVE---'
|
||||
wg show wg0 2>/dev/null | head -1 || echo 'none'
|
||||
wg_out=$(wg show wg0 2>/dev/null | head -1) || true
|
||||
if [ -n "$wg_out" ]; then echo "$wg_out"; else echo 'none'; fi
|
||||
echo '---WG_PEERS---'
|
||||
wg show wg0 peers 2>/dev/null | wc -l || echo '0'
|
||||
wg_peers=$(wg show wg0 peers 2>/dev/null | wc -l) || true
|
||||
echo "${wg_peers:-0}"
|
||||
echo '---WG_HANDSHAKE---'
|
||||
wg show wg0 latest-handshakes 2>/dev/null | head -1 || echo 'none'
|
||||
wg_hs=$(wg show wg0 latest-handshakes 2>/dev/null | head -1) || true
|
||||
if [ -n "$wg_hs" ]; then echo "$wg_hs"; else echo 'none'; fi
|
||||
echo '---NFT_FILE---'
|
||||
test -f /etc/nftables-hetzner.conf && echo 'yes' || echo 'no'
|
||||
echo '---NFT_RULES---'
|
||||
|
|
@ -430,12 +465,28 @@ pveum user token list automation@pve --output-format json 2>/dev/null \
|
|||
| python3 -c 'import sys,json; print("yes" if any(t["tokenid"]=="deploy" for t in json.load(sys.stdin)) else "no")' 2>/dev/null \
|
||||
|| echo 'no'
|
||||
echo '---AVAILABLE_DISKS---'
|
||||
used_disks=$(zpool status 2>/dev/null | grep -oP '/dev/\S+' | sed 's|/dev/||' || true)
|
||||
# Build list of whole-disk names that are part of ZFS pools.
|
||||
# zpool status shows partition names (e.g. nvme0n1p3), so strip the
|
||||
# partition suffix to get the parent disk name.
|
||||
zfs_disks=$(zpool status 2>/dev/null \
|
||||
| grep -oP '(nvme\S+|sd[a-z]+\S*|vd[a-z]+\S*)' \
|
||||
| sed -E 's/p?[0-9]+$//' \
|
||||
| sort -u || true)
|
||||
lsblk -dno NAME,SIZE,TYPE | while read name size type; do
|
||||
[ "$type" != 'disk' ] && continue
|
||||
echo "$used_disks" | grep -qw "$name" && continue
|
||||
lsblk -no MOUNTPOINT /dev/$name 2>/dev/null | grep -q '/' && continue
|
||||
echo "$name $size"
|
||||
# Skip disks used by ZFS pools
|
||||
echo "$zfs_disks" | grep -qw "$name" && continue
|
||||
# Skip disks with active mount points
|
||||
lsblk -no MOUNTPOINT "/dev/$name" 2>/dev/null | grep -q '/' && continue
|
||||
# Check for existing partition table / signatures
|
||||
partcount=$(lsblk -no NAME "/dev/$name" 2>/dev/null | wc -l)
|
||||
if [ "$partcount" -gt 1 ]; then
|
||||
# Has partitions -- detect type
|
||||
fstype=$(lsblk -no FSTYPE "/dev/$name" 2>/dev/null | grep -v '^$' | head -1)
|
||||
echo "$name $size partitioned:${fstype:-unknown}"
|
||||
else
|
||||
echo "$name $size clean"
|
||||
fi
|
||||
done
|
||||
echo '---END---'
|
||||
DETECT
|
||||
|
|
@ -482,10 +533,10 @@ _parse_section() {
|
|||
ZPOOLS) DETECT_ZPOOLS="$content" ;;
|
||||
PVE_STORAGE) DETECT_PVE_STORAGE="$content" ;;
|
||||
ENTERPRISE_REPOS)
|
||||
if [[ "$content" == "none" ]]; then
|
||||
DETECT_ENTERPRISE_ACTIVE=""
|
||||
if [[ "$content" == "yes" ]]; then
|
||||
DETECT_ENTERPRISE_ACTIVE="yes"
|
||||
else
|
||||
DETECT_ENTERPRISE_ACTIVE="$content"
|
||||
DETECT_ENTERPRISE_ACTIVE=""
|
||||
fi
|
||||
;;
|
||||
NOSUB_REPO) DETECT_NOSUB_EXISTS="$content" ;;
|
||||
|
|
@ -500,13 +551,13 @@ _parse_section() {
|
|||
NAT_ACTIVE) DETECT_NAT_ACTIVE="$content" ;;
|
||||
WG_INSTALLED) DETECT_WG_INSTALLED="$content" ;;
|
||||
WG_ACTIVE)
|
||||
if [[ "$content" != "none" ]]; then
|
||||
if [[ "$content" != "none" ]] && [[ -n "$content" ]]; then
|
||||
DETECT_WG_ACTIVE="yes"
|
||||
fi
|
||||
;;
|
||||
WG_PEERS) DETECT_WG_PEERS="${content//[^0-9]/}" ;;
|
||||
WG_HANDSHAKE)
|
||||
if [[ "$content" != "none" ]]; then
|
||||
if [[ "$content" != "none" ]] && [[ -n "$content" ]]; then
|
||||
DETECT_WG_HANDSHAKE="$content"
|
||||
fi
|
||||
;;
|
||||
|
|
@ -685,8 +736,8 @@ show_status() {
|
|||
|
||||
printf " %-20s %b" "Storage" "$(status_icon "$effective_storage")"
|
||||
case "$effective_storage" in
|
||||
done) echo " ZFS pool registered" ;;
|
||||
available) echo " ZFS pool exists, ${DETECT_AVAILABLE_DISK_COUNT} unused disk(s) available" ;;
|
||||
done) echo " system pool registered, no extra disks" ;;
|
||||
available) echo " system pool registered, ${DETECT_AVAILABLE_DISK_COUNT} extra disk(s) available" ;;
|
||||
needed) echo " ${DETECT_AVAILABLE_DISK_COUNT} unused disk(s), no pool created" ;;
|
||||
skipped) echo "" ;;
|
||||
esac
|
||||
|
|
@ -787,13 +838,30 @@ apply_repos() {
|
|||
fi
|
||||
|
||||
if [[ -n "$DETECT_ENTERPRISE_ACTIVE" ]]; then
|
||||
remote "sed -i 's/^deb/# deb/' /etc/apt/sources.list.d/pve-enterprise.list 2>/dev/null || true"
|
||||
remote "test -f /etc/apt/sources.list.d/ceph.list && sed -i 's/^deb/# deb/' /etc/apt/sources.list.d/ceph.list || true"
|
||||
# Handle both .list (PVE 8 / bookworm) and .sources (PVE 9 / trixie) formats
|
||||
# .list format: comment out deb lines
|
||||
remote "for f in /etc/apt/sources.list.d/*enterprise*.list; do [ -f \"\$f\" ] && sed -i 's/^deb/# deb/' \"\$f\"; done 2>/dev/null || true"
|
||||
# .sources/deb822 format: set Enabled: false
|
||||
remote "for f in /etc/apt/sources.list.d/*enterprise*.sources; do [ -f \"\$f\" ] && sed -i '/^Enabled:/d' \"\$f\" && echo 'Enabled: false' >> \"\$f\"; done 2>/dev/null || true"
|
||||
success "Enterprise repos disabled"
|
||||
fi
|
||||
|
||||
if [[ "$DETECT_NOSUB_EXISTS" != "yes" ]]; then
|
||||
# Detect PVE version to choose format
|
||||
local pve_major="${DETECT_PVE_VERSION%%.*}"
|
||||
if [[ "${pve_major:-8}" -ge 9 ]]; then
|
||||
# PVE 9+ uses deb822 .sources format
|
||||
remote "cat > /etc/apt/sources.list.d/proxmox.sources <<'REPO'
|
||||
Types: deb
|
||||
URIs: http://download.proxmox.com/debian/pve
|
||||
Suites: trixie
|
||||
Components: pve-no-subscription
|
||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
REPO"
|
||||
else
|
||||
# PVE 8 uses traditional .list format
|
||||
remote "echo 'deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription' > /etc/apt/sources.list.d/pve-no-subscription.list"
|
||||
fi
|
||||
success "No-subscription repo added"
|
||||
fi
|
||||
|
||||
|
|
@ -963,10 +1031,31 @@ apply_storage() {
|
|||
fi
|
||||
|
||||
info "Available disks:"
|
||||
local has_partitioned=false
|
||||
echo "$DETECT_AVAILABLE_DISKS" | while IFS= read -r line; do
|
||||
if [[ -n "$line" ]]; then echo " /dev/$line"; fi
|
||||
if [[ -n "$line" ]]; then
|
||||
local dname dsize dstate
|
||||
dname=$(echo "$line" | awk '{print $1}')
|
||||
dsize=$(echo "$line" | awk '{print $2}')
|
||||
dstate=$(echo "$line" | awk '{print $3}')
|
||||
if [[ "$dstate" == partitioned:* ]]; then
|
||||
local fsinfo="${dstate#partitioned:}"
|
||||
echo -e " /dev/${dname} ${dsize} ${YELLOW}has existing partitions (${fsinfo})${RESET}"
|
||||
else
|
||||
echo " /dev/${dname} ${dsize} clean"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for partitioned disks that need wiping
|
||||
local partitioned_disks
|
||||
partitioned_disks=$(echo "$DETECT_AVAILABLE_DISKS" | awk '/partitioned:/{print "/dev/"$1}' | tr '\n' ' ')
|
||||
if [[ -n "${partitioned_disks// /}" ]]; then
|
||||
has_partitioned=true
|
||||
warn "Disk(s) with existing partitions must be wiped before use in a ZFS pool"
|
||||
warn "Affected: ${partitioned_disks}"
|
||||
fi
|
||||
|
||||
local disk_count="$DETECT_AVAILABLE_DISK_COUNT"
|
||||
local disk_names
|
||||
disk_names=$(echo "$DETECT_AVAILABLE_DISKS" | awk 'NF{print "/dev/"$1}' | tr '\n' ' ')
|
||||
|
|
@ -984,7 +1073,11 @@ apply_storage() {
|
|||
fi
|
||||
|
||||
local pool_name
|
||||
pool_name=$(prompt_value "Pool name" "local-zfs")
|
||||
pool_name=$(prompt_value "Pool name" "vm-storage")
|
||||
|
||||
if [[ "$has_partitioned" == true ]]; then
|
||||
warn "This will wipe partition tables on: ${partitioned_disks}"
|
||||
fi
|
||||
|
||||
if ! confirm "Create ZFS pool '${pool_name}' (${pool_type}) with: ${disk_names}?"; then
|
||||
info "Skipped"
|
||||
|
|
@ -995,7 +1088,14 @@ apply_storage() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
local zpool_cmd="zpool create ${pool_name}"
|
||||
# Wipe partition tables on disks that have them
|
||||
for disk in $partitioned_disks; do
|
||||
info "Wiping partition table on ${disk}..."
|
||||
remote "wipefs -a ${disk}"
|
||||
remote "sgdisk -Z ${disk} 2>/dev/null || true"
|
||||
done
|
||||
|
||||
local zpool_cmd="zpool create -f ${pool_name}"
|
||||
case "$pool_type" in
|
||||
mirror) zpool_cmd="${zpool_cmd} mirror ${disk_names}" ;;
|
||||
raidz1) zpool_cmd="${zpool_cmd} raidz1 ${disk_names}" ;;
|
||||
|
|
|
|||
Loading…
Reference in New Issue