399 lines
12 KiB
Bash
Executable file
399 lines
12 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# ==============================================================================
|
|
# PlasmaCloud NixOS Netboot Image Builder
|
|
# ==============================================================================
|
|
# This script builds netboot images for bare-metal provisioning of PlasmaCloud.
|
|
#
|
|
# Usage:
|
|
# ./build-images.sh [--profile PROFILE] [--output-dir DIR] [--help]
|
|
#
|
|
# Options:
|
|
# --profile PROFILE Build specific profile (control-plane, worker, all-in-one, all)
|
|
# --output-dir DIR Output directory for built artifacts (default: ./artifacts)
|
|
# --help Show this help message
|
|
#
|
|
# Examples:
|
|
# ./build-images.sh # Build all profiles
|
|
# ./build-images.sh --profile control-plane # Build control plane only
|
|
# ./build-images.sh --profile all # Build all profiles
|
|
# ./build-images.sh --output-dir /srv/pxe # Custom output directory
|
|
# ==============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
# ==============================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
DEFAULT_OUTPUT_DIR="$SCRIPT_DIR/artifacts"
|
|
PXE_ASSETS_DIR="$REPO_ROOT/chainfire/baremetal/pxe-server/assets"
|
|
|
|
# Color codes for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# ==============================================================================
|
|
# FUNCTIONS
|
|
# ==============================================================================
|
|
|
|
# Print colored messages
|
|
print_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Print banner
|
|
print_banner() {
|
|
echo ""
|
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
|
echo "║ PlasmaCloud NixOS Netboot Image Builder ║"
|
|
echo "║ Building bare-metal provisioning images ║"
|
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
}
|
|
|
|
# Print usage
|
|
print_usage() {
|
|
cat << EOF
|
|
Usage: $0 [OPTIONS]
|
|
|
|
Build NixOS netboot images for PlasmaCloud bare-metal provisioning.
|
|
|
|
OPTIONS:
|
|
--profile PROFILE Build specific profile:
|
|
- control-plane: All 8 PlasmaCloud services
|
|
- worker: Compute-focused services (PlasmaVMC, PrismNET)
|
|
- all-in-one: All services for single-node deployment
|
|
- all: Build all profiles (default)
|
|
|
|
--output-dir DIR Output directory for artifacts (default: ./artifacts)
|
|
|
|
--help Show this help message
|
|
|
|
EXAMPLES:
|
|
# Build all profiles
|
|
$0
|
|
|
|
# Build control plane only
|
|
$0 --profile control-plane
|
|
|
|
# Build to custom output directory
|
|
$0 --output-dir /srv/pxe/images
|
|
|
|
PROFILES:
|
|
control-plane - Full control plane with all 8 services
|
|
worker - Worker node with PlasmaVMC and PrismNET
|
|
all-in-one - Single-node deployment with all services
|
|
|
|
OUTPUT:
|
|
The script generates the following artifacts for each profile:
|
|
- bzImage Linux kernel
|
|
- initrd Initial ramdisk
|
|
- netboot.ipxe iPXE boot script
|
|
|
|
EOF
|
|
}
|
|
|
|
# Build a single netboot profile
|
|
build_profile() {
|
|
local profile=$1
|
|
local output_dir=$2
|
|
|
|
print_info "Building netboot image for profile: $profile"
|
|
|
|
# Create profile output directory
|
|
local profile_dir="$output_dir/$profile"
|
|
mkdir -p "$profile_dir"
|
|
|
|
# Build the netboot ramdisk
|
|
print_info " Building initial ramdisk..."
|
|
if ! nix build "$REPO_ROOT#nixosConfigurations.netboot-$profile.config.system.build.netbootRamdisk" \
|
|
--out-link "$profile_dir/initrd-link" 2>&1 | tee "$profile_dir/build.log"; then
|
|
print_error "Failed to build initrd for $profile (see $profile_dir/build.log)"
|
|
return 1
|
|
fi
|
|
|
|
# Build the kernel
|
|
print_info " Building kernel..."
|
|
if ! nix build "$REPO_ROOT#nixosConfigurations.netboot-$profile.config.system.build.kernel" \
|
|
--out-link "$profile_dir/kernel-link" 2>&1 | tee -a "$profile_dir/build.log"; then
|
|
print_error "Failed to build kernel for $profile (see $profile_dir/build.log)"
|
|
return 1
|
|
fi
|
|
|
|
# Copy artifacts
|
|
print_info " Copying artifacts..."
|
|
cp -f "$profile_dir/initrd-link/initrd" "$profile_dir/initrd"
|
|
cp -f "$profile_dir/kernel-link/bzImage" "$profile_dir/bzImage"
|
|
|
|
# Resolve init path from the build (avoids hardcoding store paths)
|
|
local init_path="/init"
|
|
if toplevel=$(nix eval --raw "$REPO_ROOT#nixosConfigurations.netboot-$profile.config.system.build.toplevel" 2>/dev/null); then
|
|
if [ -n "$toplevel" ]; then
|
|
init_path="${toplevel}/init"
|
|
fi
|
|
else
|
|
print_warning "Failed to resolve init path for $profile; using /init"
|
|
fi
|
|
|
|
# Generate iPXE boot script
|
|
print_info " Generating iPXE boot script..."
|
|
cat > "$profile_dir/netboot.ipxe" << EOF
|
|
#!ipxe
|
|
|
|
# PlasmaCloud Netboot - $profile
|
|
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
|
|
|
# Set variables
|
|
set boot-server \${boot-url}
|
|
|
|
# Display info
|
|
echo Loading PlasmaCloud ($profile profile)...
|
|
echo Kernel: bzImage
|
|
echo Initrd: initrd
|
|
echo
|
|
|
|
# Load kernel and initrd
|
|
kernel \${boot-server}/$profile/bzImage init=${init_path} console=ttyS0,115200 console=tty0 loglevel=4
|
|
initrd \${boot-server}/$profile/initrd
|
|
|
|
# Boot
|
|
boot
|
|
EOF
|
|
|
|
# Calculate sizes
|
|
local kernel_size=$(du -h "$profile_dir/bzImage" | cut -f1)
|
|
local initrd_size=$(du -h "$profile_dir/initrd" | cut -f1)
|
|
local total_size=$(du -sh "$profile_dir" | cut -f1)
|
|
|
|
# Print summary
|
|
print_success "Profile $profile built successfully!"
|
|
print_info " Kernel: $kernel_size"
|
|
print_info " Initrd: $initrd_size"
|
|
print_info " Total: $total_size"
|
|
print_info " Location: $profile_dir"
|
|
echo ""
|
|
}
|
|
|
|
# Copy artifacts to PXE server assets directory
|
|
copy_to_pxe_server() {
|
|
local output_dir=$1
|
|
|
|
if [ ! -d "$PXE_ASSETS_DIR" ]; then
|
|
print_warning "PXE assets directory not found: $PXE_ASSETS_DIR"
|
|
print_warning "Skipping copy to PXE server"
|
|
return 0
|
|
fi
|
|
|
|
print_info "Copying artifacts to PXE server: $PXE_ASSETS_DIR"
|
|
|
|
for profile in control-plane worker all-in-one; do
|
|
local profile_dir="$output_dir/$profile"
|
|
if [ -d "$profile_dir" ]; then
|
|
local pxe_profile_dir="$PXE_ASSETS_DIR/nixos/$profile"
|
|
mkdir -p "$pxe_profile_dir"
|
|
|
|
cp -f "$profile_dir/bzImage" "$pxe_profile_dir/"
|
|
cp -f "$profile_dir/initrd" "$pxe_profile_dir/"
|
|
cp -f "$profile_dir/netboot.ipxe" "$pxe_profile_dir/"
|
|
|
|
# Create symlinks for convenience
|
|
ln -sf "$profile/bzImage" "$PXE_ASSETS_DIR/nixos/bzImage-$profile"
|
|
ln -sf "$profile/initrd" "$PXE_ASSETS_DIR/nixos/initrd-$profile"
|
|
|
|
print_success " Copied $profile to PXE server"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Verify build outputs
|
|
verify_outputs() {
|
|
local output_dir=$1
|
|
local profile=$2
|
|
|
|
local profile_dir="$output_dir/$profile"
|
|
local errors=0
|
|
|
|
# Check for required files
|
|
if [ ! -f "$profile_dir/bzImage" ]; then
|
|
print_error "Missing bzImage for $profile"
|
|
((errors++))
|
|
fi
|
|
|
|
if [ ! -f "$profile_dir/initrd" ]; then
|
|
print_error "Missing initrd for $profile"
|
|
((errors++))
|
|
fi
|
|
|
|
if [ ! -f "$profile_dir/netboot.ipxe" ]; then
|
|
print_error "Missing netboot.ipxe for $profile"
|
|
((errors++))
|
|
fi
|
|
|
|
# Check file sizes (should be reasonable)
|
|
if [ -f "$profile_dir/bzImage" ]; then
|
|
local kernel_size=$(stat -c%s "$profile_dir/bzImage")
|
|
if [ "$kernel_size" -lt 1000000 ]; then # Less than 1MB is suspicious
|
|
print_warning "Kernel size seems too small: $kernel_size bytes"
|
|
((errors++))
|
|
fi
|
|
fi
|
|
|
|
if [ -f "$profile_dir/initrd" ]; then
|
|
local initrd_size=$(stat -c%s "$profile_dir/initrd")
|
|
if [ "$initrd_size" -lt 10000000 ]; then # Less than 10MB is suspicious
|
|
print_warning "Initrd size seems too small: $initrd_size bytes"
|
|
((errors++))
|
|
fi
|
|
fi
|
|
|
|
return $errors
|
|
}
|
|
|
|
# Print final summary
|
|
print_summary() {
|
|
local output_dir=$1
|
|
local profiles=("$@")
|
|
shift # Remove first argument (output_dir)
|
|
|
|
echo ""
|
|
echo "╔════════════════════════════════════════════════════════════════╗"
|
|
echo "║ Build Summary ║"
|
|
echo "╚════════════════════════════════════════════════════════════════╝"
|
|
echo ""
|
|
|
|
for profile in "${profiles[@]}"; do
|
|
if [ "$profile" == "$output_dir" ]; then
|
|
continue
|
|
fi
|
|
|
|
local profile_dir="$output_dir/$profile"
|
|
if [ -d "$profile_dir" ]; then
|
|
echo "Profile: $profile"
|
|
echo " Location: $profile_dir"
|
|
if [ -f "$profile_dir/bzImage" ]; then
|
|
echo " Kernel: $(du -h "$profile_dir/bzImage" | cut -f1)"
|
|
fi
|
|
if [ -f "$profile_dir/initrd" ]; then
|
|
echo " Initrd: $(du -h "$profile_dir/initrd" | cut -f1)"
|
|
fi
|
|
echo ""
|
|
fi
|
|
done
|
|
|
|
echo "Next Steps:"
|
|
echo " 1. Deploy images to PXE server (if not done automatically)"
|
|
echo " 2. Configure DHCP/iPXE boot infrastructure"
|
|
echo " 3. Boot target machines via PXE"
|
|
echo " 4. Use nixos-anywhere for installation"
|
|
echo ""
|
|
echo "For more information, see:"
|
|
echo " - baremetal/image-builder/README.md"
|
|
echo " - docs/por/T032-baremetal-provisioning/design.md"
|
|
echo ""
|
|
}
|
|
|
|
# ==============================================================================
|
|
# MAIN
|
|
# ==============================================================================
|
|
|
|
main() {
|
|
local profile="all"
|
|
local output_dir="$DEFAULT_OUTPUT_DIR"
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--profile)
|
|
profile="$2"
|
|
shift 2
|
|
;;
|
|
--output-dir)
|
|
output_dir="$2"
|
|
shift 2
|
|
;;
|
|
--help)
|
|
print_usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
print_error "Unknown option: $1"
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Validate profile
|
|
if [[ ! "$profile" =~ ^(control-plane|worker|all-in-one|all)$ ]]; then
|
|
print_error "Invalid profile: $profile"
|
|
print_usage
|
|
exit 1
|
|
fi
|
|
|
|
print_banner
|
|
|
|
# Create output directory
|
|
mkdir -p "$output_dir"
|
|
|
|
# Build profiles
|
|
local profiles_to_build=()
|
|
if [ "$profile" == "all" ]; then
|
|
profiles_to_build=("control-plane" "worker" "all-in-one")
|
|
else
|
|
profiles_to_build=("$profile")
|
|
fi
|
|
|
|
local build_errors=0
|
|
|
|
for p in "${profiles_to_build[@]}"; do
|
|
if ! build_profile "$p" "$output_dir"; then
|
|
((build_errors++))
|
|
fi
|
|
done
|
|
|
|
# Verify outputs
|
|
print_info "Verifying build outputs..."
|
|
local verify_errors=0
|
|
for p in "${profiles_to_build[@]}"; do
|
|
if ! verify_outputs "$output_dir" "$p"; then
|
|
((verify_errors++))
|
|
fi
|
|
done
|
|
|
|
# Copy to PXE server if available
|
|
copy_to_pxe_server "$output_dir"
|
|
|
|
# Print summary
|
|
print_summary "$output_dir" "${profiles_to_build[@]}"
|
|
|
|
# Exit with error if any builds failed
|
|
if [ $build_errors -gt 0 ]; then
|
|
print_error "Build completed with $build_errors error(s)"
|
|
exit 1
|
|
fi
|
|
|
|
if [ $verify_errors -gt 0 ]; then
|
|
print_warning "Build completed with $verify_errors warning(s)"
|
|
fi
|
|
|
|
print_success "All builds completed successfully!"
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|