#!/usr/bin/bash
#
# Convert harddisk images of virtual machine
# Copyright (C) 2019-2024 by Thomas Dreibholz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Contact: dreibh@simula.no

# Bash options:
set -e


if [ $# -ne 3 ] ; then
   echo >&2 "Usage: $0 VM_name image_format image_variant"
   exit 1
fi

MACHINE_NAME="$1"
IMAGE_FORMAT="$2"
IMAGE_VARIANT="$3"


# ====== Obtain machine status ==============================================
echo -e "\x1b[34m`date +%FT%H:%M:%S`: Obtaining configuration of virtual system $MACHINE_NAME ...\x1b[0m"
MACHINE_STATUS_FILE=`mktemp`
VBoxManage showvminfo "$MACHINE_NAME" --machinereadable >$MACHINE_STATUS_FILE

CONFIG_FILE=`cat $MACHINE_STATUS_FILE | grep "^CfgFile=" | sed -e "s/^CfgFile=\"//" -e "s/\"$//g"`
CONFIG_DIR=`dirname "$CONFIG_FILE"`


# ====== Find storage controllers ===========================================
echo -e "\x1b[34m`date +%FT%H:%M:%S`: Looking for storage controllers ...\x1b[0m"
for controller in 0 1 2 3 ; do
   for port in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ; do
      controllerNames=`cat $MACHINE_STATUS_FILE | grep "^\"[a-zA-Z0-9 ]*-ImageUUID-$port-$controller" | sed -e "s/^\"\([a-zA-Z0-9 ]*\)-ImageUUID-$port-$controller\"=.*$/\1/" -e "s/ /./" || echo ""`
      for controllerName in $controllerNames ; do
         uuid=`cat $MACHINE_STATUS_FILE | grep "^\"$controllerName-ImageUUID-$port-$controller" | sed -e "s/^\"$controllerName-ImageUUID-$port-$controller\"=\"//" -e "s/\"$//" || echo ""`
         # ====== Check for image attached to controller ====================
         if [ "$uuid" != "" ] ; then
            oldImage=`cat $MACHINE_STATUS_FILE | grep "^\"$controllerName-$port-$controller" | sed -e "s/^\"$controllerName-$port-$controller\"=\"//" -e "s/\"$//" || echo ""`
            echo -e "\x1b[34m`date +%FT%H:%M:%S`: Controller $controller ($controllerName), port $port: $uuid in $oldImage\x1b[0m"
            if [[ "$oldImage" =~ ^.*(vdi|vmdk|vhd|hdd|qed|qcow)$ ]] ; then
               if [ -e "$oldImage" ] ; then
                  # ====== Check whether a conversion is necessary ==========
                  variant=`echo $IMAGE_VARIANT | tr '[:upper:]' '[:lower:]'`
                  newImage="$CONFIG_DIR/$MACHINE_NAME-DH$port-$controller.$variant.$IMAGE_FORMAT"
                  if [ "$oldImage" != "$newImage" ] ; then
                     if [ ! -e "$newImage" ] ; then

                        # ====== Apply the conversion =======================
                        echo -e "\x1b[34m`date +%FT%H:%M:%S`: Cloning $oldImage to $newImage ...\x1b[0m"
                        VBoxManage clonehd "$oldImage" "$newImage" --format "$IMAGE_FORMAT" --variant "$IMAGE_VARIANT"

                        echo -e "\x1b[34m`date +%FT%H:%M:%S`: Attaching $newImage ...\x1b[0m"
                        VBoxManage -q storageattach "$MACHINE_NAME" --storagectl "$controllerName" --port $port --device $controller --type hdd --medium "$newImage"

                        # ====== Remove old image, if not used any more =====
                        echo -e "\x1b[34m`date +%FT%H:%M:%S`: Checking whether $oldImage may be removed...\x1b[0m"
                        HDD_STATUS_FILE=`mktemp`
                        VBoxManage list hdds >$HDD_STATUS_FILE

                        diskInfo=`cat $HDD_STATUS_FILE | grep -A6 "^UUID:[ ]*$uuid" || echo ""`
                        if [ "$diskInfo" != "" ] ; then
                           usage=`echo "$diskInfo" | grep "^Usage:" || echo ""`
                           if [ "$usage" = "" ] ; then
                              echo -e "\x1b[34m`date +%FT%H:%M:%S`: Removing old image $oldImage ...\x1b[0m"
                              VBoxManage -q closemedium disk "$uuid" --delete || true
                           else
                              echo "WARNING: Disk $uuid is still used. Leaving storage registration as is!"
                              echo "$usage"
                           fi
                        else
                           echo >&2 "ERROR: Something is going wrong! No HDD status found for UUID $uuid! May be VBoxManage has changed output format?"
                        fi

                        rm -f $HDD_STATUS_FILE
                     else
                        echo -e "\x1b[34m`date +%FT%H:%M:%S`: WARNING: Disk $newImage already exists -> skipping conversion!\x1b[0m"
                     fi
                  else
                     echo -e "\x1b[34m`date +%FT%H:%M:%S`: Disk seems to be converted already. Nothing to do.\x1b[0m"
                  fi
               else
               echo -e "\x1b[34m`date +%FT%H:%M:%S`: Disk $uuid not found -> skipping conversion!\x1b[0m"
               fi
            else
               echo -e "\x1b[34m`date +%FT%H:%M:%S`: Disk $uuid is not a harddisk (probably CD/DVD). Nothing to do.\x1b[0m"
            fi
         fi
      done
   done
done

rm -f $MACHINE_STATUS_FILE
