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
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,14 @@ jobs:
if [[ "${ROCKY_MINOR}" == "9.6" ]]; then
REPO_ROOT="https://dl.rockylinux.org/vault/rocky/${ROCKY_MINOR}"
elif [[ "${ROCKY_MINOR}" == "9.7" ]]; then
REPO_ROOT="https://dl.rockylinux.org/pub/rocky/${ROCKY_MINOR}"
REPO_ROOT="https://dl.rockylinux.org/vault/rocky/${ROCKY_MINOR}"
else
echo "[ERR] Unsupported Rocky minor for V2K build: ${ROCKY_MINOR}" >&2
exit 2
fi

# Keep V2K build inputs pinned to the intended Rocky minor.
# 9.6 uses vault; 9.7 uses the normal public tree.
# 9.6 and 9.7 use vault because fixed minor trees may be removed from pub.
cat > "/etc/yum.repos.d/${REPO_PREFIX}.repo" <<EOF
[${REPO_PREFIX}-baseos]
name=Rocky Linux ${ROCKY_MINOR} - BaseOS
Expand Down Expand Up @@ -338,7 +338,7 @@ jobs:
if [[ "${ROCKY_MINOR}" == "9.6" ]]; then
REPO_ROOT="https://dl.rockylinux.org/vault/rocky/${ROCKY_MINOR}"
elif [[ "${ROCKY_MINOR}" == "9.7" ]]; then
REPO_ROOT="https://dl.rockylinux.org/pub/rocky/${ROCKY_MINOR}"
REPO_ROOT="https://dl.rockylinux.org/vault/rocky/${ROCKY_MINOR}"
else
echo "[ERR] Unsupported Rocky minor for V2K build: ${ROCKY_MINOR}" >&2
exit 2
Expand Down Expand Up @@ -577,7 +577,7 @@ jobs:
if [[ "${ROCKY_MINOR}" == "9.6" ]]; then
REPO_ROOT="https://dl.rockylinux.org/vault/rocky/${ROCKY_MINOR}"
elif [[ "${ROCKY_MINOR}" == "9.7" ]]; then
REPO_ROOT="https://dl.rockylinux.org/pub/rocky/${ROCKY_MINOR}"
REPO_ROOT="https://dl.rockylinux.org/vault/rocky/${ROCKY_MINOR}"
else
echo "[ERR] Unsupported Rocky minor for N2K build: ${ROCKY_MINOR}" >&2
exit 2
Expand Down
2 changes: 2 additions & 0 deletions bin/ablestack_v2k.sh
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ Options:
--winpe-timeout <SEC> Default: 600
--linux-bootstrap Default: auto for Linux guests
--no-linux-bootstrap
--bootstrap-fallback sata|off Default: sata
--no-bootstrap-fallback Same as --bootstrap-fallback off
--safe-mode Default: off
--force-cleanup Default: off

Expand Down
8 changes: 8 additions & 0 deletions completions/ablestack_v2k
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ _ablestack_v2k()
;;

cutover)
case "${prev}" in
--bootstrap-fallback)
COMPREPLY=( $(compgen -W "sata off" -- "${cur}") )
return 0
;;
esac

local -a cutover_opts=(
--shutdown --shutdown-force --shutdown-timeout
--define-only --apply --start
Expand All @@ -272,6 +279,7 @@ _ablestack_v2k()
--winpe-bootstrap --no-winpe-bootstrap
--winpe-iso --virtio-iso --winpe-timeout
--linux-bootstrap --no-linux-bootstrap
--bootstrap-fallback --no-bootstrap-fallback
--safe-mode
--force-cleanup
--target-provider
Expand Down
148 changes: 120 additions & 28 deletions lib/v2k/engine.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2540,6 +2540,65 @@ v2k_cloud_windows_winpe_bootstrap() {
v2k_windows_winpe_bootstrap_libvirt "${tmp_manifest}" "${tmp_vm}" "${winpe_iso}" "${virtio_iso}" "${winpe_timeout}" 1
}

v2k_bootstrap_fallback_enabled() {
local policy="${1:-sata}"
case "${policy}" in
sata|auto|"") return 0 ;;
off|none|false|0) return 1 ;;
*)
echo "Invalid bootstrap fallback policy: ${policy}" >&2
return 2
;;
esac
}

