Skip to content

Commit

Permalink
Add filtering options to Qubes Update GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
alimirjamali committed Oct 14, 2024
1 parent a3f61ca commit bcc6bc8
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 4 deletions.
4 changes: 2 additions & 2 deletions qui/tray/updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,11 @@ def domain_added(self, _submitter, _event, vmname, *_args, **_kwargs):
vm = self.qapp.domains[vmname]
updated: bool = qui.utils.check_update(vm)
supported: bool = qui.utils.check_support(vm)
except exc.QubesDaemonCommunicationError:
return
except exc.QubesException:
# a disposableVM crashed on start
return
except exc.QubesDaemonCommunicationError:
return
if not updated:
self.vms_needing_update.add(vm.name)
self.update_indicator_state()
Expand Down
18 changes: 18 additions & 0 deletions qui/updater/intro_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,30 @@ def populate_vm_list(self, qapp, settings):
for vm in qapp.domains:
if vm.klass == 'AdminVM':
try:
if settings.hide_skipped and bool(vm.features.get( \
'skip-update', False)):
continue

Check warning on line 99 in qui/updater/intro_page.py

View check run for this annotation

Codecov / codecov/patch

qui/updater/intro_page.py#L99

Added line #L99 was not covered by tests
state = bool(vm.features.get('updates-available', False))
except exc.QubesDaemonCommunicationError:
state = False
self.list_store.append_vm(vm, state)

to_update=set()
if settings.hide_updated:
cmd = ['qubes-vm-update', '--quiet', '--dry-run',

Check warning on line 107 in qui/updater/intro_page.py

View check run for this annotation

Codecov / codecov/patch

qui/updater/intro_page.py#L107

Added line #L107 was not covered by tests
'--update-if-stale', str(settings.update_if_stale)]
to_update = self._get_stale_qubes(cmd)

Check warning on line 109 in qui/updater/intro_page.py

View check run for this annotation

Codecov / codecov/patch

qui/updater/intro_page.py#L109

Added line #L109 was not covered by tests

for vm in qapp.domains:
try:
if settings.hide_skipped and bool(vm.features.get( \
'skip-update', False)):
continue

Check warning on line 115 in qui/updater/intro_page.py

View check run for this annotation

Codecov / codecov/patch

qui/updater/intro_page.py#L115

Added line #L115 was not covered by tests
if settings.hide_updated and not vm.name in to_update:
# TODO: Make re-filtering possible without App restart
continue
except exc.QubesDaemonCommunicationError:
continue

Check warning on line 120 in qui/updater/intro_page.py

View check run for this annotation

Codecov / codecov/patch

qui/updater/intro_page.py#L118-L120

Added lines #L118 - L120 were not covered by tests
if getattr(vm, 'updateable', False) and vm.klass != 'AdminVM':
self.list_store.append_vm(vm)

Expand Down
6 changes: 6 additions & 0 deletions qui/updater/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ def test_qapp_impl():
add_dom0_feature(qapp, 'gui-default-allow-utf8-titles', '')
add_dom0_feature(qapp, 'gui-default-trayicon-mode', '')
add_dom0_feature(qapp, 'qubes-vm-update-update-if-stale', None)
add_dom0_feature(qapp, 'skip-update', None)
add_dom0_feature(qapp, 'qubes-vm-update-hide-skipped', None)
add_dom0_feature(qapp, 'qubes-vm-update-hide-updated', None)

# setup labels
qapp.expected_calls[('dom0', 'admin.label.List', None, None)] = \
Expand Down Expand Up @@ -140,6 +143,7 @@ def test_qapp_impl():
add_feature_to_all(qapp, 'servicevm',
['sys-usb', 'sys-firewall', 'sys-net'])
add_feature_to_all(qapp, 'os-eol', [])
add_feature_to_all(qapp, 'skip-update', [])

return qapp

Expand Down Expand Up @@ -254,6 +258,8 @@ def __init__(self):
self.restart_service_vms = True
self.restart_other_vms = True
self.max_concurrency = None
self.hide_skipped = True
self.hide_updated = False

return MockSettings()

Expand Down
7 changes: 7 additions & 0 deletions qui/updater/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def perform_setup(self, *_args, **_kwargs):
self.log,
refresh_callback=self.intro_page.refresh_update_list,
overrides=overrides,
advanced=self.cliargs.show_advanced_settings
)

headers = [(3, "intro_name"), (3, "progress_name"), (3, "summary_name"),
Expand Down Expand Up @@ -418,6 +419,12 @@ def parse_args(args, app):
'Interaction will be required in the event '
'of an update error.')

parser.add_argument('--show-advanced-settings', action='store_true',
help='Show setting options for filtering-out already '
'updated or skipped VMs from VM selection list. '
'These options require application restart to '
'take effect.')

args = parser.parse_args(args)

