engine: do not force the destroy stop while the host may reconnect - #14233
Open
bhouse-nexthop wants to merge 4 commits into
Open
bhouse-nexthop wants to merge 4 commits into
bhouse-nexthop wants to merge 4 commits into
Conversation
With vm.destroy.forcestop=true, destroying an instance whose host is briefly Disconnected releases its NICs, addresses and volumes without stopping it. The domain keeps running unmanaged and its address is handed to the next instance. A forced stop treats an unreachable host as proof the instance is stopped. That holds for a host that is gone, not for one that is Connecting, Disconnected, Alert or Rebalancing, as happens on every agent or management server restart. Add VirtualMachineManager.shouldForceStopOnDestroy(): the value of vm.destroy.forcestop, except while the host is in one of those states. The stop then fails, the instance stays Running and the destroy can be retried. Use it on all three destroy paths, and drop the unregistered duplicate of the ConfigKey in UserVmManagerImpl. Fixes apache#14232 Signed-off-by: Brad House <bhouse@nexthop.ai>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 4.22 #14233 +/- ##
============================================
+ Coverage 17.98% 18.01% +0.02%
- Complexity 16195 16234 +39
============================================
Files 5930 5930
Lines 535649 535714 +65
Branches 65590 65600 +10
============================================
+ Hits 96343 96486 +143
+ Misses 428330 428226 -104
- Partials 10976 11002 +26
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:
|
Review of the previous commit: reading the host status before the stop misses hosts that are unreachable while still Up, such as those of a crashed management server until its peers mark them Disconnected, or an agent whose disconnect investigation is inconclusive. The decision was also baked into a job that may run much later. Decide where the StopCommand goes unanswered instead. A destroy now stops through VirtualMachineManager.advanceStopForDestroy(), which forces the stop according to vm.destroy.forcestop but, carried on VmWorkStop, releases the resources without the host's answer only when the host is gone: no host, no host record, Down or Removed. Otherwise it fails as an unforced stop does. The forced cleanup of instances that cannot enter Stopping gets the same check before it releases anything. The stop stays forced, so a host that answers still gets a hard stop. An explicit forced stop is unchanged: that is a caller stating the instance is to be treated as stopped, and it remains the way out for an instance on a host that stays Alert. Restore the vm.destroy.forcestop ConfigKey in UserVmManagerImpl, still needed for the force-stop permission check. Add tests through advanceStop() and for each destroy path. Signed-off-by: Brad House <bhouse@nexthop.ai>
…leanup Account cleanup expunges each instance even when destroying it failed. Expunge releases the instance's network resources before it stops it, so an instance whose stop fails loses its addresses while its domain keeps running. With the previous commit this is what a destroy on a briefly disconnected host now does. Skip the expunge when the destroy failed and the instance still has a host and is not stopped, and mark the account for another cleanup pass. Signed-off-by: Brad House <bhouse@nexthop.ai>
An instance stalled in Starting or Migrating cannot enter Stopping, so its stop goes through the forced cleanup, which releases whatever the host answers. It was decided from the host status before any stop, and so refused even when an Up host would have answered. Try an ordinary stop first and run the cleanup once the host confirms it, or at once if the host is gone. Look the host up once when deciding whether to release. A host removed between two lookups made the log line throw in a finally, replacing the real error and leaving the instance in Stopping. Account cleanup went on to remove the security groups, networks and resource counts of an instance it had just left running. Stop after the instance loop in that case and let a later pass finish. Test the flag through the job queue: the queued VmWorkStop, the job handler, the in-job path and serialization, the stalled-instance paths and the force-stop permission check. Make the VmWorkStop field final. Signed-off-by: Brad House <bhouse@nexthop.ai>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
With
vm.destroy.forcestop=true, destroying an instance whose host is briefly unreachable releases the instance's NICs, addresses and volumes without stopping it. The domain keeps running with no record in CloudStack, and its address is handed to the next instance.Fixes #14232
A forced stop that gets no answer from the host treats the instance as stopped. That is right for a host that is gone, and wrong for one that is only unreachable for a while: an agent or management server restart, a management server crash before its peers take over its hosts, or an inconclusive disconnect investigation.
vm.destroy.forcestopapplies that to every destroy.This change keeps
vm.destroy.forcestopand keeps the stop forced, so a host that answers still gets a hard stop. What changes is what a destroy does when the host does not confirm the stop. The decision is made at that point, not before the stop is queued:VirtualMachineManager.advanceStopForDestroy(): the stop is forced pervm.destroy.forcestop, and areleaseOnlyIfHostIsGoneflag onVmWorkStopmakesadvanceStop()release without the host's confirmation only when the host is goneCommit 1 is kept so the review history makes sense; it can be squashed on merge.
Every path that stops an instance to destroy it now goes through
advanceStopForDestroy():UserVmManagerImpl.destroyVm(DestroyVMCmd)VirtualMachineManagerImpl.destroy(), also used by host deletion withforcedestroylocalstorageVirtualMachineManagerImpl.advanceExpunge(), which also expunges virtual routers (network restart with cleanup, network deletion, network GC), CPVM/SSVM and internal load balancer VMsHA destroy is not affected: it stops the instance with an explicit forced stop before destroying it.
Behaviour that does not change:
stopVirtualMachine forced=true, HA) still releases regardless of the host. That is a caller stating the instance is to be treated as stoppedvm.destroy.forcestop=falsenothing changesVmWorkStopjob queued before the upgrade deserializes with the flag false and keeps the previous behaviour; one queued by an upgraded management server and run by an old one is read without the flag, as beforeKnown limitations
vm.destroy.forcestopalone. Stop it withforced=truefirst, which is an explicit decision (and needsallow.user.force.stop.vmfor non-admins), then destroy it. The same applies to routers:restartNetwork cleanup=trueand network GC fail on such a host until the router is stopped withforced=true.cleanupflag too. An explicit forced stop that joins a destroy's stop inherits its caution, and a destroy that joins an explicit forced stop inherits the force.account.cleanup.interval.destroyVm(DestroyVMCmd)withexpunge=trueremoves the backup offering before the stop. If the stop now fails, the instance stays without it. That ordering predates this change.Types of changes
Feature/Enhancement Scale or Bug Severity
Bug Severity
How Has This Been Tested?
advanceStop(): a destroy stop that cannot reach a Disconnected host fails, does not callreleaseVmResources()and leaves the instance Running; with the host Down it releases; an explicit forced stop still releases on a Disconnected hostVmWorkStop, that the job handler and the in-job path pass it on, and that it survivesVmWorkSerializer(a job without it reads false)isHostGone()over every host status,mayReleaseWithoutHostConfirmation(),advanceStopForDestroy(), the three destroy paths, the force-stop permission check, and the account cleanup guard and deferralVirtualMachineManagerImplTest(115),UserVmManagerImplTestandAccountManagerImplTestpassUnable to actually stop ... but continue with release because it's a force stopwithAgentUnavailableException ... Disconnectedfor a destroy. 8 of them were destroyed by management servers that already had engine: do not orphan an instance on a forced power report during start #14207, whose expunge-time StopCommand also could not reach the hostHow did you try to break this feature and the system with this change?
Two adversarial reviews, each checked against the code:
finally; and that the job-queue plumbing of the flag was untested. Commit 4 addresses all fourVmWorkStopis Java-serialized with an unchangedserialVersionUID, and a stream without the field reads false