From 41f679b1cc6d0de69937b70c8fe4b98094575ce1 Mon Sep 17 00:00:00 2001 From: Eyal Kirson Date: Sat, 27 Jun 2026 15:30:57 +0300 Subject: [PATCH] fix(pxe): scout-loader honors static_pxe_url override instead of hardcoding carbide-static-pxe.forge The scout-loader hardcoded the rootfs (scout.squashfs) download host to `carbide-static-pxe.forge` and ignored the configurable static-pxe URL that the rest of the PXE flow already honors. The loader is the most critical early-boot fetch, yet it was the one place that did not respect the override. Repro: on a BlueField dpu-mode host, when the DPU's local resolver (dnsmasq/HBN) is down or the site uses IP-only/custom DNS, the scout-loader fails with: LOADER: Failed to gather root filesystem information: curl: (6) Could not resolve host: carbide-static-pxe.forge so the host never finishes discovery -- even though the static-pxe server is reachable by IP and a `static_pxe_url` override is configured. Fix: - crates/api-core/src/ipxe.rs: append `static_pxe_url=${base-url}` to the scout.efi kernel command line (x86_64 and aarch64 host). `${base-url}` is already set by the wrapping `pxe` template to the resolved `{{ static_pxe_url }}/public/blobs/` (which honors external_static_pxe_url / static_pxe_url_override), and iPXE expands it before booting, so the scout kernel receives the resolved static-pxe base. - pxe/common_files/scout-loader-rclocal: parse `static_pxe_url=` from /proc/cmdline and build the rootfs URL from it; fall back to the existing `http://carbide-static-pxe.forge` host when it is unset. This mirrors the existing idiom in check-scout-updates.sh, which already derives its base from a kernel cmdline arg with the same hostname fallback. Backward-compatible: when no override is configured the loader falls back to the original `carbide-static-pxe.forge` URL, so behavior is unchanged for existing deployments. The explicit `newrootfs=` override continues to take precedence. Co-Authored-By: Claude Opus 4.8 --- crates/api-core/src/ipxe.rs | 10 ++++++++-- pxe/common_files/scout-loader-rclocal | 17 +++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/crates/api-core/src/ipxe.rs b/crates/api-core/src/ipxe.rs index 0450725fc3..d7b6381c3b 100644 --- a/crates/api-core/src/ipxe.rs +++ b/crates/api-core/src/ipxe.rs @@ -207,7 +207,10 @@ impl PxeInstructions { if machine_type == MachineType::Host || machine_type == MachineType::PredictedHost { InstructionGenerator { kernel: "${base-url}/internal/aarch64/scout.efi".to_string(), - command_line: format!("mac={mac_address} console=tty0 console={console},115200 pci=realloc=off iommu=off cli_cmd=auto-detect machine_id={machine_interface_id} server_uri=[api_url] pxe_uri=[pxe_url]"), + // static_pxe_url passes the (already iPXE-expanded) static-pxe + // base to the scout-loader so it fetches scout.squashfs from the + // configured static-pxe server instead of hardcoding a hostname. + command_line: format!("mac={mac_address} console=tty0 console={console},115200 pci=realloc=off iommu=off cli_cmd=auto-detect machine_id={machine_interface_id} server_uri=[api_url] pxe_uri=[pxe_url] static_pxe_url=${{base-url}}"), initrd: None, } } @@ -223,7 +226,10 @@ impl PxeInstructions { rpc::MachineArchitecture::X86 => { InstructionGenerator { kernel: "${base-url}/internal/x86_64/scout.efi".to_string(), - command_line: format!("mac={mac_address} console=tty0 console={console},115200 pci=realloc=off iommu=off cli_cmd=auto-detect machine_id={machine_interface_id} server_uri=[api_url] pxe_uri=[pxe_url]"), + // static_pxe_url passes the (already iPXE-expanded) static-pxe + // base to the scout-loader so it fetches scout.squashfs from the + // configured static-pxe server instead of hardcoding a hostname. + command_line: format!("mac={mac_address} console=tty0 console={console},115200 pci=realloc=off iommu=off cli_cmd=auto-detect machine_id={machine_interface_id} server_uri=[api_url] pxe_uri=[pxe_url] static_pxe_url=${{base-url}}"), initrd: None, } } diff --git a/pxe/common_files/scout-loader-rclocal b/pxe/common_files/scout-loader-rclocal index d6e9721248..3aa672590f 100755 --- a/pxe/common_files/scout-loader-rclocal +++ b/pxe/common_files/scout-loader-rclocal @@ -39,13 +39,26 @@ send_msg() { done } -# Default image will be scout, from the nginx container +# Default image will be scout, from the nginx container. +# Resolution order for the rootfs URL: +# 1. newrootfs= on the kernel cmdline (explicit override). +# 2. static_pxe_url= on the kernel cmdline, populated by NICo Core from +# the configured static-pxe URL (honors external_static_pxe_url overrides). +# 3. Backward-compatible fallback to the carbide-static-pxe.forge hostname. if [ "$(grep -c 'newrootfs=' /proc/cmdline)" -eq 1 ] then newrootfsurl=$(cat /proc/cmdline | sed -e 's/.*newrootfs=//' -e 's/ .*//') else arch=$(uname -m) - newrootfsurl="http://carbide-static-pxe.forge/public/blobs/internal/${arch}/scout.squashfs" + if [ "$(grep -c 'static_pxe_url=' /proc/cmdline)" -eq 1 ] + then + # static_pxe_url already points at .../public/blobs (NICo Core base-url); + # strip any trailing slash before appending the rootfs path. + static_pxe_url=$(cat /proc/cmdline | sed -e 's/.*static_pxe_url=//' -e 's/ .*//' -e 's:/*$::') + newrootfsurl="${static_pxe_url}/internal/${arch}/scout.squashfs" + else + newrootfsurl="http://carbide-static-pxe.forge/public/blobs/internal/${arch}/scout.squashfs" + fi fi # Respect the console info from the kernel command line