Hi Guys/Gals,
This week i needed to move a bunch of VMs from local storage to iSCSI so that i can accomodate for more storage. The normal way for me to do this is to either do this by hand or write a 10 lines long script for each VM... as i don't have Virtual Center yet.
So i decided that it might be nice to create a general script for this. For details and usage, see in the source. Hope you like it, this is my first attempt at a more advanced bash script, so i'm sure that some things could have been done easier. Robustness (is that a word?) was my priority.
The source might also need a bit cleanup of commented out things, but alas.. no more time now. It seems to work fine for me.
More updates/cleanup when i get the time.
New Script for moving vm to another storage
#!/bin/bash
#
\# vm-relocate.sh
#
\# Script for Virtual Infrastructure 3.x to migrate an existing VM to
\# another VMFS storage. An example of this would be to move a VM from
\# local storage to iSCSI storage.
\# In short, what the script does is; unregister the VM passed by its name.
\# Then copy all disks using vmkfstools, copy all other files, update
\# the configuration file for the changed location, and register the VM again.
\# order and details are a little different, but you get the idea.
#
\# Features:
\# - Only VMs that are powered down will be relocated.
\# - Only connected disks are migrated, making it easier to intentially skip a disk
\# - All target disks will be moved to the new location, even if your machine now
\# has disks over separate lun's
\# - You should pass it the vmname, this can be different from the display name,
\# the parameter is case insensitive.
\# - The original VM is not removed at all ONLY unregistered, so you can
\# always go back by unregistering/deleting the clone and registering the original .vmx
\# again.
#
\# Note:
\# - This is NOT a backup script, this is not the intention and never will be.
#
\# Example usage:
\# ./vm-relocate browserappliance /vmfs/volumes/lun-x/browse
\# This will relocate the vm "browserappliance.vmx" to a new folder called
\# "browse" on the volume with name "lun-x"
#
#
\# Author: Wil van Antwerpen
\# Date: June 15, 2007
\# License: GPL
\# Version: 0.2
#
\# Bugreports & suggestions can be send via PM to wila in VMTN
\# or you can leave them in this thread.
#
\# declare some global variables to work with
strVMname="" # short VM filename without path or file extension (.vmx)
strDisplayName="" # Display Name of the VM
strVMX="" # full location of the vmx file to move in UUID style
strVMXpath="" # The default path of where our source vmx (and disks) is stored
strVMDK="" # List of vmdk source disks to move, full path UUID again
strVMDKname="" # List of vmdk disks without the path
strVMDKdevice="" # List of scsi devices
strTarget="" # path where you are moving the VM towards
verbose_state=0
LOGFILE="vm-relocate.log"
#
\# Return the complete file location for the strVMname passed and store
\# that in strVMX
#
function vmxfileForVMname ()
{
local vm
echo -e -n $"Searching for vm \"$strVMname\" on our host... "
for vm in `vmware-cmd -l`
do
if echo "$vm.vmx" | grep -i -q "$strVMname" ; then
echo "Ok"
logOutput "Found VM:\n $vm"
strVMX=$vm
strVMXpath=$\{vm%/*}
logOutput "Source path = $\{strVMXpath}"
fi
done
if \[ -z "$strVMX" ]; then
verbose_state=1
logOutput "$strVMname not found, unable to comply with the request, exiting...."
exit 3
fi
} # vmxfileForVMname
#
\# Are the minimal required parameters passed?
#
#
function testParams ()
{
if \[ -z "$strVMname" ]; then
verbose_state=1
logOutput "No VM to relocate has been provided in the command line. Exiting."
showHelp
exit 3
fi
if \[ -z "$strTarget" ]; then
verbose_state=1
logOutput "No target folder to move the VM towards is passed on the command line. Exiting."
showHelp
exit 3
fi
}
#
\# Checks the preconditions that must be met for the script to
\# work properly
\# - did we find an existing vm?
\# - is it still running?
\# - does the target folder already exist?
\# - if not can we create it?
#
function checkConditions ()
{
local currentState=""
local displayName=""
if \[ -z "$strVMX" ]; then # How many times can one check the same thing ![]()
verbose_state=1
logOutput "No VM to relocate has been provided in the command line. Exiting."
logOutput ""
exit 3
fi
echo -n $"Checking to make sure the VM is not running.... "
currentState=`vmware-cmd "$strVMX" getstate -q`
strDisplayName=`vmware-cmd "$strVMX" getconfig displayName -q`
if echo $currentState | grep -i -q "on" ; then
verbose_state=1
logOutput "Running state = $currentState"
logOutput "The vm $strDisplayName is still running, this is not supported, aborting.."
exit 4
fi
echo "Ok"
echo -n $"Checking to make sure the target doesn't exist.... "
if \[ -d $strTarget ]; then
verbose_state=1
logOutput "The folder $\{strTarget} already exists. Exiting."
exit 5
fi
echo "Ok"
} # checkConditions
function showHelp ()
{
echo ""
echo "Usage:"
echo " vm-relocate.sh \[options] $\{LOGFILE}
}
function processArguments ()
{
while test "$1" != "" ; do
case $1 in
--help|-h)
showHelp
exit 0
;;
--version|V)
echo "vm-relocate.sh version 0.2"
exit 0
;;
--verbose|v)
verbose_state=1
;;
-*)
echo "Error: Unknown option $1"
showHelp
exit 1
;;
*)
strVMname=$1
shift
strTarget=$1
shift
;;
esac
shift
done
testParams
} # processArguments
#
\# Searches the vmx file for lines with vmdk files and extracts those along with
\# the relevant parameters for a later query.
#
function getVirtualDisks ()
{
local vmdk=""
IFS=$'\n'
logOutput "Find all disks in VM"
strVMDK=`cat $strVMX | grep -i ".vmdk"`
logOutput "Disks in vmx:\n$strVMDK"
for word in $strVMDK
do
word=$\{word#*=}
word=${word#*\"}
word=${word%*\"}
vmdk=`echo -e "$word\n$vmdk"`
word=$\{word##*/}
strVMDKname=`echo -e "$\{word}\n$strVMDKname"`
done
for word in $strVMDK
do
word=$\{word%=*}
word=$\{word%.fileName*}
strVMDKdevice=`echo -e "$\{word}\n$strVMDKdevice"`
done
strVMDK=$\{vmdk}
logOutput "\nDone:\n$\{strVMDK}\nDisks:\n$\{strVMDKname}\nDevices:\n$\{strVMDKdevice}"
} # getVirtualDisks
#
\# Removes the disks from our result that are marked as NOT present
\# in the vmx config file.
#
function removeNonpresentDisks ()
{
local n=0
local connected=0
local device=""
local devices=""
local svmdk=""
local vmdks=""
local vmdk_path=""
local vmdk_path_list=""
IFS=$'\n'
\# checking if the vmdk's are connected
devices=""
for device in $strVMDKdevice
do
((n += 1))
connected=`vmware-cmd "$vmx" getconfig "$\{device}.present" -q`
echo "connected $device = $connected"
if \[ $connected == "1" ]; then
create a new devices list with only the devices marked as present.
svmdk=`echo "$\{strVMDKname}" | sed -n "$\{n}p"`
vmdk_path=`echo "$\{strVMDK}" | sed -n "$\{n}p"`
logOutput "Adding connected disk: $\{svmdk} at $\{vmdk_path}"
if \[ -z $devices ]; then
devices=$\{device}
vmdk_path_list=$\{vmdk_path}
vmdks=$\{svmdk}
else
devices=`echo -e "$\{devices}\n$\{device}"`
vmdk_path_list=`echo -e "$\{vmdk_path_list}\n$\{vmdk_path}"`
vmdks=`echo -e "$\{vmdks}\n$\{svmdk}"`
fi
fi
done
strVMDKdevice=$\{devices}
strVMDKname=$\{vmdks}
strVMDK=$\{vmdk_path_list}
} # removeNonpresentDisks
#
\# Last chance!
\# Display the devices that the script will copy and ask for a confirmation.
#
function confirmMigration ()
{
local n=0
local svmdk=""
local confirmation=""
echo -e "----
"
echo -e "The details of the VM that is to be migrated are:"
echo -e "Display Name: $\{strDisplayName}"
echo -e "VM : $\{strVMX##*/}"
echo -e "Source Path : $\{strVMXpath}"
echo -e "Target Path : $\{strTarget}"
echo -e "With the following disks:"
for device in $strVMDKdevice
do
((n += 1))
svmdk=`echo "$\{strVMDKname}" | sed -n "$\{n}p"`
echo -e "$\{device} = $svmdk"
done
echo -e "----
"
echo -e "Do you want to continue (Y)es/(N)o New Script for moving vm to another storage?"
read confirmation
if \[ -z $confirmation ]; then
confirmation="N"
fi
if ! `echo "$confirmation" | grep -i -q "Y"` ; then
verbose_state=1
logOutput "Cancelled by user request."
exit 7
fi
logOutput "Confirmation accepted."
}
#
\# performs the actual copying of the disks
#
#
function copyDisks ()
{
local vmx=""
local svmdk=""
local target=""
local diskDevice=""
local n=0
IFS=$'\n'
vmx=$\{strVMX}
target=$\{strTarget}
removeNonpresentDisks
\# Confirmation on the copy
confirmMigration
echo -n $"Creating folder $strTarget .... "
mkdir $strTarget
if \[ ! -d $strTarget ]; then
verbose_state=1
logOutput "Unable to create target folder $\{strTarget}. Exiting."
exit 6
fi
echo "Ok"
\# The actual copy of the disks
for sourceDisk in $strVMDK
do
((n += 1))
svmdk=`echo "$\{strVMDKname}" | sed -n "$\{n}p"`
if echo "$sourceDisk" | grep -i -q "/" ; then
logOutput "vmkfstools -i $\{sourceDisk} $\{target}/$\{svmdk}"
vmkfstools -i "$\{sourceDisk}" "$\{target}/$\{svmdk}"
else
logOutput "vmkfstools -i $\{strVMXpath}/$\{sourceDisk} $\{target}/$\{svmdk}"
vmkfstools -i "$\{strVMXpath}/$\{sourceDisk}" "$\{target}/$\{svmdk}"
fi
done
#echo "cd $\{target}"
#logOutput `"cp $\{strVMXpath}/*.vmx $\{target}/"`
logOutput `cp $\{strVMXpath}/*.vmx $\{target}/`
#cp "$\{strVMXpath}/*.vmx" "$\{target}/" 1>>$\{LOGFILE}
logOutput `cp $\{strVMXpath}/*.vmxf $\{target}/`
#cp "$\{strVMXpath}/*.vmxf" "$\{target}/" 1>>$\{LOGFILE}
logOutput `cp $\{strVMXpath}/*.nvram $\{target}/`
#cp "$\{strVMXpath}/*.nvram" "$\{target}/" 1>>$\{LOGFILE}
} # copyDisks
#
\# unregister old, register new, update disks so that no old disks are accidentily
\# referenced by path.
#
function finalizeMigration ()
{
local VMXtarget
local svmdk
echo -e "\nAll disks migrated, now updating the host to finalize the change"
#VMXtarget="$\{strTarget}/$\{strVMname}.vmx"
VMXtarget="$\{strTarget}/$\{strVMX##*/}"
verbose_state=1
logOutput "vmware-cmd -s unregister $\{strVMX}"
logOutput `vmware-cmd -s unregister $\{strVMX}`
#vmware-cmd -s unregister \"$\{strVMX}\"
sleep 20
logOutput "vmware-cmd -s register $\{VMXtarget}"
logOutput `vmware-cmd -s register $\{VMXtarget}`
#vmware-cmd -s register \"$\{VMXtarget}\"
\# Update the config file so that all disks ARE local to the target path
for device in $strVMDKdevice
do
((n += 1))
svmdk=`echo "$\{strVMDKname}" | sed -n "$\{n}p"`
logOutput "vmware-cmd \"$\{VMXtarget}\" setconfig $\{device}.fileName \"$svmdk\""
vmware-cmd $\{VMXtarget} setconfig $\{device}.fileName "$\{strTarget}/$svmdk"
done
}
processArguments $@
vmxfileForVMname # find the vmx config file for the vmname passed
checkConditions # is VM still running? is the target folder already there? if so QUIT
getVirtualDisks
copyDisks
finalizeMigration