diff --git a/.pylintrc b/.pylintrc
index eaa3a43a1..9b5ca6970 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,6 +1,6 @@
[MASTER]
persistent=no
-ignore-paths=qubes/tests
+ignore-paths=^qubes/tests/(?!integ/(dispvm|network).*$)(?!(app|api|ext|vm/(adminvm|appvm|dispvm|mix/(dvmtemplate|net))).py$).*
init-hook="import astroid.bases; astroid.bases.POSSIBLE_PROPERTIES.add('stateless_property')"
[MESSAGES CONTROL]
@@ -61,10 +61,10 @@ class-rgx=([A-Z_][a-zA-Z0-9]+|TC_\d\d_[a-zA-Z0-9_]+)$
function-rgx=(test_[0-9]{3}_[a-z0-9_]{2,50}|[a-z_][a-z0-9_]{2,50})$
# Regular expression which should only match correct method names
-method-rgx=(test_[0-9]{3}_[a-z0-9_]{2,50}|[a-z_][a-z0-9_]{2,50})$
+method-rgx=(setUp|setUpClass|tearDown|tearDownClass|test_[0-9]{3}_[a-z0-9_]{2,50}|[a-z_][a-z0-9_]{2,50})$
# Regular expression which should only match correct instance attribute names
-attr-rgx=[a-z_][a-z0-9_]{2,40}$
+attr-rgx=(maxDiff|[a-z_][a-z0-9_]{2,40})$
# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,40}$
@@ -198,6 +198,6 @@ max-positional-arguments=7
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
-overgeneral-exceptions=Exception,EnvironmentError
+overgeneral-exceptions=builtins.Exception,builtins.EnvironmentError
# vim: ft=conf
diff --git a/qubes/ext/utils.py b/qubes/ext/utils.py
index c0c89d993..832d2ba7d 100644
--- a/qubes/ext/utils.py
+++ b/qubes/ext/utils.py
@@ -169,7 +169,7 @@ def compare_device_cache(vm, devices_cache, current_devices):
async def confirm_device_attachment(device, frontends) -> str:
try:
return await _do_confirm_device_attachment(device, frontends)
- except Exception as exc:
+ except Exception as exc: # pylint: disable=broad-exception-caught
print(str(exc.__class__.__name__) + ":", str(exc), file=sys.stderr)
return ""
diff --git a/qubes/qmemman/systemstate.py b/qubes/qmemman/systemstate.py
index 8a8abe3a1..baf4585ab 100644
--- a/qubes/qmemman/systemstate.py
+++ b/qubes/qmemman/systemstate.py
@@ -183,7 +183,7 @@ def mem_set(self, domid, val) -> None:
int(domid), int(val / 1024) + 1024
) # LIBXL_MAXMEM_CONSTANT=1024
self.xc.domain_set_target_mem(int(domid), int(val / 1024))
- except Exception:
+ except Exception: # pylint: disable=broad-exception-caught
pass
# VM sees about 16MB memory less, so adjust for it here - qmemman
# handle Xen view of memory
diff --git a/qubes/rngdoc.py b/qubes/rngdoc.py
index 2a808b060..ae4f6622b 100755
--- a/qubes/rngdoc.py
+++ b/qubes/rngdoc.py
@@ -121,7 +121,7 @@ def get_child_elements(self):
number = "\\+"
else:
print(parent.tag)
- raise Exception(
+ raise ValueError(
f"Cannot choose number format for tag {parent.tag}"
)
diff --git a/qubes/tests/api.py b/qubes/tests/api.py
index 80c6be18c..228fde64f 100644
--- a/qubes/tests/api.py
+++ b/qubes/tests/api.py
@@ -27,7 +27,7 @@
import qubes.tests
-class TestMgmt(object):
+class TestMgmt:
def __init__(self, app, src, method, dest, arg, send_event=None):
self.app = app
self.src = src
@@ -35,6 +35,7 @@ def __init__(self, app, src, method, dest, arg, send_event=None):
self.dest = dest
self.arg = arg
self.send_event = send_event
+ self.task = None
try:
self.function = {
"mgmt.success": self.success,
@@ -67,12 +68,14 @@ async def qubesexception(self, untrusted_payload):
raise qubes.exc.QubesException("qubes-exception")
async def exception(self, untrusted_payload):
+ # pylint: disable=broad-exception-raised
raise Exception("exception")
async def event(self, untrusted_payload):
future = asyncio.get_event_loop().create_future()
class Subject:
+ # pylint: disable=too-few-public-methods
name = "subject"
def __str__(self):
@@ -93,7 +96,7 @@ def __str__(self):
class TC_00_QubesDaemonProtocol(qubes.tests.QubesTestCase):
def setUp(self):
- super(TC_00_QubesDaemonProtocol, self).setUp()
+ super().setUp()
self.app = unittest.mock.Mock()
self.app.log = self.log
self.sock_client, self.sock_server = socket.socketpair()
@@ -116,7 +119,7 @@ def tearDown(self):
except AttributeError: # old python in travis
pass
self.transport.close()
- super(TC_00_QubesDaemonProtocol, self).tearDown()
+ super().tearDown()
def test_000_message_ok(self):
self.writer.write(b"mgmt.success+arg src name dest\0payload")
diff --git a/qubes/tests/api_internal.py b/qubes/tests/api_internal.py
index 86a512f48..48cbfd844 100644
--- a/qubes/tests/api_internal.py
+++ b/qubes/tests/api_internal.py
@@ -26,9 +26,9 @@
import uuid
-def mock_coro(f):
+def mock_coro(coro):
async def coro_f(*args, **kwargs):
- return f(*args, **kwargs)
+ return coro(*args, **kwargs)
return coro_f
diff --git a/qubes/tests/api_misc.py b/qubes/tests/api_misc.py
index cf885abcd..fafeb4ecf 100644
--- a/qubes/tests/api_misc.py
+++ b/qubes/tests/api_misc.py
@@ -30,7 +30,7 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
maxDiff = None
def setUp(self):
- super(TC_00_API_Misc, self).setUp()
+ super().setUp()
self.tpl = mock.NonCallableMagicMock(name="template")
del self.tpl.template
self.async_src = mock.AsyncMock()
@@ -393,9 +393,9 @@ def test_027_notify_updates_template_based_template_running(self):
self.assertEqual(self.app.mock_calls, [])
def test_028_notify_updates_template_based_dispvm(self):
- self.dvm = self.src
- self.dvm.updateable = False
- self.srv = mock.NonCallableMagicMock(template=self.dvm)
+ dvm = self.src
+ dvm.updateable = False
+ _srv = mock.NonCallableMagicMock(template=dvm)
self.src.updateable = False
self.src.features.get.return_value = False
self.src.template.is_running.return_value = False
@@ -424,7 +424,7 @@ def test_028_notify_updates_template_based_dispvm(self):
+ "\nExpected:\n"
+ str(expected).replace(",", ",\n"),
)
- self.assertEqual(self.dvm.mock_calls, [])
+ self.assertEqual(dvm.mock_calls, [])
self.assertIsInstance(self.src.updates_available, mock.Mock)
self.assertEqual(self.app.mock_calls, [mock.call.save()])
diff --git a/qubes/tests/app.py b/qubes/tests/app.py
index bbea5920b..a3ab4a4b7 100644
--- a/qubes/tests/app.py
+++ b/qubes/tests/app.py
@@ -21,8 +21,7 @@
#
import os
-import unittest
-import unittest.mock as mock
+from unittest import mock
import lxml.etree
@@ -139,7 +138,7 @@ class TC_20_QubesHost(qubes.tests.QubesTestCase):
]
def setUp(self):
- super(TC_20_QubesHost, self).setUp()
+ super().setUp()
self.app = TestApp()
self.app.vmm = mock.Mock()
self.qubes_host = qubes.app.QubesHost(self.app)
@@ -234,7 +233,7 @@ def test_002_get_vm_stats_one_vm(self):
vm.xid = 1
vm.name = "somevm"
- info_time, info = self.qubes_host.get_vm_stats(only_vm=vm)
+ info_time, _info = self.qubes_host.get_vm_stats(only_vm=vm)
self.assertIsNotNone(info_time)
self.assertEqual(
self.app.vmm.mock_calls,
@@ -491,7 +490,7 @@ class TC_89_QubesEmpty(qubes.tests.QubesTestCase):
def tearDown(self):
try:
os.unlink("/tmp/qubestest.xml")
- except:
+ except: # pylint: disable=bare-except
pass
try:
self.app.close()
@@ -558,93 +557,85 @@ def test_100_property_migrate_default_fw_netvm(self):
"""
with self.subTest("default_setup"):
- with open("/tmp/qubestest.xml", "w") as xml_file:
+ with open("/tmp/qubestest.xml", "w", encoding="ascii") as xml_file:
xml_file.write(
xml_template.format(
default_netvm="sys-firewall", default_fw_netvm="sys-net"
)
)
- self.app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
- self.assertEqual(self.app.domains["sys-net"].netvm, None)
+ app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
+ self.assertEqual(app.domains["sys-net"].netvm, None)
self.assertEqual(
- self.app.domains["sys-firewall"].netvm,
- self.app.domains["sys-net"],
+ app.domains["sys-firewall"].netvm,
+ app.domains["sys-net"],
)
# property is no longer "default"
self.assertFalse(
- self.app.domains["sys-firewall"].property_is_default("netvm")
+ app.domains["sys-firewall"].property_is_default("netvm")
)
# verify that appvm.netvm is unaffected
- self.assertTrue(
- self.app.domains["appvm"].property_is_default("netvm")
- )
+ self.assertTrue(app.domains["appvm"].property_is_default("netvm"))
self.assertEqual(
- self.app.domains["appvm"].netvm,
- self.app.domains["sys-firewall"],
+ app.domains["appvm"].netvm,
+ app.domains["sys-firewall"],
)
with self.assertRaises(AttributeError):
- self.app.default_fw_netvm
+ app.default_fw_netvm # pylint: disable=no-member
- self.app.close()
- del self.app
+ app.close()
+ del app
with self.subTest("same"):
- with open("/tmp/qubestest.xml", "w") as xml_file:
+ with open("/tmp/qubestest.xml", "w", encoding="ascii") as xml_file:
xml_file.write(
xml_template.format(
default_netvm="sys-net", default_fw_netvm="sys-net"
)
)
- self.app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
- self.assertEqual(self.app.domains["sys-net"].netvm, None)
+ app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
+ self.assertEqual(app.domains["sys-net"].netvm, None)
self.assertEqual(
- self.app.domains["sys-firewall"].netvm,
- self.app.domains["sys-net"],
+ app.domains["sys-firewall"].netvm,
+ app.domains["sys-net"],
)
self.assertTrue(
- self.app.domains["sys-firewall"].property_is_default("netvm")
+ app.domains["sys-firewall"].property_is_default("netvm")
)
# verify that appvm.netvm is unaffected
- self.assertTrue(
- self.app.domains["appvm"].property_is_default("netvm")
- )
- self.assertEqual(
- self.app.domains["appvm"].netvm, self.app.domains["sys-net"]
- )
+ self.assertTrue(app.domains["appvm"].property_is_default("netvm"))
+ self.assertEqual(app.domains["appvm"].netvm, app.domains["sys-net"])
with self.assertRaises(AttributeError):
- self.app.default_fw_netvm
+ app.default_fw_netvm # pylint: disable=no-member
- self.app.close()
- del self.app
+ app.close()
+ del app
with self.subTest("loop"):
- with open("/tmp/qubestest.xml", "w") as xml_file:
+ with open("/tmp/qubestest.xml", "w", encoding="ascii") as xml_file:
xml_file.write(
xml_template.format(
default_netvm="sys-firewall",
default_fw_netvm="sys-firewall",
)
)
- self.app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
- self.assertEqual(self.app.domains["sys-net"].netvm, None)
+ app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
+ self.assertEqual(app.domains["sys-net"].netvm, None)
# this was netvm loop, better set to none, to not crash qubesd
- self.assertEqual(self.app.domains["sys-firewall"].netvm, None)
+ self.assertEqual(app.domains["sys-firewall"].netvm, None)
self.assertFalse(
- self.app.domains["sys-firewall"].property_is_default("netvm")
+ app.domains["sys-firewall"].property_is_default("netvm")
)
# verify that appvm.netvm is unaffected
- self.assertTrue(
- self.app.domains["appvm"].property_is_default("netvm")
- )
+ self.assertTrue(app.domains["appvm"].property_is_default("netvm"))
self.assertEqual(
- self.app.domains["appvm"].netvm,
- self.app.domains["sys-firewall"],
+ app.domains["appvm"].netvm,
+ app.domains["sys-firewall"],
)
with self.assertRaises(AttributeError):
- self.app.default_fw_netvm
+ app.default_fw_netvm # pylint: disable=no-member
- self.app.close()
- del self.app
+ app.close()
+ del app
def test_101_property_migrate_label(self):
xml_template = """
@@ -672,25 +663,25 @@ def test_101_property_migrate_label(self):
"""
with self.subTest("replace_label"):
- with open("/tmp/qubestest.xml", "w") as xml_file:
+ with open("/tmp/qubestest.xml", "w", encoding="ascii") as xml_file:
xml_file.write(xml_template.format(old_gray="0x555753"))
- self.app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
- self.assertEqual(self.app.get_label("gray").color, "0x555555")
- self.app.close()
- del self.app
+ app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
+ self.assertEqual(app.get_label("gray").color, "0x555555")
+ app.close()
+ del app
with self.subTest("dont_replace_label"):
- with open("/tmp/qubestest.xml", "w") as xml_file:
+ with open("/tmp/qubestest.xml", "w", encoding="ascii") as xml_file:
xml_file.write(xml_template.format(old_gray="0x123456"))
- self.app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
- self.assertEqual(self.app.get_label("gray").color, "0x123456")
- self.app.close()
- del self.app
+ app = qubes.Qubes("/tmp/qubestest.xml", offline_mode=True)
+ self.assertEqual(app.get_label("gray").color, "0x123456")
+ app.close()
+ del app
class TC_90_Qubes(qubes.tests.QubesTestCase):
def setUp(self):
- super(TC_90_Qubes, self).setUp()
+ super().setUp()
self.app = qubes.Qubes(
"/tmp/qubestest.xml", load=False, offline_mode=True
)
@@ -725,7 +716,7 @@ def setUp(self):
def tearDown(self):
try:
os.unlink("/tmp/qubestest.xml")
- except:
+ except: # pylint: disable=bare-except
pass
del self.emitter
super().tearDown()
@@ -813,7 +804,7 @@ class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
def test_113_guivm(self):
class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
- guivm = qubes.property("guivm", default=(lambda self: "dom0"))
+ guivm = qubes.property("guivm", default=lambda self: "dom0")
holder = MyTestHolder(None)
guivm = self.app.add_new_vm(
@@ -893,7 +884,7 @@ class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
def test_115_audiovm(self):
class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
- audiovm = qubes.property("audiovm", default=(lambda self: "dom0"))
+ audiovm = qubes.property("audiovm", default=lambda self: "dom0")
holder = MyTestHolder(None)
audiovm = self.app.add_new_vm(
@@ -968,7 +959,7 @@ def test_117_remotevm_status(self):
remotevm1.get_mem(),
] == ["Running", 0, 0]
- @unittest.mock.patch("qubes.vm.qubesvm.QubesVM.untrusted_qdb")
+ @mock.patch("qubes.vm.qubesvm.QubesVM.untrusted_qdb")
def test_118_remotevm_set_relayvm(self, mock_qubesdb):
class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
relayvm = qubes.property("relayvm")
@@ -1020,7 +1011,7 @@ class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
)
def test_200_remove_template(self):
- appvm = self.app.add_new_vm(
+ self.app.add_new_vm(
"AppVM", name="test-vm", template=self.template, label="red"
)
with mock.patch.object(self.app, "vmm"):
@@ -1070,7 +1061,7 @@ def test_204_remove_appvm_dispvm(self):
dispvm = self.app.add_new_vm(
"AppVM", name="test-appvm", template=self.template, label="red"
)
- appvm = self.app.add_new_vm(
+ self.app.add_new_vm(
"AppVM",
name="test-appvm2",
template=self.template,
@@ -1089,7 +1080,7 @@ def test_205_remove_appvm_dispvm(self):
template_for_dispvms=True,
label="red",
)
- dispvm = self.app.add_new_vm(
+ self.app.add_new_vm(
"DispVM", name="test-dispvm", template=appvm, label="red"
)
with mock.patch.object(self.app, "vmm"):
@@ -1267,9 +1258,7 @@ def test_303_preload_default_dispvm_change_partial_noop(self):
@qubes.tests.skipUnlessGit
def test_900_example_xml_in_doc(self):
- self.assertXMLIsValid(
- lxml.etree.parse(
- open(os.path.join(qubes.tests.in_git, "doc/example.xml"), "rb")
- ),
- "qubes.rng",
- )
+ path = os.path.join(qubes.tests.in_git, "doc/example.xml")
+ with open(path, "rb") as file:
+ tree = lxml.etree.parse(file)
+ self.assertXMLIsValid(tree, "qubes.rng")
diff --git a/qubes/tests/ext.py b/qubes/tests/ext.py
index 7187dc99e..e98010c91 100644
--- a/qubes/tests/ext.py
+++ b/qubes/tests/ext.py
@@ -19,7 +19,8 @@
# License along with this library; if not, see .
import os
-import unittest.mock
+
+from unittest import mock
import qubes.ext.admin
import qubes.ext.audio
@@ -32,8 +33,6 @@
import qubes.tests
import qubes.vm.qubesvm
-from unittest import mock
-
class TC_00_CoreFeatures(qubes.tests.QubesTestCase):
maxDiff = None
@@ -233,7 +232,7 @@ def test_017_notify_tools_template_based(self):
("template.__bool__", (), {}),
(
"log.warning",
- ("Ignoring qubes.NotifyTools for template-based " "VM",),
+ ("Ignoring qubes.NotifyTools for template-based VM",),
{},
),
],
@@ -430,8 +429,8 @@ def test_032_distro_meta_invalid(self):
("os-distribution-like", "debian"),
{},
),
- ("log.warning", unittest.mock.ANY, {}),
- ("log.warning", unittest.mock.ANY, {}),
+ ("log.warning", mock.ANY, {}),
+ ("log.warning", mock.ANY, {}),
("features.items", (), {}),
("features.get", ("qrexec", False), {}),
],
@@ -462,8 +461,8 @@ def test_033_distro_meta_invalid2(self):
("os-distribution-like", "debian"),
{},
),
- ("log.warning", unittest.mock.ANY, {}),
- ("log.warning", unittest.mock.ANY, {}),
+ ("log.warning", mock.ANY, {}),
+ ("log.warning", mock.ANY, {}),
("features.items", (), {}),
("features.get", ("qrexec", False), {}),
],
@@ -1698,19 +1697,18 @@ def setUp(self):
self.vm.configure_mock(
**{
"features.get.side_effect": self.features.get,
- "features.check_with_template.side_effect": self.mock_check_with_template,
+ "features.check_with_template.side_effect": self.mock_check_tpl,
"features.__contains__.side_effect": self.features.__contains__,
"features.__setitem__.side_effect": self.features.__setitem__,
}
)
- def mock_check_with_template(self, name, default):
+ def mock_check_tpl(self, name, default):
if hasattr(self.vm, "template"):
return self.features.get(
name, self.template_features.get(name, default)
)
- else:
- return self.features.get(name, default)
+ return self.features.get(name, default)
def test_000_notify_tools_full(self):
del self.vm.template
@@ -1963,14 +1961,14 @@ def test_012_supported_services_remove(self):
)
def test_013_feature_set_dom0(self):
- self.test_base_dir = "/tmp/qubes-test-dir"
- self.base_dir_patch = mock.patch.dict(
- qubes.config.system_path, {"dom0_services_dir": self.test_base_dir}
+ test_base_dir = "/tmp/qubes-test-dir"
+ base_dir_patch = mock.patch.dict(
+ qubes.config.system_path, {"dom0_services_dir": test_base_dir}
)
- self.base_dir_patch.start()
- self.addCleanup(self.base_dir_patch.stop)
+ base_dir_patch.start()
+ self.addCleanup(base_dir_patch.stop)
service = "guivm-gui-agent"
- service_path = self.test_base_dir + "/" + service
+ service_path = test_base_dir + "/" + service
self.ext.on_domain_feature_set(
self.dom0,
@@ -1981,14 +1979,14 @@ def test_013_feature_set_dom0(self):
self.assertEqual(os.path.exists(service_path), True)
def test_014_feature_delete_dom0(self):
- self.test_base_dir = "/tmp/qubes-test-dir"
- self.base_dir_patch = mock.patch.dict(
- qubes.config.system_path, {"dom0_services_dir": self.test_base_dir}
+ test_base_dir = "/tmp/qubes-test-dir"
+ base_dir_patch = mock.patch.dict(
+ qubes.config.system_path, {"dom0_services_dir": test_base_dir}
)
- self.base_dir_patch.start()
- self.addCleanup(self.base_dir_patch.stop)
+ base_dir_patch.start()
+ self.addCleanup(base_dir_patch.stop)
service = "guivm-gui-agent"
- service_path = self.test_base_dir + "/" + service
+ service_path = test_base_dir + "/" + service
self.ext.on_domain_feature_set(
self.dom0,
@@ -2006,14 +2004,14 @@ def test_014_feature_delete_dom0(self):
self.assertEqual(os.path.exists(service_path), False)
def test_014_feature_set_empty_value_dom0(self):
- self.test_base_dir = "/tmp/qubes-test-dir"
- self.base_dir_patch = mock.patch.dict(
- qubes.config.system_path, {"dom0_services_dir": self.test_base_dir}
+ test_base_dir = "/tmp/qubes-test-dir"
+ base_dir_patch = mock.patch.dict(
+ qubes.config.system_path, {"dom0_services_dir": test_base_dir}
)
- self.base_dir_patch.start()
- self.addCleanup(self.base_dir_patch.stop)
+ base_dir_patch.start()
+ self.addCleanup(base_dir_patch.stop)
service = "guivm-gui-agent"
- service_path = self.test_base_dir + "/" + service
+ service_path = test_base_dir + "/" + service
self.ext.on_domain_feature_set(
self.dom0,
@@ -2631,11 +2629,9 @@ def test_000_shutdown(self):
)
def test_000_shutdown_used(self):
- with unittest.mock.patch.object(
+ with mock.patch.object(
self.ext, "attached_vms", return_value=[self.client]
- ), unittest.mock.patch.object(
- self.client, "is_running", return_value=True
- ):
+ ), mock.patch.object(self.client, "is_running", return_value=True):
self.client.is_preload = False
with self.assertRaises(qubes.exc.QubesVMInUseError):
self.ext.on_domain_pre_shutdown(
@@ -2650,13 +2646,13 @@ def test_000_shutdown_used(self):
)
def test_000_shutdown_used_by_some(self):
- with unittest.mock.patch.object(
+ with mock.patch.object(
self.ext,
"attached_vms",
return_value=[self.client, self.client_alt],
- ), unittest.mock.patch.object(
+ ), mock.patch.object(
self.client, "is_running", return_value=True
- ), unittest.mock.patch.object(
+ ), mock.patch.object(
self.client_alt, "is_running", return_value=True
):
self.client.is_preload = False
diff --git a/qubes/tests/integ/dispvm.py b/qubes/tests/integ/dispvm.py
index 62892e898..2ac559b03 100644
--- a/qubes/tests/integ/dispvm.py
+++ b/qubes/tests/integ/dispvm.py
@@ -26,7 +26,7 @@
import time
import unittest
from contextlib import suppress
-from distutils import spawn
+from shutil import which
from unittest.mock import patch, mock_open
import asyncio
import sys
@@ -34,7 +34,6 @@
import qubes.config
import qubes.tests
-import qubesadmin.exc
# nose will duplicate this logger.
logger = logging.getLogger(__name__)
@@ -49,7 +48,7 @@
class TC_04_DispVM(qubes.tests.SystemTestCase):
def setUp(self):
- super(TC_04_DispVM, self).setUp()
+ super().setUp()
self.init_default_template()
self.disp_base = self.app.add_new_vm(
qubes.vm.appvm.AppVM,
@@ -73,7 +72,7 @@ def setUp(self):
def tearDown(self):
self.app.default_dispvm = None
- super(TC_04_DispVM, self).tearDown()
+ super().tearDown()
def wait_for_dispvm_destroy(self, dispvm_name: list):
timeout = 20
@@ -138,6 +137,7 @@ def test_003_cleanup_destroyed(self):
self.assertNotIn(dispvm_name, self.app.domains)
def _count_dispvms(self, *args, **kwargs):
+ # pylint: disable=unused-argument
self.startup_counter += 1
def test_010_failed_start(self):
@@ -195,10 +195,10 @@ def test_011_failed_start_timeout(self):
self.assertEqual(self.startup_counter, 1)
-class TC_20_DispVMMixin(object):
+class TC_20_DispVMMixin:
def setUp(self): # pylint: disable=invalid-name
logger.info("start")
- super(TC_20_DispVMMixin, self).setUp()
+ super().setUp()
if "whonix-g" in self.template:
self.skipTest(
"whonix gateway is not supported as DisposableVM Template"
@@ -227,11 +227,11 @@ def setUp(self): # pylint: disable=invalid-name
self.start_vm(self.disp_base),
self.start_vm(self.disp_base_alt),
]
+ self.loop.run_until_complete(asyncio.gather(*start_tasks))
shutdown_tasks = [
self.disp_base.shutdown(wait=True),
self.disp_base_alt.shutdown(wait=True),
]
- self.loop.run_until_complete(asyncio.gather(*start_tasks))
self.loop.run_until_complete(asyncio.gather(*shutdown_tasks))
# Setting "default_dispvm" fires the preload event before patches of
# each test function is applied.
@@ -259,7 +259,7 @@ def tearDown(self): # pylint: disable=invalid-name
if "_preload_" not in self._testMethodName:
self.app.default_dispvm = None
self.app.save()
- super(TC_20_DispVMMixin, self).tearDown()
+ super().tearDown()
logger.info("end")
def _test_event_handler(
@@ -787,9 +787,7 @@ async def _test_019_preload_refresh(self):
self.log_preload()
logger.info("end")
- @unittest.skipUnless(
- spawn.find_executable("xdotool"), "xdotool not installed"
- )
+ @unittest.skipUnless(which("xdotool"), "xdotool not installed")
def test_020_gui_app(self):
dispvm = self.loop.run_until_complete(
qubes.vm.dispvm.DispVM.from_appvm(self.disp_base)
@@ -807,8 +805,8 @@ def test_020_gui_app(self):
# wait for DispVM startup:
p.stdin.write(b"echo test\n")
self.loop.run_until_complete(p.stdin.drain())
- l = self.loop.run_until_complete(p.stdout.readline())
- self.assertEqual(l, b"test\n")
+ line = self.loop.run_until_complete(p.stdout.readline())
+ self.assertEqual(line, b"test\n")
self.assertTrue(dispvm.is_running())
try:
@@ -849,9 +847,10 @@ def test_020_gui_app(self):
)
def _handle_editor(self, winid, copy=False):
- (window_title, _) = subprocess.Popen(
+ with subprocess.Popen(
["xdotool", "getwindowname", winid], stdout=subprocess.PIPE
- ).communicate()
+ ) as proc:
+ (window_title, _) = proc.communicate()
window_title = (
window_title.decode()
.strip()
@@ -861,7 +860,7 @@ def _handle_editor(self, winid, copy=False):
time.sleep(1)
if "LibreOffice" in window_title:
# wait for actual editor (we've got splash screen)
- search = subprocess.Popen(
+ with subprocess.Popen(
[
"xdotool",
"search",
@@ -873,11 +872,11 @@ def _handle_editor(self, winid, copy=False):
"disp*|Writer",
],
stdout=subprocess.PIPE,
- stderr=open(os.path.devnull, "w"),
- )
- retcode = search.wait()
- if retcode == 0:
- winid = search.stdout.read().strip()
+ stderr=subprocess.DEVNULL,
+ ) as search:
+ retcode = search.wait()
+ if retcode == 0:
+ winid = search.stdout.read().strip()
time.sleep(0.5)
subprocess.check_call(
["xdotool", "windowactivate", "--sync", winid]
@@ -929,24 +928,25 @@ def _handle_editor(self, winid, copy=False):
)
if copy:
raise NotImplementedError("copy not implemented for vim")
- else:
- subprocess.check_call(
- ["xdotool", "key", "i", "type", "Test test 2"]
- )
- subprocess.check_call(
- ["xdotool", "key", "--window", winid, "key", "Return"]
- )
- subprocess.check_call(
- ["xdotool", "key", "Escape", "colon", "w", "q", "Return"]
- )
- elif (
- "gedit" in window_title
- or "KWrite" in window_title
- or "Mousepad" in window_title
- or "Geany" in window_title
- or "Text Editor" in window_title
- # FeatherPad (default in Whonix 18), no app name in the title...
- or "test.txt" in window_title
+ subprocess.check_call(
+ ["xdotool", "key", "i", "type", "Test test 2"]
+ )
+ subprocess.check_call(
+ ["xdotool", "key", "--window", winid, "key", "Return"]
+ )
+ subprocess.check_call(
+ ["xdotool", "key", "Escape", "colon", "w", "q", "Return"]
+ )
+ elif any(
+ e in window_title
+ for e in (
+ "gedit",
+ "KWrite",
+ "Mousepad",
+ "Geany",
+ "Text Editor",
+ "test.txt",
+ )
):
subprocess.check_call(
["xdotool", "windowactivate", "--sync", winid]
@@ -984,7 +984,7 @@ def _whonix_ws_dispvm_confirm(self, action_str):
include_tray=False,
timeout=5,
)
- except Exception:
+ except Exception: # pylint: disable=broad-exception-caught
return (
False,
"Failed to find qrexec confirmation window for "
@@ -1011,9 +1011,7 @@ def _whonix_ws_dispvm_confirm(self, action_str):
)
return (True, "")
- @unittest.skipUnless(
- spawn.find_executable("xdotool"), "xdotool not installed"
- )
+ @unittest.skipUnless(which("xdotool"), "xdotool not installed")
def test_030_edit_file(self):
self.testvm1 = self.app.add_new_vm(
qubes.vm.appvm.AppVM,
@@ -1052,18 +1050,17 @@ def test_030_edit_file(self):
include_tray=False,
timeout=60,
)
- except Exception as e:
+ except Exception as e: # pylint: disable=broad-exception-caught
try:
self.loop.run_until_complete(asyncio.wait_for(p.wait(), 1))
except asyncio.TimeoutError:
raise e
- else:
- stdout = self.loop.run_until_complete(p.stdout.read())
- self.fail(
- "qvm-open-in-dvm exited prematurely with {}: {}".format(
- p.returncode, stdout
- )
+ stdout = self.loop.run_until_complete(p.stdout.read())
+ self.fail(
+ "qvm-open-in-dvm exited prematurely with {}: {}".format(
+ p.returncode, stdout
)
+ )
# let the application initialize
self.loop.run_until_complete(asyncio.sleep(1))
try:
@@ -1106,8 +1103,8 @@ def _get_open_script(self, application):
"/usr/share/qubes/tests-data/"
"dispvm-open-thunderbird-attachment",
"rb",
- ) as f:
- return f.read()
+ ) as file:
+ return file.read()
assert False
def _get_apps_list(self, template):
@@ -1130,9 +1127,7 @@ def _get_apps_list(self, template):
if l.endswith(".desktop")
]
- @unittest.skipUnless(
- spawn.find_executable("xdotool"), "xdotool not installed"
- )
+ @unittest.skipUnless(which("xdotool"), "xdotool not installed")
def test_100_open_in_dispvm(self):
if "whonix-w" in self.template:
self.skipTest(
@@ -1197,7 +1192,7 @@ def test_100_open_in_dispvm(self):
self.loop.run_until_complete(asyncio.sleep(3))
try:
- click_to_open = self.loop.run_until_complete(
+ self.loop.run_until_complete(
self.testvm1.run_for_stdio(
"./open-file test.txt",
stdout=subprocess.PIPE,
@@ -1211,7 +1206,7 @@ def test_100_open_in_dispvm(self):
self.skipTest("{} not installed".format(app_id))
self.fail(
"'./open-file test.txt' failed with {}: {}{}".format(
- err.cmd, err.returncode, err.stdout, err.stderr
+ err.returncode, err.stdout, err.stderr
)
)
@@ -1241,8 +1236,8 @@ def test_100_open_in_dispvm(self):
self.wait_for_window_hide_coro("editor", winid)
)
- with open("/var/run/qubes/qubes-clipboard.bin", "rb") as f:
- test_txt_content = f.read()
+ with open("/var/run/qubes/qubes-clipboard.bin", "rb") as file:
+ test_txt_content = file.read()
self.assertEqual(test_txt_content.strip(), b"test1")
# this doesn't really close the application, only the qrexec-client
@@ -1260,7 +1255,7 @@ def create_testcases_for_templates():
)
-def load_tests(loader, tests, pattern):
+def load_tests(loader, tests, _pattern):
tests.addTests(loader.loadTestsFromNames(create_testcases_for_templates()))
return tests
diff --git a/qubes/tests/integ/dispvm_perf.py b/qubes/tests/integ/dispvm_perf.py
index 960bc8b25..dce1d2715 100644
--- a/qubes/tests/integ/dispvm_perf.py
+++ b/qubes/tests/integ/dispvm_perf.py
@@ -21,10 +21,27 @@
import shutil
import sys
import tempfile
-
+import unittest
import qubes.tests
+def env_with_reason(env_var, default):
+ env_var = "QUBES_TEST_PERF_" + env_var
+ value = os.environ.get(env_var, default)
+ reason = "environment variable is empty: " + env_var
+ return value, reason
+
+
+CONCURRENCY = env_with_reason("CONCURRENCY", False)
+GUI = env_with_reason("GUI", True)
+EXTENDED = env_with_reason("EXTENDED", False)
+DOM0_DISPVM = env_with_reason("DOM0_DISPVM", False)
+DOM0_DISPVM_API = env_with_reason("DOM0_DISPVM_API", True)
+DOM0_VM_API = env_with_reason("DOM0_VM_API", True)
+VM_DISPVM = env_with_reason("VM_DISPVM", False)
+VM_VM = env_with_reason("VM_VM", False)
+
+
class TC_00_DispVMPerfMixin:
def setUp(self: qubes.tests.SystemTestCase):
super().setUp()
@@ -41,6 +58,8 @@ def setUp(self: qubes.tests.SystemTestCase):
self.test_dir = tempfile.mkdtemp(prefix=prefix)
self.vm2 = None
return
+ self.app.domains["dom0"].features["preload-dispvm-max"] = ""
+ self.app.domains["dom0"].features["preload-dispvm-threshold"] = ""
self.dvm = self.app.add_new_vm(
"AppVM",
name=self.make_vm_name("dvm"),
@@ -127,147 +146,256 @@ def run_reader(self, args: list):
if p.returncode:
self.fail(f"'{' '.join(cmd)}' failed: {p.returncode}")
+ @unittest.skipUnless(*VM_DISPVM)
def test_000_vm_dispvm(self):
"""Latency of vm-dispvm calls"""
self.run_test("vm-dispvm")
+ @unittest.skipUnless(*VM_DISPVM)
+ @unittest.skipUnless(*GUI)
def test_001_vm_dispvm_gui(self):
"""Latency of vm-dispvm GUI calls"""
self.run_test("vm-dispvm-gui")
+ @unittest.skipUnless(*VM_DISPVM)
+ @unittest.skipUnless(*CONCURRENCY)
def test_002_vm_dispvm_concurrent(self):
"""Latency of vm-dispvm concurrent calls"""
self.run_test("vm-dispvm-concurrent")
+ @unittest.skipUnless(*VM_DISPVM)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*CONCURRENCY)
def test_003_vm_dispvm_gui_concurrent(self):
"""Latency of vm-dispvm concurrent GUI calls"""
self.run_test("vm-dispvm-gui-concurrent")
- def test_006_dom0_dispvm(self):
+ @unittest.skipUnless(*DOM0_DISPVM)
+ def test_100_dom0_dispvm(self):
"""Latency of dom0-dispvm calls"""
self.run_test("dom0-dispvm")
- def test_007_dom0_dispvm_gui(self):
+ @unittest.skipUnless(*DOM0_DISPVM)
+ @unittest.skipUnless(*GUI)
+ def test_101_dom0_dispvm_gui(self):
"""Latency of dom0-dispvm GUI calls"""
self.run_test("dom0-dispvm-gui")
- def test_008_dom0_dispvm_concurrent(self):
+ @unittest.skipUnless(*DOM0_DISPVM)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_102_dom0_dispvm_concurrent(self):
"""Latency of dom0-dispvm concurrent calls"""
self.run_test("dom0-dispvm-concurrent")
- def test_009_dom0_dispvm_gui_concurrent(self):
+ @unittest.skipUnless(*DOM0_DISPVM)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_103_dom0_dispvm_gui_concurrent(self):
"""Latency of dom0-dispvm concurrent GUI calls"""
self.run_test("dom0-dispvm-gui-concurrent")
- def test_020_vm_dispvm_preload(self):
+ @unittest.skipUnless(*VM_DISPVM)
+ def test_200_vm_dispvm_preload(self):
"""Latency of vm-dispvm (preload) calls"""
self.run_test("vm-dispvm-preload")
- def test_021_vm_dispvm_preload_gui(self):
+ @unittest.skipUnless(*VM_DISPVM)
+ @unittest.skipUnless(*GUI)
+ def test_201_vm_dispvm_preload_gui(self):
"""Latency of vm-dispvm (preload) GUI calls"""
self.run_test("vm-dispvm-preload-gui")
- def test_022_vm_dispvm_preload_concurrent(self):
+ @unittest.skipUnless(*VM_DISPVM)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_202_vm_dispvm_preload_concurrent(self):
"""Latency of vm-dispvm (preload) concurrent calls"""
self.run_test("vm-dispvm-preload-concurrent")
- def test_023_vm_dispvm_preload_gui_concurrent(self):
+ @unittest.skipUnless(*VM_DISPVM)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_203_vm_dispvm_preload_gui_concurrent(self):
"""Latency of vm-dispvm (preload) concurrent GUI calls"""
self.run_test("vm-dispvm-preload-gui-concurrent")
- def test_026_dom0_dispvm_preload(self):
+ @unittest.skipUnless(*DOM0_DISPVM)
+ def test_300_dom0_dispvm_preload(self):
"""Latency of dom0-dispvm (preload) calls"""
self.run_test("dom0-dispvm-preload")
- def test_027_dom0_dispvm_preload_gui(self):
+ @unittest.skipUnless(*DOM0_DISPVM)
+ @unittest.skipUnless(*GUI)
+ def test_301_dom0_dispvm_preload_gui(self):
"""Latency of dom0-dispvm (preload) GUI calls"""
self.run_test("dom0-dispvm-preload-gui")
- def test_028_dom0_dispvm_preload_concurrent(self):
+ @unittest.skipUnless(*DOM0_DISPVM)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_302_dom0_dispvm_preload_concurrent(self):
"""Latency of dom0-dispvm (preload) concurrent calls"""
self.run_test("dom0-dispvm-preload-concurrent")
- def test_029_dom0_dispvm_preload_gui_concurrent(self):
+ @unittest.skipUnless(*DOM0_DISPVM)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_303_dom0_dispvm_preload_gui_concurrent(self):
"""Latency of dom0-dispvm (preload) concurrent GUI calls"""
self.run_test("dom0-dispvm-preload-gui-concurrent")
+ @unittest.skipUnless(*DOM0_DISPVM_API)
def test_400_dom0_dispvm_api(self):
"""Latency of dom0-dispvm API calls"""
self.run_test("dom0-dispvm-api")
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
def test_401_dom0_dispvm_gui_api(self):
"""Latency of dom0-dispvm GUI API calls"""
self.run_test("dom0-dispvm-gui-api")
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*CONCURRENCY)
def test_402_dom0_dispvm_concurrent_api(self):
"""Latency of dom0-dispvm concurrent API calls"""
self.run_test("dom0-dispvm-concurrent-api")
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*CONCURRENCY)
def test_403_dom0_dispvm_gui_concurrent_api(self):
"""Latency of dom0-dispvm concurrent GUI API calls"""
self.run_test("dom0-dispvm-gui-concurrent-api")
- def test_404_dom0_dispvm_preload_less_less_api(self):
- """Latency of dom0-dispvm (preload less less) API calls"""
- self.run_test("dom0-dispvm-preload-less-less-api")
-
- def test_405_dom0_dispvm_preload_less_api(self):
- """Latency of dom0-dispvm (preload less) API calls"""
- self.run_test("dom0-dispvm-preload-less-api")
-
- def test_406_dom0_dispvm_preload_api(self):
- """Latency of dom0-dispvm (preload) API calls"""
- self.run_test("dom0-dispvm-preload-api")
-
- def test_407_dom0_dispvm_preload_more_api(self):
- """Latency of dom0-dispvm (preload more) API calls"""
- self.run_test("dom0-dispvm-preload-more-api")
-
- def test_408_dom0_dispvm_preload_more_more_api(self):
- """Latency of dom0-dispvm (preload more more) API calls"""
- self.run_test("dom0-dispvm-preload-more-more-api")
-
- def test_409_dom0_dispvm_preload_gui_api(self):
- """Latency of dom0-dispvm (preload) GUI API calls"""
- self.run_test("dom0-dispvm-preload-gui-api")
-
- def test_410_dom0_dispvm_preload_concurrent_api(self):
- """Latency of dom0-dispvm (preload) concurrent GUI API calls"""
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_404_dom0_dispvm_preload_concurrent_api(self):
+ """Latency of dom0-dispvm (preload) concurrent API calls"""
self.run_test("dom0-dispvm-preload-concurrent-api")
- def test_411_dom0_dispvm_preload_gui_concurrent_api(self):
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_405_dom0_dispvm_preload_gui_concurrent_api(self):
"""Latency of dom0-dispvm (preload) concurrent GUI API calls"""
self.run_test("dom0-dispvm-preload-gui-concurrent-api")
- def test_800_vm_vm(self):
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*EXTENDED)
+ def test_410_dom0_dispvm_preload_1_api(self):
+ """Latency of dom0-dispvm (1 preload) API calls"""
+ self.run_test("dom0-dispvm-preload-1-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ def test_411_dom0_dispvm_preload_2_api(self):
+ """Latency of dom0-dispvm (2 preload) API calls"""
+ self.run_test("dom0-dispvm-preload-2-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*EXTENDED)
+ def test_412_dom0_dispvm_preload_3_api(self):
+ """Latency of dom0-dispvm (3 preload) API calls"""
+ self.run_test("dom0-dispvm-preload-3-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ def test_413_dom0_dispvm_preload_4_api(self):
+ """Latency of dom0-dispvm (4 preload) API calls"""
+ self.run_test("dom0-dispvm-preload-4-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*EXTENDED)
+ def test_414_dom0_dispvm_preload_5_api(self):
+ """Latency of dom0-dispvm (5 preload) API calls"""
+ self.run_test("dom0-dispvm-preload-5-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ def test_415_dom0_dispvm_preload_6_api(self):
+ """Latency of dom0-dispvm (6 preload) API calls"""
+ self.run_test("dom0-dispvm-preload-6-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*EXTENDED)
+ def test_420_dom0_dispvm_preload_1_gui_api(self):
+ """Latency of dom0-dispvm (1 preload) GUI API calls"""
+ self.run_test("dom0-dispvm-preload-1-gui-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
+ def test_421_dom0_dispvm_preload_2_gui_api(self):
+ """Latency of dom0-dispvm (2 preload) GUI API calls"""
+ self.run_test("dom0-dispvm-preload-2-gui-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*EXTENDED)
+ def test_422_dom0_dispvm_preload_3_gui_api(self):
+ """Latency of dom0-dispvm (3 preload) GUI API calls"""
+ self.run_test("dom0-dispvm-preload-3-gui-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
+ def test_423_dom0_dispvm_preload_4_gui_api(self):
+ """Latency of dom0-dispvm (4 preload) GUI API calls"""
+ self.run_test("dom0-dispvm-preload-4-gui-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*EXTENDED)
+ def test_424_dom0_dispvm_preload_5_gui_api(self):
+ """Latency of dom0-dispvm (5 preload) GUI API calls"""
+ self.run_test("dom0-dispvm-preload-5-gui-api")
+
+ @unittest.skipUnless(*DOM0_DISPVM_API)
+ @unittest.skipUnless(*GUI)
+ def test_425_dom0_dispvm_preload_6_gui_api(self):
+ """Latency of dom0-dispvm (6 preload) GUI API calls"""
+ self.run_test("dom0-dispvm-preload-6-gui-api")
+
+ @unittest.skipUnless(*VM_VM)
+ def test_700_vm_vm(self):
"""Latency of vm-vm calls"""
self.run_test("vm-vm")
- def test_801_vm_vm_gui(self):
+ @unittest.skipUnless(*VM_VM)
+ @unittest.skipUnless(*GUI)
+ def test_701_vm_vm_gui(self):
"""Latency of vm-vm GUI calls"""
self.run_test("vm-vm-gui")
- def test_802_vm_vm_concurrent(self):
+ @unittest.skipUnless(*VM_VM)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_702_vm_vm_concurrent(self):
"""Latency of vm-vm concurrent calls"""
self.run_test("vm-vm-concurrent")
- def test_803_vm_vm_gui_concurrent(self):
+ @unittest.skipUnless(*VM_VM)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_703_vm_vm_gui_concurrent(self):
"""Latency of vm-vm concurrent GUI calls"""
self.run_test("vm-vm-gui-concurrent")
- def test_804_dom0_vm_api(self):
+ @unittest.skipUnless(*DOM0_VM_API)
+ def test_800_dom0_vm_api(self):
"""Latency of dom0-vm API calls"""
self.run_test("dom0-vm-api")
- def test_805_dom0_vm_gui_api(self):
+ @unittest.skipUnless(*DOM0_VM_API)
+ @unittest.skipUnless(*GUI)
+ def test_801_dom0_vm_gui_api(self):
"""Latency of dom0-vm GUI API calls"""
self.run_test("dom0-vm-gui-api")
- def test_806_dom0_vm_concurrent_api(self):
+ @unittest.skipUnless(*DOM0_VM_API)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_802_dom0_vm_concurrent_api(self):
"""Latency of dom0-vm concurrent API calls"""
self.run_test("dom0-vm-concurrent-api")
- def test_807_dom0_vm_gui_concurrent_api(self):
+ @unittest.skipUnless(*DOM0_VM_API)
+ @unittest.skipUnless(*GUI)
+ @unittest.skipUnless(*CONCURRENCY)
+ def test_803_dom0_vm_gui_concurrent_api(self):
"""Latency of dom0-vm concurrent GUI API calls"""
self.run_test("dom0-vm-gui-concurrent-api")
diff --git a/qubes/tests/integ/network.py b/qubes/tests/integ/network.py
index d114e9e11..2c97f0798 100644
--- a/qubes/tests/integ/network.py
+++ b/qubes/tests/integ/network.py
@@ -19,7 +19,7 @@
# License along with this library; if not, see .
#
import contextlib
-from distutils import spawn
+from shutil import which
import asyncio
import subprocess
@@ -35,7 +35,7 @@
# noinspection PyAttributeOutsideInit,PyPep8Naming
@qubes.tests.skipIfTemplate("whonix")
-class VmNetworkingMixin(object):
+class VmNetworkingMixin:
test_ip = "192.168.123.45"
test_name = "test.example.com"
@@ -80,7 +80,7 @@ def setUp(self):
"""
:type self: qubes.tests.SystemTestCase | VmNetworkingMixin
"""
- super(VmNetworkingMixin, self).setUp()
+ super().setUp()
if self.template.startswith("whonix-"):
self.skipTest(
"Test not supported here - Whonix uses its own "
@@ -163,7 +163,7 @@ def tearDown(self):
)
self._run_cmd_and_log_output(self.app.domains[0], "xl list")
del self.netvms
- super(VmNetworkingMixin, self).tearDown()
+ super().tearDown()
def configure_netvm(self):
"""
@@ -211,11 +211,10 @@ def run_netvm_cmd(qube, cmd):
ip=self.test_ip, name=self.test_name
),
)
+ cmd = "rm -f /etc/resolv.conf && echo nameserver"
run_netvm_cmd(
qube,
- "rm -f /etc/resolv.conf && echo nameserver {} > /etc/resolv.conf".format(
- self.test_ip
- ),
+ "{} {} > /etc/resolv.conf".format(cmd, self.test_ip),
)
run_netvm_cmd(qube, "systemctl try-restart systemd-resolved || :")
run_netvm_cmd(qube, "/usr/lib/qubes/qubes-setup-dnat-to-ns")
@@ -474,9 +473,7 @@ def test_010_simple_proxyvm(self):
)
@qubes.tests.expectedFailureIfTemplate("debian-7")
- @unittest.skipUnless(
- spawn.find_executable("xdotool"), "xdotool not installed"
- )
+ @unittest.skipUnless(which("xdotool"), "xdotool not installed")
def test_020_simple_proxyvm_nm(self):
"""
:type self: qubes.tests.SystemTestCase | VmNetworkingMixin
@@ -1157,9 +1154,8 @@ def test_203_fake_ip_inter_vm_allow(self):
self.loop.run_until_complete(self.start_vm(self.testvm1))
self.loop.run_until_complete(self.start_vm(self.testvm2))
- cmd = "nft add ip qubes custom-forward ip saddr {} ip daddr {} accept".format(
- self.testvm2.ip, self.testvm1.ip
- )
+ cmd = "nft add ip qubes custom-forward ip saddr "
+ cmd += "{} ip daddr {} accept".format(self.testvm2.ip, self.testvm1.ip)
try:
self.loop.run_until_complete(
self.proxy.run_for_stdio(cmd, user="root")
@@ -1170,9 +1166,8 @@ def test_203_fake_ip_inter_vm_allow(self):
) from None
try:
- cmd = "nft add ip qubes custom-input ip saddr {} counter accept".format(
- self.testvm2.ip
- )
+ cmd = "nft add ip qubes custom-input ip saddr "
+ cmd += "{} counter accept".format(self.testvm2.ip)
self.loop.run_until_complete(
self.testvm1.run_for_stdio(cmd, user="root")
)
@@ -1369,7 +1364,7 @@ def create_testcases_for_templates():
)
-def load_tests(loader, tests, pattern):
+def load_tests(loader, tests, _pattern):
tests.addTests(loader.loadTestsFromNames(create_testcases_for_templates()))
return tests
diff --git a/qubes/tests/integ/network_ipv6.py b/qubes/tests/integ/network_ipv6.py
index 646638acb..6665fc17e 100644
--- a/qubes/tests/integ/network_ipv6.py
+++ b/qubes/tests/integ/network_ipv6.py
@@ -24,7 +24,7 @@
import sys
import time
import unittest
-from distutils import spawn
+from shutil import which
import qubes.firewall
import qubes.tests
@@ -46,7 +46,7 @@ class VmIPv6NetworkingMixin(VmNetworkingMixin):
)
def setUp(self):
- super(VmIPv6NetworkingMixin, self).setUp()
+ super().setUp()
self.ping6_ip = self.ping6_cmd.format(target=self.test_ip6)
self.ping6_name = self.ping6_cmd.format(target=self.test_name)
self.ping6_deadline_ip = self.ping6_deadline_cmd.format(
@@ -92,7 +92,7 @@ def run_netvm_cmd(qube, cmd):
for qube in self.netvms:
qube.features["ipv6"] = True
- super(VmIPv6NetworkingMixin, self).configure_netvm()
+ super().configure_netvm()
for qube in self.netvms:
run_netvm_cmd(
@@ -106,12 +106,11 @@ def run_netvm_cmd(qube, cmd):
)
# ignore failure
self.run_cmd(qube, "while pkill dnsmasq; do sleep 1; done")
- run_netvm_cmd(
- qube,
- "dnsmasq -a {ip} -A /{name}/{ip} -A /{name}/{ip6} -i test0 -z".format(
- ip=self.test_ip, ip6=self.test_ip6, name=self.test_name
- ),
+ dnsmasq_args = "-a {ip} -A /{name}/{ip} -A /{name}/{ip6}".format(
+ ip=self.test_ip, ip6=self.test_ip6, name=self.test_name
)
+ dnsmasq_cmd = "dnsmasq {} -i test0 -z".format(dnsmasq_args)
+ run_netvm_cmd(qube, dnsmasq_cmd)
def test_500_ipv6_simple_networking(self):
"""
@@ -204,9 +203,7 @@ def test_510_ipv6_simple_proxyvm(self):
)
@qubes.tests.expectedFailureIfTemplate("debian-7")
- @unittest.skipUnless(
- spawn.find_executable("xdotool"), "xdotool not installed"
- )
+ @unittest.skipUnless(which("xdotool"), "xdotool not installed")
def test_520_ipv6_simple_proxyvm_nm(self):
"""
:type self: qubes.tests.SystemTestCase | VmIPv6NetworkingMixin
@@ -533,13 +530,10 @@ def test_550_ipv6_spoof_ip(self):
)
retcode = self.run_cmd(self.testnetvm, cmd)
if retcode == 127:
+ ip6_args = "! -s {} -p icmpv6 -j LOG".format(self.testvm1.ip6)
+ ip6_cmd = "ip6tables -I INPUT -i vif+ {}".format(ip6_args)
self.assertEqual(
- self.run_cmd(
- self.testnetvm,
- "ip6tables -I INPUT -i vif+ ! -s {} -p icmpv6 -j LOG".format(
- self.testvm1.ip6
- ),
- ),
+ self.run_cmd(self.testnetvm, ip6_cmd),
0,
)
iptables = True
@@ -690,7 +684,7 @@ def create_testcases_for_templates():
)
-def load_tests(loader, tests, pattern):
+def load_tests(loader, tests, _pattern):
tests.addTests(loader.loadTestsFromNames(create_testcases_for_templates()))
return tests
diff --git a/qubes/tests/vm/adminvm.py b/qubes/tests/vm/adminvm.py
index 55734ef8d..5d16105c5 100644
--- a/qubes/tests/vm/adminvm.py
+++ b/qubes/tests/vm/adminvm.py
@@ -23,10 +23,6 @@
import unittest
import unittest.mock
-import functools
-
-import asyncio
-
import qubes
import qubes.exc
import qubes.vm
@@ -69,7 +65,7 @@ def tearDown(self) -> None:
del self.emitter
try:
os.unlink("/tmp/qubestest.xml")
- except:
+ except: # pylint: disable=bare-except
pass
super().tearDown()
@@ -121,8 +117,8 @@ def test_311_suspend(self):
def test_700_run_service(self, mock_subprocess):
# if there is a user in 'qubes' group, it should be used by default
try:
- gr = grp.getgrnam("qubes")
- default_user = gr.gr_mem[0]
+ group = grp.getgrnam("qubes")
+ default_user = group.gr_mem[0]
command_prefix = ["runuser", "-u", default_user, "--"]
except (KeyError, IndexError):
command_prefix = []
diff --git a/qubes/tests/vm/appvm.py b/qubes/tests/vm/appvm.py
index adac199c1..05e1639f5 100644
--- a/qubes/tests/vm/appvm.py
+++ b/qubes/tests/vm/appvm.py
@@ -29,19 +29,20 @@
import qubes.vm.templatevm
-class TestApp(object):
+class TestApp:
+ # pylint: disable=too-few-public-methods
labels = {1: qubes.Label(1, "0xcc0000", "red")}
def __init__(self):
self.domains = {}
-class TestProp(object):
+class TestProp:
# pylint: disable=too-few-public-methods
__name__ = "testprop"
-class TestVM(object):
+class TestVM:
# pylint: disable=too-few-public-methods
app = TestApp()
@@ -56,13 +57,13 @@ def is_running(self):
class TestVolume(qubes.storage.Volume):
- def create(self):
+ def create(self): # pylint: disable=invalid-overridden-method
pass
class TestPool(qubes.storage.Pool):
def __init__(self, *args, **kwargs):
- super(TestPool, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
self._volumes = {}
def init_volume(self, vm, volume_config):
@@ -98,7 +99,7 @@ def cleanup_appvm(self):
self.app.domains.clear()
self.app.pools.clear()
- def get_vm(self, **kwargs):
+ def get_vm(self, *_args, **kwargs):
vm = qubes.vm.appvm.AppVM(
self.app,
None,
@@ -189,13 +190,13 @@ def test_500_property_migrate_template_for_dispvms(self):
vm = qubes.vm.appvm.AppVM(self.app, xml)
self.assertEqual(vm.template_for_dispvms, True)
with self.assertRaises(AttributeError):
- vm.dispvm_allowed
+ vm.dispvm_allowed # pylint: disable=no-member,pointless-statement
xml = lxml.etree.XML(xml_template.format(value="False"))
vm = qubes.vm.appvm.AppVM(self.app, xml)
self.assertEqual(vm.template_for_dispvms, False)
with self.assertRaises(AttributeError):
- vm.dispvm_allowed
+ vm.dispvm_allowed # pylint: disable=no-member,pointless-statement
def test_600_load_volume_config(self):
xml_template = """
diff --git a/qubes/tests/vm/dispvm.py b/qubes/tests/vm/dispvm.py
index 7644f5fb7..281595145 100644
--- a/qubes/tests/vm/dispvm.py
+++ b/qubes/tests/vm/dispvm.py
@@ -19,7 +19,7 @@
# License along with this library; if not, see .
import unittest
-import unittest.mock as mock
+from unittest import mock
import asyncio
@@ -34,7 +34,7 @@
class TestApp(qubes.tests.vm.TestApp):
def __init__(self):
- super(TestApp, self).__init__()
+ super().__init__()
self.qid_counter = 0
def add_new_vm(self, cls, **kwargs):
@@ -52,7 +52,7 @@ def add_new_vm(self, cls, **kwargs):
class TC_00_DispVM(qubes.tests.QubesTestCase):
def setUp(self):
- super(TC_00_DispVM, self).setUp()
+ super().setUp()
self.app = TestApp()
self.app.save = mock.Mock()
self.app.pools["default"] = qubes.tests.vm.appvm.TestPool(
@@ -85,7 +85,7 @@ def setUp(self):
def tearDown(self):
del self.emitter
- super(TC_00_DispVM, self).tearDown()
+ super().tearDown()
def cleanup_dispvm(self):
if hasattr(self, "dispvm"):
@@ -335,7 +335,7 @@ def test_000_get_preload_templates(self):
def test_001_from_appvm_reject_not_allowed(self):
with self.assertRaises(qubes.exc.QubesException):
- dispvm = self.loop.run_until_complete(
+ self.loop.run_until_complete(
qubes.vm.dispvm.DispVM.from_appvm(self.appvm)
)
@@ -352,20 +352,20 @@ def test_002_template_change(self):
"__getitem__.side_effect": orig_getitem,
}
)
- self.dispvm = self.app.add_new_vm(
+ dispvm = self.app.add_new_vm(
qubes.vm.dispvm.DispVM, name="test-dispvm", template=self.appvm
)
- self.dispvm.template = self.appvm
- self.loop.run_until_complete(self.dispvm.start())
+ dispvm.template = self.appvm
+ self.loop.run_until_complete(dispvm.start())
if not self.app.vmm.offline_mode:
assert not dispvm.is_halted()
with self.assertRaises(qubes.exc.QubesVMNotHaltedError):
- self.dispvm.template = self.appvm
+ dispvm.template = self.appvm
with self.assertRaises(qubes.exc.QubesValueError):
- self.dispvm.template = qubes.property.DEFAULT
- self.loop.run_until_complete(self.dispvm.kill())
- self.dispvm.template = self.appvm
+ dispvm.template = qubes.property.DEFAULT
+ self.loop.run_until_complete(dispvm.kill())
+ dispvm.template = self.appvm
def test_003_dvmtemplate_template_change(self):
self.appvm.template_for_dispvms = True
@@ -381,7 +381,7 @@ def test_003_dvmtemplate_template_change(self):
"__setitem__.side_effect": orig_domains.__setitem__,
}
)
- self.dispvm = self.app.add_new_vm(
+ self.app.add_new_vm(
qubes.vm.dispvm.DispVM, name="test-dispvm", template=self.appvm
)
@@ -403,7 +403,7 @@ def test_004_dvmtemplate_allowed_change(self):
"__setitem__.side_effect": orig_domains.__setitem__,
}
)
- self.dispvm = self.app.add_new_vm(
+ self.app.add_new_vm(
qubes.vm.dispvm.DispVM, name="test-dispvm", template=self.appvm
)
@@ -422,11 +422,10 @@ def test_010_create_direct(self):
"__getitem__.side_effect": orig_getitem,
}
)
- self.dispvm = self.app.add_new_vm(
+ dispvm = self.app.add_new_vm(
qubes.vm.dispvm.DispVM, name="test-dispvm", template=self.appvm
)
mock_domains.get_new_unused_dispid.assert_called_once_with()
- dispvm = self.dispvm
self.assertEqual(dispvm.name, "test-dispvm")
self.assertEqual(dispvm.template, self.appvm)
self.assertEqual(dispvm.label, self.appvm.label)
@@ -475,7 +474,7 @@ def test_011_create_direct_reject(self):
@mock.patch("os.symlink")
@mock.patch("os.makedirs")
- def test_020_copy_storage_pool(self, mock_makedirs, mock_symlink):
+ def test_020_copy_storage_pool(self, _mock_makedirs, _mock_symlink):
self.app.pools["alternative"] = qubes.tests.vm.appvm.TestPool(
name="alternative"
)
@@ -525,11 +524,11 @@ def test_021_storage_template_change(self):
"__setitem__.side_effect": orig_domains.__setitem__,
}
)
- vm = self.dispvm = self.app.add_new_vm(
+ dispvm = self.app.add_new_vm(
qubes.vm.dispvm.DispVM, name="test-dispvm", template=self.appvm
)
self.assertIs(
- vm.volume_config["root"]["source"],
+ dispvm.volume_config["root"]["source"],
self.template.volumes["root"],
)
# create new mock, so new template will get different volumes
@@ -543,20 +542,21 @@ def test_021_storage_template_change(self):
self.app.domains[template2] = template2
self.appvm.template = template2
- self.assertFalse(vm.volume_config["root"]["save_on_stop"])
- self.assertTrue(vm.volume_config["root"]["snap_on_start"])
+ self.assertFalse(dispvm.volume_config["root"]["save_on_stop"])
+ self.assertTrue(dispvm.volume_config["root"]["snap_on_start"])
self.assertNotEqual(
- vm.volume_config["root"]["source"], self.template.volumes["root"]
+ dispvm.volume_config["root"]["source"],
+ self.template.volumes["root"],
)
self.assertIs(
- vm.volume_config["root"]["source"], template2.volumes["root"]
+ dispvm.volume_config["root"]["source"], template2.volumes["root"]
)
self.assertIs(
- vm.volume_config["root"]["source"],
+ dispvm.volume_config["root"]["source"],
self.appvm.volume_config["root"]["source"],
)
self.assertIs(
- vm.volume_config["private"]["source"],
+ dispvm.volume_config["private"]["source"],
self.appvm.volumes["private"],
)
@@ -575,7 +575,7 @@ def test_022_storage_app_change(self):
"__setitem__.side_effect": orig_domains.__setitem__,
}
)
- vm = self.dispvm = self.app.add_new_vm(
+ vm = dispvm = self.app.add_new_vm(
qubes.vm.dispvm.DispVM, name="test-dispvm", template=self.appvm
)
self.assertTrue(vm.events_enabled)
@@ -600,9 +600,9 @@ def test_022_storage_app_change(self):
app2.template_for_dispvms = True
self.app.domains[app2.name] = app2
self.app.domains[app2] = app2
- self.dispvm.template = app2
+ dispvm.template = app2
- self.assertIs(vm, self.dispvm)
+ self.assertIs(vm, dispvm)
self.assertFalse(vm.volume_config["root"]["save_on_stop"])
self.assertTrue(vm.volume_config["root"]["snap_on_start"])
self.assertFalse(vm.volume_config["private"]["save_on_stop"])
@@ -630,7 +630,7 @@ def test_022_storage_app_change(self):
@mock.patch("os.symlink")
@mock.patch("os.makedirs")
- def test_023_inherit_ephemeral(self, mock_makedirs, mock_symlink):
+ def test_023_inherit_ephemeral(self, _mock_makedirs, _mock_symlink):
self.app.pools["alternative"] = qubes.tests.vm.appvm.TestPool(
name="alternative"
)
diff --git a/qubes/tests/vm/mix/dvmtemplate.py b/qubes/tests/vm/mix/dvmtemplate.py
index 154473fdc..238e877cf 100755
--- a/qubes/tests/vm/mix/dvmtemplate.py
+++ b/qubes/tests/vm/mix/dvmtemplate.py
@@ -33,7 +33,7 @@
class TestApp(qubes.tests.vm.TestApp):
def __init__(self):
- super(TestApp, self).__init__()
+ super().__init__()
self.qid_counter = 0
def add_new_vm(self, cls, **kwargs):
@@ -54,7 +54,7 @@ class TC_00_DVMTemplateMixin(
qubes.tests.QubesTestCase,
):
def setUp(self):
- super(TC_00_DVMTemplateMixin, self).setUp()
+ super().setUp()
self.app = TestApp()
self.app.save = mock.Mock()
self.app.pools["default"] = qubes.tests.vm.appvm.TestPool(
@@ -93,7 +93,7 @@ def setUp(self):
def tearDown(self):
self.app.default_dispvm = None
del self.emitter
- super(TC_00_DVMTemplateMixin, self).tearDown()
+ super().tearDown()
def cleanup_adminvm(self):
self.adminvm.close()
diff --git a/qubes/tests/vm/mix/net.py b/qubes/tests/vm/mix/net.py
index 1d286262f..f28ce98ae 100644
--- a/qubes/tests/vm/mix/net.py
+++ b/qubes/tests/vm/mix/net.py
@@ -20,7 +20,6 @@
# License along with this library; if not, see .
#
import ipaddress
-import unittest
from unittest.mock import patch
import qubes
@@ -35,7 +34,7 @@ class TC_00_NetVMMixin(
qubes.tests.vm.qubesvm.QubesVMTestsMixin, qubes.tests.QubesTestCase
):
def setUp(self):
- super(TC_00_NetVMMixin, self).setUp()
+ super().setUp()
self.app = qubes.tests.vm.TestApp()
self.app.vmm.offline_mode = True
diff --git a/qubes/tests/vm/qubesvm.py b/qubes/tests/vm/qubesvm.py
index 50bfa1821..2af1763b3 100644
--- a/qubes/tests/vm/qubesvm.py
+++ b/qubes/tests/vm/qubesvm.py
@@ -29,9 +29,6 @@
from uuid import UUID
import datetime
-import asyncio
-
-import functools
import lxml.etree
import unittest.mock
diff --git a/qubes/vm/appvm.py b/qubes/vm/appvm.py
index f046d05d3..0089a1f6d 100644
--- a/qubes/vm/appvm.py
+++ b/qubes/vm/appvm.py
@@ -114,6 +114,7 @@ def __init__(self, app, xml, **kwargs):
@qubes.stateless_property
def icon(self):
if self.template_for_dispvms:
+ # pylint: disable=no-member
return "templatevm-" + self.label.name
# multi-inheritance and properties confuses pylint here
# pylint: disable=no-member
diff --git a/qvm-tools/qubes-hcl-report b/qvm-tools/qubes-hcl-report
index d71b76a38..e8540697a 100755
--- a/qvm-tools/qubes-hcl-report
+++ b/qvm-tools/qubes-hcl-report
@@ -103,6 +103,11 @@ cat /etc/qubes-release > "$TEMP_DIR/qubes-release" || exit
cat /proc/cpuinfo > "$TEMP_DIR/cpuinfo" || exit
sudo lspci -nnvk > "$TEMP_DIR/lspci"
cat /proc/scsi/scsi > "$TEMP_DIR/scsi"
+for drive in /sys/class/nvme-subsystem/nvme-subsys*/; do
+ test -d "$drive" || continue
+ cat "$drive"/{serial,model,firmware_rev} |
+ tr "\n" " " | sed "s/\s$//" > "$TEMP_DIR/nvme"
+done
sudo dmidecode > "$TEMP_DIR/dmidecode"
xl info > "$TEMP_DIR/xl-info" || exit
xl dmesg > "$TEMP_DIR/xl-dmesg" || exit
@@ -204,6 +209,11 @@ CHIPSET=$(grep "00:00.0.*Host bridge" "$TEMP_DIR/lspci" |cut -d ':' -f3- |sed -e
VGA=$(grep -E 'VGA|Display' "$TEMP_DIR/lspci" |cut -d ':' -f3- |sed -e "s/^[[:space:]]*/\ /")
NET=$(grep -E 'Network|Ethernet' "$TEMP_DIR/lspci" |cut -d ':' -f3- |sed -e "s/^[[:space:]]*/\ /")
SCSI=$(grep Model "$TEMP_DIR/scsi"|cut -d ':' -f3-|sed -e "s/^[[:space:]]*/\ /")
+if test -s "$TEMP_DIR/nvme"; then
+ NVME=$(sed -e "s/^[[:space:]]*/ /" "$TEMP_DIR/nvme")
+else
+ NVME=""
+fi
RAM=$(grep total_memory "$TEMP_DIR/xl-info"|cut -d ':' -f2 |tr -d ' ')
USB=$(grep -c USB "$TEMP_DIR/lspci")
XEN_MAJOR=$(grep xen_major "$TEMP_DIR/xl-info"|cut -d: -f2 |tr -d ' ')
@@ -378,6 +388,8 @@ memory: |
$RAM
scsi: |
$SCSI
+nvme: |
+$NVME
usb: |
$USB
certified:
diff --git a/tests-data/dispvm_perf/2025121204-4.3.json b/tests-data/dispvm_perf/2025121204-4.3.json
new file mode 100644
index 000000000..0fb6568a9
--- /dev/null
+++ b/tests-data/dispvm_perf/2025121204-4.3.json
@@ -0,0 +1,1638 @@
+{
+ "debian-13-xfce_dom0-dispvm-api": {
+ "name": "dom0-dispvm-api",
+ "gui": false,
+ "concurrent": false,
+ "from_dom0": true,
+ "preload_max": 0,
+ "target_dispvm": true,
+ "admin_api": true,
+ "extra_id": "",
+ "pretty_name": "Dom0 runs simple app in a disposable (API)",
+ "template": "debian-13-xfce",
+ "api_results": {
+ "iteration": {
+ "1": {
+ "dom": 0.016,
+ "disp": 0.68,
+ "start": 5.745,
+ "exec": 0.318,
+ "clean": 1.204,
+ "total": 7.963
+ },
+ "2": {
+ "dom": 0.02,
+ "disp": 0.645,
+ "start": 6.093,
+ "exec": 0.274,
+ "clean": 0.958,
+ "total": 7.989
+ },
+ "3": {
+ "dom": 0.013,
+ "disp": 0.863,
+ "start": 5.836,
+ "exec": 0.349,
+ "clean": 0.961,
+ "total": 8.022
+ },
+ "4": {
+ "dom": 0.02,
+ "disp": 0.58,
+ "start": 4.967,
+ "exec": 0.378,
+ "clean": 0.934,
+ "total": 6.879
+ },
+ "5": {
+ "dom": 0.019,
+ "disp": 0.764,
+ "start": 4.981,
+ "exec": 0.475,
+ "clean": 1.207,
+ "total": 7.446
+ },
+ "6": {
+ "dom": 0.016,
+ "disp": 0.463,
+ "start": 5.329,
+ "exec": 0.312,
+ "clean": 0.956,
+ "total": 7.077
+ },
+ "7": {
+ "dom": 0.015,
+ "disp": 0.523,
+ "start": 6.468,
+ "exec": 0.266,
+ "clean": 1.067,
+ "total": 8.338
+ },
+ "8": {
+ "dom": 0.019,
+ "disp": 0.485,
+ "start": 4.68,
+ "exec": 0.455,
+ "clean": 0.856,
+ "total": 6.496
+ },
+ "9": {
+ "dom": 0.016,
+ "disp": 0.438,
+ "start": 5.182,
+ "exec": 0.319,
+ "clean": 1.096,
+ "total": 7.051
+ },
+ "10": {
+ "dom": 0.014,
+ "disp": 0.644,
+ "start": 5.536,
+ "exec": 0.309,
+ "clean": 0.952,
+ "total": 7.454
+ },
+ "11": {
+ "dom": 0.018,
+ "disp": 0.489,
+ "start": 5.324,
+ "exec": 0.487,
+ "clean": 0.99,
+ "total": 7.309
+ },
+ "12": {
+ "dom": 0.025,
+ "disp": 0.622,
+ "start": 5.592,
+ "exec": 0.301,
+ "clean": 0.916,
+ "total": 7.457
+ }
+ },
+ "stage": {
+ "dom": {
+ "values": [
+ 0.016,
+ 0.02,
+ 0.013,
+ 0.02,
+ 0.019,
+ 0.016,
+ 0.015,
+ 0.019,
+ 0.016,
+ 0.014,
+ 0.018,
+ 0.025
+ ],
+ "mean": 0.018,
+ "median": 0.017
+ },
+ "disp": {
+ "values": [
+ 0.68,
+ 0.645,
+ 0.863,
+ 0.58,
+ 0.764,
+ 0.463,
+ 0.523,
+ 0.485,
+ 0.438,
+ 0.644,
+ 0.489,
+ 0.622
+ ],
+ "mean": 0.6,
+ "median": 0.601
+ },
+ "start": {
+ "values": [
+ 5.745,
+ 6.093,
+ 5.836,
+ 4.967,
+ 4.981,
+ 5.329,
+ 6.468,
+ 4.68,
+ 5.182,
+ 5.536,
+ 5.324,
+ 5.592
+ ],
+ "mean": 5.478,
+ "median": 5.432
+ },
+ "exec": {
+ "values": [
+ 0.318,
+ 0.274,
+ 0.349,
+ 0.378,
+ 0.475,
+ 0.312,
+ 0.266,
+ 0.455,
+ 0.319,
+ 0.309,
+ 0.487,
+ 0.301
+ ],
+ "mean": 0.354,
+ "median": 0.319
+ },
+ "clean": {
+ "values": [
+ 1.204,
+ 0.958,
+ 0.961,
+ 0.934,
+ 1.207,
+ 0.956,
+ 1.067,
+ 0.856,
+ 1.096,
+ 0.952,
+ 0.99,
+ 0.916
+ ],
+ "mean": 1.008,
+ "median": 0.96
+ },
+ "total": {
+ "values": [
+ 7.963,
+ 7.989,
+ 8.022,
+ 6.879,
+ 7.446,
+ 7.077,
+ 8.338,
+ 6.496,
+ 7.051,
+ 7.454,
+ 7.309,
+ 7.457
+ ],
+ "mean": 7.457,
+ "median": 7.45
+ }
+ }
+ },
+ "iterations": 12,
+ "mean": 7.457,
+ "total": 89.481,
+ "date": "2025-12-12T12:00:33",
+ "memory": 400,
+ "maxmem": 4000,
+ "vcpus": 2,
+ "qrexec_timeout": 60,
+ "shutdown_timeout": 60,
+ "kernel": "6.17.9-1.fc41",
+ "kernelopts": "swiotlb=2048",
+ "template-buildtime": "2025-09-19 11:46:06",
+ "last-update": "2025-09-22 11:32:55",
+ "os": "Linux",
+ "os-distribution": "debian",
+ "os-version": 13,
+ "hcl-qubes": "R4.3-rc3",
+ "hcl-xen": "4.19.3",
+ "hcl-kernel": "6.17.9-1",
+ "hcl-memory": 32404
+ },
+ "debian-13-xfce_dom0-dispvm-gui-api": {
+ "name": "dom0-dispvm-gui-api",
+ "gui": true,
+ "concurrent": false,
+ "from_dom0": true,
+ "preload_max": 0,
+ "target_dispvm": true,
+ "admin_api": true,
+ "extra_id": "",
+ "pretty_name": "Dom0 runs GUI app in a disposable (API)",
+ "template": "debian-13-xfce",
+ "api_results": {
+ "iteration": {
+ "1": {
+ "dom": 0.016,
+ "disp": 0.581,
+ "start": 4.878,
+ "exec": 2.447,
+ "clean": 1.021,
+ "total": 8.942
+ },
+ "2": {
+ "dom": 0.014,
+ "disp": 0.675,
+ "start": 5.334,
+ "exec": 2.023,
+ "clean": 1.08,
+ "total": 9.126
+ },
+ "3": {
+ "dom": 0.017,
+ "disp": 0.578,
+ "start": 5.065,
+ "exec": 1.629,
+ "clean": 1.077,
+ "total": 8.365
+ },
+ "4": {
+ "dom": 0.015,
+ "disp": 0.467,
+ "start": 4.796,
+ "exec": 1.649,
+ "clean": 1.182,
+ "total": 8.109
+ },
+ "5": {
+ "dom": 0.02,
+ "disp": 0.627,
+ "start": 5.964,
+ "exec": 1.734,
+ "clean": 1.0,
+ "total": 9.346
+ },
+ "6": {
+ "dom": 0.017,
+ "disp": 0.516,
+ "start": 5.865,
+ "exec": 1.562,
+ "clean": 1.132,
+ "total": 9.092
+ },
+ "7": {
+ "dom": 0.015,
+ "disp": 0.462,
+ "start": 5.304,
+ "exec": 1.927,
+ "clean": 0.999,
+ "total": 8.706
+ },
+ "8": {
+ "dom": 0.018,
+ "disp": 0.608,
+ "start": 5.74,
+ "exec": 1.616,
+ "clean": 0.947,
+ "total": 8.93
+ },
+ "9": {
+ "dom": 0.014,
+ "disp": 0.518,
+ "start": 6.002,
+ "exec": 1.743,
+ "clean": 1.028,
+ "total": 9.306
+ },
+ "10": {
+ "dom": 0.021,
+ "disp": 0.839,
+ "start": 5.442,
+ "exec": 1.851,
+ "clean": 1.009,
+ "total": 9.162
+ },
+ "11": {
+ "dom": 0.013,
+ "disp": 0.576,
+ "start": 5.204,
+ "exec": 1.762,
+ "clean": 1.018,
+ "total": 8.575
+ },
+ "12": {
+ "dom": 0.016,
+ "disp": 0.511,
+ "start": 5.189,
+ "exec": 1.597,
+ "clean": 0.886,
+ "total": 8.2
+ }
+ },
+ "stage": {
+ "dom": {
+ "values": [
+ 0.016,
+ 0.014,
+ 0.017,
+ 0.015,
+ 0.02,
+ 0.017,
+ 0.015,
+ 0.018,
+ 0.014,
+ 0.021,
+ 0.013,
+ 0.016
+ ],
+ "mean": 0.016,
+ "median": 0.016
+ },
+ "disp": {
+ "values": [
+ 0.581,
+ 0.675,
+ 0.578,
+ 0.467,
+ 0.627,
+ 0.516,
+ 0.462,
+ 0.608,
+ 0.518,
+ 0.839,
+ 0.576,
+ 0.511
+ ],
+ "mean": 0.58,
+ "median": 0.577
+ },
+ "start": {
+ "values": [
+ 4.878,
+ 5.334,
+ 5.065,
+ 4.796,
+ 5.964,
+ 5.865,
+ 5.304,
+ 5.74,
+ 6.002,
+ 5.442,
+ 5.204,
+ 5.189
+ ],
+ "mean": 5.399,
+ "median": 5.319
+ },
+ "exec": {
+ "values": [
+ 2.447,
+ 2.023,
+ 1.629,
+ 1.649,
+ 1.734,
+ 1.562,
+ 1.927,
+ 1.616,
+ 1.743,
+ 1.851,
+ 1.762,
+ 1.597
+ ],
+ "mean": 1.795,
+ "median": 1.739
+ },
+ "clean": {
+ "values": [
+ 1.021,
+ 1.08,
+ 1.077,
+ 1.182,
+ 1.0,
+ 1.132,
+ 0.999,
+ 0.947,
+ 1.028,
+ 1.009,
+ 1.018,
+ 0.886
+ ],
+ "mean": 1.032,
+ "median": 1.019
+ },
+ "total": {
+ "values": [
+ 8.942,
+ 9.126,
+ 8.365,
+ 8.109,
+ 9.346,
+ 9.092,
+ 8.706,
+ 8.93,
+ 9.306,
+ 9.162,
+ 8.575,
+ 8.2
+ ],
+ "mean": 8.822,
+ "median": 8.936
+ }
+ }
+ },
+ "iterations": 12,
+ "mean": 8.822,
+ "total": 105.86,
+ "date": "2025-12-12T12:03:00",
+ "memory": 400,
+ "maxmem": 4000,
+ "vcpus": 2,
+ "qrexec_timeout": 60,
+ "shutdown_timeout": 60,
+ "kernel": "6.17.9-1.fc41",
+ "kernelopts": "swiotlb=2048",
+ "template-buildtime": "2025-09-19 11:46:06",
+ "last-update": "2025-09-22 11:32:55",
+ "os": "Linux",
+ "os-distribution": "debian",
+ "os-version": 13,
+ "hcl-qubes": "R4.3-rc3",
+ "hcl-xen": "4.19.3",
+ "hcl-kernel": "6.17.9-1",
+ "hcl-memory": 32404
+ },
+ "debian-13-xfce_dom0-dispvm-preload-2-api": {
+ "name": "dom0-dispvm-preload-2-api",
+ "gui": false,
+ "concurrent": false,
+ "from_dom0": true,
+ "preload_max": 2,
+ "target_dispvm": true,
+ "admin_api": true,
+ "extra_id": "dom0-dispvm-preload-api",
+ "pretty_name": "Dom0 runs simple app in disposables (2 preloaded) (API)",
+ "template": "debian-13-xfce",
+ "api_results": {
+ "iteration": {
+ "1": {
+ "dom": 0.018,
+ "disp": 0.184,
+ "exec": 0.19,
+ "clean": 2.075,
+ "total": 2.466
+ },
+ "2": {
+ "dom": 0.018,
+ "disp": 0.184,
+ "exec": 0.102,
+ "clean": 2.209,
+ "total": 2.513
+ },
+ "3": {
+ "dom": 0.022,
+ "disp": 1.585,
+ "exec": 0.14,
+ "clean": 2.227,
+ "total": 3.973
+ },
+ "4": {
+ "dom": 0.045,
+ "disp": 0.279,
+ "exec": 0.126,
+ "clean": 2.252,
+ "total": 2.702
+ },
+ "5": {
+ "dom": 0.062,
+ "disp": 1.432,
+ "exec": 0.125,
+ "clean": 2.158,
+ "total": 3.776
+ },
+ "6": {
+ "dom": 0.021,
+ "disp": 0.609,
+ "exec": 0.139,
+ "clean": 2.483,
+ "total": 3.252
+ },
+ "7": {
+ "dom": 0.066,
+ "disp": 1.015,
+ "exec": 0.165,
+ "clean": 2.324,
+ "total": 3.57
+ },
+ "8": {
+ "dom": 0.066,
+ "disp": 1.084,
+ "exec": 0.149,
+ "clean": 2.301,
+ "total": 3.599
+ },
+ "9": {
+ "dom": 0.063,
+ "disp": 1.127,
+ "exec": 0.133,
+ "clean": 2.3,
+ "total": 3.624
+ },
+ "10": {
+ "dom": 0.069,
+ "disp": 0.583,
+ "exec": 0.117,
+ "clean": 2.379,
+ "total": 3.149
+ },
+ "11": {
+ "dom": 0.069,
+ "disp": 0.956,
+ "exec": 0.127,
+ "clean": 2.26,
+ "total": 3.411
+ },
+ "12": {
+ "dom": 0.063,
+ "disp": 1.068,
+ "exec": 0.182,
+ "clean": 2.083,
+ "total": 3.396
+ }
+ },
+ "stage": {
+ "dom": {
+ "values": [
+ 0.018,
+ 0.018,
+ 0.022,
+ 0.045,
+ 0.062,
+ 0.021,
+ 0.066,
+ 0.066,
+ 0.063,
+ 0.069,
+ 0.069,
+ 0.063
+ ],
+ "mean": 0.049,
+ "median": 0.062
+ },
+ "disp": {
+ "values": [
+ 0.184,
+ 0.184,
+ 1.585,
+ 0.279,
+ 1.432,
+ 0.609,
+ 1.015,
+ 1.084,
+ 1.127,
+ 0.583,
+ 0.956,
+ 1.068
+ ],
+ "mean": 0.842,
+ "median": 0.985
+ },
+ "exec": {
+ "values": [
+ 0.19,
+ 0.102,
+ 0.14,
+ 0.126,
+ 0.125,
+ 0.139,
+ 0.165,
+ 0.149,
+ 0.133,
+ 0.117,
+ 0.127,
+ 0.182
+ ],
+ "mean": 0.141,
+ "median": 0.136
+ },
+ "clean": {
+ "values": [
+ 2.075,
+ 2.209,
+ 2.227,
+ 2.252,
+ 2.158,
+ 2.483,
+ 2.324,
+ 2.301,
+ 2.3,
+ 2.379,
+ 2.26,
+ 2.083
+ ],
+ "mean": 2.254,
+ "median": 2.256
+ },
+ "total": {
+ "values": [
+ 2.466,
+ 2.513,
+ 3.973,
+ 2.702,
+ 3.776,
+ 3.252,
+ 3.57,
+ 3.599,
+ 3.624,
+ 3.149,
+ 3.411,
+ 3.396
+ ],
+ "mean": 3.286,
+ "median": 3.404
+ }
+ }
+ },
+ "iterations": 12,
+ "mean": 3.286,
+ "total": 39.433,
+ "date": "2025-12-12T12:04:34",
+ "memory": 400,
+ "maxmem": 4000,
+ "vcpus": 2,
+ "qrexec_timeout": 60,
+ "shutdown_timeout": 60,
+ "kernel": "6.17.9-1.fc41",
+ "kernelopts": "swiotlb=2048",
+ "template-buildtime": "2025-09-19 11:46:06",
+ "last-update": "2025-09-22 11:32:55",
+ "os": "Linux",
+ "os-distribution": "debian",
+ "os-version": 13,
+ "hcl-qubes": "R4.3-rc3",
+ "hcl-xen": "4.19.3",
+ "hcl-kernel": "6.17.9-1",
+ "hcl-memory": 32404
+ },
+ "debian-13-xfce_dom0-dispvm-preload-4-api": {
+ "name": "dom0-dispvm-preload-4-api",
+ "gui": false,
+ "concurrent": false,
+ "from_dom0": true,
+ "preload_max": 4,
+ "target_dispvm": true,
+ "admin_api": true,
+ "extra_id": "",
+ "pretty_name": "Dom0 runs simple app in disposables (4 preloaded) (API)",
+ "template": "debian-13-xfce",
+ "api_results": {
+ "iteration": {
+ "1": {
+ "dom": 0.023,
+ "disp": 0.175,
+ "exec": 0.088,
+ "clean": 2.186,
+ "total": 2.472
+ },
+ "2": {
+ "dom": 0.053,
+ "disp": 0.144,
+ "exec": 0.108,
+ "clean": 2.261,
+ "total": 2.566
+ },
+ "3": {
+ "dom": 0.02,
+ "disp": 0.255,
+ "exec": 0.16,
+ "clean": 2.469,
+ "total": 2.904
+ },
+ "4": {
+ "dom": 0.069,
+ "disp": 0.162,
+ "exec": 0.345,
+ "clean": 2.401,
+ "total": 2.978
+ },
+ "5": {
+ "dom": 0.03,
+ "disp": 0.177,
+ "exec": 0.096,
+ "clean": 1.392,
+ "total": 1.695
+ },
+ "6": {
+ "dom": 0.019,
+ "disp": 1.558,
+ "exec": 0.229,
+ "clean": 2.87,
+ "total": 4.676
+ },
+ "7": {
+ "dom": 0.018,
+ "disp": 0.155,
+ "exec": 0.122,
+ "clean": 3.437,
+ "total": 3.731
+ },
+ "8": {
+ "dom": 0.046,
+ "disp": 0.162,
+ "exec": 0.145,
+ "clean": 2.389,
+ "total": 2.742
+ },
+ "9": {
+ "dom": 0.019,
+ "disp": 0.244,
+ "exec": 0.189,
+ "clean": 1.458,
+ "total": 1.909
+ },
+ "10": {
+ "dom": 1.213,
+ "disp": 0.146,
+ "exec": 0.267,
+ "clean": 1.261,
+ "total": 2.887
+ },
+ "11": {
+ "dom": 0.02,
+ "disp": 1.537,
+ "exec": 0.169,
+ "clean": 2.492,
+ "total": 4.218
+ },
+ "12": {
+ "dom": 0.022,
+ "disp": 0.15,
+ "exec": 0.088,
+ "clean": 3.632,
+ "total": 3.893
+ }
+ },
+ "stage": {
+ "dom": {
+ "values": [
+ 0.023,
+ 0.053,
+ 0.02,
+ 0.069,
+ 0.03,
+ 0.019,
+ 0.018,
+ 0.046,
+ 0.019,
+ 1.213,
+ 0.02,
+ 0.022
+ ],
+ "mean": 0.129,
+ "median": 0.022
+ },
+ "disp": {
+ "values": [
+ 0.175,
+ 0.144,
+ 0.255,
+ 0.162,
+ 0.177,
+ 1.558,
+ 0.155,
+ 0.162,
+ 0.244,
+ 0.146,
+ 1.537,
+ 0.15
+ ],
+ "mean": 0.405,
+ "median": 0.168
+ },
+ "exec": {
+ "values": [
+ 0.088,
+ 0.108,
+ 0.16,
+ 0.345,
+ 0.096,
+ 0.229,
+ 0.122,
+ 0.145,
+ 0.189,
+ 0.267,
+ 0.169,
+ 0.088
+ ],
+ "mean": 0.167,
+ "median": 0.152
+ },
+ "clean": {
+ "values": [
+ 2.186,
+ 2.261,
+ 2.469,
+ 2.401,
+ 1.392,
+ 2.87,
+ 3.437,
+ 2.389,
+ 1.458,
+ 1.261,
+ 2.492,
+ 3.632
+ ],
+ "mean": 2.354,
+ "median": 2.395
+ },
+ "total": {
+ "values": [
+ 2.472,
+ 2.566,
+ 2.904,
+ 2.978,
+ 1.695,
+ 4.676,
+ 3.731,
+ 2.742,
+ 1.909,
+ 2.887,
+ 4.218,
+ 3.893
+ ],
+ "mean": 3.056,
+ "median": 2.896
+ }
+ }
+ },
+ "iterations": 12,
+ "mean": 3.056,
+ "total": 36.671,
+ "date": "2025-12-12T12:06:10",
+ "memory": 400,
+ "maxmem": 4000,
+ "vcpus": 2,
+ "qrexec_timeout": 60,
+ "shutdown_timeout": 60,
+ "kernel": "6.17.9-1.fc41",
+ "kernelopts": "swiotlb=2048",
+ "template-buildtime": "2025-09-19 11:46:06",
+ "last-update": "2025-09-22 11:32:55",
+ "os": "Linux",
+ "os-distribution": "debian",
+ "os-version": 13,
+ "hcl-qubes": "R4.3-rc3",
+ "hcl-xen": "4.19.3",
+ "hcl-kernel": "6.17.9-1",
+ "hcl-memory": 32404
+ },
+ "debian-13-xfce_dom0-dispvm-preload-2-gui-api": {
+ "name": "dom0-dispvm-preload-2-gui-api",
+ "gui": true,
+ "concurrent": false,
+ "from_dom0": true,
+ "preload_max": 2,
+ "target_dispvm": true,
+ "admin_api": true,
+ "extra_id": "dom0-dispvm-preload-gui-api",
+ "pretty_name": "Dom0 runs GUI app in disposables (2 preloaded) (API)",
+ "template": "debian-13-xfce",
+ "api_results": {
+ "iteration": {
+ "1": {
+ "dom": 0.015,
+ "disp": 0.152,
+ "exec": 1.765,
+ "clean": 1.494,
+ "total": 3.426
+ },
+ "2": {
+ "dom": 0.015,
+ "disp": 0.141,
+ "exec": 1.714,
+ "clean": 1.554,
+ "total": 3.424
+ },
+ "3": {
+ "dom": 0.029,
+ "disp": 0.241,
+ "exec": 1.716,
+ "clean": 1.832,
+ "total": 3.819
+ },
+ "4": {
+ "dom": 0.025,
+ "disp": 0.17,
+ "exec": 1.618,
+ "clean": 1.866,
+ "total": 3.679
+ },
+ "5": {
+ "dom": 0.032,
+ "disp": 0.198,
+ "exec": 1.845,
+ "clean": 1.63,
+ "total": 3.705
+ },
+ "6": {
+ "dom": 0.016,
+ "disp": 0.14,
+ "exec": 2.233,
+ "clean": 1.288,
+ "total": 3.677
+ },
+ "7": {
+ "dom": 0.016,
+ "disp": 0.144,
+ "exec": 1.777,
+ "clean": 1.891,
+ "total": 3.828
+ },
+ "8": {
+ "dom": 0.019,
+ "disp": 0.143,
+ "exec": 1.927,
+ "clean": 1.654,
+ "total": 3.743
+ },
+ "9": {
+ "dom": 0.043,
+ "disp": 0.22,
+ "exec": 1.894,
+ "clean": 1.681,
+ "total": 3.838
+ },
+ "10": {
+ "dom": 0.016,
+ "disp": 0.183,
+ "exec": 1.865,
+ "clean": 1.485,
+ "total": 3.549
+ },
+ "11": {
+ "dom": 0.02,
+ "disp": 0.137,
+ "exec": 1.698,
+ "clean": 2.102,
+ "total": 3.957
+ },
+ "12": {
+ "dom": 0.027,
+ "disp": 0.178,
+ "exec": 1.978,
+ "clean": 1.7,
+ "total": 3.883
+ }
+ },
+ "stage": {
+ "dom": {
+ "values": [
+ 0.015,
+ 0.015,
+ 0.029,
+ 0.025,
+ 0.032,
+ 0.016,
+ 0.016,
+ 0.019,
+ 0.043,
+ 0.016,
+ 0.02,
+ 0.027
+ ],
+ "mean": 0.023,
+ "median": 0.019
+ },
+ "disp": {
+ "values": [
+ 0.152,
+ 0.141,
+ 0.241,
+ 0.17,
+ 0.198,
+ 0.14,
+ 0.144,
+ 0.143,
+ 0.22,
+ 0.183,
+ 0.137,
+ 0.178
+ ],
+ "mean": 0.171,
+ "median": 0.161
+ },
+ "exec": {
+ "values": [
+ 1.765,
+ 1.714,
+ 1.716,
+ 1.618,
+ 1.845,
+ 2.233,
+ 1.777,
+ 1.927,
+ 1.894,
+ 1.865,
+ 1.698,
+ 1.978
+ ],
+ "mean": 1.836,
+ "median": 1.811
+ },
+ "clean": {
+ "values": [
+ 1.494,
+ 1.554,
+ 1.832,
+ 1.866,
+ 1.63,
+ 1.288,
+ 1.891,
+ 1.654,
+ 1.681,
+ 1.485,
+ 2.102,
+ 1.7
+ ],
+ "mean": 1.681,
+ "median": 1.667
+ },
+ "total": {
+ "values": [
+ 3.426,
+ 3.424,
+ 3.819,
+ 3.679,
+ 3.705,
+ 3.677,
+ 3.828,
+ 3.743,
+ 3.838,
+ 3.549,
+ 3.957,
+ 3.883
+ ],
+ "mean": 3.711,
+ "median": 3.724
+ }
+ }
+ },
+ "iterations": 12,
+ "mean": 3.711,
+ "total": 44.529,
+ "date": "2025-12-12T12:07:52",
+ "memory": 400,
+ "maxmem": 4000,
+ "vcpus": 2,
+ "qrexec_timeout": 60,
+ "shutdown_timeout": 60,
+ "kernel": "6.17.9-1.fc41",
+ "kernelopts": "swiotlb=2048",
+ "template-buildtime": "2025-09-19 11:46:06",
+ "last-update": "2025-09-22 11:32:55",
+ "os": "Linux",
+ "os-distribution": "debian",
+ "os-version": 13,
+ "hcl-qubes": "R4.3-rc3",
+ "hcl-xen": "4.19.3",
+ "hcl-kernel": "6.17.9-1",
+ "hcl-memory": 32404
+ },
+ "debian-13-xfce_dom0-dispvm-preload-4-gui-api": {
+ "name": "dom0-dispvm-preload-4-gui-api",
+ "gui": true,
+ "concurrent": false,
+ "from_dom0": true,
+ "preload_max": 4,
+ "target_dispvm": true,
+ "admin_api": true,
+ "extra_id": "",
+ "pretty_name": "Dom0 runs GUI app in disposables (4 preloaded) (API)",
+ "template": "debian-13-xfce",
+ "api_results": {
+ "iteration": {
+ "1": {
+ "dom": 0.017,
+ "disp": 0.183,
+ "exec": 1.741,
+ "clean": 1.462,
+ "total": 3.403
+ },
+ "2": {
+ "dom": 0.018,
+ "disp": 0.192,
+ "exec": 1.94,
+ "clean": 1.57,
+ "total": 3.72
+ },
+ "3": {
+ "dom": 0.024,
+ "disp": 0.209,
+ "exec": 2.207,
+ "clean": 1.472,
+ "total": 3.912
+ },
+ "4": {
+ "dom": 0.037,
+ "disp": 0.171,
+ "exec": 2.418,
+ "clean": 1.129,
+ "total": 3.755
+ },
+ "5": {
+ "dom": 0.019,
+ "disp": 0.196,
+ "exec": 1.932,
+ "clean": 2.053,
+ "total": 4.2
+ },
+ "6": {
+ "dom": 0.022,
+ "disp": 0.764,
+ "exec": 1.849,
+ "clean": 1.806,
+ "total": 4.442
+ },
+ "7": {
+ "dom": 0.028,
+ "disp": 0.153,
+ "exec": 1.73,
+ "clean": 1.83,
+ "total": 3.741
+ },
+ "8": {
+ "dom": 0.02,
+ "disp": 0.136,
+ "exec": 1.71,
+ "clean": 1.831,
+ "total": 3.697
+ },
+ "9": {
+ "dom": 0.022,
+ "disp": 0.163,
+ "exec": 2.422,
+ "clean": 1.134,
+ "total": 3.741
+ },
+ "10": {
+ "dom": 0.02,
+ "disp": 0.152,
+ "exec": 1.992,
+ "clean": 1.62,
+ "total": 3.784
+ },
+ "11": {
+ "dom": 0.024,
+ "disp": 0.173,
+ "exec": 1.817,
+ "clean": 1.715,
+ "total": 3.729
+ },
+ "12": {
+ "dom": 0.022,
+ "disp": 0.138,
+ "exec": 2.053,
+ "clean": 1.523,
+ "total": 3.736
+ }
+ },
+ "stage": {
+ "dom": {
+ "values": [
+ 0.017,
+ 0.018,
+ 0.024,
+ 0.037,
+ 0.019,
+ 0.022,
+ 0.028,
+ 0.02,
+ 0.022,
+ 0.02,
+ 0.024,
+ 0.022
+ ],
+ "mean": 0.023,
+ "median": 0.022
+ },
+ "disp": {
+ "values": [
+ 0.183,
+ 0.192,
+ 0.209,
+ 0.171,
+ 0.196,
+ 0.764,
+ 0.153,
+ 0.136,
+ 0.163,
+ 0.152,
+ 0.173,
+ 0.138
+ ],
+ "mean": 0.219,
+ "median": 0.172
+ },
+ "exec": {
+ "values": [
+ 1.741,
+ 1.94,
+ 2.207,
+ 2.418,
+ 1.932,
+ 1.849,
+ 1.73,
+ 1.71,
+ 2.422,
+ 1.992,
+ 1.817,
+ 2.053
+ ],
+ "mean": 1.984,
+ "median": 1.936
+ },
+ "clean": {
+ "values": [
+ 1.462,
+ 1.57,
+ 1.472,
+ 1.129,
+ 2.053,
+ 1.806,
+ 1.83,
+ 1.831,
+ 1.134,
+ 1.62,
+ 1.715,
+ 1.523
+ ],
+ "mean": 1.595,
+ "median": 1.595
+ },
+ "total": {
+ "values": [
+ 3.403,
+ 3.72,
+ 3.912,
+ 3.755,
+ 4.2,
+ 4.442,
+ 3.741,
+ 3.697,
+ 3.741,
+ 3.784,
+ 3.729,
+ 3.736
+ ],
+ "mean": 3.822,
+ "median": 3.741
+ }
+ }
+ },
+ "iterations": 12,
+ "mean": 3.822,
+ "total": 45.86,
+ "date": "2025-12-12T12:09:40",
+ "memory": 400,
+ "maxmem": 4000,
+ "vcpus": 2,
+ "qrexec_timeout": 60,
+ "shutdown_timeout": 60,
+ "kernel": "6.17.9-1.fc41",
+ "kernelopts": "swiotlb=2048",
+ "template-buildtime": "2025-09-19 11:46:06",
+ "last-update": "2025-09-22 11:32:55",
+ "os": "Linux",
+ "os-distribution": "debian",
+ "os-version": 13,
+ "hcl-qubes": "R4.3-rc3",
+ "hcl-xen": "4.19.3",
+ "hcl-kernel": "6.17.9-1",
+ "hcl-memory": 32404
+ },
+ "debian-13-xfce_dom0-vm-api": {
+ "name": "dom0-vm-api",
+ "gui": false,
+ "concurrent": false,
+ "from_dom0": true,
+ "preload_max": 0,
+ "target_dispvm": false,
+ "admin_api": true,
+ "extra_id": "",
+ "pretty_name": "Dom0 runs simple app in another running qube (API)",
+ "template": "debian-13-xfce",
+ "api_results": {
+ "iteration": {
+ "1": {
+ "dom": 0.024,
+ "exec": 0.019,
+ "total": 0.042
+ },
+ "2": {
+ "dom": 0.017,
+ "exec": 0.017,
+ "total": 0.035
+ },
+ "3": {
+ "dom": 0.016,
+ "exec": 0.015,
+ "total": 0.031
+ },
+ "4": {
+ "dom": 0.017,
+ "exec": 0.019,
+ "total": 0.036
+ },
+ "5": {
+ "dom": 0.015,
+ "exec": 0.022,
+ "total": 0.037
+ },
+ "6": {
+ "dom": 0.014,
+ "exec": 0.022,
+ "total": 0.037
+ },
+ "7": {
+ "dom": 0.015,
+ "exec": 0.024,
+ "total": 0.039
+ },
+ "8": {
+ "dom": 0.021,
+ "exec": 0.031,
+ "total": 0.051
+ },
+ "9": {
+ "dom": 0.018,
+ "exec": 0.019,
+ "total": 0.038
+ },
+ "10": {
+ "dom": 0.015,
+ "exec": 0.022,
+ "total": 0.037
+ },
+ "11": {
+ "dom": 0.018,
+ "exec": 0.028,
+ "total": 0.046
+ },
+ "12": {
+ "dom": 0.023,
+ "exec": 0.028,
+ "total": 0.051
+ }
+ },
+ "stage": {
+ "dom": {
+ "values": [
+ 0.024,
+ 0.017,
+ 0.016,
+ 0.017,
+ 0.015,
+ 0.014,
+ 0.015,
+ 0.021,
+ 0.018,
+ 0.015,
+ 0.018,
+ 0.023
+ ],
+ "mean": 0.018,
+ "median": 0.017
+ },
+ "exec": {
+ "values": [
+ 0.019,
+ 0.017,
+ 0.015,
+ 0.019,
+ 0.022,
+ 0.022,
+ 0.024,
+ 0.031,
+ 0.019,
+ 0.022,
+ 0.028,
+ 0.028
+ ],
+ "mean": 0.022,
+ "median": 0.022
+ },
+ "total": {
+ "values": [
+ 0.042,
+ 0.035,
+ 0.031,
+ 0.036,
+ 0.037,
+ 0.037,
+ 0.039,
+ 0.051,
+ 0.038,
+ 0.037,
+ 0.046,
+ 0.051
+ ],
+ "mean": 0.04,
+ "median": 0.037
+ }
+ }
+ },
+ "iterations": 12,
+ "mean": 0.04,
+ "total": 0.481,
+ "date": "2025-12-12T12:10:32",
+ "memory": 400,
+ "maxmem": 4000,
+ "vcpus": 2,
+ "qrexec_timeout": 60,
+ "shutdown_timeout": 60,
+ "kernel": "6.17.9-1.fc41",
+ "kernelopts": "swiotlb=2048",
+ "template-buildtime": "2025-09-19 11:46:06",
+ "last-update": "2025-09-22 11:32:55",
+ "os": "Linux",
+ "os-distribution": "debian",
+ "os-version": 13,
+ "hcl-qubes": "R4.3-rc3",
+ "hcl-xen": "4.19.3",
+ "hcl-kernel": "6.17.9-1",
+ "hcl-memory": 32404
+ },
+ "debian-13-xfce_dom0-vm-gui-api": {
+ "name": "dom0-vm-gui-api",
+ "gui": true,
+ "concurrent": false,
+ "from_dom0": true,
+ "preload_max": 0,
+ "target_dispvm": false,
+ "admin_api": true,
+ "extra_id": "",
+ "pretty_name": "Dom0 runs GUI app in another running qube (API)",
+ "template": "debian-13-xfce",
+ "api_results": {
+ "iteration": {
+ "1": {
+ "dom": 0.018,
+ "exec": 0.032,
+ "total": 0.049
+ },
+ "2": {
+ "dom": 0.016,
+ "exec": 0.022,
+ "total": 0.038
+ },
+ "3": {
+ "dom": 0.016,
+ "exec": 0.022,
+ "total": 0.038
+ },
+ "4": {
+ "dom": 0.025,
+ "exec": 0.026,
+ "total": 0.05
+ },
+ "5": {
+ "dom": 0.022,
+ "exec": 0.024,
+ "total": 0.046
+ },
+ "6": {
+ "dom": 0.018,
+ "exec": 0.021,
+ "total": 0.039
+ },
+ "7": {
+ "dom": 0.022,
+ "exec": 0.023,
+ "total": 0.045
+ },
+ "8": {
+ "dom": 0.018,
+ "exec": 0.024,
+ "total": 0.042
+ },
+ "9": {
+ "dom": 0.017,
+ "exec": 0.02,
+ "total": 0.037
+ },
+ "10": {
+ "dom": 0.017,
+ "exec": 0.023,
+ "total": 0.04
+ },
+ "11": {
+ "dom": 0.027,
+ "exec": 0.05,
+ "total": 0.077
+ },
+ "12": {
+ "dom": 0.016,
+ "exec": 0.021,
+ "total": 0.036
+ }
+ },
+ "stage": {
+ "dom": {
+ "values": [
+ 0.018,
+ 0.016,
+ 0.016,
+ 0.025,
+ 0.022,
+ 0.018,
+ 0.022,
+ 0.018,
+ 0.017,
+ 0.017,
+ 0.027,
+ 0.016
+ ],
+ "mean": 0.019,
+ "median": 0.018
+ },
+ "exec": {
+ "values": [
+ 0.032,
+ 0.022,
+ 0.022,
+ 0.026,
+ 0.024,
+ 0.021,
+ 0.023,
+ 0.024,
+ 0.02,
+ 0.023,
+ 0.05,
+ 0.021
+ ],
+ "mean": 0.026,
+ "median": 0.023
+ },
+ "total": {
+ "values": [
+ 0.049,
+ 0.038,
+ 0.038,
+ 0.05,
+ 0.046,
+ 0.039,
+ 0.045,
+ 0.042,
+ 0.037,
+ 0.04,
+ 0.077,
+ 0.036
+ ],
+ "mean": 0.045,
+ "median": 0.041
+ }
+ }
+ },
+ "iterations": 12,
+ "mean": 0.045,
+ "total": 0.538,
+ "date": "2025-12-12T12:11:09",
+ "memory": 400,
+ "maxmem": 4000,
+ "vcpus": 2,
+ "qrexec_timeout": 60,
+ "shutdown_timeout": 60,
+ "kernel": "6.17.9-1.fc41",
+ "kernelopts": "swiotlb=2048",
+ "template-buildtime": "2025-09-19 11:46:06",
+ "last-update": "2025-09-22 11:32:55",
+ "os": "Linux",
+ "os-distribution": "debian",
+ "os-version": 13,
+ "hcl-qubes": "R4.3-rc3",
+ "hcl-xen": "4.19.3",
+ "hcl-kernel": "6.17.9-1",
+ "hcl-memory": 32404
+ }
+}
\ No newline at end of file
diff --git a/tests/dispvm_perf.py b/tests/dispvm_perf.py
index 5b5d09c44..f65ec20c6 100755
--- a/tests/dispvm_perf.py
+++ b/tests/dispvm_perf.py
@@ -91,7 +91,7 @@ class TestConfig:
:param bool concurrent: request concurrently
:param bool from_dom0: initiate call from dom0
:param int preload_max: number of disposables to preload
- :param bool non_dispvm: target a non disposable qube
+ :param bool target_dispvm: target or not a disposable qube
:param bool admin_api: use the Admin API directly
:param str extra_id: base test that extra ID varies from
:param str pretty_name: human-readable name
@@ -143,7 +143,7 @@ class TestConfig:
concurrent: bool = False
from_dom0: bool = False
preload_max: int = 0
- non_dispvm: bool = False
+ target_dispvm: bool = True
admin_api: bool = False
extra_id: str = ""
pretty_name: str = dataclasses.field(init=False)
@@ -171,9 +171,7 @@ def __post_init__(self):
else:
pretty_what = "simple"
- if self.non_dispvm:
- pretty_to = "in another running qube"
- else:
+ if self.target_dispvm:
disp_suffix = ""
disp_prefix = "a "
if self.preload_max:
@@ -189,6 +187,8 @@ def __post_init__(self):
disp_prefix = ""
disp_suffix = "s"
pretty_to = "in {}disposable{}".format(disp_prefix, disp_suffix)
+ else:
+ pretty_to = "in another running qube"
pretty_name = "{} runs".format(pretty_from.capitalize())
if pretty_strategy:
@@ -207,7 +207,7 @@ def __post_init__(self):
POLICY_FILE = "/run/qubes/policy.d/10-test-dispvm-perf.policy"
# MAX_PRELOAD is the number doesn't overpreload or underpreload (best
# performance) on sequential calls between the tests
-# "dispvm-preload(-(more|less))-api" (tested on fedora-42-xfce). Machines with
+# "dispvm-preload(-NUMBER)-api" (tested on fedora-42-xfce). Machines with
# different hardware or domains that boot faster or slower can theoretically
# have a different best value.
MAX_PRELOAD = 4
@@ -223,24 +223,26 @@ def __post_init__(self):
ROUND_PRECISION = 3
ALL_TESTS = [
- TestConfig("vm-vm", non_dispvm=True),
- TestConfig("vm-vm-gui", gui=True, non_dispvm=True),
- TestConfig("vm-vm-concurrent", concurrent=True, non_dispvm=True),
+ TestConfig("vm-vm", target_dispvm=False),
+ TestConfig("vm-vm-gui", gui=True, target_dispvm=False),
+ TestConfig("vm-vm-concurrent", concurrent=True, target_dispvm=False),
+ TestConfig(
+ "vm-vm-gui-concurrent", gui=True, concurrent=True, target_dispvm=False
+ ),
TestConfig(
- "vm-vm-gui-concurrent", gui=True, concurrent=True, non_dispvm=True
+ "dom0-vm-api", target_dispvm=False, admin_api=True, from_dom0=True
),
- TestConfig("dom0-vm-api", non_dispvm=True, admin_api=True, from_dom0=True),
TestConfig(
"dom0-vm-gui-api",
gui=True,
- non_dispvm=True,
+ target_dispvm=False,
admin_api=True,
from_dom0=True,
),
TestConfig(
"dom0-vm-concurrent-api",
concurrent=True,
- non_dispvm=True,
+ target_dispvm=False,
admin_api=True,
from_dom0=True,
),
@@ -248,7 +250,7 @@ def __post_init__(self):
"dom0-vm-gui-concurrent-api",
gui=True,
concurrent=True,
- non_dispvm=True,
+ target_dispvm=False,
admin_api=True,
from_dom0=True,
),
@@ -256,12 +258,6 @@ def __post_init__(self):
TestConfig("vm-dispvm-gui", gui=True),
TestConfig("vm-dispvm-concurrent", concurrent=True),
TestConfig("vm-dispvm-gui-concurrent", gui=True, concurrent=True),
- TestConfig("dom0-dispvm", from_dom0=True),
- TestConfig("dom0-dispvm-gui", gui=True, from_dom0=True),
- TestConfig("dom0-dispvm-concurrent", concurrent=True, from_dom0=True),
- TestConfig(
- "dom0-dispvm-gui-concurrent", gui=True, concurrent=True, from_dom0=True
- ),
TestConfig("vm-dispvm-preload", preload_max=MAX_PRELOAD),
TestConfig("vm-dispvm-preload-gui", gui=True, preload_max=MAX_PRELOAD),
TestConfig(
@@ -275,6 +271,12 @@ def __post_init__(self):
concurrent=True,
preload_max=MAX_CONCURRENCY,
),
+ TestConfig("dom0-dispvm", from_dom0=True),
+ TestConfig("dom0-dispvm-gui", gui=True, from_dom0=True),
+ TestConfig("dom0-dispvm-concurrent", concurrent=True, from_dom0=True),
+ TestConfig(
+ "dom0-dispvm-gui-concurrent", gui=True, concurrent=True, from_dom0=True
+ ),
TestConfig("dom0-dispvm-preload", from_dom0=True, preload_max=MAX_PRELOAD),
TestConfig(
"dom0-dispvm-preload-gui",
@@ -311,50 +313,97 @@ def __post_init__(self):
from_dom0=True,
),
TestConfig(
- "dom0-dispvm-preload-less-less-api",
- preload_max=MAX_PRELOAD - 2,
+ "dom0-dispvm-preload-1-api",
+ preload_max=1,
+ admin_api=True,
+ extra_id="dom0-dispvm-preload-api",
+ from_dom0=True,
+ ),
+ TestConfig(
+ "dom0-dispvm-preload-1-gui-api",
+ preload_max=1,
+ gui=True,
admin_api=True,
- extra_id="dispvm-preload-api",
+ extra_id="dom0-dispvm-preload-gui-api",
from_dom0=True,
),
TestConfig(
- "dom0-dispvm-preload-less-api",
- preload_max=MAX_PRELOAD - 1,
+ "dom0-dispvm-preload-2-api",
+ preload_max=2,
admin_api=True,
- extra_id="dispvm-preload-api",
+ extra_id="dom0-dispvm-preload-api",
from_dom0=True,
),
TestConfig(
- "dom0-dispvm-preload-api",
- preload_max=MAX_PRELOAD,
+ "dom0-dispvm-preload-2-gui-api",
+ preload_max=2,
+ gui=True,
admin_api=True,
+ extra_id="dom0-dispvm-preload-gui-api",
from_dom0=True,
),
TestConfig(
- "dom0-dispvm-preload-concurrent-api",
- concurrent=True,
- preload_max=MAX_CONCURRENCY,
+ "dom0-dispvm-preload-3-api",
+ preload_max=3,
admin_api=True,
+ extra_id="dom0-dispvm-preload-api",
from_dom0=True,
),
TestConfig(
- "dom0-dispvm-preload-more-api",
- preload_max=MAX_PRELOAD + 1,
+ "dom0-dispvm-preload-3-gui-api",
+ preload_max=3,
+ gui=True,
admin_api=True,
- extra_id="dispvm-preload-api",
+ extra_id="dom0-dispvm-preload-gui-api",
from_dom0=True,
),
TestConfig(
- "dom0-dispvm-preload-more-more-api",
- preload_max=MAX_PRELOAD + 2,
+ "dom0-dispvm-preload-4-api",
+ preload_max=4,
admin_api=True,
- extra_id="dispvm-preload-api",
from_dom0=True,
),
TestConfig(
- "dom0-dispvm-preload-gui-api",
+ "dom0-dispvm-preload-4-gui-api",
+ preload_max=4,
+ gui=True,
+ admin_api=True,
+ from_dom0=True,
+ ),
+ TestConfig(
+ "dom0-dispvm-preload-5-api",
+ preload_max=5,
+ admin_api=True,
+ extra_id="dom0-dispvm-preload-api",
+ from_dom0=True,
+ ),
+ TestConfig(
+ "dom0-dispvm-preload-5-gui-api",
+ preload_max=5,
+ gui=True,
+ admin_api=True,
+ extra_id="dom0-dispvm-preload-gui-api",
+ from_dom0=True,
+ ),
+ TestConfig(
+ "dom0-dispvm-preload-6-api",
+ preload_max=6,
+ admin_api=True,
+ extra_id="dom0-dispvm-preload-api",
+ from_dom0=True,
+ ),
+ TestConfig(
+ "dom0-dispvm-preload-6-gui-api",
+ preload_max=6,
gui=True,
- preload_max=MAX_PRELOAD,
+ admin_api=True,
+ extra_id="dom0-dispvm-preload-gui-api",
+ from_dom0=True,
+ ),
+ TestConfig(
+ "dom0-dispvm-preload-concurrent-api",
+ concurrent=True,
+ preload_max=MAX_CONCURRENCY,
admin_api=True,
from_dom0=True,
),
@@ -398,6 +447,8 @@ def hcl() -> dict:
"hcl-model": report["model"].rstrip(),
"hcl-bios": report["bios"].rstrip(),
"hcl-cpu": report["cpu"].rstrip(),
+ "hcl-scsi": report["scsi"].rstrip(),
+ "hcl-nvme": report["nvme"].rstrip(),
}
)
return data
@@ -493,10 +544,10 @@ def run_latency_calls(self, test):
caller += f"--dispvm={self.dvm.name} "
cmd = f"{caller} -- {service}"
else:
- if test.non_dispvm:
- target = self.vm2.name
- else:
+ if test.target_dispvm:
target = "@dispvm"
+ else:
+ target = self.vm2.name
cmd = f"qrexec-client-vm -- {target} {service}"
code = (
@@ -547,35 +598,44 @@ def call_api(self, test, service, qube):
start_time = get_time()
app = qubesadmin.Qubes()
domains = app.domains
- if test.non_dispvm:
+ target_time = None
+ startup_time = None
+ if test.target_dispvm:
+ appvm = domains[qube]
+ domain_time = get_time()
+ target_wrapper = qubesadmin.vm.DispVM.from_appvm(app, appvm)
+ target_qube = target_wrapper.create_disposable()
+ target_time = get_time()
+ if not test.preload_max:
+ target_qube.start()
+ startup_time = get_time()
+ pre_exec_time = startup_time
+ else:
+ pre_exec_time = target_time
+ else:
# Even though we already have the qube object passed from the
# class, assume we don't so we can calculate gathering.
target_qube = domains[self.vm1.name]
domain_time = get_time()
- else:
- appvm = domains[qube]
- domain_time = get_time()
- target_qube = qubesadmin.vm.DispVM.from_appvm(app, appvm)
- name = target_qube.name
- # A very small number, if it appears, it will show a bottleneck at
- # DispVM.from_appvm.
- target_time = get_time()
+ pre_exec_time = domain_time
try:
target_qube.run_service_for_stdio(service, timeout=60)
except subprocess.CalledProcessError as e:
+ name = target_qube.name
raise Exception(
f"'{name}': service '{service}' failed ({e.returncode}):"
f" {e.stdout},"
f" {e.stderr}"
)
except subprocess.TimeoutExpired as e:
+ name = target_qube.name
raise Exception(
f"'{name}': service '{service}' failed: timeout expired:"
f" {e.stdout},"
f" {e.stderr}"
)
run_service_time = get_time()
- if not test.non_dispvm:
+ if test.target_dispvm:
target_qube.cleanup()
cleanup_time = get_time()
end_time = cleanup_time
@@ -583,10 +643,16 @@ def call_api(self, test, service, qube):
end_time = get_time()
runtime = {}
runtime["dom"] = round(domain_time - start_time, ROUND_PRECISION)
- if not test.non_dispvm:
+ if test.target_dispvm:
runtime["disp"] = round(target_time - domain_time, ROUND_PRECISION)
- runtime["exec"] = round(run_service_time - target_time, ROUND_PRECISION)
- if not test.non_dispvm:
+ if not test.preload_max:
+ runtime["start"] = round(
+ startup_time - target_time, ROUND_PRECISION
+ )
+ runtime["exec"] = round(
+ run_service_time - pre_exec_time, ROUND_PRECISION
+ )
+ if test.target_dispvm:
runtime["clean"] = round(
cleanup_time - run_service_time, ROUND_PRECISION
)
@@ -611,10 +677,10 @@ async def run_latency_api_calls(self, test):
service = self.gui_service
else:
service = self.nogui_service
- if test.non_dispvm:
- qube = self.vm2
- else:
+ if test.target_dispvm:
qube = self.dvm
+ else:
+ qube = self.vm2
results = {}
results["api_results"] = {}
@@ -771,10 +837,10 @@ async def run_test(self, test: TestConfig):
with open(POLICY_FILE, "w", encoding="ascii") as policy:
gui_prefix = f"{self.gui_service} * {self.vm1.name}"
nogui_prefix = f"{self.nogui_service} * {self.vm1.name}"
- if test.non_dispvm:
- target = f"{self.vm2.name}"
- else:
+ if test.target_dispvm:
target = "@dispvm"
+ else:
+ target = f"{self.vm2.name}"
policy.write(
f"{gui_prefix} {target} allow\n"
f"{nogui_prefix} {target} allow\n"
@@ -848,7 +914,7 @@ async def run_test(self, test: TestConfig):
if not os.getenv("QUBES_TEST_SKIP_TEARDOWN_SLEEP"):
logger.info("Load before sleep: '%s'", get_load())
delay = 5
- if not test.non_dispvm:
+ if test.target_dispvm:
delay += 10
if test.gui:
delay += 2
diff --git a/tests/dispvm_perf_reader.py b/tests/dispvm_perf_reader.py
index cab868139..c3db859af 100755
--- a/tests/dispvm_perf_reader.py
+++ b/tests/dispvm_perf_reader.py
@@ -235,12 +235,23 @@ def __init__(
)
if not self.data:
logging.critical(
- "Default template specified not found: '%s'",
+ "Template specified not found: '%s'",
self.default_template,
)
sys.exit(1)
else:
logging.info("Default template not specified, using newest fedora")
+ fedora_templates = [
+ v
+ for v in self.orig_data.values()
+ if v["os-distribution"] == "fedora"
+ ]
+ if not fedora_templates:
+ logging.critical(
+ "Tried to find any fedora template but ultimately failed",
+ self.default_template,
+ )
+ sys.exit(1)
fedora_template = max(
(
v
@@ -279,7 +290,7 @@ def __init__(
self.dispvm_api_tests = filter_data(
self.api_tests,
{
- "non_dispvm": False,
+ "target_dispvm": True,
"admin_api": True,
"concurrent": False,
"extra_id": "",
@@ -290,7 +301,7 @@ def __init__(
self.dispvm_tests = filter_data(
self.data,
{
- "non_dispvm": False,
+ "target_dispvm": True,
"admin_api": False,
"concurrent": False,
"extra_id": "",
@@ -311,15 +322,30 @@ def __init__(
for name, test in self.dispvm_tests.items()
]
+ self.vm_stage_dict = {
+ "dom": {
+ "color": COLORS["Icon Dark Gray"],
+ "legend": "Initialization",
+ },
+ "exec": {"color": COLORS["Warning Orange"], "legend": "Execution"},
+ }
+
self.stage_dict = {
- "dom": {"color": COLORS["Icon Dark Gray"], "legend": "Others"},
- "disp": {"color": COLORS["Middle Gray"], "legend": "Others"},
+ "dom": {
+ "color": COLORS["Icon Dark Gray"],
+ "legend": "Initialization",
+ },
+ "disp": {"color": COLORS["Purple"], "legend": "Creation"},
+ "start": {"color": COLORS["Danger Red"], "legend": "Startup"},
"exec": {"color": COLORS["Warning Orange"], "legend": "Execution"},
"clean": {"color": COLORS["Sub Gray"], "legend": "Cleanup"},
}
self.preload_stage_dict = {
- "dom": {"color": COLORS["Icon Dark Gray"], "legend": "Others"},
- "disp": {"color": COLORS["Middle Gray"], "legend": "Others"},
+ "dom": {
+ "color": COLORS["Icon Dark Gray"],
+ "legend": "Initialization",
+ },
+ "disp": {"color": COLORS["Purple"], "legend": "Creation"},
"exec": {"color": COLORS["Primary Blue"], "legend": "Execution"},
"clean": {"color": COLORS["Sub Gray"], "legend": "Cleanup"},
}
@@ -328,7 +354,7 @@ def __init__(
for test, result in self.api_tests.items():
if result.get("api_results", {}).get("stage", {}):
self.stages.extend(result["api_results"]["stage"].keys())
- self.stage_order = ["dom", "disp", "exec", "clean", "total"]
+ self.stage_order = [*self.stage_dict.keys(), "total"]
self.stages = sorted(
list(set(self.stages)),
key=lambda s: (
@@ -343,7 +369,6 @@ def run(self) -> None:
"""Loop through all enabled graphs."""
avail_graphs = get_graphs()
failed_graphs = []
- # TODO: almost every graph should target all templates
for graph in avail_graphs:
method = getattr(self, "graph_" + graph.replace("-", "_"))
if not (not self.graphs or graph in self.graphs):
@@ -372,7 +397,7 @@ def end(self, plot, name: str = "", skip_fname: bool = False) -> None:
else:
if not skip_fname:
name = get_fname()
- name = self.default_template + "_" + name
+ name = "dispvm_perf-" + self.default_template + "_" + name
if self.output_dir:
logging.info("Saving figure %s", name)
@@ -404,7 +429,7 @@ def bar_plot(
supxlabel: str = "",
file_prefix: str = "",
) -> None:
- # pylint: disable=too-many-locals
+ # pylint: disable=too-many-locals,too-many-positional-arguments
"""
Assemble figure of bar plot where normal and preload tests are grouped.
Each test passed is subplotted.
@@ -555,42 +580,53 @@ def graph_00_specs(self) -> None:
"""System specifications graph."""
first_test = list(self.data.keys())[0]
data = self.data[first_test]
- specs = {
- "date": data["date"],
- "template-buildtime": data["template-buildtime"],
- "kernel": data["kernel"],
- "hcl-memory": data["hcl-memory"],
- "hcl-certified": data["hcl-certified"],
- "hcl-qubes": data["hcl-qubes"],
- "hcl-xen": data["hcl-xen"],
- "hcl-model": data["hcl-model"],
- "hcl-bios": data["hcl-bios"],
- "hcl-cpu": data["hcl-cpu"],
- }
specs_text = """
System specifications:
- - Date: {}
- - Template: {}
- - Template build time: {}
- - Certified: {}
- - Qubes: {}
- - Kernel: {}
- - Xen: {}
- - RAM: {} MiB
- - CPU: {}
- - BIOS: {}
+ - Global:
+ - Date: {date}
+ - Qubes: {hcl_qubes}
+ - Xen: {hcl_xen}
+ - Global Kernel: {hcl_kernel}
+ - Template:
+ - Name: {template}
+ - Build time: {template_buildtime}
+ - Last update: {template_last_update}
+ - Virtual CPUs: {vcpus}
+ - Bootstrap memory: {memory}
+ - Maximum memory: {maxmem}
+ - Kernel: {kernel}
+ - Kernel options: {kernelopts}
+ - Hardware:
+ - Certified: {hcl_certified}
+ - Brand: {hcl_brand}
+ - Model: {hcl_model}
+ - CPU: {hcl_cpu}
+ - RAM: {hcl_memory} MiB
+ - BIOS: {hcl_bios}
+ - SCSI: {hcl_scsi}
+ - NVMe: {hcl_nvme}
""".format(
- specs["date"],
- self.default_template,
- specs["template-buildtime"],
- specs["hcl-certified"],
- specs["hcl-qubes"],
- specs["kernel"],
- specs["hcl-xen"],
- specs["hcl-memory"],
- specs["hcl-cpu"],
- specs["hcl-bios"],
+ date=data["date"],
+ template=self.default_template,
+ template_buildtime=data["template-buildtime"],
+ template_last_update=data["last-update"] or None,
+ memory=data["memory"],
+ maxmem=data["maxmem"],
+ vcpus=data["vcpus"],
+ kernel=data["kernel"],
+ kernelopts=data["kernelopts"],
+ hcl_memory=data["hcl-memory"],
+ hcl_certified=data.get("hcl-certified"),
+ hcl_qubes=data["hcl-qubes"],
+ hcl_xen=data["hcl-xen"],
+ hcl_kernel=data["hcl-kernel"],
+ hcl_brand=data.get("hcl-brand"),
+ hcl_model=data.get("hcl-model"),
+ hcl_bios=data.get("hcl-bios"),
+ hcl_cpu=data.get("hcl-cpu"),
+ hcl_scsi=data.get("hcl-scsi"),
+ hcl_nvme=data.get("hcl-nvme"),
)
fig = plt.figure(figsize=(2 * WIDTH, 2 * HEIGHT))
fig.clf()
@@ -625,6 +661,7 @@ def graph_01_bar(self) -> None:
def graph_02_stage_stack(self) -> None:
"""Stacked bar by stage graph."""
+ # pylint: disable=too-many-locals
tests = filter_data(self.dispvm_api_tests, {"preload_max": 0})
assert_items_match(tests, ["iterations"])
iterations = get_value(tests, "iterations")[0]
@@ -635,15 +672,23 @@ def graph_02_stage_stack(self) -> None:
{"preload_max": operator.ge},
)
- important_stages = ["exec", "clean", "total"]
label_array = np.arange(len(tests_names_pretty))
label_count = len(tests_names_pretty)
width = 0.8 / label_count
+ # Don't count "dom" stage, when there are other large values, it
+ # overlaps the label.
up_to_exec = self.stages[:-2]
- for num, stage_intro in enumerate([up_to_exec, self.stages]):
+ up_to_exec.remove("dom")
+ up_to_last = self.stages
+ up_to_last.remove("dom")
+ important_stages = up_to_last
+ # pylint: disable=too-many-nested-blocks
+ for num, stage_intro in enumerate([up_to_exec, up_to_last]):
fig, axs = plt.subplots(figsize=(WIDTH * 3, HEIGHT * 3))
bottom = np.zeros(len(tests_names_pretty))
bottom_preload = np.zeros(len(tests_names_pretty))
+ stored_means_stage = []
+ stored_preload_means_stage = []
for stage in stage_intro:
means_stage = np.array(
[
@@ -651,18 +696,27 @@ def graph_02_stage_stack(self) -> None:
for test in tests.values()
]
)
- preload_means_stage = np.array(
- [
- test["api_results"]["stage"][stage]["mean"]
- for test in preload_tests.values()
- ]
- )
+ stored_means_stage.append(means_stage)
+ if stage == "start":
+ preload_means_stage = np.zeros(len(means_stage))
+ else:
+ preload_means_stage = np.array(
+ [
+ test["api_results"]["stage"][stage]["mean"]
+ for test in preload_tests.values()
+ ]
+ )
+ stored_preload_means_stage.append(preload_means_stage)
assert_items_length_match(tests, preload_tests)
if stage in ["exec", "total"]:
+ sum_means_stage = [sum(x) for x in zip(*stored_means_stage)]
+ sum_preload_means_stage = [
+ sum(x) for x in zip(*stored_preload_means_stage)
+ ]
ratio = np.divide(
- means_stage,
- preload_means_stage,
+ sum_means_stage,
+ sum_preload_means_stage,
out=np.zeros_like(means_stage, dtype=float),
where=preload_means_stage != 0,
)
@@ -673,12 +727,21 @@ def graph_02_stage_stack(self) -> None:
if stage == "exec" and "total" in stage_intro:
break
xpos = label_array[i]
- min_mean = min(means_stage[i], preload_means_stage[i])
- max_mean = max(means_stage[i], preload_means_stage[i])
- height = max(
- min_mean + 1, min_mean + ((max_mean - min_mean) / 2)
- )
- height = min_mean + 1
+ if stage == "total":
+ min_mean = min(
+ means_stage[i], preload_means_stage[i]
+ )
+ max_mean = max(
+ means_stage[i], preload_means_stage[i]
+ )
+ height = max(
+ min_mean + 1,
+ min_mean + ((max_mean - min_mean) / 2),
+ )
+ else:
+ height = (
+ sum_preload_means_stage[i] + sum_means_stage[i]
+ ) / 2
if ratio[i] > 1:
color = COLORS["Success Green"]
else:
@@ -704,19 +767,21 @@ def graph_02_stage_stack(self) -> None:
bottom += means_stage
positions = label_array + width / 2
- bars_preload = axs.bar(
- positions,
- preload_means_stage,
- width=width,
- bottom=bottom_preload,
- color=self.preload_stage_dict[stage]["color"],
- )
+ if stage != "start":
+ bars_preload = axs.bar(
+ positions,
+ preload_means_stage,
+ width=width,
+ bottom=bottom_preload,
+ color=self.preload_stage_dict[stage]["color"],
+ )
bottom_preload += preload_means_stage
if (
stage in important_stages
- or stage == list(self.stage_dict.keys())[-1]
+ or stage == list(self.stages)[-1]
):
+ # pylint: disable=possibly-used-before-assignment
for bbar in [bars_normal, bars_preload]:
axs.bar_label(
bbar,
@@ -724,7 +789,26 @@ def graph_02_stage_stack(self) -> None:
label_type="center",
fontsize="xx-large",
)
-
+ if stage not in ["exec", "clean"]:
+ continue
+ if stage == "exec" and "total" in stage_intro:
+ continue
+ axs.bar_label(bbar, fmt="%.2f", fontsize="xx-large")
+
+ means_stage_total = [
+ test["api_results"]["stage"]["total"]["mean"]
+ for test in tests.values()
+ ]
+ preload_means_stage_total = [
+ test["api_results"]["stage"]["total"]["mean"]
+ for test in preload_tests.values()
+ ]
+ top_value = math.ceil(
+ max(*means_stage_total, *preload_means_stage_total)
+ )
+ top_tick = int(10 * top_value / 10)
+ yticks = [round(x, 1) for x in np.linspace(0, top_tick, 4)]
+ plt.yticks(yticks)
plt.xticks(label_array, tests_names_pretty)
plt.ylabel("Time (s)")
pretty_iterations = " over {} iterations".format(iterations)
@@ -737,7 +821,7 @@ def graph_02_stage_stack(self) -> None:
if stage in stage_intro
]
legend_handles.insert(
- -2,
+ -3,
*[
matplotlib.patches.Patch(
color=value["color"], label="Preload " + value["legend"]
@@ -760,20 +844,41 @@ def graph_02_stage_stack(self) -> None:
wrap=True,
color=CAPTION_COLOR,
)
- self.end(plt, str(num))
+ self.end(plt, str(num) + "_until_" + stage_intro[-1])
def graph_03_stage_dist(self) -> None:
"""Scattered and jittered stage distribution graph."""
- tests = self.dispvm_api_tests
+ tests = filter_data(
+ self.api_tests,
+ {
+ "concurrent": False,
+ "extra_id": "",
+ },
+ )
assert_items_match(tests, ["iterations"])
tests_names_pretty = get_value(tests, "pretty_name")
- for num, stage in enumerate(["exec", "clean", "total"]):
+ for num, stage in enumerate(self.stages + ["response"]):
stage_values = []
for _, test in tests.items():
- stage_values.append(
- test["api_results"]["stage"][stage]["values"]
- )
- fig, axs = plt.subplots(figsize=(WIDTH * 3, HEIGHT * 3))
+ api_results = test["api_results"]["stage"]
+ if stage == "response":
+ if test["target_dispvm"]:
+ stage_values.append(
+ [
+ x - y
+ for x, y in zip(
+ api_results["total"]["values"],
+ api_results["clean"]["values"],
+ )
+ ]
+ )
+ else:
+ stage_values.append(api_results["total"]["values"])
+ else:
+ stage_values.append(
+ api_results.get(stage, {}).get("values", [])
+ )
+ fig, axs = plt.subplots(figsize=(WIDTH * 4, HEIGHT * 4))
x_pos = np.arange(1, len(stage_values) + 1)
jitter_strength = 0.1
for i, values in enumerate(stage_values, start=1):
@@ -788,7 +893,7 @@ def graph_03_stage_dist(self) -> None:
axs.scatter(x_jittered, values, s=100, alpha=0.8, color=color)
axs.set_xticks(x_pos, wrap_text(tests_names_pretty, 25))
axs.set_ylabel("Time (s)")
- if stage == "total":
+ if stage in ["total", "response"]:
stage_pretty = stage
else:
stage_pretty = self.stage_dict[stage]["legend"].lower()
@@ -807,72 +912,166 @@ def graph_03_stage_dist(self) -> None:
def graph_04_line(self) -> None:
"""Line graph of performance of each iteration."""
- def filter_preload(value, cond): # pylint: disable=unused-argument
- return value == 0 or (3 <= value <= 5)
+ def filter_preload_small(value, _cond):
+ return value <= 3
- tests = filter_data(
+ def filter_preload(value, _cond):
+ return value == 0 or (4 <= value <= 6)
+
+ base_tests = filter_data(
self.api_tests,
{
- "non_dispvm": False,
"admin_api": True,
"concurrent": False,
- "gui": False,
- "preload_max": None,
- },
- {
- "preload_max": filter_preload,
},
)
+ base_tests_gui = filter_data(base_tests, {"gui": True})
+ base_tests_nogui = filter_data(base_tests, {"gui": False})
+
+ tests = filter_data(
+ base_tests_nogui,
+ {"preload_max": None},
+ {"preload_max": filter_preload},
+ )
+ tests_small = filter_data(
+ base_tests_nogui,
+ {"preload_max": None},
+ {"preload_max": filter_preload_small},
+ )
+
+ tests_gui = filter_data(
+ base_tests_gui,
+ {"preload_max": None},
+ {"preload_max": filter_preload},
+ )
+ tests_small_gui = filter_data(
+ base_tests_gui,
+ {"preload_max": None},
+ {"preload_max": filter_preload_small},
+ )
+
tests = dict(sorted(tests.items(), key=lambda x: x[1]["preload_max"]))
- for num, stage in enumerate(["exec", "clean", "total"]):
- fig, axs = plt.subplots(figsize=(WIDTH * 3, HEIGHT * 3))
- points_seen = set()
- for test in tests.values():
- name = test["pretty_name"]
- iteration_data = test.get("api_results", {}).get(
- "iteration", {}
- )
- iterations = range(1, test["iterations"] + 1)
- stage_data = test.get("api_results", {}).get("stage", {})
- mean = round(stage_data[stage]["mean"], 1)
- times = [iteration_data[str(it)][stage] for it in iterations]
- axs.plot(
- iterations,
- times,
- label=f"{name} \u03bc {mean}",
- linestyle="--",
- linewidth=3,
- )
- rounder = 1
- for x_val, y_val in zip(iterations, times):
- if (x_val, round(y_val, rounder)) in points_seen:
+ tests_small = dict(
+ sorted(tests_small.items(), key=lambda x: x[1]["preload_max"])
+ )
+ tests_gui = dict(
+ sorted(tests_gui.items(), key=lambda x: x[1]["preload_max"])
+ )
+ tests_small_gui = dict(
+ sorted(tests_small_gui.items(), key=lambda x: x[1]["preload_max"])
+ )
+ all_tests = {
+ "nogui-small": {
+ "for simple calls with few preloaded disposables": tests_small
+ },
+ "gui-small": {
+ "for GUI calls with few preloaded disposables": tests_small_gui
+ },
+ "nogui-large": {
+ "for simple calls with some preloaded disposables": tests
+ },
+ "gui-large": {
+ "for GUI calls with some preloaded disposables": tests_gui
+ },
+ }
+
+ num_list = []
+ rounder = 2
+ # pylint: disable=too-many-nested-blocks
+ for idx, (test_type, test_value) in enumerate(all_tests.items()):
+ desc = list(test_value.keys())[0]
+ tests = list(test_value.values())[0]
+ for num, stage in enumerate(self.stages + ["response"]):
+ num_list.append(num)
+ fig, axs = plt.subplots(figsize=(WIDTH * 3, HEIGHT * 3))
+ points_seen = set()
+ for test in tests.values():
+ if (
+ not test["target_dispvm"]
+ and stage != "response"
+ and stage not in self.vm_stage_dict
+ ):
continue
- points_seen.add((x_val, round(y_val, rounder)))
- axs.text(
- x_val,
- y_val,
- str(round(y_val, rounder)),
- ha="center",
- fontsize="xx-large",
+ name = test["pretty_name"]
+ iteration_data = test.get("api_results", {}).get(
+ "iteration", {}
)
- if stage == "total":
- stage_pretty = stage
- else:
- stage_pretty = self.stage_dict[stage]["legend"].lower()
- axs.set_ylabel("Time (seconds)")
- axs.set_title(f"{stage_pretty.capitalize()} time per iteration")
- axs.set_xticks(iterations)
- axs.legend()
- caption = (
- f"Compares the {stage_pretty} per iteration of normal "
- + "disposables with preloaded disposables."
- )
- fig.supxlabel(
- caption,
- wrap=True,
- color=CAPTION_COLOR,
- )
- self.end(plt, str(num) + "_" + stage)
+ iterations = range(1, test["iterations"] + 1)
+ stage_data = test.get("api_results", {}).get("stage", {})
+ if not (stage == "start" and test["preload_max"] > 0):
+ if stage == "response":
+ if test["target_dispvm"]:
+ mean_response = (
+ stage_data["total"]["mean"]
+ - stage_data["clean"]["mean"]
+ )
+ mean = round(mean_response, rounder)
+ times = [
+ iteration_data[str(it)]["total"]
+ - iteration_data[str(it)]["clean"]
+ for it in iterations
+ ]
+ else:
+ mean_response = stage_data["total"]["mean"]
+ mean = round(mean_response, rounder)
+ times = [
+ iteration_data[str(it)]["total"]
+ for it in iterations
+ ]
+ else:
+ mean = round(stage_data[stage]["mean"], rounder)
+ times = [
+ iteration_data[str(it)][stage]
+ for it in iterations
+ ]
+ else:
+ continue
+ axs.plot(
+ iterations,
+ times,
+ label=f"{name} \u03bc {mean}",
+ linestyle="--",
+ linewidth=3,
+ )
+ for x_val, y_val in zip(iterations, times):
+ if (x_val, round(y_val, rounder)) in points_seen:
+ continue
+ points_seen.add((x_val, round(y_val, rounder)))
+ axs.text(
+ x_val,
+ y_val,
+ str(round(y_val, rounder)),
+ ha="center",
+ fontsize="xx-large",
+ )
+ if stage in ["total", "response"]:
+ stage_pretty = stage
+ else:
+ stage_pretty = self.stage_dict[stage]["legend"].lower()
+ axs.set_ylabel("Time (seconds)")
+ axs.set_title(
+ f"{stage_pretty.capitalize()} time per iteration {desc}"
+ )
+ axs.set_xticks(iterations)
+ axs.legend()
+ caption = (
+ f"Compares the {stage_pretty} per iteration of normal "
+ + "disposables with preloaded disposables."
+ )
+ fig.supxlabel(
+ caption,
+ wrap=True,
+ color=CAPTION_COLOR,
+ )
+ idx_str = str(idx)
+ if idx < 10:
+ idx_str = "0" + idx_str
+ self.end(
+ plt,
+ "{}_{}_{}_{}".format(
+ str(idx_str), str(num), stage, test_type
+ ),
+ )
def graph_08_template(self) -> None:
"""Graph including all available templates."""
@@ -880,7 +1079,7 @@ def graph_08_template(self) -> None:
self.orig_data,
{
"admin_api": True,
- "non_dispvm": False,
+ "target_dispvm": True,
"extra_id": "",
"concurrent": False,
},
@@ -910,11 +1109,19 @@ def graph_08_template(self) -> None:
def graph_09_method(self) -> None:
"""Graph including different callers comparing GUI and concurrency."""
orig_tests = filter_data(
- self.data, {"non_dispvm": False, "extra_id": ""}
+ self.data, {"target_dispvm": True, "extra_id": ""}
)
+
+ concurrent_tests = filter_data(orig_tests, {"concurrent": True})
+ gui_tests = filter_data(orig_tests, {"gui": True})
for query in ["concurrent-gui", "gui", "concurrent", "gui-concurrent"]:
+ if ("concurrent" in query and not concurrent_tests) or (
+ "gui" in query and not gui_tests
+ ):
+ logging.warning("Skipped query without tests: %s", query)
+ continue
if query == "concurrent-gui":
- tests = filter_data(orig_tests, {"concurrent": True})
+ tests = concurrent_tests
query_name = "with concurrency (with and without GUI),"
query_file = "wconc_nogui_gui"
elif query == "gui":
@@ -926,7 +1133,7 @@ def graph_09_method(self) -> None:
query_name = "with and without concurrency,"
query_file = "noconc_conc"
elif query == "gui-concurrent":
- tests = filter_data(orig_tests, {"gui": True})
+ tests = gui_tests
query_name = "with GUI (with and without concurrency),"
query_file = "wgui_noconc_conc"
else:
@@ -941,7 +1148,7 @@ def graph_09_method(self) -> None:
vm_qrexec = filter_data(
tests, {"from_dom0": False, "admin_api": False}
)
-
+ method_tests = [t for t in [dom0_api, dom0_qvm, vm_qrexec] if t]
for key in ["mean", "total"]:
preload_tests = filter_data(
tests, {"preload_max": 1}, {"preload_max": operator.ge}
@@ -949,11 +1156,11 @@ def graph_09_method(self) -> None:
assert_items_match(preload_tests, ["preload_max"])
preload_max = get_value(preload_tests, "preload_max")[0]
caption = (
- f"Compares workflows of normal disposables with "
- + "{preload_max} preloaded disposables."
+ "Compares workflows of normal disposables with "
+ + f"{preload_max} preloaded disposables."
)
self.bar_plot(
- [dom0_api, dom0_qvm, vm_qrexec],
+ method_tests,
titles=["dom0 API", "dom0 qvm", "qube qrexec-client-vm"],
title_prefix=False,
keys=[key],
@@ -1057,6 +1264,7 @@ def main() -> None:
logging.info("Loaded data")
if args.output_dir:
os.makedirs(args.output_dir, exist_ok=True)
+ logging.info("Output directory is %s", repr(args.output_dir))
graph = Graph(
data,
default_template=args.template,