radio_müsli
Contributor
Contributor

Script to automate DRS-Groups Manger affinity

Jump to solution

Hello

I'm trying to automate affinity rules for drs groups manger. The idea is to let certain VMs run on

assigned esx hosts (Type "Run VMs on Hosts" / "Should run on hosts in group").

Background: We've esx hosts in two datacenters interconnected with 10Gig and a netapp metrocluster on each site hosting nfs exports. I have created two groups of type "Host DRS Groups" (esx hosts in datacenter a go into one, hosts in datacenter b go into the other) plus two groups of type "Virtual Machine DRS Groups" (VMs hosted on nfs export a go into one while VMs hosted on nfs export b go into the other). This way we can prevent nfs/iscsi traffic to run across datacenters.

Creating the rules manually in a small cluster does work. However with dozens of VMs in other clusters there must be a way to automate the task and have it run regualary. I would let powercli generate a list of VMs according to nfs export (location of .vmdk is either on site a or site b) then import this into the relevant Virtual Machine DRS Group.

Using get-drsrule -Cluster "cluster" | Export-CliXml does not show details for HOST DRS Groups or Virtual Machine DRS Groups. Is it possible to do this at all? I recon using set-drsrule would let me import the rules.

Any help / ideas is appreceated!

Regards

Sascha

0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership

The current DrsRule cmdlets do not, afaik, allow you to work with DRS groups.

As an alternative you can use the ReconfigureComputeResource method that is available.

The script would look something like this

$clusterName = "MyCluster"
$cluster = Get-Cluster -Name $clusterName

$spec = New-Object VMware.Vim.ClusterConfigSpecEx $groupVM = New-Object VMware.Vim.ClusterGroupSpec
$groupVM.operation = "add"
$groupVM
.Info = New-Object VMware.Vim.ClusterVmGroup
$groupVM.Info.Name = "Grouped VM"

Get-VM
-Name MyVM1* | %{     $groupVM.Info.VM += $_.Extensiondata.MoRef } $spec.GroupSpec += $groupVM
$groupESX
= New-Object VMware.Vim.ClusterGroupSpec
$groupESX
.operation = "add"
$groupESX.Info = New-Object VMware.Vim.ClusterHostGroup
$groupESX.Info.Name = "Grouped Host"

Get-VMHost
-Name MyHost1* | %{     $groupESX.Info.Host += $_.Extensiondata.MoRef } $spec.GroupSpec += $groupESX
$rule
= New-Object VMware.Vim.ClusterRuleSpec
$rule
.operation = "add"
$rule.info = New-Object VMware.Vim.ClusterVmHostRuleInfo
$rule.info.enabled = $true
$rule.info.name = "Test DRS group rule"
$rule.info.mandatory = $true
$rule
.info.vmGroupName = "Grouped VM"
$rule
.info.affineHostGroupName = "Group Host"
$spec.RulesSpec += $rule
$cluster.ExtensionData.ReconfigureComputeResource($spec,$true)

The script first creates 2 DRS groups. One with the VMs and one with the ESX(i) servers.

In the example it takes all guests whose name starts with "MyVM1" for one group and all hosts whose name starts with "MyHost1" for the other group.

These 2 selections will of course have to be changed for your setup. Let me know what selection rules you want to implement for the VMs and the hosts and I can update the script.

Then the script creates a rule. Notice that the rule specifies $true for the 'mandatory' property.

This means that a VM will not be powered on if its host placement doesn't correspond with the rule.

The 2nd parameter on the ReconfigureComputeResource method allows you to specify if the changes defined in the $spec are incremental or not.

If this is not $true all your cluster settings, except for the groups and the rule will be removed. So watch out!


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

View solution in original post

0 Kudos
22 Replies
LucD
Leadership
Leadership

The current DrsRule cmdlets do not, afaik, allow you to work with DRS groups.

As an alternative you can use the ReconfigureComputeResource method that is available.

The script would look something like this

$clusterName = "MyCluster"
$cluster = Get-Cluster -Name $clusterName

