From dd9d75184dcc6e01cd29ac51ecca24b10e2535d0 Mon Sep 17 00:00:00 2001 From: hozan23 Date: Mon, 8 Jul 2024 03:45:28 +0200 Subject: init commit --- Makefile | 17 +++++ README.md | 83 +++++++++++++++++++++ default.conf | 13 ++++ vms.sh | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 344 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 default.conf create mode 100755 vms.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..052bd4d --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +VMS_CMD = vms +VMS_CMD_PATH = $(DESTDIR)$(PREFIX)/bin/$(VMS_CMD) +VMS_BASH_FILE = $(VMS_CMD).sh + +.PHONY: all install uninstall + +all: install + +install: $(VMS_BASH_FILE) + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp $(VMS_BASH_FILE) $(VMS_CMD_PATH) + chmod 755 $(VMS_CMD_PATH) + +uninstall: + rm -f $(VMS_CMD_PATH) + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..abca406 --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# vms + +This script is a simple tool that uses QEMU to create, run, and manage virtual +machines. + +It is a tool to simplify the management of multiple headless VMs without a +graphical interface, using tools like `ssh`, `sshfs`, and `rsync`. The script +uses minimal QEMU arguments to avoid excessive CPU usage. If you want to use +`Spice` or other tools with QEMU, feel free to open a pull request to integrate +that into the configuration. + +## Prerequisites + +Ensure you have the following dependencies installed: + +- `qemu-img` +- `qemu-system-x86_64` + +## Installation + +Run: + +```sh +$ make PREFIX=/home/USER/.local install +``` + +## Usage + +The first time you run `vms` command, it will create a `vms` directory under +your home directory to store the images, configs, and ISOs for the VMs. + +First, download the ISO image, for example, [arch linux](https://archlinux.org/download/). + +The following command will create a new image in `vms` direcotry using `qemu-img` +with the provided size, and create a config file. + + +```sh +$ vms create arch 50G +``` + +Read/Modify the config file in: `/home/USER/vms/arch.conf` + +Then you can boot from the ISO: + +```sh +$ vms boot arch /home/USER/download/arch.iso +``` + +After installing, you can run this command whenever you want to run the VM: + +```sh +$ vms run arch +``` + +You can list all VMs by using this command: + +```sh +$ vms list +``` + +## Configuration + +By default, the script uses the following configurations for the virtual +machines: + +```sh +### Default vm configuration +# ram=12G +# cpu=host +# graphic=yes +# audio=no +# +# Forward host port 10022 to guest port 22 and host port 8080 to guest port 80 +# ports=10022:22 8080:80 +# +# display=sdl,grab-mod=rctrl +# vgadevices=VGA,vgamem_mb=64 +# audiodevices=intel-hda hda-duplex +# devices= +``` + +These default settings can be customized by modifying config files. diff --git a/default.conf b/default.conf new file mode 100644 index 0000000..d0d03e0 --- /dev/null +++ b/default.conf @@ -0,0 +1,13 @@ +### Default vm configuration +# audio=no +# devices= +# ram=12G +# cpu=host +# bios_path=/usr/share/qemu/bios.bin +# audiodevices=intel-hda hda-duplex +# graphic=yes +# ports=10022:22 8080:80 +# net=nic +# boot=menu=on +# display=sdl,grab-mod=rctrl +# vgadevices=VGA,vgamem_mb=64 diff --git a/vms.sh b/vms.sh new file mode 100755 index 0000000..10568dd --- /dev/null +++ b/vms.sh @@ -0,0 +1,231 @@ +#!/bin/bash + +set -e + +vms_path=$(realpath ~/vms) + +# Default vm configuration +# Declare an associative array +declare -A config +config=( + [graphic]="yes" + [audio]="no" + [boot]="menu=on" + [ram]="12G" + [cpu]="host" + [display]="sdl,grab-mod=rctrl" + [ports]="10022:22 8080:80" + [audiodevices]="intel-hda hda-duplex" + [vgadevices]="VGA,vgamem_mb=64" + [devices]="" + [bios_path]="/usr/share/qemu/bios.bin" + [net]="nic" +) + +# Load config from a file +load_config() { + local file="$1" value + while read -r value; do + # Check if the line contains an equals sign + if [[ $value = *?=* ]]; then + # skip if it's a comment + if [[ "$value" == \#* ]]; then + continue + fi + local key=${value%%=*} + config[$key]=${value#*=} + fi + done <"$file" +} + +# Save config to a file +save_config() { + local file="$1" key + printf "### Default vm configuration\n" >"$file" + for key in "${!config[@]}"; do + printf "# %s=%s\n" "$key" "${config[$key]}" >>"$file" + done +} + +# Print the current config +print_config() { + local key + for key in "${!config[@]}"; do + printf "# %s=%s\n" "$key" "${config[$key]}" + done +} + +# Print error message to stderr +printerr() { printf "%s\n" "$*" >&2; } + +# Check if a file exists +file_exists() { + if [ ! -f "$1" ]; then + printerr "error: file $1 not exist" + exit 1 + fi +} + +# Check if the number of parameters is correct +check_params() { + if [ "$1" -lt "$2" ]; then + printerr "error: wrong parameters" + exit 1 + fi +} + +# Print help information +usage() { + printf "vms: + vms path: %s + commands: + $ vms run IMAGE_NAME + $ vms run arch + $ vms boot IMAGE_NAME ISO_PATH + $ vms boot arch /path/to/arch.iso + $ vms create IMAGE_NAME SIZE_OF_IMAGE + $ vms create arch 50g + $ vms list\n" "$vms_path" + exit 1 +} + +# Run QEMU +run_qemu() { + + load_config "$1" + + # Define the QEMU arguments + local qemu_args=( + --enable-kvm + -bios "${config[bios_path]}" + -boot "${config[boot]}" + -m "${config[ram]}" + -cpu "${config[cpu]}" + ) + + if [ -n "${config[net]}" ]; then + qemu_args+=(-net "${config[net]}") + local qemu_net_arg="user" ports + for ports in ${config[ports]}; do + IFS=':' read -ra p <<<"$ports" + if [ ${#p[@]} != 2 ]; then + printerr "error: wrong port: ${p[*]}" + exit 1 + fi + qemu_net_arg+=",hostfwd=tcp::${p[0]}-:${p[1]}" + done + + qemu_args+=(-net "$qemu_net_arg") + fi + + local device + + if [[ "${config[graphic]}" == "yes" ]]; then + qemu_args+=(-display "${config[display]}") + for device in ${config[vgadevices]}; do + qemu_args+=(-device "${device}") + done + + if [[ "${config[audio]}" == "yes" ]]; then + for device in ${config[audiodevices]}; do + qemu_args+=(-device "${device}") + done + fi + else + qemu_args+=(-nographic) + fi + + for device in ${config[devices]}; do + qemu_args+=(-device "${device}") + done + + qemu-system-x86_64 "${qemu_args[@]}" "${@:2}" +} + +# Create a new vm +create_new_vm() { + qemu-img create "$vms_path/$1.img" "$2" +} + +# Create the vms directory if it doesn't exists +if [ ! -d "$vms_path" ]; then + printf "Creating vms directory in %s\n" "$vms_path" + mkdir -p "$vms_path" +fi + +# Print usage information if no arguments are provided +if [ -z "$1" ]; then + usage +fi + +# Ensure required commands are available +for cmd in qemu-img qemu-system-x86_64; do + if ! command -v "$cmd" &>/dev/null; then + printerr "error: $cmd is not installed" + exit 1 + fi +done + +case "$1" in +"run") + check_params $# 2 + + img_path="$vms_path/$2.img" + conf_path="$vms_path/$2.conf" + + file_exists "$img_path" + file_exists "$conf_path" + + img_args=( + -drive "file=$img_path,format=raw" + ) + + run_qemu "$conf_path" "${img_args[@]}" + ;; + +"boot") + check_params $# 3 + + img_path="$vms_path/$2.img" + conf_path="$vms_path/$2.conf" + iso_path=$(realpath "$3") + + file_exists "$img_path" + file_exists "$iso_path" + file_exists "$conf_path" + + img_args=( + -drive "file=$img_path,format=raw" + -cdrom "$iso_path" + ) + + run_qemu "$conf_path" "${img_args[@]}" + ;; + +"create") + check_params $# 3 + + iso_path="$vms_path/$2.iso" + + create_new_vm "$2" "$3" + + save_config "$vms_path/$2.conf" + + printf "Created %s Successfully!\n" "$2" + ;; + +"list") + for img in "$vms_path"/*.img; do + printf " - %s\n" "$(basename "$img" .img)" + done + ;; + +"help") + usage + ;; + +*) + printerr "error: command $1 not found" + usage + ;; +esac -- cgit v1.2.3