Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ const-rgx=(([A-Za-z_][A-Za-z0-9_]*)|(__.*__))$
class-rgx=([A-Z_][a-zA-Z0-9]+|TC_\d\d_[a-zA-Z0-9_]+)$

# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
function-rgx=[a-z_][a-z0-9_]{2,40}$

# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
method-rgx=[a-z_][a-z0-9_]{2,40}$

# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
Expand Down
2 changes: 1 addition & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ X-Python3-Version: >= 3.4
Package: qubes-manager
Architecture: any
Depends:
python3-qubesadmin (>= 4.3.13),
python3-qubesadmin (>= 4.3.33),
python3-qubesimgconverter,
python3-pyqt6,
python3-pyinotify,
Expand Down
4 changes: 2 additions & 2 deletions qubesmanager/bootfromdevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from PyQt6 import QtWidgets, QtGui, QtCore # pylint: disable=import-error
from qubesadmin import tools
from qubesadmin import exc
from qubesadmin.tools import qvm_start
from qubesadmin import utils as admin_utils

# this is needed for icons to actually work
# pylint: disable=unused-import, no-name-in-module
Expand Down Expand Up @@ -178,7 +178,7 @@ def main(args=None):
window = utils.run_synchronous(
functools.partial(VMBootFromDeviceWindow, vm))
if window.result() == 1 and window.cdrom_location is not None:
qvm_start.main(['--cdrom', window.cdrom_location, vm.name])
admin_utils.start_expert(domain=vm, drive="cdrom:" + window.cdrom_location)

if __name__ == "__main__":
main()
30 changes: 25 additions & 5 deletions qubesmanager/qube_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

from qubesadmin import exc
from qubesadmin import utils
from qubesadmin.tools import qvm_start

# pylint: disable=import-error
from PyQt6 import QtWidgets
Expand Down Expand Up @@ -696,6 +695,20 @@ def run(self):
self.msg = ("Error starting Qube!", str(ex))


# pylint: disable=too-few-public-methods
class ShutdownVMThread(common_threads.QubesThread):
def __init__(self, vm, force: bool = False, wait: bool = False):
super().__init__(vm)
self.force = force
self.wait_end = wait # wait() is callable, let's not interfere.

def run(self):
try:
self.vm.shutdown(force=self.force, wait=self.wait_end)
except exc.QubesException as ex:
self.msg = ("Error starting Qube!", str(ex))


# pylint: disable=too-few-public-methods
class UpdateVMsThread(common_threads.QubesThread):
def run(self):
Expand Down Expand Up @@ -1599,8 +1612,9 @@ def action_startvm_tools_install_triggered(self):
self.tr("'qubes-windows-tools' is not installed in dom0."))
for vm_info in self.get_selected_vms():
vm = vm_info.vm
qvm_start.main(['--cdrom',
'dom0:/usr/lib/qubes/qubes-windows-tools.iso', vm.name])
utils.start_expert(
domain=vm, drive="cdrom:dom0:/usr/lib/qubes/qubes-windows-tools.iso"
)

