959 líneas
25 KiB
Bash
Archivo Ejecutable
959 líneas
25 KiB
Bash
Archivo Ejecutable
#!/bin/bash
|
|
|
|
##############################################################################
|
|
# EasyQEMU - Intuitive QEMU Virtual Machine Management Tool
|
|
#
|
|
# A comprehensive tool for managing QEMU virtual machines with features like:
|
|
# - VM creation, modification, deletion
|
|
# - Volume management (create, import, export, convert)
|
|
# - Snapshot management
|
|
# - Repository system for VMs and volumes (Docker-like registry)
|
|
# - Support for multiple disk formats (raw, qcow2, vmdk, vdi, vhdx)
|
|
#
|
|
# Author: EasyQEMU Project
|
|
# License: MIT
|
|
##############################################################################
|
|
|
|
set -e
|
|
|
|
# Version
|
|
VERSION="1.0.0"
|
|
|
|
# Default paths
|
|
EASYQEMU_HOME="${EASYQEMU_HOME:-$HOME/.easyqemu}"
|
|
EASYQEMU_VMS="$EASYQEMU_HOME/vms"
|
|
EASYQEMU_VOLUMES="$EASYQEMU_HOME/volumes"
|
|
EASYQEMU_CONFIGS="$EASYQEMU_HOME/configs"
|
|
EASYQEMU_REPO="$EASYQEMU_HOME/repository"
|
|
EASYQEMU_LOCKS="$EASYQEMU_HOME/locks"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
MAGENTA='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
##############################################################################
|
|
# Utility Functions
|
|
##############################################################################
|
|
|
|
info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
error() {
|
|
echo -e "${RED}[ERROR]${NC} $1" >&2
|
|
}
|
|
|
|
fatal() {
|
|
error "$1"
|
|
exit 1
|
|
}
|
|
|
|
# Initialize EasyQEMU directories
|
|
init_dirs() {
|
|
mkdir -p "$EASYQEMU_HOME"
|
|
mkdir -p "$EASYQEMU_VMS"
|
|
mkdir -p "$EASYQEMU_VOLUMES"
|
|
mkdir -p "$EASYQEMU_CONFIGS"
|
|
mkdir -p "$EASYQEMU_REPO"
|
|
mkdir -p "$EASYQEMU_LOCKS"
|
|
}
|
|
|
|
# Check if QEMU is installed
|
|
check_qemu() {
|
|
if ! command -v qemu-system-x86_64 &> /dev/null; then
|
|
fatal "QEMU is not installed. Please install qemu-system-x86_64"
|
|
fi
|
|
if ! command -v qemu-img &> /dev/null; then
|
|
fatal "qemu-img is not installed. Please install qemu-utils"
|
|
fi
|
|
}
|
|
|
|
# Generate VM ID
|
|
generate_vm_id() {
|
|
local name="$1"
|
|
echo "${name}_$(date +%s)"
|
|
}
|
|
|
|
# Save VM configuration
|
|
save_vm_config() {
|
|
local vm_id="$1"
|
|
local config_file="$EASYQEMU_CONFIGS/${vm_id}.json"
|
|
shift
|
|
|
|
cat > "$config_file" << EOF
|
|
{
|
|
"id": "$vm_id",
|
|
"name": "${VM_NAME:-unknown}",
|
|
"created": "$(date -Iseconds)",
|
|
"memory": "${VM_MEMORY:-2048}",
|
|
"cpus": "${VM_CPUS:-2}",
|
|
"disk": "${VM_DISK:-}",
|
|
"cdrom": "${VM_CDROM:-}",
|
|
"network": "${VM_NETWORK:-user}",
|
|
"display": "${VM_DISPLAY:-gtk}",
|
|
"boot": "${VM_BOOT:-cd}",
|
|
"additional_args": "${VM_ADDITIONAL_ARGS:-}"
|
|
}
|
|
EOF
|
|
success "Configuration saved: $config_file"
|
|
}
|
|
|
|
# Load VM configuration
|
|
load_vm_config() {
|
|
local vm_id="$1"
|
|
local config_file="$EASYQEMU_CONFIGS/${vm_id}.json"
|
|
|
|
if [[ ! -f "$config_file" ]]; then
|
|
error "Configuration not found for VM: $vm_id"
|
|
return 1
|
|
fi
|
|
|
|
# Parse JSON (simple extraction)
|
|
VM_NAME=$(grep '"name"' "$config_file" | cut -d'"' -f4)
|
|
VM_MEMORY=$(grep '"memory"' "$config_file" | cut -d'"' -f4)
|
|
VM_CPUS=$(grep '"cpus"' "$config_file" | cut -d'"' -f4)
|
|
VM_DISK=$(grep '"disk"' "$config_file" | cut -d'"' -f4)
|
|
VM_CDROM=$(grep '"cdrom"' "$config_file" | cut -d'"' -f4)
|
|
VM_NETWORK=$(grep '"network"' "$config_file" | cut -d'"' -f4)
|
|
VM_DISPLAY=$(grep '"display"' "$config_file" | cut -d'"' -f4)
|
|
VM_BOOT=$(grep '"boot"' "$config_file" | cut -d'"' -f4)
|
|
VM_ADDITIONAL_ARGS=$(grep '"additional_args"' "$config_file" | cut -d'"' -f4)
|
|
}
|
|
|
|
##############################################################################
|
|
# VM Management Functions
|
|
##############################################################################
|
|
|
|
# Create a new VM
|
|
vm_create() {
|
|
local name=""
|
|
local memory="2048"
|
|
local cpus="2"
|
|
local disk=""
|
|
local disk_size="20G"
|
|
local cdrom=""
|
|
local network="user"
|
|
local display="gtk"
|
|
local boot="cd"
|
|
local additional_args=""
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--name)
|
|
name="$2"
|
|
shift 2
|
|
;;
|
|
--memory)
|
|
memory="$2"
|
|
shift 2
|
|
;;
|
|
--cpus)
|
|
cpus="$2"
|
|
shift 2
|
|
;;
|
|
--disk)
|
|
disk="$2"
|
|
shift 2
|
|
;;
|
|
--disk-size)
|
|
disk_size="$2"
|
|
shift 2
|
|
;;
|
|
--cdrom)
|
|
cdrom="$2"
|
|
shift 2
|
|
;;
|
|
--network)
|
|
network="$2"
|
|
shift 2
|
|
;;
|
|
--display)
|
|
display="$2"
|
|
shift 2
|
|
;;
|
|
--boot)
|
|
boot="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
additional_args="$additional_args $1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
[[ -z "$name" ]] && fatal "VM name is required. Use --name <name>"
|
|
|
|
info "Creating VM: $name"
|
|
|
|
# Generate VM ID
|
|
local vm_id=$(generate_vm_id "$name")
|
|
|
|
# Create disk if not provided
|
|
if [[ -z "$disk" ]]; then
|
|
disk="$EASYQEMU_VOLUMES/${vm_id}.qcow2"
|
|
info "Creating disk: $disk ($disk_size)"
|
|
qemu-img create -f qcow2 "$disk" "$disk_size"
|
|
fi
|
|
|
|
# Save configuration
|
|
VM_NAME="$name"
|
|
VM_MEMORY="$memory"
|
|
VM_CPUS="$cpus"
|
|
VM_DISK="$disk"
|
|
VM_CDROM="$cdrom"
|
|
VM_NETWORK="$network"
|
|
VM_DISPLAY="$display"
|
|
VM_BOOT="$boot"
|
|
VM_ADDITIONAL_ARGS="$additional_args"
|
|
|
|
save_vm_config "$vm_id"
|
|
|
|
success "VM created successfully: $vm_id"
|
|
echo "Use 'easyqemu vm start $vm_id' to start the VM"
|
|
}
|
|
|
|
# List VMs
|
|
vm_list() {
|
|
info "Available VMs:"
|
|
echo ""
|
|
printf "%-30s %-20s %-15s %-10s %-10s\n" "VM ID" "NAME" "CREATED" "MEMORY" "CPUS"
|
|
echo "--------------------------------------------------------------------------------"
|
|
|
|
for config in "$EASYQEMU_CONFIGS"/*.json; do
|
|
[[ ! -f "$config" ]] && continue
|
|
|
|
local vm_id=$(basename "$config" .json)
|
|
load_vm_config "$vm_id"
|
|
|
|
local created=$(grep '"created"' "$config" | cut -d'"' -f4 | cut -d'T' -f1)
|
|
printf "%-30s %-20s %-15s %-10s %-10s\n" "$vm_id" "$VM_NAME" "$created" "${VM_MEMORY}M" "$VM_CPUS"
|
|
done
|
|
}
|
|
|
|
# Start VM
|
|
vm_start() {
|
|
local vm_id="$1"
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
|
|
load_vm_config "$vm_id" || fatal "Failed to load VM configuration"
|
|
|
|
info "Starting VM: $vm_id ($VM_NAME)"
|
|
|
|
# Build QEMU command
|
|
local qemu_cmd="qemu-system-x86_64"
|
|
qemu_cmd="$qemu_cmd -name $VM_NAME"
|
|
qemu_cmd="$qemu_cmd -m $VM_MEMORY"
|
|
qemu_cmd="$qemu_cmd -smp $VM_CPUS"
|
|
qemu_cmd="$qemu_cmd -enable-kvm"
|
|
|
|
# Disk
|
|
if [[ -n "$VM_DISK" ]]; then
|
|
qemu_cmd="$qemu_cmd -drive file=$VM_DISK,format=qcow2,if=virtio"
|
|
fi
|
|
|
|
# CDROM
|
|
if [[ -n "$VM_CDROM" ]]; then
|
|
qemu_cmd="$qemu_cmd -cdrom $VM_CDROM"
|
|
fi
|
|
|
|
# Network
|
|
qemu_cmd="$qemu_cmd -net nic -net $VM_NETWORK"
|
|
|
|
# Display
|
|
if [[ "$VM_DISPLAY" == "none" ]]; then
|
|
qemu_cmd="$qemu_cmd -nographic"
|
|
else
|
|
qemu_cmd="$qemu_cmd -display $VM_DISPLAY"
|
|
fi
|
|
|
|
# Boot
|
|
qemu_cmd="$qemu_cmd -boot $VM_BOOT"
|
|
|
|
# Additional args
|
|
if [[ -n "$VM_ADDITIONAL_ARGS" ]]; then
|
|
qemu_cmd="$qemu_cmd $VM_ADDITIONAL_ARGS"
|
|
fi
|
|
|
|
info "Executing: $qemu_cmd"
|
|
eval "$qemu_cmd"
|
|
}
|
|
|
|
# Delete VM
|
|
vm_delete() {
|
|
local vm_id="$1"
|
|
local force="$2"
|
|
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
|
|
local config_file="$EASYQEMU_CONFIGS/${vm_id}.json"
|
|
[[ ! -f "$config_file" ]] && fatal "VM not found: $vm_id"
|
|
|
|
load_vm_config "$vm_id"
|
|
|
|
if [[ "$force" != "-f" ]] && [[ "$force" != "--force" ]]; then
|
|
read -p "Delete VM '$VM_NAME' ($vm_id)? This will remove the disk. [y/N] " -n 1 -r
|
|
echo
|
|
[[ ! $REPLY =~ ^[Yy]$ ]] && fatal "Deletion cancelled"
|
|
fi
|
|
|
|
info "Deleting VM: $vm_id"
|
|
|
|
# Remove disk if it exists
|
|
if [[ -f "$VM_DISK" ]]; then
|
|
rm -f "$VM_DISK"
|
|
success "Disk removed: $VM_DISK"
|
|
fi
|
|
|
|
# Remove configuration
|
|
rm -f "$config_file"
|
|
|
|
success "VM deleted: $vm_id"
|
|
}
|
|
|
|
# Show VM info
|
|
vm_info() {
|
|
local vm_id="$1"
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
|
|
local config_file="$EASYQEMU_CONFIGS/${vm_id}.json"
|
|
[[ ! -f "$config_file" ]] && fatal "VM not found: $vm_id"
|
|
|
|
info "VM Information:"
|
|
echo ""
|
|
cat "$config_file" | sed 's/^/ /'
|
|
}
|
|
|
|
# Modify VM
|
|
vm_modify() {
|
|
local vm_id="$1"
|
|
shift
|
|
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
|
|
load_vm_config "$vm_id" || fatal "Failed to load VM configuration"
|
|
|
|
# Parse modification arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--name)
|
|
VM_NAME="$2"
|
|
shift 2
|
|
;;
|
|
--memory)
|
|
VM_MEMORY="$2"
|
|
shift 2
|
|
;;
|
|
--cpus)
|
|
VM_CPUS="$2"
|
|
shift 2
|
|
;;
|
|
--cdrom)
|
|
VM_CDROM="$2"
|
|
shift 2
|
|
;;
|
|
--network)
|
|
VM_NETWORK="$2"
|
|
shift 2
|
|
;;
|
|
--display)
|
|
VM_DISPLAY="$2"
|
|
shift 2
|
|
;;
|
|
--boot)
|
|
VM_BOOT="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
warning "Unknown option: $1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
save_vm_config "$vm_id"
|
|
success "VM modified: $vm_id"
|
|
}
|
|
|
|
##############################################################################
|
|
# Volume Management Functions
|
|
##############################################################################
|
|
|
|
# Create volume
|
|
volume_create() {
|
|
local name=""
|
|
local size="10G"
|
|
local format="qcow2"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--name)
|
|
name="$2"
|
|
shift 2
|
|
;;
|
|
--size)
|
|
size="$2"
|
|
shift 2
|
|
;;
|
|
--format)
|
|
format="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
[[ -z "$name" ]] && fatal "Volume name is required. Use --name <name>"
|
|
|
|
local volume_path="$EASYQEMU_VOLUMES/${name}.${format}"
|
|
|
|
info "Creating volume: $volume_path ($size)"
|
|
qemu-img create -f "$format" "$volume_path" "$size"
|
|
|
|
success "Volume created: $volume_path"
|
|
}
|
|
|
|
# List volumes
|
|
volume_list() {
|
|
info "Available volumes:"
|
|
echo ""
|
|
printf "%-40s %-10s %-15s %-15s\n" "NAME" "FORMAT" "SIZE" "ACTUAL SIZE"
|
|
echo "--------------------------------------------------------------------------------"
|
|
|
|
for volume in "$EASYQEMU_VOLUMES"/*; do
|
|
[[ ! -f "$volume" ]] && continue
|
|
|
|
local name=$(basename "$volume")
|
|
local info=$(qemu-img info "$volume")
|
|
local format=$(echo "$info" | grep "file format:" | awk '{print $3}')
|
|
local vsize=$(echo "$info" | grep "virtual size:" | awk '{print $3}')
|
|
local dsize=$(echo "$info" | grep "disk size:" | awk '{print $3}')
|
|
|
|
printf "%-40s %-10s %-15s %-15s\n" "$name" "$format" "$vsize" "$dsize"
|
|
done
|
|
}
|
|
|
|
# Convert volume
|
|
volume_convert() {
|
|
local source=""
|
|
local target=""
|
|
local format="qcow2"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--source)
|
|
source="$2"
|
|
shift 2
|
|
;;
|
|
--target)
|
|
target="$2"
|
|
shift 2
|
|
;;
|
|
--format)
|
|
format="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
[[ -z "$source" ]] && fatal "Source volume is required. Use --source <path>"
|
|
[[ -z "$target" ]] && fatal "Target volume is required. Use --target <path>"
|
|
|
|
info "Converting: $source -> $target (format: $format)"
|
|
qemu-img convert -f raw -O "$format" "$source" "$target"
|
|
|
|
success "Volume converted: $target"
|
|
}
|
|
|
|
# Volume info
|
|
volume_info() {
|
|
local volume="$1"
|
|
[[ -z "$volume" ]] && fatal "Volume path is required"
|
|
|
|
[[ ! -f "$volume" ]] && fatal "Volume not found: $volume"
|
|
|
|
info "Volume Information:"
|
|
echo ""
|
|
qemu-img info "$volume"
|
|
}
|
|
|
|
# Import volume
|
|
volume_import() {
|
|
local source="$1"
|
|
local name="$2"
|
|
|
|
[[ -z "$source" ]] && fatal "Source path is required"
|
|
[[ -z "$name" ]] && name=$(basename "$source")
|
|
|
|
local target="$EASYQEMU_VOLUMES/$name"
|
|
|
|
info "Importing volume: $source -> $target"
|
|
cp "$source" "$target"
|
|
|
|
success "Volume imported: $target"
|
|
}
|
|
|
|
# Export volume
|
|
volume_export() {
|
|
local name="$1"
|
|
local target="$2"
|
|
|
|
[[ -z "$name" ]] && fatal "Volume name is required"
|
|
[[ -z "$target" ]] && fatal "Target path is required"
|
|
|
|
local source="$EASYQEMU_VOLUMES/$name"
|
|
[[ ! -f "$source" ]] && fatal "Volume not found: $name"
|
|
|
|
info "Exporting volume: $source -> $target"
|
|
cp "$source" "$target"
|
|
|
|
success "Volume exported: $target"
|
|
}
|
|
|
|
# Delete volume
|
|
volume_delete() {
|
|
local name="$1"
|
|
local force="$2"
|
|
|
|
[[ -z "$name" ]] && fatal "Volume name is required"
|
|
|
|
local volume_path="$EASYQEMU_VOLUMES/$name"
|
|
[[ ! -f "$volume_path" ]] && fatal "Volume not found: $name"
|
|
|
|
if [[ "$force" != "-f" ]] && [[ "$force" != "--force" ]]; then
|
|
read -p "Delete volume '$name'? [y/N] " -n 1 -r
|
|
echo
|
|
[[ ! $REPLY =~ ^[Yy]$ ]] && fatal "Deletion cancelled"
|
|
fi
|
|
|
|
rm -f "$volume_path"
|
|
success "Volume deleted: $name"
|
|
}
|
|
|
|
##############################################################################
|
|
# Snapshot Management Functions
|
|
##############################################################################
|
|
|
|
# Create snapshot
|
|
snapshot_create() {
|
|
local vm_id="$1"
|
|
local snapshot_name="$2"
|
|
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
[[ -z "$snapshot_name" ]] && snapshot_name="snapshot_$(date +%Y%m%d_%H%M%S)"
|
|
|
|
load_vm_config "$vm_id" || fatal "Failed to load VM configuration"
|
|
|
|
[[ ! -f "$VM_DISK" ]] && fatal "Disk not found: $VM_DISK"
|
|
|
|
info "Creating snapshot: $snapshot_name for VM: $vm_id"
|
|
qemu-img snapshot -c "$snapshot_name" "$VM_DISK"
|
|
|
|
success "Snapshot created: $snapshot_name"
|
|
}
|
|
|
|
# List snapshots
|
|
snapshot_list() {
|
|
local vm_id="$1"
|
|
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
|
|
load_vm_config "$vm_id" || fatal "Failed to load VM configuration"
|
|
|
|
[[ ! -f "$VM_DISK" ]] && fatal "Disk not found: $VM_DISK"
|
|
|
|
info "Snapshots for VM: $vm_id ($VM_DISK)"
|
|
echo ""
|
|
qemu-img snapshot -l "$VM_DISK"
|
|
}
|
|
|
|
# Apply snapshot
|
|
snapshot_apply() {
|
|
local vm_id="$1"
|
|
local snapshot_name="$2"
|
|
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
[[ -z "$snapshot_name" ]] && fatal "Snapshot name is required"
|
|
|
|
load_vm_config "$vm_id" || fatal "Failed to load VM configuration"
|
|
|
|
[[ ! -f "$VM_DISK" ]] && fatal "Disk not found: $VM_DISK"
|
|
|
|
info "Applying snapshot: $snapshot_name for VM: $vm_id"
|
|
qemu-img snapshot -a "$snapshot_name" "$VM_DISK"
|
|
|
|
success "Snapshot applied: $snapshot_name"
|
|
}
|
|
|
|
# Delete snapshot
|
|
snapshot_delete() {
|
|
local vm_id="$1"
|
|
local snapshot_name="$2"
|
|
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
[[ -z "$snapshot_name" ]] && fatal "Snapshot name is required"
|
|
|
|
load_vm_config "$vm_id" || fatal "Failed to load VM configuration"
|
|
|
|
[[ ! -f "$VM_DISK" ]] && fatal "Disk not found: $VM_DISK"
|
|
|
|
info "Deleting snapshot: $snapshot_name for VM: $vm_id"
|
|
qemu-img snapshot -d "$snapshot_name" "$VM_DISK"
|
|
|
|
success "Snapshot deleted: $snapshot_name"
|
|
}
|
|
|
|
##############################################################################
|
|
# Repository Management Functions
|
|
##############################################################################
|
|
|
|
# Save VM to repository
|
|
repo_save() {
|
|
local vm_id="$1"
|
|
local repo_name="$2"
|
|
|
|
[[ -z "$vm_id" ]] && fatal "VM ID is required"
|
|
[[ -z "$repo_name" ]] && repo_name="$vm_id"
|
|
|
|
load_vm_config "$vm_id" || fatal "Failed to load VM configuration"
|
|
|
|
local repo_dir="$EASYQEMU_REPO/$repo_name"
|
|
mkdir -p "$repo_dir"
|
|
|
|
info "Saving VM to repository: $repo_name"
|
|
|
|
# Copy configuration
|
|
cp "$EASYQEMU_CONFIGS/${vm_id}.json" "$repo_dir/config.json"
|
|
|
|
# Copy disk
|
|
if [[ -f "$VM_DISK" ]]; then
|
|
cp "$VM_DISK" "$repo_dir/disk.qcow2"
|
|
fi
|
|
|
|
# Create metadata
|
|
cat > "$repo_dir/metadata.json" << EOF
|
|
{
|
|
"name": "$repo_name",
|
|
"original_vm_id": "$vm_id",
|
|
"saved": "$(date -Iseconds)",
|
|
"version": "1.0"
|
|
}
|
|
EOF
|
|
|
|
success "VM saved to repository: $repo_name"
|
|
}
|
|
|
|
# Load VM from repository
|
|
repo_load() {
|
|
local repo_name="$1"
|
|
local new_name="$2"
|
|
|
|
[[ -z "$repo_name" ]] && fatal "Repository name is required"
|
|
|
|
local repo_dir="$EASYQEMU_REPO/$repo_name"
|
|
[[ ! -d "$repo_dir" ]] && fatal "Repository not found: $repo_name"
|
|
|
|
# Generate new VM ID
|
|
local vm_id=$(generate_vm_id "${new_name:-$repo_name}")
|
|
|
|
info "Loading VM from repository: $repo_name -> $vm_id"
|
|
|
|
# Copy configuration
|
|
cp "$repo_dir/config.json" "$EASYQEMU_CONFIGS/${vm_id}.json"
|
|
|
|
# Update VM ID in config
|
|
sed -i "s/\"id\": \"[^\"]*\"/\"id\": \"$vm_id\"/" "$EASYQEMU_CONFIGS/${vm_id}.json"
|
|
|
|
if [[ -n "$new_name" ]]; then
|
|
sed -i "s/\"name\": \"[^\"]*\"/\"name\": \"$new_name\"/" "$EASYQEMU_CONFIGS/${vm_id}.json"
|
|
fi
|
|
|
|
# Copy disk
|
|
if [[ -f "$repo_dir/disk.qcow2" ]]; then
|
|
cp "$repo_dir/disk.qcow2" "$EASYQEMU_VOLUMES/${vm_id}.qcow2"
|
|
sed -i "s|\"disk\": \"[^\"]*\"|\"disk\": \"$EASYQEMU_VOLUMES/${vm_id}.qcow2\"|" "$EASYQEMU_CONFIGS/${vm_id}.json"
|
|
fi
|
|
|
|
success "VM loaded from repository: $vm_id"
|
|
echo "Use 'easyqemu vm start $vm_id' to start the VM"
|
|
}
|
|
|
|
# List repository entries
|
|
repo_list() {
|
|
info "Repository entries:"
|
|
echo ""
|
|
printf "%-30s %-20s\n" "NAME" "SAVED"
|
|
echo "--------------------------------------------------------------------------------"
|
|
|
|
for repo_dir in "$EASYQEMU_REPO"/*; do
|
|
[[ ! -d "$repo_dir" ]] && continue
|
|
|
|
local name=$(basename "$repo_dir")
|
|
local metadata="$repo_dir/metadata.json"
|
|
|
|
if [[ -f "$metadata" ]]; then
|
|
local saved=$(grep '"saved"' "$metadata" | cut -d'"' -f4 | cut -d'T' -f1)
|
|
printf "%-30s %-20s\n" "$name" "$saved"
|
|
else
|
|
printf "%-30s %-20s\n" "$name" "unknown"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Delete repository entry
|
|
repo_delete() {
|
|
local repo_name="$1"
|
|
local force="$2"
|
|
|
|
[[ -z "$repo_name" ]] && fatal "Repository name is required"
|
|
|
|
local repo_dir="$EASYQEMU_REPO/$repo_name"
|
|
[[ ! -d "$repo_dir" ]] && fatal "Repository not found: $repo_name"
|
|
|
|
if [[ "$force" != "-f" ]] && [[ "$force" != "--force" ]]; then
|
|
read -p "Delete repository entry '$repo_name'? [y/N] " -n 1 -r
|
|
echo
|
|
[[ ! $REPLY =~ ^[Yy]$ ]] && fatal "Deletion cancelled"
|
|
fi
|
|
|
|
rm -rf "$repo_dir"
|
|
success "Repository entry deleted: $repo_name"
|
|
}
|
|
|
|
##############################################################################
|
|
# Main Command Dispatcher
|
|
##############################################################################
|
|
|
|
show_help() {
|
|
cat << EOF
|
|
EasyQEMU v${VERSION} - Intuitive QEMU Virtual Machine Management
|
|
|
|
USAGE:
|
|
easyqemu <command> [options]
|
|
|
|
COMMANDS:
|
|
VM Management:
|
|
vm create Create a new virtual machine
|
|
vm list List all virtual machines
|
|
vm start Start a virtual machine
|
|
vm stop Stop a virtual machine (use QEMU controls)
|
|
vm delete Delete a virtual machine
|
|
vm info Show VM information
|
|
vm modify Modify VM configuration
|
|
|
|
Volume Management:
|
|
volume create Create a new volume
|
|
volume list List all volumes
|
|
volume info Show volume information
|
|
volume convert Convert volume format
|
|
volume import Import external volume
|
|
volume export Export volume
|
|
volume delete Delete a volume
|
|
|
|
Snapshot Management:
|
|
snapshot create Create a snapshot
|
|
snapshot list List snapshots for a VM
|
|
snapshot apply Apply/restore a snapshot
|
|
snapshot delete Delete a snapshot
|
|
|
|
Repository Management:
|
|
repo save Save VM to repository
|
|
repo load Load VM from repository
|
|
repo list List repository entries
|
|
repo delete Delete repository entry
|
|
|
|
Other:
|
|
version Show version information
|
|
help Show this help message
|
|
|
|
EXAMPLES:
|
|
# Create a new VM
|
|
easyqemu vm create --name ubuntu --memory 4096 --cpus 4 --disk-size 50G
|
|
|
|
# Start VM with ISO
|
|
easyqemu vm create --name test --cdrom /path/to/iso
|
|
easyqemu vm start test_<timestamp>
|
|
|
|
# List VMs
|
|
easyqemu vm list
|
|
|
|
# Create snapshot
|
|
easyqemu snapshot create <vm_id> my_snapshot
|
|
|
|
# Save VM to repository
|
|
easyqemu repo save <vm_id> my_saved_vm
|
|
|
|
# Convert volume
|
|
easyqemu volume convert --source disk.raw --target disk.qcow2 --format qcow2
|
|
|
|
For more information, see the README.md file.
|
|
EOF
|
|
}
|
|
|
|
show_version() {
|
|
echo "EasyQEMU version $VERSION"
|
|
}
|
|
|
|
main() {
|
|
# Initialize
|
|
init_dirs
|
|
check_qemu
|
|
|
|
# Parse command
|
|
local command="${1:-help}"
|
|
shift || true
|
|
|
|
case "$command" in
|
|
vm)
|
|
local subcommand="${1:-list}"
|
|
shift || true
|
|
|
|
case "$subcommand" in
|
|
create)
|
|
vm_create "$@"
|
|
;;
|
|
list)
|
|
vm_list "$@"
|
|
;;
|
|
start)
|
|
vm_start "$@"
|
|
;;
|
|
delete)
|
|
vm_delete "$@"
|
|
;;
|
|
info)
|
|
vm_info "$@"
|
|
;;
|
|
modify)
|
|
vm_modify "$@"
|
|
;;
|
|
*)
|
|
error "Unknown VM subcommand: $subcommand"
|
|
echo "Use 'easyqemu help' for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
volume)
|
|
local subcommand="${1:-list}"
|
|
shift || true
|
|
|
|
case "$subcommand" in
|
|
create)
|
|
volume_create "$@"
|
|
;;
|
|
list)
|
|
volume_list "$@"
|
|
;;
|
|
info)
|
|
volume_info "$@"
|
|
;;
|
|
convert)
|
|
volume_convert "$@"
|
|
;;
|
|
import)
|
|
volume_import "$@"
|
|
;;
|
|
export)
|
|
volume_export "$@"
|
|
;;
|
|
delete)
|
|
volume_delete "$@"
|
|
;;
|
|
*)
|
|
error "Unknown volume subcommand: $subcommand"
|
|
echo "Use 'easyqemu help' for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
snapshot)
|
|
local subcommand="${1:-list}"
|
|
shift || true
|
|
|
|
case "$subcommand" in
|
|
create)
|
|
snapshot_create "$@"
|
|
;;
|
|
list)
|
|
snapshot_list "$@"
|
|
;;
|
|
apply)
|
|
snapshot_apply "$@"
|
|
;;
|
|
delete)
|
|
snapshot_delete "$@"
|
|
;;
|
|
*)
|
|
error "Unknown snapshot subcommand: $subcommand"
|
|
echo "Use 'easyqemu help' for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
repo)
|
|
local subcommand="${1:-list}"
|
|
shift || true
|
|
|
|
case "$subcommand" in
|
|
save)
|
|
repo_save "$@"
|
|
;;
|
|
load)
|
|
repo_load "$@"
|
|
;;
|
|
list)
|
|
repo_list "$@"
|
|
;;
|
|
delete)
|
|
repo_delete "$@"
|
|
;;
|
|
*)
|
|
error "Unknown repo subcommand: $subcommand"
|
|
echo "Use 'easyqemu help' for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
version)
|
|
show_version
|
|
;;
|
|
|
|
help|--help|-h)
|
|
show_help
|
|
;;
|
|
|
|
*)
|
|
error "Unknown command: $command"
|
|
echo "Use 'easyqemu help' for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|