...
This commit is contained in:
parent
69c6a57fa4
commit
bcef07baa4
295
tools/VM_README.md
Normal file
295
tools/VM_README.md
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
# Ubuntu VM Management with Cloud Hypervisor
|
||||||
|
|
||||||
|
This directory contains scripts for creating and managing Ubuntu VMs using Cloud Hypervisor with btrfs thin provisioning.
|
||||||
|
|
||||||
|
## Scripts Overview
|
||||||
|
|
||||||
|
1. **`ubuntu_vm_start.sh`** - Creates and starts new VMs
|
||||||
|
2. **`ubuntu_vm_manage.sh`** - Manages existing VMs (list, status, console, stop, etc.)
|
||||||
|
3. **`setup_vm_network.sh`** - Sets up networking for SSH access to VMs
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Root access
|
||||||
|
- Btrfs filesystem (script will detect and guide setup)
|
||||||
|
- Cloud Hypervisor installed
|
||||||
|
- Basic networking tools
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Set up networking (one-time setup)
|
||||||
|
```bash
|
||||||
|
sudo ./setup_vm_network.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create and start a VM
|
||||||
|
```bash
|
||||||
|
sudo ./ubuntu_vm_start.sh my-vm 2048 2
|
||||||
|
```
|
||||||
|
This creates a VM named "my-vm" with 2GB RAM and 2 CPU cores.
|
||||||
|
|
||||||
|
### 3. List all VMs
|
||||||
|
```bash
|
||||||
|
sudo ./ubuntu_vm_manage.sh list
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Connect to VM console
|
||||||
|
```bash
|
||||||
|
sudo ./ubuntu_vm_manage.sh console my-vm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detailed Usage
|
||||||
|
|
||||||
|
### Creating VMs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ./ubuntu_vm_start.sh <vm_name> <memory_mb> <cpu_cores>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
# Create a small VM
|
||||||
|
sudo ./ubuntu_vm_start.sh test-vm 1024 1
|
||||||
|
|
||||||
|
# Create a larger VM
|
||||||
|
sudo ./ubuntu_vm_start.sh dev-vm 4096 4
|
||||||
|
```
|
||||||
|
|
||||||
|
**What happens:**
|
||||||
|
- Downloads Ubuntu 24.04 cloud image (first time only)
|
||||||
|
- Creates btrfs subvolumes for thin provisioning
|
||||||
|
- Sets up cloud-init with default user
|
||||||
|
- Creates network interfaces
|
||||||
|
- Starts the VM with Cloud Hypervisor
|
||||||
|
|
||||||
|
### Managing VMs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ./ubuntu_vm_manage.sh <command> [vm_name]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Available commands:**
|
||||||
|
|
||||||
|
| Command | Description | Example |
|
||||||
|
|---------|-------------|---------|
|
||||||
|
| `list` | Show all VMs and their status | `./ubuntu_vm_manage.sh list` |
|
||||||
|
| `status <vm>` | Detailed status of specific VM | `./ubuntu_vm_manage.sh status my-vm` |
|
||||||
|
| `console <vm>` | Connect to VM console | `./ubuntu_vm_manage.sh console my-vm` |
|
||||||
|
| `stop <vm>` | Stop a running VM | `./ubuntu_vm_manage.sh stop my-vm` |
|
||||||
|
| `delete <vm>` | Delete VM completely | `./ubuntu_vm_manage.sh delete my-vm` |
|
||||||
|
| `ssh <vm>` | Show SSH connection info | `./ubuntu_vm_manage.sh ssh my-vm` |
|
||||||
|
|
||||||
|
### Accessing VMs
|
||||||
|
|
||||||
|
#### Method 1: Console Access (Always Available)
|
||||||
|
```bash
|
||||||
|
sudo ./ubuntu_vm_manage.sh console my-vm
|
||||||
|
```
|
||||||
|
- Direct serial console access
|
||||||
|
- No network required
|
||||||
|
- Press `Ctrl+A` then `X` to exit
|
||||||
|
- Default login: `ubuntu` / `ubuntu`
|
||||||
|
|
||||||
|
#### Method 2: SSH Access (Requires Network Setup)
|
||||||
|
1. Set up networking first:
|
||||||
|
```bash
|
||||||
|
sudo ./setup_vm_network.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Find VM IP address:
|
||||||
|
```bash
|
||||||
|
vm-ips
|
||||||
|
# or
|
||||||
|
arp -a | grep 192.168.100
|
||||||
|
```
|
||||||
|
|
||||||
|
3. SSH to the VM:
|
||||||
|
```bash
|
||||||
|
ssh ubuntu@192.168.100.X
|
||||||
|
```
|
||||||
|
Default password: `ubuntu`
|
||||||
|
|
||||||
|
## Network Configuration
|
||||||
|
|
||||||
|
The `setup_vm_network.sh` script configures:
|
||||||
|
|
||||||
|
- **Bridge interface**: `br0` with IP `192.168.100.1/24`
|
||||||
|
- **DHCP server**: Assigns IPs `192.168.100.10-100` to VMs
|
||||||
|
- **NAT**: Enables internet access for VMs
|
||||||
|
- **DNS**: Uses Google DNS (8.8.8.8, 8.8.4.4)
|
||||||
|
|
||||||
|
### Network Troubleshooting
|
||||||
|
|
||||||
|
1. **Check bridge status:**
|
||||||
|
```bash
|
||||||
|
ip addr show br0
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Check VM IP assignments:**
|
||||||
|
```bash
|
||||||
|
vm-ips
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Check DHCP leases:**
|
||||||
|
```bash
|
||||||
|
cat /var/lib/dhcp/dhcpd.leases
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Restart networking:**
|
||||||
|
```bash
|
||||||
|
systemctl restart dnsmasq
|
||||||
|
```
|
||||||
|
|
||||||
|
## Storage (Btrfs)
|
||||||
|
|
||||||
|
VMs use btrfs subvolumes for efficient storage:
|
||||||
|
|
||||||
|
- **Base image**: Shared read-only Ubuntu image
|
||||||
|
- **VM snapshots**: Copy-on-write clones for each VM
|
||||||
|
- **Thin provisioning**: Only changed blocks use disk space
|
||||||
|
|
||||||
|
### Storage locations:
|
||||||
|
- Base images: `/var/lib/vms/base/`
|
||||||
|
- VM instances: `/var/lib/vms/vms/<vm-name>/`
|
||||||
|
|
||||||
|
### Storage commands:
|
||||||
|
```bash
|
||||||
|
# List subvolumes
|
||||||
|
btrfs subvolume list /var/lib/vms
|
||||||
|
|
||||||
|
# Show space usage
|
||||||
|
btrfs filesystem usage /var/lib/vms
|
||||||
|
|
||||||
|
# Show individual VM sizes
|
||||||
|
du -sh /var/lib/vms/vms/*
|
||||||
|
```
|
||||||
|
|
||||||
|
## VM Configuration
|
||||||
|
|
||||||
|
### Default VM Setup:
|
||||||
|
- **OS**: Ubuntu 24.04 LTS
|
||||||
|
- **User**: `ubuntu` (password: `ubuntu`)
|
||||||
|
- **Sudo**: Passwordless sudo enabled
|
||||||
|
- **SSH**: Enabled with password authentication
|
||||||
|
- **Packages**: curl, wget, git, htop, vim pre-installed
|
||||||
|
|
||||||
|
### Customizing VMs:
|
||||||
|
Edit the cloud-init configuration in `ubuntu_vm_start.sh` to:
|
||||||
|
- Add SSH keys
|
||||||
|
- Install additional packages
|
||||||
|
- Set custom passwords
|
||||||
|
- Configure services
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### VM won't start:
|
||||||
|
1. Check if Cloud Hypervisor is installed:
|
||||||
|
```bash
|
||||||
|
which cloud-hypervisor
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Check btrfs filesystem:
|
||||||
|
```bash
|
||||||
|
btrfs filesystem show /var/lib/vms
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Check available resources:
|
||||||
|
```bash
|
||||||
|
free -h
|
||||||
|
nproc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Can't connect to VM:
|
||||||
|
1. Verify VM is running:
|
||||||
|
```bash
|
||||||
|
./ubuntu_vm_manage.sh status my-vm
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Try console access first:
|
||||||
|
```bash
|
||||||
|
./ubuntu_vm_manage.sh console my-vm
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Check network setup:
|
||||||
|
```bash
|
||||||
|
ip addr show br0
|
||||||
|
vm-ips
|
||||||
|
```
|
||||||
|
|
||||||
|
### VM is slow or unresponsive:
|
||||||
|
1. Check host resources:
|
||||||
|
```bash
|
||||||
|
top
|
||||||
|
iostat
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Adjust VM resources:
|
||||||
|
- Stop VM: `./ubuntu_vm_manage.sh stop my-vm`
|
||||||
|
- Delete and recreate with different specs
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- Default password is `ubuntu` - change after first login
|
||||||
|
- Consider adding SSH keys instead of password auth
|
||||||
|
- VMs have internet access through NAT
|
||||||
|
- Console access requires root privileges on host
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Create a development environment:
|
||||||
|
```bash
|
||||||
|
# Set up networking
|
||||||
|
sudo ./setup_vm_network.sh
|
||||||
|
|
||||||
|
# Create VM
|
||||||
|
sudo ./ubuntu_vm_start.sh dev-env 4096 4
|
||||||
|
|
||||||
|
# Wait for boot, then connect
|
||||||
|
sudo ./ubuntu_vm_manage.sh console dev-env
|
||||||
|
|
||||||
|
# Inside VM, change password and install tools
|
||||||
|
passwd
|
||||||
|
sudo apt update && sudo apt install -y build-essential nodejs npm
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create multiple VMs:
|
||||||
|
```bash
|
||||||
|
# Create web server
|
||||||
|
sudo ./ubuntu_vm_start.sh web-server 2048 2
|
||||||
|
|
||||||
|
# Create database server
|
||||||
|
sudo ./ubuntu_vm_start.sh db-server 4096 2
|
||||||
|
|
||||||
|
# Create load balancer
|
||||||
|
sudo ./ubuntu_vm_start.sh lb 1024 1
|
||||||
|
|
||||||
|
# List all VMs
|
||||||
|
sudo ./ubuntu_vm_manage.sh list
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Tips
|
||||||
|
|
||||||
|
1. **Use appropriate VM sizing** - Don't over-allocate resources
|
||||||
|
2. **Monitor host resources** - Ensure sufficient RAM/CPU
|
||||||
|
3. **Use btrfs compression** - Add `compress=zstd` mount option
|
||||||
|
4. **Regular cleanup** - Delete unused VMs to free space
|
||||||
|
5. **SSD storage** - Use SSD for better I/O performance
|
||||||
|
|
||||||
|
## Backup and Recovery
|
||||||
|
|
||||||
|
### Backup a VM:
|
||||||
|
```bash
|
||||||
|
# Stop VM first
|
||||||
|
sudo ./ubuntu_vm_manage.sh stop my-vm
|
||||||
|
|
||||||
|
# Create snapshot
|
||||||
|
sudo btrfs subvolume snapshot /var/lib/vms/vms/my-vm /var/lib/vms/backups/my-vm-$(date +%Y%m%d)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore from backup:
|
||||||
|
```bash
|
||||||
|
# Delete current VM
|
||||||
|
sudo ./ubuntu_vm_manage.sh delete my-vm
|
||||||
|
|
||||||
|
# Restore from snapshot
|
||||||
|
sudo btrfs subvolume snapshot /var/lib/vms/backups/my-vm-20231215 /var/lib/vms/vms/my-vm
|
@ -176,3 +176,5 @@ if command -v cloud-hypervisor &> /dev/null; then
|
|||||||
else
|
else
|
||||||
error "Cloud Hypervisor installation failed. Binary not found in PATH."
|
error "Cloud Hypervisor installation failed. Binary not found in PATH."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
apt update && apt install -y genisoimage
|
151
tools/setup_vm_network.sh
Executable file
151
tools/setup_vm_network.sh
Executable file
@ -0,0 +1,151 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# VM Network Setup Script
|
||||||
|
# This script sets up networking for VMs to enable SSH access
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BRIDGE_NAME="br0"
|
||||||
|
BRIDGE_IP="192.168.100.1/24"
|
||||||
|
NETWORK="192.168.100.0/24"
|
||||||
|
|
||||||
|
log "Setting up VM networking..."
|
||||||
|
|
||||||
|
# Check if bridge already exists and has IP
|
||||||
|
if ip link show "$BRIDGE_NAME" &>/dev/null; then
|
||||||
|
if ip addr show "$BRIDGE_NAME" | grep -q "192.168.100.1"; then
|
||||||
|
info "Bridge $BRIDGE_NAME already configured with IP"
|
||||||
|
else
|
||||||
|
log "Adding IP address to existing bridge..."
|
||||||
|
ip addr add "$BRIDGE_IP" dev "$BRIDGE_NAME"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "Bridge $BRIDGE_NAME not found. It will be created when VMs start."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable IP forwarding
|
||||||
|
log "Enabling IP forwarding..."
|
||||||
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||||
|
|
||||||
|
# Make IP forwarding persistent
|
||||||
|
if ! grep -q "net.ipv4.ip_forward=1" /etc/sysctl.conf; then
|
||||||
|
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
|
||||||
|
log "IP forwarding made persistent in /etc/sysctl.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set up NAT for VM network
|
||||||
|
log "Setting up NAT for VM network..."
|
||||||
|
|
||||||
|
# Remove existing rules to avoid duplicates
|
||||||
|
iptables -t nat -D POSTROUTING -s "$NETWORK" -j MASQUERADE 2>/dev/null || true
|
||||||
|
iptables -D FORWARD -i "$BRIDGE_NAME" -o "$BRIDGE_NAME" -j ACCEPT 2>/dev/null || true
|
||||||
|
iptables -D FORWARD -i "$BRIDGE_NAME" -j ACCEPT 2>/dev/null || true
|
||||||
|
iptables -D FORWARD -o "$BRIDGE_NAME" -j ACCEPT 2>/dev/null || true
|
||||||
|
|
||||||
|
# Add new rules
|
||||||
|
iptables -t nat -A POSTROUTING -s "$NETWORK" -j MASQUERADE
|
||||||
|
iptables -A FORWARD -i "$BRIDGE_NAME" -o "$BRIDGE_NAME" -j ACCEPT
|
||||||
|
iptables -A FORWARD -i "$BRIDGE_NAME" -j ACCEPT
|
||||||
|
iptables -A FORWARD -o "$BRIDGE_NAME" -j ACCEPT
|
||||||
|
|
||||||
|
log "NAT rules configured"
|
||||||
|
|
||||||
|
# Install and configure dnsmasq for DHCP
|
||||||
|
if ! command -v dnsmasq &>/dev/null; then
|
||||||
|
log "Installing dnsmasq for DHCP..."
|
||||||
|
apt update && apt install -y dnsmasq
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure dnsmasq for VM network
|
||||||
|
DNSMASQ_CONF="/etc/dnsmasq.d/vm-network.conf"
|
||||||
|
log "Configuring DHCP for VM network..."
|
||||||
|
|
||||||
|
cat > "$DNSMASQ_CONF" << EOF
|
||||||
|
# VM Network DHCP Configuration
|
||||||
|
# Only bind to the bridge interface to avoid conflicts with systemd-resolved
|
||||||
|
interface=$BRIDGE_NAME
|
||||||
|
bind-interfaces
|
||||||
|
|
||||||
|
# Disable DNS server functionality (only DHCP)
|
||||||
|
port=0
|
||||||
|
|
||||||
|
# DHCP configuration
|
||||||
|
dhcp-range=192.168.100.10,192.168.100.100,12h
|
||||||
|
dhcp-option=3,192.168.100.1
|
||||||
|
dhcp-option=6,8.8.8.8,8.8.4.4
|
||||||
|
|
||||||
|
# Disable reading /etc/hosts and /etc/resolv.conf
|
||||||
|
no-hosts
|
||||||
|
no-resolv
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Restart dnsmasq
|
||||||
|
systemctl restart dnsmasq
|
||||||
|
systemctl enable dnsmasq
|
||||||
|
|
||||||
|
log "DHCP server configured and started"
|
||||||
|
|
||||||
|
# Create a script to show VM IPs
|
||||||
|
cat > "/usr/local/bin/vm-ips" << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "VM DHCP Leases:"
|
||||||
|
echo "==============="
|
||||||
|
if [ -f /var/lib/dhcp/dhcpd.leases ]; then
|
||||||
|
awk '/lease/ { ip = $2 } /client-hostname/ { hostname = $2; gsub(/[";]/, "", hostname) } /binding state active/ { print ip " - " hostname }' /var/lib/dhcp/dhcpd.leases
|
||||||
|
elif [ -f /var/lib/dhcpcd5/dhcpcd.leases ]; then
|
||||||
|
cat /var/lib/dhcpcd5/dhcpcd.leases
|
||||||
|
else
|
||||||
|
echo "DHCP lease file not found. Checking dnsmasq leases..."
|
||||||
|
if [ -f /var/lib/dhcp/dhcpd.leases ]; then
|
||||||
|
cat /var/lib/dhcp/dhcpd.leases
|
||||||
|
else
|
||||||
|
echo "No lease information available"
|
||||||
|
echo "Try: arp -a | grep 192.168.100"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x /usr/local/bin/vm-ips
|
||||||
|
|
||||||
|
log "Network setup completed!"
|
||||||
|
echo ""
|
||||||
|
info "Network Configuration Summary:"
|
||||||
|
info "- Bridge: $BRIDGE_NAME with IP $BRIDGE_IP"
|
||||||
|
info "- DHCP range: 192.168.100.10 - 192.168.100.100"
|
||||||
|
info "- DNS servers: 8.8.8.8, 8.8.4.4"
|
||||||
|
info "- NAT configured for internet access"
|
||||||
|
echo ""
|
||||||
|
info "To see VM IP addresses: vm-ips"
|
||||||
|
info "To check bridge status: ip addr show $BRIDGE_NAME"
|
||||||
|
info "To see DHCP leases: cat /var/lib/dhcp/dhcpd.leases"
|
||||||
|
echo ""
|
||||||
|
warn "Note: VMs need to be restarted to get DHCP IP addresses"
|
392
tools/ubuntu_vm_manage.sh
Executable file
392
tools/ubuntu_vm_manage.sh
Executable file
@ -0,0 +1,392 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Ubuntu VM Management Script
|
||||||
|
# Usage: ubuntu_vm_manage.sh <command> [vm_name]
|
||||||
|
|
||||||
|
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
|
||||||
|
VM_BASE_DIR="/var/lib/vms"
|
||||||
|
VMS_SUBVOL="$VM_BASE_DIR/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}"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_usage() {
|
||||||
|
echo "Ubuntu VM Management Script"
|
||||||
|
echo ""
|
||||||
|
echo "Usage: $0 <command> [vm_name]"
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo " list - List all VMs and their status"
|
||||||
|
echo " status <vm_name> - Show detailed status of a specific VM"
|
||||||
|
echo " console <vm_name> - Connect to VM console (serial)"
|
||||||
|
echo " ssh <vm_name> - SSH to VM (requires network setup)"
|
||||||
|
echo " stop <vm_name> - Stop a running VM"
|
||||||
|
echo " start <vm_name> - Start a stopped VM"
|
||||||
|
echo " delete <vm_name> - Delete a VM completely"
|
||||||
|
echo " ip <vm_name> - Show VM IP address"
|
||||||
|
echo " logs <vm_name> - Show VM logs"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 list"
|
||||||
|
echo " $0 status test-vm"
|
||||||
|
echo " $0 console test-vm"
|
||||||
|
echo " $0 ssh test-vm"
|
||||||
|
}
|
||||||
|
|
||||||
|
list_vms() {
|
||||||
|
log "Listing all VMs..."
|
||||||
|
echo ""
|
||||||
|
printf "%-15s %-10s %-8s %-15s %-20s\n" "VM NAME" "STATUS" "PID" "MEMORY" "STARTED"
|
||||||
|
printf "%-15s %-10s %-8s %-15s %-20s\n" "-------" "------" "---" "------" "-------"
|
||||||
|
|
||||||
|
if [ ! -d "$VMS_SUBVOL" ]; then
|
||||||
|
warn "No VMs directory found at $VMS_SUBVOL"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
for vm_dir in "$VMS_SUBVOL"/*; do
|
||||||
|
if [ -d "$vm_dir" ]; then
|
||||||
|
vm_name=$(basename "$vm_dir")
|
||||||
|
vm_info_file="$vm_dir/vm-info.txt"
|
||||||
|
|
||||||
|
if [ -f "$vm_info_file" ]; then
|
||||||
|
# Safely source the file with error handling
|
||||||
|
if source "$vm_info_file" 2>/dev/null; then
|
||||||
|
# Check if VM is running
|
||||||
|
if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then
|
||||||
|
status="${GREEN}RUNNING${NC}"
|
||||||
|
pid="$VM_PID"
|
||||||
|
else
|
||||||
|
status="${RED}STOPPED${NC}"
|
||||||
|
pid="N/A"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle missing or malformed STARTED field
|
||||||
|
if [ -z "$STARTED" ]; then
|
||||||
|
STARTED="Unknown"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%-15s %-18s %-8s %-15s %-20s\n" "$vm_name" "$(printf "%b" "$status")" "$pid" "${MEMORY_MB}MB" "$STARTED"
|
||||||
|
else
|
||||||
|
printf "%-15s %-18s %-8s %-15s %-20s\n" "$vm_name" "$(printf "%b" "${RED}ERROR${NC}")" "N/A" "N/A" "Config Error"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "%-15s %-18s %-8s %-15s %-20s\n" "$vm_name" "$(printf "%b" "${YELLOW}UNKNOWN${NC}")" "N/A" "N/A" "N/A"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
show_vm_status() {
|
||||||
|
local vm_name="$1"
|
||||||
|
local vm_dir="$VMS_SUBVOL/$vm_name"
|
||||||
|
local vm_info_file="$vm_dir/vm-info.txt"
|
||||||
|
|
||||||
|
if [ ! -d "$vm_dir" ]; then
|
||||||
|
error "VM '$vm_name' not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$vm_info_file" ]; then
|
||||||
|
error "VM info file not found for '$vm_name'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
source "$vm_info_file"
|
||||||
|
|
||||||
|
log "VM Status for: $vm_name"
|
||||||
|
echo ""
|
||||||
|
echo "VM Name: $VM_NAME"
|
||||||
|
echo "Memory: ${MEMORY_MB}MB"
|
||||||
|
echo "CPU Cores: $CPU_CORES"
|
||||||
|
echo "Started: $STARTED"
|
||||||
|
echo "Image Path: $VM_IMAGE_PATH"
|
||||||
|
echo "Cloud-init: $CLOUD_INIT_PATH"
|
||||||
|
echo "Socket: $VM_SOCKET"
|
||||||
|
echo "TAP Interface: $TAP_NAME"
|
||||||
|
echo "Bridge: $BRIDGE_NAME"
|
||||||
|
|
||||||
|
# Check if VM is running
|
||||||
|
if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then
|
||||||
|
echo "Status: ${GREEN}RUNNING${NC} (PID: $VM_PID)"
|
||||||
|
|
||||||
|
# Show network info
|
||||||
|
if ip link show "$TAP_NAME" &>/dev/null; then
|
||||||
|
echo "Network: ${GREEN}TAP interface active${NC}"
|
||||||
|
else
|
||||||
|
echo "Network: ${RED}TAP interface not found${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show socket info
|
||||||
|
if [ -S "$VM_SOCKET" ]; then
|
||||||
|
echo "API Socket: ${GREEN}Available${NC}"
|
||||||
|
else
|
||||||
|
echo "API Socket: ${RED}Not available${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Status: ${RED}STOPPED${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
connect_console() {
|
||||||
|
local vm_name="$1"
|
||||||
|
local vm_dir="$VMS_SUBVOL/$vm_name"
|
||||||
|
local vm_info_file="$vm_dir/vm-info.txt"
|
||||||
|
|
||||||
|
if [ ! -f "$vm_info_file" ]; then
|
||||||
|
error "VM '$vm_name' not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
source "$vm_info_file"
|
||||||
|
|
||||||
|
if [ -z "$VM_PID" ] || ! kill -0 "$VM_PID" 2>/dev/null; then
|
||||||
|
error "VM '$vm_name' is not running"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Connecting to console for VM '$vm_name'"
|
||||||
|
info "Press Ctrl+A then X to exit console"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Connect to the VM's console via the API socket
|
||||||
|
if [ -S "$VM_SOCKET" ]; then
|
||||||
|
# Use socat to connect to the console
|
||||||
|
if command -v socat &>/dev/null; then
|
||||||
|
socat - UNIX-CONNECT:"$VM_SOCKET"
|
||||||
|
else
|
||||||
|
warn "socat not found. Installing..."
|
||||||
|
apt update && apt install -y socat
|
||||||
|
socat - UNIX-CONNECT:"$VM_SOCKET"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
error "VM socket not found at $VM_SOCKET"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ssh_to_vm() {
|
||||||
|
local vm_name="$1"
|
||||||
|
|
||||||
|
warn "SSH connection requires:"
|
||||||
|
warn "1. VM to be fully booted"
|
||||||
|
warn "2. Network bridge configured with IP"
|
||||||
|
warn "3. VM to have received IP via DHCP"
|
||||||
|
echo ""
|
||||||
|
info "To set up networking:"
|
||||||
|
info "1. Configure bridge IP: ip addr add 192.168.100.1/24 dev br0"
|
||||||
|
info "2. Enable IP forwarding: echo 1 > /proc/sys/net/ipv4/ip_forward"
|
||||||
|
info "3. Set up NAT: iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -j MASQUERADE"
|
||||||
|
echo ""
|
||||||
|
info "Default login: ubuntu / ubuntu"
|
||||||
|
info "SSH command: ssh ubuntu@<vm-ip>"
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_vm() {
|
||||||
|
local vm_name="$1"
|
||||||
|
local vm_dir="$VMS_SUBVOL/$vm_name"
|
||||||
|
local vm_info_file="$vm_dir/vm-info.txt"
|
||||||
|
|
||||||
|
if [ ! -f "$vm_info_file" ]; then
|
||||||
|
error "VM '$vm_name' not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
source "$vm_info_file"
|
||||||
|
|
||||||
|
if [ -z "$VM_PID" ] || ! kill -0 "$VM_PID" 2>/dev/null; then
|
||||||
|
warn "VM '$vm_name' is not running"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Stopping VM '$vm_name' (PID: $VM_PID)..."
|
||||||
|
|
||||||
|
# Graceful shutdown first
|
||||||
|
kill -TERM "$VM_PID" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Wait a bit for graceful shutdown
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# Force kill if still running
|
||||||
|
if kill -0 "$VM_PID" 2>/dev/null; then
|
||||||
|
warn "Forcing shutdown..."
|
||||||
|
kill -KILL "$VM_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cleanup network
|
||||||
|
if [ -n "$TAP_NAME" ]; then
|
||||||
|
ip link delete "$TAP_NAME" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove socket
|
||||||
|
if [ -n "$VM_SOCKET" ]; then
|
||||||
|
rm -f "$VM_SOCKET"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "VM '$vm_name' stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_vm() {
|
||||||
|
local vm_name="$1"
|
||||||
|
local vm_dir="$VMS_SUBVOL/$vm_name"
|
||||||
|
|
||||||
|
if [ ! -d "$vm_dir" ]; then
|
||||||
|
error "VM '$vm_name' not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop VM first if running
|
||||||
|
if [ -f "$vm_dir/vm-info.txt" ]; then
|
||||||
|
source "$vm_dir/vm-info.txt"
|
||||||
|
if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then
|
||||||
|
log "Stopping VM before deletion..."
|
||||||
|
stop_vm "$vm_name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Confirm deletion
|
||||||
|
echo -e "${RED}WARNING: This will permanently delete VM '$vm_name' and all its data!${NC}"
|
||||||
|
read -p "Are you sure? (yes/no): " confirm
|
||||||
|
|
||||||
|
if [ "$confirm" = "yes" ]; then
|
||||||
|
log "Deleting VM '$vm_name'..."
|
||||||
|
btrfs subvolume delete "$vm_dir"
|
||||||
|
log "VM '$vm_name' deleted successfully"
|
||||||
|
else
|
||||||
|
info "Deletion cancelled"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start_vm() {
|
||||||
|
local vm_name="$1"
|
||||||
|
local vm_dir="$VMS_SUBVOL/$vm_name"
|
||||||
|
local vm_info_file="$vm_dir/vm-info.txt"
|
||||||
|
|
||||||
|
if [ ! -d "$vm_dir" ]; then
|
||||||
|
error "VM '$vm_name' not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$vm_info_file" ]; then
|
||||||
|
error "VM info file not found for '$vm_name'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
source "$vm_info_file"
|
||||||
|
|
||||||
|
# Check if VM is already running
|
||||||
|
if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then
|
||||||
|
warn "VM '$vm_name' is already running (PID: $VM_PID)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Starting VM '$vm_name'..."
|
||||||
|
|
||||||
|
# Create TAP interface
|
||||||
|
if ! ip link show "$TAP_NAME" &>/dev/null; then
|
||||||
|
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"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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 "/var/lib/vms/base/hypervisor-fw" \
|
||||||
|
--disk "path=$VM_IMAGE_PATH" "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-file /tmp/cloud-hypervisor-$VM_NAME.log &
|
||||||
|
|
||||||
|
NEW_VM_PID=$!
|
||||||
|
|
||||||
|
# Update VM info file with new PID
|
||||||
|
sed -i "s/VM_PID=.*/VM_PID=$NEW_VM_PID/" "$vm_info_file"
|
||||||
|
|
||||||
|
log "VM '$vm_name' started with PID $NEW_VM_PID"
|
||||||
|
log "VM socket: $VM_SOCKET"
|
||||||
|
log "TAP interface: $TAP_NAME"
|
||||||
|
log "To connect to console: ./ubuntu_vm_manage.sh console $vm_name"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main script logic
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
show_usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
COMMAND="$1"
|
||||||
|
VM_NAME="$2"
|
||||||
|
|
||||||
|
case "$COMMAND" in
|
||||||
|
"list")
|
||||||
|
list_vms
|
||||||
|
;;
|
||||||
|
"status")
|
||||||
|
if [ -z "$VM_NAME" ]; then
|
||||||
|
error "VM name required for status command"
|
||||||
|
fi
|
||||||
|
show_vm_status "$VM_NAME"
|
||||||
|
;;
|
||||||
|
"console")
|
||||||
|
if [ -z "$VM_NAME" ]; then
|
||||||
|
error "VM name required for console command"
|
||||||
|
fi
|
||||||
|
connect_console "$VM_NAME"
|
||||||
|
;;
|
||||||
|
"ssh")
|
||||||
|
if [ -z "$VM_NAME" ]; then
|
||||||
|
error "VM name required for ssh command"
|
||||||
|
fi
|
||||||
|
ssh_to_vm "$VM_NAME"
|
||||||
|
;;
|
||||||
|
"stop")
|
||||||
|
if [ -z "$VM_NAME" ]; then
|
||||||
|
error "VM name required for stop command"
|
||||||
|
fi
|
||||||
|
stop_vm "$VM_NAME"
|
||||||
|
;;
|
||||||
|
"start")
|
||||||
|
if [ -z "$VM_NAME" ]; then
|
||||||
|
error "VM name required for start command"
|
||||||
|
fi
|
||||||
|
start_vm "$VM_NAME"
|
||||||
|
;;
|
||||||
|
"delete")
|
||||||
|
if [ -z "$VM_NAME" ]; then
|
||||||
|
error "VM name required for delete command"
|
||||||
|
fi
|
||||||
|
delete_vm "$VM_NAME"
|
||||||
|
;;
|
||||||
|
"ip"|"logs")
|
||||||
|
warn "Command '$COMMAND' not yet implemented"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Unknown command: $COMMAND"
|
||||||
|
show_usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
@ -82,10 +82,13 @@ fi
|
|||||||
mkdir -p "$VM_BASE_DIR"
|
mkdir -p "$VM_BASE_DIR"
|
||||||
|
|
||||||
# Check if the base directory is on btrfs
|
# Check if the base directory is on btrfs
|
||||||
if ! btrfs filesystem show "$VM_BASE_DIR" &>/dev/null; then
|
FILESYSTEM_TYPE=$(stat -f -c %T "$VM_BASE_DIR" 2>/dev/null)
|
||||||
error "Base directory $VM_BASE_DIR is not on a btrfs filesystem. Please create a btrfs filesystem first."
|
if [ "$FILESYSTEM_TYPE" != "btrfs" ]; then
|
||||||
|
error "Base directory $VM_BASE_DIR is not on a btrfs filesystem (detected: $FILESYSTEM_TYPE). Please create a btrfs filesystem first."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
log "Btrfs filesystem detected at $VM_BASE_DIR"
|
||||||
|
|
||||||
# Create base and vms subvolumes if they don't exist
|
# Create base and vms subvolumes if they don't exist
|
||||||
if [ ! -d "$BASE_SUBVOL" ]; then
|
if [ ! -d "$BASE_SUBVOL" ]; then
|
||||||
log "Creating base subvolume at $BASE_SUBVOL"
|
log "Creating base subvolume at $BASE_SUBVOL"
|
||||||
@ -240,12 +243,11 @@ cloud-hypervisor \
|
|||||||
--memory "size=${MEMORY_MB}M" \
|
--memory "size=${MEMORY_MB}M" \
|
||||||
--cpus "boot=$CPU_CORES" \
|
--cpus "boot=$CPU_CORES" \
|
||||||
--kernel "$FIRMWARE_PATH" \
|
--kernel "$FIRMWARE_PATH" \
|
||||||
--disk "path=$VM_IMAGE_PATH" \
|
--disk "path=$VM_IMAGE_PATH" "path=$CLOUD_INIT_PATH,readonly=on" \
|
||||||
--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)))" \
|
--net "tap=$TAP_NAME,mac=52:54:00:$(printf '%02x:%02x:%02x' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))" \
|
||||||
--serial tty \
|
--serial tty \
|
||||||
--console off \
|
--console off \
|
||||||
--log-level info &
|
--log-file /tmp/cloud-hypervisor-$VM_NAME.log &
|
||||||
|
|
||||||
VM_PID=$!
|
VM_PID=$!
|
||||||
|
|
||||||
@ -266,7 +268,7 @@ MEMORY_MB=$MEMORY_MB
|
|||||||
CPU_CORES=$CPU_CORES
|
CPU_CORES=$CPU_CORES
|
||||||
VM_IMAGE_PATH=$VM_IMAGE_PATH
|
VM_IMAGE_PATH=$VM_IMAGE_PATH
|
||||||
CLOUD_INIT_PATH=$CLOUD_INIT_PATH
|
CLOUD_INIT_PATH=$CLOUD_INIT_PATH
|
||||||
STARTED=$(date)
|
STARTED="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
log "VM information saved to $VM_INFO_FILE"
|
log "VM information saved to $VM_INFO_FILE"
|
||||||
|
Loading…
Reference in New Issue
Block a user