Skip to content

Commit 4e03608

Browse files
committed
vmupdate: wait for full VM shutdown before next queued update
VMs with assigned PCI devices must be fully shut down before the next update starts, because the hypervisor holds resources until shutdown completes. Previously, qube_connection.py called vm.shutdown() and moved on immediately, leaving PCI resources held. Use shutdown_domains() from vmupdate.utils to issue a force shutdown and wait for the domain to finish. Regular VMs (no PCI devices) are unaffected and still use the fast non-blocking path. Add unit tests covering: - shutdown_domains is called and vm.shutdown is not for PCI VMs - vm.shutdown is called and shutdown_domains is not for non-PCI VMs - neither is called when the VM was already running before the update Fixes: QubesOS/qubes-issues#10617
1 parent 12e9e32 commit 4e03608

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# coding=utf-8
2+
#
3+
# The Qubes OS Project, https://www.qubes-os.org
4+
#
5+
# Copyright (C) 2025 Jayant Saxena <jayantmcom@gmail.com>
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License
9+
# as published by the Free Software Foundation; either version 2
10+
# of the License, or (at your option) any later version.
11+
#
12+
# This program is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with this program; if not, write to the Free Software
19+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20+
# USA.
21+
from unittest.mock import Mock, patch
22+
23+
from vmupdate.qube_connection import QubeConnection
24+
25+
26+
@patch("vmupdate.qube_connection.shutdown_domains")
27+
def test_wait_for_shutdown_when_vm_started_by_update(shutdown_domains):
28+
vm = Mock()
29+
vm.name = "hvm1"
30+
vm.is_running.side_effect = [False, True]
31+
vm.devices = {'pci': Mock()}
32+
vm.devices['pci'].get_assigned_devices.return_value = ["00_1f.2"]
33+
status_notifier = Mock()
34+
logger = Mock()
35+
36+
with QubeConnection(
37+
vm, "/tmp/qubes-update", cleanup=False, logger=logger,
38+
show_progress=False, status_notifier=status_notifier):
39+
pass
40+
41+
shutdown_domains.assert_called_once_with([vm], logger)
42+
vm.shutdown.assert_not_called()
43+
44+
45+
@patch("vmupdate.qube_connection.shutdown_domains")
46+
def test_do_not_wait_for_shutdown_without_assigned_pci(shutdown_domains):
47+
vm = Mock()
48+
vm.name = "hvm2"
49+
vm.is_running.side_effect = [False, True]
50+
vm.devices = {'pci': Mock()}
51+
vm.devices['pci'].get_assigned_devices.return_value = []
52+
status_notifier = Mock()
53+
logger = Mock()
54+
55+
with QubeConnection(
56+
vm, "/tmp/qubes-update", cleanup=False, logger=logger,
57+
show_progress=False, status_notifier=status_notifier):
58+
pass
59+
60+
vm.shutdown.assert_called_once_with()
61+
shutdown_domains.assert_not_called()
62+
63+
64+
@patch("vmupdate.qube_connection.shutdown_domains")
65+
def test_do_not_shutdown_if_vm_was_already_running(shutdown_domains):
66+
vm = Mock()
67+
vm.name = "hvm3"
68+
vm.is_running.return_value = True
69+
vm.devices = {'pci': Mock()}
70+
vm.devices['pci'].get_assigned_devices.return_value = ["00_1f.2"]
71+
status_notifier = Mock()
72+
logger = Mock()
73+
74+
with QubeConnection(
75+
vm, "/tmp/qubes-update", cleanup=False, logger=logger,
76+
show_progress=False, status_notifier=status_notifier):
77+
pass
78+
79+
vm.shutdown.assert_not_called()
80+
shutdown_domains.assert_not_called()

0 commit comments

Comments
 (0)