255 lines
9.6 KiB
Bash
Executable File
255 lines
9.6 KiB
Bash
Executable File
#!/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
|