Possible Owner in Failover Cluster. Part 3

Possible Owner

  • Part 1 covers some theory and GUI configuration.
  • Part 2 focus on setting Preferred Owners with PowerShell.
  • Part 3 (this) will explain logic behind Possible Owners in PowerShell function.
  • Part 4 will cover the last step – setting anti-affinity groups.
  • Part 5 will describe reporting of current configuration in the cluster.

    Today let’s focus on logic behind Possible Owners feature in Failover clustering.

    General concept

    This is a little trickier. Before a VM can have its ‘Possible Owner’ property set, it must be on one of the Hyper-V nodes first. There can be a few scenarios – VM1 and 4 Hyper-V Nodes: Node1-4.

    • I want to reset configuration to defaults – in other words set it to Node1-4
    • I want to set configuration to Node2 and VM is on Node2
    • I want to set configuration to Node1-2 and VM is on Node2
    • I want to set configuration to Node1-2 and VM is on Node3

First, in the begin{} block I will:

  • create PSSesion to the Cluster
  • if $PossibleOwner not set, I’ll assign all current cluster nodes to it
  • If $PossibleOwner I will validate whether provided nodes are valid

Then I’ll loop through each VM. If it’s found then:

  • I’ll get it’s current configuration
  • Compare to expected and if there’s a mismatch I’ll proceed
    • Scenario 1 is the simplest – I’ll just set PossibleOwner to all nodes.
    • Scenario 2 and 3 are similar, but provided parameter $PossibleOwner will be used
    • Scenario 4 requires some additional work:
      • First, I need to reset PossibleOwner to default to allow migration
        • If the VM is turned on – I’ll use Live Migration
        • If the VM is turned off – I’ll use Quick Migration
      • After migration was successful I can finally set PossibleOwner to desired state
  • In the end I’ll return a custom object for each processed VM with 4 parameters:
    • Cluster
    • VMName
    • CurrentNode VM is
    • OwnerNode value which is the PossibleOwner we wanted to achieve
  • And finally will remove PSSession

    Full Script

    Here’s the full script:


