Repository navigation
fix(server): Power On right after InsertMedia started the VM before its ISO import (empty CD-ROM, import pod stuck on the RWO volume) - #36
Conversation
|
Hi @itayherzberg. Thanks for your PR. PRs from untrusted users cannot be marked as trusted with I understand the commands that are listed here. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
InsertMedia is answered with 202 before its job runs, and the job only puts the importing label on the VM after creating the import pod. Power On is deferred only while that label is present, and Ironic sends it right after InsertMedia, so it landed in the gap: the VM started with an empty CD-ROM and took the RWO ISO volume away from the import pod, which then hung on Multi-Attach (or failed with Permission denied on the same node) and left the label on the VM, deferring every later power action. The handler now writes a dated placeholder into the importing label and the image URL annotation before answering. The job replaces it with the pod name as before. A repeated InsertMedia while the placeholder is set is answered as in progress instead of queueing a second job. The job's terminal failure clears the placeholder, and the periodic import-pod reconcile removes placeholders older than five minutes, whose job was lost with the in-memory queue. Signed-off-by: itayherzberg <itayherzberg@gmail.com>
1668ee2 to
d80d1e3
Compare
…ert/power-on race and follow-ups) (fork-only) Signed-off-by: itayherzberg <itayherzberg@gmail.com>
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: MarSik The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Problem
When Ironic (metal3) inspects or provisions a VM-backed BareMetalHost it sends
InsertMedia, the boot-devicePATCH, andComputerSystem.ResetOnwithin about 50 ms of each other. Some hosts then sit at the firmware prompt and inspection times out:The VM was started before the ISO was written to its CD-ROM. The VM's
virt-launcherthen holds the ReadWriteOnce ISO volume, so the import helper pod cannot run, and what happens next depends on where it lands:ContainerCreatingwithMulti-Attach error for volume "pvc-..." Volume is already used by pod(s) virt-launcher-...; theimporting.redfish/cdrom0label never comes off the VM, and because every power action is deferred while that label is present, Ironic's later power-off is deferred too. The host cannot recover on its own;dd: failed to open '/mnt/iso/disk.img': Permission denied, the label is removed, and the VM keeps running from an empty CD-ROM whileGET .../VirtualMedia/CdreportsInserted: true(the PVC is bound).Which of the two happens is luck: the import pod and
virt-launcherare created within a second of each other and whichever attaches the volume first wins. With many hosts enrolled at once the downloads are slower and the VM wins more often, so the failure shows up as "a handful of hosts out of a batch are stuck ininspecting, the rest came up", and the stuck set changes from run to run. A host that raced but whose import pod attached first boots a complete ISO and looks healthy.Cause
handleInsertVirtualMediaanswers202as soon as it has queued a task; a worker runsInsertVirtualMedialater. The only thing that holds back a Power On isIsImportInProgress, which looks for theimporting.redfish/<device>label, and that label is the last thing the job writes: after the VM spec change (CD-ROM disk and volume added, CD placed first in the boot order), after the PVC, and after creating the helper pod (copyISOToPVC) or theVolumeImportSource(CDI path). Nothing marks the VM between the202and that write, which on a quiet cluster is tens of milliseconds and on a loaded one seconds. Ironic's Power On lands in that window,handlePowerActionsees no label and starts a VM whose spec already says "boot from the CD-ROM on PVC X" while PVC X is still empty.The time the job spends in the queue is on top of that and is not what decides it: the window exists even when a worker picks the job up immediately, because the label is written at the end of the job.
Fix
The handler marks the VM before it answers. After
CheckInsertedMediasays nothing is inserted,handleInsertVirtualMediacallsMarkImportPending, which puts a dated placeholder (pending-<unix seconds>) intoimporting.redfish/<device>and records the image URL invirtual-media.redfish/<device>, in one metadata merge patch, and only then queues the task. A Power On that arrives from then on is deferred the way it already is during the import (power-after-import.redfishlabel,202with "deferred until ISO import completes") and runs when the label comes off. The job replaces the placeholder with the helper pod name (or the CDI marker) through the unchangedsetImportingLabel, so the pod watcher's "is this pod still the tracked import" check and the eject path are untouched.A repeated
InsertMediawhile the placeholder is set queues no second job. Ironic retries anInsertMediawhose answer it lost.CheckInsertedMedianow treats the importing label as "media on its way" even before the PVC exists: same image →202"still in progress", different image →409.MarkImportPendingitself refuses to overwrite an existing label and the handler answers "in progress" in that case, which narrows the window for two requests that pass the check at the same time (it is a GET followed by a merge patch, so it does not close it; that hazard exists today as well and is not made worse).A placeholder nobody will replace is removed. Two cases:
Worker.processJobclears the placeholder (ClearPendingImport, which only removes apending-value and leaves a pod name or CDI marker to the watchers);reconcileExistingImportPods, at watcher start and every minute) removes placeholders older than 5 minutes (stalePendingImportAge). The age check means a bridge instance starting during a rollout does not clear a placeholder the other instance is about to act on.Without this a VM with an orphaned placeholder would have every power action deferred forever.
The new code is in
pkg/kubevirt/import_pending.go;CheckInsertedMediaandreconcileExistingImportPodsget a few lines, the handler and the worker a few more. No new goroutine; all state lives on the VM object, so a restart in the middle of an insert changes nothing.Behaviour changes:
InsertMediawrites theimporting.redfish/<device>label (valuepending-<unix seconds>) and the URL annotation before answering; a repeatedInsertMediawhile a request is pending answers202"in progress" (same image) or409(other image) instead of queueing again; a pending import whose job failed or vanished is cleared within about 5 minutes and logged as a warning.Observed, not changed here (each is pre-existing and separate):
ForceOff/GracefulShutdownshould run immediately and cancel the deferred power-on — fix(server): a power-off is never deferred by a pending ISO import, and withdraws a deferred power-on #37;Failed) removes the label and lets the deferred power-on start the VM on an empty CD-ROM, andGET VirtualMediareportsInserted: truefor any bound PVC — fix(kubevirt): a failed ISO import must not start the VM, read as inserted, or block a re-insert #39;Inserting virtual media ... with image <URL>logs the full image URL, which for an assisted-installer discovery ISO carries the image service token — fix(logger): do not log the virtual media image URL (it carries the image service token) #38.Verification on the cluster
OpenShift 4.22 / OpenShift Virtualization 4.22, three nodes, 8 halted VMs, the bridge built from this tree and driven by a script that replays Ironic's sequence against all 8 VMs in parallel (
InsertMedia, boot overrideCd/Once,Reset Onone second after the insert). The ISO was served from an in-cluster nginx rate-limited to 1.5 MB/s so a copy takes about 45 s, long enough for a prematurely started VM to grab the volume first.Before (same tree without this change): 5 of 8 Power On requests executed (
200), 3 deferred. Bridge log of two of the five;Successfully startedis written after the start call returned, so "during import" and "before label" are the same race seen from two sides:All five imports failed that way (every pod landed on the node that already ran the VM), the five VMs stayed
Runningon an empty CD-ROM andGET .../VirtualMedia/CdansweredInserted: true. The three deferred VMs imported in 45 s and started afterwards.After, same script, same VMs, same ISO server: 8 of 8 deferred, 8 of 8 imports succeeded, 8 VMs started after their
Removed importing label. The placeholder is on the VM before the job starts, and the deferral forrl-vm-5hit the placeholder (the job's own label came 1.2 s later):The label removal and the deferred
Onfollowed the pod's success for each VM; 45 s after the run all 8 VMIs wereRunning, no VM carried animporting.redfish/*orpower-after-import.redfishlabel, and no import pod was left.InsertMediawith an image URL that cannot be parsed:202, placeholder on the VM, the job fails on every attempt, and 30 s later (three retries with the existing backoff)Cleared pending import of cdrom0 from VM ...; the label is gone and the VM accepts power actions.importing.redfish/cdrom0=pending-1000000000) was removed by the next reconcile sweep, 36 s later:Import of cdrom0 on VM ... was accepted but never started (label importing.redfish/cdrom0=pending-1000000000), clearing it.PATCHto complete before sending Power On (22 s after the insert on this cluster, where thatPATCHis slow), both trees defer all 8; the change only matters in the window, which is where Ironic normally lands.Tests
pkg/server/insert_pending_test.go, with a task manager that has no workers so queued jobs never run (the state a Power On meets right after the202):InsertMedialeaves the placeholder and the URL annotation on the VM before answering, and a Power On then is answered202deferred withpower-after-import.redfish=Onand the VM still halted; a repeatedInsertMediaanswers "in progress" and queues no second task, a different image answers409; a job that fails after its retries clears the placeholder and marks the taskException. With the handler change reverted (the tests need the new client helpers to compile, so they cannot run onmainas is) the first fails withlabel importing.redfish/cdrom0 = "" after InsertMediaand the repeat test withtasks = 2 after the repeat, want 1; with the worker change reverted the third fails withlabel ... = "pending-..." is still on the VM after the job failed.pkg/kubevirt/import_pending_test.go:MarkImportPendingwrites label and annotation and leaves other metadata alone, refuses to overwrite a running or pending import, errors on a missing VM;ClearPendingImportremoves a placeholder and keeps a pod name or CDI marker; placeholder values parse and age correctly, an unparsable one counts as stale; the sweep removes only stale placeholders, in the given namespace, and leaves the other device's label on a VM alone;CheckInsertedMediareports importing/conflict on a placeholder without a PVC and nothing-inserted when only a stale URL annotation is left;setImportingLabelreplaces the placeholder.go build ./...,go vet ./...,go test ./...,gofmt,go mod tidy(no change) pass;go test -race ./pkg/kubevirt/passes.Related
updateVM; this one adds metadata-only patches next tosetImportingLabel, which fix(kubevirt): concurrent virtual-media insert and boot override could drop the CD-ROM disk from the VM spec #33 leaves alone on purpose). Verified by merging this branch onto a tree that carries fix(server): Worker.Stop blocked forever, so shutdown waited for SIGKILL #29–fix(kubevirt): concurrent virtual-media insert and boot override could drop the CD-ROM disk from the VM spec #33: builds, tests pass, and the cluster runs above were made with that merged tree.vmGVRincache.go; this PR usesvirtualMachinesGVRso the two merge in either order.