param( [string]$SrcDatastore, [string]$DstDatastore, [string]$RPool, [string]$vDiskFormat = "as-source", [int]$MoveTemplate="0", [int]$MinLeft="10", [int]$ForceMove="0", [int]$WhatIf="1", [int]$SendMail="0") $SMTPSRV = "" $EmailFrom = "" $EmailTo = "" $RelocateMaxRetry = "5" $StartDate = Get-Date $ErrorActionPreference = "SilentlyContinue" $WarningPreference = "SilentlyContinue" function Move-VMThinIf { param( [Parameter(Mandatory=$true,ValueFromPipeline=$true,HelpMessage="Virtual Machine Object to Migrate")] [ValidateNotNullOrEmpty()] [System.String]$VM ,[Parameter(Mandatory=$true,ValueFromPipeline=$true,HelpMessage="Virtual Machine Objects to Migrate")] [ValidateNotNullOrEmpty()] [Object]$VMtoMove ) Begin {} Process { $TaskObj = "" | Select VM,State,"TaskTime (Min)" $TaskObj.VM = $VM if (($VMtoMove|?{$_.name -match $VM}|?{$_.Runtime.PowerState -eq "PoweredOn"}|?{$_.Snapshot}|Measure-Object).count -gt 0 -and $ForceMove -eq 0) {$TaskObj.State = "ko : PowerOn/Snapshot"} elseif (($VMtoMove|?{$_.name -match $VM}|%{$_.Config.Hardware.Device}|?{$_.GetType().Name -eq "VirtualDisk"}|%{$_.Backing.CompatibilityMode}|Measure-Object).count -gt 0) {$TaskObj.State = "ko : RDM"} elseif (($VMtoMove|?{$_.name -match $VM}).Datastore.length -gt 1 -and $ForceMove -eq 0) {$TaskObj.State = "ko : Multiple datastores"} else {$TaskObj.State = "ok"} $TaskObj."TaskTime (Min)" = "N/A" } End{ Return $TaskObj } } function Move-VMThin { #modded from http://poshcode.org/1579 PARAM( [Parameter(Mandatory=$true,ValueFromPipeline=$true,HelpMessage="Virtual Machine Object to Migrate")] [ValidateNotNullOrEmpty()] [System.String]$VM ,[Parameter(Mandatory=$true,HelpMessage="Destination Datastore")] [ValidateNotNullOrEmpty()] [System.String]$Datastore ) Begin { #Nothing Necessary to process } #Begin Process { #Prepare Migration info, uses .NET API to specify a transformation to thin disk $vmView = Get-View -ViewType VirtualMachine -Filter @{"Name" = "$VM"} $dsView = Get-View -ViewType Datastore -Filter @{"Name" = "^$Datastore$"} #Abort Migration if free space on destination datastore is less than $($MinLeft)GB if (($dsView.info.freespace / 1GB) -lt $MinLeft) { Write-Host -ForegroundColor Red "Less than $($MinLeft)GB free space left on $Datastore Datastore !" #Add report $TaskObj = "" | Select VM,State,"TaskTime (Min)" $TaskObj.VM = $VM $TaskObj.State = "aborted" $TaskObj."TaskTime (Min)" = "0" Return $TaskObj } #Warn Migration if free space on destination datastore is less than the vm size if (($vmView|%{$_.Config.Hardware.Device}|?{$_.GetType().Name -eq "VirtualDisk"}|%{$_.CapacityInKB / 1MB}|measure-object -sum).sum -gt ($dsView.info.freespace / 1GB)) {Write-Host -ForegroundColor Yellow "Low free space left on $Datastore Datastore !"} #Prepare VM Relocation Specificatoin $spec = New-Object VMware.Vim.VirtualMachineRelocateSpec $spec.datastore = $dsView.MoRef $spec.transform = $vDiskFormat #Perform Migration $VMRelocateTask = $vmView.RelocateVM_Task($spec, $null) #Wait until task is finished Write-Host -ForegroundColor Green "Moving $VM from $SrcDatastore to $DstDatastore..." -NoNewline sleep 15 While ((Get-Task|?{$_.id -match $VMRelocateTask.value}).state -match "Running") { Write-Host -ForegroundColor Green "." -NoNewline #Abort Migration if free space on destination datastore is getting less than $($MinLeft)GB during svmotion if (((Get-View -ViewType Datastore -Filter @{"Name" = "^$Datastore$"}).info.freespace / 1GB) -lt $MinLeft) { Write-Host "" Write-Host -ForegroundColor Red "Less than $($MinLeft)GB free space left on $Datastore Datastore !" Write-Host -ForegroundColor Red "Aborting $VM move task..." Stop-Task (Get-Task|?{$_.id -match $VMRelocateTask.value}) -Confirm:$false #Add report $TaskObj = "" | Select VM,State,"TaskTime (Min)" $TaskObj.VM = $VM $TaskObj.State = "aborted" $TaskObj."TaskTime (Min)" = "0" Return $TaskObj } sleep 30 #sleep until move is finished } if (!(Get-Task|?{$_.id -match $VMRelocateTask.value}).state) { $svmStatus = "failed" Write-Host -ForegroundColor Red $svmStatus $svmtime = 0 } else { $svmStatus = (Get-Task|?{$_.id -match $VMRelocateTask.value}).state if ($svmStatus -match "success") {Write-Host -ForegroundColor Green $svmStatus} else {Write-Host -ForegroundColor Red $svmStatus} $svmtime = [math]::Round(((Get-Task|?{$_.id -match $VMRelocateTask.value}).FinishTime - (Get-Task|?{$_.id -match $VMRelocateTask.value}).StartTime).TotalMinutes,0) } #Add report $TaskObj = "" | Select VM,State,"TaskTime (Min)" $TaskObj.VM = $VM $TaskObj.State = $svmStatus $TaskObj."TaskTime (Min)" = $svmtime } #Process End{ Return $TaskObj } } function Send-SMTPmail($to, $from, $subject, $smtpserver, $body) { $mailer = new-object Net.Mail.SMTPclient($smtpserver) $msg = new-object Net.Mail.MailMessage($from,$to,$subject,$body) $msg.IsBodyHTML = $true $mailer.send($msg) } if (!$SrcDatastore -or !$DstDatastore) {Write-Host -ForegroundColor Red "Src or Dst Datastore missing";break} #check args if ($SrcDatastore -eq $DstDatastore) {Write-Host -ForegroundColor Red "Src Datastore is Dst Datastore !";break} #check DS name if (!($SrcDsObj = Get-Datastore $SrcDatastore) -or !($DstDsObj = Get-Datastore $DstDatastore)) #check Datastores {Write-Host -ForegroundColor Red "Src or Dst Datastore is not valid";break} if (!($SrcDsObj|get-view).vm.count -gt 0) #check empty Src datastore {Write-Host -ForegroundColor Red "Src Datastore is empty";break} if (($SrcDsObj.CapacityMB - $SrcDsObj.FreeSpaceMB) -gt $DstDsObj.FreeSpaceMB -and $WhatIf -eq "0") #check Dst free space { Write-Host -ForegroundColor Yellow "Not enough free space on $DstDatastore Datastore !" Remove-Variable -name "Quizz" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue While ($Quizz -cne "YES" -and $Quizz -cne "NO") { $Quizz = (Read-Host "Force ? [YES/NO]") } if ($Quizz -ceq "NO") {Write-Host -ForegroundColor Yellow "Check $DstDatastore Datastore free space";break} } Remove-Variable -name "VMtoMove" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue if ($RPool) { $SrcRPool = (Get-ResourcePool $RPool).id if (!$SrcRPool) {Write-Host -ForegroundColor Red "Bad ResourcePool";break} #check RP $VMtoMove = (get-view -ViewType "datastore" -Filter @{"Name" = "^$SrcDsObj$" }).vm|%{get-view $_}|?{$_.ResourcePool -match "^$SrcRPool$"}|sort -Property {$_.Summary.Storage.Committed} } else {$VMtoMove = (get-view -ViewType "datastore" -Filter @{"Name" = "^$SrcDsObj$" }).vm|%{get-view $_}|sort -Property {$_.Summary.Storage.Committed}} if ($WhatIf -eq "0"){ if ($MoveTemplate -eq "1") {Remove-Variable -name "TemplateList" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue;$RWWarn = "WRITE MODE - VM & Templates will be moved"} else {$RWWarn = "WRITE MODE - VM will be moved"} #$vDiskFormat if ($vDiskFormat -ceq "thin") {$vDiskFormat = "sparse"} elseif ($vDiskFormat -ceq "thick") {$vDiskFormat = "flat"} elseif ($vDiskFormat -ceq "as-source") {Remove-Variable -name "vDiskFormat" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue} } $RelocateReport = @() $RelocateRetry = 0 $RetryList = @() $Quizz = "" if ($WhatIf -eq "1"){Write-Host -ForegroundColor Yellow "READ ONLY MODE - Nothing will be changed"} if($RPool) { if ($WhatIf -eq "0"){ Write-Host -ForegroundColor RED "WRITE MODE - VM will be moved" While ($Quizz -cne "YES" -and $Quizz -cne "NO") { $Quizz = (Read-Host "Go ? [YES/NO]") } if ($Quizz -ceq "NO") {Write-Host -ForegroundColor Yellow "Bye";break} } While (($VMcount = (($VMsleft = $SrcDsObj|Get-VM|?{$_.ResourcePoolId -match "^$SrcRPool$"})|Measure-Object).count) -gt 0) { ForEach ($VMleft in $VMsleft) { if ($WhatIf -eq "0"){ if ($ForceMove -eq 0){ if (!((($VMtoMove|?{$_.Runtime.PowerState -eq "PoweredOn"}|?{$_.name -eq $VMleft.name}).Snapshot|Measure-Object).count -gt 0 -or ($VMtoMove|?{$_.name -eq $VMleft.name}|%{$_.Config.Hardware.Device}|?{$_.GetType().Name -eq "VirtualDisk"}|%{$_.Backing.CompatibilityMode}|Measure-Object).count -gt 0 -or ($VMtoMove|?{$_.name -match $VMleft.name}).Datastore.length -gt 1 -or $RetryList -match $VMleft.name)){ Write-Host -ForegroundColor Yellow "$VMcount VM left..." $svmTask = Move-VMThin -VM $VMleft.name -Datastore $DstDatastore $RelocateReport += $svmTask if ($svmTask.state -match "success"){$VMcount = $VMcount-1} } elseif ($RetryList -match $VMleft.name){} else {$RetryList += $VMleft.name;$VMcount = $VMcount-1;Write-Host -ForegroundColor Yellow "$VMleft skipped" } } elseif ($ForceMove -eq 1){ if (!(($VMtoMove|?{$_.name -eq $VMleft.name}|%{$_.Config.Hardware.Device}|?{$_.GetType().Name -eq "VirtualDisk"}|%{$_.Backing.CompatibilityMode}|Measure-Object).count -gt 0 -or $RetryList -match $VMleft.name)){ Write-Host -ForegroundColor Yellow "$VMcount VM left..." $svmTask = Move-VMThin -VM $VMleft.name -Datastore $DstDatastore $RelocateReport += $svmTask if ($svmTask.state -match "success"){$VMcount = $VMcount-1} } elseif ($RetryList -match $VMleft.name){} else {$RetryList += $VMleft.name;$VMcount = $VMcount-1;Write-Host -ForegroundColor Yellow "$VMleft skipped" } } } elseif ($WhatIf -eq "1"){ $svmTask = Move-VMThinIf -VM $VMleft.name -VMtoMove $VMtoMove $RelocateReport += $svmTask $VMcount = $VMcount-1 } } if ($WhatIf -eq "0"){ $RelocateRetry++ if ($RelocateRetry -ge $RelocateMaxRetry) {Write-Host -ForegroundColor Yellow "Max retry count exceeded !";break} } elseif ($WhatIf -eq "1"){break} } } else { if ($WhatIf -eq "0"){ Write-Host -ForegroundColor RED $RWWarn While ($Quizz -cne "YES" -and $Quizz -cne "NO") { $Quizz = (Read-Host "Go ? [YES/NO]") } if ($Quizz -ceq "NO") {Write-Host -ForegroundColor Yellow "Bye";break} if ($MoveTemplate -eq "1") { if (((($TemplateList = Get-Template|Get-View|?{$_.Datastore -match $SrcDsObj.id}|select name)|Measure-Object)).count -gt 0){$VMToConvert = $TemplateList ; $TemplateList|%{Get-Template $_.name | Set-Template -tovm | out-null}} } } While (($VMcount = (($VMsleft = $SrcDsObj|Get-VM)|Measure-Object).count) -gt 0) { ForEach ($VMleft in $VMsleft) { if ($WhatIf -eq "0"){ if ($ForceMove -eq 0){ if (!((($VMtoMove|?{$_.Runtime.PowerState -eq "PoweredOn"}|?{$_.name -eq $VMleft.name}).Snapshot|Measure-Object).count -gt 0 -or ($VMtoMove|?{$_.name -eq $VMleft.name}|%{$_.Config.Hardware.Device}|?{$_.GetType().Name -eq "VirtualDisk"}|%{$_.Backing.CompatibilityMode}|Measure-Object).count -gt 0 -or ($VMtoMove|?{$_.name -match $VMleft.name}).Datastore.length -gt 1 -or $RetryList -match $VMleft.name)){ Write-Host -ForegroundColor Yellow "$VMcount VM left..." $svmTask = Move-VMThin -VM $VMleft.name -Datastore $DstDatastore $RelocateReport += $svmTask if ($svmTask.state -match "success"){$VMcount = $VMcount-1} } elseif ($RetryList -match $VMleft.name){} else {$RetryList += $VMleft.name;$VMcount = $VMcount-1;Write-Host -ForegroundColor Yellow "$VMleft skipped" } } elseif ($ForceMove -eq 1){ if (!(($VMtoMove|?{$_.name -eq $VMleft.name}|%{$_.Config.Hardware.Device}|?{$_.GetType().Name -eq "VirtualDisk"}|%{$_.Backing.CompatibilityMode}|Measure-Object).count -gt 0 -or $RetryList -match $VMleft.name)){ Write-Host -ForegroundColor Yellow "$VMcount VM left..." $svmTask = Move-VMThin -VM $VMleft.name -Datastore $DstDatastore $RelocateReport += $svmTask if ($svmTask.state -match "success"){$VMcount = $VMcount-1} } elseif ($RetryList -match $VMleft.name){} else {$RetryList += $VMleft.name;$VMcount = $VMcount-1;Write-Host -ForegroundColor Yellow "$VMleft skipped" } } } elseif ($WhatIf -eq "1"){ $svmTask = Move-VMThinIf -VM $VMleft.name -VMtoMove $VMtoMove $RelocateReport += $svmTask $VMcount = $VMcount-1 } } if ($WhatIf -eq "0"){ $RelocateRetry++ if ($RelocateRetry -ge $RelocateMaxRetry) {Write-Host -ForegroundColor Yellow "Max retry count exceeded !";break} } elseif ($WhatIf -eq "1"){break} } } if (((($TemplateList = Get-Template|Get-View|?{$_.Datastore -match $SrcDsObj.id})|Measure-Object)).count -gt 0 -and $WhatIf -eq "1"){ foreach ($TemplateObj in $TemplateList){ $TaskObj = "" | Select VM,State,"TaskTime (Min)" $TaskObj.VM = $TemplateObj.name if ($MoveTemplate -eq "0") {$TaskObj.State = "ko : Template"} else {$TaskObj.State = "ok : Template"} $TaskObj."TaskTime (Min)" = "N/A" $RelocateReport += $TaskObj } } if ($MoveTemplate -eq "1" -and $VMToConvert -and $WhatIf -eq "0") {$VMToConvert|%{(Get-VM $_.name|Get-View).MarkAsTemplate()|Out-Null}} if ($SMTPSRV -and $EmailFrom -and $EmailTo -and !$RPool -and $SendMail -eq "1") { $RelocateReport|ft -AutoSize if (!$RPool -and $WhatIf -eq 0){ Write-host -ForegroundColor Cyan "Relocated $([Math]::Round(($SrcDsObj.CapacityMB - $SrcDsObj.FreeSpaceMB)/1024,2))GB in $([Math]::Round($(($(Get-Date) - $StartDate).TotalMinutes),1))min @ $([Math]::Round($(($SrcDsObj.CapacityMB - $SrcDsObj.FreeSpaceMB))/$($(($(Get-Date) - $StartDate).TotalMinutes))/60),2)MB/s"} $RelocateReport = $RelocateReport|ConvertTo-Html send-SMTPmail $EmailTo $EmailFrom "[VMware] move-datastore from $SrcDatastore to $DstDatastore in $([Math]::Round($(($(Get-Date) - $StartDate).TotalMinutes),1))min @ $([Math]::Round($(($SrcDsObj.CapacityMB - $SrcDsObj.FreeSpaceMB))/$($(($(Get-Date) - $StartDate).TotalMinutes))/60),2)MB/s" $SMTPSRV $RelocateReport } elseif ($SMTPSRV -and $EmailFrom -and $EmailTo -and $RPool -and $SendMail -eq "1") { $RelocateReport|ft -AutoSize if (!$RPool -and $WhatIf -eq 0){ Write-host -ForegroundColor Cyan "Relocated $([Math]::Round(($SrcDsObj.CapacityMB - $SrcDsObj.FreeSpaceMB)/1024,2))GB in $([Math]::Round($(($(Get-Date) - $StartDate).TotalMinutes),1))min @ $([Math]::Round($(($SrcDsObj.CapacityMB - $SrcDsObj.FreeSpaceMB))/$($(($(Get-Date) - $StartDate).TotalMinutes))/60,2))MB/s"} $RelocateReport = $RelocateReport|ConvertTo-Html send-SMTPmail $EmailTo $EmailFrom "[VMware] move-datastore from $SrcDatastore to $DstDatastore filtered on $RPool" $SMTPSRV $RelocateReport } else {$RelocateReport|ft -AutoSize if (!$RPool -and $WhatIf -eq 0){ Write-host -ForegroundColor Cyan "Relocated $([Math]::Round(($SrcDsObj.CapacityMB - $SrcDsObj.FreeSpaceMB)/1024,2))GB in $([Math]::Round($(($(Get-Date) - $StartDate).TotalMinutes),1))min @ $([Math]::Round($(($SrcDsObj.CapacityMB - $SrcDsObj.FreeSpaceMB))/$($(($(Get-Date) - $StartDate).TotalMinutes))/60,2))MB/s"} }