This post provides a script to help with storage vMotion – migrating a VMware virtual machine from one VMFS datastore to another with no downtime. It’s a great tool, but in VI35 at least it’s not exposed through the GUI or VC scheduled tasks, so I’ve written a script to perform a few checks and allow for very simplistic 'scheduling' (delay).
I’ve only tested this with VI3.5 and PowerCLI 1.0, but I have no reason to think this wouldn’t with with vSphere vCenter and VI 4.0 hosts.
There are a few caveats with storage vMotion (in VC 2.5/ESX 3.5) at least, you can’t:
- Move the storage of a VM that has a snapshot if the VM is powered on
- Move the storage of a VM that has a snapshot (in any power state) if the VM has disks in a different location than the config file.
Based on these caveats, if instructed the script will suspend a VM with snapshots in order to move the storage, then power the VM back on. Use this with caution, as this may cause an outage of your VMs.
#
# Description:
# Relocate a virtual machine from one datastore to another.
#
# Limitations:
# -
#
# Assumptions, this script works on the assumption that:
# The caller provides credentials with permissions to perform the operations
#
# Arguments:
# vmName, lowecase short name for the virtual machine, eg pibutepr03
# dataStoreName, the new datastore to migrate the VM to
# suspend, Whether or not to suspend a VM and move if the VM has snapshots (which prevent live storage vMotion vi VI35)
# username, The username to connect with, default to the current username environment variable
# password, The Password to use for the connection, not specifying a password will result in a prompt to enter a secure string
# delay, The optional number of seconds to delay before starting the operation
#
#
# Usage:
# PowerShell . .\RelocateVM.ps1 -vmName "vm01" -datastore 'ds02'
# PowerShell . .\RelocateVM.ps1 -vmName "vm01" -datastore 'ds02' -suspend yes -username domain\user
#
# Changes:
# 07/04/2009, Wayne Martin, Initial version
#
param (
$vmName = "",
$dataStoreName = "",
$suspendIfRequired = $false,
$username = $env:username,
$password = "",
$delay = 0
)
$ErrorActionPreference = "Continue"
$invalidArgs = $false
if ($dataStoreName -eq "") { write-output "Please specify the datastore target for the VM, eg. ds02"; $invalidArgs = $true}
if ($vmName -eq "") { write-output "Please specify the virtual machine to move, eg vm01"; $invalidArgs = $true}
if ($account -eq "") { write-output "Please specify a user account to connect with, eg domain\user"; $invalidArgs = $true}
if ($invalidArgs) { write-output "Invalid Arguments, terminating"; exit}
Write-Output "Moving VM '$vmName' to the '$dataStoreName' datastore"
if ($delay -gt 0) {
$hours = $delay / 60 /60
Write-Output "Delaying $delay seconds before beginning ($hours hours)"
Sleep -seconds $delay
}
if ($suspendIfRequired) {
Write-Output "The virtual machine will be suspended if snapshots are preventing storage vMotion"
} else {
Write-Output "If the virtual machine has snapshots and is powered on the storage vMotion will not work"
}
if ($password -eq "" -and !($pass)) {
write-output "No password specified from the command-line"
$pass = Read-Host "Password?" -assecurestring
}
$credential = new-object System.Management.Automation.PSCredential($username,$pass)
$viServer = $null
$suspend = $false
$poweredOn = ""
$snapshot = $null
$hasSnapshot = $null
$viServer = Connect-VIServer -server $vcServerName -Credential $credential
if ($viServer) {
Write-Output (Get-Date -format "dd/MM/yyyy HH:mm:ss")
write-output ("Connected to server " + $viServer.Name + " on port " + $viServer.Port)
$vm = get-vm -name $vmName
if ($vm -and $vm -isnot [object[]]) {
Write-Output ("Found " + $vm.Name)
$hardDisks = $vm.hardDisks
$vmSize = 0
$vmSizeMB = 0
foreach ($harddisk in $vm.hardDisks) {
$vmSize += $hardDisk.CapacityKB
}
$vmSizeMB = $vmSize /1024
$datastore = get-datastore -name $dataStoreName
if ($datastore) {
$freeSpace = $datastore.FreeSpaceMB
if ($freeSpace -gt $vmSizeMB)
{
Write-Output "The datastore $datastoreName has $freeSpace MB available, the VM has disks totalling $vmSizeMB MB"
switch ($vm.PowerState)
{
([VMware.VimAutomation.Types.PowerState]::PoweredOn)
{
Write-Output "The virtual machine is currently powered on"
$poweredOn = $true
}
([VMware.VimAutomation.Types.PowerState]::PoweredOff)
{
Write-Output "The virtual machine is currently powered off"
}
([VMware.VimAutomation.Types.PowerState]::Suspended)
{
Write-Output "The virtual machine is currently suspended"
}
default
{
write-output "Virtual machine power state unknown"
}
}
[object[]]$snapshot = get-snapshot -vm $vm
if ($snapshot)
{
$hasSnapshot = $true
$numSnapshots = $snapshot.Count
Write-Output "$vmName currently has $numSnapshots snapshot(s)"
if ($poweredOn) {
if ($suspendIfRequired) {
Write-Output "$vmName is powered on and has a snapshot, and will be suspended during the move"
$suspend = $true
} else {
Write-Output "Error: $vmName is powered on and has a snapshot, but will not be suspended, process aborted"
$suspend = $false
exit 2
}
}
} else {
$suspend = $false
$hasSnapshot = False
Write-Output "No snapshots currently found for $vmName"
}
if ($suspend) {
Write-Output "Suspending $vmName"
Suspend-VM -vm $vm -confirm:$false
}
Write-Output "Moving $vmName to $datastoreName"
Move-VM -vm $vmName -datastore $datastoreName
if ($suspend) {
Write-Output "Bringing $vmName out of suspension"
Start-VM -vm $vm -confirm:$false
}
Write-Output "$vmName migrated to $datastoreName"
} else {
Write-Output "The datastore only has $freeSpace MB available, but the VM has disks totalling $vmSizeMB MB"
}
} else {
Write-Output "Error: datastore $datastoreName not found"
}
} else {
if ($vm -is [object[]])
{
Write-Output "Multiple objects returned for $vmname, please specify a single VM"
} else {
write-output "VM Not found - $vmName"
}
}
} else {
write-output "ERROR: VI server not found - $viServer"
}
Write-Output (Get-Date -format "dd/MM/yyyy HH:mm:ss")
Disconnect-VIServer -confirm:$false
exit 0
Wayne's World of IT (WWoIT), Copyright 2009 Wayne Martin.
Read more!