Skip to content

qvm-template-upgrade: add orchestration workflow and in-VM agent#213

Open
nihalxkumar wants to merge 5 commits into
QubesOS:mainfrom
nihalxkumar:qvm-template-upgrade
Open

qvm-template-upgrade: add orchestration workflow and in-VM agent#213
nihalxkumar wants to merge 5 commits into
QubesOS:mainfrom
nihalxkumar:qvm-template-upgrade

Conversation

@nihalxkumar

@nihalxkumar nihalxkumar commented May 25, 2026

Copy link
Copy Markdown
Contributor

This PR introduces the qvm-template-upgrade dom0 command-line utility, that performs an in-place N -> N+1 distribution upgrade of Debian and Fedora TemplateVM or StandaloneVM

fixes: QubesOS/qubes-issues#8605
GSoC 2026 project: Automate Template Version Upgrade

@ben-grande ben-grande left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yay, started. As I've done a review now, I will receive a notification every time you commit. Let me know when you need another look or have doubts by mentioning me.

Comment thread vmupdate/tests/test_template_upgrade.py
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
@codecov-commenter

codecov-commenter commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.62931% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.31%. Comparing base (4973653) to head (c3f550e).

Files with missing lines Patch % Lines
vmupdate/agent/source/apt/apt_cli.py 88.88% 10 Missing ⚠️
vmupdate/agent/source/apt/apt_api.py 30.00% 7 Missing ⚠️
vmupdate/agent/source/common/package_manager.py 81.48% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #213      +/-   ##
==========================================
- Coverage   71.42%   69.31%   -2.12%     
==========================================
  Files          12       31      +19     
  Lines        1344     3021    +1677     
==========================================
+ Hits          960     2094    +1134     
- Misses        384      927     +543     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nihalxkumar
nihalxkumar marked this pull request as ready for review June 2, 2026 05:29
@ben-grande

Copy link
Copy Markdown
Contributor

PipelineRetryFailed

@nihalxkumar

Copy link
Copy Markdown
Contributor Author

It's showing a successful run

image image

@ben-grande

Copy link
Copy Markdown
Contributor

Some tests are not enabled on this repo, such as mypy, black and pylint. See this as an example: https://github.com/QubesOS/qubes-core-admin/blob/main/.gitlab-ci.yml. Can you create a separate PR to enable those checks?

@nihalxkumar

Copy link
Copy Markdown
Contributor Author

Sure, will do

Comment thread vmupdate/template_upgrade.py
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
@nihalxkumar

Copy link
Copy Markdown
Contributor Author

We can squash here if it's looks fine. Will also have to rebase as this is 24 commits behind.

@ben-grande

Copy link
Copy Markdown
Contributor

Looks fine to squash.

@marmarek

Copy link
Copy Markdown
Member

Oh no, commit message in the other PR closed it ...

@marmarek marmarek reopened this Jun 10, 2026
@marmarek

Copy link
Copy Markdown
Member

I see a conflict here - CI will not run until it's resolved.

@nihalxkumar
nihalxkumar force-pushed the qvm-template-upgrade branch 6 times, most recently from 16b99f5 to f1f8fc1 Compare June 14, 2026 16:29
@nihalxkumar nihalxkumar changed the title qvm-template-upgrade: add CLI skeleton and orchestration flow qvm-template-upgrade: add orchestration workflow and in-VM agent Jun 14, 2026

@ben-grande ben-grande left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have taken a look through the new code. Thanks for the progress, I like to see it evolving. Just minor review though

Comment thread vmupdate/agent/source/common/package_manager.py
Comment thread vmupdate/agent/source/common/package_manager.py
Comment thread vmupdate/agent/source/dnf/dnf_cli.py Outdated
Comment thread vmupdate/agent/source/dnf/dnf_cli.py Outdated
@nihalxkumar
nihalxkumar force-pushed the qvm-template-upgrade branch from ad97dca to a0289a7 Compare June 15, 2026 17:11
@ben-grande

Copy link
Copy Markdown
Contributor

@nihalxkumar

Copy link
Copy Markdown
Contributor Author

