Bash Shell: Old School Migration of ESXi Guest Virtual Machines

Step 0: Preparations

Estimate file-copying duration for cut-over:
– Sustained storage transfer speed using a 1GB Ethernet Connection is approximately 99.12 MB/s (Mebibytes per second)
– To move 1GB (1024MB) of storage, it takes 10.33 seconds
– 100GB will require 1033 seconds (17.22 minutes)

Perform some systems discovery on ESXi hosts:

# The quick method of listing available volumes
mountDirectory=/vmfs/volumes
availableVolumes=$(echo "ls $mountDirectory"|readlink -f $_)

# Alternative and more convoluted method of listing available volumes
mountDirectory=/vmfs/volumes
symlinkMarker="\->"
symlinks=$(ls -la $mountDirectory/ | grep $symlinkMarker)
availableVolumes=$(echo $symlinks | eval "sed -n -e 's/^.*$symlinkMarker //p'"|echo $mountDirectory/$_)
# Explain:
# - Pipe $symlinks to eval to execute an expression. Similar to Invoke-Command in Powow Shill
# - Sed is the go-to tool for search and replace
# - -n to default as not to print anything
# - -e follows the sed command as default syntax. It probably stands for evaluate
# - s is to search and replace
# - /^.* means all characters from beginning of string toward delimiter of $symlinkMarker as the $matches
# - // means empty string as replacement for the $matches
# - p is to print the result
# - Last section is to append $mountDirectory to the sub-directory names

# List mounts
echo $availableVolumes

# List contents of each mount. Please note that explicit casting of variables with double quotes is a
# requirement for pipe operations, allowing processing of such variables line-by-line, rendering $_ variable as per line
echo "$availableVolumes" | ls $_

Define these variables:

# Define source variables
SOURCESERVER=10.10.10.101
SOURCESERVERPORT=22
SOURCEUSERNAME=brucelee
sourceStoragePath=/vmfs/volumes/5ed459d2-8f5430cd-3762-94c691ac4caa
sourceDirectoryName=Windows-PC1
SOURCEDIRECTORY=$sourceStoragePath/$sourceDirectoryName/
SOURCEFILES=$SOURCEDIRECTORY*.*

# Define destination variables
DESTINATIONSERVER=10.10.10.100
DESTINATIONSERVERPORT=22
DESINATIONUSERNAME=brucelee
destinationStoragePath=/vmfs/volumes/5c8206f0-49fe1606-76b6-94c691abb6e2
destinationDirectoryName=Windows-PC1
DESTINATIONDIRECTORY=$destinationStoragePath/$destinationDirectoryName/

vmxName=$destinationDirectoryName.vmx
vmdkName=$destinationDirectoryName.vmdk
VMXFILE=$DESTINATIONDIRECTORY$vmxName
VMDKFILE=$DESTINATIONDIRECTORY$vmdkName

resizedVmdkName=$destinationDirectoryName-resized.vmdk
resizedVmdk=$DESTINATIONDIRECTORY$resizedVmdkName

Remove any localized resources on the reference VM that the destination server does not have access, such as:
– Virtual disks
– Virtual networks
– ISO mounts
– USB pass-through dongles

Step 1: Ensure that SSH Server is running at Destination and SSH Client is running at Source

SSH Server Settings:
Host > Manage > Services > right-click the SSH service with alias “TSM-SSH” > Start SSH service (if not already started)

SSH Client Settings:
Networking > Firewall rules > right-click SSH Client > “Enable” (if not already enabled)

Step 2: Sanitize the destination
# ssh into Destination server
ssh $DESINATIONUSERNAME@$DESTINATIONSERVER$DESTINATIONSERVERPORT
# Optional: create the destination directory if not exists
mkdir -p $destinationStoragePath/$destinationDirectoryName
# Optional: Empty destination directory
rm -r $DESTINATIONDIRECTORY*
Step 3: Check SSH Services while login to a SSH session of the Source Server
# ssh into Source server
ssh $SOURCEUSERNAME@$SOURCESERVER -p$SOURCESERVERPORT
# Verify Destination SSH reachability
ssh $DESINATIONUSERNAME@$DESTINATIONSERVER -p$DESTINATIONSERVERPORT "echo 2>&1" && echo $DESTINATIONSERVER OK || echo $DESTINATIONSERVER NOT-OK
Step 4: Perform the copy
# ssh into Source server
ssh $SOURCEUSERNAME@$SOURCESERVER -p$SOURCESERVERPORT
# Use SCP to copy files
scp -P $DESTINATIONSERVERPORT $SOURCEFILES $DESINATIONUSERNAME@$DESTINATIONSERVER:$DESTINATIONDIRECTORY
Step 5: Register the Migrated VM on New Host
# ssh into Destination server
ssh $DESINATIONUSERNAME@$DESTINATIONSERVER$DESTINATIONSERVERPORT
# Register VM with this host
vmid=$(vim-cmd solo/registervm $VMXFILE)

$ Power On VM
# List all VMs: vim-cmd /vmsvc/getallvms
# Check VM power state: vim-cmd /vmsvc/power.getstate $vmid
vim-cmd vmsvc/power.on $vmid

# If this machine is freshly registered on this host, it will require a confirmation that it has been "copied" or "moved" via the ESXi GUI. In such context, migrated correlates to moved.

Sample Output:

[brucelee@esx02:~] echo $VMXFILE
/vmfs/volumes/5ed456fa-5daa2d25-e3bd-94c691ac4caa/Windows-PC1/Windows-PC1.vmx
[brucelee@esx02:~] vim-cmd solo/registervm $VMXFILE
3

Optional: resize the main disk and attach the resized

# Convert from thick to thin provisioning by cloning the original disk to a thin-provisioned copy
vmkfstools -i $VMDKFILE -d thin $resizedVmdk

# Reset the resizing virtual disk to a $desiredDiskSize:
desiredDiskSize=120g
vmkfstools -X $desiredDiskSize $resizedVmdk

# Detach the original disk
# Usage: device.diskremove vmid 'controller number' 'unit number' 'delete file'(y/n)
vim-cmd vmsvc/device.diskremove $vmid 0 0 n

# Attach resized disk to scsi controller zero disk index 0 (or any available index)
vim-cmd vmsvc/device.diskaddexisting $vmid $resizedVmdk 0 0
Step 6: Unregister VM from Old Host
# ssh into Source server
ssh $SOURCEUSERNAME@$SOURCESERVER -p$SOURCESERVERPORT
# Check all VMs
[brucelee@esx02:~] vim-cmd vmsvc/getallvms
Vmid Name File Guest OS Version Annotation
2 Windows-PC1 [Micron-SSD-894GB] Windows-PC1/Windows-PC1.vmx windows9_64Guest vmx-14

# Set $vmid according to result(s) above
vmid=2

# Unregister VM
[brucelee@esx02:~] vim-cmd /vmsvc/unregister $vmid

Easy lap, right? This is a lot simpler than a 10-page of GUI screenshots, more likely to save your Change Control Board members’ time so they can enjoy other items on the agenda. With such competence and efficiency, one could even move to entertain Boardies with Operation Kung Fu Panda (taking breaks for Chinese food), or something.

Leave a Reply

Your email address will not be published. Required fields are marked *