args.non_default_select = {
Expand Down
52 changes: 51 additions & 1 deletion qui/updater/updater_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class Settings:
MAX_UPDATE_IF_STALE = 99
DEFAULT_RESTART_SERVICEVMS = True
DEFAULT_RESTART_OTHER_VMS = False
DEFAULT_HIDE_SKIPPED = True
DEFAULT_HIDE_UPDATED = False

def __init__(
self,
Expand All @@ -61,6 +63,7 @@ def __init__(
log,
refresh_callback: Callable,
overrides: OverriddenSettings = OverriddenSettings(),
advanced: Optional[bool] = False
):
self.qapp = qapp
self.log = log
Expand All @@ -78,6 +81,14 @@ def __init__(

self.settings_window: Gtk.Window = self.builder.get_object(
"main_window")

# Filtering options for advanced users
self.builder.get_object("filtering_options").set_visible(advanced)
self.builder.get_object("hide_skipped").set_visible(advanced)
self.builder.get_object("hide_updated").set_visible(advanced)
self.settings_window.set_default_size(
self.settings_window.get_size().width, 640 if advanced else 550)

self.settings_window.set_transient_for(main_window)
self.settings_window.connect("delete-event", self.close_without_saving)

Expand Down Expand Up @@ -108,6 +119,12 @@ def __init__(
self.restart_other_checkbox.connect(
"toggled", self._show_restart_exceptions)

self.hide_skipped_checkbox: Gtk.CheckButton = \
self.builder.get_object("hide_skipped")

self.hide_updated_checkbox: Gtk.CheckButton = \
self.builder.get_object("hide_updated")

self.available_vms = [
vm for vm in self.qapp.domains
if vm.klass == 'DispVM' and not vm.auto_cleanup
Expand Down Expand Up @@ -138,6 +155,8 @@ def __init__(
self._init_restart_other_vms: Optional[bool] = None
self._init_limit_concurrency: Optional[bool] = None
self._init_max_concurrency: Optional[int] = None
self._init_hide_skipped: Optional[bool] = None
self._init_hide_updated: Optional[bool] = None

@property
def update_if_stale(self) -> int:
Expand Down Expand Up @@ -176,6 +195,18 @@ def restart_other_vms(self) -> bool:
self.vm, "qubes-vm-update-restart-other",
Settings.DEFAULT_RESTART_OTHER_VMS)

@property
def hide_skipped(self) -> bool:
return get_boolean_feature(
self.vm, "qubes-vm-update-hide-skipped",
Settings.DEFAULT_HIDE_SKIPPED)

@property
def hide_updated(self) -> bool:
return get_boolean_feature(
self.vm, "qubes-vm-update-hide-updated",
Settings.DEFAULT_HIDE_UPDATED)

@property
def max_concurrency(self) -> Optional[int]:
"""Return the current (set by this window or manually) option value."""
Expand Down Expand Up @@ -210,6 +241,11 @@ def load_settings(self):
if self._init_limit_concurrency:
self.max_concurrency_button.set_value(self._init_max_concurrency)

self._init_hide_skipped = self.hide_skipped
self._init_hide_updated = self.hide_updated
self.hide_skipped_checkbox.set_active(self._init_hide_skipped)
self.hide_updated_checkbox.set_active(self._init_hide_updated)

def _show_restart_exceptions(self, _emitter=None):
if self.restart_other_checkbox.get_active():
self.restart_exceptions_page.show_all()
Expand All @@ -226,7 +262,7 @@ def _limit_concurrency_toggled(self, _emitter=None):
def show(self):
"""Show a hidden window."""
self.load_settings()
self.settings_window.show_all()
self.settings_window.show()
self._show_restart_exceptions()
self._limit_concurrency_toggled()

Expand Down Expand Up @@ -262,6 +298,20 @@ def save_and_close(self, _emitter):
default=Settings.DEFAULT_RESTART_OTHER_VMS
)

self._save_option(
name="hide-skipped",
value=self.hide_skipped_checkbox.get_active(),
init=self._init_hide_skipped,
default=Settings.DEFAULT_HIDE_SKIPPED
)

self._save_option(
name="hide-updated",
value=self.hide_updated_checkbox.get_active(),
init=self._init_hide_updated,
default=Settings.DEFAULT_HIDE_UPDATED
)

limit_concurrency = self.limit_concurrency_checkbox.get_active()
if self._init_limit_concurrency or limit_concurrency:
if limit_concurrency:
Expand Down
68 changes: 67 additions & 1 deletion qui/updater_settings.glade
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<property name="title" translatable="yes">Qubes OS Updater Settings</property>
<property name="resizable">False</property>
<property name="default-width">458</property>
<property name="default-height">571</property>
<property name="default-height">640</property>
<child>
<!-- n-columns=3 n-rows=2 -->
<object class="GtkGrid">
Expand Down Expand Up @@ -495,6 +495,72 @@
<property name="position">10</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="filtering_options">
<property name="label" translatable="yes">Filtering Options</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="margin-top">18</property>
<property name="hexpand">True</property>
<property name="use-markup">True</property>
<style>
<class name="section_title"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">11</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="hide_skipped">
<property name="label" translatable="yes">Hide qubes with 'skip-update' feature from selection page.</property>
<property name="tooltip-text" translatable="yes">Requires application restart to take effect.</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="margin-top">5</property>
<property name="use-underline">True</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
<style>
<class name="explanation_text"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">12</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="hide_updated">
<property name="label" translatable="yes">Hide already updated qubes from selection page.</property>
<property name="tooltip-text" translatable="yes">Requires application restart to take effect.</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="margin-top">5</property>
<property name="use-underline">True</property>
<property name="active">False</property>
<property name="draw-indicator">True</property>
<style>
<class name="explanation_text"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">13</property>
</packing>
</child>
</object>
</child>
</object>
Expand Down

0 comments on commit bcc6bc8

Please sign in to comment.