Skip to main content
Tag Based Affinity Rules
  1. Posts/

Tag Based Affinity Rules

·6 mins·

vROPS has a feature called Business Intent, which allows us to control the placement of virtual machines through the use of vSphere tags. Business intent allows us to control the placement of machines on a cluster basis or a host basis, allowing you to optimize the performance and placement of vms in accordance with licensing constraints, performance requirements, replication standards, etc. One downside of this feature is that vROPS does not currently allow you to control both cluster and host based placement at the same time. Today I’ll present a way that allows you to leverage cluster based business intent, with a secondary method of controlling host placement within the target clusters.

OVERVIEW
#

There are two modes for vROPS Business Intent: Cluster Based and Host Based. Cluster based business intent causes vROPS to look at vSphere tags applied to clusters and compares them to tags applied to virtual machines. vROPS will then identify violations of these rules and allow you to remediate them manually, on a schedule or automatically. You are able to build intent rules consuming multiple vSphere tag catagories and tags. Here’s an example of what it looks like when configured with an intent violation highlight.

Intent Violation

Today, we will be focusing on how to leverage host based intent. We will cover cluster based intent and the potential options for mixing the two in a future post. When using host based intent, vROPS will control placement of VMs within clusters using affinity rules, vROPS is currently only able to consume a single vSphere tag category, and each object can only have a single tag within that category. This can make it a bit limiting, as you are unable to create a scenario of declaring that host A can run windows and MSSQL servers, and host B can run windows servers such as the following scenario

Can’t Do this

If you do try to assign multiple tags to an object - you’ll get a big NOPE from vROPS when you try to configure the rules!

NOPE

Depening on your requirements you can perform via an approach that is less explicit in the way you declare your desired placement intentions. You can read more about that here.

If that method does not meet your needs, and you don’t feel like waiting for a feature request to fix this, you can utilize a script (provided below) to configure host base affinity rules using multiple tags and categories. Be warned, the script provided below doesn’t check if your tagged rules would create an affinity/anti-affinity conflict and cause DRS issues.

Yeah!

This method has the downside of pulling the management of the business intent out of vROPS hands from a host base intent perspective, but it allows you to configure cluster based business intent to understand where violations are occuring. We will cover in a future post how you can blend this script with cluster based business intent.

Before using this script, besure to vet it and perform proper testing.

param (
[Parameter(Mandatory=$true)][string[]]$vcenterlist,
[Parameter(Mandatory=$true)][string]$vCUsername,
[Parameter(Mandatory=$true)][string]$vcPassword,
[Parameter(Mandatory=$true)][string[]]$tagcategories
)
#$cred = Get-Credential
#$vcenterlist = "lab-comp01-vcenter.int.sentania.net"
#$tagcategories = "businessIntent"

###CYCLE THROUGH EACH vCENTER
foreach ($vcenter in $vcenterlist)
{
  $vCConn = Connect-VIServer -Server $vcenter -User $vCUsername -Password $vcPassword
  Write-host "Processing DRS Rules via tags for: $vcenter..."

  ####CYCLE THROUGH EACH TAG CATEGORY
  foreach ($tagCategory in $tagcategories)
  {
    Write-host "Processing tag category: $tagcategory..."
    $tagcategoryObj = Get-TagCategory -Name $tagCategory -server $vCConn
    $tagcategoryObj

    ####cycle through each tag in the category
    $tags = get-tag -Category $tagcategoryObj
    foreach ($tag in $tags)
    {
       Write-host "Processing tag: $tag" -ForegroundColor Yellow
        ###cycle through each cluster for every tag
       $clusters = get-cluster -Server $vCConn
       foreach ($cluster in $clusters)
       {
            Write-host "Processing Cluster: $cluster..."
            

            #get all hosts that have this tag
            $vmhosts = $cluster | get-vmhost -Tag $tag
            $vms = $cluster | get-vm -Tag $tag
            $vmgroupName = "$cluster.affinitygroup.vm."+ $tag.category +"." + $tag.name
            $hostgroupName = "$cluster.affinitygroup.host."+ $tag.category +"." + $tag.name
            $rulename = "$cluster.affinityRule."+ $tag.category +"." + $tag.name
            if (($vms.count -gt 0) -and ($vmhosts.count -gt 0))
            {
                ###add hosts to host group
                if (($hostgroup = Get-DrsClusterGroup -name $hostgroupName -ea SilentlyContinue))
                {
                    Write-host -ForegroundColor Green "Host Group Exists, updating if required..."
                    ###EVALUTE GROUP FOR REMOVED MEMBERS
                    $currentHostMembers = $hostgroupName.member
                    foreach ( $thishost in $currentHostMembers)
                    {
                        $hostMembersToRemove = @()
                        if ($vmhosts -notcontains $thishost) 
                        {
                            
                            $hostMembersToRemove += $thishost
                        }
                    }
                    if ($hostMembersToRemove.length -gt 0)
                    {
                        Write-host -ForegroundColor Red "Hosts have been removed from the group, updating...."   
                        $hostGroup = Set-DrsClusterGroup -DrsClusterGroup $vmgroupName -VMHost $hostMembersToRemove -Remove
                    }
                    ###EVALUTE GROUP FOR MISSING MEMBERS
                    $currentHostMembers = $hostgroup.member
                    foreach ( $thishost in $vmhosts)
                    {
                        $hostMembersToAdd = @()
                        if ($currentHostMembers -notcontains $thishost) 
                        {
                            
                            $hostMembersToAdd += $thishost
                        }
                    }
                    if ($hostMembersToAdd.Length -gt 0)
                    {
                        Write-host -ForegroundColor Red "Hosts are missing from the group, updating...." 
                        $hostGroup = set-DrsClusterGroup -DrsClusterGroup $hostgroup -VMHost $hostMembersToAdd -Add -Confirm:$false
                    }
                    else
                    {
                        Write-host -ForegroundColor Green "Host group membership is up to date"
                    }
                }
                else
                {
                    Write-host -ForegroundColor DarkYellow "Host Group does not exist, creating..."
                    $hostGroup = New-DrsClusterGroup -Name $hostgroupName -VMHost $vmhosts -Cluster $cluster -Confirm:$false
                }
                    
                if (($vmGroup = Get-DrsClusterGroup -name $vmgroupName  -ea SilentlyContinue))
                {
                    Write-host -ForegroundColor Green "VM Group Exists, updating if required..."
                    ####EVALUATE FOR REMOVED MEMBERS 
                    $currentVMMembers = $vmGroup.member
                    foreach ( $vm in $currentVMMembers)
                    {
                        $vmMembersToRemove = @()
                        if ($vms -notcontains $vm) 
                        {
                            $vmMembersToRemove += $vm
                        }
                    }
                    if ($vmMembersToRemove.Length -gt 0)
                    {
                        $vmGroup = Set-DrsClusterGroup -DrsClusterGroup $vmgroupName -vm $vmMembersToRemove -Remove -Confirm:$false
                    }
                    ####EVALUATE FOR MISSING MEMBERS 
                    $currentVMMembers = $vmGroup.member
                    foreach ( $vm in $vms)
                    {
                        $vmMembersToAdd = @()
                        if ($currentVMMembers -notcontains $vm) 
                        {
                            $vmMembersToAdd += $vm
                        }
                    }
                    if ($vmMembersToAdd.Length -gt 0)
                    {
                        $vmGroup = Set-DrsClusterGroup -DrsClusterGroup $vmgroupName -vm $vmMembersToRemove -Add -Confirm:$false
                    }
                    else
                    {
                        Write-host -ForegroundColor Green "VM group membership is up to date"
                    }

                }
                else
                {
                    Write-host -ForegroundColor DarkYellow "VM Group does not exist, creating..."
                    $vmGroup = new-DrsClusterGroup -Name $vmgroupName -VM $vms -Cluster $cluster -Confirm:$false
                }
                
                if (($vmhostRule = Get-DrsVMHostRule -Name $rulename -Cluster $clusters -ea SilentlyContinue))
                {
                    Write-Host -ForegroundColor DarkGray "VM Host rule exists - group updates will automatically cascade..."
                }
                else
                {                   
                    Write-Host -ForegroundColor DarkGray "VM Host RUle does not exist, updating"    
                    $vmhostrule = New-DrsVMHostRule -Name $rulename -Enabled $true -VMGroup $vmGroup -VMHostGroup $hostGroup -Cluster $cluster -Type ShouldRunOn -Confirm:$false
                }
            }
            else 
            {
                ###REMOVE GROUPS NO LONGER TAGGED 
                Write-host "REMOVING UNUSED RULE and GROUPS....."
                if ($vmGroup = Get-DrsClusterGroup -name $vmgroupName -Cluster $cluster -ErrorAction SilentlyContinue)
                {
                    Write-host "REMOVING RULE: $vmgroup"
                    Write-host ""
                    Remove-DrsClusterGroup -DrsClusterGroup $vmGroup -Confirm:$false
                }
                if ($hostGroup = Get-DrsClusterGroup -name $hostgroupName -Cluster $cluster -ErrorAction SilentlyContinue)
                {
                    Write-host "REMOVING RULE: $hostgroup"
                    Write-host ""
                    Remove-DrsClusterGroup -DrsClusterGroup $hostgroup -Confirm:$false
                }
                if ($drsVMHostRule = Get-DrsVMHostRule -name $rulename -Cluster $cluster -ErrorAction SilentlyContinue)
                {
                    Write-host "REMOVING RULE: $drsvmhostrule"
                    Write-host ""
                    Remove-DrsVMHostRule -RunAsync $drsVMHostRule -Confirm:$false
                }

            }
        }
    }
  }

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

Related

Day Two Automation Self Service Framework – Part 1

In my years on the administration and engineering side of the VMware world a task I was frequently responsible for was creating policies around the standards of my environment and then defining a process to ensure those policies were adhered to. Some examples of the policies I authored was things around snapshot lifetime, security hardening or datastore freespace, etc. Often times these standards conflicted with the pressures I faced to complete requests in a timely fashion on the requests faced by my team and I. When faced with conflicting pressures like this you can chose to not enforce your established polices, which can have significant negative impacts when your team is audited or you suffer a negative impact to your environment as the result of not enforcing your established policies; or you can choose to not deliver the requested service in a timely fashion, which will likely have equally bad outcomes. In this multi-part blog series we will explore one path mitigating the conflicting pressures by utilizing vROPS to monitor for violations of a given policy, and when detected performing automatic remediation of policy violations, while allowing a for exceptions. We will close with exploring how we can build upon this monitoring framework to deliver self-service capabilities of day two actions.

How to find properties in the Code Stream output schema

I’ve been doing a bit of work in Code Stream lately, and was running into some issues with a pipeline I built a while ago, but had updated the Cloud Template it was interacting with, as a result of these changes in the template I was now getting the error message Unable to resolve task input property : tasks[‘Build.Deploy Worker VM’][‘output.deploymentDetails’]. I proceeded to bang my head against the wall for a bit: