particle-os/scripts/test-bootloader-virsh.sh
2025-08-10 20:24:11 -07:00

299 lines
8 KiB
Bash
Executable file

#!/bin/bash
set -euo pipefail
# Particle-OS Bootloader Test Script using virsh
# This script creates a bootable disk image and tests it with libvirt/virsh
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
BUILD_DIR="$PROJECT_ROOT/build"
TEST_DIR="$PROJECT_ROOT/test"
VM_NAME="particle-os-test"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check prerequisites
check_prerequisites() {
log_info "Checking prerequisites..."
local missing_tools=()
for tool in podman virsh virt-install qemu-img; do
if ! command -v "$tool" >/dev/null 2>&1; then
missing_tools+=("$tool")
fi
done
# Check for system tools in /usr/sbin
for tool in parted grub-install partprobe; do
if [ ! -x "/usr/sbin/$tool" ]; then
missing_tools+=("$tool")
fi
done
# Check if libvirt is running
if ! virsh list >/dev/null 2>&1; then
log_error "libvirt not accessible. Make sure libvirtd is running and you're in the libvirt group."
log_info "You may need to run: sudo systemctl start libvirtd && sudo usermod -a -G libvirt $USER"
exit 1
fi
if [ ${#missing_tools[@]} -gt 0 ]; then
log_error "Missing required tools: ${missing_tools[*]}"
log_info "Install with: sudo apt install libvirt-clients libvirt-daemon-system virtinst qemu-utils"
exit 1
fi
# Check if Phase 2 image exists
if ! podman image exists particle-os:phase2; then
log_error "Phase 2 image not found. Run 'just build-phase2' first."
exit 1
fi
log_success "All prerequisites satisfied"
}
# Create test directories
setup_test_environment() {
log_info "Setting up test environment..."
mkdir -p "$BUILD_DIR" "$TEST_DIR"
log_success "Test environment ready"
}
# Create bootable disk image
create_bootable_image() {
local image_size="4G"
local image_path="$TEST_DIR/particle-os-virsh.img"
log_info "Creating bootable disk image ($image_size)..."
# Create raw disk image
qemu-img create -f raw "$image_path" "$image_size"
# Partition the disk
echo "yes" | /usr/sbin/parted "$image_path" mklabel gpt
/usr/sbin/parted "$image_path" mkpart primary fat32 1MiB 512MiB
/usr/sbin/parted "$image_path" mkpart primary ext4 512MiB 100%
/usr/sbin/parted "$image_path" set 1 boot on
/usr/sbin/parted "$image_path" set 1 esp on
log_success "Disk image created: $image_path"
echo "$image_path"
}
# Mount and prepare the image
prepare_image() {
local image_path="$1"
log_info "Preparing bootable image..."
# Create loop device
log_info "Creating loop device for: $image_path"
local loop_dev
loop_dev=$(sudo losetup --find --show "$image_path")
if [ -z "$loop_dev" ]; then
log_error "Failed to create loop device"
exit 1
fi
log_info "Using loop device: $loop_dev"
# Wait for device nodes to appear
sleep 2
# Verify partition devices exist
if [ ! -b "${loop_dev}p1" ] || [ ! -b "${loop_dev}p2" ]; then
log_error "Partition devices not found. Creating them manually..."
sudo partprobe "$loop_dev"
sleep 3
# Check again
if [ ! -b "${loop_dev}p1" ] || [ ! -b "${loop_dev}p2" ]; then
log_error "Partition devices still not found after partprobe"
sudo losetup -d "$loop_dev" 2>/dev/null || true
exit 1
fi
fi
# Create filesystems
sudo mkfs.fat -F32 "${loop_dev}p1"
sudo mkfs.ext4 "${loop_dev}p2"
# Mount partitions
local mount_root="$TEST_DIR/mount"
local mount_boot="$mount_root/boot"
local mount_rootfs="$mount_root/rootfs"
mkdir -p "$mount_boot" "$mount_rootfs"
sudo mount "${loop_dev}p1" "$mount_boot"
sudo mount "${loop_dev}p2" "$mount_rootfs"
# Extract Phase 2 container to rootfs
log_info "Extracting Phase 2 container..."
podman create --name temp-phase2 particle-os:phase2
podman export temp-phase2 | sudo tar -x -C "$mount_rootfs"
podman rm temp-phase2
# Set up bootloader
log_info "Setting up bootloader..."
sudo /usr/sbin/grub-install --target=x86_64-efi --efi-directory="$mount_boot" --boot-directory="$mount_boot" --removable "$loop_dev"
# Create GRUB config
sudo tee "$mount_boot/grub/grub.cfg" > /dev/null <<EOF
set timeout=5
set default=0
menuentry "Particle-OS Phase 2" {
search --set=root --file /vmlinuz-6.12.38+deb13-amd64
linux /vmlinuz-6.12.38+deb13-amd64 root=/dev/vda2 rw console=ttyS0
initrd /initrd.img-6.12.38+deb13-amd64
}
EOF
# Cleanup mounts
sudo umount "$mount_boot" "$mount_rootfs"
sudo losetup -d "$loop_dev"
log_success "Image preparation complete"
}
# Create libvirt VM definition
create_vm_definition() {
local image_path="$1"
log_info "Creating libvirt VM definition..."
# Remove existing VM if it exists
if virsh list --all --name | grep -q "^$VM_NAME$"; then
log_info "Removing existing VM: $VM_NAME"
virsh destroy "$VM_NAME" 2>/dev/null || true
virsh undefine "$VM_NAME" 2>/dev/null || true
fi
# Create new VM using virt-install
log_info "Creating VM: $VM_NAME"
virt-install \
--name "$VM_NAME" \
--memory 2048 \
--vcpus 2 \
--disk "$image_path",format=raw,bus=virtio \
--network network=default \
--graphics none \
--console pty,target_type=serial \
--boot hd \
--os-variant debian12 \
--noautoconsole \
--import
log_success "VM created: $VM_NAME"
}
# Test bootloader with virsh
test_bootloader() {
log_info "Testing bootloader with virsh..."
# Start the VM
log_info "Starting VM: $VM_NAME"
virsh start "$VM_NAME"
# Wait for VM to start
sleep 5
# Check VM status
local status
status=$(virsh domstate "$VM_NAME")
log_info "VM status: $status"
if [ "$status" = "running" ]; then
log_success "VM is running successfully!"
log_info "You can connect to the console with: virsh console $VM_NAME"
log_info "Or monitor with: virsh domdisplay $VM_NAME"
read -p "Connect to console now? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
virsh console "$VM_NAME"
fi
else
log_error "VM failed to start properly"
exit 1
fi
}
# Cleanup function
cleanup() {
log_info "Cleaning up..."
# Stop and remove VM
if virsh list --all --name | grep -q "^$VM_NAME$"; then
virsh destroy "$VM_NAME" 2>/dev/null || true
virsh undefine "$VM_NAME" 2>/dev/null || true
log_info "VM removed: $VM_NAME"
fi
# Remove test image
if [ -f "$TEST_DIR/particle-os-virsh.img" ]; then
rm -f "$TEST_DIR/particle-os-virsh.img"
log_info "Test image removed"
fi
log_success "Cleanup completed"
}
# Main execution
main() {
log_info "Starting Particle-OS bootloader test with virsh..."
check_prerequisites
setup_test_environment
local image_path
image_path=$(create_bootable_image)
prepare_image "$image_path"
create_vm_definition "$image_path"
log_success "Bootloader test setup complete!"
log_info "VM created: $VM_NAME"
log_info "Image created at: $image_path"
read -p "Test bootloader now? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
test_bootloader
fi
log_info "To test later, use:"
log_info " virsh start $VM_NAME"
log_info " virsh console $VM_NAME"
log_info " virsh destroy $VM_NAME"
}
# Trap cleanup on exit
trap cleanup EXIT
# Run main function
main "$@"