$spec = New-Object VMware.Vim.ClusterConfigSpecEx $groupVM = New-Object VMware.Vim.ClusterGroupSpec
$groupVM.operation = "add"
$groupVM
.Info = New-Object VMware.Vim.ClusterVmGroup
$groupVM.Info.Name = "Grouped VM"

Get-VM
-Name MyVM1* | %{     $groupVM.Info.VM += $_.Extensiondata.MoRef } $spec.GroupSpec += $groupVM
$groupESX
= New-Object VMware.Vim.ClusterGroupSpec
$groupESX
.operation = "add"
$groupESX.Info = New-Object VMware.Vim.ClusterHostGroup
$groupESX.Info.Name = "Grouped Host"

Get-VMHost
-Name MyHost1* | %{     $groupESX.Info.Host += $_.Extensiondata.MoRef } $spec.GroupSpec += $groupESX
$rule
= New-Object VMware.Vim.ClusterRuleSpec
$rule
.operation = "add"
$rule.info = New-Object VMware.Vim.ClusterVmHostRuleInfo
$rule.info.enabled = $true
$rule.info.name = "Test DRS group rule"
$rule.info.mandatory = $true
$rule
.info.vmGroupName = "Grouped VM"
$rule
.info.affineHostGroupName = "Group Host"
$spec.RulesSpec += $rule
$cluster.ExtensionData.ReconfigureComputeResource($spec,$true)

The script first creates 2 DRS groups. One with the VMs and one with the ESX(i) servers.

In the example it takes all guests whose name starts with "MyVM1" for one group and all hosts whose name starts with "MyHost1" for the other group.

These 2 selections will of course have to be changed for your setup. Let me know what selection rules you want to implement for the VMs and the hosts and I can update the script.

Then the script creates a rule. Notice that the rule specifies $true for the 'mandatory' property.

This means that a VM will not be powered on if its host placement doesn't correspond with the rule.

The 2nd parameter on the ReconfigureComputeResource method allows you to specify if the changes defined in the $spec are incremental or not.

If this is not $true all your cluster settings, except for the groups and the rule will be removed. So watch out!


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

View solution in original post

0 Kudos
radio_müsli
Contributor
Contributor

Awesome! Just what I was looking for Smiley Happy

0 Kudos
broedi
Contributor
Contributor

Hi,

great script! 🙂

Is it possible to create an example how to add additional VMs to that existing Group "Grouped VM"?

Thanks!

0 Kudos
GotMoo
Contributor
Contributor

I was also looking for a script to modify/update Virtual Machine DRS Groups in the DRS Group Manager.

I modified the script above to create this:

$vCenterServer = "vCenter.Example.com"
#authenticating and Connecting to the VC

$CurrentUserName = [Security.Principal.WindowsIdentity]::getcurrent().name
$cred = Get-Credential $CurrentUserName
Write-Output "Connecting to vCenter. Please stand by..."
Connect-VIServer -Server $vCenterServer -Credential $Cred


#Function for updating the Resource VM Groups
function updateDrsVmGroup ($clusterName,$resourcePoolName,$groupVMName){
    $cluster = Get-Cluster -Name $clusterName
    $spec = New-Object VMware.Vim.ClusterConfigSpecEx
    $groupVM = New-Object VMware.Vim.ClusterGroupSpec
    #Operation edit will replace the contents of the GroupVMName with the new contents seleced below.
    $groupVM.operation = "edit"

    $groupVM.Info = New-Object VMware.Vim.ClusterVmGroup
    $groupVM.Info.Name = $groupVMName

# Perform your VM selection here. I use resource pools per cluster to identify group members,
# but you could use any method to select your VM's.
    get-cluster $clusterName | Get-ResourcePool $resourcePoolName | get-vm | %{
        $groupVM.Info.VM += $_.Extensiondata.MoRef
    }
    $spec.GroupSpec += $groupVM

    #Apply the settings to the cluster
    $cluster.ExtensionData.ReconfigureComputeResource($spec,$true)
}

# Calling the function. I've found the group names to be case sensitive, so watch for that.
#updateDrsVmGroup ("ClusterName") ("ResourcePool Name") ("DRS VM Groupname")
updateDrsVmGroup ("Cluster_PROD") ("Group A") ("Group A VMs (Odd)")
updateDrsVmGroup ("Cluster_PROD") ("Group B") ("Group B VMs (Even)")
updateDrsVmGroup ("Cluster_STAGE") ("Group A") ("Group A VMs (Odd)")
updateDrsVmGroup ("Cluster_STAGE") ("Group B") ("Group B VMs (Even)")


Disconnect-VIServer -Confirm:$False

This script allows me to manage guest to host affinity across clusters on the same hardware (blade chassis) based on resource pool membership.  The VM group contents will be overwritten by whatever the script finds in the resource pools matching the name.

0 Kudos
JustinL3
Contributor
Contributor

LucD and GotMoo,

Thanks for posting your scripts to create and modify DRS Groups.  Work great and give me peace of mind that I'm not forgetting to seperate any of my clustered VM nodes.

J

0 Kudos
rosarre
Contributor
Contributor

Hello LucD

Do you have a similar script the will output the contents of a DRS group as well as the Virtual Machine DRS groups and the Host DRS groups

Thanks

BobR

0 Kudos
EsVau
Contributor
Contributor

Hi,

I'm want to set the Group affinity based on a cutom attribute that we are using. I tried it using get-annotation but it's not really working.

Get-Cluster $clusterName | Get-VM | Get-Annotation | where {$_.value -eq $Datacenter} | Select-Object AnnotatedEntity

With that I get the needed output, but placed in the whole script it's doesn't work.

Somebody have any Ideas?

THX, Stefan

0 Kudos
LucD
Leadership
Leadership

Could you perhaps explain in which context in your script you want to use the AnnotatedEntity property ?


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

0 Kudos
EsVau
Contributor
Contributor

Of cource, sorry.

I want to set the affinity of my VM's to DR's groups based on a custom attribute we set (Datacenter). It should work like the script of GotMoo, but instead of based on RessourcePools it should be based on the entry of the Datacenter-Attribute.

"Get-Cluster $clusterName | Get-VM | Get-Annotation | where {$_.value -eq $Datacenter} | Select-Object AnnotatedEntity" gives me the names of the VM's as output. Similar to "get-cluster $clusterName | Get-ResourcePool $resourcePoolName | get-vm" in the script of GotMoo.

I just started using PS, so maybe it's the total different way. Please don't gve to much attention on what I've done, if there is a better way I'm happy for tips.

0 Kudos
LucD
Leadership
Leadership

Try replacing that part of the script with this

    Get-Cluster $clusterName | Get-VM | 
    where {Get-Annotation -Entity $_ | where {$_.Value -eq $Datacenter}} | %{
        $groupVM.Info.VM += $_.Extensiondata.MoRef
    }
}

This will get all the VMs in the cluster and then filter, with a Where-clause, the ones that have the content of the $Datacenter variable in one of their custom attributes.


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

0 Kudos
EsVau
Contributor
Contributor

Works perfect!!! :smileylaugh:

THX very much!!!

0 Kudos
illvilja
Hot Shot
Hot Shot

Hi fellas,

Looking for a way to use Datastore Clusters in the same way you use Resource pools/annotations.

Is there a way to select the VMs stored on a specific datastore cluster? I find the get-datastorecluster to be very limited.

Basically what we want to do is to fill our two VM DRS Groups (stretched HA cluster) with the VMs filled in the resource pool named PROD.

We have two Datastore Clusters named PROD_SITE_A and PROD_SITE_B.

I can get the output of where all the VM resides, but I don't see a way to implement it into the script.

Get-ResourcePool PROD |Get-VM | Select Name, @{N="Datastore";E={Get-Datastorecluster -VM $_}}

Will give a nice output of each VM and what Datastore Cluster its stored on.

It would be so much easier if you just could get-datastorecluster PROD_SITE_A | get-vm ...

Any suggestions?

0 Kudos
LucD
Leadership
Leadership

I agree that the Datastorecluster related cmdlets should be expanded.

Let's hope the next PowerCLI release will bring that.

In the meantime, you can use this function to retrieve the VMs that are located on a Datastorecluster

