Add a soft host-tag VM placement preference (affinity processor) - #14083
nagaboinaramgopal wants to merge 4 commits into
Conversation
Adds a host-tag affinity processor. An affinity group of type "host tag affinity" takes the group name as a host tag; when a member VM is deployed, routing hosts carrying that tag in the VM's zone get their deployment priority raised. It is a preference and not a constraint: no host is ever added to the avoid set, so deployment still succeeds when no tagged host is available, and check() never fails a planned destination. The raised priority is not consulted by automatic DRS. Unit tests cover the three paths: tagged hosts get priority raised (nothing excluded), an empty match is a no-op, and check() returns true.
|
@weizhouapache how does this fit in functionally with hard - and soft affinity and -anti-affinity? |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #14083 +/- ##
============================================
+ Coverage 19.78% 19.91% +0.13%
- Complexity 19995 20209 +214
============================================
Files 6371 6374 +3
Lines 575909 577264 +1355
Branches 70509 70703 +194
============================================
+ Hits 113950 114987 +1037
- Misses 449526 449702 +176
- Partials 12433 12575 +142
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
@DaanHoogland . This is meant as the soft counterpart of host tags on service offerings, not as another VM-to-VM affinity. A group of this type points its VMs at the hosts carrying the matching tag and raises their deployment priority. No host is excluded, so a deployment still succeeds when the tagged hosts are full. With the existing types, it uses the same host priority adjustment as the non-strict affinity and anti-affinity processors, so a VM that belongs to both kinds of group gets the combined adjustment. The strict processors still filter hosts through the avoid list before any priority is applied, so they always take precedence. It is implemented as a separate processor module and does not change the existing ones. If you or @weizhouapache think this fits better as an option on host tags than as a new affinity group type, I am happy to rework it that way. |
|
your code looks good @nagaboinaramgopal , but I still have some functional doubts (due to lack of understanding probably) |
|
Right, this is operator-driven by design: the group name is the host tag, and tagging hosts is the operator's job, same as the existing host tag on offerings. The group is just how you pin that preference onto a set of VMs. That said, the gap you're pointing at is real. I left it inheriting |
Host tags are set by the operator, so a group whose name is used as a host tag should be admin-controlled too. Override isAdminControlledGroup() to true (as ExplicitDedication does) so a non-admin cannot create a group named after an operator tag to bias their VMs onto those hosts.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Host priority is applied only after cluster selection, so the zone-wide preference fails across clusters.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Adds a soft host-tag affinity processor that prioritizes tagged hosts without excluding alternatives.
Changes:
- Adds host-tag affinity processing and Spring registration.
- Integrates the plugin into Maven and client packaging.
- Adds unit tests for priority, fallback, and administration behavior.
| File | Description |
|---|---|
plugins/pom.xml |
Registers the plugin module. |
client/pom.xml |
Packages the plugin with the client. |
plugins/affinity-group-processors/host-tag-affinity/pom.xml |
Defines the Maven module. |
.../HostTagAffinityProcessor.java |
Implements host-tag priority adjustment. |
.../HostTagAffinityProcessorTest.java |
Tests processor behavior. |
.../spring-host-tag-affinity-context.xml |
Registers the processor bean and type. |
.../module.properties |
Defines plugin module metadata. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } | ||
|
|
||
| for (HostVO host : preferredHosts) { | ||
| Integer priority = adjustHostPriority(plan, host.getId()); |
@nagaboinaramgopal , I think it would be better to have a pattern that such tags must be adhering to and forbid the user to create any group adhering to that pattern. Though I think the functionality might be useful, I fear it leaves a bit to much opportunity for abuse. |
| return; | ||
| } | ||
|
|
||
| for (HostVO host : preferredHosts) { |
There was a problem hiding this comment.
what happens if the tagged host is in a different cluster than the first one the planner tries? looks like the higher priority only reorders hosts inside one cluster, so the vm could still land somewhere else
There was a problem hiding this comment.
Yeah, you're right. The priority bump only reorders hosts inside whichever cluster the planner ends up choosing, so if the tagged host sits in a cluster the planner reaches later, an untagged host in an earlier cluster can still win. I can keep this scoped as a within-cluster preference and fix the wording so it doesn't claim more than it does, or make it bias cluster ordering too so it holds across the whole zone (that one's a bigger change to how clusters get picked). Do you have a preference?
There was a problem hiding this comment.
keeping it inside one cluster and fixing the wording sounds good to me, thats how the existing non-strict host affinity works too. changing how clusters get picked feels like its own pr
There was a problem hiding this comment.
Perfect, thanks. I've scoped it as a within-cluster preference and fixed the wording in both the code and the description so it doesn't imply anything zone-wide. Cluster ordering can be its own PR later if we decide we want it.
|
Thanks @DaanHoogland. With the admin-controlled change it's now only an admin who can create one of these groups, so a regular user can't point one at an arbitrary tag anymore, which I think closes the main abuse path. If you'd still like a naming pattern the tags must follow on top of that (so it can only target tags meant for this, not operational ones), I'm happy to add a configurable prefix and enforce it at creation. Would admin-controlled be enough, or do you want the pattern as well? |
if we can guarantee normal users cannot create groups named after these tags, I'm fine. the prefix would then only be a nice operational feature. |
…e javadoc The priority hint reorders hosts within the cluster the planner selects, it does not change cluster selection, so the doc no longer implies a zone-wide guarantee.
|
Thanks @DaanHoogland. Confirmed on the guarantee: createAffinityGroup throws PermissionDeniedException for a non-root-admin on an admin-controlled group, and the type isn't even listed to non-admins, so a regular user can't create one of these. I'll leave the tag prefix out for now and we can add it later as the operational nicety you mentioned. |


Description
Adds a new affinity group processor for a soft host-tag placement preference. A group of type "host tag affinity" uses the group name as a host tag, and when a member VM deploys, hosts carrying that tag get a higher deployment priority within the cluster the planner selects. It does not change which cluster is chosen, so it is a within-cluster preference, the same way the existing non-strict host affinity works. It is only a preference: no host is excluded, deployment still works when no tagged host is free, and check() never fails a destination. The group is admin-controlled, so only an admin can create one and a regular user cannot name a group after a host tag. This is the soft counterpart to the existing strict host tag on service and disk offerings. The raised priority is a planner hint and is not used by automatic DRS.
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
N/A
How Has This Been Tested?
Unit tests cover the three paths: tagged hosts get a higher priority, no matching tag is a no-op, and check() returns true.
Also on a live lab with three KVM hosts in one zone: tagged a single host, created a "host tag affinity" group named after the tag, and deployed. All the VMs in the group landed on the tagged host, and a VM outside the group landed on an untagged host.
How did you try to break this feature and the system with this change?
Deployed with no host carrying the tag: the processor does nothing and the VM still deploys. Ran a VM outside the group to confirm the untagged host was a valid target, so the tag preference is what steered placement, not a lack of options. Since it only raises priority and never excludes a host, a full or missing tagged host cannot block a deployment.