Skip to content

Settle incoming migrations before declaring a hypervisor evicted#347

Open
fwiesel wants to merge 4 commits into
mainfrom
settle-incoming-migrations
Open

Settle incoming migrations before declaring a hypervisor evicted#347
fwiesel wants to merge 4 commits into
mainfrom
settle-incoming-migrations

Conversation

@fwiesel

@fwiesel fwiesel commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

During the running phase of a live-migration, Nova's Instance.host still points at the source. If the operator queries the destination at that moment, it sees zero servers and declares it evicted — while a VM is about to land. A terminal guard now restarts eviction when NumInstances > 0 despite the Eviction CR reporting success.

After disabling the compute service, the maintenance controller now queries os-migrations for in-flight migrations targeting the host, aborts abortable ones, and waits for post-migrating ones to complete. Eviction only proceeds once no incoming migrations remain.

The query uses a 6h changes-since window. Nova updates Migration.updated_at every ~5s during live-migration, so any active migration is guaranteed to appear. 6h also covers the post-migrating phase, which writes updated_at only once on entry.

New condition: IncomingMigrationsSettled. Missing/False blocks terminal state. Requeues every 10s while unsettled.

Summary by CodeRabbit

  • New Features

    • Added status reporting for incoming migration settlement, including waiting, aborting, and settled states.
    • Maintenance now detects incoming migrations before evicting a hypervisor.
    • Abortable live migrations are stopped automatically; non-abortable migrations are monitored until complete.
    • Eviction resumes automatically once incoming migrations have settled.
  • Bug Fixes

    • Corrected eviction handling when instances remain after reported completion.
    • Cleans up stale maintenance conditions when maintenance is canceled.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@fwiesel, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a9931635-4290-4baa-800d-581738171b27

📥 Commits

Reviewing files that changed from the base of the PR and between 090ba86 and 92103cc.

📒 Files selected for processing (7)
  • api/v1/eviction_types.go
  • internal/controller/hypervisor_maintenance_controller.go
  • internal/controller/hypervisor_maintenance_controller_test.go
  • internal/openstack/migrations.go
  • internal/openstack/migrations_test.go
  • internal/utils/conditions.go
  • internal/utils/conditions_test.go
📝 Walkthrough

Walkthrough

Incoming migration discovery and settlement now gate hypervisor eviction. The controller records settlement conditions, requeues while migrations remain, uses SSA for status and Eviction resources, and re-enters draining when eviction completes while instances remain.

Changes

Incoming migration eviction flow

Layer / File(s) Summary
Migration condition contract
api/v1/eviction_types.go
Adds the IncomingMigrationsSettled condition and settled, aborting, and waiting reasons.
SSA condition utilities
internal/utils/conditions.go, internal/utils/conditions_test.go
Adds condition conversion and apply-configuration update helpers with transition-time handling and tests.
Nova migration discovery and settling
internal/openstack/migrations.go, internal/openstack/migrations_test.go
Lists active incoming migrations, aborts eligible live migrations, classifies waiting migrations, and tests request, filtering, and error behavior.
Eviction reconciliation gating
internal/controller/hypervisor_maintenance_controller.go, internal/controller/hypervisor_maintenance_controller_test.go
Applies status through SSA, gates eviction on migration settlement, requeues outstanding migrations, prunes stale conditions, and restores draining when instances remain.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HypervisorMaintenanceController
  participant SettleIncomingMigrations
  participant NovaAPI
  participant EvictionAPI
  HypervisorMaintenanceController->>SettleIncomingMigrations: Settle incoming migrations
  SettleIncomingMigrations->>NovaAPI: List active incoming migrations
  NovaAPI-->>SettleIncomingMigrations: Migration states
  SettleIncomingMigrations->>NovaAPI: Abort eligible live migrations
  SettleIncomingMigrations-->>HypervisorMaintenanceController: Settlement status
  HypervisorMaintenanceController->>EvictionAPI: Create or apply Eviction
  EvictionAPI-->>HypervisorMaintenanceController: Eviction status
Loading

Possibly related PRs

Suggested reviewers: mchristianl

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the PR’s main change: settling incoming migrations before marking a hypervisor evicted.
Docstring Coverage ✅ Passed Docstring coverage is 94.12% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch settle-incoming-migrations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@fwiesel
fwiesel changed the base branch from main to ssa-hypervisor-maintenance July 15, 2026 09:55
@fwiesel