function Get-VMonDsc{
  param(
  [CmdletBinding()]
  [parameter(Position = 0, ValueFromPipeline = $true)]
  [string]$Name = "*"  )

  begin{
   function Get-StoragePodInternal{
     param(
     [CmdletBinding()]
     [parameter(Mandatory = $true,ValueFromPipeline = $true)]
     [VMware.Vim.Folder]$Folder     )

     $Folder.ChildEntity | %{
       if($_.Type -eq "StoragePod"){
         Get-View -Id $_
       }       
elseif($_.Type -eq "Folder"){          Get-View -Id $_ | Get-StoragePodInternal
       }      }     }   }     process{     Get-Folder -Name datastore | %{       Get-StoragePodInternal -Folder $_.ExtensionData | where {$_.Name -like $Name} | %{         Get-VIObjectByVIView -MORef $_.ChildEntity | Get-VM
      }     }   } }
$vms = Get-VMOnDsc -Name "DSC1"

The function is based on my vSphere 5 Top 10 – Storage DRS post.


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

0 Kudos
illvilja
Hot Shot
Hot Shot

Thanks alot Luc!

It works like a charm.

0 Kudos
LucD
Leadership
Leadership

If you want to use a .ps1 file like that, you can add a param stateent at the top and call the function with that parameter.

The script becomes

param([string]$Name)

function Get-VMonDsc{
  param(
  [CmdletBinding()]
  [parameter(Position = 0, ValueFromPipeline = $true)]
  [string]$Name = "*"  )

  begin{
   function Get-StoragePodInternal{
     param(
     [CmdletBinding()]
     [parameter(Mandatory = $true,ValueFromPipeline = $true)]
     [VMware.Vim.Folder]$Folder
     )     
$Folder.ChildEntity | %{        if($_.Type -eq "StoragePod"){          Get-View -Id $_       }        elseif($_.Type -eq "Folder"){          Get-View -Id $_ | Get-StoragePodInternal
       }      }     }   }    
process{     Get-Folder -Name datastore | %{       Get-StoragePodInternal -Folder $_.ExtensionData | where {$_.Name -like $Name} | %{         Get-VIObjectByVIView -MORef $_.ChildEntity | Get-VM
      }     }   } }
Get-VMOnDsc -Name $name

Save this as a .ps1 file and the you can call it like you tried.

<ps1> -name PROD_SITE_A


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

0 Kudos
illvilja
Hot Shot
Hot Shot

Again, Thanks ALOT Luc! This also works like a charm.

Please remind me to buy you a beer at VMworldUS if you're going. Smiley Happy

0 Kudos
stacycarter
Enthusiast
Enthusiast

I need to create a script (scheduled to run at least once a day) that will check the datastore names for VMs in one or more stretched clusters and their VM DRS group membership and add them (or change them) to the correct VM DRS group based on the first several characters of a datastore name (if it starts with xyz it's at this site, if it starts with abc it's at this site).  Would also like to have it send an email with what was found and what changes were made.

Another thought would be including a check to see if someone accidentally split the disks for a VM so they're hosted on datastores at separate sites.

Since I'm still a PowerCLI noob, any assistance/feedback would be appreciated!

0 Kudos
philippeletreul
Contributor
Contributor

Hello,

 

I’m not a PowerShell expert and I need your help.

I want to write a PS Script to compare where the VM actually running (ESX host) -> may be like “Get-VM | select name, VMhost” with the DRS hosts Group (list of ESX hosts) where she should run, if it doesn’t violate the DRS Host Affinity.

I don’t know how to collect the list of ESX Host (in DRS host group) which are associated with the VM DRS Group?

Thanks a lot for your help

Philippe

0 Kudos
LucD
Leadership
Leadership

Try something like this

$clusterName = "MyCluster"

$rules = Get-DrsRule -Cluster $clusterName -Type VMHostAffinity
$rules |
Select @{N="VM";E={Get-View $_.VMIDS | Select -ExpandProperty Name}},
 
@{N="Rule Host";E={Get-View $_.AffineHostIds | Select -ExpandProperty Name}},
 
@{N="Running on";E={
   
Get-View (Get-View $_.VMIDS | %{$_.Runtime.Host}) | Select -ExpandProperty Name
  }}


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

0 Kudos