aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md33
-rwxr-xr-xkvmx358
-rwxr-xr-xkvmx-create246
-rwxr-xr-xkvmx-vdagent17
-rw-r--r--kvmxfile50
5 files changed, 625 insertions, 79 deletions
diff --git a/README.md b/README.md
index 0156622..a64544d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,32 @@
-KVMX: QEMU KVM Wrapper
-======================
+KVMX: vagrant-like QEMU KVM Wrapper
+===================================
+
+KVMX is a lightweight implementation of a virtual machine manager
+inspired by vagrant.
+
+It may be used for development or as a wrapper for GUI isolation.
+
+This is simple stuff. Don't use it if you need any complex behavior
+or integration. In the other hand, if you're looking for a small
+application that doesn't depend on software installed from unstrusted
+sources, you'll feel welcome here :)
+
+## Instalation
+
+Simply clone it and add to your `$PATH`:
+
+ git clone https://git.fluxo.info/kvmx
+ cd kvmx && git verify-commit HEAD
+
+## Basic usage
+
+ kvmx init [project-name] [project-folder] # initialize
+ kvmx edit [project-name] # customize
+ kvmx up [project-name] # bring it up!
+
+If no project name is specified, the current folder name is assumed as the project name.
+If no folder is specified, the current folder is assumed as the project home.
+
+## Further reading
-Wrapper to provide easy to use GUI isolation.
See https://blog.fluxo.info/suckless/virtual for details.
diff --git a/kvmx b/kvmx
index 00a41be..75c0c3a 100755
--- a/kvmx
+++ b/kvmx
@@ -1,66 +1,56 @@
#!/bin/bash
#
-# KVM and SPICE client wrapper
+# KVMX Manager.
#
-# Parameters
+# Basic parameters
BASENAME="`basename $0`"
DIRNAME="`dirname $0`"
-STORAGE="/var/cache/qemu"
-SHARED="/var/data/load"
-PORT="$(($RANDOM + 1024))"
-SSH="$(($PORT + 22))"
ACTION="$1"
VM="$2"
-BOX="$STORAGE/$VM/box.img"
-PIDFILE="$STORAGE/$VM/pid"
-PORTFILE="$STORAGE/$VM/port"
-SSHFILE="$STORAGE/$VM/ssh"
-SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=FATAL -i $DIRNAME/ssh/insecure_private_key"
-LOGIN="user"
+GLOBAL_USER_CONFIG_FOLDER="$HOME/.config/kvmx"
# Run spice client
-function kvmx_spice_client {
- # https://lists.freedesktop.org/archives/spice-devel/2013-September/014643.html
- SPICE_NOGRAB=1 spicec --host localhost --port $PORT &
- #spicy -h localhost -p $PORT
- #remote-viewer spice://localhost:$PORT
+function kvmx_spice {
+ if [ "$run_spice_client" == "1" ]; then
+ # https://lists.freedesktop.org/archives/spice-devel/2013-September/014643.html
+ SPICE_NOGRAB=1 spicec --host localhost --port $PORT &
+ #spicy -h localhost -p $PORT
+ #remote-viewer spice://localhost:$PORT
- # Give time to boot
- sleep 5
+ # Give time to boot
+ sleep 5
- # Fix window titles
- xdotool search --name "SPICEc:0" set_window --name $VM
-}
-
-# Restart vdagent inside the guest
-function kvmx_clip {
- instances="`ps -o pid,command -e | grep "spice-vdagent$" | cut -d ' ' -f 2 | xargs`"
-
- # Kill old instances
- for pid in $instances; do
- kill -9 $pid &> /dev/null
- done
-
- # Just to make sure we're inside a virtual machine
- if which spice-vdagent &> /dev/null ; then
- spice-vdagent
+ # Fix window titles
+ if which /usr/bin/xdotool &> /dev/null; then
+ xdotool search --name "SPICEc:0" set_window --name $VM
+ fi
fi
}
# Bring virtual machine up
function kvmx_up {
- # FIXME
- # Check if machine is up
+ if kvmx_running; then
+ echo "$BASENAME: guest $VM is already running"
+ exit 1
+ fi
+
+ if [ "$shared_folder" ]; then
+ local shared="-fsdev local,id=shared,path=$shared_folder,security_model=none -device virtio-9p-pci,fsdev=shared,mount_tag=shared"
+ fi
+
+ # Check if image exists, create otherwise
+ if [ ! -e "$image" ]; then
+ kvmx-create $GLOBAL_USER_CONFIG_FOLDER/$VM
+ fi
# Run virtual machine
- kvm -m 2048 -name $VM -drive file=$BOX,if=virtio -vga qxl \
+ kvm -m 2048 -name $VM -drive file=$image,if=virtio -vga qxl $shared \
-spice port=$PORT,addr=127.0.0.1,disable-ticketing,streaming-video=off,jpeg-wan-compression=never,playback-compression=off,zlib-glz-wan-compression=never,image-compression=off \
-device virtio-serial-pci \
-device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 \
-chardev spicevmc,id=spicechannel0,name=vdagent \
- -smp 2 -soundhw ac97 -cpu host -balloon virtio \
- -fsdev local,id=shared,path=$SHARED,security_model=none -device virtio-9p-pci,fsdev=shared,mount_tag=shared \
+ -smp 2 -soundhw ac97 -cpu host -balloon virtio \
-net nic,model=virtio \
-net user,hostfwd=tcp:127.0.0.1:$SSH-:22 &
@@ -71,50 +61,270 @@ function kvmx_up {
echo $PORT > $PORTFILE
echo $SSH > $SSHFILE
- kvmx_spice_client
+ kvmx_spice
}
-# Check
-if [ -z "$VM" ] && [ "$ACTION" != "clip" ]; then
- echo "usage: $BASENAME <action> <vm>"
- exit 1
-elif [ ! -e "$BOX" ] && [ "$ACTION" != "clip" ]; then
- echo "file not found: $BOX"
+# Display usage
+function kvmx_usage {
+ echo "usage: $BASENAME <action> [options]"
+ echo "examples:"
+ echo ""
+ echo "$BASENAME list"
+ echo "$BASENAME init <machine> [folder]"
+ echo "$BASENAME clone <orig> <dest>"
+
exit 1
-fi
+}
-# TODO: check for a ~/.kvmx config
-# TODO: check for a kvmxfile
+# Log into the guest using SSH
+function kvmx_ssh {
+ if ! kvmx_running; then
+ echo "$BASENAME: guest $VM is not running"
+ exit 1
+ fi
+
+ shift 2
+ SSH="`cat $SSHFILE`"
+ $SSH_COMMAND -p $SSH $SSH_LOGIN@127.0.0.1 $*
+}
+
+# Suspend the virtual machine
+function kvmx_suspend {
+ if ! kvmx_running; then
+ echo "$BASENAME: guest $VM is not running"
+ exit 1
+ fi
-# Dispatch
-if [ "$ACTION" == "up" ]; then
- kvmx_up
-elif [ "$ACTION" == "clip" ]; then
- kvmx_clip
-elif [ "$ACTION" == "suspend" ]; then
PID="`cat $PIDFILE`"
kill -STOP $PID
-elif [ "$ACTION" == "resume" ]; then
+}
+
+# Check if a guest is running
+function kvmx_running {
+ if [ ! -e "$PIDFILE" ]; then
+ return 1
+ fi
+
+ PID="`cat $PIDFILE`"
+ ps $PID &> /dev/null
+
+ return $?
+}
+
+# Resume the guest
+function kvmx_resume {
+ if ! kvmx_running; then
+ echo "$BASENAME: guest $VM is not running"
+ exit 1
+ fi
+
PID="`cat $PIDFILE`"
kill -CONT $PID
-elif [ "$ACTION" == "poweroff" ]; then
- echo TODO
-elif [ "$ACTION" == "ssh" ]; then
- shift 2
- SSH="`cat $SSHFILE`"
- $SSH_COMMAND -p $SSH $LOGIN@127.0.0.1 $*
-elif [ "$ACTION" == "rsync" ]; then
+}
+
+# Poweroff the guest
+function kvmx_poweroff {
+ kvmx_ssh /usr/bin/sudo poweroff
+}
+
+# Rsync files to the guest
+function kvmx_rsync {
+ if ! kvmx_running; then
+ echo "$BASENAME: guest $VM is not running"
+ exit 1
+ fi
+
ORIG="$3"
DEST="$4"
SSH="`cat $SSHFILE`"
- rsync -av "$SSH_COMMAND -p $SSH" $ORIG/ $LOGIN@127.0.0.1:$DEST/
-elif [ "$ACTION" == "provision" ]; then
- echo TODO
-elif [ "$ACTION" == "create" ]; then
- echo TODO
-elif [ "$ACTION" == "init" ]; then
- # TODO: copy from template
- touch .kvmxfile
-elif [ "$ACTION" == "upgrade" ]; then
- echo TODO
+ rsync -av "$SSH_COMMAND -p $SSH" $ORIG/ $SSH_LOGIN@127.0.0.1:$DEST/
+}
+
+# List guests
+function kvmx_list {
+ ls $GLOBAL_USER_CONFIG_FOLDER
+}
+
+# Upgrade guest
+function kvmx_upgrade {
+ echo "sudo apt-get update && sudo apt-get dist-upgrade -y && sudo apt-get autoremove -y" | kvmx_ssh
+}
+
+# Initialize
+function kvmx_initialize {
+ if [ "$ACTION" == "init" ] || [ "$ACTION" == "list" ]; then
+ return
+ fi
+
+ if [ -z "$VM" ]; then
+ VM="$(basename `pwd`)"
+ fi
+
+ # Default parameters
+ PORT="$(($RANDOM + 1024))"
+ SSH="$(($PORT + 22))"
+ SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=FATAL -i $DIRNAME/ssh/insecure_private_key"
+ SSH_LOGIN="user"
+
+ # Initalize
+ mkdir -p $GLOBAL_USER_CONFIG_FOLDER
+
+ # Load and check guest config
+ if [ "$ACTION" != "init" ] && [ "$ACTION" != "list" ] && [ "$ACTION" != "edit" ]; then
+ if [ ! -e "$GLOBAL_USER_CONFIG_FOLDER/$VM" ]; then
+ echo "$BASENAME: config not found: $GLOBAL_USER_CONFIG_FOLDER/$VM"
+ exit 1
+ else
+ source $GLOBAL_USER_CONFIG_FOLDER/$VM
+ fi
+
+ if [ -z "$image" ]; then
+ image="/var/cache/qemu/$VM/box.img"
+ fi
+
+ if [ -z "$KVMXFILE" ]; then
+ KVMXFILE="/var/cache/qemu/$VM/kvmxfile"
+ fi
+
+ # Box and folder config
+ STORAGE="`dirname $image`"
+ STATE_DIR="$STORAGE/state"
+ PIDFILE="$STATE_DIR/pid"
+ PORTFILE="$STATE_DIR/port"
+ SSHFILE="$STATE_DIR/ssh"
+ mkdir -p $STATE_DIR
+
+ if [ ! -e "$image" ] && [ "$ACTION" != "up" ]; then
+ echo "$BASENAME: file not found: $image"
+ exit 1
+ fi
+ fi
+}
+
+# Initializes a new guest
+function kvmx_init {
+ FOLDER="$3"
+
+ if [ -z "$VM" ]; then
+ VM="$(basename `pwd`)"
+ fi
+
+ if [ -e "$GLOBAL_USER_CONFIG_FOLDER/$VM" ]; then
+ echo "$BASENAME: guest $VM already exists"
+ exit 1
+ fi
+
+ if [ -z "$FOLDER" ]; then
+ FOLDER="."
+ fi
+
+ if [ ! -d "$FOLDER" ]; then
+ mkdir -p $FOLDER
+ fi
+
+ # Ensure we have an absolute folder name
+ FOLDER="`cd $FOLDER &> /dev/null && pwd`"
+
+ # Copy config from template
+ if [ ! -e "$FOLDER/kvmxfile" ]; then
+ cp $DIRNAME/kvmxfile $FOLDER/
+ fi
+
+ # Create config entry
+ ( cd $GLOBAL_USER_CONFIG_FOLDER && ln -s $FOLDER/kvmxfile $VM )
+}
+
+# Clone a guest
+function kvmx_clone {
+ if kvmx_running; then
+ echo "$BASENAME: orig $VM is running, cannot clone."
+ exit 1
+ fi
+
+ FOLDER="$3"
+ DEST="`basename $FOLDER`"
+
+ if [ -z "$FOLDER" ]; then
+ kvmx_usage
+ fi
+
+ # Check if dest machine exists
+ if [ -e "$GLOBAL_USER_CONFIG_FOLDER/$DEST" ]; then
+ echo "$BASENAME: destination guest $DEST already exists."
+ exit 1
+ fi
+
+ if [ -d "$FOLDER" ]; then
+ echo "$BASENAME: destination $FOLDER already exists."
+ exit 1
+ fi
+
+ # Ensure we have an absolute folder name
+ mkdir -p $FOLDER
+ FOLDER="`cd $FOLDER &> /dev/null && pwd`"
+ rmdir $FOLDER
+
+ # Copy image and configuration
+ cp -r `dirname $image` $FOLDER/
+ cp $GLOBAL_USER_CONFIG_FOLDER/$VM $GLOBAL_USER_CONFIG_FOLDER/$DEST
+
+ # Update config file
+ new_image="$FOLDER/`basename $image`"
+ sed -i -e "s|image=\"$image\"|image=\"$new_image\"|g" $GLOBAL_USER_CONFIG_FOLDER/$DEST
+}
+
+# Edit guest config
+function kvmx_edit {
+ if [ -z "$EDITOR" ]; then
+ EDITOR="vi"
+ fi
+
+ if [ -e "$GLOBAL_USER_CONFIG_FOLDER/$VM" ]; then
+ $EDITOR $GLOBAL_USER_CONFIG_FOLDER/$VM
+ else
+ echo "$BASENAME: $GLOBAL_USER_CONFIG_FOLDER/$VM: file not found."
+ fi
+}
+
+# Stop a guest
+function kvmx_stop {
+ if kvmx_running; then
+ PID="`cat $PIDFILE`"
+ kill $PID
+ fi
+}
+
+# Destroy a guest
+function kvmx_destroy {
+ kvmx_stop
+
+ #rm -f $image
+ rm -f $PIDFILE
+ rm -f $SSHFILE
+ rm -f $PORTFILE
+
+ echo "$BASENAME: please inspect and remove `dirname $image` manually."
+}
+
+# Purge a guest and all its configuration
+function kvmx_purge {
+ kvmx_destroy
+ rm -f $GLOBAL_USER_CONFIG_FOLDER/$VM
+}
+
+# Print guest status
+function kvmx_status {
+ if kvmx_running; then
+ echo "$BASENAME: $VM guest is running"
+ PID="`cat $PIDFILE`"
+ ps $PID
+ else
+ echo "$BASENAME: $VM guest is stopped"
+ fi
+}
+
+# Dispatch
+if type kvmx_$ACTION 2> /dev/null | grep -q 'function'; then
+ kvmx_initialize
+ kvmx_$ACTION $*
fi
diff --git a/kvmx-create b/kvmx-create
new file mode 100755
index 0000000..2eb97d5
--- /dev/null
+++ b/kvmx-create
@@ -0,0 +1,246 @@
+#!/bin/bash
+#
+# System installer, vmdebootstrap version.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+
+
+# Parameters
+BASENAME="`basename $0`"
+
+# Load configuration
+function kvmx_config_load {
+ if [ ! -z "$1" ] && [ -e "$1" ]; then
+ source $1
+ fi
+}
+
+# Read a parameter from user
+function kvmx_user_input {
+ local input
+ local param="$1"
+ local default="$2"
+ shift 2
+
+ if echo $param | grep -q 'passwd'; then
+ read -s -rep "$* (defaults to $default): " input
+ else
+ read -rep "$* (defaults to $default): " input
+ fi
+
+ if [ -z "$input" ]; then
+ export $param="$default"
+ else
+ export $param="$input"
+ fi
+}
+
+# Get a configuration parameter if not previously defined by a sourced file
+function kvmx_user_config {
+ local param="$1"
+ local default="$2"
+ shift 2
+
+ if [ -z "`eval echo '$'$param`" ]; then
+ kvmx_user_input $param $default $*
+ fi
+}
+
+# Install a package
+function kvmx_install_package {
+ if [ -z "$1" ]; then
+ return
+ fi
+
+ dpkg -s $1 &> /dev/null
+
+ if [ "$?" == "1" ]; then
+ echo "Installing package $1..."
+ DEBIAN_FRONTEND=noninteractive $SUDO apt-get install $1 -y || exit 1
+ fi
+}
+
+# Abort on error
+function kvmx_exit_on_error {
+ if [ "$?" != "0" ]; then
+ echo "Error: $*"
+ exit 1
+ fi
+}
+
+# Run a command using sudo and abort on error
+function kvmx_sudo_run {
+ if [ "`whoami`" != 'root' ]; then
+ SUDO="sudo"
+ fi
+
+ $SUDO $*
+ kvmx_exit_on_error $*
+}
+
+# Make sure there is provision config.
+function kvmx_config {
+ kvmx_user_config image /var/cache/qemu/debian/box.img "Destination image"
+ kvmx_user_config size 3G "Image size"
+ kvmx_user_config format qcow2 "Image format: raw or qcow2"
+ kvmx_user_config method custom "Bootstrap method: custom or vmdeboostrap"
+ kvmx_user_config hostname machine "Hostname"
+ kvmx_user_config domain example.org "Domain"
+ kvmx_user_config arch amd64 "System arch"
+ kvmx_user_config version stretch "Distro version"
+ kvmx_user_config mirror http://http.debian.net/debian/ "Debian mirror"
+}
+
+# Load config file
+kvmx_config_load $1
+
+# Get config parameters
+kvmx_config
+
+# Check
+if [ -e "$image" ]; then
+ echo "error: $image already exists."
+ exit 1
+fi
+
+# Ensure base folder exists
+kvmx_sudo_run mkdir -p `dirname $image`
+
+#
+# vmdebootstrap version
+#
+function kvmx_create_vmdebootstrap {
+ # Check for requirements
+ for req in vmdebootstrap mbr; do
+ kvmx_install_package $req
+ done
+
+ # Image format
+ if [ "$format" == "qcow2" ]; then
+ format="--convert-qcow2"
+ else
+ formt=""
+ fi
+
+ # Run
+ kvmx_sudo_run vmdebootstrap --verbose --image=$image --size=$size --distribution=$version \
+ --mirror=$mirror --arch=$arch --hostname=$hostname.$domain \
+ --grub $format
+
+ # Fix permissions
+ kvmx_sudo_run chown -R `whoami`. `dirname $image`
+
+ # Cleanup
+ kvmx_sudo_run rm debootstrap.log
+ kvmx_sudo_run rm ${image}.raw
+}
+
+#
+# Custom version
+#
+function kvmx_create_custom {
+ WORK="`mktemp -d`"
+
+ # Check for requirements.
+ for req in debootstrap grub-pc parted; do
+ kvmx_install_package $req
+ done
+
+ echo "Creating image..."
+ #kvmx_sudo_run dd if=/dev/zero of=$image bs=$size count=1
+ kvmx_sudo_run qemu-img create -f raw $image $size
+ device="`sudo losetup --find --show $image`"
+
+ echo "Partitioning image at $device..."
+ kvmx_sudo_run parted -s -- $device mklabel gpt
+ kvmx_sudo_run parted -s -- $device unit MB mkpart non-fs 2 3
+ kvmx_sudo_run parted -s -- $device set 1 bios_grub on
+ kvmx_sudo_run parted -s -- $device unit MB mkpart ext2 3 -1
+ kvmx_sudo_run parted -s -- $device set 2 boot on
+ kvmx_sudo_run mkfs.ext4 ${device}p2
+ kvmx_sudo_run mount ${device}p2 $WORK/
+
+ # Non-interactive installation
+ APT_INSTALL="kvmx_sudo_run LC_ALL=C DEBIAN_FRONTEND=noninteractive chroot $WORK/ apt-get install -y"
+
+ # Initial system install.
+ echo "Installing base system..."
+ kvmx_sudo_run LC_ALL=C DEBIAN_FRONTEND=noninteractive debootstrap --arch=$arch $version $WORK/ $mirror
+
+ # Initial configuration.
+ echo "Applying initial configuration..."
+ kvmx_sudo_run mount none -t proc $WORK/proc
+ kvmx_sudo_run mount none -t sysfs $WORK/sys
+ kvmx_sudo_run mount -o bind /dev/ $WORK/dev
+ echo LANG=C | $SUDO tee $WORK/etc/default/locale > /dev/null
+
+ # Hostname configuration.
+ echo $hostname.$domain | $SUDO tee $WORK/etc/hostname > /dev/null
+ echo "127.0.0.1 localhost" | $SUDO tee -a $WORK/etc/hosts > /dev/null
+
+ # This ordering is important for facter correctly guess the domain name
+ echo "127.0.0.1 $hostname.$domain $hostname" | $SUDO tee -a $WORK/etc/hosts > /dev/null
+
+ # Invert hostname contents to avoid http://projects.puppetlabs.com/issues/2533
+ tac $WORK/etc/hosts | $SUDO tee $WORK/etc/hosts.new > /dev/null
+ kvmx_sudo_run mv $WORK/etc/hosts.new $WORK/etc/hosts
+
+ # Initial upgrade
+ echo "Applying initial upgrades..."
+ kvmx_sudo_run chroot $WORK/ apt-get update
+ kvmx_sudo_run chroot $WORK/ apt-get upgrade -y
+
+ if [ "$arch" == "i386" ]; then
+ kernel_arch="686"
+ else
+ kernel_arch="$arch"
+ fi
+
+ $APT_INSTALL locales
+ $APT_INSTALL screen cron lsb-release openssl -y
+ $APT_INSTALL linux-image-$kernel_arch -y
+ $APT_INSTALL grub-pc -y
+ kvmx_sudo_run chroot $WORK/ update-grub
+ kvmx_sudo_run chroot $WORK/ grub-install $device
+
+ # Teardown
+ kvmx_sudo_run umount $WORK/proc
+ kvmx_sudo_run umount $WORK/sys
+ kvmx_sudo_run umount $WORK/dev
+ kvmx_sudo_run umount $WORK
+ kvmx_sudo_run rmdir $WORK
+ kvmx_sudo_run losetup -d $device
+
+ # Image conversion
+ if [ "$format" == "qcow2" ]; then
+ echo "Converting raw image to qcow2..."
+ kvmx_sudo_run mv $image $image.raw
+ kvmx_sudo_run qemu-img convert -O qcow2 ${image}.raw $image
+ kvmx_sudo_run rm ${image}.raw
+ fi
+
+ # Fix permissions
+ kvmx_sudo_run chown -R `whoami`. `dirname $image`
+}
+
+# Dispatch
+if [ "$method" == "custom" ]; then
+ kvmx_create_custom
+elif [ "$method" == "vmdebootstrap" ]; then
+ kvmx_create_vmdebootstrap
+else
+ echo "$BASENAME: invalid method $method"
+ exit 1
+fi
diff --git a/kvmx-vdagent b/kvmx-vdagent
new file mode 100755
index 0000000..d29e2b3
--- /dev/null
+++ b/kvmx-vdagent
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Restart spice-vdagent inside the guest
+#
+
+# Get instances
+instances="`ps -o pid,command -e | grep "spice-vdagent$" | cut -d ' ' -f 2 | xargs`"
+
+# Kill old instances
+for pid in $instances; do
+ kill -9 $pid &> /dev/null
+done
+
+# Just to make sure we're inside a virtual machine
+if which spice-vdagent &> /dev/null ; then
+ spice-vdagent
+fi
diff --git a/kvmxfile b/kvmxfile
index c35b081..7f65bc9 100644
--- a/kvmxfile
+++ b/kvmxfile
@@ -1,2 +1,48 @@
-HEADLESS="0"
-BASEBOX="stretch"
+#
+# Sample kvmx file
+#
+
+# Which base box you should use.
+# If none is set, kvmx will bootstrap one for you.
+#basebox="stretch"
+
+# Absolute or relative path for a provision script.
+#provision_script="default"
+
+# Set this is you want to be able to share folders between host and guest.
+#shared_folder="."
+#shared_folder_mountpoint="/media/shared"
+
+# Set this if you want to automatically attach an spice client when the machine
+# boots.
+run_spice_client="1"
+
+# Set host_port-:guest_port mapping pairs.
+#port_mapping="8080-:80,8443-:443"
+
+# Where the guest image is stored
+image="$HOME/.local/share/kvmx/$VM/box.img"
+
+# Image size
+size="10G"
+
+# Image format: raw or qcow2
+format="qcow2"
+
+# Bootstrap method: custom or vmdeboostrap
+method="custom"
+
+# Hostname
+hostname="machine"
+
+# Domain
+domain="example.org"
+
+# System arch
+arch="amd64"
+
+# Box distribution when bootstraping a new image
+version="stretch"
+
+# Debian mirror
+mirror="http://http.debian.net/debian/"