honestly its been months, i found unstaged changes and forgot
This commit is contained in:
1 parent
b5d1ffda95
commit
ef8d5645e3
55 files changed
+1554
-180
No files matched your search
Executable
+367
@@ -0,0 +1,367 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HOST_VARS_DIR=host_vars
|
||||
TEMPLATE_VMID=9000
|
||||
TEMPLATE_STORAGE=local-lvm
|
||||
TEMPLATE_BRIDGE=vmbr0
|
||||
PBS_STORAGE=
|
||||
DRY_RUN=0
|
||||
ASSUME_YES=0
|
||||
INTERACTIVE=1
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Initialise Proxmox VMs from host_vars/*.yml.
|
||||
|
||||
Run this on a Proxmox node as root from the repository root.
|
||||
|
||||
Each host var file must set proxmox_vmid to be managed by this script.
|
||||
Files without proxmox_vmid are skipped.
|
||||
|
||||
Supported host vars:
|
||||
proxmox_vmid: 101
|
||||
proxmox_name: cloud
|
||||
proxmox_template_vmid: 9000
|
||||
proxmox_storage: local-lvm
|
||||
proxmox_bridge: vmbr0
|
||||
proxmox_memory: 4096
|
||||
proxmox_cores: 2
|
||||
proxmox_cpu: host
|
||||
proxmox_disk_size: 64G
|
||||
proxmox_backup_enabled: true
|
||||
proxmox_ipconfig0: ip=dhcp
|
||||
proxmox_onboot: true
|
||||
proxmox_start: false
|
||||
|
||||
Options:
|
||||
--host-vars-dir PATH Host vars directory. Default: host_vars
|
||||
--template-vmid ID Default template VMID. Default: 9000
|
||||
--template-storage NAME Storage used if template must be created. Default: local-lvm
|
||||
--template-bridge NAME Bridge used if template must be created. Default: vmbr0
|
||||
--pbs-storage NAME PBS storage/datastore ID. Passed to template script and used as default backup target note.
|
||||
--dry-run Print commands without executing them.
|
||||
--yes Accept defaults for prompts.
|
||||
--non-interactive Do not prompt.
|
||||
-h, --help Show this help.
|
||||
USAGE
|
||||
}
|
||||
|
||||
log() {
|
||||
printf '%s\n' "$*"
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
run() {
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '+'
|
||||
printf ' %q' "$@"
|
||||
printf '\n'
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
need_command() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
|
||||
}
|
||||
|
||||
prompt_value() {
|
||||
local label=$1
|
||||
local default_value=$2
|
||||
local value
|
||||
|
||||
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||
printf '%s\n' "$default_value"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '%s [%s]: ' "$label" "$default_value" >&2
|
||||
read -r value
|
||||
if [ -z "$value" ]; then
|
||||
printf '%s\n' "$default_value"
|
||||
else
|
||||
printf '%s\n' "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
prompt_optional_value() {
|
||||
local label=$1
|
||||
local value
|
||||
|
||||
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||
printf '\n'
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '%s [leave empty]: ' "$label" >&2
|
||||
read -r value
|
||||
printf '%s\n' "$value"
|
||||
}
|
||||
|
||||
prompt_confirm() {
|
||||
local label=$1
|
||||
local value
|
||||
|
||||
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '%s [Y/n]: ' "$label" >&2
|
||||
read -r value
|
||||
case "$value" in
|
||||
""|y|Y|yes|YES)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
yaml_scalar() {
|
||||
local file=$1
|
||||
local key=$2
|
||||
awk -v key="$key" '
|
||||
$0 ~ "^[[:space:]]*" key ":" {
|
||||
sub("^[[:space:]]*" key ":[[:space:]]*", "", $0)
|
||||
sub("[[:space:]]+#.*$", "", $0)
|
||||
gsub(/^["'\'']|["'\'']$/, "", $0)
|
||||
print $0
|
||||
exit
|
||||
}
|
||||
' "$file"
|
||||
}
|
||||
|
||||
bool_true() {
|
||||
case "${1:-}" in
|
||||
true|True|TRUE|yes|Yes|YES|1|on|On|ON)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--host-vars-dir)
|
||||
HOST_VARS_DIR=${2:?missing value for --host-vars-dir}
|
||||
shift 2
|
||||
;;
|
||||
--template-vmid)
|
||||
TEMPLATE_VMID=${2:?missing value for --template-vmid}
|
||||
shift 2
|
||||
;;
|
||||
--template-storage)
|
||||
TEMPLATE_STORAGE=${2:?missing value for --template-storage}
|
||||
shift 2
|
||||
;;
|
||||
--template-bridge)
|
||||
TEMPLATE_BRIDGE=${2:?missing value for --template-bridge}
|
||||
shift 2
|
||||
;;
|
||||
--pbs-storage)
|
||||
PBS_STORAGE=${2:?missing value for --pbs-storage}
|
||||
shift 2
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
--yes)
|
||||
ASSUME_YES=1
|
||||
shift
|
||||
;;
|
||||
--non-interactive)
|
||||
INTERACTIVE=0
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
die "unknown option: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
collect_config() {
|
||||
TEMPLATE_VMID=$(prompt_value "Default template VMID" "$TEMPLATE_VMID")
|
||||
TEMPLATE_STORAGE=$(prompt_value "Default template/storage name" "$TEMPLATE_STORAGE")
|
||||
TEMPLATE_BRIDGE=$(prompt_value "Default bridge" "$TEMPLATE_BRIDGE")
|
||||
if [ -z "$PBS_STORAGE" ]; then
|
||||
PBS_STORAGE=$(prompt_optional_value "PBS storage/datastore ID")
|
||||
fi
|
||||
}
|
||||
|
||||
template_exists() {
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
return 1
|
||||
fi
|
||||
qm status "$TEMPLATE_VMID" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
vm_exists() {
|
||||
local vmid=$1
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
return 1
|
||||
fi
|
||||
qm status "$vmid" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
ensure_template() {
|
||||
if template_exists; then
|
||||
log "Template VMID $TEMPLATE_VMID already exists."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Template VMID $TEMPLATE_VMID not found; creating it first."
|
||||
local args=(
|
||||
scripts/create-proxmox-template.sh
|
||||
--vmid "$TEMPLATE_VMID"
|
||||
--storage "$TEMPLATE_STORAGE"
|
||||
--bridge "$TEMPLATE_BRIDGE"
|
||||
--yes
|
||||
--non-interactive
|
||||
)
|
||||
if [ -n "$PBS_STORAGE" ]; then
|
||||
args+=(--pbs-storage "$PBS_STORAGE")
|
||||
fi
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
args+=(--dry-run)
|
||||
fi
|
||||
run "${args[@]}"
|
||||
}
|
||||
|
||||
disk_volume() {
|
||||
local vmid=$1
|
||||
local storage=$2
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf '%s:vm-%s-disk-0\n' "$storage" "$vmid"
|
||||
return 0
|
||||
fi
|
||||
qm config "$vmid" | awk -F': ' '/^scsi0:/{split($2, parts, ","); print parts[1]; exit}'
|
||||
}
|
||||
|
||||
init_vm_from_host_vars() {
|
||||
local file=$1
|
||||
local stem
|
||||
stem=$(basename "$file" .yml)
|
||||
|
||||
local vmid
|
||||
vmid=$(yaml_scalar "$file" proxmox_vmid)
|
||||
if [ -z "$vmid" ]; then
|
||||
log "Skipping $file: proxmox_vmid not set"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local name template_vmid storage bridge memory cores cpu disk_size backup_enabled ipconfig0 onboot start_vm
|
||||
name=$(yaml_scalar "$file" proxmox_name)
|
||||
[ -n "$name" ] || name=$(yaml_scalar "$file" nbops_hostname)
|
||||
[ -n "$name" ] || name=$stem
|
||||
template_vmid=$(yaml_scalar "$file" proxmox_template_vmid)
|
||||
[ -n "$template_vmid" ] || template_vmid=$TEMPLATE_VMID
|
||||
storage=$(yaml_scalar "$file" proxmox_storage)
|
||||
[ -n "$storage" ] || storage=$TEMPLATE_STORAGE
|
||||
bridge=$(yaml_scalar "$file" proxmox_bridge)
|
||||
[ -n "$bridge" ] || bridge=$TEMPLATE_BRIDGE
|
||||
memory=$(yaml_scalar "$file" proxmox_memory)
|
||||
[ -n "$memory" ] || memory=2048
|
||||
cores=$(yaml_scalar "$file" proxmox_cores)
|
||||
[ -n "$cores" ] || cores=2
|
||||
cpu=$(yaml_scalar "$file" proxmox_cpu)
|
||||
[ -n "$cpu" ] || cpu=host
|
||||
disk_size=$(yaml_scalar "$file" proxmox_disk_size)
|
||||
[ -n "$disk_size" ] || disk_size=32G
|
||||
backup_enabled=$(yaml_scalar "$file" proxmox_backup_enabled)
|
||||
[ -n "$backup_enabled" ] || backup_enabled=false
|
||||
ipconfig0=$(yaml_scalar "$file" proxmox_ipconfig0)
|
||||
[ -n "$ipconfig0" ] || ipconfig0=ip=dhcp
|
||||
onboot=$(yaml_scalar "$file" proxmox_onboot)
|
||||
[ -n "$onboot" ] || onboot=true
|
||||
start_vm=$(yaml_scalar "$file" proxmox_start)
|
||||
[ -n "$start_vm" ] || start_vm=false
|
||||
|
||||
if vm_exists "$vmid"; then
|
||||
log "Skipping $name ($vmid): VM already exists"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Initialising $name ($vmid) from $file"
|
||||
run qm clone "$template_vmid" "$vmid" --name "$name" --full true --storage "$storage"
|
||||
run qm set "$vmid" --memory "$memory" --cores "$cores" --cpu "$cpu"
|
||||
run qm set "$vmid" --net0 "virtio,bridge=$bridge"
|
||||
run qm set "$vmid" --ipconfig0 "$ipconfig0"
|
||||
|
||||
if bool_true "$onboot"; then
|
||||
run qm set "$vmid" --onboot 1
|
||||
else
|
||||
run qm set "$vmid" --onboot 0
|
||||
fi
|
||||
|
||||
local volume backup_flag
|
||||
volume=$(disk_volume "$vmid" "$storage")
|
||||
backup_flag=0
|
||||
if bool_true "$backup_enabled"; then
|
||||
backup_flag=1
|
||||
fi
|
||||
run qm set "$vmid" --scsi0 "$volume,discard=on,ssd=1,backup=$backup_flag"
|
||||
|
||||
if [ -n "$disk_size" ]; then
|
||||
run qm resize "$vmid" scsi0 "$disk_size"
|
||||
fi
|
||||
|
||||
if bool_true "$start_vm"; then
|
||||
run qm start "$vmid"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
collect_config
|
||||
|
||||
if [ "$DRY_RUN" -eq 0 ]; then
|
||||
need_command qm
|
||||
need_command awk
|
||||
[ "$(id -u)" -eq 0 ] || die "run this script as root on a Proxmox node"
|
||||
fi
|
||||
|
||||
[ -d "$HOST_VARS_DIR" ] || die "host vars directory not found: $HOST_VARS_DIR"
|
||||
|
||||
cat <<SUMMARY
|
||||
|
||||
VM initialisation:
|
||||
Host vars: $HOST_VARS_DIR/*.yml
|
||||
Template VMID: $TEMPLATE_VMID
|
||||
Default storage: $TEMPLATE_STORAGE
|
||||
Default bridge: $TEMPLATE_BRIDGE
|
||||
PBS storage: ${PBS_STORAGE:-not set}
|
||||
Mode: $([ "$DRY_RUN" -eq 1 ] && printf 'dry-run' || printf 'apply')
|
||||
|
||||
SUMMARY
|
||||
|
||||
if ! prompt_confirm "Initialise VMs from host vars?"; then
|
||||
die "cancelled"
|
||||
fi
|
||||
|
||||
ensure_template
|
||||
|
||||
local found=0
|
||||
for file in "$HOST_VARS_DIR"/*.yml; do
|
||||
[ -e "$file" ] || continue
|
||||
found=1
|
||||
init_vm_from_host_vars "$file"
|
||||
done
|
||||
[ "$found" -eq 1 ] || die "no .yml files found in $HOST_VARS_DIR"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in new issue
Block a user