DanTourangeau
Contributor
Contributor

Move 2 VMs at the on 2 or more different Hosts

Jump to solution

I found this PowerCli script from LucD. It does work really well. 🙂

However I'm trying to find a way to run only 2 storage vMotions on 2 or more hosts reading a list of VMs for each host.
It does work but only 1 host at the time, no sure how to run to foreach at the same time or it will be an entire new way?

That is what I have so far, I've tried to add some if or else if but always ends up with the same results.
Any help or suggestion will be gladly appreciate. 🙂

Thanks,

$vmhost1 = "hostname1"
$vmhost2 = "hostname2"

$vmlist = Import-CSV c:\Temp\migration\VMs_List-123.csv
$vmlist1 = Import-CSV c:\Temp\migration\VMs_List-123.csv | where {$_.vmhost -eq $vmhost1}
$vmlist2 = Import-CSV c:\Temp\migration\VMs_List-123.csv | where {$_.vmhost -eq $vmhost2}

$maxParallel = 2


foreach ($VM1 in $VMlist1) {

write-host "Currently moving VM" $vm1.name "to the DatastoreCluster " $ds -ForegroundColor Green
Move-VM -VM (Get-VM -Name $vm1.name) -Datastore $ds -RunAsync
do {
sleep 5
}
while((Get-Task -Status Running | where{$_.Name -eq 'ApplyStorageDrsRecommendation_Task'}).Count -ge $maxParallel)

foreach ($VM2 in $VMlist2) {

write-host "Currently moving VM" $vm2.name "to the DatastoreCluster " $ds -ForegroundColor Green
Move-VM -VM (Get-VM -Name $vm2.name) -Datastore $ds -RunAsync
do {
sleep 5
}
while((Get-Task -Status Running | where{$_.Name -eq 'ApplyStorageDrsRecommendation_Task'}).Count -ge $maxParallel)
}

}
Disconnect-VIServer -Server * -Confirm:$false

0 Kudos
70 Replies
LucD
Leadership
Leadership

Just enter a new line with only Get-Task on it.
Do that before the other instances of the Get-Task cmdlet


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
DanTourangeau
Contributor
Contributor

This is before the 1st Get-Task cmdlet.

However I had to stop the script and run Get-Task as just adding a new line with Get-Task did show the tasks.

0 Kudos
LucD
Leadership
Leadership

Ok, I did some more testing and it looks as if the Task event doesn not contain the VM's name in EntityName when the Datastore parameter on the Move-VM cmdlet has a DatastoreCluster.
When it is a Datastore, the EntityName does contain the VM's name.

Are you doing the Move-VM to a DatastoreCluster?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
DanTourangeau
Contributor
Contributor

Yes for this migration all the VMs will be moved to a Datastores Cluster.

Thanks,

0 Kudos
LucD
Leadership
Leadership

Ok, then we need to rethink the logic of the script


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
DanTourangeau
Contributor
Contributor

I don't think I will help you much with this as I am not really advance in PowerCli.

However, I found that this line "$vm.ExtensionData.RecentTask.value" return the task ID of this $vm.
Is it possible to match that info with the $TaskArr.Id to find the correct VM and then Power it on?

I'm trying to add that to the script but I'm struggling.

Thanks! 

0 Kudos
LucD
Leadership
Leadership

Ok, I tried to rewrite the code to use a Hash Table.
That way we can use a Key (Task.Id) and a Value (VmName).

That allows use to find back the name of the VM based on the TaskId.

$vmhost1 = "hostname1"
$vmhost2 = "hostname2"
$maxParallel = 2

#[System.Collections.ArrayList]$TaskArr = @()
$TaskArr = @{}

$groups = Import-Csv -Path 'c:\Temp\migration\VMs_List-123.csv' -UseCulture |
Group-Object -Property vmhost

$i = $j = 0

