diff --git a/tools/install_cloudhypervisor.sh b/tools/install_cloudhypervisor.sh new file mode 100755 index 0000000..8eaa6e9 --- /dev/null +++ b/tools/install_cloudhypervisor.sh @@ -0,0 +1,178 @@ +#!/bin/bash + +# Cloud Hypervisor Installation Script +# Downloads and installs Cloud Hypervisor from GitHub releases +# Compatible with Ubuntu/Debian systems + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Cloud Hypervisor configuration +CH_VERSION="v46.0" +CH_URL="https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/${CH_VERSION}/cloud-hypervisor-static" +CH_BINARY="cloud-hypervisor" +MIN_SIZE_MB=4 + +log() { + echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" +} + +warn() { + echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}" +} + +error() { + echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" + exit 1 +} + +# Check if running as root +if [ "$EUID" -eq 0 ]; then + log "Running as root. Cloud Hypervisor will be installed system-wide." + INSTALL_DIR="/usr/local/bin" +else + log "Installing Cloud Hypervisor for current user" + INSTALL_DIR="$HOME/.local/bin" + # Create local bin directory if it doesn't exist + mkdir -p "$INSTALL_DIR" +fi + +# Check if Cloud Hypervisor is already installed +if command -v cloud-hypervisor &> /dev/null; then + CH_CURRENT_VERSION=$(cloud-hypervisor --version 2>/dev/null | head -n1 || echo "") + if [ -n "$CH_CURRENT_VERSION" ]; then + warn "Cloud Hypervisor is already installed: $CH_CURRENT_VERSION" + read -p "Do you want to update/reinstall? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + log "Installation cancelled" + exit 0 + fi + fi +fi + +# Check for required dependencies +log "Checking system dependencies..." +MISSING_DEPS=() + +# Check for curl +if ! command -v curl &> /dev/null; then + MISSING_DEPS+=("curl") +fi + +# Install missing dependencies +if [ ${#MISSING_DEPS[@]} -gt 0 ]; then + log "Installing missing dependencies: ${MISSING_DEPS[*]}" + if [ "$EUID" -eq 0 ]; then + apt update + apt install -y "${MISSING_DEPS[@]}" + else + log "Please install the following packages and run this script again:" + log "sudo apt update && sudo apt install -y ${MISSING_DEPS[*]}" + exit 1 + fi +fi + +log "Starting Cloud Hypervisor installation..." +log "Version: $CH_VERSION" +log "Install directory: $INSTALL_DIR" + +# Create temporary directory for download +TEMP_DIR=$(mktemp -d) +TEMP_FILE="$TEMP_DIR/$CH_BINARY" + +# Cleanup function +cleanup() { + rm -rf "$TEMP_DIR" +} +trap cleanup EXIT + +log "Downloading Cloud Hypervisor from $CH_URL..." + +# Download with size verification +if ! curl -L --fail --progress-bar -o "$TEMP_FILE" "$CH_URL"; then + error "Failed to download Cloud Hypervisor from $CH_URL" +fi + +# Check file size +FILE_SIZE=$(stat -c%s "$TEMP_FILE" 2>/dev/null || stat -f%z "$TEMP_FILE" 2>/dev/null || echo "0") +FILE_SIZE_MB=$((FILE_SIZE / 1024 / 1024)) + +log "Downloaded file size: ${FILE_SIZE_MB}MB" + +if [ "$FILE_SIZE_MB" -lt "$MIN_SIZE_MB" ]; then + error "Downloaded file is too small (${FILE_SIZE_MB}MB). Expected at least ${MIN_SIZE_MB}MB. Download may be corrupted." +fi + +log "✅ Download size verification passed (${FILE_SIZE_MB}MB >= ${MIN_SIZE_MB}MB)" + +# Make the binary executable +chmod +x "$TEMP_FILE" + +# Test the binary before installation +log "Testing downloaded binary..." +if ! "$TEMP_FILE" --version &> /dev/null; then + error "Downloaded binary is not working correctly" +fi + +log "✅ Binary test passed" + +# Install the binary +log "Installing Cloud Hypervisor to $INSTALL_DIR..." +if [ "$EUID" -eq 0 ]; then + cp "$TEMP_FILE" "$INSTALL_DIR/$CH_BINARY" +else + cp "$TEMP_FILE" "$INSTALL_DIR/$CH_BINARY" + # Add to PATH if not already there + if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then + warn "Adding $INSTALL_DIR to PATH in ~/.bashrc" + echo "export PATH=\"$INSTALL_DIR:\$PATH\"" >> ~/.bashrc + export PATH="$INSTALL_DIR:$PATH" + fi +fi + +# Verify installation +log "Verifying Cloud Hypervisor installation..." + +# Set PATH for verification +if [ "$EUID" -ne 0 ]; then + export PATH="$INSTALL_DIR:$PATH" +fi + +if command -v cloud-hypervisor &> /dev/null; then + CH_VERSION_OUTPUT=$(cloud-hypervisor --version 2>/dev/null || echo "") + if [ -n "$CH_VERSION_OUTPUT" ]; then + log "✅ Cloud Hypervisor installation successful!" + log "Version: $CH_VERSION_OUTPUT" + log "Location: $(which cloud-hypervisor)" + + # Test basic functionality + log "Testing basic functionality..." + if cloud-hypervisor --help &> /dev/null; then + log "✅ Basic functionality test passed" + else + warn "Basic functionality test failed, but binary is installed" + fi + + log "✅ Cloud Hypervisor installation completed successfully!" + log "" + log "Next steps:" + if [ "$EUID" -eq 0 ]; then + log "- Cloud Hypervisor is available system-wide" + else + log "- Run 'source ~/.bashrc' to update PATH in this session" + log "- Or restart your terminal" + fi + log "- Run 'cloud-hypervisor --help' to see available options" + log "- Check the documentation at: https://github.com/cloud-hypervisor/cloud-hypervisor" + else + error "Cloud Hypervisor was installed but version check failed" + fi +else + error "Cloud Hypervisor installation failed. Binary not found in PATH." +fi \ No newline at end of file diff --git a/tools/ubuntu_vm_start.sh b/tools/ubuntu_vm_start.sh new file mode 100755 index 0000000..2b9a000 --- /dev/null +++ b/tools/ubuntu_vm_start.sh @@ -0,0 +1,293 @@ +#!/bin/bash + +# Ubuntu VM Start Script with Cloud Hypervisor and Btrfs Thin Provisioning +# Usage: ubuntu_vm_start.sh $name $mem $cores + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +BASE_IMAGE_NAME="ubuntu-24.04-server-cloudimg-amd64" +BASE_IMAGE_URL="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img" +FIRMWARE_URL="https://github.com/cloud-hypervisor/rust-hypervisor-firmware/releases/download/0.5.0/hypervisor-fw" +VM_BASE_DIR="/var/lib/vms" +BTRFS_MOUNT_POINT="/var/lib/vms" +BASE_SUBVOL="$BTRFS_MOUNT_POINT/base" +VMS_SUBVOL="$BTRFS_MOUNT_POINT/vms" + +log() { + echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" +} + +warn() { + echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}" +} + +error() { + echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" + exit 1 +} + +info() { + echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1${NC}" +} + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + error "This script must be run as root for btrfs operations" +fi + +# Parse arguments +if [ $# -ne 3 ]; then + error "Usage: $0 " +fi + +VM_NAME="$1" +MEMORY_MB="$2" +CPU_CORES="$3" + +# Validate arguments +if ! [[ "$MEMORY_MB" =~ ^[0-9]+$ ]]; then + error "Memory must be a number (in MB)" +fi + +if ! [[ "$CPU_CORES" =~ ^[0-9]+$ ]]; then + error "CPU cores must be a number" +fi + +if [[ "$VM_NAME" =~ [^a-zA-Z0-9_-] ]]; then + error "VM name can only contain alphanumeric characters, hyphens, and underscores" +fi + +log "Starting VM: $VM_NAME with ${MEMORY_MB}MB RAM and $CPU_CORES CPU cores" + +# Check if cloud-hypervisor is available +if ! command -v cloud-hypervisor &> /dev/null; then + error "cloud-hypervisor not found. Please install it first." +fi + +# Check if qemu-img is available (for image conversion) +if ! command -v qemu-img &> /dev/null; then + warn "qemu-img not found. Installing qemu-utils..." + apt update && apt install -y qemu-utils +fi + +# Create base directory structure +mkdir -p "$VM_BASE_DIR" + +# Check if the base directory is on btrfs +if ! btrfs filesystem show "$VM_BASE_DIR" &>/dev/null; then + error "Base directory $VM_BASE_DIR is not on a btrfs filesystem. Please create a btrfs filesystem first." +fi + +# Create base and vms subvolumes if they don't exist +if [ ! -d "$BASE_SUBVOL" ]; then + log "Creating base subvolume at $BASE_SUBVOL" + btrfs subvolume create "$BASE_SUBVOL" +fi + +if [ ! -d "$VMS_SUBVOL" ]; then + log "Creating VMs subvolume at $VMS_SUBVOL" + btrfs subvolume create "$VMS_SUBVOL" +fi + +# Define paths +BASE_IMAGE_PATH="$BASE_SUBVOL/${BASE_IMAGE_NAME}.raw" +FIRMWARE_PATH="$BASE_SUBVOL/hypervisor-fw" +VM_SUBVOL_PATH="$VMS_SUBVOL/$VM_NAME" +VM_IMAGE_PATH="$VM_SUBVOL_PATH/${VM_NAME}.raw" +CLOUD_INIT_PATH="$VM_SUBVOL_PATH/cloud-init.img" + +# Download and prepare base image if it doesn't exist +if [ ! -f "$BASE_IMAGE_PATH" ]; then + log "Base image not found. Downloading Ubuntu cloud image..." + + # Download the qcow2 image + TEMP_QCOW2="/tmp/${BASE_IMAGE_NAME}.img" + if ! curl -L --fail --progress-bar -o "$TEMP_QCOW2" "$BASE_IMAGE_URL"; then + error "Failed to download Ubuntu cloud image from $BASE_IMAGE_URL" + fi + + log "Converting qcow2 image to raw format..." + qemu-img convert -p -f qcow2 -O raw "$TEMP_QCOW2" "$BASE_IMAGE_PATH" + + # Cleanup temporary file + rm -f "$TEMP_QCOW2" + + log "Base image created at $BASE_IMAGE_PATH" +fi + +# Download firmware if it doesn't exist +if [ ! -f "$FIRMWARE_PATH" ]; then + log "Downloading Cloud Hypervisor firmware..." + if ! curl -L --fail --progress-bar -o "$FIRMWARE_PATH" "$FIRMWARE_URL"; then + error "Failed to download firmware from $FIRMWARE_URL" + fi + chmod +x "$FIRMWARE_PATH" + log "Firmware downloaded to $FIRMWARE_PATH" +fi + +# Create VM subvolume by cloning from base +if [ -d "$VM_SUBVOL_PATH" ]; then + warn "VM subvolume $VM_NAME already exists. Removing it..." + btrfs subvolume delete "$VM_SUBVOL_PATH" +fi + +log "Creating VM subvolume by cloning base subvolume..." +btrfs subvolume snapshot "$BASE_SUBVOL" "$VM_SUBVOL_PATH" + +# Copy the base image to VM subvolume (this will be a CoW copy initially) +log "Creating VM disk image (thin provisioned)..." +cp --reflink=always "$BASE_IMAGE_PATH" "$VM_IMAGE_PATH" + +# Create cloud-init image for first boot +log "Creating cloud-init configuration..." +cat > "/tmp/user-data" << EOF +#cloud-config +users: + - name: ubuntu + sudo: ALL=(ALL) NOPASSWD:ALL + shell: /bin/bash + lock_passwd: false + passwd: \$6\$rounds=4096\$saltsalt\$L9.LKkHxeed8Kn9.Kk8nNWn8W.XhHPyjKJJXYqKoTFJJy7P8dMCFK + ssh_authorized_keys: + - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC... # Add your SSH public key here + groups: sudo + home: /home/ubuntu + +ssh_pwauth: true +disable_root: false +chpasswd: + expire: false + +# Enable SSH +ssh_authorized_keys: [] + +# Package updates and installs +package_update: true +package_upgrade: true +packages: + - curl + - wget + - git + - htop + - vim + +# Final message +final_message: "Cloud-init setup complete. VM is ready!" +EOF + +# Create meta-data file +cat > "/tmp/meta-data" << EOF +instance-id: $VM_NAME +local-hostname: $VM_NAME +EOF + +# Create cloud-init ISO +log "Creating cloud-init ISO..." +if command -v genisoimage &> /dev/null; then + genisoimage -output "$CLOUD_INIT_PATH" -volid cidata -joliet -rock /tmp/user-data /tmp/meta-data +elif command -v mkisofs &> /dev/null; then + mkisofs -o "$CLOUD_INIT_PATH" -V cidata -J -r /tmp/user-data /tmp/meta-data +else + error "Neither genisoimage nor mkisofs found. Please install genisoimage or cdrtools." +fi + +# Cleanup temporary files +rm -f /tmp/user-data /tmp/meta-data + +log "Cloud-init ISO created at $CLOUD_INIT_PATH" + +# Resize the VM disk to give it more space (optional, expand to 20GB) +log "Resizing VM disk to 20GB..." +qemu-img resize "$VM_IMAGE_PATH" 20G + +# Create network configuration +BRIDGE_NAME="br0" +TAP_NAME="tap-$VM_NAME" + +# Check if bridge exists, create if not +if ! ip link show "$BRIDGE_NAME" &>/dev/null; then + log "Creating bridge interface $BRIDGE_NAME..." + ip link add name "$BRIDGE_NAME" type bridge + ip link set dev "$BRIDGE_NAME" up + # You may want to configure the bridge with an IP address + # ip addr add 192.168.100.1/24 dev "$BRIDGE_NAME" +fi + +# Create TAP interface for the VM +log "Creating TAP interface $TAP_NAME..." +ip tuntap add dev "$TAP_NAME" mode tap +ip link set dev "$TAP_NAME" up +ip link set dev "$TAP_NAME" master "$BRIDGE_NAME" + +# Start the VM with Cloud Hypervisor +log "Starting VM $VM_NAME..." +VM_SOCKET="/tmp/cloud-hypervisor-$VM_NAME.sock" + +# Remove existing socket if it exists +rm -f "$VM_SOCKET" + +# Start Cloud Hypervisor in background +cloud-hypervisor \ + --api-socket "$VM_SOCKET" \ + --memory "size=${MEMORY_MB}M" \ + --cpus "boot=$CPU_CORES" \ + --kernel "$FIRMWARE_PATH" \ + --disk "path=$VM_IMAGE_PATH" \ + --disk "path=$CLOUD_INIT_PATH,readonly=on" \ + --net "tap=$TAP_NAME,mac=52:54:00:$(printf '%02x:%02x:%02x' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))" \ + --serial tty \ + --console off \ + --log-level info & + +VM_PID=$! + +log "VM $VM_NAME started with PID $VM_PID" +log "VM socket: $VM_SOCKET" +log "TAP interface: $TAP_NAME" +log "Bridge interface: $BRIDGE_NAME" + +# Save VM information for management +VM_INFO_FILE="$VM_SUBVOL_PATH/vm-info.txt" +cat > "$VM_INFO_FILE" << EOF +VM_NAME=$VM_NAME +VM_PID=$VM_PID +VM_SOCKET=$VM_SOCKET +TAP_NAME=$TAP_NAME +BRIDGE_NAME=$BRIDGE_NAME +MEMORY_MB=$MEMORY_MB +CPU_CORES=$CPU_CORES +VM_IMAGE_PATH=$VM_IMAGE_PATH +CLOUD_INIT_PATH=$CLOUD_INIT_PATH +STARTED=$(date) +EOF + +log "VM information saved to $VM_INFO_FILE" + +# Function to cleanup on exit +cleanup() { + log "Cleaning up VM $VM_NAME..." + if kill -0 "$VM_PID" 2>/dev/null; then + kill "$VM_PID" + wait "$VM_PID" 2>/dev/null + fi + ip link delete "$TAP_NAME" 2>/dev/null || true + rm -f "$VM_SOCKET" +} + +# Set trap for cleanup on script exit +trap cleanup EXIT INT TERM + +log "VM $VM_NAME is running. Press Ctrl+C to stop." +log "To connect via SSH (once VM is booted): ssh ubuntu@" +log "Default password: ubuntu (change after first login)" + +# Wait for the VM process +wait "$VM_PID" \ No newline at end of file