When I tried upgrading fedora 41 -> 42 there was a cleanup failure which happened after the successful version upgrade.

logs as shared privately on tuesday:

https://gist.github.com/nihalxkumar/01fb990deab3960cb28d680773bb1089#file-upgrade42-log-L48-L55

we can see Complete! in the above log.

https://gist.github.com/nihalxkumar/0e4edccb4f4409b1a4daf5c44b6f576f

After version_upgrade() returns success, the agent still runs cleanup. The old code used the cleanup exit code when deciding the final agent exit code.
For dnf this includes DNFCLI.clean() (dnf clean packages), and the transport also removes /run/qubes-update/.

a90eee1 (last commit) is based on that failure boundary. After the release transaction has succeeded, a later cleanup failure should not cause rollback of the upgraded clone.

@ben-grande

Copy link
Copy Markdown
Contributor

But we can't ignore all cleanup failures. We don't know what is causing it, so this needs to be investigated.

@ben-grande

Copy link
Copy Markdown
Contributor

a90eee1 wasn't ignoring all cleanup failures 😅

a90eee1

How not? It was ignoring all cleanup failures if pkg_mng.version_upgrade returned EXIT.OK. Can you explain in more details? I didn't check if this change remained in the latest commits.

@ben-grande

Copy link
Copy Markdown
Contributor

f66d1e7

Unsure about this one also. If shutdown failed, the upgrade might have failed despite the package manager not returning an error during upgrade. Which exception were you receiving?

@nihalxkumar

Copy link
Copy Markdown
Contributor Author

a90eee1 wasn't ignoring all cleanup failures 😅

a90eee1

How not? It was ignoring all cleanup failures if pkg_mng.version_upgrade returned EXIT.OK. Can you explain in more details? I didn't check if this change remained in the latest commits.

By "all" I thought we are including the regular-updates cleanup behavior as well, which were untouched. On the version-upgrade path, though, you're right: once the upgrade returned EXIT.OK, a90eee1 suppressed any non-zero clean() code. My reasoning was that a failing clean() after a completed release transaction is just a cache issue, but the dom0 tool rolls back on any agent exit != EXIT.OK (template_upgrade.py#L307) a fatal cleanup code would delete a good upgrade. So I tolerated it only on the success path and kept it fatal for normal updates.

I've since dropped that tolerance change. As in every run dnf clean actually returned 0, so it was never being triggered; and the rollbacks I was chasing were exit-26 (ERR_VM_UNHANDLED), whereas a cleanup failure can only ever be exit-25 (ERR_VM_CLEANUP) (dnf_cli.py#L145-L151), which is already in VM_HANDLED (exit_codes.py#L34-L40) and never escalates to 26. So cleanup wasn't the cause. The real source of exit-26 was the teardown, which bd9a723 addresses.

@nihalxkumar

Copy link
Copy Markdown
Contributor Author

f66d1e7

Unsure about this one also. If shutdown failed, the upgrade might have failed despite the package manager not returning an error during upgrade. Which exception were you receiving?

The exception was an AttributeError:

2026-06-26 07:52:44,585 Cannot shutdown fedora-42-xfce, because of error: 'DeviceCollection' object has no attribute 'get_assigned_devices'

It's raised in the pre-existing _has_assigned_pci_devices helper (qube_connection.py#L109-L112, added in 12e9e32 3 months ago) while __exit__ shutsdown the clone.

To your concern that a failed shutdown could mean a failed upgrade: in this case it didn't. The agent finishes cleanly: distro-sync exit 0, dnf clean packages exit 0 and the failure only appears afterwards, at shutdown (logs shared earlier):