$eof = $false
while (-not $eof) {
  Write-Verbose "EOF: $eof   I: $i   J:$j"
  if ($i -lt $groups[0].Group.Count) {
    $vm = Get-VM -Name $groups[0].Group[$i].name
    Shutdown-VMGuest -VM $vm -Confirm:$false
    while ((Get-VM -Name $vm).PowerState -ne 'PoweredOff') {
      Start-Sleep 5
    }
    $t = Move-VM -VM $vm -Datastore $ds -RunAsync
    Write-Verbose "Move $($vm.Name) to datastore $($ds.Name) "
    if ($t) {
      Write-Verbose "Task created: $($t.Id)"
#      $TaskArr.Add($t) | Out-Null
      $TaskArr.Add($t.Id,$vm.Name)
      Write-Verbose "TaskArr Count : $($TaskArr.Count)"
    } else {
      Write-Verbose "Move Task is empty"
    }
    $i++
  }
  if ($j -lt $groups[1].Group.Count) {
    $vm = Get-VM -Name $groups[1].Group[$j].name
    Shutdown-VMGuest -VM $vm -Confirm:$false
    while ((Get-VM -Name $vm).PowerState -ne 'PoweredOff') {
      Start-Sleep 5
    }
    $t = Move-VM -VM $vm -Datastore $ds -RunAsync
    Write-Verbose "Move $($vm.Name) to datastore $($ds.Name) "
    if ($t) {
      Write-Verbose "Task created: $($t.Id)"
#      $TaskArr.Add($t) | Out-Null
      $TaskArr.Add($t.Id, $vm.Name)
      Write-Verbose "TaskArr Count : $($TaskArr.Count)"
    } else {
      Write-Verbose "Move Task is empty"
    }
    $j++
  }
  Write-Verbose "EOF: $eof   I: $i   J:$j"
  Write-Verbose "TaskArr Count : $($TaskArr.Count)"
#  Write-Verbose "IDs : $($TaskArr.Id -join '|')"
  Write-Verbose "IDs : $($TaskArr.Keys -join '|')"

  while ((Get-Task -PipelineVariable $tCompleted |
#      Where-Object { $_.State -eq 'Running' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Id -contains $_.Id }).Count -ge $maxParallel) {
      Where-Object { $_.State -eq 'Running' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Keys -contains $_.Id }).Count -ge $maxParallel) {
    Write-Verbose "Still background tasks running - going to sleep"
    Start-Sleep 5
  }

  Write-Verbose "Checking for VMs to start"
  Write-Verbose "Tasks in hash table $($TaskArr.Count)"
  Get-Task |
#  Where-Object { $_.State -eq 'Success' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Id -contains $_.Id } |
  Where-Object { $_.State -eq 'Success' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Keys -contains $_.Id } |
  ForEach-Object -Process {
    Write-Verbose "Start VM $($_.ExtensionData.Info.EntityName)"
#    Get-VM -Name $_.ExtensionData.Info.EntityName | Start-VM -Confirm:$false
    Get-VM -Name $TaskArr[$_.Id] | Start-VM -Confirm:$false
#    $TaskArr.Remove($_)
    $TaskArr.Remove($_.Id)
  }
  Write-Verbose "Task in hash table $($TaskArr.Count)"

  if ($i -eq $groups[0].Group.Count -and $j -lt $groups[1].Group.Count) {
    Write-Verbose "Reached the end of both VM lists"
    $eof = $true
  }
}

Write-Verbose "Wait till remaining Tasks are finished"
while ($TaskArr.Count -gt 0) {
  Get-Task |
#  Where-Object { $_.State -eq 'Success' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Id -contains $_.Id } |
  Where-Object { $_.State -eq 'Success' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Keys -contains $_.Id } |
  ForEach-Object -Process {
    Write-Verbose "Start VM $($_.ExtensionData.Info.EntityName)"
#    Get-VM -Name $_.ExtensionData.Info.EntityName | Start-VM -Confirm:$false
    Get-VM -Name $TaskArr[$_.Id] | Start-VM -Confirm:$false
#    $TaskArr.Remove($_)
    $TaskArr.Remove($_.Id)
  }
  $erroredOut = @()
  Get-Task |
#  Where-Object { $_.State -eq 'Error' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Id -contains $_.Id } |
  Where-Object { $_.State -eq 'Error' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Keys -contains $_.Id } |
  ForEach-Object -Process {
#    Write-Verbose "Error for VM $($_.ExtensionData.Info.EntityName)"
#    $erroredOut += $_.ExtensionData.Info.EntityName
    Write-Verbose "Error for VM $($TaskArr[$_.Id])"
    $erroredOut += $TaskArr[$_.Id]
    $TaskArr.Remove($_.Id)
  }
}
if ($erroredOut.Count -gt 0) {
  Write-Error "vMotion failed for VMs $($erroredOut -join ',')"
}

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
DanTourangeau
Contributor
Contributor