fwiesel commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Depends on #339 and transitively on #335.

@fwiesel
fwiesel force-pushed the settle-incoming-migrations branch from 19e9855 to ce12efe Compare July 15, 2026 09:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/controller/hypervisor_maintenance_controller.go (1)

183-191: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Always clear the migration-settlement condition when maintenance ends.

If eviction never started because migrations were unsettled, Evicted is false and no Evicting condition exists, so this branch leaves IncomingMigrationsSettled=False stale after maintenance is cancelled.

Proposed fix
 case kvmv1.MaintenanceUnset:
-	if hv.Status.Evicted || meta.RemoveStatusCondition(&hv.Status.Conditions, kvmv1.ConditionTypeEvicting) {
+	stateChanged := hv.Status.Evicted
+	stateChanged = meta.RemoveStatusCondition(
+		&hv.Status.Conditions, kvmv1.ConditionTypeEvicting,
+	) || stateChanged
+	stateChanged = meta.RemoveStatusCondition(
+		&hv.Status.Conditions, kvmv1.ConditionTypeIncomingMigrationsSettled,
+	) || stateChanged
+	if stateChanged {
 		err := k8sclient.IgnoreNotFound(hec.Delete(ctx, eviction))
 		hv.Status.Evicted = false
-		meta.RemoveStatusCondition(&hv.Status.Conditions, kvmv1.ConditionTypeIncomingMigrationsSettled)
 		return ctrl.Result{}, err
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/hypervisor_maintenance_controller.go` around lines 183 -
191, Update the MaintenanceUnset branch in the maintenance reconciliation logic
to always remove the ConditionTypeIncomingMigrationsSettled condition when
maintenance ends, even when Evicted is false and no Evicting condition exists.
Keep eviction deletion conditional as currently implemented, but move the
migration-settlement cleanup outside that condition before returning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@applyconfigurations/api/v1/hypervisorspec.go`:
- Around line 16-18: Update the source comments for HypervisorSpec fields Reboot
and EvacuateOnReboot to use “requests a reboot” and “requests an evacuation”
wording, then regenerate the derived applyconfigurations HypervisorSpec output
and CRD YAML so all generated documentation matches.

In `@internal/controller/hypervisor_maintenance_controller.go`:
- Around line 237-243: The status-false branch handling remaining instances must
restart eviction instead of leaving the completed Eviction CR in place. In the
hv status reconciliation logic, delete the finished eviction resource, set the
Hypervisor Evicting condition to True with ConditionReasonRunning, and requeue
reconciliation; add regression coverage for an absent or already-Running
condition rather than a pre-seeded Succeeded condition.

In `@internal/openstack/migrations.go`:
- Around line 144-152: The abort branch in SettleIncomingMigrations must only
process migrations whose MigrationType is "live-migration". Update the condition
around AbortMigration so running evacuations remain in waiting without issuing a
DELETE, and add a test covering that behavior.

---

Outside diff comments:
In `@internal/controller/hypervisor_maintenance_controller.go`:
- Around line 183-191: Update the MaintenanceUnset branch in the maintenance
reconciliation logic to always remove the ConditionTypeIncomingMigrationsSettled
condition when maintenance ends, even when Evicted is false and no Evicting
condition exists. Keep eviction deletion conditional as currently implemented,
but move the migration-settlement cleanup outside that condition before
returning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d6ac979f-7f1c-4d7c-846a-1a65c98e733b

📥 Commits

Reviewing files that changed from the base of the PR and between 71421ef and 19e9855.

📒 Files selected for processing (24)
  • api/v1/eviction_types.go
  • applyconfigurations/api/v1/aggregate.go
  • applyconfigurations/api/v1/aggregategroup.go
  • applyconfigurations/api/v1/capabilities.go
  • applyconfigurations/api/v1/cell.go
  • applyconfigurations/api/v1/domaincapabilities.go
  • applyconfigurations/api/v1/eviction.go
  • applyconfigurations/api/v1/evictionspec.go
  • applyconfigurations/api/v1/evictionstatus.go
  • applyconfigurations/api/v1/group.go
  • applyconfigurations/api/v1/hypervisor.go
  • applyconfigurations/api/v1/hypervisorspec.go
  • applyconfigurations/api/v1/hypervisorstatus.go
  • applyconfigurations/api/v1/hypervisorupdatestatus.go
  • applyconfigurations/api/v1/instance.go
  • applyconfigurations/api/v1/operatingsystemstatus.go
  • applyconfigurations/api/v1/traitgroup.go
  • applyconfigurations/internal/internal.go
  • charts/openstack-hypervisor-operator/crds/kvm.cloud.sap_evictions.yaml
  • charts/openstack-hypervisor-operator/crds/kvm.cloud.sap_hypervisors.yaml
  • internal/controller/hypervisor_maintenance_controller.go
  • internal/controller/hypervisor_maintenance_controller_test.go
  • internal/openstack/migrations.go
  • internal/openstack/migrations_test.go

Comment thread applyconfigurations/api/v1/hypervisorspec.go Outdated
Comment thread internal/controller/hypervisor_maintenance_controller.go Outdated
Comment thread internal/openstack/migrations.go
@fwiesel
fwiesel force-pushed the settle-incoming-migrations branch 3 times, most recently from f2b7e0f to 9183e07 Compare July 15, 2026 10:24
@fwiesel
fwiesel changed the base branch from ssa-hypervisor-maintenance to main July 15, 2026 10:26
@fwiesel
fwiesel force-pushed the settle-incoming-migrations branch from 9183e07 to 090ba86 Compare July 15, 2026 11:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
internal/controller/hypervisor_maintenance_controller_test.go (1)

586-621: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the 10-second requeue contract.

These scenarios verify status and resource state but discard ctrl.Result. If RequeueAfter regresses, no Eviction exists to generate another event and the host can remain blocked indefinitely. Capture the reconcile result and assert RequeueAfter == settleRequeueInterval for both aborting and waiting cases.

Also applies to: 624-657

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/controller/hypervisor_maintenance_controller_test.go` around lines
586 - 621, The running incoming migration scenarios currently discard the
reconcile result, so they do not verify the required retry interval. Update the
relevant test cases around the incoming migration aborting and waiting contexts
to capture ctrl.Result and assert that RequeueAfter equals
settleRequeueInterval, including both cases identified by the existing
status/resource assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/controller/hypervisor_maintenance_controller.go`:
- Around line 286-298: Update the status condition message in the waiting branch
of the maintenance controller to describe all non-abortable incoming migrations,
including evacuations and other states. Replace the post-migrating-specific
wording in the WithMessage call while preserving the existing count and
condition behavior.
- Around line 186-199: At the start of the active-maintenance branch covering
MaintenanceManual, MaintenanceAuto, and MaintenanceTermination, seed the
existing IncomingMigrationsSettled and Evicting status conditions into statusCfg
before any mutation. Remove the later duplicate seeding in the
unsettled-migration and eviction paths, preserving existing condition timestamps
and avoiding duplicate entries across reconciles.
- Around line 81-84: Update the status construction around HypervisorStatus and
ConditionTypeHypervisorDisabled so this controller does not copy or apply
HypervisorDisabled when HA mode is enabled, avoiding ownership conflicts with
kvm-ha-service. Preserve the existing condition propagation for non-HA mode and
continue applying the evicted status.

---

Nitpick comments:
In `@internal/controller/hypervisor_maintenance_controller_test.go`:
- Around line 586-621: The running incoming migration scenarios currently
discard the reconcile result, so they do not verify the required retry interval.
Update the relevant test cases around the incoming migration aborting and
waiting contexts to capture ctrl.Result and assert that RequeueAfter equals
settleRequeueInterval, including both cases identified by the existing
status/resource assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2018bcdb-b826-4889-aa0b-0de9caed9776

📥 Commits

Reviewing files that changed from the base of the PR and between 19e9855 and 090ba86.

📒 Files selected for processing (7)
  • api/v1/eviction_types.go
  • internal/controller/hypervisor_maintenance_controller.go
  • internal/controller/hypervisor_maintenance_controller_test.go
  • internal/openstack/migrations.go
  • internal/openstack/migrations_test.go
  • internal/utils/conditions.go
  • internal/utils/conditions_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/v1/eviction_types.go

Comment thread internal/controller/hypervisor_maintenance_controller.go
Comment thread internal/controller/hypervisor_maintenance_controller.go
Comment thread internal/controller/hypervisor_maintenance_controller.go
@fwiesel
fwiesel force-pushed the settle-incoming-migrations branch 2 times, most recently from 9caf5f8 to efcca20 Compare July 16, 2026 10:12
fwiesel added 4 commits July 16, 2026 12:23
Introduce ConditionFromStatus and SetApplyConfigurationStatusCondition
as the foundation for migrating all controllers to server-side apply
of status subresources.

ConditionFromStatus converts a single metav1.Condition (as stored in
a status subresource) to a ConditionApplyConfiguration suitable for
SSA. ObservedGeneration is preserved so gating logic (e.g. the taint
controller's skip-check) keeps working.

SetApplyConfigurationStatusCondition mirrors the semantics of
meta.SetStatusCondition on the apply-configuration slice: append or
update by Type, refresh LastTransitionTime only when Status changes,
and guard against nil/empty Type to prevent panics.

Together they enable per-condition seeding so each controller claims
field ownership only over the conditions it actually manages, without
disturbing conditions owned by other field managers on Apply.
Replace PatchHypervisorStatusWithRetry with Status().Apply() for
ConditionTypeHypervisorDisabled, ConditionTypeEvicting, and the
Evicted scalar. ConditionTypeEvicting is removed by omitting it from
the apply — SSA prunes map-list entries no longer claimed by the sole
owner.

The status config is built once in Reconcile and passed to
reconcileComputeService and reconcileEviction which mutate it
directly. This eliminates intermediate condition return values and
makes the single-apply-per-reconcile explicit. The early-return guard
for already-enabled/disabled state is inlined into each branch.

Seed only the conditions owned by this controller so SSA does not
claim ownership over conditions written by others. For the
delete-by-omission case, owned conditions are seeded selectively so
the target condition is simply absent from the payload.

Also clear the forced_down flag when enabling a compute service so an
earlier HA event that set forced_down=true does not leave the service
reachable but treated as down.

SetupWithManager now delegates to registerWithManager instead of
inlining the same ctrl.NewControllerManagedBy call, which had left
registerWithManager unreachable.
Replace Create + SetControllerReference with Apply for the Eviction CR.
The owner reference and labels are set in the apply configuration
metadata, and SSA handles the upsert. A Get is still performed after
the apply to read the current eviction status conditions.

The apply configuration constructor for the cluster-scoped Eviction
resource takes only a name (no namespace).
During the running phase of a live-migration, Nova's Instance.host
still points at the source. If the operator queries the destination
at that moment, it sees zero servers and declares it evicted — while
a VM is about to land. A terminal guard now restarts eviction when
NumInstances > 0 despite the Eviction CR reporting success.

After disabling the compute service, the maintenance controller now
queries os-migrations for in-flight migrations targeting the host,
aborts abortable ones, and waits for post-migrating ones to complete.
Eviction only proceeds once no incoming migrations remain.

The query uses a 6h changes-since window. Nova updates
Migration.updated_at every ~5s during live-migration, so any active
migration is guaranteed to appear. 6h also covers the post-migrating
phase, which writes updated_at only once on entry.

New condition: IncomingMigrationsSettled. Missing/False blocks terminal
state. Requeues every 10s while unsettled.
@fwiesel
fwiesel force-pushed the settle-incoming-migrations branch from efcca20 to 92103cc Compare July 16, 2026 10:24
@github-actions

Copy link
Copy Markdown

Merging this branch will increase overall coverage

Impacted Packages Coverage Δ 🤖
github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1 0.00% (ø)
github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/controller 69.84% (+0.93%) 👍
github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/openstack 72.92% (+6.47%) 👍
github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/utils 89.83% (+16.50%) 🎉

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1/eviction_types.go 0.00% (ø) 0 0 0
github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/controller/hypervisor_maintenance_controller.go 83.20% (+4.79%) 125 (+37) 104 (+35) 21 (+2) 👍
github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/openstack/migrations.go 97.50% (+97.50%) 40 (+40) 39 (+39) 1 (+1) 🌟
github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/utils/conditions.go 95.45% (+95.45%) 44 (+44) 42 (+42) 2 (+2) 🌟

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/controller/hypervisor_maintenance_controller_test.go
  • github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/openstack/migrations_test.go
  • github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/utils/conditions_test.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant