tests: validate installer ISO on XCP-ng

This commit is contained in:
2026-04-04 19:06:48 +02:00
parent 604ad82f4f
commit 43c155bb9f
3 changed files with 553 additions and 25 deletions

View File

@@ -24,6 +24,8 @@ Fruix currently has:
- `fruix system install` - `fruix system install`
- a bootable Fruix-managed installer environment: - a bootable Fruix-managed installer environment:
- `fruix system installer` - `fruix system installer`
- a bootable Fruix-managed installer ISO:
- `fruix system installer-iso`
Validated boot modes still are: Validated boot modes still are:
@@ -36,45 +38,44 @@ The validated Phase 18 installation work currently uses:
## Latest completed achievement ## Latest completed achievement
### 2026-04-04 — Phase 18.2 completed ### 2026-04-04 — Phase 18.3 completed
Fruix now boots a minimal installer environment and installs a target system from inside it. Fruix now builds a bootable installer ISO, boots it, installs from it, and boots the installed target successfully.
Highlights: Highlights:
- added in `modules/fruix/system/freebsd.scm`: - added in `modules/fruix/system/freebsd.scm`:
- `installer-operating-system` - `operating-system-installer-iso-spec`
- `operating-system-installer-image-spec` - `materialize-installer-iso`
- `materialize-installer-image`
- added CLI support in `scripts/fruix.scm`: - added CLI support in `scripts/fruix.scm`:
- `fruix system installer` - `fruix system installer-iso`
- `--install-target-device DEVICE` - the installer ISO now carries:
- the installer image now carries: - a UEFI El Torito boot image
- its own installer closure - `/boot/root.img` as the installer mdroot payload
- the installer closure
- the selected target closure - the selected target closure
- the target store closure - the target runtime store closure needed for installation
- a staged target rootfs payload
- in-guest installer state/log/scripts - in-guest installer state/log/scripts
- validated workflow: - validated workflows:
- boot installer image in QEMU/UEFI/TCG - local QEMU/UEFI/TCG boot, install, and installed-target reboot
- reach installer over SSH - real XCP-ng VM boot, install, and installed-target reboot
- install target system onto second disk from inside the guest - a platform-specific installer detail is now recorded in-tree:
- boot the installed target successfully - QEMU ISO path installs onto `/dev/vtbd0`
- XCP-ng ISO path installs onto `/dev/ada0`
Validation: Validation:
- `PASS phase18-installer-environment` - `PASS phase18-installer-iso`
- regression re-checks: - `PASS phase18-installer-iso-xcpng`
- `PASS phase18-system-install`
- `PASS phase17-source-revisions-qemu`
Report: Report:
- `docs/reports/phase18-installer-environment-freebsd.md` - `docs/reports/phase18-installer-iso-freebsd.md`
Commit: Commits:
- `1d00907``Add Fruix bootable installer environment` - `1970c5c``system: add UEFI installer ISO builder`
- `604ad82``system: validate UEFI installer ISO boot path`
## Recent major milestones ## Recent major milestones

View File

@@ -163,7 +163,7 @@ installer_iso_install=ok
installed_target_boot=ok installed_target_boot=ok
``` ```
Notable ISO-specific validation detail: Notable QEMU-specific ISO validation detail:
- unlike the disk-image-style installer environment from Phase 18.2, the ISO boots from `cd0`, so the target virtio disk appears as: - unlike the disk-image-style installer environment from Phase 18.2, the ISO boots from `cd0`, so the target virtio disk appears as:
- `/dev/vtbd0` - `/dev/vtbd0`
@@ -194,6 +194,75 @@ The harness verified all of the following:
- `sshd` is running - `sshd` is running
- activation completed successfully - activation completed successfully
### Real XCP-ng validation
Added:
- `tests/system/run-phase18-installer-iso-xcpng.sh`
This harness validates the same installer-iso workflow on the approved real XCP-ng path:
- VM: `90490f2e-e8fc-4b7a-388e-5c26f0157289`
- ISO SR: `537a6219-8452-7cb5-8d56-5eed6910c7a2`
- target VDIs:
- `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- `7061d761-3639-4bec-87f7-2ba1af924eaa`
Because the current `xo-cli disk.import @=/path/to.iso` path returned an HTTP 500 error in this environment, the harness imports the ISO into the ISO SR via a temporary local HTTP URL, then inserts the resulting ISO VDI into the VM's CD drive.
Passing validation:
- `PASS phase18-installer-iso-xcpng`
Validated result summary:
```text
vm_id=90490f2e-e8fc-4b7a-388e-5c26f0157289
iso_id=<temporary-imported-iso-vdi>
guest_ip=192.168.213.62
installer_state=done
installer_target_device=/dev/ada0
kern_disks=cd0 ada1 ada0
installer_run_current_system=/frx/store/16969e825dbb65b5c27180030d4a7d98821893460fb3dccdc863ff6156ed61e0-fruix-system-fruix-freebsd-installer
installer_sshd_status=running
target_run_current_system=/frx/store/a98d3af6a1afbc4a927d47cea6458d5a70747b051ed994e5d9ff1ae79c4f2b42-fruix-system-fruix-freebsd
target_sshd_status=running
target_shepherd_status=running
```
Important XCP-ng-specific details:
- the installer ISO still boots from:
- `cd0`
- on this Xen HVM path, the primary target disk is exposed through Xen block front as `xbd0` and appears to FreeBSD as:
- `/dev/ada0`
- therefore the XCP-ng installer-iso path must target:
- `/dev/ada0`
rather than QEMU's:
- `/dev/vtbd0`
- the visible EFI console can appear to stop at:
- `console vidconsole is unavailable`
but boot still continues and the installer becomes reachable over SSH; that message was not the actual failure mode on XCP-ng
The harness verified all of the following on the real VM path:
1. `fruix system installer-iso` builds a bootable ISO with `--install-target-device /dev/ada0`
2. the ISO can be imported into the operator-approved ISO SR and attached to the approved VM
3. the VM boots the Fruix installer ISO successfully under UEFI
4. the installer environment becomes reachable over SSH
5. inside the installer guest:
- `kern.disks` includes `cd0` and `ada0`
- `/run/current-system` points at the installer closure
- the installer reaches `state=done`
6. the installed target on `ada0` is partitioned and formatted correctly
7. after ejecting the ISO and rebooting, the installed target boots successfully on the same XCP-ng VM
8. after target boot:
- `/run/current-system` points at the target closure
- shepherd is running
- `sshd` is running
- activation completed successfully
- `/var/lib/fruix/install.scm` still records the materialized source store provenance
## Result ## Result
Phase 18.3 is complete. Phase 18.3 is complete.
@@ -202,4 +271,7 @@ Fruix now has a validated bootable UEFI installer ISO on FreeBSD that can:
- boot into a Fruix-managed installer environment from ISO media - boot into a Fruix-managed installer environment from ISO media
- perform the non-interactive installation flow onto a target disk - perform the non-interactive installation flow onto a target disk
- and boot the installed target successfully - boot the installed target successfully
- and do so on both:
- local `QEMU/UEFI/TCG`
- the approved real `XCP-ng` VM path

View File

@@ -0,0 +1,455 @@
#!/bin/sh
set -eu
repo_root=${PROJECT_ROOT:-$(pwd)}
script_dir=$(CDPATH= cd -- "$(dirname "$0")" && pwd)
fruix_cmd=$repo_root/bin/fruix
vm_id=${VM_ID:-90490f2e-e8fc-4b7a-388e-5c26f0157289}
iso_sr_id=${ISO_SR_ID:-537a6219-8452-7cb5-8d56-5eed6910c7a2}
os_template=${OS_TEMPLATE:-$script_dir/phase18-installer-target-operating-system.scm.in}
system_name=${SYSTEM_NAME:-phase18-target-operating-system}
store_dir=${STORE_DIR:-/frx/store}
install_target_device=${INSTALL_TARGET_DEVICE:-/dev/ada0}
installer_root_size=${INSTALLER_ROOT_SIZE:-}
base_name=${BASE_NAME:-phase18-installer-iso-target}
base_version_label=${BASE_VERSION_LABEL:-15.0-STABLE-installer-iso-target}
base_release=${BASE_RELEASE:-15.0-STABLE}
base_branch=${BASE_BRANCH:-stable/15}
source_name=${SOURCE_NAME:-stable15-installer-iso-target-source}
source_ref=${SOURCE_REF:-stable/15}
source_commit=${SOURCE_COMMIT:-332708a606f6bf0841c1d4a74c0d067f5640fe89}
declared_source_root=${DECLARED_SOURCE_ROOT:-/var/empty/fruix-unused-source-root-installer-iso-target}
metadata_target=${METADATA_OUT:-}
root_authorized_key_file=${ROOT_AUTHORIZED_KEY_FILE:-$HOME/.ssh/id_ed25519.pub}
root_ssh_private_key_file=${ROOT_SSH_PRIVATE_KEY_FILE:-$HOME/.ssh/id_ed25519}
iso_http_port=${ISO_HTTP_PORT:-$(jot -r 1 18080 18999)}
keep_imported_iso=${KEEP_IMPORTED_ISO:-0}
[ -x "$fruix_cmd" ] || {
echo "fruix command is not executable: $fruix_cmd" >&2
exit 1
}
[ -f "$os_template" ] || {
echo "missing operating-system template: $os_template" >&2
exit 1
}
[ -f "$root_authorized_key_file" ] || {
echo "missing root authorized key file: $root_authorized_key_file" >&2
exit 1
}
[ -f "$root_ssh_private_key_file" ] || {
echo "missing root SSH private key file: $root_ssh_private_key_file" >&2
exit 1
}
command -v node >/dev/null 2>&1 || {
echo "node is required to serve the ISO for XO import" >&2
exit 1
}
command -v xo-cli >/dev/null 2>&1 || {
echo "xo-cli is required" >&2
exit 1
}
command -v jq >/dev/null 2>&1 || {
echo "jq is required" >&2
exit 1
}
cleanup=0
if [ -n "${WORKDIR:-}" ]; then
workdir=$WORKDIR
mkdir -p "$workdir"
else
workdir=$(mktemp -d /tmp/fruix-phase18-installer-iso-xcpng.XXXXXX)
cleanup=1
fi
if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then
cleanup=0
fi
target_os_file=$workdir/phase18-installer-iso-target-operating-system.scm
installer_out=$workdir/installer-iso.txt
metadata_file=$workdir/phase18-installer-iso-xcpng-metadata.txt
vm_info_json=$workdir/vm-info.json
vdi_info_json=$workdir/vdi-info.json
installer_log_file=$workdir/installer.log
installer_gpart_file=$workdir/installer-gpart.txt
installer_kern_disks_file=$workdir/installer-kern-disks.txt
target_install_metadata_file=$workdir/target-install.scm
server_script=$workdir/serve-iso.mjs
server_log=$workdir/serve-iso.log
arp_scan_log=$workdir/arp-scan.log
server_pid=
imported_iso_id=
imported_iso_name=
guest_ip=
vm_mac=
cleanup_workdir() {
if [ -n "$server_pid" ]; then
kill "$server_pid" >/dev/null 2>&1 || true
fi
xo-cli vm.ejectCd id=$vm_id >/dev/null 2>&1 || true
if [ -n "$imported_iso_id" ] && [ "$keep_imported_iso" -ne 1 ]; then
xo-cli vdi.delete id=$imported_iso_id >/dev/null 2>&1 || true
fi
if [ "$cleanup" -eq 1 ]; then
rm -rf "$workdir"
fi
}
trap cleanup_workdir EXIT INT TERM
root_authorized_key=$(tr -d '\n' < "$root_authorized_key_file")
sed \
-e "s|__BASE_NAME__|$base_name|g" \
-e "s|__BASE_VERSION_LABEL__|$base_version_label|g" \
-e "s|__BASE_RELEASE__|$base_release|g" \
-e "s|__BASE_BRANCH__|$base_branch|g" \
-e "s|__SOURCE_NAME__|$source_name|g" \
-e "s|__SOURCE_REF__|$source_ref|g" \
-e "s|__SOURCE_COMMIT__|$source_commit|g" \
-e "s|__DECLARED_SOURCE_ROOT__|$declared_source_root|g" \
-e "s|__ROOT_AUTHORIZED_KEY__|$root_authorized_key|g" \
"$os_template" > "$target_os_file"
action_env() {
sudo env \
HOME="$HOME" \
GUILE_AUTO_COMPILE=0 \
FRUIX_FREEBSD_BUILD_JOBS="${FRUIX_FREEBSD_BUILD_JOBS:-8}" \
GUIX_SOURCE_DIR="${GUIX_SOURCE_DIR:-$HOME/repos/guix}" \
GUILE_BIN="${GUILE_BIN:-/tmp/guile-freebsd-validate-install/bin/guile}" \
GUILE_EXTRA_PREFIX="${GUILE_EXTRA_PREFIX:-/tmp/guile-gnutls-freebsd-validate-install}" \
SHEPHERD_PREFIX="${SHEPHERD_PREFIX:-/tmp/shepherd-freebsd-validate-install}" \
"$@"
}
if [ -n "$installer_root_size" ]; then
action_env "$fruix_cmd" system installer-iso "$target_os_file" \
--system "$system_name" \
--store "$store_dir" \
--install-target-device "$install_target_device" \
--root-size "$installer_root_size" >"$installer_out"
else
action_env "$fruix_cmd" system installer-iso "$target_os_file" \
--system "$system_name" \
--store "$store_dir" \
--install-target-device "$install_target_device" >"$installer_out"
fi
field() {
sed -n "s/^$1=//p" "$installer_out" | tail -n 1
}
iso_store_path=$(field iso_store_path)
installer_iso_image=$(field iso_image)
installer_boot_efi_image=$(field boot_efi_image)
installer_root_image=$(field root_image)
installer_closure_path=$(field installer_closure_path)
target_closure_path=$(field target_closure_path)
installer_host_name=$(field installer_host_name)
install_target_device_out=$(field install_target_device)
installer_state_path=$(field installer_state_path)
installer_log_path=$(field installer_log_path)
iso_volume_label=$(field iso_volume_label)
root_size_out=$(field root_size)
freebsd_source_kind_out=$(field freebsd_source_kind)
freebsd_source_ref_out=$(field freebsd_source_ref)
freebsd_source_commit_out=$(field freebsd_source_commit)
freebsd_source_file=$(field freebsd_source_file)
freebsd_source_materializations_file=$(field freebsd_source_materializations_file)
materialized_source_store_count=$(field materialized_source_store_count)
materialized_source_stores=$(field materialized_source_stores)
host_base_store_count=$(field host_base_store_count)
native_base_store_count=$(field native_base_store_count)
native_base_stores=$(field native_base_stores)
store_item_count=$(field store_item_count)
target_store_item_count=$(field target_store_item_count)
installer_store_item_count=$(field installer_store_item_count)
store_layout_file=$(field store_layout_file)
[ -d "$iso_store_path" ] || { echo "missing installer ISO store path: $iso_store_path" >&2; exit 1; }
[ -f "$installer_iso_image" ] || { echo "missing installer ISO image: $installer_iso_image" >&2; exit 1; }
[ -f "$installer_boot_efi_image" ] || { echo "missing installer EFI boot image: $installer_boot_efi_image" >&2; exit 1; }
[ -f "$installer_root_image" ] || { echo "missing installer root image: $installer_root_image" >&2; exit 1; }
[ -n "$installer_closure_path" ] || { echo "missing installer closure path" >&2; exit 1; }
[ -n "$target_closure_path" ] || { echo "missing target closure path" >&2; exit 1; }
[ "$install_target_device_out" = "$install_target_device" ] || { echo "unexpected install target device: $install_target_device_out" >&2; exit 1; }
[ "$installer_host_name" = fruix-freebsd-installer ] || { echo "unexpected installer host name: $installer_host_name" >&2; exit 1; }
[ -n "$iso_volume_label" ] || { echo "missing ISO volume label" >&2; exit 1; }
[ "$freebsd_source_kind_out" = git ] || { echo "unexpected source kind: $freebsd_source_kind_out" >&2; exit 1; }
[ "$freebsd_source_ref_out" = "$source_ref" ] || { echo "unexpected source ref: $freebsd_source_ref_out" >&2; exit 1; }
[ "$freebsd_source_commit_out" = "$source_commit" ] || { echo "unexpected source commit: $freebsd_source_commit_out" >&2; exit 1; }
[ "$materialized_source_store_count" = 1 ] || { echo "unexpected materialized source store count: $materialized_source_store_count" >&2; exit 1; }
[ "$host_base_store_count" = 0 ] || { echo "expected zero host base stores, got: $host_base_store_count" >&2; exit 1; }
[ "$native_base_store_count" = 3 ] || { echo "expected three native base stores, got: $native_base_store_count" >&2; exit 1; }
[ -f "$freebsd_source_file" ] || { echo "missing freebsd source file: $freebsd_source_file" >&2; exit 1; }
[ -f "$freebsd_source_materializations_file" ] || { echo "missing source materializations file: $freebsd_source_materializations_file" >&2; exit 1; }
[ -f "$store_layout_file" ] || { echo "missing store layout file: $store_layout_file" >&2; exit 1; }
case "$materialized_source_stores" in
/frx/store/*-freebsd-source-$source_name) : ;;
*) echo "unexpected materialized source store path: $materialized_source_stores" >&2; exit 1 ;;
esac
[ "$store_item_count" -ge "$target_store_item_count" ] || { echo "combined store item count smaller than target store item count" >&2; exit 1; }
[ "$installer_store_item_count" -ge 1 ] || { echo "expected installer store items" >&2; exit 1; }
xo-cli list-objects id=$vm_id >"$vm_info_json"
vdi_id=$(xo-cli list-objects type=VBD | jq -r '.[] | select(.VM=="'$vm_id'" and .is_cd_drive==false and .position=="0") | .VDI' | head -n 1)
secondary_vdi_id=$(xo-cli list-objects type=VBD | jq -r '.[] | select(.VM=="'$vm_id'" and .is_cd_drive==false and .position=="1") | .VDI' | head -n 1)
[ -n "$vdi_id" ] || { echo "failed to discover primary target VDI for VM $vm_id" >&2; exit 1; }
xo-cli list-objects type=VDI | jq '[.[] | select(.id=="'$vdi_id'")]' >"$vdi_info_json"
vdi_size=$(jq -r '.[0].size' "$vdi_info_json")
[ -n "$vdi_size" ] || { echo "failed to discover VDI size for $vdi_id" >&2; exit 1; }
vif_id=$(jq -r '.[0].VIFs[0]' "$vm_info_json")
if [ -n "$vif_id" ] && [ "$vif_id" != null ]; then
vm_mac=$(xo-cli list-objects type=VIF | jq -r '.[] | select(.id=="'$vif_id'") | .MAC' | tr 'A-Z' 'a-z')
else
vm_mac=
fi
[ -n "$vm_mac" ] || { echo "failed to discover VM MAC address" >&2; exit 1; }
host_interface=$(route -n get default | awk '/interface:/{print $2; exit}')
host_ip=$(ifconfig "$host_interface" | awk '/inet /{print $2; exit}')
subnet_prefix=${host_ip%.*}
cat >"$server_script" <<'EOF'
import { createServer } from 'node:http'
import { createReadStream, statSync } from 'node:fs'
const file = process.argv[2]
const port = Number(process.argv[3])
const size = statSync(file).size
createServer((req, res) => {
if (req.url !== '/installer.iso') {
res.statusCode = 404
res.end('not found\n')
return
}
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Length': size,
'Content-Disposition': 'attachment; filename="installer.iso"'
})
createReadStream(file).pipe(res)
}).listen(port, '0.0.0.0', () => {
console.log(`listening ${port}`)
})
EOF
node "$server_script" "$installer_iso_image" "$iso_http_port" >"$server_log" 2>&1 &
server_pid=$!
for _ in $(jot 50 1 50); do
if grep -q 'listening' "$server_log"; then
break
fi
sleep 1
if ! kill -0 "$server_pid" >/dev/null 2>&1; then
echo "temporary ISO HTTP server failed to start" >&2
cat "$server_log" >&2 || true
exit 1
fi
done
iso_import_url="http://$host_ip:$iso_http_port/installer.iso"
imported_iso_name="fruix-installer-iso-ada0-$(date +%s).iso"
imported_iso_id=$(xo-cli disk.import name="$imported_iso_name" sr="$iso_sr_id" type=iso url="$iso_import_url")
kill "$server_pid" >/dev/null 2>&1 || true
server_pid=
refresh_guest_ip() {
guest_ip=$(arp -an | awk -v mac="$vm_mac" 'tolower($4)==mac {gsub(/[()]/,"",$2); print $2; exit}')
}
ping_sweep() {
: >"$arp_scan_log"
for host in $(jot 254 1 254); do
ip=$subnet_prefix.$host
(
ping -c 1 -W 1000 "$ip" >/dev/null 2>&1 && echo "$ip" >>"$arp_scan_log"
) &
done
wait
}
ssh_guest() {
ssh -i "$root_ssh_private_key_file" \
-o BatchMode=yes \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o LogLevel=ERROR \
-o ConnectTimeout=5 \
root@"$guest_ip" "$@"
}
wait_for_guest_command() {
probe=$1
attempts=$2
delay=$3
guest_ip=
for attempt in $(jot "$attempts" 1 "$attempts"); do
refresh_guest_ip
if [ -z "$guest_ip" ]; then
ping_sweep
refresh_guest_ip
fi
if [ -n "$guest_ip" ]; then
if ssh_guest "$probe" >/dev/null 2>&1; then
return 0
fi
fi
sleep "$delay"
done
return 1
}
xo-cli vm.stop id=$vm_id force=true >/dev/null 2>&1 || true
xo-cli vm.insertCd id=$vm_id cd_id=$imported_iso_id force=true >"$workdir/insert-cd.out"
xo-cli vm.setBootOrder vm=$vm_id order=dcn >"$workdir/set-boot-order.out"
xo-cli vm.start id=$vm_id >"$workdir/vm-start-installer.out"
wait_for_guest_command 'test -e /var/lib/fruix/installer/state' 90 5 || {
echo "installer ISO guest never became reachable over SSH" >&2
exit 1
}
installer_state=missing
for attempt in $(jot 180 1 180); do
if ssh_guest 'test -e /var/lib/fruix/installer/state' >/dev/null 2>&1; then
installer_state=$(ssh_guest "cat '$installer_state_path' 2>/dev/null || echo missing")
[ "$installer_state" = done ] && break
fi
sleep 5
done
installer_run_current_system=$(ssh_guest 'readlink /run/current-system')
installer_sshd_status=$(ssh_guest 'service sshd onestatus >/dev/null 2>&1 && echo running || echo stopped')
installer_log=$(ssh_guest "cat '$installer_log_path' 2>/dev/null || true")
installer_target_device=$(ssh_guest 'cat /var/lib/fruix/installer/target-device')
installer_kern_disks=$(ssh_guest 'sysctl -n kern.disks')
installer_gpart=$(ssh_guest 'gpart show ada0')
installer_esp_fstype=$(ssh_guest 'fstyp /dev/ada0p1')
installer_root_fstype=$(ssh_guest 'fstyp /dev/ada0p2')
printf '%s\n' "$installer_log" >"$installer_log_file"
printf '%s\n' "$installer_kern_disks" >"$installer_kern_disks_file"
printf '%s\n' "$installer_gpart" >"$installer_gpart_file"
[ "$installer_state" = done ] || { echo "installer ISO environment did not finish installation: $installer_state" >&2; exit 1; }
[ "$installer_run_current_system" = "/frx/store/$(basename "$installer_closure_path")" ] || { echo "unexpected installer current-system target: $installer_run_current_system" >&2; exit 1; }
[ "$installer_sshd_status" = running ] || { echo "installer sshd is not running" >&2; exit 1; }
[ "$installer_target_device" = "$install_target_device" ] || { echo "unexpected installer target device in guest: $installer_target_device" >&2; exit 1; }
[ "$installer_esp_fstype" = msdosfs ] || { echo "unexpected target ESP filesystem: $installer_esp_fstype" >&2; exit 1; }
[ "$installer_root_fstype" = ufs ] || { echo "unexpected target root filesystem: $installer_root_fstype" >&2; exit 1; }
case "$installer_log" in
*fruix-installer:done*) : ;;
*) echo "installer log does not show completion" >&2; exit 1 ;;
esac
case "$installer_kern_disks" in
*cd0*ada0*) : ;;
*) echo "unexpected installer kern.disks output: $installer_kern_disks" >&2; exit 1 ;;
esac
case "$installer_gpart" in
*ada0*GPT*) : ;;
*) echo "unexpected gpart output for ada0" >&2; exit 1 ;;
esac
xo-cli vm.ejectCd id=$vm_id >"$workdir/eject-cd.out"
xo-cli vm.restart id=$vm_id force=true >"$workdir/vm-restart-target.out"
wait_for_guest_command 'test -f /var/lib/fruix/ready' 120 5 || {
echo "installed target never became reachable over SSH" >&2
exit 1
}
target_run_current_system=$(ssh_guest 'readlink /run/current-system')
target_shepherd_status=$(ssh_guest '/usr/local/etc/rc.d/fruix-shepherd onestatus >/dev/null 2>&1 && echo running || echo stopped')
target_sshd_status=$(ssh_guest 'service sshd onestatus >/dev/null 2>&1 && echo running || echo stopped')
target_activate_log=$(ssh_guest 'cat /var/log/fruix-activate.log 2>/dev/null || true' | tr '\n' ' ')
target_install_metadata=$(ssh_guest 'cat /var/lib/fruix/install.scm')
printf '%s\n' "$target_install_metadata" >"$target_install_metadata_file"
[ "$target_run_current_system" = "/frx/store/$(basename "$target_closure_path")" ] || { echo "unexpected booted target current-system: $target_run_current_system" >&2; exit 1; }
[ "$target_shepherd_status" = running ] || { echo "fruix-shepherd is not running in booted target" >&2; exit 1; }
[ "$target_sshd_status" = running ] || { echo "sshd is not running in booted target" >&2; exit 1; }
case "$target_install_metadata" in
*"$target_closure_path"*) : ;;
*) echo "booted target metadata does not record target closure path" >&2; exit 1 ;;
esac
case "$target_install_metadata" in
*"$materialized_source_stores"*) : ;;
*) echo "booted target metadata does not record materialized source store" >&2; exit 1 ;;
esac
case "$target_install_metadata" in
*"$install_target_device"*) : ;;
*) echo "booted target metadata does not record install target device" >&2; exit 1 ;;
esac
case "$target_activate_log" in
*fruix-activate:done*) : ;;
*) echo "booted target activation log does not show success" >&2; exit 1 ;;
esac
cat >"$metadata_file" <<EOF
workdir=$workdir
vm_id=$vm_id
primary_vdi_id=$vdi_id
secondary_vdi_id=$secondary_vdi_id
vdi_size=$vdi_size
iso_sr_id=$iso_sr_id
imported_iso_id=$imported_iso_id
imported_iso_name=$imported_iso_name
guest_ip=$guest_ip
target_os_file=$target_os_file
installer_iso_store_path=$iso_store_path
installer_iso_image=$installer_iso_image
installer_boot_efi_image=$installer_boot_efi_image
installer_root_image=$installer_root_image
install_target_device=$install_target_device
installer_root_size=$root_size_out
iso_volume_label=$iso_volume_label
freebsd_source_kind=$freebsd_source_kind_out
freebsd_source_ref=$freebsd_source_ref_out
freebsd_source_commit=$freebsd_source_commit_out
freebsd_source_file=$freebsd_source_file
freebsd_source_materializations_file=$freebsd_source_materializations_file
materialized_source_store_count=$materialized_source_store_count
materialized_source_store=$materialized_source_stores
installer_closure_path=$installer_closure_path
target_closure_path=$target_closure_path
native_base_store_count=$native_base_store_count
native_base_stores=$native_base_stores
store_item_count=$store_item_count
target_store_item_count=$target_store_item_count
installer_store_item_count=$installer_store_item_count
installer_state_path=$installer_state_path
installer_log_path=$installer_log_path
installer_state=$installer_state
installer_run_current_system=$installer_run_current_system
installer_sshd_status=$installer_sshd_status
installer_target_device=$installer_target_device
installer_kern_disks=$installer_kern_disks
installer_log_file=$installer_log_file
installer_gpart_file=$installer_gpart_file
installer_kern_disks_file=$installer_kern_disks_file
target_esp_fstype=$installer_esp_fstype
target_root_fstype=$installer_root_fstype
target_run_current_system=$target_run_current_system
target_shepherd_status=$target_shepherd_status
target_sshd_status=$target_sshd_status
target_install_metadata_file=$target_install_metadata_file
boot_backend=xcp-ng-xo-cli
installer_iso_boot=ok
installer_iso_install=ok
installed_target_boot=ok
EOF
if [ -n "$metadata_target" ]; then
mkdir -p "$(dirname "$metadata_target")"
cp "$metadata_file" "$metadata_target"
fi
printf 'PASS phase18-installer-iso-xcpng\n'
printf 'Work directory: %s\n' "$workdir"
printf 'Metadata file: %s\n' "$metadata_file"
if [ -n "$metadata_target" ]; then
printf 'Copied metadata to: %s\n' "$metadata_target"
fi
printf '%s\n' '--- metadata ---'
cat "$metadata_file"