Wow Luc, that is great work!

The VMs are able to stop, migrate and start.
Quick question however, how many VMs are you testing the script with?

I tested it with 8 VMs with a "$maxParallel = 2" and on 8 VMs there is 1 or 2 that didn't transfer and the script doesn't give me any errors and doesn't reach the eof.

I tested it with 8 VMs with a "$maxParallel = 4" and on 8 there is 2 or 3 that didn't transfer and the script doesn't give me any errors and doesn't reach the eof. After the script start about 5 VM, I don't see it going to sleep anymore but looping in :

  Write-Verbose "Checking for VMs to start"
  Write-Verbose "Tasks in hash table $($TaskArr.Count)"
  Get-Task |
#  Where-Object { $_.State -eq 'Success' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Id -contains $_.Id } |
  Where-Object { $_.State -eq 'Success' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Keys -contains $_.Id } |
  ForEach-Object -Process {
    Write-Verbose "Start VM $($_.ExtensionData.Info.EntityName)"
#    Get-VM -Name $_.ExtensionData.Info.EntityName | Start-VM -Confirm:$false
    Get-VM -Name $TaskArr[$_.Id] | Start-VM -Confirm:$false
#    $TaskArr.Remove($_)
    $TaskArr.Remove($_.Id)
  }
  Write-Verbose "Task in hash table $($TaskArr.Count)"

  if ($i -eq $groups[0].Group.Count -and $j -lt $groups[1].Group.Count) {
    Write-Verbose "Reached the end of both VM lists"
    $eof = $true
  }

 

Thanks again for all the hard works!

Tags (1)
0 Kudos
LucD
Leadership
Leadership

I tested of course in a limited environment, 4 VMs with MaxParallel set to 2.

I have no clue why some VMs failed to move.
Perhaps have a look at the events for those VMs.

The script is of course not handling cases when a Task fails (Error status).
That would require additional code.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
DanTourangeau
Contributor
Contributor

I checked and there is no info, like there are been skipped, also it's different VMs each time.
I'll keep testing, Thanks again! 🙂

0 Kudos
DanTourangeau
Contributor
Contributor

Hi LucD, just an FYI...

I added another check after the $maxParallel as when it reach close of the eof and there is only 2 VMs left but the $maxParallel is set to 4 it was skipping them. The added check will see if there is still some tasks running and if so it will continue sleeping/checking for VMs to start.

Thanks again for your help, I wouldn't be able to have accomplish this without you!

  while ((Get-Task -PipelineVariable $tCompleted |
      Where-Object { $_.State -eq 'Running' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Keys -contains $_.Id }).Count -ge $maxParallel) {
    Write-Host "Still background tasks running - going to sleep" -ForegroundColor Yellow
    Start-Sleep 60
    }
    if ($i -eq $groups[0].Group.Count -and $j -eq $groups[1].Group.Count -and $TaskArr.Count -ge 1 ) {
    Write-Host "Still some background tasks running - going to sleep" -ForegroundColor Yellow
    Start-Sleep 30
    Write-Verbose "Checking for VMs to start"
    Write-Verbose "Tasks in hash table $($TaskArr.Count)"
    Get-Task |
    Where-Object { $_.State -eq 'Success' -and $_.Name -eq 'ApplyStorageDrsRecommendation_Task' -and $TaskArr.Keys -contains $_.Id } |
        ForEach-Object -Process {
        Write-Host "Start VM $($TaskArr[$_.Id])" -ForegroundColor Magenta
        Get-VM -Name $TaskArr[$_.Id] | Start-VM -Confirm:$false
        $TaskArr.Remove($_.Id)
  }
  }

 

0 Kudos