Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 9029 #224

Merged
merged 3 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 36 additions & 48 deletions qui/tray/updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,44 +126,42 @@ def check_vms_needing_update(self):
self.vms_needing_update.clear()
self.obsolete_vms.clear()
for vm in self.qapp.domains:
try:
updates_available = vm.features.get('updates-available', False)
except exc.QubesDaemonCommunicationError:
updates_available = False
if updates_available and \
(getattr(vm, 'updateable', False) or vm.klass == 'AdminVM'):
updated: bool = qui.utils.check_update(vm)
supported: bool = qui.utils.check_support(vm)
if not updated:
self.vms_needing_update.add(vm)
try:
supported = qui.utils.check_support(vm)
except exc.QubesDaemonCommunicationError:
supported = True
if not supported:
self.obsolete_vms.add(vm.name)

def connect_events(self):
self.dispatcher.add_handler('domain-feature-set:updates-available',
self.feature_set)
self.feature_change)
self.dispatcher.add_handler('domain-feature-delete:updates-available',
self.feature_unset)
self.feature_change)
self.dispatcher.add_handler('domain-feature-set:skip-update',
self.feature_change)
self.dispatcher.add_handler('domain-feature-delete:skip-update',
self.feature_change)
self.dispatcher.add_handler('domain-add', self.domain_added)
self.dispatcher.add_handler('domain-delete', self.domain_removed)
self.dispatcher.add_handler('domain-feature-set:os-eol',
self.feature_set)
self.feature_change)

def domain_added(self, _submitter, _event, vm, *_args, **_kwargs):
def domain_added(self, _submitter, _event, vmname, *_args, **_kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong, "vm" is used as keyword argument, so you can't change its name. I'll fix it.

I should have put that through openQA earlier...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ops. Sorry for mistake. I wonder why it did not show up during the unit tests. I guess there is no unit test for installing an outdated or obsolete template.

try:
vm_object = self.qapp.domains[vm]
vm = self.qapp.domains[vmname]
except exc.QubesDaemonCommunicationError:
return
except exc.QubesException:
# a disposableVM crashed on start
return
try:
updates_available = vm_object.features.get(
'updates-available', False)
except exc.QubesDaemonCommunicationError:
updates_available = False
if updates_available and (getattr(vm_object, 'updateable', False) or
vm_object.klass == 'AdminVM'):
self.vms_needing_update.add(vm_object.name)
updated: bool = qui.utils.check_update(vm)
supported: bool = qui.utils.check_support(vm)
if not updated:
self.vms_needing_update.add(vm.name)
self.update_indicator_state()
if not supported:
self.obsolete_vms.add(vm)
self.update_indicator_state()

def domain_removed(self, _submitter, _event, vm, *_args, **_kwargs):
Expand All @@ -174,34 +172,24 @@ def domain_removed(self, _submitter, _event, vm, *_args, **_kwargs):
self.obsolete_vms.remove(vm)
self.update_indicator_state()

def feature_unset(self, vm, event, feature, **_kwargs):
def feature_change(self, vm, event, feature, **_kwargs):
# pylint: disable=unused-argument
if vm in self.vms_needing_update:
updated: bool = qui.utils.check_update(vm)
supported: bool = qui.utils.check_support(vm)

if not updated and vm not in self.vms_needing_update:
self.vms_needing_update.add(vm)
notification = Gio.Notification.new(
_("New updates are available for {}.").format(vm.name))
notification.set_priority(Gio.NotificationPriority.NORMAL)
self.send_notification(None, notification)
elif updated and vm in self.vms_needing_update:
self.vms_needing_update.remove(vm)
self.update_indicator_state()

def feature_set(self, vm, event, feature, value, **_kwargs):
# pylint: disable=unused-argument
if feature == 'updates-available':
if value and vm not in self.vms_needing_update and\
getattr(vm, 'updateable', False):
self.vms_needing_update.add(vm)

notification = Gio.Notification.new(
_("New updates are available for {}.").format(vm.name))
notification.set_priority(Gio.NotificationPriority.NORMAL)
self.send_notification(None, notification)
elif not value and vm in self.vms_needing_update:
self.vms_needing_update.remove(vm)
elif feature == 'os-eol':
try:
supported = qui.utils.check_support(vm)
except exc.QubesDaemonCommunicationError:
supported = True
if supported and vm.name in self.obsolete_vms:
self.obsolete_vms.remove(vm.name)
elif not supported and vm.name not in self.obsolete_vms:
self.obsolete_vms.add(vm.name)
if not supported and vm not in self.obsolete_vms:
self.obsolete_vms.add(vm.name)
elif supported and vm in self.obsolete_vms:
self.obsolete_vms.remove(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
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',
'--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)

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
42 changes: 42 additions & 0 deletions 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 @@ -78,6 +80,7 @@ def __init__(

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

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

Expand Down Expand Up @@ -108,6 +111,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 +147,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 +187,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 +233,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 Down Expand Up @@ -262,6 +290,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
Loading