PR 1175 review, round 3

harvester-ui-extension PR 1175 (abonillabeeche), skip the restart prompt when only a NIC's network changes, at 9499c335, reviewed 2026-11-06. Alejandro asked for this round after running it end to end on v1.10.0-rc1.

Preview: harvesterpr1175m1206r.13.48.147.135.sslip.io, built from 9499c335 merged with PR 1206 (7554cedf), backend Harvester .105 (v1.8.0-rc5, PCI add-on on). Also: the PR alone, and the shared main preview rebuilt today from 73eaccc2.

Verdict: approve once PR 1206 merges, two small points to raise now

Test it yourself

These previews won't show the feature on their own. Our Harvesters run v1.8 and v1.9, so the flag is off, and they're single-node, so no VM has the Migrate action. All the results below come from a Playwright run that changes the responses in the browser: the server version, the extra NICs, the Migrate action, two extra networks and the affinity. Every save stays in the browser.

Don't save on these previews. They talk to the real .105 with real writes, and a save on main stores hostDevices: [] on the VM. What you can check by hand without saving: open the bar's PR + 1206 link and look at the Filesystem tab in DevTools for the stray attribute in card 2. To run a scenario from the tables, ask me: the harness takes a scenario name and a preview, and leaves the VM alone.

Round 2, then and now

Round 2AlejandroChecked now
Nit: the banner also shows on stopped VMs and VMs that can't migrate, where the save still asks for a restartFixed: banner and save share a new canLiveUpdateNADRef getter. He also dropped "otherwise it takes effect after a restart" from the text.Works. No Migrate action: no banner, dialog. Stopped VM with its real actions: no banner, no dialog. The dropped sentence is what causes card 1.
After a silent save nothing tells the user, and the guest usually keeps its old IP"The operator's responsibility": a deliberate change, and the banner covers itOpen thread, your call (card 4)
Blocker moved to PR 1206: the tabs write hostDevices: []1206 is a listed dependencyStill needed, see below. You approved 1206 at 7554cedf; not merged yet.

The rest of the code is unchanged since round 2. New commits: the shared getter, tests for the restart prompt, and 9499c335 for the affinity.

What's new: the affinity fix

Alejandro's v1.10 run found a second cause of the dialog. When the Node Scheduling form opens, it adds empty placeholders straight onto a VM's existing nodeAffinity: preferredDuringScheduling…: [], or an empty required if the VM only has preferred rules. Every VM on a VLAN network has a nodeAffinity, so a network change looked like a second change. 9499c335 ignores those empty terms when deciding whether only the network changed.

Read in the shell code and confirmed in the saved request: the fix is right. It's in the PR's own code path only, which is what card 3 is about.

Bridge to bridge on a running VLAN VM: the banner shows, and the save goes through with no dialog.
Bridge to bridge on a running VLAN VM: the banner shows, and the save goes through with no dialog.

Dialog on a bridge to bridge change, PCI add-on on, tabs loaded

BuildRestart dialoghostDevices in the save
mainYes[]
PR aloneYes, 3 of 3[]
PR alone, device lists held backNoNot sent
PR + 1206No, 0 of 3Not sent

The third row shows the dialog on the second row comes from the device tabs, not the network logic. A word on timing: my first runs waited a fixed 8 s, and on a slow load they saved before the lists arrived, so the PR alone looked fine. The harness now waits for the PCI and USB responses, and these numbers come from that version.

Every case, on PR + 1206

Edit, on a running VM unless notedBannerDialogRight?
Bridge to bridge, VLAN VM (required rule)YesNoYes, 3 of 3
Same, with no affinity, affinity: {}, preferred only, required + preferred, pod anti-affinity only, required + anti-affinityYesNoYes, all six
Same, network name without a namespace, in the forklift namespaceYesNoYes
Bridge to overlayNoYesYes
VM without the Migrate actionNoYesYes
Stopped VM, real actionsNoNoYes
Server v1.9.1, flag offNoYesYes, unchanged
Remove the first NIC, next NIC untouchedNoNot savedYes, the round 1 row-key bug stays fixed
Network + CPU
Network + a new NIC
Two NICs, one to a bridge and one to an overlay
YesYesDialog right, banner wrong: card 1
VLAN VM, no changes
VLAN VM, network changed and changed back
NoYesSame on main: card 3
VM with no node affinity, no changesNoNoYes

Jest: 122 passed on the PR's touched suites. 185 passed on a wider set against the merged code. The 1 skipped test is older than this PR. CI is green.

Findings

1

The banner promises no restart, then the save asks for one

pkg/harvester/l10n/en-us.yaml:872
Suggestion

