This commit is contained in:
bzo committed 2026-09-05 15:38:46 +03:00
commit cd56e73e36
2 files changed
+512

No files matched your search

+254
View File
@@ -0,0 +1,254 @@
#!/usr/bin/env bash
#
# grafana-opensuse-ct.sh — interactive (whiptail) installer that provisions an
# openSUSE LXC container on Proxmox and installs/configures Grafana inside it.
#
# Mimics the community-scripts.github.io/ProxmoxVE UX: a Default/Advanced menu,
# whiptail prompts, then a scripted pct create + pct exec install flow.
#
# Run this ON the Proxmox host as root:
# ./grafana-opensuse-ct.sh
set -euo pipefail
APP="Grafana"
# ---------- helpers ----------
msg_info() { echo -e "\e[1;34m[*]\e[0m $1"; }
msg_ok() { echo -e "\e[1;32m[OK]\e[0m $1"; }
msg_err() { echo -e "\e[1;31m[ERR]\e[0m $1" >&2; }
die() { msg_err "$1"; exit 1; }
command -v pct >/dev/null 2>&1 || die "pct not found — run this on a Proxmox VE host."
command -v whiptail >/dev/null 2>&1 || { msg_info "whiptail not found, installing..."; apt-get update -qq && apt-get install -y whiptail; }
# ---------- welcome + mode ----------
whiptail --title "$APP LXC Installer" --msgbox \
"This will create an openSUSE LXC container on this Proxmox host and install Grafana inside it." \
10 65
MODE=$(whiptail --title "$APP LXC Installer" --menu "Choose setup mode" 12 65 2 \
"default" "Quick install — sane defaults, minimal prompts" \
"advanced" "Advanced — customize every setting" \
3>&1 1>&2 2>&3) || exit 1
# ---------- gather settings ----------
NEXTID=$(pvesh get /cluster/nextid)
if [[ "$MODE" == "default" ]]; then
CTID="$NEXTID"
CT_HOSTNAME="grafana"
CT_STORAGE=$(pvesm status -content rootdir | awk 'NR==2{print $1}')
[[ -n "$CT_STORAGE" ]] || die "No rootdir-capable storage found."
DISK_GB=8
CORES=2
MEMORY=1024
SWAP=512
BRIDGE="vmbr0"
VLAN=""
NET_MODE="dhcp"
UNPRIVILEGED=1
GRAFANA_PORT=3000
ROOT_PASSWORD=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 20)
GRAFANA_ADMIN_PASSWORD=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 20)
SSH_PUBKEY_FILE=""
else
CTID=$(whiptail --inputbox "Container ID" 8 58 "$NEXTID" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
CT_HOSTNAME=$(whiptail --inputbox "Hostname" 8 58 "grafana" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
mapfile -t STORAGES < <(pvesm status -content rootdir | awk 'NR>1{print $1}')
[[ ${#STORAGES[@]} -gt 0 ]] || die "No rootdir-capable storage found."
RADIO_ARGS=(); first=1
for s in "${STORAGES[@]}"; do
if [[ $first -eq 1 ]]; then RADIO_ARGS+=("$s" "" ON); first=0; else RADIO_ARGS+=("$s" "" OFF); fi
done
CT_STORAGE=$(whiptail --radiolist "Storage for rootfs" 15 60 6 "${RADIO_ARGS[@]}" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
DISK_GB=$(whiptail --inputbox "Disk size (GB)" 8 58 "8" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
CORES=$(whiptail --inputbox "CPU cores" 8 58 "2" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
MEMORY=$(whiptail --inputbox "Memory (MB)" 8 58 "1024" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
SWAP=$(whiptail --inputbox "Swap (MB)" 8 58 "512" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
mapfile -t BRIDGES < <(ip -o link show type bridge | awk -F': ' '{print $2}')
[[ ${#BRIDGES[@]} -gt 0 ]] || die "No bridges found on this host."
RADIO_ARGS=(); first=1
for b in "${BRIDGES[@]}"; do
if [[ $first -eq 1 ]]; then RADIO_ARGS+=("$b" "" ON); first=0; else RADIO_ARGS+=("$b" "" OFF); fi
done
BRIDGE=$(whiptail --radiolist "Network bridge" 15 60 6 "${RADIO_ARGS[@]}" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
VLAN=$(whiptail --inputbox "VLAN tag (blank = untagged)" 8 58 "" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
NET_MODE=$(whiptail --menu "Network mode" 12 58 2 \
"dhcp" "DHCP" \
"static" "Static IP" \
--title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
if [[ "$NET_MODE" == "static" ]]; then
STATIC_IP_CIDR=$(whiptail --inputbox "IP/CIDR, e.g. 192.168.10.50/24" 8 58 "" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
STATIC_GW=$(whiptail --inputbox "Gateway" 8 58 "" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
fi
if whiptail --title "$APP LXC" --yesno "Create as unprivileged container? (recommended)" 8 58; then
UNPRIVILEGED=1
else
UNPRIVILEGED=0
fi
GRAFANA_PORT=$(whiptail --inputbox "Grafana HTTP port" 8 58 "3000" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
ROOT_PASSWORD=$(whiptail --passwordbox "Root password for the CT" 8 58 --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
GRAFANA_ADMIN_PASSWORD=$(whiptail --passwordbox "Grafana admin password" 8 58 --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
SSH_PUBKEY_FILE=$(whiptail --inputbox "SSH public key file to inject (blank = skip)" 8 58 "$HOME/.ssh/id_rsa.pub" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
fi
pct status "$CTID" >/dev/null 2>&1 && die "CT $CTID already exists."
# ---------- template ----------
mapfile -t TEMPLATES < <(pveam available -section system | awk '{print $2}' | grep -i opensuse || true)
if [[ ${#TEMPLATES[@]} -eq 0 ]]; then
msg_info "Refreshing template index..."
pveam update
mapfile -t TEMPLATES < <(pveam available -section system | awk '{print $2}' | grep -i opensuse || true)
fi
[[ ${#TEMPLATES[@]} -gt 0 ]] || die "No openSUSE templates found in the Proxmox appliance list."
if [[ "$MODE" == "default" ]]; then
TEMPLATE="${TEMPLATES[-1]}" # newest listed
else
RADIO_ARGS=(); first=1
for t in "${TEMPLATES[@]}"; do
if [[ $first -eq 1 ]]; then RADIO_ARGS+=("$t" "" ON); first=0; else RADIO_ARGS+=("$t" "" OFF); fi
done
TEMPLATE=$(whiptail --radiolist "Choose openSUSE template" 20 72 10 "${RADIO_ARGS[@]}" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
fi
if ! pveam list local | grep -q "$TEMPLATE"; then
msg_info "Downloading template $TEMPLATE..."
pveam download local "$TEMPLATE"
msg_ok "Template downloaded."
fi
# ---------- confirm ----------
SUMMARY="CTID: $CTID
Hostname: $CT_HOSTNAME
Template: $TEMPLATE
Storage: $CT_STORAGE (${DISK_GB}G)
CPU/RAM: ${CORES} cores / ${MEMORY}MB (+${SWAP}MB swap)
Bridge: $BRIDGE${VLAN:+ (vlan $VLAN)}
Network: ${NET_MODE}${STATIC_IP_CIDR:+ $STATIC_IP_CIDR via $STATIC_GW}
Priv: $([[ $UNPRIVILEGED -eq 1 ]] && echo unprivileged || echo privileged)
Grafana: port $GRAFANA_PORT"
whiptail --title "Confirm" --yesno "$SUMMARY
Proceed?" 20 65 || exit 1
# ---------- build net string ----------
if [[ "$NET_MODE" == "static" ]]; then
NET_STR="name=eth0,bridge=${BRIDGE},ip=${STATIC_IP_CIDR},gw=${STATIC_GW}"
else
NET_STR="name=eth0,bridge=${BRIDGE},ip=dhcp"
fi
[[ -n "$VLAN" ]] && NET_STR="${NET_STR},tag=${VLAN}"
# ---------- create + start ----------
CREATE_ARGS=(
"$CTID" "local:vztmpl/${TEMPLATE}"
--hostname "$CT_HOSTNAME"
--cores "$CORES"
--memory "$MEMORY"
--swap "$SWAP"
--rootfs "${CT_STORAGE}:${DISK_GB}"
--net0 "$NET_STR"
--unprivileged "$UNPRIVILEGED"
--onboot 1
--features "nesting=1"
--password "$ROOT_PASSWORD"
)
[[ -n "${SSH_PUBKEY_FILE:-}" && -f "${SSH_PUBKEY_FILE:-}" ]] && CREATE_ARGS+=(--ssh-public-keys "$SSH_PUBKEY_FILE")
msg_info "Creating CT $CTID ($CT_HOSTNAME)..."
pct create "${CREATE_ARGS[@]}" >/dev/null
msg_ok "Container created."
msg_info "Starting CT $CTID..."
pct start "$CTID"
msg_info "Waiting for network..."
for i in {1..30}; do
pct exec "$CTID" -- sh -c "true" >/dev/null 2>&1 && pct exec "$CTID" -- ping -c1 -W2 1.1.1.1 >/dev/null 2>&1 && break
sleep 2
[[ $i -eq 30 ]] && die "CT never came up with network access."
done
msg_ok "Network is up."
# ---------- install grafana inside the CT ----------
run() { pct exec "$CTID" -- bash -c "$1"; }
msg_info "Refreshing zypper repos..."
run "zypper --non-interactive refresh" >/dev/null
msg_ok "Repos refreshed."
msg_info "Installing prerequisites (curl, gpg2, firewalld)..."
run "zypper --non-interactive install curl gpg2 firewalld" >/dev/null
msg_ok "Prerequisites installed."
msg_info "Adding Grafana repo..."
run "zypper -n ar --refresh https://rpm.grafana.com grafana || true"
run "zypper --gpg-auto-import-keys --non-interactive refresh" >/dev/null
msg_ok "Grafana repo added."
msg_info "Installing Grafana..."
run "zypper --non-interactive install grafana" >/dev/null
msg_ok "Grafana installed."
msg_info "Configuring Grafana (port ${GRAFANA_PORT}, admin password)..."
run "sed -i '/^\[server\]/,/^\[/{s/^;\{0,1\}[[:space:]]*http_port[[:space:]]*=.*/http_port = ${GRAFANA_PORT}/}' /etc/grafana/grafana.ini"
run "sed -i '/^\[security\]/,/^\[/{s/^;\{0,1\}[[:space:]]*admin_password[[:space:]]*=.*/admin_password = ${GRAFANA_ADMIN_PASSWORD}/}' /etc/grafana/grafana.ini"
msg_ok "Grafana configured."
msg_info "Enabling firewalld and opening ports 22/${GRAFANA_PORT}..."
run "systemctl enable --now firewalld" >/dev/null
run "firewall-cmd --permanent --add-port=22/tcp" >/dev/null
run "firewall-cmd --permanent --add-port=${GRAFANA_PORT}/tcp" >/dev/null
run "firewall-cmd --reload" >/dev/null
msg_ok "Firewall configured."
msg_info "Starting Grafana..."
run "systemctl enable --now grafana-server" >/dev/null
msg_info "Waiting for Grafana to answer..."
for i in {1..15}; do
run "curl -sf http://localhost:${GRAFANA_PORT}/api/health >/dev/null" && break
sleep 3
[[ $i -eq 15 ]] && die "Grafana did not come up in time — check: pct exec $CTID -- systemctl status grafana-server"
done
msg_ok "Grafana is up."
# ---------- summary ----------
CT_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}')
whiptail --title "$APP LXC Installer — Done" --msgbox \
"Grafana is running.
CT ID: $CTID
URL: http://${CT_IP}:${GRAFANA_PORT}
User: admin
Password: ${GRAFANA_ADMIN_PASSWORD}
Root pass: ${ROOT_PASSWORD}
(also printed below in the terminal)" 16 65
cat <<EOF
========================================
$APP LXC ready
========================================
CT ID: $CTID
URL: http://${CT_IP}:${GRAFANA_PORT}
Admin user: admin
Admin pass: ${GRAFANA_ADMIN_PASSWORD}
Root pass: ${ROOT_PASSWORD}
========================================
EOF