Prototype1
This commit is contained in:
+95
-92
@@ -2,17 +2,27 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
# ---------------------------
|
||||
# Logging / helpers
|
||||
# Logging / Errors / Traps
|
||||
# ---------------------------
|
||||
_ts() { date '+%F %T'; }
|
||||
|
||||
_is_tty() { [[ -t 1 ]]; }
|
||||
|
||||
# log to stderr (wichtig: damit Funktions-Rückgaben via stdout sauber bleiben)
|
||||
log() { echo "[$(_ts)] $*" >&2; }
|
||||
info() { log "INFO: $*"; }
|
||||
warn() { log "WARN: $*"; }
|
||||
die() { log "ERROR: $*"; exit 1; }
|
||||
|
||||
on_error() {
|
||||
local lineno="$1"
|
||||
local cmd="$2"
|
||||
local code="$3"
|
||||
die "Failed at line ${lineno}: ${cmd} (exit=${code})"
|
||||
}
|
||||
|
||||
setup_traps() {
|
||||
trap 'on_error "$LINENO" "$BASH_COMMAND" "$?"' ERR
|
||||
}
|
||||
|
||||
need_cmd() {
|
||||
local c
|
||||
for c in "$@"; do
|
||||
@@ -20,13 +30,30 @@ need_cmd() {
|
||||
done
|
||||
}
|
||||
|
||||
on_error() {
|
||||
local lineno="$1" cmd="$2" code="$3"
|
||||
die "Failed at line ${lineno}: ${cmd} (exit=${code})"
|
||||
# ---------------------------
|
||||
# Safe secret generators (NO tr|head pipelines)
|
||||
# ---------------------------
|
||||
# Hex secret, length = bytes*2 chars
|
||||
gen_hex() {
|
||||
local bytes="${1:-32}"
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -hex "$bytes"
|
||||
return 0
|
||||
fi
|
||||
# fallback python
|
||||
python3 - <<PY
|
||||
import secrets
|
||||
print(secrets.token_hex($bytes))
|
||||
PY
|
||||
}
|
||||
|
||||
setup_traps() {
|
||||
trap 'on_error "${LINENO}" "${BASH_COMMAND}" "$?"' ERR
|
||||
# URL-safe token with approx length
|
||||
gen_urlsafe() {
|
||||
local nbytes="${1:-32}"
|
||||
python3 - <<PY
|
||||
import secrets
|
||||
print(secrets.token_urlsafe($nbytes))
|
||||
PY
|
||||
}
|
||||
|
||||
# ---------------------------
|
||||
@@ -42,124 +69,100 @@ pve_bridge_exists() {
|
||||
[[ -d "/sys/class/net/${br}/bridge" ]]
|
||||
}
|
||||
|
||||
# Ensure we return ONLY the template reference on stdout.
|
||||
# All logs go to stderr to avoid contaminating command substitution.
|
||||
pve_template_ensure_debian12() {
|
||||
local preferred_store="$1"
|
||||
local tpl="debian-12-standard_12.12-1_amd64.tar.zst"
|
||||
local store="$preferred_store"
|
||||
# Gibt NUR den template-string auf stdout zurück
|
||||
# Logs gehen auf stderr (info/warn), damit TEMPLATE nicht "vermüllt".
|
||||
local desired="debian-12-standard_12.12-1_amd64.tar.zst"
|
||||
local store="$1"
|
||||
|
||||
need_cmd pveam awk grep
|
||||
|
||||
# Some storages (e.g. local-zfs) are not template storages for pveam.
|
||||
# prüfen, ob storage für templates geeignet ist
|
||||
if ! pveam list "$store" >/dev/null 2>&1; then
|
||||
warn "pveam storage '${store}' not available for templates; falling back to 'local'"
|
||||
store="local"
|
||||
fi
|
||||
|
||||
# Update list and download if missing
|
||||
pveam update >/dev/null 2>&1 || true
|
||||
|
||||
if ! pveam list "$store" | awk '{print $2}' | grep -qx "$tpl"; then
|
||||
info "Downloading CT template to ${store}: ${tpl}"
|
||||
pveam download "$store" "$tpl" >/dev/null
|
||||
# vorhanden?
|
||||
if ! pveam list "$store" | awk '{print $2}' | grep -qx "$desired"; then
|
||||
# check available list
|
||||
if pveam available | awk '{print $2}' | grep -qx "$desired"; then
|
||||
info "Downloading CT template to ${store}: ${desired}"
|
||||
pveam download "$store" "$desired" >/dev/null
|
||||
else
|
||||
die "Template not available via pveam: ${desired}"
|
||||
fi
|
||||
else
|
||||
# optional: trotzdem loggen
|
||||
:
|
||||
fi
|
||||
|
||||
echo "${store}:vztmpl/${tpl}"
|
||||
echo "${store}:vztmpl/${desired}"
|
||||
}
|
||||
|
||||
pve_build_net0() {
|
||||
local bridge="$1" ip="$2" vlan="${3:-}"
|
||||
if [[ "$ip" == "dhcp" ]]; then
|
||||
if [[ -n "$vlan" ]]; then
|
||||
echo "name=eth0,bridge=${bridge},tag=${vlan},ip=dhcp"
|
||||
else
|
||||
echo "name=eth0,bridge=${bridge},ip=dhcp"
|
||||
fi
|
||||
else
|
||||
if [[ -n "$vlan" ]]; then
|
||||
echo "name=eth0,bridge=${bridge},tag=${vlan},ip=${ip}"
|
||||
else
|
||||
echo "name=eth0,bridge=${bridge},ip=${ip}"
|
||||
fi
|
||||
local bridge="$1"
|
||||
local ipcfg="$2"
|
||||
local vlan="${3:-}"
|
||||
|
||||
local base="name=eth0,bridge=${bridge},ip=${ipcfg}"
|
||||
if [[ -n "${vlan}" && "${vlan}" != "0" ]]; then
|
||||
base="${base},tag=${vlan}"
|
||||
fi
|
||||
echo "$base"
|
||||
}
|
||||
|
||||
# Execute inside CT with sane locale env to avoid perl warnings mid-run.
|
||||
pct_exec() {
|
||||
local ctid="$1"; shift
|
||||
local cmd="$*"
|
||||
need_cmd pct
|
||||
pct exec "$ctid" -- bash -lc "export LANG=C.UTF-8 LC_ALL=C.UTF-8; ${cmd}"
|
||||
# CTID nach "Unixzeit - 1e9" (Kundensicher bis 2038) + lokale Kollisionsprüfung
|
||||
pve_select_customer_ctid() {
|
||||
need_cmd date pct awk sort tail
|
||||
|
||||
local base
|
||||
base="$(($(date +%s) - 1000000000))"
|
||||
|
||||
# Falls lokal belegt, hochzählen bis frei
|
||||
local ctid="$base"
|
||||
while pct status "$ctid" >/dev/null 2>&1; do
|
||||
ctid="$((ctid + 1))"
|
||||
done
|
||||
echo "$ctid"
|
||||
}
|
||||
|
||||
pct_wait_for_ip() {
|
||||
local ctid="$1"
|
||||
local i ip
|
||||
local tries="${2:-60}"
|
||||
local i=0
|
||||
|
||||
for i in {1..60}; do
|
||||
# Try eth0 first (standard CT)
|
||||
ip="$(pct exec "$ctid" -- bash -lc "ip -4 -o addr show dev eth0 2>/dev/null | awk '{print \$4}' | cut -d/ -f1 | head -n1" 2>/dev/null || true)"
|
||||
need_cmd pct awk grep
|
||||
|
||||
while (( i < tries )); do
|
||||
# pct exec ip -4 addr show dev eth0 → robust bei DHCP
|
||||
local ip=""
|
||||
ip="$(pct exec "$ctid" -- bash -lc "ip -4 -o addr show dev eth0 | awk '{print \$4}' | cut -d/ -f1 | head -n1" 2>/dev/null || true)"
|
||||
if [[ -n "$ip" ]]; then
|
||||
echo "$ip"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((i++))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------
|
||||
# Cluster VMID discovery (robust)
|
||||
# ---------------------------
|
||||
# Outputs one VMID per line (containers + VMs), cluster-wide if possible.
|
||||
pve_cluster_vmids() {
|
||||
need_cmd pvesh awk sed grep
|
||||
|
||||
# Try cluster resources; output-format json gives stable machine output.
|
||||
# If pvesh fails (no cluster or temporary glitch), fall back to local IDs.
|
||||
if pvesh get /cluster/resources --output-format json >/dev/null 2>&1; then
|
||||
# No python/jq parsing. Extract "vmid": <num> safely via awk/grep.
|
||||
pvesh get /cluster/resources --output-format json \
|
||||
| tr -d '\n' \
|
||||
| sed 's/},{/}\n{/g' \
|
||||
| grep -oE '"vmid":[0-9]+' \
|
||||
| grep -oE '[0-9]+' \
|
||||
| sort -n -u
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fallback: local node only
|
||||
{
|
||||
pct list 2>/dev/null | awk 'NR>1 {print $1}'
|
||||
qm list 2>/dev/null | awk 'NR>1 {print $1}'
|
||||
} | sort -n -u
|
||||
pct_exec() {
|
||||
local ctid="$1"; shift
|
||||
pct exec "$ctid" -- bash -lc "$*"
|
||||
}
|
||||
|
||||
pve_vmid_exists_cluster() {
|
||||
local id="$1"
|
||||
pve_cluster_vmids | awk -v x="$id" '$0==x {found=1} END{exit(found?0:1)}'
|
||||
# Locale fix (Debian 12)
|
||||
pct_fix_locales() {
|
||||
local ctid="$1"
|
||||
local locale="${2:-de_DE.UTF-8}"
|
||||
pct_exec "$ctid" "export DEBIAN_FRONTEND=noninteractive; apt-get update -y"
|
||||
pct_exec "$ctid" "export DEBIAN_FRONTEND=noninteractive; apt-get install -y locales"
|
||||
pct_exec "$ctid" "sed -i 's/^# *${locale} UTF-8/${locale} UTF-8/' /etc/locale.gen || true"
|
||||
pct_exec "$ctid" "locale-gen ${locale} >/dev/null"
|
||||
pct_exec "$ctid" "update-locale LANG=${locale} LC_ALL=${locale}"
|
||||
}
|
||||
|
||||
# Customer CTID: unix_time - 1_000_000_000 (good until 2038),
|
||||
# then increment until free cluster-wide.
|
||||
pve_select_customer_ctid() {
|
||||
local base now ctid tries
|
||||
now="$(date +%s)"
|
||||
base=$(( now - 1000000000 ))
|
||||
# keep it within a sane positive range
|
||||
if (( base < 100 )); then
|
||||
base=100
|
||||
fi
|
||||
|
||||
ctid="$base"
|
||||
for tries in {1..2000}; do
|
||||
if ! pve_vmid_exists_cluster "$ctid"; then
|
||||
echo "$ctid"
|
||||
return 0
|
||||
fi
|
||||
ctid=$((ctid + 1))
|
||||
done
|
||||
|
||||
die "CTID selection failed: tried ${base}..$((base+1999)) (all occupied?)"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user