v2k_select_bootstrap_fallback() {
local manifest="$1" phase="$2" code="$3" reason="$4" policy="${5:-sata}"
local bus="sata"

if ! v2k_bootstrap_fallback_enabled "${policy}"; then
return 1
fi

v2k_manifest_runtime_set "${manifest}" ".bootstrap_fallback" \
"$(jq -nc \
--arg bus "${bus}" \
--arg phase "${phase}" \
--arg reason "${reason}" \
--argjson code "${code}" \
'{enabled:true,bus:$bus,phase:$phase,code:$code,reason:$reason}')" || true
v2k_event WARN "cutover" "" "bootstrap_fallback_selected" \
"$(jq -nc \
--arg bus "${bus}" \
--arg phase "${phase}" \
--arg reason "${reason}" \
--argjson code "${code}" \
'{bus:$bus,phase:$phase,code:$code,reason:$reason}')" || true
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] ${reason}. Falling back to SATA disk controller."
fi
return 0
}

v2k_libvirt_redefine_with_bootstrap_fallback() {
local manifest="$1" vcpu="$2" memory="$3" bridge="$4" vlan="$5" network="$6"
local vm xml_path
vm="$(jq -r '.target.libvirt.name' "${manifest}")"
v2k_target_undefine_libvirt "${vm}" || true
if [[ -n "${bridge}" ]]; then
xml_path="$(v2k_target_generate_libvirt_xml "${manifest}" \
--vcpu "${vcpu}" --memory "${memory}" \
--bridge "${bridge}" $( [[ -n "${vlan}" ]] && echo --vlan "${vlan}" ) \
)"
else
xml_path="$(v2k_target_generate_libvirt_xml "${manifest}" \
--vcpu "${vcpu}" --memory "${memory}" \
--network "${network}" \
)"
fi
v2k_target_define_libvirt "${xml_path}"
}

