From a020e83b854128bb4848aa0e031211e0b157ff40 Mon Sep 17 00:00:00 2001 From: Ali Mirjamali Date: Mon, 14 Oct 2024 02:20:52 +0330 Subject: [PATCH] Add filtering options to Qubes Update GUI fixes: https://github.com/QubesOS/qubes-issues/issues/9029 --- qui/updater/intro_page.py | 17 +++++++++ qui/updater/updater.py | 7 ++++ qui/updater/updater_settings.py | 52 ++++++++++++++++++++++++- qui/updater_settings.glade | 68 ++++++++++++++++++++++++++++++++- 4 files changed, 142 insertions(+), 2 deletions(-) diff --git a/qui/updater/intro_page.py b/qui/updater/intro_page.py index 47cd311..a9f8855 100644 --- a/qui/updater/intro_page.py +++ b/qui/updater/intro_page.py @@ -94,12 +94,29 @@ 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 state = bool(vm.features.get('updates-available', False)) except exc.QubesDaemonCommunicationError: state = False self.list_store.append_vm(vm, state) + if settings.hide_updated: + cmd = ['qubes-vm-update', '--quiet', '--dry-run', + '--update-if-stale', str(settings.update_if_stale)] + to_update = self._get_stale_qubes(cmd) + for vm in qapp.domains: + try: + if settings.hide_skipped and bool(vm.features.get( \ + 'skip-update', False)): + continue + if settings.hide_updated and not vm.name in to_update: + # TODO: Make re-filtering possible without App restart + continue + except exc.QubesDaemonCommunicationError: + continue if getattr(vm, 'updateable', False) and vm.klass != 'AdminVM': self.list_store.append_vm(vm) diff --git a/qui/updater/updater.py b/qui/updater/updater.py index 47443f3..3075ff1 100644 --- a/qui/updater/updater.py +++ b/qui/updater/updater.py @@ -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"), @@ -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 = { diff --git a/qui/updater/updater_settings.py b/qui/updater/updater_settings.py index f80a65f..e8ebcac 100644 --- a/qui/updater/updater_settings.py +++ b/qui/updater/updater_settings.py @@ -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, @@ -61,6 +63,7 @@ def __init__( log, refresh_callback: Callable, overrides: OverriddenSettings = OverriddenSettings(), + advanced: Optional[bool] = False ): self.qapp = qapp self.log = log @@ -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) @@ -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 @@ -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: @@ -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.""" @@ -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() @@ -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() @@ -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: diff --git a/qui/updater_settings.glade b/qui/updater_settings.glade index 1a18156..0a6a264 100644 --- a/qui/updater_settings.glade +++ b/qui/updater_settings.glade @@ -9,7 +9,7 @@ Qubes OS Updater Settings False 458 - 571 + 640 @@ -495,6 +495,72 @@ 10 + + + Filtering Options + True + False + start + start + 18 + True + True + + + + False + True + 11 + + + + + Hide qubes with 'skip-update' feature from selection page. + Requires application restart to take effect. + True + True + False + start + start + 5 + True + True + True + + + + False + True + 12 + + + + + Hide already updated qubes from selection page. + Requires application restart to take effect. + True + True + False + start + start + 5 + True + False + True + + + + False + True + 13 + +