2026-06-26 05:40:02,642 [Agent] run command: dnf clean all
2026-06-26 05:40:04,387 [Agent] command exit code: 0
2026-06-26 05:40:04,391 [Agent] run command: dnf --releasever=42 distro-sync --best --allowerasing --assumeyes
2026-06-26 07:52:09,778 [Agent] command exit code: 0
2026-06-26 07:52:09,798 [Agent] version-upgrade out: Removed 24 files, 11 directories (total of 87 MiB). 0 errors occurred.
2026-06-26 07:52:09,808 [Agent] version-upgrade out: 
2026-06-26 07:52:09,817 [Agent] Notify dom0 about upgrades.
2026-06-26 07:52:38,827 [Agent] run command: dnf clean packages
2026-06-26 07:52:39,630 [Agent] command exit code: 0
2026-06-26 07:52:43,427 Remove /run/qubes-update/
2026-06-26 07:52:43,427 run command in fedora-42-xfce: rm -r /run/qubes-update/
2026-06-26 07:52:43,428 Wait for output
2026-06-26 07:52:44,585 Cannot shutdown fedora-42-xfce, because of error: 'DeviceCollection' object has no attribute 'get_assigned_devices'
2026-06-26 07:52:44,586 agent output: 
2026-06-26 07:52:44,586 agent output: 
2026-06-26 07:52:44,587 agent exit code: 0

My dom0 is on qubes-core-admin-client-4.2.18, whose DeviceCollection still exposes assignments()/attached() and not get_assigned_devices(). The latter is the R4.3/main device API that this branch targets, so on an actual R4.3 dom0 this specific AttributeError shouldn't fire.

bd9a723 logs the shutdown error instead of letting a teardown exception propagate out of _run_agent, get caught by update_qube's broad except, and surface as ERR_VM_UNHANDLED which then rolls back and deletes a clone whose upgrade already completed. It also appends the agent's out/err to the UpgradeError, so the real cause reaches the tool log next time instead of being discarded.

We could also harden the source instead, _has_assigned_pci_devices fall back across both device APIs (or catch AttributeError there) so the shutdown path itself is robust.

@ben-grande

Copy link
Copy Markdown
Contributor

My dom0 is on qubes-core-admin-client-4.2.18, whose DeviceCollection still exposes assignments()/attached() and not get_assigned_devices(). The latter is the R4.3/main device API that this branch targets, so on an actual R4.3 dom0 this specific AttributeError shouldn't fire.

Can you upgrade to R4.3? It would make testing much easier, cause currently you are testing an EOL release.

bd9a723 logs the shutdown error instead of letting a teardown exception propagate out of _run_agent, get caught by update_qube's broad except, and surface as ERR_VM_UNHANDLED which then rolls back and deletes a clone whose upgrade already completed. It also appends the agent's out/err to the UpgradeError, so the real cause reaches the tool log next time instead of being discarded.

The exception should be raised so the command fails. It won't be raised when you have R4.3.

@nihalxkumar
nihalxkumar force-pushed the qvm-template-upgrade branch from bd9a723 to d3ab852 Compare July 6, 2026 18:29
@nihalxkumar

Copy link
Copy Markdown
Contributor Author

I upgraded my system to R4.3 and the upgrade ran successfully end to end upgrade with no errors. I have reset the branch to d3ab852 as of june15.

The shutdown exception propagates, and failure detail it was trying to surface is already in /var/log/qubes/update-<name>.log, so no replacement was needed.

Next thing is disk handling for temporary cache volume

@ben-grande ben-grande left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi. Could you also take a look at:

  • Debian integration
  • API usage for better progress reporting

Comment thread vmupdate/agent/source/dnf/dnf_cli.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py Outdated
Comment thread vmupdate/template_upgrade.py
@ben-grande

Copy link
Copy Markdown
Contributor

In the future, this tool will be tested by openqa and when it is good enough, it should serve this use case:

In other words, it should be able to do, what this command from the link, does.


After you add API support, check if upgrading simultaneously with CLI and API breaks things or it is safely handled.

@nihalxkumar

Copy link
Copy Markdown
Contributor Author

@ben-grande Should debian integration be also included in this PR?

@ben-grande

Copy link
Copy Markdown
Contributor

Yes please.

@nihalxkumar
nihalxkumar force-pushed the qvm-template-upgrade branch from 3f7bb44 to 1271315 Compare July 13, 2026 11:46
@nihalxkumar

Copy link
Copy Markdown
Contributor Author

1271315 (fedora disk handling) automates the manual cache-volume procedure. I went with attaching a throwaway cache volume to the clone every time instead of just as a fallback when dnf complains about space because a whole-release distro-sync on a template's small root disk will basically always need it, so it's provisioned upfront instead of waiting for the first attempt to fail.