v2k_cmd_cutover() {
v2k_require_manifest
v2k_load_runtime_flags_from_manifest
Expand All @@ -2561,6 +2620,7 @@ v2k_cmd_cutover() {
local vcpu_set=0 memory_set=0
local network="default" bridge="" vlan=""
local force_cleanup=0
local bootstrap_fallback="${V2K_BOOTSTRAP_FALLBACK:-sata}"

#Linux bootstrap (host-chroot initramfs rebuild)
local linux_bootstrap=-1
Expand Down Expand Up @@ -2600,6 +2660,18 @@ v2k_cmd_cutover() {
linux_bootstrap=0
shift 1
;;
--bootstrap-fallback)
bootstrap_fallback="${2:-}"
case "${bootstrap_fallback}" in
sata|auto|off|none|false|0) ;;
*) echo "Invalid --bootstrap-fallback: ${bootstrap_fallback}" >&2; exit 2 ;;
esac
shift 2
;;
--no-bootstrap-fallback)
bootstrap_fallback="off"
shift 1
;;
--target-provider) target_provider="${2:-}"; target_provider_arg_set=1; shift 2;;
--cloud-endpoint) cloud_endpoint="${2:-}"; cloud_config_arg_set=1; shift 2;;
--cloud-api-key) cloud_api_key="${2:-}"; shift 2;;
Expand Down Expand Up @@ -2794,19 +2866,21 @@ v2k_cmd_cutover() {
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Cloud Linux bootstrap(initramfs) requested (guestFamily=linuxGuest)"
fi
v2k_linux_bootstrap_initramfs "${V2K_MANIFEST}"
local rc=$?
if [[ "${rc}" -ne 0 ]]; then
if v2k_linux_bootstrap_initramfs "${V2K_MANIFEST}"; then
v2k_event INFO "cutover" "" "cloud_linux_bootstrap_done" "{}"
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Cloud Linux bootstrap(initramfs) done (guestFamily=linuxGuest)"
fi
else
local rc=$?
v2k_event ERROR "cutover" "" "cloud_linux_bootstrap_failed" "{\"code\":${rc}}"
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Cloud Linux bootstrap(initramfs) failed (guestFamily=linuxGuest)"
fi
echo "Cloud Linux bootstrap (initramfs rebuild) failed. code=${rc}" >&2
exit 74
fi
v2k_event INFO "cutover" "" "cloud_linux_bootstrap_done" "{}"
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Cloud Linux bootstrap(initramfs) done (guestFamily=linuxGuest)"
if ! v2k_select_bootstrap_fallback "${V2K_MANIFEST}" "linux_bootstrap" "${rc}" "Cloud Linux bootstrap(initramfs) failed" "${bootstrap_fallback}"; then
echo "Cloud Linux bootstrap (initramfs rebuild) failed. code=${rc}" >&2
exit 74
fi
fi
else
v2k_event INFO "cutover" "" "cloud_linux_bootstrap_skipped" "{\"reason\":\"cli_or_policy\"}"
Expand All @@ -2817,16 +2891,18 @@ v2k_cmd_cutover() {
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Cloud Windows WinPE bootstrap requested (guestFamily=windowsGuest)"
fi
v2k_cloud_windows_winpe_bootstrap "${winpe_iso}" "${virtio_iso}" "${winpe_timeout}"
local rc=$?
if [[ "${rc}" -ne 0 ]]; then
if v2k_cloud_windows_winpe_bootstrap "${winpe_iso}" "${virtio_iso}" "${winpe_timeout}"; then
v2k_event INFO "cutover" "" "cloud_winpe_bootstrap_done" "{}"
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Cloud Windows WinPE bootstrap done (guestFamily=windowsGuest)"
fi
else
local rc=$?
v2k_event ERROR "cutover" "" "cloud_winpe_bootstrap_failed" "{\"code\":${rc}}"
echo "Cloud Windows WinPE bootstrap failed. code=${rc}" >&2
exit "${rc}"
fi
v2k_event INFO "cutover" "" "cloud_winpe_bootstrap_done" "{}"
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Cloud Windows WinPE bootstrap done (guestFamily=windowsGuest)"
if ! v2k_select_bootstrap_fallback "${V2K_MANIFEST}" "winpe_bootstrap" "${rc}" "Cloud Windows WinPE bootstrap failed" "${bootstrap_fallback}"; then
echo "Cloud Windows WinPE bootstrap failed. code=${rc}" >&2
exit "${rc}"
fi
fi
fi

Expand Down Expand Up @@ -2876,19 +2952,21 @@ v2k_cmd_cutover() {
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Linux bootstrap(initramfs) requested (guestFamily=linuxGuest)"
fi
v2k_linux_bootstrap_initramfs "${V2K_MANIFEST}"
local rc=$?
if [[ "${rc}" -ne 0 ]]; then
if v2k_linux_bootstrap_initramfs "${V2K_MANIFEST}"; then
v2k_event INFO "cutover" "" "linux_bootstrap_done" "{}"
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Linux bootstrap(initramfs) done (guestFamily=linuxGuest)"
fi
else
local rc=$?
v2k_event ERROR "cutover" "" "linux_bootstrap_failed" "{\"code\":${rc}}"
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Linux bootstrap(initramfs) failed (guestFamily=linuxGuest)"
fi
echo "Linux bootstrap (initramfs rebuild) failed. code=${rc}" >&2
exit 74
fi
v2k_event INFO "cutover" "" "linux_bootstrap_done" "{}"
if [[ "${V2K_JSON_OUT:-0}" -ne 1 ]]; then
echo "[v2k] Linux bootstrap(initramfs) done (guestFamily=linuxGuest)"
if ! v2k_select_bootstrap_fallback "${V2K_MANIFEST}" "linux_bootstrap" "${rc}" "Linux bootstrap(initramfs) failed" "${bootstrap_fallback}"; then
echo "Linux bootstrap (initramfs rebuild) failed. code=${rc}" >&2
exit 74
fi
fi
else
v2k_event INFO "cutover" "" "linux_bootstrap_skipped" "{\"reason\":\"cli_or_policy\"}"
Expand Down Expand Up @@ -2941,7 +3019,21 @@ v2k_cmd_cutover() {
if [[ "${winpe_bootstrap}" -eq 1 ]]; then
local vm
vm="$(jq -r '.target.libvirt.name' "${V2K_MANIFEST}")"
v2k_windows_winpe_bootstrap_libvirt "${V2K_MANIFEST}" "${vm}" "${winpe_iso}" "${virtio_iso}" "${winpe_timeout}" 0
if v2k_windows_winpe_bootstrap_libvirt "${V2K_MANIFEST}" "${vm}" "${winpe_iso}" "${virtio_iso}" "${winpe_timeout}" 0; then
:
else
local rc=$?
v2k_event ERROR "cutover" "" "libvirt_winpe_bootstrap_failed" "{\"code\":${rc}}"
if v2k_select_bootstrap_fallback "${V2K_MANIFEST}" "winpe_bootstrap" "${rc}" "Windows WinPE bootstrap failed" "${bootstrap_fallback}"; then
if ! v2k_libvirt_redefine_with_bootstrap_fallback "${V2K_MANIFEST}" "${vcpu}" "${memory}" "${bridge}" "${vlan}" "${network}"; then
echo "libvirt redefine with SATA fallback failed." >&2
exit 65
fi
else
echo "Windows WinPE bootstrap failed. code=${rc}" >&2
exit "${rc}"
fi
fi
fi

# WinPE may trigger host-side auto-unmap on guest shutdown. Re-prepare persistent RBD maps before the final boot.
Expand Down
6 changes: 4 additions & 2 deletions lib/v2k/target_cloud.sh
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,10 @@ v2k_cloud_target_source_deploy_params_json() {
| ((.target.cloud.cpu_speed // "1000") | tostring) as $cpu_speed
| (($vm.firmware // "") | tostring | ascii_downcase) as $firmware
| (($vm.secure_boot // false) == true) as $secure_boot
| (controller(.disks[0].controller.type // "")) as $root_controller
| (controller((.disks[1:] // [] | map(.controller.type // "") | map(select((. | tostring | length) > 0)) | first) // "")) as $data_controller
| ((.runtime.bootstrap_fallback.bus // "") | tostring | ascii_downcase) as $fallback_bus
| ((.disks // []) | length) as $disk_count
| (if $fallback_bus == "sata" then "sata" else controller(.disks[0].controller.type // "") end) as $root_controller
| (if $fallback_bus == "sata" and $disk_count > 1 then "sata" else controller((.disks[1:] // [] | map(.controller.type // "") | map(select((. | tostring | length) > 0)) | first) // "") end) as $data_controller
| {}
+ (if $cpu > 0 then {"details[0].cpuNumber": ($cpu | floor | tostring)} else {} end)
+ {"details[0].cpuSpeed": $cpu_speed}
Expand Down
28 changes: 22 additions & 6 deletions lib/v2k/target_libvirt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,14 @@ _v2k_rbd_dev_path_from_uri() {

_v2k_target_disk_xml() {
local manifest="$1" idx="$2"
local path bus dev st
local path bus dev st fallback_bus
path="$(jq -r ".disks[$idx].transfer.target_path" "${manifest}")"
bus="$(_v2k_disk_bus_from_controller_type "$(jq -r ".disks[$idx].controller.type // empty" "${manifest}")")"
fallback_bus="$(jq -r '.runtime.bootstrap_fallback.bus // empty' "${manifest}" 2>/dev/null || true)"
if [[ "${fallback_bus}" == "sata" ]]; then
bus="sata"
else
bus="$(_v2k_disk_bus_from_controller_type "$(jq -r ".disks[$idx].controller.type // empty" "${manifest}")")"
fi
dev="sd$(_v2k_letter "$idx")"
st="$(jq -r '.target.storage.type // "file"' "${manifest}")"

Expand All @@ -98,6 +103,11 @@ _v2k_target_disk_xml() {
if [[ -z "${block_path}" ]]; then
block_path="$(_v2k_rbd_dev_path_from_uri "${path}")"
fi
local address_xml=""
if [[ "${bus}" != "sata" ]]; then
address_xml=" <alias name='scsi0-0-0-${idx}'/>
<address type='drive' controller='0' bus='0' target='0' unit='${idx}'/>"
fi
serial="$(printf ''%s'' "${path#rbd:}" | tr -cd ''[:alnum:]'' | cut -c1-20)"
[[ -n "${serial}" ]] || serial="rbd${idx}"
cat <<EOF
Expand All @@ -106,8 +116,7 @@ _v2k_target_disk_xml() {
<source dev='$(_v2k_escape_xml "${block_path}")' index='${idx}'/>
<target dev='${dev}' bus='${bus}'/>
<serial>${serial}</serial>
<alias name='scsi0-0-0-${idx}'/>
<address type='drive' controller='0' bus='0' target='0' unit='${idx}'/>
${address_xml}
</disk>
EOF
return 0
Expand Down Expand Up @@ -252,7 +261,14 @@ v2k_target_generate_libvirt_xml() {
fi

local disks_xml=""
local count
local fallback_bus controller_xml
fallback_bus="$(jq -r '.runtime.bootstrap_fallback.bus // empty' "${manifest}" 2>/dev/null || true)"
if [[ "${fallback_bus}" == "sata" ]]; then
controller_xml="<controller type='sata' index='0'/>"
else
controller_xml="<controller type='scsi' index='0' model='virtio-scsi'/>"
fi
local count i
count="$(jq -r '.disks | length' "${manifest}")"

for ((i=0;i<count;i++)); do
Expand Down Expand Up @@ -287,7 +303,7 @@ $(_v2k_target_disk_xml "${manifest}" "$i")"
<topology sockets='1' cores='${vcpu}' threads='1'/>
</cpu>
<devices>
<controller type='scsi' index='0' model='virtio-scsi'/>
${controller_xml}
${disks_xml}
${iface_xml}
${tpm_xml}
Expand Down
Loading