The text used to be conditional. After our round 2 nit it became "The virtual machine is live-migrated to apply the network change without a restart." The banner only looks at this NIC, so it stays up when something else in the same edit forces a restart: another field, a new NIC, or another NIC moving to an overlay.

So the screen says "without a restart" with a restart dialog on top of it. The dialog is correct. His own Cypress case 2 goes through this path.

Network + CPU changed: the banner says "without a restart" behind the restart dialog.
Network + CPU changed: the banner says "without a restart" behind the restart dialog.

Suggested fix

One short clause in the text, along the lines of "if this is the only change". The banner logic doesn't need to change, and I wouldn't ask for it to track the whole form.

2

The new prop was also added to the Filesystem and Sysprep tabs

pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue:1010, 1206
Nit

Only the Networks tab uses canLiveUpdateNADRef. Neither of the other two components declares it, so Vue puts it on their root element. I saw can-live-update-n-a-d-ref on div.vm-filesystem in the page. Sysprep needs a Windows VM, so that one is from the code only. It has no effect on behaviour, and the fix is deleting the two lines. It looks like a slip from the line above, since all three follow :namespace.

3

A save with no changes on a VLAN VM still asks for a restart

pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue:533, pkg/harvester/utils/vm.js:85
Your call

Same placeholder, same symptom as 1206, on the path that 9499c335 doesn't cover: specChanged is computed before the cleanup, and the cleanup only runs inside isNADOnlyChange. Changing the network and then changing it back hits this too.

Same on main (dialog with the device lists held back, so it isn't hostDevices), so it isn't this PR's bug. I'm raising it because it undercuts what 1206 set out to do: every VM on a VLAN network keeps the empty-save prompt after both PRs merge.

VLAN VM, nothing changed: Save still opens the restart dialog. Main does the same.
VLAN VM, nothing changed: Save still opens the restart dialog. Main does the same.

Simplest fix, if you want it

Apply the cleanup before specChanged is computed, so both paths compare the same thing. That's one call site moved, not new logic. I haven't built this. It's in the suggested text as optional, so drop it if you'd rather keep the PR focused or raise it separately.

4

Your silent-save thread: I would accept his answer and resolve it

pkg/harvester/edit/kubevirt.io.virtualmachine/index.vue:543
Your call

His argument holds: moving a VM to another VLAN is deliberate, and forcing a restart would take away the common case where the subnet is the same. The banner mentions the DHCP renew before the save.

One small gap, not worth a comment on its own: his reply says the banner tells people the guest isn't notified and may need a restart. The text only says it "may need to renew its DHCP lease".

5

Copilot's two comments don't need a change

pkg/harvester/utils/vm.js:98, 103
FYI

High: "an empty required selector is a real scheduling change". That empty value is exactly the placeholder the form adds to a VM with only preferred rules; I saw it in the saved request. In a server dry run on .105, the API accepted it and left it out of the result, so nothing stored changes. This was v1.8.0-rc5, not v1.10.

Medium: "affinity: {} left behind causes an extra prompt". It's reachable but rare: a VM with no affinity key at all, where someone also picks "scheduling rules" without adding a rule. All 9 VMs on .105 have the key. And it fails safe: one extra dialog.

These threads are Alejandro's to close, so I left them out of the suggested text.

6

Other things worth knowing

not in the suggested text
FYI

What I didn't check, and what I did on the backends

Suggested review

Comment now, Approve after 1206 merges. Short intro, with each point inline. Screenshots get uploaded when you ask for the pending review. No preview link and no code, as usual.

Body

Thanks for the end to end run. The affinity fix and the banner fix both work. With PR 1206 merged in I get no dialog on a network-only save, so I'll approve once that one is in. A few small points inline, none blocking.

en-us.yaml:872

Since this no longer has a condition, it now promises no restart even when the save asks for one: another field changed in the same edit, a new NIC, or a second NIC moved to an overlay network. Your Cypress case 2 goes through this. Could the text say it applies when the network is the only change?

[screenshot: banner and restart dialog together]

index.vue:1010 (same on 1206)

Filesystem and WindowsSysprep don't have this prop, so it ends up as an attribute on their root element. Only the Networks tab needs it.

Optional: index.vue:533

Not from this PR, since main does the same: a save with no changes on a VM on a VLAN network still asks for a restart, from the same empty affinity terms. Changing the network and changing it back hits it too. Would it make sense to do the cleanup before this comparison instead of only in isNADOnlyChange? Then both cases are covered. Fine as a follow-up if you'd rather keep this PR focused.

[screenshot: restart dialog on a VM with no changes]

Your silent-save thread

Resolve without a reply, if you agree with card 4.