dom0 creates a sparse 5GB image on disk, formats it ext4, and attaches it to the clone as a loop device through qvm-block, same as the manual steps. The one deliberate difference from the doc is that the agent mounts it by filesystem UUID rather than a device path like /dev/xvdi which isn't guaranteed stable.

Everything is torn down afterwards regardless of outcome: the loop device is detached and the image deleted on success, failure, or rollback, and a lock file lets a later run detect and reclaim an image left behind by one that got killed mid-way.

Open to design decisions, after the reviews I will test it on my system and then we can take on Debian upgrade.

Comment thread vmupdate/agent/source/dnf/dnf_cli.py Outdated
Comment thread vmupdate/agent/source/dnf/dnf_cli.py Outdated
Comment thread vmupdate/agent/entrypoint.py Outdated
Add a new qvm-template-upgrade command to safely upgrade TemplateVMs and
StandaloneVMs. The command validates the source qube, derives the next
release clone name, clones the template, updates metadata, and removes
failed clones unless told to keep them. The in-VM upgrade agent is stubbed
for now, letting the orchestration and tests land first.

fixes: QubesOS/qubes-issues#8605
@nihalxkumar
nihalxkumar force-pushed the qvm-template-upgrade branch from 1271315 to 8d7ce28 Compare July 16, 2026 11:38
Add dnf support for upgrading templates to the next major release. Add a
version_upgrade method to the package manager that dnf implements to check
the distribution, only allow single-step updates, and sync to the target
release. On dom0, run the clone through the existing vmupdate qrexec
transport and show progress in the log. Clean up the clone and keep its
inherited features if it's a standalone VM.
@nihalxkumar
nihalxkumar force-pushed the qvm-template-upgrade branch from 8d7ce28 to 0a8f8ac Compare July 16, 2026 12:27
Comment thread vmupdate/agent/source/apt/apt_cli.py
Comment thread vmupdate/agent/source/apt/apt_cli.py Outdated
Comment thread vmupdate/agent/source/apt/apt_cli.py Outdated
Comment thread vmupdate/agent/source/apt/apt_cli.py Outdated

@parulin parulin 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.

I have some suggestion for better integration with the rest of the docs. If you have any question, feel free to ask.

This file is in doc/tools instead of doc/manpages, why?

Comment thread doc/tools/qvm-template-upgrade.rst
Comment thread doc/tools/qvm-template-upgrade.rst
Comment thread doc/tools/qvm-template-upgrade.rst
Comment thread doc/tools/qvm-template-upgrade.rst
Comment thread doc/tools/qvm-template-upgrade.rst
Comment thread doc/tools/qvm-template-upgrade.rst
Comment thread doc/tools/qvm-template-upgrade.rst
@parulin

parulin commented Jul 21, 2026

Copy link
Copy Markdown

As I understand, this tool should also be promoted in qubes-doc, in the user/templates/*/*-upgrade.rst files for fedora and debian.

@nihalxkumar

Copy link
Copy Markdown
Contributor Author

@parulin you are mistaking this repository as qubes-core-admin-client

Add Debian release upgrade support in APTCLI to upgrade templates to the
next release. Reuse the shared release-upgrade checks and reporting code
from the dnf implementation by moving them to PackageManager. Read
os-release once instead of multiple times, remove single-use helper
functions.
Add in-qube version upgrade path via DNFCLI, dom0 orchestrator, and
qrexec transport with streamed progress logging.
@nihalxkumar
nihalxkumar force-pushed the qvm-template-upgrade branch from 80e3d99 to c3f550e Compare July 23, 2026 21:04
@nihalxkumar

Copy link
Copy Markdown
Contributor Author

Debian upgrade end-to-end successful

https://gist.github.com/nihalxkumar/3f64e3a056e9ce367e9787d5bd94b9df
https://gist.github.com/nihalxkumar/212e885811589b85fdd23dafdfddf887

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.

[Contribution] qvm-upgrade-template (easy in-place upgrades for Debian and Fedora templates)

5 participants