Things for final

- bump kernel-version
- add virtio_pci to modules-essential.list
- WIP little script to start vm with vmlinuz.efi to test in CH
This commit is contained in:
2025-08-22 16:48:59 +02:00
parent 948a10a3ce
commit 610963984f
4 changed files with 600 additions and 2 deletions

595
runit.sh Executable file
View File

@@ -0,0 +1,595 @@
#!/bin/bash
# Cloud Hypervisor VM with Bridge Networking and Multi-Instance Support
# Usage: ./run-vm.sh [start|stop|restart|status|config|init]
set -e
# Default Configuration (can be overridden by .chconfig)
VM_NAME="myvm"
MEMORY_SIZE="8192M"
CPU_COUNT="4"
DISK_COUNT="1"
DISK_1="10G:vm-disk.img"
BRIDGE_NAME="zosbr"
VM_IP="192.168.1.100"
VM_MASK="255.255.255.0"
KERNEL_PATH="output/vmlinuz.efi"
INITRD_PATH="output/initrd.img"
CONSOLE_ARGS="console=ttyS0,115200n8"
# Runtime configuration
INSTANCE_ID=""
TAP_INTERFACE=""
VM_MAC=""
PID_FILE=""
CONFIG_FILE=".chconfig"
DISK_PATHS=()
# 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() {
echo -e "${GREEN}[INFO]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
exit 1
}
info() {
echo -e "${BLUE}[CONFIG]${NC} $1"
}
generate_instance_id() {
# 8-character instance ID for files and collision resistance
INSTANCE_ID=$(head /dev/urandom | tr -dc a-z0-9 | head -c 8)
}
generate_mac() {
# Generate random MAC with VMware OUI prefix
VM_MAC=$(printf "52:54:00:%02x:%02x:%02x" $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))
}
load_config() {
if [ -f "$CONFIG_FILE" ]; then
log "Loading configuration from $CONFIG_FILE"
# Source the config file
source "$CONFIG_FILE"
# Validate required settings
[ -z "$VM_NAME" ] && error "VM_NAME not set in config"
[ -z "$MEMORY_SIZE" ] && error "MEMORY_SIZE not set in config"
[ -z "$CPU_COUNT" ] && error "CPU_COUNT not set in config"
[ -z "$DISK_COUNT" ] && DISK_COUNT=0
# Validate numeric values
if ! [[ "$CPU_COUNT" =~ ^[0-9]+$ ]]; then
error "CPU_COUNT must be a number"
fi
if ! [[ "$DISK_COUNT" =~ ^[0-9]+$ ]]; then
error "DISK_COUNT must be a number"
fi
info "VM Name: $VM_NAME"
info "Memory: $MEMORY_SIZE"
info "CPUs: $CPU_COUNT"
info "Disk Count: $DISK_COUNT"
info "Bridge: $BRIDGE_NAME"
info "Kernel: $KERNEL_PATH"
if [ -f "$INITRD_PATH" ]; then
info "Initrd: $INITRD_PATH"
fi
else
warn "No config file found ($CONFIG_FILE), using defaults"
fi
# Generate instance-specific configuration
generate_instance_id
generate_mac
# TAP interface naming - stay under 15 char limit
# Format: ch-XXXXXXXX (11 chars max) - safe and collision-resistant
TAP_INTERFACE="ch-${INSTANCE_ID}"
PID_FILE="/tmp/${VM_NAME}-${INSTANCE_ID}.pid"
info "Instance ID: $INSTANCE_ID"
info "TAP Interface: $TAP_INTERFACE (${#TAP_INTERFACE} chars)"
info "MAC Address: $VM_MAC"
# Verify TAP name length (safety check)
if [ ${#TAP_INTERFACE} -gt 15 ]; then
error "TAP interface name too long: $TAP_INTERFACE (${#TAP_INTERFACE} chars, max 15)"
fi
# Process disk configuration
process_disk_config
}
process_disk_config() {
DISK_PATHS=()
for ((i=1; i<=DISK_COUNT; i++)); do
disk_var="DISK_$i"
disk_config="${!disk_var}"
if [ -z "$disk_config" ]; then
warn "DISK_$i not defined, skipping"
continue
fi
# Parse "size:name" format
disk_size="${disk_config%%:*}"
disk_name="${disk_config#*:}"
# Auto-generate name if empty
if [ -z "$disk_name" ]; then
disk_name="${VM_NAME}-${INSTANCE_ID}-disk${i}.img"
else
# Add instance ID to prevent conflicts
disk_name="${VM_NAME}-${INSTANCE_ID}-${disk_name}"
fi
DISK_PATHS+=("$disk_size:$disk_name")
info "Disk $i: $disk_size -> $disk_name"
done
}
create_config() {
if [ -f "$CONFIG_FILE" ]; then
read -p "Config file $CONFIG_FILE already exists. Overwrite? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log "Keeping existing config file"
return
fi
fi
log "Creating default config file: $CONFIG_FILE"
cat > "$CONFIG_FILE" << 'EOF'
# Cloud Hypervisor VM Configuration
# Lines starting with # are comments
# Basic VM configuration
VM_NAME=myvm
MEMORY_SIZE=8192M
CPU_COUNT=4
# Disk configuration (array format)
# Format: "size:name" - if name is empty, auto-generated
# Set DISK_COUNT=0 for no disks
DISK_COUNT=2
DISK_1="10G:data.img"
DISK_2="5G:swap.img"
# DISK_3="20G:" # Auto-generated name
# DISK_4="1G:temp.img"
# Network configuration
BRIDGE_NAME=zosbr
VM_IP=192.168.1.100
VM_MASK=255.255.255.0
# Paths
KERNEL_PATH=output/vmlinuz.efi
INITRD_PATH=output/initrd.img
# Console
CONSOLE_ARGS=console=ttyS0,115200n8
EOF
log "Config file created. Edit $CONFIG_FILE and run again."
log ""
log "Example configurations:"
log " Small VM: MEMORY_SIZE=2G, CPU_COUNT=1, DISK_COUNT=1"
log " Large VM: MEMORY_SIZE=16G, CPU_COUNT=8, DISK_COUNT=3"
log " No disks: DISK_COUNT=0"
}
check_requirements() {
log "Checking requirements..."
command -v cloud-hypervisor >/dev/null 2>&1 || error "cloud-hypervisor not found"
command -v ip >/dev/null 2>&1 || error "ip command not found"
if [ "$DISK_COUNT" -gt 0 ]; then
command -v qemu-img >/dev/null 2>&1 || error "qemu-img not found (needed for disk creation)"
fi
[ -f "$KERNEL_PATH" ] || error "Kernel file not found: $KERNEL_PATH"
if [ "$EUID" -ne 0 ]; then
error "This script must be run as root (for network setup)"
fi
}
create_disks() {
if [ "$DISK_COUNT" -eq 0 ]; then
log "No disks configured"
return
fi
log "Creating disk images..."
for disk_config in "${DISK_PATHS[@]}"; do
disk_size="${disk_config%%:*}"
disk_path="${disk_config#*:}"
if [ ! -f "$disk_path" ]; then
log "Creating disk: $disk_path ($disk_size)"
qemu-img create -f raw "$disk_path" "$disk_size"
else
log "Disk already exists: $disk_path"
# Show disk info
DISK_INFO=$(qemu-img info "$disk_path" 2>/dev/null | grep "virtual size" || echo "Unable to get size")
info " $DISK_INFO"
fi
done
}
setup_bridge() {
log "Setting up bridge network: $BRIDGE_NAME"
# Create bridge if it doesn't exist
if ! ip link show "$BRIDGE_NAME" >/dev/null 2>&1; then
log "Creating bridge: $BRIDGE_NAME"
ip link add name "$BRIDGE_NAME" type bridge
ip link set "$BRIDGE_NAME" up
# Configure bridge IP (adjust as needed)
BRIDGE_IP=$(echo "$VM_IP" | sed 's/\.[0-9]*$/.1/')
ip addr add "${BRIDGE_IP}/24" dev "$BRIDGE_NAME" 2>/dev/null || true
info "Bridge IP: $BRIDGE_IP"
else
log "Bridge $BRIDGE_NAME already exists"
fi
}
setup_tap() {
log "Setting up TAP interface: $TAP_INTERFACE"
# Remove existing TAP if it exists (very unlikely with 8-char random IDs)
if ip link show "$TAP_INTERFACE" >/dev/null 2>&1; then
warn "TAP interface $TAP_INTERFACE already exists, removing..."
ip link delete "$TAP_INTERFACE" 2>/dev/null || true
fi
# Create TAP interface
ip tuntap add "$TAP_INTERFACE" mode tap
ip link set "$TAP_INTERFACE" up
# Add TAP to bridge
ip link set "$TAP_INTERFACE" master "$BRIDGE_NAME"
log "TAP interface $TAP_INTERFACE added to bridge $BRIDGE_NAME"
}
cleanup_tap() {
if [ -n "$TAP_INTERFACE" ]; then
log "Cleaning up TAP interface: $TAP_INTERFACE"
if ip link show "$TAP_INTERFACE" >/dev/null 2>&1; then
ip link delete "$TAP_INTERFACE"
log "TAP interface $TAP_INTERFACE removed"
fi
fi
}
cleanup_disks() {
if [ "$1" = "--remove-disks" ]; then
log "Removing disk images..."
for disk_config in "${DISK_PATHS[@]}"; do
disk_path="${disk_config#*:}"
if [ -f "$disk_path" ]; then
rm -f "$disk_path"
log "Removed: $disk_path"
fi
done
fi
}
start_vm() {
log "Starting VM: $VM_NAME"
load_config
check_requirements
create_disks
setup_bridge
setup_tap
# Build cloud-hypervisor command
CH_CMD="cloud-hypervisor"
CH_CMD="$CH_CMD --kernel $KERNEL_PATH"
CH_CMD="$CH_CMD --memory size=$MEMORY_SIZE"
CH_CMD="$CH_CMD --cpus boot=$CPU_COUNT"
CH_CMD="$CH_CMD --net tap=$TAP_INTERFACE,mac=$VM_MAC"
CH_CMD="$CH_CMD --serial tty"
CH_CMD="$CH_CMD --console off"
CH_CMD="$CH_CMD --cmdline '$CONSOLE_ARGS'"
# Add initrd if it exists
if [ -f "$INITRD_PATH" ]; then
CH_CMD="$CH_CMD --initramfs $INITRD_PATH"
log "Using initrd: $INITRD_PATH"
fi
# Add disks
for disk_config in "${DISK_PATHS[@]}"; do
disk_path="${disk_config#*:}"
CH_CMD="$CH_CMD --disk path=$disk_path"
log "Attached disk: $disk_path"
done
log "Starting cloud-hypervisor..."
log "Command: $CH_CMD"
# Save instance info
cat > "${PID_FILE}.info" << EOF
VM_NAME=$VM_NAME
INSTANCE_ID=$INSTANCE_ID
TAP_INTERFACE=$TAP_INTERFACE
VM_MAC=$VM_MAC
MEMORY_SIZE=$MEMORY_SIZE
CPU_COUNT=$CPU_COUNT
DISK_COUNT=$DISK_COUNT
BRIDGE_NAME=$BRIDGE_NAME
EOF
# Start VM in background and save PID
eval "$CH_CMD" &
VM_PID=$!
echo $VM_PID > "$PID_FILE"
log "VM started with PID: $VM_PID"
log "Instance ID: $INSTANCE_ID"
log "TAP Interface: $TAP_INTERFACE"
log "Connect to serial console (Ctrl+C to exit)"
# Wait for VM process
wait $VM_PID
log "VM process ended"
# Cleanup on exit
cleanup_tap
rm -f "$PID_FILE" "${PID_FILE}.info"
}
stop_vm() {
local instance_pattern="${1:-}"
local remove_disks=""
if [ "$1" = "--remove-disks" ] || [ "$2" = "--remove-disks" ]; then
remove_disks="--remove-disks"
if [ "$1" = "--remove-disks" ]; then
instance_pattern=""
fi
fi
# If no specific instance, load config and stop current
if [ -z "$instance_pattern" ]; then
load_config
log "Stopping VM: $VM_NAME (Instance: $INSTANCE_ID)"
if [ -f "$PID_FILE" ]; then
VM_PID=$(cat "$PID_FILE")
if kill -0 "$VM_PID" 2>/dev/null; then
log "Terminating VM process: $VM_PID"
kill "$VM_PID"
sleep 2
# Force kill if still running
if kill -0 "$VM_PID" 2>/dev/null; then
warn "Force killing VM process: $VM_PID"
kill -9 "$VM_PID"
fi
fi
# Load TAP interface from info file
if [ -f "${PID_FILE}.info" ]; then
source "${PID_FILE}.info"
fi
cleanup_tap
if [ -n "$remove_disks" ]; then
cleanup_disks --remove-disks
fi
rm -f "$PID_FILE" "${PID_FILE}.info"
else
warn "No PID file found"
fi
else
# Stop specific instance by pattern
log "Stopping VM instances matching: $instance_pattern"
for pid_file in /tmp/*${instance_pattern}*.pid; do
if [ -f "$pid_file" ]; then
VM_PID=$(cat "$pid_file")
info_file="${pid_file}.info"
if [ -f "$info_file" ]; then
source "$info_file"
log "Stopping $VM_NAME (Instance: $INSTANCE_ID)"
fi
if kill -0 "$VM_PID" 2>/dev/null; then
kill "$VM_PID"
sleep 1
if kill -0 "$VM_PID" 2>/dev/null; then
kill -9 "$VM_PID"
fi
fi
# Cleanup TAP if info available
if [ -n "$TAP_INTERFACE" ] && ip link show "$TAP_INTERFACE" >/dev/null 2>&1; then
ip link delete "$TAP_INTERFACE"
fi
rm -f "$pid_file" "$info_file"
fi
done
fi
log "VM stopped"
}
status_vm() {
local show_all="${1:-}"
if [ "$show_all" = "--all" ]; then
log "All running VM instances:"
echo
local found=0
for pid_file in /tmp/*.pid; do
# Look for files matching our naming pattern
if [ -f "$pid_file" ] && [[ "$(basename "$pid_file")" =~ -[a-z0-9]{8}\.pid$ ]]; then
VM_PID=$(cat "$pid_file" 2>/dev/null)
info_file="${pid_file}.info"
if kill -0 "$VM_PID" 2>/dev/null && [ -f "$info_file" ]; then
source "$info_file"
echo " VM: $VM_NAME"
echo " Instance ID: $INSTANCE_ID"
echo " PID: $VM_PID"
echo " Memory: $MEMORY_SIZE"
echo " CPUs: $CPU_COUNT"
echo " Disks: $DISK_COUNT"
echo " TAP: $TAP_INTERFACE (${#TAP_INTERFACE} chars)"
echo " Bridge: $BRIDGE_NAME"
echo
found=1
fi
fi
done
if [ $found -eq 0 ]; then
log "No running VM instances found"
fi
else
load_config
if [ -f "$PID_FILE" ]; then
VM_PID=$(cat "$PID_FILE")
if kill -0 "$VM_PID" 2>/dev/null; then
log "VM '$VM_NAME' (Instance: $INSTANCE_ID) is running with PID: $VM_PID"
log "TAP Interface: $TAP_INTERFACE (${#TAP_INTERFACE} chars)"
return 0
else
warn "VM PID file exists but process is not running"
rm -f "$PID_FILE" "${PID_FILE}.info"
fi
fi
log "VM '$VM_NAME' is not running"
return 1
fi
}
show_config() {
load_config
echo
echo "Current Configuration:"
echo "====================="
echo "VM Name: $VM_NAME"
echo "Instance ID: $INSTANCE_ID"
echo "Memory: $MEMORY_SIZE"
echo "CPUs: $CPU_COUNT"
echo "Disk Count: $DISK_COUNT"
if [ "$DISK_COUNT" -gt 0 ]; then
echo "Disks:"
for i in "${!DISK_PATHS[@]}"; do
disk_config="${DISK_PATHS[$i]}"
disk_size="${disk_config%%:*}"
disk_path="${disk_config#*:}"
echo " $((i+1)): $disk_size -> $disk_path"
done
fi
echo "Bridge: $BRIDGE_NAME"
echo "TAP: $TAP_INTERFACE (${#TAP_INTERFACE} chars)"
echo "VM MAC: $VM_MAC"
echo "VM IP: $VM_IP"
echo "Kernel: $KERNEL_PATH"
echo "Initrd: $INITRD_PATH"
echo "Console: $CONSOLE_ARGS"
echo "PID File: $PID_FILE"
echo
}
# Trap to cleanup on script exit
trap 'cleanup_tap 2>/dev/null; rm -f "$PID_FILE" "${PID_FILE}.info" 2>/dev/null' EXIT INT TERM
case "${1:-start}" in
start)
if [ -f "$CONFIG_FILE" ]; then
load_config
fi
start_vm
;;
stop)
if [ "$2" = "--all" ]; then
stop_vm "*" "$3"
else
stop_vm "$2" "$3"
fi
;;
restart)
stop_vm
sleep 1
start_vm
;;
status)
status_vm "$2"
;;
config)
show_config
;;
init|create-config)
create_config
;;
*)
echo "Usage: $0 {start|stop|restart|status|config|init}"
echo ""
echo "Commands:"
echo " start - Start the VM"
echo " stop [pattern] - Stop VM(s) [matching pattern]"
echo " stop --all - Stop all running VMs"
echo " stop --remove-disks - Stop and remove disk files"
echo " restart - Restart the VM"
echo " status - Show VM status"
echo " status --all - Show all running VMs"
echo " config - Show current configuration"
echo " init - Create default .chconfig file"
echo ""
echo "Examples:"
echo " $0 stop myvm - Stop VMs with 'myvm' in name"
echo " $0 stop --all - Stop all running VMs"
echo " $0 status --all - List all running VMs"
echo ""
echo "Configuration file: $CONFIG_FILE"
if [ -f "$CONFIG_FILE" ]; then
VM_NAME_TEMP=$(grep "^VM_NAME=" "$CONFIG_FILE" 2>/dev/null | cut -d= -f2 || echo "unknown")
MEM_SIZE_TEMP=$(grep "^MEMORY_SIZE=" "$CONFIG_FILE" 2>/dev/null | cut -d= -f2 || echo "unknown")
DISK_COUNT_TEMP=$(grep "^DISK_COUNT=" "$CONFIG_FILE" 2>/dev/null | cut -d= -f2 || echo "unknown")
echo "Current VM: $VM_NAME_TEMP (Memory: $MEM_SIZE_TEMP, Disks: $DISK_COUNT_TEMP)"
else
echo "No config file found. Run '$0 init' to create one."
fi
exit 1
;;
esac