@pyqtSlot(name='on_action_pausevm_triggered')
def action_pausevm_triggered(self):
Expand Down Expand Up @@ -1660,9 +1674,15 @@ def shutdown_vm(self, vm, force=False, check_time=vm_restart_check_timeout,

force = True
for connected_vm in connected_vms:
connected_vm.shutdown(force=force)
thread = ShutdownVMThread(connected_vm, force=force)
self.threads_list.append(thread)
thread.finished.connect(self.clear_threads)
thread.start()

vm.shutdown(force=force)
thread = ShutdownVMThread(vm, force=force, wait=True)
self.threads_list.append(thread)
thread.finished.connect(self.clear_threads)
thread.start()
except exc.QubesException as ex:
QMessageBox.warning(
self,
Expand Down
5 changes: 3 additions & 2 deletions qubesmanager/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from qubesadmin import device_protocol
from qubesadmin.device_protocol import DeviceCategory
from qubesadmin import utils as admin_utils
from qubesadmin.tools import qvm_start
import qubesadmin.exc

from . import bootfromdevice
Expand Down Expand Up @@ -1393,7 +1392,9 @@ def boot_from_cdrom_button_pressed(self):
)
if boot_dialog.exec():
self.save_and_apply()
qvm_start.main(["--cdrom", boot_dialog.cdrom_location, self.vm.name])
admin_utils.start_expert(
domain=self.vm, drive="cdrom:" + boot_dialog.cdrom_location
)

def virt_mode_changed(self, new_idx): # pylint: disable=unused-argument
self.update_pv_warning()
Expand Down
46 changes: 37 additions & 9 deletions qubesmanager/tests/test_qube_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,11 +441,13 @@ def test_209_shutdownvm(mock_monitor, mock_timer, _mock_question,
assert qubes_manager.action_shutdownvm.isEnabled()
vm = qubes_manager.qubes_app.domains['test-blue']

with mock.patch.object(vm, 'shutdown') as mock_shutdown:
qubes_manager.action_shutdownvm.trigger()
mock_shutdown.assert_called_once_with(force=False)
mock_monitor.assert_called_once_with(vm, mock.ANY, mock.ANY, mock.ANY)
mock_timer.assert_called_once_with(mock.ANY, mock.ANY)
expected_shutdown = ('test-blue', 'admin.vm.Shutdown', 'wait', None)
assert expected_shutdown not in qubes_manager.qubes_app.actual_calls
qubes_manager.qubes_app.expected_calls[expected_shutdown] = b'0\x00'

qubes_manager.action_shutdownvm.trigger()
mock_monitor.assert_called_once_with(vm, mock.ANY, mock.ANY, mock.ANY)
mock_timer.assert_called_once_with(mock.ANY, mock.ANY)


def test_211_remove_adminvm(qubes_manager):
Expand Down Expand Up @@ -508,10 +510,12 @@ def test_214_restartvm(_msgbox, mock_monitor, _qtimer, qubes_manager):
assert qubes_manager.action_restartvm.isEnabled()
vm = qubes_manager.qubes_app.domains['test-blue']

with mock.patch.object(vm, 'shutdown') as mock_shutdown:
qubes_manager.action_restartvm.trigger()
mock_shutdown.assert_called_once_with(force=True)
mock_monitor.assert_called_once_with(vm, 1000, True, mock.ANY)
expected_shutdown = ('test-blue', 'admin.vm.Shutdown', 'force+wait', None)
assert expected_shutdown not in qubes_manager.qubes_app.actual_calls
qubes_manager.qubes_app.expected_calls[expected_shutdown] = b'0\x00'

qubes_manager.action_restartvm.trigger()
mock_monitor.assert_called_once_with(vm, 1000, True, mock.ANY)


@mock.patch('qubesmanager.qube_manager.UpdateVMsThread')
Expand Down Expand Up @@ -1450,6 +1454,30 @@ def test_602_startvm_thread_error():
assert thread.msg is not None


def test_603_shutdownvm_thread():
vm = mock.Mock(spec=['shutdown'])

thread = qube_manager.ShutdownVMThread(vm)
thread.run()
vm.shutdown.assert_called_once_with(force=False, wait=False)
vm.shutdown.reset_mock()

thread = qube_manager.ShutdownVMThread(vm, force=True, wait=True)
thread.run()
vm.shutdown.assert_called_once_with(force=True, wait=True)


def test_604_shutdownvm_thread_error():
vm = mock.Mock(
spec=['shutdown'],
**{'shutdown.side_effect': exc.QubesException('Error')})

thread = qube_manager.ShutdownVMThread(vm)
thread.run()

assert thread.msg is not None


def test_610_run_command_thread():
vm = mock.Mock(spec=['run'])

Expand Down
4 changes: 2 additions & 2 deletions qubesmanager/tests/test_vm_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,7 @@ def test_211_virtmode(settings_fixture):
assert expected_call in settings_window.qubesapp.actual_calls


@mock.patch("qubesadmin.tools.qvm_start.main")
@mock.patch("qubesadmin.utils.start_expert")
@mock.patch("qubesmanager.bootfromdevice.VMBootFromDeviceWindow")
@check_errors
@pytest.mark.parametrize("settings_fixture", TEST_VMS, indirect=True)
Expand All @@ -1120,7 +1120,7 @@ def test_212_boot_from_device(mock_boot, mock_start, settings_fixture):
parent=settings_window,
)

mock_start.assert_called_with(["--cdrom", mock.ANY, vm.name])
mock_start.assert_called_with(domain=vm, drive=mock.ANY)


@check_errors
Expand Down
2 changes: 1 addition & 1 deletion rpm_spec/qmgr.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ URL: https://www.qubes-os.org
Requires: python%{python3_pkgversion}
Requires: python%{python3_pkgversion}-pyqt6
Requires: python%{python3_pkgversion}-inotify
Requires: python%{python3_pkgversion}-qubesadmin >= 4.3.13
Requires: python%{python3_pkgversion}-qubesadmin >= 4.3.33
Requires: python%{python3_pkgversion}-qubesimgconverter
Requires: python%{python3_pkgversion}-qasync
Requires: python%{python3_pkgversion}-pyxdg
Expand Down