Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ layouts are understood and the algorithm follows from the hash length.
| --- | --- |
| `vm_import_disk <vmid> <image> <storage> [format]` | `qm disk import` (or `qm importdisk` on older hosts) with `DISK_IMPORT_FORMAT`; sets `VM_IMPORTED_DISK` to the volume Proxmox reported or the `unusedN` entry the import added. Never guesses a name. |
| `vm_claim_vmid` | Call right before `qm create`: re-checks `VMID`, which was picked before the download. An auto-picked ID another run took moves to the next free one and the disk names follow; an ID chosen in advanced settings stops with exit 205. |
| `vm_prompt_keyboard [default]` | Advanced-settings choice of the keyboard layout (Proxmox codes: `de`, `en-us`, `fr-ch`, …) into `VM_KEYBOARD`. |
| `vm_start_vm [label]` | Starts the VM when `START_VM=yes`. |
| `vm_wait_guest_agent <vmid> [timeout]` | Waits until the guest agent answers. |
| `vm_guest_exec <vmid> <timeout> [--] <cmd>...` | Runs a command through the guest agent; prints its stdout and returns its exit code (124: still running). |
Expand Down
2 changes: 2 additions & 0 deletions incus/vm-core.func
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,8 @@ vm_guest_exec() { _incus_vm_qm_only vm_guest_exec; }
vm_wait_for_ip() { _incus_vm_qm_only vm_wait_for_ip; }
# Incus VMs are named, so there is no numeric ID to claim.
vm_claim_vmid() { :; }
# Incus has no VNC keyboard setting; the console's layout is the client's.
vm_prompt_keyboard() { :; }

# The Proxmox counterpart picks a disk format from the storage type because qm
# importdisk has to be told one. Incus decides the on-pool layout itself and
Expand Down
131 changes: 131 additions & 0 deletions pve/vm-core.func
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,7 @@ vm_echo_default_settings() {
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}"
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}${VLAN:-Default}${CL}"
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}${MTU:-Default}${CL}"
echo -e "${DEFAULT}${BOLD}${DGN}Keyboard Layout: ${BGN}$(kb="$(vm_keyboard_default)"; echo "${kb:-Proxmox default}")${CL}"
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}${START_VM}${CL}"
# Shown on the default path too, so VERBOSE=yes is visible.
echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}${VERBOSE:-no}${CL}"
Expand Down Expand Up @@ -1557,6 +1558,121 @@ vm_prompt_vlan() {
done
}

# vm_keyboard_default
# VM_KEYBOARD, else the datacenter's keyboard setting (Proxmox layout codes:
# de, en-us, fr-ch, ...). Applied to the VM and to prepared images alike: the
# Y/Z swap in noVNC comes from the two not matching.
vm_keyboard_default() {
if [[ -n "${VM_KEYBOARD:-}" ]]; then
printf '%s' "$VM_KEYBOARD"
else
sed -n 's/^keyboard: *//p' /etc/pve/datacenter.cfg 2>/dev/null | head -n1 | tr -d '[:space:]'
fi
}

_VM_KEYBOARDS="de de-ch da en-gb en-us es fi fr fr-be fr-ca fr-ch hu is it ja lt mk nl no pl pt pt-br sl sv tr"

_vm_keyboard_label() {
case "$1" in
de) echo "German" ;;
de-ch) echo "German (Switzerland)" ;;
da) echo "Danish" ;;
en-gb) echo "English (UK)" ;;
en-us) echo "English (US)" ;;
es) echo "Spanish" ;;
fi) echo "Finnish" ;;
fr) echo "French" ;;
fr-be) echo "French (Belgium)" ;;
fr-ca) echo "French (Canada)" ;;
fr-ch) echo "French (Switzerland)" ;;
hu) echo "Hungarian" ;;
is) echo "Icelandic" ;;
it) echo "Italian" ;;
ja) echo "Japanese" ;;
lt) echo "Lithuanian" ;;
mk) echo "Macedonian" ;;
nl) echo "Dutch" ;;
no) echo "Norwegian" ;;
pl) echo "Polish" ;;
pt) echo "Portuguese" ;;
pt-br) echo "Portuguese (Brazil)" ;;
sl) echo "Slovenian" ;;
sv) echo "Swedish" ;;
tr) echo "Turkish" ;;
*) echo "$1" ;;
esac
}

# _vm_guest_keymap <proxmox code>
# Prints "<xkb layout> <xkb variant or -> <console keymap>" for the guest.
_vm_guest_keymap() {
case "$1" in
de) echo "de - de-latin1" ;;
de-ch) echo "ch - sg-latin1" ;;
da) echo "dk - dk" ;;
en-gb) echo "gb - uk" ;;
en-us) echo "us - us" ;;
es) echo "es - es" ;;
fi) echo "fi - fi" ;;
fr) echo "fr - fr" ;;
fr-be) echo "be - be-latin1" ;;
fr-ca) echo "ca - cf" ;;
fr-ch) echo "ch fr fr_CH-latin1" ;;
hu) echo "hu - hu" ;;
is) echo "is - is-latin1" ;;
it) echo "it - it" ;;
ja) echo "jp - jp106" ;;
lt) echo "lt - lt" ;;
mk) echo "mk - mk" ;;
nl) echo "nl - nl" ;;
no) echo "no - no" ;;
pl) echo "pl - pl" ;;
pt) echo "pt - pt-latin1" ;;
pt-br) echo "br - br-abnt2" ;;
sl) echo "si - slovene" ;;
sv) echo "se - sv-latin1" ;;
tr) echo "tr - trq" ;;
*) return 1 ;;
esac
}

