Last active
December 15, 2023 12:33
-
-
Save alainassaf/0e73ce6e55412b7268edbe552a58f883 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Reviews and evenly distributes VDA's within a Citrix Hypervisor pool. | |
.DESCRIPTION | |
Reviews and evenly distributes VDA's within a Citrix Hypervisor pool. | |
.PARAMETER XenServer_poolmaster | |
Mandatory string parameter. IP of the Citrix Hypervisor pool master to optimize. | |
.PARAMETER XenServer_networkname | |
Mandatory string parameter. Network name of the Citrix Hpervisor pool's management network | |
.PARAMETER XenServer_SRName | |
Mandatory string parameter. Storage Repository name that stores the vDisks of the Citrix Hpervisor pool | |
.EXAMPLE | |
optimize-hypervisorvdas.ps1 -xenserver_poolmaster "127.0.0.1" -xenserver_networkname "HypervisorMGMTNet" -XenServer_SRName "HypervisorSR" -verbose | |
Counts the total VMs in the 127.0.0.1 Hypervisor pool and evently distributes them with additional console feedback. | |
.INPUTS | |
None | |
.OUTPUTS | |
None | |
.NOTES | |
NAME: optimize-hypervisorvdas.ps1 | |
VERSION: 1.0.2 | |
CHANGE LOG - Version - When - What - Who | |
0.0.1 - 12/06/2022 - Initial script - Alain Assaf | |
0.0.2 - 12/22/2022 - Added hypervisor connection/disconnection functions - Alain Assaf | |
0.0.3 - 12/28/2022 - Added new functions get-hypNetwork & get-hypSR - Alain Assaf | |
0.0.4 - 12/28/2022 - Added params XenServer_networkname & XenServer_SRName - Alain Assaf | |
0.0.5 - 12/29/2022 - Added change to get-ctxHypVMTable function - Alain Assaf | |
0.0.6 - 12/30/2022 - Added additional functions related to migration - Alain Assaf | |
0.0.7 - 2/14/2023 - Added new link. Updates function calls for new params - Alain Assaf | |
0.0.8 - 2/23/2023 - Make sure all existing XenServer connected are stopped before script starts - Alain Assaf | |
1.0.0 - 3/06/2023 - Removed commented lines and converted write-host to write-verbose - Alain Assaf | |
1.0.1 - 3/06/2023 - Updated Requries statement - Alain Assaf | |
1.0.2 - 3/07/2023 - Updated help - Alain Assaf | |
AUTHOR: Alain Assaf | |
LASTEDIT: March 07, 2023 | |
.LINK | |
https://jordantheitguy.com/2019/06/24/how-to-powershell-progress-bars/ | |
https://discussions.citrix.com/topic/415099-invoke-xenvm-xenaction-migratesend/page/2/ | |
https://discussions.citrix.com/topic/399153-vm-migration-to-homeserver-via-powershell/ | |
https://devblogs.microsoft.com/scripting/rounding-numberspowershell-style/ | |
http: //www.linkedin.com/in/alainassaf/ | |
#> | |
[CmdletBinding()] | |
Param( | |
[parameter(Mandatory)] | |
[string]$XenServer_poolmaster, | |
[parameter(Mandatory)] | |
[string]$XenServer_networkname, | |
[parameter(Mandatory)] | |
[string]$XenServer_SRName | |
) | |
#region functions | |
function get-scriptCredential { | |
param ( | |
[Parameter(Mandatory = $true)] | |
[String]$userName, | |
[Parameter(Mandatory = $true)] | |
[String]$PassPath | |
) | |
# If Password file does not exist, create it | |
if ((Test-Path -Path $PassPath) -eq $False) { | |
(Get-Credential -Message "ENTER CREDENTIALS" -UserName $userName).Password | ConvertFrom-SecureString | Out-File $PassPath | |
} | |
$cred_password = Get-Content $PassPath | ConvertTo-SecureString | |
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName, $cred_password | |
Return $credential | |
} | |
Function Get-MySnapin { | |
<# | |
.SYNOPSIS | |
Checks for a PowerShell Snapin(s) and imports it if available, otherwise it will display a warning and exit. | |
.DESCRIPTION | |
Checks for a PowerShell Snapin(s) and imports it if available, otherwise it will display a warning and exit. | |
.PARAMETER snapins | |
Required parameter. List of PSSnapins separated by commas. | |
.INPUTS | |
None | |
.OUTPUTS | |
None | |
.EXAMPLE | |
PS> get-MySnapin PSSNAPIN | |
Checks system for installed PSSNAPIN and imports it if available. | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
https://github.com/alainassaf/get-mysnapin | |
.NOTES | |
NAME : Get-MySnapin | |
VERSION : 1.01 | |
CHANGE LOG - Version - When - What - Who | |
1.00 - 02/13/2017 - Initial script - Alain Assaf | |
1.01 - 11/7/2017 - Added try/catch - Alain Assaf | |
LAST UPDATED: 11/7/2017 | |
AUTHOR : Alain Assaf | |
#> | |
Param([string]$snapins) | |
$ErrorActionPreference = 'silentlycontinue' | |
foreach ($snap in $snapins.Split(",")) { | |
if (-not(Get-PSSnapin -Name $snap)) { | |
if (Get-PSSnapin -Registered | Where-Object { $_.name -like $snap }) { | |
try { | |
Add-PSSnapin -Name $snap | |
$true | |
} catch { | |
Write-Warning "$snap PowerShell Cmdlet not available." | |
Write-Warning "Please run this script from a system with the $snap PowerShell Cmdlet installed." | |
exit 1 | |
} | |
} | |
} | |
} | |
} | |
function connect-ctxhyp { | |
<# | |
.SYNOPSIS | |
Connects to Citrix Hypervisor. Returns object with XenServer Connection | |
.PARAMETER poolMaster | |
Required string parameter. IP of the Hypervisor poolmaster. | |
.PARAMETER hypCreds | |
Required PSCredential parameter. Username and password PSCredentail object to authenticate to the Hypervisor. | |
.INPUTS | |
None | |
.OUTPUTS | |
XenAPI.Session | |
.EXAMPLE | |
PS> connect-ctxhyp -poolMaster "192.168.25.1" -hypCreds $credentials | |
Connects Citrix Hypervisor at 192.168.25.1 using $credentials for authentication. | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : connect-ctxhyp | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 12/22/2022 - Initial script - Alain Assaf | |
LAST UPDATED: 12/22/2022 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
param ( | |
[Parameter(Mandatory)] | |
[string]$poolMaster, | |
[Parameter(Mandatory)] | |
[System.Management.Automation.PSCredential]$hypCreds | |
) | |
Try { | |
Connect-XenServer -Server $poolMaster -Creds $hypCreds -NoWarnNewCertificates -SetDefaultSession | |
$hypSession = Get-XenSession | |
return $hypSession | |
} Catch [XenAPI.Failure] { | |
Write-Warning "Failed to connect to [$poolMaster]" | |
Exit 1 | |
} | |
} | |
function disconnect-ctxhyp { | |
<# | |
.SYNOPSIS | |
Disconnect all open Citrix Hypervisor sessions. | |
.INPUTS | |
None | |
.OUTPUTS | |
None | |
.EXAMPLE | |
PS> disconnect-ctxhyp | |
Disconnects and closes all open Citrix Hyperivsor sessions | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : disconnect-ctxhyp | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 12/22/2022 - Initial script - Alain Assaf | |
LAST UPDATED: 12/22/2022 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
param () | |
try { | |
Get-XenSession | Disconnect-XenServer | Out-Null | |
} catch { | |
Write-Warning "Already disconnected from XenServer" | |
} | |
} | |
function get-ctxHypVMTable { | |
<# | |
.SYNOPSIS | |
Generates a table with the hypervisor host, uuid, and VM Count. | |
.INPUTS | |
XenAPI.Session | |
.OUTPUTS | |
PSCustomObject | |
.EXAMPLE | |
PS> get-ctxHypVMTable | |
Generates a table with the hypervisor host, uuid, and VM Count for the current Citrix hypervisor session | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : get-ctxHypVMTable | |
VERSION : 1.0.2 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 12/22/2022 - Initial script - Alain Assaf | |
1.0.1 - 12/29/2022 - Added write-progress to show progress - Alain Assaf | |
1.0.2 - 2/14/2023 - Added Session param - Alain Assaf | |
LAST UPDATED: 12/29/2022 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter()] | |
[XenAPI.Session]$hypSession | |
) | |
$hypTable = @() | |
[int]$hypCount = 1 | |
$hypHosts = Get-XenHost -SessionOpaqueRef $hypSession.opaque_ref | Sort-Object name_label | |
foreach ($hypervisor in $hypHosts) { | |
Write-Progress -Activity "Creating VM Table" -Status "Examing Host - $($hypervisor.Name_label) $hypCount of $($hypHosts.Count)" | |
$row1 = $hypervisor.name_label | |
$row2 = $hypervisor.uuid | |
$row3 = ($hypervisor | Select-Object -ExpandProperty resident_VMs | ForEach-Object { (Get-XenVM -opaque_ref $_) | Where-Object { $_.is_control_domain -eq $false } }).count | |
$hypTable += [PSCustomObject]@{HypervisorName = $row1; UUID = $row2; VMCount = $row3 } | |
$hypCount++ | |
} | |
return $hypTable | |
} | |
function get-ctxHypSource { | |
<# | |
.SYNOPSIS | |
Returns a table of hypervisor hosts with more than the 'ideal' number of VMs. | |
.INPUTS | |
PSCustomObject | |
.OUTPUTS | |
PSCustomObject | |
.EXAMPLE | |
PS> get-ctxHypSource -idealNumberOfVMs 8 -hypTable $HyperVisorTable | |
Returns a subset of items from $HypervisorTable with a VM count higher than 8 | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : get-ctxHypSource | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 12/29/2022 - Initial script - Alain Assaf | |
LAST UPDATED: 12/29/2022 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
[Int]$idealNumberOfVMs, | |
[Parameter(Mandatory)] | |
[PSCustomObject]$hypTable | |
) | |
$hypSourceTable = @() | |
[int]$hypCount = 1 | |
foreach ($hypervisor in $hypTable) { | |
Write-Progress -Activity "Creating Hypervisor Source Table" -Status "Examing Host - $($hypervisor.Name_label) $hypCount of $($hypTable.Count)" | |
if ($hypervisor.VMCount -gt $idealNumberOfVMs) { | |
$row1 = $hypervisor.HypervisorName | |
$row2 = $hypervisor.uuid | |
$row3 = $hypervisor.VMcount | |
$hypSourceTable += [PSCustomObject]@{HypervisorName = $row1; UUID = $row2; VMCount = $row3 } | |
$hypCount++ | |
} | |
} | |
return $hypSourceTable | |
} | |
function get-ctxHypDest { | |
<# | |
.SYNOPSIS | |
Returns a table of hypervisor hosts with less than the 'ideal' number of VMs. | |
.INPUTS | |
PSCustomObject | |
.OUTPUTS | |
PSCustomObject | |
.EXAMPLE | |
PS> get-ctxHypDest -idealNumberOfVMs 8 -hypTable $HyperVisorTable | |
Returns a subset of items from $HypervisorTable with a VM count less than 8 | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : get-ctxHypDest | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 12/29/2022 - Initial script - Alain Assaf | |
LAST UPDATED: 12/29/2022 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
[Int]$idealNumberOfVMs, | |
[Parameter(Mandatory)] | |
[PSCustomObject]$hypTable | |
) | |
$hypDestTable = @() | |
[int]$hypCount = 1 | |
foreach ($hypervisor in $hypTable) { | |
Write-Progress -Activity "Creating Hypervisor Destination Table" -Status "Examing Host - $($hypervisor.Name_label) $hypCount of $($hypTable.Count)" | |
if ($hypervisor.VMCount -lt $idealNumberOfVMs) { | |
$row1 = $hypervisor.HypervisorName | |
$row2 = $hypervisor.uuid | |
$row3 = $hypervisor.VMcount | |
$hypDestTable += [PSCustomObject]@{HypervisorName = $row1; UUID = $row2; VMCount = $row3 } | |
$hypCount++ | |
} | |
} | |
return $hypDestTable | |
} | |
function get-hypHost { | |
<# | |
.SYNOPSIS | |
Returns a hypervisor host object | |
.INPUTS | |
None | |
.OUTPUTS | |
XenAPI.Host | |
.EXAMPLE | |
PS> get-hypHost -hostUUID "b671f78b-db28-4902-ad07-062c368fd8c2" | |
Retrurns XenAPI.Host object with uuid b671f78b-db28-4902-ad07-062c368fd8c2 | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : get-hypHost | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 12/23/2022 - Initial script - Alain Assaf | |
LAST UPDATED: 12/23/2022 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
$hostUUID | |
) | |
try { | |
$hypHost = Get-XenHost -Uuid $hostUUID | |
Return $hypHost | |
} catch { | |
Write-Warning "Could not find Hypervisor with uuid: [$hostUUID]" | |
Exit 1 | |
} | |
} | |
function get-hypNetwork { | |
<# | |
.SYNOPSIS | |
Returns a hypervisor network | |
.INPUTS | |
None | |
.OUTPUTS | |
XenAPI.Network | |
.EXAMPLE | |
PS> get-hypNetwork -xenNetName "PRODUCTION" | |
Retrurns XenAPI.Network object with name_label'"PRODUCTION' | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : get-hypNetwork | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 12/28/2022 - Initial script - Alain Assaf | |
LAST UPDATED: 12/28/2022 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
[String]$xenNetName | |
) | |
try { | |
$hypNetwork = Get-XenNetwork -name_label $xenNetName | |
Return $hypNetwork | |
} catch { | |
Write-Warning "Could not find Hypervisor network named: [$xenNetName]" | |
Write-Warning "Closing Citrix Hypervisor connection" | |
disconnect-ctxhyp | |
Exit 1 | |
} | |
} | |
function get-hypSR { | |
<# | |
.SYNOPSIS | |
Returns a hypervisor Storage Repository | |
.INPUTS | |
None | |
.OUTPUTS | |
XenAPI.Storage | |
.EXAMPLE | |
PS> get-hypSR xenSRName "VDA-SR" | |
Retrurns XenAPI.Storage object with name_label 'VDA-SR' | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : get-hypSR | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 12/28/2022 - Initial script - Alain Assaf | |
LAST UPDATED: 12/28/2022 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
[String]$xenSRName | |
) | |
try { | |
$hypSR = Get-XenSR -name_label $xenSRName | |
Return $hypSR | |
} catch { | |
Write-Warning "Could not find Hypervisor Storage Repository named: [$xenSRName]" | |
Write-Warning "Closing Citrix Hypervisor connection" | |
disconnect-ctxhyp | |
Exit 1 | |
} | |
} | |
function get-vmToMigrate { | |
<# | |
.SYNOPSIS | |
Returns the first VM object from a hypervisor with its disks. This VM will be migrated off the hypervisor host. | |
.PARAMETER hypTableRow | |
Mandatory PSCustomObject of a hypTable row | |
.PARAMETER vmSR | |
Mandatory Storage Repository where migration VM disks reside | |
.INPUTS | |
PSCustomObject | |
.OUTPUTS | |
PSCustomObject | |
.EXAMPLE | |
PS> get-vmToMigrateName -hypTableRow $HypTableRow -vmSR $vmSR | |
Queries hypervisor for VMs and returns first 1 to migrate with the VM's disks | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
.NOTES | |
NAME : get-vmToMigrateName | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 1/26/2023 - Initial script - Alain Assaf | |
LAST UPDATED: 1/26/2023 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
[PSCustomObject]$hypTableRow, | |
[Parameter(Mandatory)] | |
$vmSR | |
) | |
$migrateHostVM = (Get-XenHost -Uuid $hypTableRow.uuid) | Select-Object -ExpandProperty resident_VMs | Select-Object -First 1 | |
$VMtoMove = Get-XenVM -opaque_ref $migrateHostVM | |
##VDI's should be resolved from $tmpVM.VBDs https://discussions.citrix.com/topic/415099-invoke-xenvm-xenaction-migratesend/page/2/ | |
$vdirefs = @() | |
foreach ($itemvbd in $VMtoMove.VBDs) { | |
$vbdvm = Get-XenVBD -opaque_ref $itemvbd.opaque_ref | |
if ($vbdvm.type -eq "Disk") { | |
$vdirefs += Get-XenVDI -opaque_ref $vbdvm.VDI.opaque_ref | ConvertTo-XenRef | |
} | |
} | |
#Map disks to $vdimap to use in migrate tasks | |
$vdimap = @{} | |
foreach ($vdiref in $vdirefs) { | |
$vdimap.Add([XenAPI.XenRef[XenAPI.VDI]]$vdiref, [XenAPI.XenRef[XenAPI.SR]]$vmSR); | |
} | |
$migrateVM = [PSCustomObject]@{vmToMigrate = $VMtoMove; vdiMap = $vdimap } | |
Return $migrateVM | |
} | |
function get-destHyp { | |
<# | |
.SYNOPSIS | |
Gets destination host to migrate a VM to | |
.PARAMETER hypTableRow | |
Mandatory PSCustomObject of a hypTable row | |
.PARAMETER hypNet | |
Mandatory [XenAPI.Network] that VM resides on | |
.INPUTS | |
PSCustomObject,XenAPI.Network | |
.OUTPUTS | |
PSCustomObject | |
.EXAMPLE | |
PS> get-destHyp -hypTableRow $HypTableRow -hypNet $HypervisorNetwork | |
Returns a XenAPI.VM of the destination hypervisor | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
https://discussions.citrix.com/topic/415099-invoke-xenvm-xenaction-migratesend/page/2/ | |
.NOTES | |
NAME : get-destHyp | |
VERSION : 1.0.0 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 1/27/2023 - Initial script - Alain Assaf | |
LAST UPDATED: 1/27/2023 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
[PSCustomObject]$hypTableRow, | |
[Parameter(Mandatory)] | |
[XenAPI.Network]$hypNet | |
) | |
$desthost = Get-hypHost -hostUUID $hypTableRow.uuid | |
$VMdest = Invoke-XenHost -XenHost $desthost -XenAction migratereceive -network $hypNet -PassThru | |
return $VMdest | |
} | |
function invoke-vmMigration { | |
<# | |
.SYNOPSIS | |
Initates VM Migration | |
.PARAMETER VMtoMigrate | |
Mandatory XenAPI.VM parameter of VM to migrate | |
.PARAMETER DestinationHost | |
Mandatory object of the destination Hypervisor | |
.PARAMETER VMVIFMap | |
Mandatory object array of Migration VM's vifs | |
.PARAMETER VMVDIMap | |
Mandatory object array of Migration VM's disks | |
.PARAMETER hpySession | |
Mandatory XenAPI.Session object of the XenServer session where the migration will occur | |
.INPUTS | |
PSCustomObject,XenAPI.Network | |
.OUTPUTS | |
PSCustomObject | |
.EXAMPLE | |
PS> invoke-vmMigration -hypTableRow $HypTableRow -hypNet $HypervisorNetwork | |
Starts migratio of | |
.LINK | |
http://www.linkedin.com/in/alainassaf/ | |
https://discussions.citrix.com/topic/415099-invoke-xenvm-xenaction-migratesend/page/2/ | |
.NOTES | |
NAME : get-destHyp | |
VERSION : 1.0.2 | |
CHANGE LOG - Version - When - What - Who | |
1.0.0 - 1/27/2023 - Initial script - Alain Assaf | |
1.0.1 - 1/30/2023 - Added additional parameters - Alain Assaf | |
1.0.2 - 2/14/2023 - Added VMVIFMap param - Alain Assaf | |
LAST UPDATED: 2/14/2023 | |
AUTHOR : Alain Assaf | |
#> | |
#Requires -Version 4 | |
#Requires -Module XenServerPSModule | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory)] | |
[XenAPI.VM]$VMtoMigrate, | |
[Parameter(Mandatory)] | |
$DestinationHost, | |
[Parameter(Mandatory)] | |
[AllowNull()] | |
$VMVIFMap, | |
[Parameter(Mandatory)] | |
$VMVDIMap, | |
[Parameter(Mandatory)] | |
[XenAPI.Session]$hpySession | |
) | |
$xenMotionTask = Invoke-XenVM $VMtoMigrate -XenAction migratesend -live $true -dest $DestinationHost -VifMap $VMVIFMap -VdiMap $VMVDIMap -SessionOpaqueRef $hypSession.opaque_ref -BestEffort -Async -PassThru | |
return $xenMotionTask | |
} | |
#endregion | |
#region variables | |
$datetime = Get-Date -Format "MM-dd-yyyy_HH-mm" | |
$Domain = (Get-ChildItem env:USERDNSDOMAIN).value | |
$ScriptRunner = (Get-ChildItem env:username).value | |
$compname = (Get-ChildItem env:COMPUTERNAME).value | |
$scriptName = $MyInvocation.MyCommand.Name | |
$scriptpath = $MyInvocation.MyCommand.Path | |
#$currentDir = Split-Path $MyInvocation.MyCommand.Path | |
#endregion | |
#region modules | |
# Import the Citrix Broker PowerShell module | |
Get-MySnapin Citrix.Broker.Admin.V2 | |
# Import the XenServer Module | |
Get-MySnapin XenServerPowerShell | |
#endregion | |
#Remove any existing XenServer sessions | |
disconnect-ctxhyp | |
#Get XenServer Credential | |
Write-Verbose "Getting XenServer Credentials (for root user)..." | |
$XenServer_credential = get-scriptCredential -userName "root" -passpath "c:\temp\XenServer_pool.pwd" | |
#Connect to XenServer | |
$session = connect-ctxhyp -poolMaster "$XenServer_poolmaster" -hypCreds $XenServer_credential | |
#Get Pool Friendly name. Populate to match your environment | |
switch ($XenServer_poolmaster) { | |
"127.0.0.1" { $xsn = "XS-Pool1"; break } | |
"127.0.0.10" { $xsn = "XS-Pool2"; break } | |
"127.0.0.20" { $xsn = "XS-Pool3"; break } | |
"127.0.0.30" { $xsn = "XS-Pool4"; break } | |
default { "UNKNOWN XENSERVER"; break } | |
} | |
#This network will be used for VM migration | |
$xennet = get-hypNetwork -xenNetName $XenServer_networkname | |
if ($null -eq $xennet) { | |
Write-Warning "XenServer network [$XenServer_networkname] was not correct. Try another network name." | |
disconnect-ctxhyp | |
Write-Warning "Exiting script" | |
exit 1 | |
} | |
# Get Storage Repository to migrate VM to (usually the same if in the same pool) | |
$sr2ref = get-hypSR -xenSRName $XenServer_SRName | ConvertTo-XenRef | |
if ($null -eq $sr2ref) { | |
Write-Warning "XenServer Storage Repository [$XenServer_SRName] was not correct. Try another SR name." | |
disconnect-ctxhyp | |
Write-Warning "Exiting script" | |
exit 1 | |
} | |
#region main | |
$hostsToBalance = get-ctxHypVMTable -hypSession $session | |
$totalHosts = $hostsToBalance.count | |
$vms = Get-XenVM | Where-Object { $_.is_a_snapshot -eq $false -and $_.is_a_template -eq $false -and $_.is_control_domain -eq $false } | |
$vdas = $vms | Where-Object { $_.is_a_snapshot -eq $false -and $_.is_a_template -eq $false -and $_.is_control_domain -eq $false } | Select-Object name_label | Sort-Object name_label | |
$totalVms = $vdas.count | |
$optimalVMCount = [math]::Round($totalVms / $totalHosts) | |
Write-Host "Optimizing XS Pool: [$xsn]" | |
Write-Host "Number of hosts: [$totalHosts]" | |
Write-Host "Number of virtual machines: [$totalVms]" | |
Write-Host "Optimal VMs to Host count: [$optimalVMCount]" | |
# Get set of hypervisors with fewer and more than $optimalVMCount | |
$DestinationHypervisors = get-ctxHypDest -idealNumberOfVMs $optimalVMCount -hypTable $hostsToBalance | |
$SourceHypervisors = get-ctxHypSource -idealNumberOfVMs $optimalVMCount -hypTable $hostsToBalance | |
# Balance VMs | |
foreach ($srchyp in $SourceHypervisors) { | |
while ($srchyp.VMCount -gt $optimalVMCount) { | |
foreach ($desthyp in $DestinationHypervisors) { | |
if ($desthyp.VMCount -lt $optimalVMCount) { | |
$srcVM = get-vmToMigrate -hypTableRow $srchyp -vmSR $sr2ref | |
[string]$tmpName = $srcvm.vmToMigrate.name_label | |
$destHypHost = get-destHyp -hypTableRow $desthyp -hypNet $xennet | |
Write-Verbose "Migrating [$tmpName]" | |
$migrateTask = invoke-vmMigration -VMtoMigrate $srcVM.vmToMigrate -DestinationHost $destHypHost -VMVIFMap $vifmap -VMVDIMap $srcVM.vdiMap -hpySession $session | |
Wait-XenTask -Task $migrateTask -ShowProgress | |
$desthyp.VMCount++ | |
$srchyp.VMCount-- | |
Write-Verbose "---------------------------------------" | |
Write-Verbose "Source Host" | |
$srchyp | Select-Object -Property * | |
Write-Verbose "Destination Host" | |
$desthyp | Select-Object -Property * | |
Write-Verbose "---------------------------------------" | |
break | |
} | |
} | |
} | |
} | |
#endregion | |
#Disconnect from XenServer | |
disconnect-ctxhyp | |
#End script info | |
Write-Verbose "SCRIPT NAME: $scriptName" | |
Write-Verbose "SCRIPT PATH: $scriptPath" | |
Write-Verbose "SCRIPT RUNTIME: $datetime" | |
Write-Verbose "SCRIPT USER: $ScriptRunner" | |
Write-Verbose "SCRIPT SYSTEM: $compname.$domain" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment