Skip to content

Commit 85b247c

Browse files
committed
Merge remote-tracking branch 'origin/pr/224'
* origin/pr/224: Move catching exception inside helper functions Add filtering options to Qubes Update GUI Do not notify outdated & EOL VMs if `skip-update` set
2 parents 8126510 + 276c164 commit 85b247c

File tree

6 files changed

+202
-60
lines changed

6 files changed

+202
-60
lines changed

qui/tray/updates.py

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -126,44 +126,42 @@ def check_vms_needing_update(self):
126126
self.vms_needing_update.clear()
127127
self.obsolete_vms.clear()
128128
for vm in self.qapp.domains:
129-
try:
130-
updates_available = vm.features.get('updates-available', False)
131-
except exc.QubesDaemonCommunicationError:
132-
updates_available = False
133-
if updates_available and \
134-
(getattr(vm, 'updateable', False) or vm.klass == 'AdminVM'):
129+
updated: bool = qui.utils.check_update(vm)
130+
supported: bool = qui.utils.check_support(vm)
131+
if not updated:
135132
self.vms_needing_update.add(vm)
136-
try:
137-
supported = qui.utils.check_support(vm)
138-
except exc.QubesDaemonCommunicationError:
139-
supported = True
140133
if not supported:
141134
self.obsolete_vms.add(vm.name)
142135

143136
def connect_events(self):
144137
self.dispatcher.add_handler('domain-feature-set:updates-available',
145-
self.feature_set)
138+
self.feature_change)
146139
self.dispatcher.add_handler('domain-feature-delete:updates-available',
147-
self.feature_unset)
140+
self.feature_change)
141+
self.dispatcher.add_handler('domain-feature-set:skip-update',
142+
self.feature_change)
143+
self.dispatcher.add_handler('domain-feature-delete:skip-update',
144+
self.feature_change)
148145
self.dispatcher.add_handler('domain-add', self.domain_added)
149146
self.dispatcher.add_handler('domain-delete', self.domain_removed)
150147
self.dispatcher.add_handler('domain-feature-set:os-eol',
151-
self.feature_set)
148+
self.feature_change)
152149

153-
def domain_added(self, _submitter, _event, vm, *_args, **_kwargs):
150+
def domain_added(self, _submitter, _event, vmname, *_args, **_kwargs):
154151
try:
155-
vm_object = self.qapp.domains[vm]
152+
vm = self.qapp.domains[vmname]
153+
except exc.QubesDaemonCommunicationError:
154+
return
156155
except exc.QubesException:
157156
# a disposableVM crashed on start
158157
return
159-
try:
160-
updates_available = vm_object.features.get(
161-
'updates-available', False)
162-
except exc.QubesDaemonCommunicationError:
163-
updates_available = False
164-
if updates_available and (getattr(vm_object, 'updateable', False) or
165-
vm_object.klass == 'AdminVM'):
166-
self.vms_needing_update.add(vm_object.name)
158+
updated: bool = qui.utils.check_update(vm)
159+
supported: bool = qui.utils.check_support(vm)
160+
if not updated:
161+
self.vms_needing_update.add(vm.name)
162+
self.update_indicator_state()
163+
if not supported:
164+
self.obsolete_vms.add(vm)
167165
self.update_indicator_state()
168166

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

177-
def feature_unset(self, vm, event, feature, **_kwargs):
175+
def feature_change(self, vm, event, feature, **_kwargs):
178176
# pylint: disable=unused-argument
179-
if vm in self.vms_needing_update:
177+
updated: bool = qui.utils.check_update(vm)
178+
supported: bool = qui.utils.check_support(vm)
179+
180+
if not updated and vm not in self.vms_needing_update:
181+
self.vms_needing_update.add(vm)
182+
notification = Gio.Notification.new(
183+
_("New updates are available for {}.").format(vm.name))
184+
notification.set_priority(Gio.NotificationPriority.NORMAL)
185+
self.send_notification(None, notification)
186+
elif updated and vm in self.vms_needing_update:
180187
self.vms_needing_update.remove(vm)
181-
self.update_indicator_state()
182188

183-
def feature_set(self, vm, event, feature, value, **_kwargs):
184-
# pylint: disable=unused-argument
185-
if feature == 'updates-available':
186-
if value and vm not in self.vms_needing_update and\
187-
getattr(vm, 'updateable', False):
188-
self.vms_needing_update.add(vm)
189-
190-
notification = Gio.Notification.new(
191-
_("New updates are available for {}.").format(vm.name))
192-
notification.set_priority(Gio.NotificationPriority.NORMAL)
193-
self.send_notification(None, notification)
194-
elif not value and vm in self.vms_needing_update:
195-
self.vms_needing_update.remove(vm)
196-
elif feature == 'os-eol':
197-
try:
198-
supported = qui.utils.check_support(vm)
199-
except exc.QubesDaemonCommunicationError:
200-
supported = True
201-
if supported and vm.name in self.obsolete_vms:
202-
self.obsolete_vms.remove(vm.name)
203-
elif not supported and vm.name not in self.obsolete_vms:
204-
self.obsolete_vms.add(vm.name)
189+
if not supported and vm not in self.obsolete_vms:
190+
self.obsolete_vms.add(vm.name)
191+
elif supported and vm in self.obsolete_vms:
192+
self.obsolete_vms.remove(vm.name)
205193

206194
self.update_indicator_state()
207195

qui/updater/intro_page.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,30 @@ def populate_vm_list(self, qapp, settings):
9494
for vm in qapp.domains:
9595
if vm.klass == 'AdminVM':
9696
try:
97+
if settings.hide_skipped and bool(vm.features.get( \
98+
'skip-update', False)):
99+
continue
97100
state = bool(vm.features.get('updates-available', False))
98101
except exc.QubesDaemonCommunicationError:
99102
state = False
100103
self.list_store.append_vm(vm, state)
101104

105+
to_update=set()
106+
if settings.hide_updated:
107+
cmd = ['qubes-vm-update', '--quiet', '--dry-run',
108+
'--update-if-stale', str(settings.update_if_stale)]
109+
to_update = self._get_stale_qubes(cmd)
110+
102111
for vm in qapp.domains:
112+
try:
113+
if settings.hide_skipped and bool(vm.features.get( \
114+
'skip-update', False)):
115+
continue
116+
if settings.hide_updated and not vm.name in to_update:
117+
# TODO: Make re-filtering possible without App restart
118+
continue
119+
except exc.QubesDaemonCommunicationError:
120+
continue
103121
if getattr(vm, 'updateable', False) and vm.klass != 'AdminVM':
104122
self.list_store.append_vm(vm)
105123

qui/updater/tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def test_qapp_impl():
6161
add_dom0_feature(qapp, 'gui-default-allow-utf8-titles', '')
6262
add_dom0_feature(qapp, 'gui-default-trayicon-mode', '')
6363
add_dom0_feature(qapp, 'qubes-vm-update-update-if-stale', None)
64+
add_dom0_feature(qapp, 'skip-update', None)
65+
add_dom0_feature(qapp, 'qubes-vm-update-hide-skipped', None)
66+
add_dom0_feature(qapp, 'qubes-vm-update-hide-updated', None)
6467

6568
# setup labels
6669
qapp.expected_calls[('dom0', 'admin.label.List', None, None)] = \
@@ -140,6 +143,7 @@ def test_qapp_impl():
140143
add_feature_to_all(qapp, 'servicevm',
141144
['sys-usb', 'sys-firewall', 'sys-net'])
142145
add_feature_to_all(qapp, 'os-eol', [])
146+
add_feature_to_all(qapp, 'skip-update', [])
143147

144148
return qapp
145149

@@ -254,6 +258,8 @@ def __init__(self):
254258
self.restart_service_vms = True
255259
self.restart_other_vms = True
256260
self.max_concurrency = None
261+
self.hide_skipped = True
262+
self.hide_updated = False
257263

258264
return MockSettings()
259265

qui/updater/updater_settings.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class Settings:
5353
MAX_UPDATE_IF_STALE = 99
5454
DEFAULT_RESTART_SERVICEVMS = True
5555
DEFAULT_RESTART_OTHER_VMS = False
56+
DEFAULT_HIDE_SKIPPED = True
57+
DEFAULT_HIDE_UPDATED = False
5658

5759
def __init__(
5860
self,
@@ -78,6 +80,7 @@ def __init__(
7880

7981
self.settings_window: Gtk.Window = self.builder.get_object(
8082
"main_window")
83+
8184
self.settings_window.set_transient_for(main_window)
8285
self.settings_window.connect("delete-event", self.close_without_saving)
8386

@@ -108,6 +111,12 @@ def __init__(
108111
self.restart_other_checkbox.connect(
109112
"toggled", self._show_restart_exceptions)
110113

114+
self.hide_skipped_checkbox: Gtk.CheckButton = \
115+
self.builder.get_object("hide_skipped")
116+
117+
self.hide_updated_checkbox: Gtk.CheckButton = \
118+
self.builder.get_object("hide_updated")
119+
111120
self.available_vms = [
112121
vm for vm in self.qapp.domains
113122
if vm.klass == 'DispVM' and not vm.auto_cleanup
@@ -138,6 +147,8 @@ def __init__(
138147
self._init_restart_other_vms: Optional[bool] = None
139148
self._init_limit_concurrency: Optional[bool] = None
140149
self._init_max_concurrency: Optional[int] = None
150+
self._init_hide_skipped: Optional[bool] = None
151+
self._init_hide_updated: Optional[bool] = None
141152

142153
@property
143154
def update_if_stale(self) -> int:
@@ -176,6 +187,18 @@ def restart_other_vms(self) -> bool:
176187
self.vm, "qubes-vm-update-restart-other",
177188
Settings.DEFAULT_RESTART_OTHER_VMS)
178189

190+
@property
191+
def hide_skipped(self) -> bool:
192+
return get_boolean_feature(
193+
self.vm, "qubes-vm-update-hide-skipped",
194+
Settings.DEFAULT_HIDE_SKIPPED)
195+
196+
@property
197+
def hide_updated(self) -> bool:
198+
return get_boolean_feature(
199+
self.vm, "qubes-vm-update-hide-updated",
200+
Settings.DEFAULT_HIDE_UPDATED)
201+
179202
@property
180203
def max_concurrency(self) -> Optional[int]:
181204
"""Return the current (set by this window or manually) option value."""
@@ -210,6 +233,11 @@ def load_settings(self):
210233
if self._init_limit_concurrency:
211234
self.max_concurrency_button.set_value(self._init_max_concurrency)
212235

236+
self._init_hide_skipped = self.hide_skipped
237+
self._init_hide_updated = self.hide_updated
238+
self.hide_skipped_checkbox.set_active(self._init_hide_skipped)
239+
self.hide_updated_checkbox.set_active(self._init_hide_updated)
240+
213241
def _show_restart_exceptions(self, _emitter=None):
214242
if self.restart_other_checkbox.get_active():
215243
self.restart_exceptions_page.show_all()
@@ -262,6 +290,20 @@ def save_and_close(self, _emitter):
262290
default=Settings.DEFAULT_RESTART_OTHER_VMS
263291
)
264292

293+
self._save_option(
294+
name="hide-skipped",
295+
value=self.hide_skipped_checkbox.get_active(),
296+
init=self._init_hide_skipped,
297+
default=Settings.DEFAULT_HIDE_SKIPPED
298+
)
299+
300+
self._save_option(
301+
name="hide-updated",
302+
value=self.hide_updated_checkbox.get_active(),
303+
init=self._init_hide_updated,
304+
default=Settings.DEFAULT_HIDE_UPDATED
305+
)
306+
265307
limit_concurrency = self.limit_concurrency_checkbox.get_active()
266308
if self._init_limit_concurrency or limit_concurrency:
267309
if limit_concurrency:

qui/updater_settings.glade

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<property name="title" translatable="yes">Qubes OS Updater Settings</property>
1010
<property name="resizable">False</property>
1111
<property name="default-width">458</property>
12-
<property name="default-height">571</property>
12+
<property name="default-height">640</property>
1313
<child>
1414
<!-- n-columns=3 n-rows=2 -->
1515
<object class="GtkGrid">
@@ -495,6 +495,72 @@
495495
<property name="position">10</property>
496496
</packing>
497497
</child>
498+
<child>
499+
<object class="GtkLabel" id="filtering_options">
500+
<property name="label" translatable="yes">Filtering Options</property>
501+
<property name="visible">True</property>
502+
<property name="can-focus">False</property>
503+
<property name="halign">start</property>
504+
<property name="valign">start</property>
505+
<property name="margin-top">18</property>
506+
<property name="hexpand">True</property>
507+
<property name="use-markup">True</property>
508+
<style>
509+
<class name="section_title"/>
510+
</style>
511+
</object>
512+
<packing>
513+
<property name="expand">False</property>
514+
<property name="fill">True</property>
515+
<property name="position">11</property>
516+
</packing>
517+
</child>
518+
<child>
519+
<object class="GtkCheckButton" id="hide_skipped">
520+
<property name="label" translatable="yes">Hide qubes with 'skip-update' feature from selection page.</property>
521+
<property name="tooltip-text" translatable="yes">Requires application restart to take effect.</property>
522+
<property name="visible">True</property>
523+
<property name="can-focus">True</property>
524+
<property name="receives-default">False</property>
525+
<property name="halign">start</property>
526+
<property name="valign">start</property>
527+
<property name="margin-top">5</property>
528+
<property name="use-underline">True</property>
529+
<property name="active">True</property>
530+
<property name="draw-indicator">True</property>
531+
<style>
532+
<class name="explanation_text"/>
533+
</style>
534+
</object>
535+
<packing>
536+
<property name="expand">False</property>
537+
<property name="fill">True</property>
538+
<property name="position">12</property>
539+
</packing>
540+
</child>
541+
<child>
542+
<object class="GtkCheckButton" id="hide_updated">
543+
<property name="label" translatable="yes">Hide already updated qubes from selection page.</property>
544+
<property name="tooltip-text" translatable="yes">Requires application restart to take effect.</property>
545+
<property name="visible">True</property>
546+
<property name="can-focus">True</property>
547+
<property name="receives-default">False</property>
548+
<property name="halign">start</property>
549+
<property name="valign">start</property>
550+
<property name="margin-top">5</property>
551+
<property name="use-underline">True</property>
552+
<property name="active">False</property>
553+
<property name="draw-indicator">True</property>
554+
<style>
555+
<class name="explanation_text"/>
556+
</style>
557+
</object>
558+
<packing>
559+
<property name="expand">False</property>
560+
<property name="fill">True</property>
561+
<property name="position">13</property>
562+
</packing>
563+
</child>
498564
</object>
499565
</child>
500566
</object>

0 commit comments

Comments
 (0)