_vm_apply_keyboard() {
local kb
kb="$(vm_keyboard_default)"
[[ -n "$kb" ]] || return 0
qm set "$VMID" --keyboard "$kb" >/dev/null 2>&1 ||
msg_warn "Could not set the keyboard layout ${kb} on VM ${VMID}"
return 0
}

# vm_prompt_keyboard [default]
vm_prompt_keyboard() {
local default="${1:-$(vm_keyboard_default)}" code state
local -a items=()
[[ " $_VM_KEYBOARDS " == *" $default "* ]] || default="en-us"
for code in $_VM_KEYBOARDS; do
state=OFF
[[ "$code" == "$default" ]] && state=ON
items+=("$code" "$(_vm_keyboard_label "$code")" "$state")
done
if vm_dialog radiolist "KEYBOARD LAYOUT" "Layout for the noVNC console and the guest" --cancel-button Exit-Script 22 60 14 "${items[@]}"; then
VM_KEYBOARD="$VM_DIALOG_RESULT"
echo -e "${DEFAULT}${BOLD}${DGN}Keyboard Layout: ${BGN}${VM_KEYBOARD}${CL}"
else
exit_script
fi
}

# vm_timezone_default
# VM_TIMEZONE, else the host's zone; empty when neither names a known zone.
vm_timezone_default() {
local tz="${VM_TIMEZONE:-}"
[[ -n "$tz" ]] || tz="$(cat /etc/timezone 2>/dev/null)"
[[ -n "$tz" ]] || tz="$(timedatectl show -p Timezone --value 2>/dev/null)"
[[ -n "$tz" && -f "/usr/share/zoneinfo/$tz" ]] && printf '%s' "$tz"
return 0
}

vm_prompt_mtu() {
local default_mtu="${1:-}"
local input_mtu
Expand Down Expand Up @@ -2009,6 +2125,20 @@ vm_prepare_cloud_image() {
'[ -f /etc/ssh/sshd_config ] || exit 0
sed -i "s/^#*PermitRootLogin.*/PermitRootLogin yes/;s/^#*PasswordAuthentication.*/PasswordAuthentication yes/" /etc/ssh/sshd_config'

local tz kb layout variant keymap
tz="$(vm_timezone_default)"
[[ -n "$tz" ]] && _vm_customize "timezone ${tz}" "$image" --timezone "$tz"
kb="$(vm_keyboard_default)"
if [[ -n "$kb" ]] && read -r layout variant keymap < <(_vm_guest_keymap "$kb"); then
[[ "$variant" == "-" ]] && variant=""
_vm_customize "keyboard ${kb}" "$image" --run-command "
mkdir -p /etc/default
[ -f /etc/default/keyboard ] && sed -i '/^XKBLAYOUT=/d;/^XKBVARIANT=/d' /etc/default/keyboard
printf 'XKBLAYOUT=\"%s\"\nXKBVARIANT=\"%s\"\n' '${layout}' '${variant}' >>/etc/default/keyboard
[ -f /etc/vconsole.conf ] && sed -i '/^KEYMAP=/d' /etc/vconsole.conf
printf 'KEYMAP=%s\n' '${keymap}' >>/etc/vconsole.conf"
fi

vm_enable_consoles "$image"
vm_install_guest_agent "$image" || true
_VM_PREPARE_COLLECTING=0
Expand Down Expand Up @@ -2171,6 +2301,7 @@ vm_boot_order() {
vm_mark_created() {
VM_CREATED=1
trap 'vm_post_create_error $LINENO "$BASH_COMMAND"' ERR
_vm_apply_keyboard
}

vm_post_create_error() {
Expand Down
18 changes: 18 additions & 0 deletions tests/vm-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,24 @@ assert_contains "$ERRORS" "already in use"
QM_EXISTS="" METHOD=default VMID=100
ok "vm_claim_vmid re-checks the ID right before qm create"

# ── keyboard and timezone ───────────────────────────────────────────────────
assert_eq "$(_vm_guest_keymap de)" "de - de-latin1" "German"
assert_eq "$(_vm_guest_keymap en-gb)" "gb - uk" "UK"
assert_eq "$(_vm_guest_keymap fr-ch)" "ch fr fr_CH-latin1" "Swiss French has a variant"
if _vm_guest_keymap klingon >/dev/null; then fail "an unknown layout must not map"; fi
reset_logs
VM_KEYBOARD=de VMID=100
assert_eq "$(vm_keyboard_default)" "de" "VM_KEYBOARD wins over the datacenter"
_vm_apply_keyboard
assert_contains "$LOG" "qm set 100 --keyboard de"
VM_KEYBOARD=""
VM_TIMEZONE=Europe/Berlin
assert_eq "$(vm_timezone_default)" "Europe/Berlin" "VM_TIMEZONE"
VM_TIMEZONE=Mars/Olympus
assert_eq "$(vm_timezone_default)" "" "an unknown zone is dropped"
unset VM_TIMEZONE
ok "keyboard layout maps to the guest and timezone defaults are validated"

# ── release and index discovery ─────────────────────────────────────────────
FORGE_JSON=""
_forge_release_json() {
Expand Down
Loading