function Set-ClusterVMPossibleOwner {
<#
.SYNOPSIS
Will configure Possible Owners for Virtual Machine running on Failover Cluster
.DESCRIPTION
Uses Invoke-Command to allow for PSCredential.
If PossibleOwner is provided first will verify if it matches cluster Owner Nodes.
If yes – will set PossibleOwner for given VMs.
If no – will abort.
If $null, or '' is provided will reset PossibleOwner to defaults for given VM. Will query Cluster for possible nodes
Before Possible Owner can be set, VM must be on given node.
First it check if VM is on given node. If not – Migrates. Then verifies again and if all is ok – will set Possible Owner.
.PARAMETER Cluster
Cluster Name
.PARAMETER Credential
Optional PSCredential used to connect to cluster
.PARAMETER VMName
Virtual Machine Name or array of names to process
.PARAMETER PossibleOwner
Possible Owner to set for given VM. If not provided, or null will set to default.
.EXAMPLE
$props = @{
Cluster = 'Cluster1'
VMName = 'VM1'
PossibleOwner = 'Node1','Node2'
}
Set-ClusterVMPossibleOwner @props -Verbose
Will set possible owners to Node1 and Node2 for VM1 on Cluster1
VERBOSE: Processing with default credentials of user {mczerniawski_admin}
VERBOSE: Provided possible owner {Node1} was found in nodes of cluster {Cluster1}
VERBOSE: Provided possible owner {Node2} was found in nodes of cluster {Cluster1}
VERBOSE: Processing VM {VM1}
VERBOSE: Testing current Possible Owner configuration for VM {VM1}
VERBOSE: Current possible owner {Node1,Node2,Node3,Node4,Node5,Node6} for VM {VM1} it not in expected state: {Node1,Node2}
VERBOSE: VM {VM1} on matching Node {Node1,Node2}. Currently on node {Node1}. Processing with Setting Possible Owner
VERBOSE: Setting Possible Owner for VM {VM1} to {Node1,Node2}
Cluster VMName CurrentNode OwnerNode
——- —— ———– ———
Cluster1 VM1 Node1 {Node1, Node2}
.EXAMPLE
$props = @{
Cluster = 'Cluster1'
VMName = 'VM1','VM2'
PossibleOwner = $null
Credential = Get-Credential
}
Set-ClusterVMPossibleOwner @props -Verbose
VERBOSE: Processing with provided credentials {constoso\mczerniawski_admin}
VERBOSE: Provided no Possible Owner. Will reset to default setting
VERBOSE: Processing VM {VM1}
VERBOSE: Testing current Possible Owner configuration for VM {VM1}
VERBOSE: Current possible owner {Node1,Node4} for VM {VM1} it not in expected state: {Node1,Node2,Node3,Node4,Node5,Node6}
VERBOSE: VM {VM1} on matching Node {Node1,Node2,Node3,Node4,Node5,Node6}. Currently on node {Node1}. Processing with Setting Possible Owner
VERBOSE: Setting Possible Owner for VM {VM1} to {Node1,Node2,Node3,Node4,Node5,Node6}
VERBOSE: Processing VM {VM2}
VERBOSE: Testing current Possible Owner configuration for VM {VM2}
VERBOSE: Current possible owner for VM {VM2} set correctly to nodes {Node1,Node2,Node3,Node4,Node5,Node6}
Cluster VMName CurrentNode OwnerNode
——- —— ———– ———
Cluster1 VM1 Node1 {Node1, Node2, Node3, Node4…}
Cluster1 VM2 Node1 {Node1, Node2, Node3, Node4…}
#>
[CmdletBinding()]
param(
[Parameter(Mandatory, HelpMessage = 'Provide Cluster Name',
ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[System.String]
$Cluster,
[Parameter(Mandatory = $false, HelpMessage = 'Provide Credentials for Cluster',
ValueFromPipeline, ValueFromPipelineByPropertyName)]
[System.Management.Automation.PSCredential]
$Credential,
[Parameter(Mandatory, HelpMessage = 'Provide VMName',
ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[System.String[]]
$VMName,
[Parameter(Mandatory = $false, HelpMessage = 'Provide Possible Owners Nodes to restrict VM to failover to',
ValueFromPipeline, ValueFromPipelineByPropertyName)]
[System.String[]]
$PossibleOwner
)
begin {
#region PSSession parameters
$connectionParams = @{
ComputerName = $Cluster
}
if ($PSBoundParameters.ContainsKey('Credential')) {
$connectionParams.Credential = $Credential
Write-Verbose Message "Processing with provided credentials {$($Credential.UserName)}"
}
else {
Write-Verbose Message "Processing with default credentials of user {$($env:USERNAME)}"
}
$ClusterSession = New-PSSession @connectionParams ErrorAction Stop
#endregion
#region Validate PossibleOwner
$nodesInCluster = Invoke-Command Session $ClusterSession ScriptBlock {
Get-ClusterNode | Select-Object ExpandProperty Name
}
#null or empty string – reset to defaults
if (-not $PossibleOwner) {
Write-Verbose Message "Provided no Possible Owner. Will reset to default setting"
$PossibleOwner = $nodesInCluster
}
#check if given hosts given in $PreferredOwner are members of $Cluster
else {
switch (Compare-Object ReferenceObject @($nodesInCluster) DifferenceObject @($PossibleOwner) IncludeEqual ) {
{$PSItem.SideIndicator -eq '=>'} {
Write-Error Message "Provided possible owner {$($PSItem.InputObject)} not found in nodes of cluster {$Cluster}. Aborting" ErrorAction Stop
break
}
{$PSItem.SideIndicator -eq '=='} {
Write-Verbose Message "Provided possible owner {$($PSItem.InputObject)} was found in nodes of cluster {$Cluster}"
}
}
}
#endregion
}
process {
foreach ($VM in $VMName) {
Write-Verbose Message "Processing VM {$VM}"
$ClusterVM = Invoke-Command Session $ClusterSession ScriptBlock {
Get-ClusterResource Name ('Virtual Machine {0}' -f $USING:VM) ErrorAction SilentlyContinue | Select-Object *
}
if (-not $ClusterVM) {
Write-Error Message "VM {$VM} not found on cluster {$Cluster}"
}
else {
#region check current configuration for PossibleOwner
Write-Verbose Message "Testing current Possible Owner configuration for VM {$VM}"
$currentPossibleOwner = Invoke-Command Session $ClusterSession ScriptBlock {
Get-ClusterResource Name ('Virtual Machine {0}' -f $USING:VM) |
Get-ClusterOwnerNode | Select-Object ExpandProperty OwnerNodes | Select-Object ExpandProperty Name
}
#region configuration matches expected state
$compareObjects = Compare-Object ReferenceObject @($PossibleOwner) DifferenceObject @($currentPossibleOwner) | Measure-Object | Select-Object ExpandProperty Count
if ($compareObjects -eq 0) {
Write-Verbose Message " Current possible owner for VM {$VM} set correctly to nodes {$($PossibleOwner -join ',')}"
}
#endregion
#endregion
else {
Write-Verbose Message " Current possible owner {$($currentPossibleOwner -join ',')} for VM {$VM} it not in expected state: {$($PossibleOwner -join ',')}"
#region check if VM is currently NOT on any of PossibleOwner nodes and migrate if necessary
if ($PossibleOwner -notcontains $ClusterVM.OwnerNode.Name) {
Write-Verbose Message " VM {$VM} is not on any of PossibleOwner nodes: {$($PossibleOwner -join ',')}. Currently on node {$($ClusterVM.OwnerNode.Name)}."
Write-Verbose Message " Clearing current possible owner for VM {$VM} to allow migration"
#region Clear current possible owner
Invoke-Command Session $ClusterSession ScriptBlock {
$ClusterNodes = Get-ClusterNode | Select-Object ExpandProperty Name
Get-ClusterResource Name ('Virtual Machine {0}' -f $USING:VM) ErrorAction SilentlyContinue |
Set-ClusterOwnerNode Owners $ClusterNodes
}
#endregion
#region VM State is Offline – quick migrate
if ($ClusterVM.State.Value -eq 'Offline') {
Write-Verbose Message " VM {$VM} is turned off. Quick Migrating."
#getting rid of any output after quick migration
[void](Invoke-Command Session $ClusterSession ScriptBlock {
Move-ClusterVirtualMachineRole Name $USING:ClusterVM.OwnerGroup.Name MigrationType Quick node ($USING:PossibleOwner)[0]
})
}
#endregion
#region VM State is Online – live migrate
elseif ($ClusterVM.State.Value -eq 'Online') {
Write-Verbose Message " VM {$VM} is turned on. Live Migrating."
#getting rid of any output after live migration
[void](Invoke-Command Session $ClusterSession ScriptBlock {
Move-ClusterVirtualMachineRole Name $USING:ClusterVM.OwnerGroup.Name MigrationType Live node ($USING:PossibleOwner)[0]
})
}
#endregion
#region test if vm is migrated on node[0] of $PossibleOwner
$testClusterVM = Invoke-Command Session $ClusterSession ScriptBlock {
Get-ClusterResource Name ('Virtual Machine {0}' -f $USING:VM) ErrorAction SilentlyContinue | Select-Object *
}
if ($PossibleOwner -contains $testClusterVM.OwnerNode.Name) {
Write-Verbose Message "VM {$VM} is on node {$($testClusterVM.OwnerNode.Name)}. Processing"
}
else {
Write-Error Message "Could not migrate VM {$VM} to matching Node {$($PossibleOwner -join ',')}. Currently on node {$($testClusterVM.OwnerNode.Name)}" ErrorAction Continue
continue
}
#endregion
}
#endregion
else {
Write-Verbose Message "VM {$VM} on matching Node {$($PossibleOwner -join ',')}. Currently on node {$($ClusterVM.OwnerNode.Name)}. Processing with Setting Possible Owner"
}
#region Set PossibleOwner
Write-Verbose Message " Setting Possible Owner for VM {$VM} to {$($PossibleOwner -join ',')}"
Invoke-Command Session $ClusterSession ScriptBlock {
Get-ClusterResource Name ('Virtual Machine {0}' -f $USING:VM) ErrorAction SilentlyContinue |
Set-ClusterOwnerNode Owners $USING:PossibleOwner
}
#endregion
}
#region LAST VALIDATION Test current status
$finalVMHVNode = Invoke-Command Session $ClusterSession ScriptBlock {
Get-ClusterResource Name ('Virtual Machine {0}' -f $USING:VM) | Select-Object ExpandProperty OwnerNode
}
$finalOwnerNode = Invoke-Command Session $ClusterSession ScriptBlock {
Get-ClusterResource Name ('Virtual Machine {0}' -f $USING:VM) |
Get-ClusterOwnerNode | Select-Object ExpandProperty OwnerNodes | Select-Object ExpandProperty Name
}
$finalCompareObjects = Compare-Object ReferenceObject @($PossibleOwner) DifferenceObject @($finalOwnerNode) | Measure-Object | Select-Object ExpandProperty Count
if ($finalCompareObjects -eq 0) {
#Write-Verbose -Message "Last Validation! VM {$VM} possible owner is set to {$($testCurrentPossibleOwner -join ',')}"
[pscustomobject]@{
Cluster = $Cluster
VMName = $VM
CurrentNode = $finalVMHVNode
OwnerNode = $finalOwnerNode
}
}
else {
Write-Error Message "Could not set Possible Owners for VM {$VM} to nodes {$($PossibleOwner -join ',')}." ErrorAction Continue
}
#endregion
}
}
}
end {
$ClusterSession | Remove-PSSession ErrorAction SilentlyContinue
}
}

view raw

Possible.ps1

hosted with ❤ by GitHub

 

And here’s the output from PowerShell Core:

  • Reset to defaults

  • Set to two nodes. VMs are on one of the nodes:

  • Set to two different nodes than VMs are on:

Summary

This one requires a little bit of more work than before. Mainly because involves moving VMs between nodes. One last to go and we’ll get into reporting 🙂

One thought on “Possible Owner in Failover Cluster. Part 3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s