|
|
@@ -193,6 +193,16 @@ done
|
|
|
|
|
|
|
|
|
|
|
|
ssh_target="${PROXMOX_SSH_USER}@${node}"
|
|
|
|
ssh_target="${PROXMOX_SSH_USER}@${node}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Proxmox tools (pvesh, qm, pct) require root access to the cluster IPC
|
|
|
|
|
|
|
|
# socket. When SSH-ing as a non-root user with sudo, prefix every remote
|
|
|
|
|
|
|
|
# Proxmox command with sudo.
|
|
|
|
|
|
|
|
sudo_prefix=""
|
|
|
|
|
|
|
|
sudo_display=""
|
|
|
|
|
|
|
|
if [[ "$PROXMOX_SSH_USER" != "root" ]]; then
|
|
|
|
|
|
|
|
sudo_prefix="sudo"
|
|
|
|
|
|
|
|
sudo_display="sudo "
|
|
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
remote() {
|
|
|
|
remote() {
|
|
|
|
if [[ "$dry_run" -eq 1 ]]; then
|
|
|
|
if [[ "$dry_run" -eq 1 ]]; then
|
|
|
|
echo "[dry-run] ssh ${ssh_target} -- $*"
|
|
|
|
echo "[dry-run] ssh ${ssh_target} -- $*"
|
|
|
@@ -214,10 +224,10 @@ cmd_modify() {
|
|
|
|
|
|
|
|
|
|
|
|
echo "Looking up VMID ${vmid} on ${node}..."
|
|
|
|
echo "Looking up VMID ${vmid} on ${node}..."
|
|
|
|
local kind current_cores current_memory disk_key
|
|
|
|
local kind current_cores current_memory disk_key
|
|
|
|
if ssh "$ssh_target" "qm status ${vmid}" >/dev/null 2>&1; then
|
|
|
|
if ssh "$ssh_target" "${sudo_prefix} qm status ${vmid}" >/dev/null 2>&1; then
|
|
|
|
kind="vm"
|
|
|
|
kind="vm"
|
|
|
|
disk_key="scsi0"
|
|
|
|
disk_key="scsi0"
|
|
|
|
elif ssh "$ssh_target" "pct status ${vmid}" >/dev/null 2>&1; then
|
|
|
|
elif ssh "$ssh_target" "${sudo_prefix} pct status ${vmid}" >/dev/null 2>&1; then
|
|
|
|
kind="lxc"
|
|
|
|
kind="lxc"
|
|
|
|
disk_key="rootfs"
|
|
|
|
disk_key="rootfs"
|
|
|
|
else
|
|
|
|
else
|
|
|
@@ -225,8 +235,8 @@ cmd_modify() {
|
|
|
|
exit 1
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
local config_cmd="qm config ${vmid}"
|
|
|
|
local config_cmd="${sudo_prefix} qm config ${vmid}"
|
|
|
|
[[ "$kind" == "lxc" ]] && config_cmd="pct config ${vmid}"
|
|
|
|
[[ "$kind" == "lxc" ]] && config_cmd="${sudo_prefix} pct config ${vmid}"
|
|
|
|
local current_config
|
|
|
|
local current_config
|
|
|
|
current_config="$(ssh "$ssh_target" "$config_cmd")"
|
|
|
|
current_config="$(ssh "$ssh_target" "$config_cmd")"
|
|
|
|
current_cores="$(echo "$current_config" | grep -oP '^cores:\s*\K\S+' || echo '?')"
|
|
|
|
current_cores="$(echo "$current_config" | grep -oP '^cores:\s*\K\S+' || echo '?')"
|
|
|
@@ -250,9 +260,9 @@ cmd_modify() {
|
|
|
|
exit 1
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
local set_cmd="qm set"
|
|
|
|
local set_cmd="${sudo_prefix} qm set"
|
|
|
|
local resize_cmd="qm resize"
|
|
|
|
local resize_cmd="${sudo_prefix} qm resize"
|
|
|
|
[[ "$kind" == "lxc" ]] && set_cmd="pct set" && resize_cmd="pct resize"
|
|
|
|
[[ "$kind" == "lxc" ]] && set_cmd="${sudo_prefix} pct set" && resize_cmd="${sudo_prefix} pct resize"
|
|
|
|
|
|
|
|
|
|
|
|
if [[ -n "$cores" || -n "$memory" ]]; then
|
|
|
|
if [[ -n "$cores" || -n "$memory" ]]; then
|
|
|
|
local args=""
|
|
|
|
local args=""
|
|
|
@@ -359,14 +369,15 @@ else
|
|
|
|
echo
|
|
|
|
echo
|
|
|
|
echo "==> Checking ${node} for an existing VM/CT identified as '${host}'..."
|
|
|
|
echo "==> Checking ${node} for an existing VM/CT identified as '${host}'..."
|
|
|
|
ssh_check_status=0
|
|
|
|
ssh_check_status=0
|
|
|
|
existing="$(ssh "$ssh_target" bash -s -- "$host" <<'REMOTE_SCRIPT'
|
|
|
|
existing="$(ssh "$ssh_target" bash -s -- "$host" "$sudo_prefix" <<'REMOTE_SCRIPT'
|
|
|
|
target="$1"
|
|
|
|
target="$1"
|
|
|
|
for id in $(qm list 2>/dev/null | awk 'NR>1{print $1}'); do
|
|
|
|
sudo_pfx="$2"
|
|
|
|
n="$(qm config "$id" 2>/dev/null | grep -oP '^name:\s*\K\S+' || true)"
|
|
|
|
for id in $($sudo_pfx qm list 2>/dev/null | awk 'NR>1{print $1}'); do
|
|
|
|
|
|
|
|
n="$($sudo_pfx qm config "$id" 2>/dev/null | grep -oP '^name:\s*\K\S+' || true)"
|
|
|
|
[[ "$n" == "$target" ]] && echo "vm ${id} ${n}"
|
|
|
|
[[ "$n" == "$target" ]] && echo "vm ${id} ${n}"
|
|
|
|
done
|
|
|
|
done
|
|
|
|
for id in $(pct list 2>/dev/null | awk 'NR>1{print $1}'); do
|
|
|
|
for id in $($sudo_pfx pct list 2>/dev/null | awk 'NR>1{print $1}'); do
|
|
|
|
n="$(pct config "$id" 2>/dev/null | grep -oP '^hostname:\s*\K\S+' || true)"
|
|
|
|
n="$($sudo_pfx pct config "$id" 2>/dev/null | grep -oP '^hostname:\s*\K\S+' || true)"
|
|
|
|
[[ "$n" == "$target" ]] && echo "lxc ${id} ${n}"
|
|
|
|
[[ "$n" == "$target" ]] && echo "lxc ${id} ${n}"
|
|
|
|
done
|
|
|
|
done
|
|
|
|
exit 0
|
|
|
|
exit 0
|
|
|
@@ -453,12 +464,12 @@ REMOTE_SCRIPT
|
|
|
|
if [[ "$kind" == "vm" ]]; then
|
|
|
|
if [[ "$kind" == "vm" ]]; then
|
|
|
|
# qm destroy has no --force to stop-then-destroy in one call (pct's
|
|
|
|
# qm destroy has no --force to stop-then-destroy in one call (pct's
|
|
|
|
# does) -- stop explicitly first if it's running.
|
|
|
|
# does) -- stop explicitly first if it's running.
|
|
|
|
if ssh "$ssh_target" "qm status ${id}" 2>/dev/null | grep -q running; then
|
|
|
|
if ssh "$ssh_target" "${sudo_prefix} qm status ${id}" 2>/dev/null | grep -q running; then
|
|
|
|
ssh "$ssh_target" "qm stop ${id}"
|
|
|
|
ssh "$ssh_target" "${sudo_prefix} qm stop ${id}"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
ssh "$ssh_target" "qm destroy ${id} --purge 1"
|
|
|
|
ssh "$ssh_target" "${sudo_prefix} qm destroy ${id} --purge 1"
|
|
|
|
else
|
|
|
|
else
|
|
|
|
ssh "$ssh_target" "pct destroy ${id} --force 1 --purge 1"
|
|
|
|
ssh "$ssh_target" "${sudo_prefix} pct destroy ${id} --force 1 --purge 1"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
fi
|
|
|
@@ -487,7 +498,7 @@ if [[ -z "$vmid" ]]; then
|
|
|
|
vmid="<next-free-vmid>"
|
|
|
|
vmid="<next-free-vmid>"
|
|
|
|
echo "[dry-run] would ask ${node} for the next free VMID (pvesh get /cluster/nextid)"
|
|
|
|
echo "[dry-run] would ask ${node} for the next free VMID (pvesh get /cluster/nextid)"
|
|
|
|
else
|
|
|
|
else
|
|
|
|
vmid="$(ssh "$ssh_target" "pvesh get /cluster/nextid" | tr -d '[:space:]')"
|
|
|
|
vmid="$(ssh "$ssh_target" "${sudo_prefix} pvesh get /cluster/nextid" | tr -d '[:space:]')"
|
|
|
|
echo "Auto-assigned VMID: ${vmid}"
|
|
|
|
echo "Auto-assigned VMID: ${vmid}"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
else
|
|
|
|
else
|
|
|
@@ -501,8 +512,8 @@ if [[ "$dry_run" -eq 0 ]]; then
|
|
|
|
# both. Any success here means something is already using this ID --
|
|
|
|
# both. Any success here means something is already using this ID --
|
|
|
|
# refuse to go anywhere near it. (Reconfiguring an existing resource is
|
|
|
|
# refuse to go anywhere near it. (Reconfiguring an existing resource is
|
|
|
|
# --modify's job, not this one's.)
|
|
|
|
# --modify's job, not this one's.)
|
|
|
|
if ssh "$ssh_target" "qm status ${vmid}" >/dev/null 2>&1 \
|
|
|
|
if ssh "$ssh_target" "${sudo_prefix} qm status ${vmid}" >/dev/null 2>&1 \
|
|
|
|
|| ssh "$ssh_target" "pct status ${vmid}" >/dev/null 2>&1; then
|
|
|
|
|| ssh "$ssh_target" "${sudo_prefix} pct status ${vmid}" >/dev/null 2>&1; then
|
|
|
|
echo "ERROR: VMID ${vmid} already exists on ${node}. Refusing to touch an" >&2
|
|
|
|
echo "ERROR: VMID ${vmid} already exists on ${node}. Refusing to touch an" >&2
|
|
|
|
echo "existing resource here -- use --modify to reconfigure it, pick a" >&2
|
|
|
|
echo "existing resource here -- use --modify to reconfigure it, pick a" >&2
|
|
|
|
echo "different --vmid, or omit it to auto-assign." >&2
|
|
|
|
echo "different --vmid, or omit it to auto-assign." >&2
|
|
|
@@ -702,15 +713,6 @@ REMOTE_SCRIPT
|
|
|
|
echo "Built on ${node}: ${remote_path}"
|
|
|
|
echo "Built on ${node}: ${remote_path}"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
else
|
|
|
|
else
|
|
|
|
# PROXMOX_SSH_USER defaults to root (env.sh), which needs no sudo and
|
|
|
|
|
|
|
|
# can't assume it's even installed on a minimal node -- only shell out
|
|
|
|
|
|
|
|
# through sudo when actually running as a non-root SSH user.
|
|
|
|
|
|
|
|
sudo_prefix="sudo"
|
|
|
|
|
|
|
|
sudo_display="sudo "
|
|
|
|
|
|
|
|
if [[ "$PROXMOX_SSH_USER" == "root" ]]; then
|
|
|
|
|
|
|
|
sudo_prefix=""
|
|
|
|
|
|
|
|
sudo_display=""
|
|
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [[ "$dry_run" -eq 1 ]]; then
|
|
|
|
if [[ "$dry_run" -eq 1 ]]; then
|
|
|
|
echo "[dry-run] would build on ${node}: nix build --no-use-registries --no-accept-flake-config${nix_opts_display} \\"
|
|
|
|
echo "[dry-run] would build on ${node}: nix build --no-use-registries --no-accept-flake-config${nix_opts_display} \\"
|
|
|
|
echo "[dry-run] .#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript"
|
|
|
|
echo "[dry-run] .#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript"
|
|
|
@@ -812,9 +814,9 @@ if [[ "$type" == "lxc" ]]; then
|
|
|
|
# hands the whole string to `ssh` as a single command for the *remote*
|
|
|
|
# hands the whole string to `ssh` as a single command for the *remote*
|
|
|
|
# shell to parse -- unquoted, that `;` would be read as a remote
|
|
|
|
# shell to parse -- unquoted, that `;` would be read as a remote
|
|
|
|
# command separator and silently truncate this into two commands.
|
|
|
|
# command separator and silently truncate this into two commands.
|
|
|
|
create_cmd="pct create ${vmid} ${iso_storage}:vztmpl/${remote_filename} --unprivileged ${unprivileged_flag} --features '${PROXMOX_DEFAULT_LXC_FEATURES}' --rootfs ${storage}:${local_disk_size} --hostname ${name} --cores ${cores} --memory ${memory} --swap ${local_swap} --net0 name=eth0,bridge=${bridge},ip=dhcp"
|
|
|
|
create_cmd="${sudo_prefix} pct create ${vmid} ${iso_storage}:vztmpl/${remote_filename} --unprivileged ${unprivileged_flag} --features '${PROXMOX_DEFAULT_LXC_FEATURES}' --rootfs ${storage}:${local_disk_size} --hostname ${name} --cores ${cores} --memory ${memory} --swap ${local_swap} --net0 name=eth0,bridge=${bridge},ip=dhcp"
|
|
|
|
remote "$create_cmd"
|
|
|
|
remote "$create_cmd"
|
|
|
|
remote "pct start ${vmid}"
|
|
|
|
remote "${sudo_prefix} pct start ${vmid}"
|
|
|
|
else
|
|
|
|
else
|
|
|
|
echo "==> Creating VM ${vmid} (${name})..."
|
|
|
|
echo "==> Creating VM ${vmid} (${name})..."
|
|
|
|
# pre-enrolled-keys=0 disables OVMF's Secure Boot key pre-enrollment --
|
|
|
|
# pre-enrolled-keys=0 disables OVMF's Secure Boot key pre-enrollment --
|
|
|
@@ -825,29 +827,29 @@ else
|
|
|
|
# without this flag Proxmox never creates the channel it listens on, so
|
|
|
|
# without this flag Proxmox never creates the channel it listens on, so
|
|
|
|
# `qm guest exec`/`qm agent` and the UI's IP-address display silently
|
|
|
|
# `qm guest exec`/`qm agent` and the UI's IP-address display silently
|
|
|
|
# never work for any VM this script creates.
|
|
|
|
# never work for any VM this script creates.
|
|
|
|
remote "qm create ${vmid} --name ${name} --memory ${memory} --cores ${cores} \
|
|
|
|
remote "${sudo_prefix} qm create ${vmid} --name ${name} --memory ${memory} --cores ${cores} \
|
|
|
|
--net0 virtio,bridge=${bridge} --bios ovmf --machine q35 --scsihw virtio-scsi-pci \
|
|
|
|
--net0 virtio,bridge=${bridge} --bios ovmf --machine q35 --scsihw virtio-scsi-pci \
|
|
|
|
--efidisk0 ${storage}:1,efitype=4m,pre-enrolled-keys=0 --agent enabled=1"
|
|
|
|
--efidisk0 ${storage}:1,efitype=4m,pre-enrolled-keys=0 --agent enabled=1"
|
|
|
|
|
|
|
|
|
|
|
|
if [[ "$dry_run" -eq 1 ]]; then
|
|
|
|
if [[ "$dry_run" -eq 1 ]]; then
|
|
|
|
echo "[dry-run] ssh ${ssh_target} -- qm importdisk ${vmid} ${remote_path} ${storage}"
|
|
|
|
echo "[dry-run] ssh ${ssh_target} -- ${sudo_display}qm importdisk ${vmid} ${remote_path} ${storage}"
|
|
|
|
echo "[dry-run] (would parse the resulting disk identifier from that output)"
|
|
|
|
echo "[dry-run] (would parse the resulting disk identifier from that output)"
|
|
|
|
echo "[dry-run] ssh ${ssh_target} -- qm set ${vmid} --scsi0 ${storage}:<parsed-disk-id>"
|
|
|
|
echo "[dry-run] ssh ${ssh_target} -- ${sudo_display}qm set ${vmid} --scsi0 ${storage}:<parsed-disk-id>"
|
|
|
|
else
|
|
|
|
else
|
|
|
|
importdisk_output="$(ssh "$ssh_target" "qm importdisk ${vmid} ${remote_path} ${storage}")"
|
|
|
|
importdisk_output="$(ssh "$ssh_target" "${sudo_prefix} qm importdisk ${vmid} ${remote_path} ${storage}")"
|
|
|
|
echo "$importdisk_output"
|
|
|
|
echo "$importdisk_output"
|
|
|
|
disk_id="$(echo "$importdisk_output" | grep -oP "(?<=Successfully imported disk as ')[^']+" | sed 's/^unused[0-9]*://')"
|
|
|
|
disk_id="$(echo "$importdisk_output" | grep -oP "(?<=Successfully imported disk as ')[^']+" | sed 's/^unused[0-9]*://')"
|
|
|
|
if [[ -z "$disk_id" ]]; then
|
|
|
|
if [[ -z "$disk_id" ]]; then
|
|
|
|
echo "ERROR: couldn't parse the imported disk identifier from qm importdisk's output above." >&2
|
|
|
|
echo "ERROR: couldn't parse the imported disk identifier from qm importdisk's output above." >&2
|
|
|
|
echo "The VM shell (${vmid}) and imported disk both exist -- finish attaching it by hand:" >&2
|
|
|
|
echo "The VM shell (${vmid}) and imported disk both exist -- finish attaching it by hand:" >&2
|
|
|
|
echo " ssh ${ssh_target} -- qm set ${vmid} --scsi0 ${storage}:<disk-id-from-output-above>" >&2
|
|
|
|
echo " ssh ${ssh_target} -- ${sudo_display}qm set ${vmid} --scsi0 ${storage}:<disk-id-from-output-above>" >&2
|
|
|
|
echo " ssh ${ssh_target} -- qm set ${vmid} --boot order=scsi0" >&2
|
|
|
|
echo " ssh ${ssh_target} -- ${sudo_display}qm set ${vmid} --boot order=scsi0" >&2
|
|
|
|
exit 1
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
remote "qm set ${vmid} --scsi0 ${disk_id}"
|
|
|
|
remote "${sudo_prefix} qm set ${vmid} --scsi0 ${disk_id}"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
remote "qm set ${vmid} --boot order=scsi0"
|
|
|
|
remote "${sudo_prefix} qm set ${vmid} --boot order=scsi0"
|
|
|
|
remote "qm start ${vmid}"
|
|
|
|
remote "${sudo_prefix} qm start ${vmid}"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
echo
|
|
|
|