Skip to content

Commit 16f7bd0

Browse files
committed
Add preload disposable option to advanced settings
For: QubesOS/qubes-issues#1512
1 parent 31c9d53 commit 16f7bd0

File tree

4 files changed

+239
-38
lines changed

4 files changed

+239
-38
lines changed

.gitlab-ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ checks:tests:
2121
stage: checks
2222
variables:
2323
PYTEST_ADDOPTS: "--color=yes"
24+
BLACK_ARGS: -l88 -v --diff --color --check
2425
before_script: &before-script
2526
- "PATH=$PATH:$HOME/.local/bin"
2627
- sudo dnf install -y python3-gobject python3-pytest python3-pytest-asyncio
27-
python3-coverage sequoia-sqv python3-pip python3-pytest-qt
28+
python3-coverage sequoia-sqv python3-pip python3-pytest-qt
2829
xorg-x11-server-Xvfb python3-PyQt6-devel python3-pyqt6
2930
- pip3 install --quiet -r ci/requirements.txt
3031
- git clone https://github.com/QubesOS/qubes-core-admin-client ~/core-admin-client

qubesmanager/settings.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ def run(self):
126126

127127
for vm in vms_to_refresh:
128128
self.button.setText(
129-
self.tr(
130-
"Refresh in progress (refreshing applications " "from {})"
131-
).format(vm.name)
129+
self.tr("Refresh in progress (refreshing applications from {})").format(
130+
vm.name
131+
)
132132
)
133133
try:
134134
if not utils.is_running(vm, True):
@@ -208,6 +208,11 @@ def __init__(
208208
self.init_mem.editingFinished.connect(self.check_mem_changes)
209209
self.max_mem_size.editingFinished.connect(self.check_mem_changes)
210210
self.check_mem_changes()
211+
212+
self.dvm_template_checkbox.stateChanged.connect(
213+
self.dvm_template_checkbox_changed
214+
)
215+
211216
self.boot_from_device_button.clicked.connect(
212217
self.boot_from_cdrom_button_pressed
213218
)
@@ -962,7 +967,7 @@ def clone_vm(self):
962967

963968
######### advanced tab
964969

965-
def __init_advanced_tab__(self):
970+
def __init_advanced_tab__(self): # pylint: disable=too-many-statements
966971

967972
vm_memory = getattr(self.vm, "memory", None)
968973
vm_maxmem = getattr(self.vm, "maxmem", None)
@@ -1097,6 +1102,23 @@ def __init_advanced_tab__(self):
10971102
if not hasattr(self.vm, "template_for_dispvms"):
10981103
self.dvm_template_checkbox.setEnabled(False)
10991104

1105+
self.preload_dispvm.setMinimum(0)
1106+
self.preload_dispvm.setMaximum(50)
1107+
if (
1108+
self.vm.name == getattr(self.vm.app, "default_dispvm", None)
1109+
and self.vm.app.domains["dom0"].features.get("preload-dispvm-max", None)
1110+
is not None
1111+
):
1112+
self.warn_default_dispvm_preload_label.setVisible(True)
1113+
else:
1114+
self.warn_default_dispvm_preload_label.setVisible(False)
1115+
self.preload_dispvm.setEnabled(self.dvm_template_checkbox.isChecked())
1116+
if self.preload_dispvm.isEnabled():
1117+
vm_preload_dispvm = int(self.vm.features.get("preload-dispvm-max") or 0)
1118+
else:
1119+
vm_preload_dispvm = 0
1120+
self.preload_dispvm.setValue(vm_preload_dispvm)
1121+
11001122
self.provides_network_checkbox.setChecked(
11011123
getattr(self.vm, "provides_network", False)
11021124
)
@@ -1106,8 +1128,8 @@ def __init_advanced_tab__(self):
11061128
self.provides_network_checkbox.setEnabled(False)
11071129
self.provides_network_checkbox.setToolTip(
11081130
self.tr(
1109-
"Cannot change this setting while this qube is used as a "
1110-
"NetVM by the following qubes:\n"
1131+
"Cannot change this setting while this qube is used as "
1132+
"a NetVM by the following qubes:\n"
11111133
)
11121134
+ "\n".join(domains_using)
11131135
)
@@ -1212,6 +1234,17 @@ def __apply_advanced_tab__(self):
12121234
if self.vcpus.isEnabled() and self.vcpus.value() != int(self.vm.vcpus):
12131235
self.vm.vcpus = self.vcpus.value()
12141236

1237+
if (
1238+
self.dvm_template_checkbox.isChecked()
1239+
and self.preload_dispvm.isEnabled()
1240+
):
1241+
curr_preload_dispvm = (
1242+
int(self.vm.features.get("preload-dispvm-max") or 0)
1243+
)
1244+
preload_dispvm = self.preload_dispvm.value()
1245+
if preload_dispvm != curr_preload_dispvm:
1246+
self.vm.features["preload-dispvm-max"] = preload_dispvm
1247+
12151248
except qubesadmin.exc.QubesException as ex:
12161249
msg.append(str(ex))
12171250

@@ -1318,6 +1351,9 @@ def include_in_balancing_changed(self, state):
13181351
if self.include_in_balancing.isChecked():
13191352
self.check_mem_changes()
13201353

1354+
def dvm_template_checkbox_changed(self, state): # pylint: disable=unused-argument
1355+
self.preload_dispvm.setEnabled(self.dvm_template_checkbox.isChecked())
1356+
13211357
def boot_from_cdrom_button_pressed(self):
13221358
boot_dialog = bootfromdevice.VMBootFromDeviceWindow(
13231359
vm=self.vm.name, qapp=self.qapp, qubesapp=self.qubesapp, parent=self

qubesmanager/tests/test_vm_settings.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,20 @@ def settings_fixture(
192192
mock_subprocess.side_effect = mock_subprocess_complex
193193
expected_call = (vm.name, "admin.vm.notes.Get", None, None)
194194
test_qubes_app.expected_calls[expected_call] = b"0\x00Some Notes\x00"
195+
expected_call_preload = (
196+
vm.name,
197+
"admin.vm.feature.Get",
198+
"preload-dispvm-max",
199+
None,
200+
)
201+
test_qubes_app.expected_calls[expected_call_preload] = b"0\x00"
202+
expected_call_preload = (
203+
"dom0",
204+
"admin.vm.feature.Get",
205+
"preload-dispvm-max",
206+
None,
207+
)
208+
test_qubes_app.expected_calls[expected_call_preload] = b"0\x00"
195209
vms = vm_settings.VMSettingsWindow(vm, page, qapp, test_qubes_app)
196210
yield vms, page, vm.name
197211

@@ -288,6 +302,10 @@ def test_002_data(settings_fixture):
288302
assert settings_window.dvm_template_checkbox.isChecked() == getattr(
289303
vm, "template_for_dispvms", False
290304
)
305+
assert (
306+
settings_window.preload_dispvm.isEnabled()
307+
== settings_window.dvm_template_checkbox.isChecked()
308+
)
291309

292310
if hasattr(vm, "default_dispvm"):
293311
if vm.property_is_default("default_dispvm"):
@@ -916,9 +934,18 @@ def test_206_dispvmtempl(settings_fixture):
916934
assert settings_window.dvm_template_checkbox.isEnabled()
917935

918936
assert settings_window.dvm_template_checkbox.isChecked() == vm.template_for_dispvms
937+
assert (
938+
settings_window.preload_dispvm.isEnabled()
939+
== settings_window.dvm_template_checkbox.isChecked()
940+
)
919941

920942
settings_window.dvm_template_checkbox.setChecked(not vm.template_for_dispvms)
921943

944+
assert (
945+
settings_window.preload_dispvm.isEnabled()
946+
== settings_window.dvm_template_checkbox.isChecked()
947+
)
948+
922949
expected_calls = [
923950
(
924951
vm.name,

ui/settingsdlg.ui

Lines changed: 168 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,13 @@ border-width: 1px;</string>
742742
<property name="topMargin">
743743
<number>15</number>
744744
</property>
745+
<item row="1" column="0">
746+
<widget class="QCheckBox" name="run_in_debug_mode">
747+
<property name="text">
748+
<string>Run in debug mode</string>
749+
</property>
750+
</widget>
751+
</item>
745752
<item row="2" column="0" colspan="2">
746753
<layout class="QHBoxLayout" name="horizontalLayout_prohibit_start">
747754
<item>
@@ -780,40 +787,143 @@ border-width: 1px;</string>
780787
</property>
781788
</widget>
782789
</item>
783-
<item row="8" column="0" colspan="2">
784-
<widget class="QPushButton" name="boot_from_device_button">
790+
<item row="5" column="0">
791+
<widget class="QLabel" name="preload_dispvm_label">
792+
<property name="toolTip">
793+
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Configure how many disposables this template must preload.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
794+
</property>
785795
<property name="text">
786-
<string>Boot qube from DISC or block device</string>
796+
<string>Preload disposables:</string>
787797
</property>
788798
</widget>
789799
</item>
790-
<item row="9" column="0" colspan="2">
791-
<layout class="QHBoxLayout" name="horizontalLayout_8">
792-
<item>
793-
<widget class="QPushButton" name="seamless_on_button">
794-
<property name="toolTip">
795-
<string>Windows (with Qubes Windows Tools installed) only.
796-
The qube must be running to enable seamless mode. This setting is not persistent.</string>
797-
</property>
798-
<property name="text">
799-
<string>Enable seamless mode</string>
800-
</property>
801-
</widget>
802-
</item>
803-
<item>
804-
<widget class="QPushButton" name="seamless_off_button">
805-
<property name="toolTip">
806-
<string>Windows (with Qubes Windows Tools installed) only.
807-
The qube must be running to disable seamless mode. This setting is not persistent.</string>
808-
</property>
809-
<property name="text">
810-
<string>Disable seamless mode</string>
811-
</property>
812-
</widget>
813-
</item>
814-
</layout>
800+
<item row="5" column="1">
801+
<widget class="QSpinBox" name="preload_dispvm">
802+
<property name="sizePolicy">
803+
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
804+
<horstretch>0</horstretch>
805+
<verstretch>0</verstretch>
806+
</sizepolicy>
807+
</property>
808+
<property name="alignment">
809+
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
810+
</property>
811+
<property name="maximum">
812+
<number>50</number>
813+
</property>
814+
</widget>
815815
</item>
816816
<item row="6" column="0" colspan="2">
817+
<widget class="QLabel" name="warn_default_dispvm_preload_label">
818+
<property name="palette">
819+
<palette>
820+
<active>
821+
<colorrole role="WindowText">
822+
<brush brushstyle="SolidPattern">
823+
<color alpha="255">
824+
<red>239</red>
825+
<green>41</green>
826+
<blue>41</blue>
827+
</color>
828+
</brush>
829+
</colorrole>
830+
<colorrole role="Text">
831+
<brush brushstyle="SolidPattern">
832+
<color alpha="255">
833+
<red>239</red>
834+
<green>41</green>
835+
<blue>41</blue>
836+
</color>
837+
</brush>
838+
</colorrole>
839+
<colorrole role="PlaceholderText">
840+
<brush brushstyle="SolidPattern">
841+
<color alpha="128">
842+
<red>239</red>
843+
<green>41</green>
844+
<blue>41</blue>
845+
</color>
846+
</brush>
847+
</colorrole>
848+
</active>
849+
<inactive>
850+
<colorrole role="WindowText">
851+
<brush brushstyle="SolidPattern">
852+
<color alpha="255">
853+
<red>239</red>
854+
<green>41</green>
855+
<blue>41</blue>
856+
</color>
857+
</brush>
858+
</colorrole>
859+
<colorrole role="Text">
860+
<brush brushstyle="SolidPattern">
861+
<color alpha="255">
862+
<red>239</red>
863+
<green>41</green>
864+
<blue>41</blue>
865+
</color>
866+
</brush>
867+
</colorrole>
868+
<colorrole role="PlaceholderText">
869+
<brush brushstyle="SolidPattern">
870+
<color alpha="128">
871+
<red>239</red>
872+
<green>41</green>
873+
<blue>41</blue>
874+
</color>
875+
</brush>
876+
</colorrole>
877+
</inactive>
878+
<disabled>
879+
<colorrole role="WindowText">
880+
<brush brushstyle="SolidPattern">
881+
<color alpha="255">
882+
<red>190</red>
883+
<green>190</green>
884+
<blue>190</blue>
885+
</color>
886+
</brush>
887+
</colorrole>
888+
<colorrole role="Text">
889+
<brush brushstyle="SolidPattern">
890+
<color alpha="255">
891+
<red>190</red>
892+
<green>190</green>
893+
<blue>190</blue>
894+
</color>
895+
</brush>
896+
</colorrole>
897+
<colorrole role="PlaceholderText">
898+
<brush brushstyle="SolidPattern">
899+
<color alpha="128">
900+
<red>0</red>
901+
<green>0</green>
902+
<blue>0</blue>
903+
</color>
904+
</brush>
905+
</colorrole>
906+
</disabled>
907+
</palette>
908+
</property>
909+
<property name="font">
910+
<font>
911+
<italic>true</italic>
912+
<bold>true</bold>
913+
</font>
914+
</property>
915+
<property name="text">
916+
<string>This qube is the default disposable template the global preload feature is set, therefore, it will honor the Global Config preference until the feature is deleted.</string>
917+
</property>
918+
<property name="alignment">
919+
<set>Qt::AlignJustify|Qt::AlignVCenter</set>
920+
</property>
921+
<property name="wordWrap">
922+
<bool>true</bool>
923+
</property>
924+
</widget>
925+
</item>
926+
<item row="7" column="0" colspan="2">
817927
<layout class="QHBoxLayout" name="horizontalLayout_6">
818928
<item>
819929
<widget class="QLabel" name="label_26">
@@ -843,13 +953,39 @@ The qube must be running to disable seamless mode. This setting is not persisten
843953
</item>
844954
</layout>
845955
</item>
846-
<item row="1" column="0">
847-
<widget class="QCheckBox" name="run_in_debug_mode">
956+
<item row="8" column="0" colspan="2">
957+
<widget class="QPushButton" name="boot_from_device_button">
848958
<property name="text">
849-
<string>Run in debug mode</string>
959+
<string>Boot qube from DISC or block device</string>
850960
</property>
851961
</widget>
852962
</item>
963+
<item row="9" column="0" colspan="2">
964+
<layout class="QHBoxLayout" name="horizontalLayout_8">
965+
<item>
966+
<widget class="QPushButton" name="seamless_on_button">
967+
<property name="toolTip">
968+
<string>Windows (with Qubes Windows Tools installed) only.
969+
The qube must be running to enable seamless mode. This setting is not persistent.</string>
970+
</property>
971+
<property name="text">
972+
<string>Enable seamless mode</string>
973+
</property>
974+
</widget>
975+
</item>
976+
<item>
977+
<widget class="QPushButton" name="seamless_off_button">
978+
<property name="toolTip">
979+
<string>Windows (with Qubes Windows Tools installed) only.
980+
The qube must be running to disable seamless mode. This setting is not persistent.</string>
981+
</property>
982+
<property name="text">
983+
<string>Disable seamless mode</string>
984+
</property>
985+
</widget>
986+
</item>
987+
</layout>
988+
</item>
853989
</layout>
854990
</widget>
855991
</item>
@@ -1933,6 +2069,7 @@ For user reference only.
19332069
<tabstop>run_in_debug_mode</tabstop>
19342070
<tabstop>provides_network_checkbox</tabstop>
19352071
<tabstop>dvm_template_checkbox</tabstop>
2072+
<tabstop>preload_dispvm</tabstop>
19362073
<tabstop>default_dispvm</tabstop>
19372074
<tabstop>boot_from_device_button</tabstop>
19382075
<tabstop>seamless_on_button</tabstop>

0 commit comments

Comments
 (0)