diff options
| author | Silvio Rhatto <rhatto@riseup.net> | 2017-03-09 19:04:24 -0300 | 
|---|---|---|
| committer | Silvio Rhatto <rhatto@riseup.net> | 2017-03-09 19:04:24 -0300 | 
| commit | 1cddbe0234efe158b6c05f21d742eb9489887cc4 (patch) | |
| tree | e40bab6faf590e6d22e7b2360904155bbb0cef58 | |
| parent | 65f7ac258a41402bb9b00c87af621a65dfbfa6e0 (diff) | |
| download | kvmx-1cddbe0234efe158b6c05f21d742eb9489887cc4.tar.gz kvmx-1cddbe0234efe158b6c05f21d742eb9489887cc4.tar.bz2 | |
Project revamp: full workflow
| -rw-r--r-- | README.md | 33 | ||||
| -rwxr-xr-x | kvmx | 358 | ||||
| -rwxr-xr-x | kvmx-create | 246 | ||||
| -rwxr-xr-x | kvmx-vdagent | 17 | ||||
| -rw-r--r-- | kvmxfile | 50 | 
5 files changed, 625 insertions, 79 deletions
| @@ -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. @@ -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 @@ -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/"   | 
