Add universal VM template manager script
Introduces vm-manager.sh, a comprehensive Bash script for Proxmox VE to create, deploy, and list VM templates with optional cloud-init and post-install automation. Supports multiple Linux distributions, customizable resources, and post-install options for Docker, Podman, and Portainer.
This commit is contained in:
parent
62ac9e9470
commit
f77c19be52
637
vm/vm-manager.sh
Normal file
637
vm/vm-manager.sh
Normal file
@ -0,0 +1,637 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2025 community-scripts ORG
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Universal VM Template Manager - Create, Deploy, List Templates with optional Cloud-Init
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================================
|
||||
# OS IMAGE CATALOG
|
||||
# ============================================================================
|
||||
|
||||
declare -A OS_IMAGES=(
|
||||
# Debian - Cloud-Init enabled
|
||||
["debian-13"]="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2"
|
||||
["debian-12"]="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2"
|
||||
|
||||
# Debian - NoCloud variants (without cloud-init pre-installed)
|
||||
["debian-13-nocloud"]="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.qcow2"
|
||||
["debian-12-nocloud"]="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2"
|
||||
|
||||
# Ubuntu - Cloud-Init enabled
|
||||
["ubuntu-24.04"]="https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
["ubuntu-22.04"]="https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img"
|
||||
["ubuntu-20.04"]="https://cloud-images.ubuntu.com/releases/20.04/release/ubuntu-20.04-server-cloudimg-amd64.img"
|
||||
|
||||
# AlmaLinux
|
||||
["alma-9"]="https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2"
|
||||
["alma-8"]="https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-latest.x86_64.qcow2"
|
||||
|
||||
# Rocky Linux
|
||||
["rocky-9"]="https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2"
|
||||
["rocky-8"]="https://dl.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-GenericCloud-Base.latest.x86_64.qcow2"
|
||||
|
||||
# Fedora
|
||||
["fedora-41"]="https://download.fedoraproject.org/pub/fedora/linux/releases/41/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-41-1.4.x86_64.qcow2"
|
||||
["fedora-40"]="https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-40-1.14.x86_64.qcow2"
|
||||
|
||||
# Arch Linux
|
||||
["arch"]="https://geo.mirror.pkgbuild.com/images/latest/Arch-Linux-x86_64-cloudimg.qcow2"
|
||||
|
||||
# CentOS Stream
|
||||
["centos-9"]="https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2"
|
||||
|
||||
# Alpine Linux
|
||||
["alpine-3.20"]="https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/cloud/generic_alpine-3.20.0-x86_64-bios-cloudinit-r0.qcow2"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURATION
|
||||
# ============================================================================
|
||||
|
||||
# Defaults
|
||||
MODE=""
|
||||
TEMPLATE_PREFIX="template"
|
||||
TEMPLATE_ID_START=900
|
||||
VMID=""
|
||||
OS_KEY=""
|
||||
HOSTNAME=""
|
||||
CORES=2
|
||||
MEMORY=2048
|
||||
DISK_SIZE=30
|
||||
STORAGE=""
|
||||
BRIDGE="vmbr0"
|
||||
START_VM="no"
|
||||
MACHINE_TYPE="q35"
|
||||
|
||||
# Cloud-Init Options
|
||||
ENABLE_CLOUDINIT="yes" # yes|no - Enable/disable cloud-init drive
|
||||
CI_USER=""
|
||||
CI_PASSWORD=""
|
||||
CI_SSH_KEY=""
|
||||
CI_FILE=""
|
||||
|
||||
# Post-Install Scripts
|
||||
POST_INSTALL="" # none|docker|podman|portainer
|
||||
POST_INSTALL_TIMEOUT=300 # Timeout in seconds for post-install completion
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;36m'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ============================================================================
|
||||
# HELPER FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
info() { echo -e "${BLUE}ℹ${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}✓${NC} $*"; }
|
||||
error() {
|
||||
echo -e "${RED}✗${NC} $*" >&2
|
||||
exit 1
|
||||
}
|
||||
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
|
||||
|
||||
get_next_vmid() {
|
||||
local start_id=${1:-100}
|
||||
local id=$start_id
|
||||
while [ -f "/etc/pve/qemu-server/${id}.conf" ] || [ -f "/etc/pve/lxc/${id}.conf" ]; do
|
||||
id=$((id + 1))
|
||||
done
|
||||
echo "$id"
|
||||
}
|
||||
|
||||
get_default_storage() {
|
||||
pvesm status -content images 2>/dev/null | awk 'NR==2 {print $1}' || echo "local-lvm"
|
||||
}
|
||||
|
||||
list_storage_pools() {
|
||||
pvesm status -content images 2>/dev/null | awk 'NR>1 {print $1}'
|
||||
}
|
||||
|
||||
get_snippet_storage() {
|
||||
pvesm status -content snippets 2>/dev/null | awk 'NR==2 {print $1}' || echo "local"
|
||||
}
|
||||
|
||||
find_template_by_name() {
|
||||
local name=$1
|
||||
qm list 2>/dev/null | awk -v n="$name" '$2 == n {print $1; exit}'
|
||||
}
|
||||
|
||||
is_template() {
|
||||
local vmid=$1
|
||||
qm config "$vmid" 2>/dev/null | grep -q "^template: 1"
|
||||
}
|
||||
|
||||
cleanup_vm() {
|
||||
if [ -n "${VMID:-}" ] && qm status "$VMID" &>/dev/null; then
|
||||
warn "Cleanup: Removing VM $VMID"
|
||||
qm destroy "$VMID" &>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_vm_ready() {
|
||||
local vmid=$1
|
||||
local timeout=${2:-120}
|
||||
local elapsed=0
|
||||
|
||||
info "Waiting for VM $vmid to be ready..."
|
||||
|
||||
while [ $elapsed -lt $timeout ]; do
|
||||
if qm guest exec $vmid -- test -f /usr/bin/systemctl &>/dev/null; then
|
||||
ok "VM is ready"
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
elapsed=$((elapsed + 5))
|
||||
done
|
||||
|
||||
warn "VM readiness timeout after ${timeout}s"
|
||||
return 1
|
||||
}
|
||||
|
||||
run_post_install() {
|
||||
local vmid=$1
|
||||
local script_type=$2
|
||||
|
||||
[ -z "$script_type" ] || [ "$script_type" = "none" ] && return 0
|
||||
|
||||
info "Running post-install: $script_type"
|
||||
|
||||
# Wait for VM to be ready
|
||||
wait_for_vm_ready "$vmid" 180 || {
|
||||
warn "VM not ready, skipping post-install"
|
||||
return 1
|
||||
}
|
||||
|
||||
case "$script_type" in
|
||||
docker)
|
||||
info "Installing Docker..."
|
||||
qm guest exec "$vmid" -- bash -c '
|
||||
curl -fsSL https://get.docker.com | sh && \
|
||||
systemctl enable --now docker && \
|
||||
usermod -aG docker $(whoami) 2>/dev/null || true
|
||||
' || {
|
||||
warn "Docker installation failed"
|
||||
return 1
|
||||
}
|
||||
ok "Docker installed successfully"
|
||||
;;
|
||||
|
||||
podman)
|
||||
info "Installing Podman..."
|
||||
qm guest exec "$vmid" -- bash -c '
|
||||
if command -v apt-get &>/dev/null; then
|
||||
apt-get update && apt-get install -y podman
|
||||
elif command -v dnf &>/dev/null; then
|
||||
dnf install -y podman
|
||||
elif command -v yum &>/dev/null; then
|
||||
yum install -y podman
|
||||
else
|
||||
echo "Package manager not supported"
|
||||
exit 1
|
||||
fi && \
|
||||
systemctl enable --now podman || true
|
||||
' || {
|
||||
warn "Podman installation failed"
|
||||
return 1
|
||||
}
|
||||
ok "Podman installed successfully"
|
||||
;;
|
||||
|
||||
portainer)
|
||||
info "Installing Portainer (requires Docker)..."
|
||||
# First install Docker
|
||||
run_post_install "$vmid" "docker" || return 1
|
||||
|
||||
info "Deploying Portainer container..."
|
||||
qm guest exec "$vmid" -- bash -c '
|
||||
docker volume create portainer_data && \
|
||||
docker run -d \
|
||||
-p 8000:8000 \
|
||||
-p 9443:9443 \
|
||||
--name portainer \
|
||||
--restart=always \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v portainer_data:/data \
|
||||
portainer/portainer-ce:latest
|
||||
' || {
|
||||
warn "Portainer deployment failed"
|
||||
return 1
|
||||
}
|
||||
ok "Portainer deployed successfully"
|
||||
info "Access Portainer at: https://<vm-ip>:9443"
|
||||
;;
|
||||
|
||||
*)
|
||||
warn "Unknown post-install script: $script_type"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# TEMPLATE OPERATIONS
|
||||
# ============================================================================
|
||||
|
||||
list_templates() {
|
||||
echo -e "\n${BOLD}${CYAN}Available VM Templates:${NC}\n"
|
||||
echo "┌──────┬────────────────────────────┬─────────┬────────┬──────────┬────────────┐"
|
||||
echo "│ VMID │ Name │ Cores │ Memory │ Disk │ Cloud-Init │"
|
||||
echo "├──────┼────────────────────────────┼─────────┼────────┼──────────┼────────────┤"
|
||||
|
||||
local found=0
|
||||
while IFS= read -r line; do
|
||||
local vmid=$(echo "$line" | awk '{print $1}')
|
||||
local name=$(echo "$line" | awk '{print $2}')
|
||||
|
||||
if is_template "$vmid"; then
|
||||
local config=$(qm config "$vmid" 2>/dev/null)
|
||||
local cores=$(echo "$config" | grep "^cores:" | awk '{print $2}')
|
||||
local memory=$(echo "$config" | grep "^memory:" | awk '{print $2}')
|
||||
local disk=$(echo "$config" | grep "scsi0:" | grep -oP '\d+G' | head -1)
|
||||
local has_ci=$(echo "$config" | grep -q "ide2:.*cloudinit" && echo "Yes" || echo "No")
|
||||
|
||||
printf "│ %-4s │ %-26s │ %-7s │ %-6s │ %-8s │ %-10s │\n" \
|
||||
"$vmid" "$name" "${cores:-N/A}" "${memory:-N/A}MB" "${disk:-N/A}" "$has_ci"
|
||||
found=$((found + 1))
|
||||
fi
|
||||
done < <(qm list 2>/dev/null | tail -n +2)
|
||||
|
||||
echo "└──────┴────────────────────────────┴─────────┴────────┴──────────┴────────────┘"
|
||||
|
||||
if [ $found -eq 0 ]; then
|
||||
echo -e "\n${YELLOW}No templates found.${NC}"
|
||||
echo -e "Create one with: $0 create --os <os-key>\n"
|
||||
else
|
||||
echo -e "\n${GREEN}Total: $found template(s)${NC}\n"
|
||||
fi
|
||||
}
|
||||
|
||||
list_os_options() {
|
||||
echo -e "\n${BOLD}${CYAN}Available OS Images:${NC}\n"
|
||||
local i=1
|
||||
for key in $(echo "${!OS_IMAGES[@]}" | tr ' ' '\n' | sort); do
|
||||
printf "%2d) %-20s %s\n" $i "$key" "${OS_IMAGES[$key]}"
|
||||
i=$((i + 1))
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
create_template() {
|
||||
# Validate OS
|
||||
[ -z "$OS_KEY" ] && error "OS not specified. Use --os <os-key>"
|
||||
[ -z "${OS_IMAGES[$OS_KEY]:-}" ] && error "Unknown OS: $OS_KEY (use --list-os)"
|
||||
|
||||
local image_url="${OS_IMAGES[$OS_KEY]}"
|
||||
local template_name="${TEMPLATE_PREFIX}-${OS_KEY}"
|
||||
|
||||
# Check if template already exists
|
||||
local existing_id=$(find_template_by_name "$template_name")
|
||||
if [ -n "$existing_id" ]; then
|
||||
warn "Template '$template_name' already exists (ID: $existing_id)"
|
||||
read -p "Overwrite? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
info "Aborted"
|
||||
exit 0
|
||||
fi
|
||||
qm destroy "$existing_id" &>/dev/null || true
|
||||
fi
|
||||
|
||||
# Get VM ID
|
||||
[ -z "$VMID" ] && VMID=$(get_next_vmid $TEMPLATE_ID_START)
|
||||
[ -z "$STORAGE" ] && STORAGE=$(get_default_storage)
|
||||
|
||||
info "Creating template: $template_name (ID: $VMID)"
|
||||
[ "$ENABLE_CLOUDINIT" = "yes" ] && info "Cloud-Init: Enabled" || info "Cloud-Init: Disabled"
|
||||
|
||||
# Download/cache image
|
||||
local cache_dir="/var/lib/vz/template/cache"
|
||||
local image_file="$cache_dir/$(basename "$image_url")"
|
||||
mkdir -p "$cache_dir"
|
||||
|
||||
if [ ! -f "$image_file" ]; then
|
||||
info "Downloading image..."
|
||||
curl -fL --progress-bar -o "$image_file" "$image_url" || error "Download failed"
|
||||
ok "Image downloaded"
|
||||
else
|
||||
ok "Using cached image"
|
||||
fi
|
||||
|
||||
# Create VM
|
||||
info "Creating VM shell"
|
||||
qm create "$VMID" \
|
||||
--name "$template_name" \
|
||||
--machine "$MACHINE_TYPE" \
|
||||
--bios ovmf \
|
||||
--cores "$CORES" \
|
||||
--memory "$MEMORY" \
|
||||
--net0 "virtio,bridge=$BRIDGE" \
|
||||
--scsihw virtio-scsi-single \
|
||||
--ostype l26 \
|
||||
--agent enabled=1 \
|
||||
>/dev/null || error "VM creation failed"
|
||||
|
||||
ok "VM shell created"
|
||||
|
||||
# Import disk
|
||||
info "Importing disk"
|
||||
local import_out
|
||||
if command -v "qm" &>/dev/null && qm disk import --help &>/dev/null 2>&1; then
|
||||
import_out=$(qm disk import "$VMID" "$image_file" "$STORAGE" --format qcow2 2>&1 || true)
|
||||
else
|
||||
import_out=$(qm importdisk "$VMID" "$image_file" "$STORAGE" 2>&1 || true)
|
||||
fi
|
||||
|
||||
local disk_ref=$(echo "$import_out" | grep -oP "vm-$VMID-disk-\d+" | head -1)
|
||||
[ -z "$disk_ref" ] && disk_ref=$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $5}' | sort | tail -n1)
|
||||
[ -z "$disk_ref" ] && error "Disk import failed"
|
||||
|
||||
ok "Disk imported: $disk_ref"
|
||||
|
||||
# Configure disks
|
||||
info "Configuring disks"
|
||||
if [ "$ENABLE_CLOUDINIT" = "yes" ]; then
|
||||
qm set "$VMID" \
|
||||
--scsi0 "${STORAGE}:${disk_ref},discard=on" \
|
||||
--boot order=scsi0 \
|
||||
--ide2 "${STORAGE}:cloudinit" \
|
||||
>/dev/null || error "Disk configuration failed"
|
||||
else
|
||||
qm set "$VMID" \
|
||||
--scsi0 "${STORAGE}:${disk_ref},discard=on" \
|
||||
--boot order=scsi0 \
|
||||
>/dev/null || error "Disk configuration failed"
|
||||
fi
|
||||
|
||||
# Resize disk
|
||||
qm resize "$VMID" scsi0 "${DISK_SIZE}G" >/dev/null 2>&1 || warn "Disk resize failed"
|
||||
ok "Disk configured (${DISK_SIZE}G)"
|
||||
|
||||
# Cloud-Init configuration
|
||||
if [ "$ENABLE_CLOUDINIT" = "yes" ]; then
|
||||
if [ -n "$CI_USER" ] && [ -n "$CI_SSH_KEY" ]; then
|
||||
info "Configuring Cloud-Init credentials"
|
||||
qm set "$VMID" --ciuser "$CI_USER" >/dev/null
|
||||
qm set "$VMID" --sshkeys <(echo "$CI_SSH_KEY") >/dev/null
|
||||
[ -n "$CI_PASSWORD" ] && qm set "$VMID" --cipassword "$CI_PASSWORD" >/dev/null
|
||||
ok "Cloud-Init configured"
|
||||
else
|
||||
info "Cloud-Init drive created (configure after deployment)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Convert to template
|
||||
info "Converting to template"
|
||||
qm template "$VMID" >/dev/null || error "Template conversion failed"
|
||||
|
||||
ok "Template created successfully!"
|
||||
echo ""
|
||||
echo " Template ID: $VMID"
|
||||
echo " Template Name: $template_name"
|
||||
echo " OS: $OS_KEY"
|
||||
echo " Cloud-Init: $ENABLE_CLOUDINIT"
|
||||
echo ""
|
||||
}
|
||||
|
||||
deploy_from_template() {
|
||||
local template_name="${TEMPLATE_PREFIX}-${OS_KEY}"
|
||||
local template_id=$(find_template_by_name "$template_name")
|
||||
|
||||
[ -z "$template_id" ] && error "Template '$template_name' not found"
|
||||
|
||||
if ! is_template "$template_id"; then
|
||||
error "VM $template_id is not a template"
|
||||
fi
|
||||
|
||||
[ -z "$VMID" ] && VMID=$(get_next_vmid)
|
||||
[ -z "$HOSTNAME" ] && HOSTNAME="${OS_KEY}-vm-${VMID}"
|
||||
|
||||
info "Cloning template $template_id -> VM $VMID ($HOSTNAME)"
|
||||
|
||||
# Full clone
|
||||
qm clone "$template_id" "$VMID" --name "$HOSTNAME" --full 1 >/dev/null || error "Clone failed"
|
||||
ok "VM cloned"
|
||||
|
||||
# Reconfigure network (remove MAC to get new one)
|
||||
qm set "$VMID" --delete net0 >/dev/null
|
||||
qm set "$VMID" --net0 "virtio,bridge=$BRIDGE" >/dev/null
|
||||
ok "Network reconfigured"
|
||||
|
||||
# Resize if different from template
|
||||
local template_size=$(qm config "$template_id" | grep "scsi0:" | grep -oP '\d+G' | head -1)
|
||||
template_size=${template_size%G}
|
||||
if [ "$DISK_SIZE" -gt "$template_size" ]; then
|
||||
local diff=$((DISK_SIZE - template_size))
|
||||
info "Expanding disk by ${diff}G"
|
||||
qm resize "$VMID" scsi0 "+${diff}G" >/dev/null 2>&1 || warn "Resize failed"
|
||||
fi
|
||||
|
||||
# Start VM if requested or if post-install is needed
|
||||
local need_start="no"
|
||||
[ "$START_VM" = "yes" ] && need_start="yes"
|
||||
[ -n "$POST_INSTALL" ] && [ "$POST_INSTALL" != "none" ] && need_start="yes"
|
||||
|
||||
if [ "$need_start" = "yes" ]; then
|
||||
info "Starting VM"
|
||||
qm start "$VMID" || { warn "Start failed"; }
|
||||
ok "VM started"
|
||||
fi
|
||||
|
||||
# Execute post-install scripts if specified
|
||||
if [ -n "$POST_INSTALL" ] && [ "$POST_INSTALL" != "none" ]; then
|
||||
if run_post_install "$VMID" "$POST_INSTALL"; then
|
||||
ok "Post-install completed: $POST_INSTALL"
|
||||
else
|
||||
warn "Post-install had issues, but VM is deployed"
|
||||
fi
|
||||
fi
|
||||
|
||||
ok "VM deployed successfully!"
|
||||
echo ""
|
||||
echo " VM ID: $VMID"
|
||||
echo " Hostname: $HOSTNAME"
|
||||
echo " Template: $template_name (ID: $template_id)"
|
||||
[ -n "$POST_INSTALL" ] && [ "$POST_INSTALL" != "none" ] && echo " Post-Install: $POST_INSTALL"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# USAGE
|
||||
# ============================================================================
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
${BOLD}Usage:${NC} $0 <COMMAND> [OPTIONS]
|
||||
|
||||
${BOLD}Commands:${NC}
|
||||
create Create a new VM template
|
||||
deploy Deploy VM from template
|
||||
list List all templates
|
||||
list-os Show available OS images
|
||||
|
||||
${BOLD}Options:${NC}
|
||||
--os KEY OS from catalog (e.g. debian-12, ubuntu-24.04)
|
||||
Use -nocloud suffix for images without cloud-init
|
||||
--vmid ID VM/Template ID (auto-assigned if not specified)
|
||||
--hostname NAME Hostname for deployed VM
|
||||
--cores NUM CPU cores (default: $CORES)
|
||||
--memory MB RAM in MB (default: $MEMORY)
|
||||
--disk GB Disk size in GB (default: $DISK_SIZE)
|
||||
--storage NAME Storage pool
|
||||
--bridge NAME Network bridge (default: $BRIDGE)
|
||||
--start Start VM after deployment
|
||||
--no-cloudinit Disable cloud-init drive (default: enabled)
|
||||
|
||||
${BOLD}Cloud-Init:${NC}
|
||||
--ci-user USER Cloud-Init username
|
||||
--ci-password PASS Cloud-Init password
|
||||
--ci-ssh-key KEY SSH public key
|
||||
|
||||
${BOLD}Post-Install:${NC}
|
||||
--post-install PKG Install software after first boot
|
||||
Options: docker, podman, portainer
|
||||
Note: Requires cloud-init + SSH access
|
||||
|
||||
${BOLD}Examples:${NC}
|
||||
${BOLD}# Create templates${NC}
|
||||
$0 create --os debian-12
|
||||
$0 create --os debian-12-nocloud --no-cloudinit
|
||||
$0 create --os ubuntu-24.04 --cores 4 --memory 4096
|
||||
$0 create --os debian-12 --ci-user admin --ci-ssh-key "ssh-rsa AAA..."
|
||||
|
||||
${BOLD}# Deploy VMs${NC}
|
||||
$0 deploy --os debian-12 --hostname webserver --start
|
||||
$0 deploy --os ubuntu-24.04 --hostname docker-host --post-install docker
|
||||
$0 deploy --os debian-12 --hostname portainer --post-install portainer --start
|
||||
$0 deploy --os rocky-9 --hostname podman-host --post-install podman --disk 100
|
||||
|
||||
${BOLD}# List resources${NC}
|
||||
$0 list
|
||||
$0 list-os
|
||||
|
||||
${BOLD}Post-Install Details:${NC}
|
||||
docker - Installs Docker CE via get.docker.com
|
||||
podman - Installs Podman via system package manager
|
||||
portainer - Installs Docker + Portainer CE container
|
||||
Access at https://<vm-ip>:9443
|
||||
|
||||
${BOLD}NoCloud Images:${NC}
|
||||
NoCloud variants (e.g., debian-12-nocloud) are minimal images
|
||||
without cloud-init pre-installed. Use --no-cloudinit with these.
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# ARGUMENT PARSING
|
||||
# ============================================================================
|
||||
|
||||
[ $# -eq 0 ] && usage
|
||||
|
||||
MODE="$1"
|
||||
shift
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--os)
|
||||
OS_KEY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--vmid)
|
||||
VMID="$2"
|
||||
shift 2
|
||||
;;
|
||||
--hostname)
|
||||
HOSTNAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--cores)
|
||||
CORES="$2"
|
||||
shift 2
|
||||
;;
|
||||
--memory)
|
||||
MEMORY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--disk)
|
||||
DISK_SIZE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--storage)
|
||||
STORAGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--bridge)
|
||||
BRIDGE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--start)
|
||||
START_VM="yes"
|
||||
shift
|
||||
;;
|
||||
--no-cloudinit)
|
||||
ENABLE_CLOUDINIT="no"
|
||||
shift
|
||||
;;
|
||||
--ci-user)
|
||||
CI_USER="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ci-password)
|
||||
CI_PASSWORD="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ci-ssh-key)
|
||||
CI_SSH_KEY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--post-install)
|
||||
POST_INSTALL="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h | --help) usage ;;
|
||||
*) error "Unknown option: $1 (use --help)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# CHECKS
|
||||
# ============================================================================
|
||||
|
||||
[ "$(id -u)" -ne 0 ] && error "Root privileges required"
|
||||
command -v qm >/dev/null 2>&1 || error "qm not found - Is Proxmox VE installed?"
|
||||
command -v pvesm >/dev/null 2>&1 || error "pvesm not found"
|
||||
|
||||
# ============================================================================
|
||||
# MAIN
|
||||
# ============================================================================
|
||||
|
||||
trap cleanup_vm EXIT
|
||||
|
||||
case "$MODE" in
|
||||
create)
|
||||
create_template
|
||||
;;
|
||||
deploy)
|
||||
deploy_from_template
|
||||
;;
|
||||
list)
|
||||
list_templates
|
||||
;;
|
||||
list-os)
|
||||
list_os_options
|
||||
;;
|
||||
*)
|
||||
error "Unknown command: $MODE (use --help)"
|
||||
;;
|
||||
esac
|
||||
Loading…
x
Reference in New Issue
Block a user