From 6462ae88b76e22d9605d6752769cc2f7e68b5a41 Mon Sep 17 00:00:00 2001 From: Wojtek Porczyk Date: Wed, 12 Jul 2017 14:08:34 +0200 Subject: [PATCH] qubesmanager/utils --- qubesmanager/settings.py | 133 ++++++++++++------------------------- qubesmanager/utils.py | 137 +++++++++++++++++++++++++++++++++++++++ rpm_spec/qmgr.spec | 3 + 3 files changed, 182 insertions(+), 91 deletions(-) create mode 100644 qubesmanager/utils.py diff --git a/qubesmanager/settings.py b/qubesmanager/settings.py index 7629415..6cec6a2 100755 --- a/qubesmanager/settings.py +++ b/qubesmanager/settings.py @@ -32,6 +32,7 @@ import subprocess import qubesadmin import qubesadmin.tools +from . import utils from .ui_settingsdlg import * from .appmenu_select import * @@ -186,7 +187,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): if len(ret) > 0 : thread_monitor.set_error_msg('\n'.join(ret)) - thread_monitor.set_finished() + utils.debug('\n'.join(ret)) def current_tab_changed(self, idx): @@ -201,11 +202,10 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): "a working Firewall VM.").format(vm=self.vm.name)) - ######### basic tab # TODO LISTENERS - # - vm start/shutdown -> setEnabled on fields: template + # - vm start/shutdown -> setEnabled on fields: template labels # - vm create/delete -> choices lists, whole window deactiv (if self.vm) # - property-set -> individual fields @@ -213,45 +213,8 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): # netvm -> networking_groupbox # hvm -> include_in_balancing - def setup_vm_choice(self, widget, propname, default, filter_function, - allow_internal=False, allow_none=False): - # some basic information - oldvalue = getattr(self.vm, propname) - is_default = self.vm.property_is_default(propname) - idx = -1 - - vmlist = filter(filter_function, self.app.domains) - if not allow_internal: - vmlist = filter((lambda vm: not vm.features.get('internal', False)), - vmlist) - vmlist = list(vmlist) - vmlist.insert(0, qubesadmin.DEFAULT) - if allow_none: - vmlist.append(None) - - for i, vm in enumerate(vmlist): - # 0: default (unset) - if vm is qubesadmin.DEFAULT: - text = 'default ({})'.format( - default.name if default else 'none') - # N+1: explicit None - elif vm is None: - text = 'none' - # 1..N: choices - else: - text = vm.name - - if vm is qubesadmin.DEFAULT and is_default \ - or vm is oldvalue: - text += self.tr(' (current)') - idx = i - - widget.insertItem(i, text) - - setattr(self, propname + '_idx', idx) - setattr(self, propname + '_list', list) - widget.setCurrentIndex(idx) - + # TODO REMOVE + # other_groupbox def __init_basic_tab__(self): self.vmname.setText(self.vm.name) @@ -261,27 +224,32 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): if self.vm.qid == 0: self.vmlabel.setVisible(False) else: + self.label_list, self.label_idx = utils.prepare_label_choice( + self.vmlabel, + self.vm, 'label', + None, + allow_default=False + ) self.vmlabel.setVisible(True) - self.label_list = list(self.vm.app.labels) - self.label_list.sort(key=lambda l: l.index) - self.label_idx = 0 - for i, label in enumerate(self.label_list): - if label == self.vm.label: - self.label_idx = i - self.vmlabel.insertItem(i, label.name) - self.vmlabel.setItemIcon(i, QIcon.fromTheme(label.icon)) - self.vmlabel.setCurrentIndex(self.label_idx) - self.vmlabel.setEnabled(not self.vm.is_running()) + self.vmlabel.setEnabled(not self.vm.is_running()) if isinstance(self.vm, qubesadmin.vm.AppVM): - self.setup_vm_choice(self.template_name, 'template', - (lambda vm: isinstance(vm, qubesadmin.vm.TemplateVM))) + self.template_list, self.template_idx = utils.prepare_vm_choice( + self.template_name, + self.vm, 'template', + self.vm.app.default_template, + (lambda vm: isinstance(vm, qubesadmin.vm.TemplateVM)), + allow_default=True, allow_none=False) else: self.template_name.setEnabled(False) self.template_idx = -1 - self.setup_vm_choice(self.netVM, 'netvm', - (lambda vm: vm.provides_network)) + self.netvm_list, self.netvm_idx = utils.prepare_vm_choice( + self.netVM, + self.vm, 'netvm', + self.vm.app.default_netvm, + (lambda vm: vm.provides_network), + allow_default=True, allow_none=True) self.include_in_backups.setChecked(self.vm.include_in_backups) @@ -463,47 +431,30 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): except AttributeError: pass try: - self.root_img_path.setText(self.vm.storage.root_img) + self.root_img_path.setText('{volume.pool}:{volume.vid}'.format( + volume=self.vm.volumes['root'])) except AttributeError: self.root_img_path.setText("n/a") try: - self.volatile_img_path.setText(self.vm.storage.volatile_img) + self.volatile_img_path.setText('{volume.pool}:{volume.vid}'.format( + volume=self.vm.volumes['volatile'])) except AttributeError: self.volatile_img_path.setText('n/a') - self.private_img_path.setText(self.vm.storage.private_img) + self.private_img_path.setText('{volume.pool}:{volume.vid}'.format( + volume=self.vm.volumes['private'])) #kernel #in case VM is HVM - if not hasattr(self.vm, "kernel"): - self.kernel_groupbox.setVisible(False) - else: + if hasattr(self.vm, "kernel"): self.kernel_groupbox.setVisible(True) - # construct available kernels list - text = "default (" + self.app.get_default_kernel() +")" - kernel_list = [text] - for k in os.listdir(system_path["qubes_kernels_base_dir"]): - kernel_list.append(k) - kernel_list.append("none") - - self.kernel_idx = 0 - - # put available kernels to a combobox - for (i, k) in enumerate(kernel_list): - text = k - # and mark the current choice - if (text.startswith("default") and self.vm.uses_default_kernel) or ( self.vm.kernel == k and not self.vm.uses_default_kernel) or (k=="none" and self.vm.kernel==None): - text += " (current)" - self.kernel_idx = i - self.kernel.insertItem(i,text) - self.kernel.setCurrentIndex(self.kernel_idx) - - #kernel opts - if self.vm.uses_default_kernelopts: - self.kernel_opts.setText(self.vm.kernelopts + " (default)") - else: - self.kernel_opts.setText(self.vm.kernelopts) + self.kernel_list, self.kernel_idx = utils.prepare_kernel_choice( + self.kernel, self.vm, 'kernel', + self.vm.app.default_kernel, + allow_default=True, allow_none=True) + else: + self.kernel_groupbox.setVisible(False) if not hasattr(self.vm, "drive"): self.drive_groupbox.setVisible(False) @@ -660,6 +611,8 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): self.vm.dispvm_netvm = dispvm_netvm self.anything_changed = True except Exception as ex: + if utils.is_debug(): + traceback.print_exc() msg.append(str(ex)) return msg @@ -742,6 +695,8 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog): self.vm.pcidevs = pcidevs self.anything_changed = True except Exception as ex: + if utils.is_debug(): + traceback.print_exc() msg.append(str(ex)) return msg @@ -1005,13 +960,9 @@ parser = qubesadmin.tools.QubesArgumentParser(vmname_nargs=1) parser.add_argument('--tab', metavar='TAB', action='store', choices=VMSettingsWindow.tabs_indices.keys()) -parser.add_argument('--debug', - action='store_true', - help='debug mode') parser.set_defaults( tab='basic', - debug=os.getenv('QUBES_MANAGER_DEBUG', '0') != '0', ) def main(args=None): @@ -1025,7 +976,7 @@ def main(args=None): qapp.setOrganizationDomain("https://www.qubes-os.org/") qapp.setApplicationName("Qubes VM Settings") - if not args.debug: + if not utils.is_debug(): sys.excepthook = handle_exception settings_window = VMSettingsWindow(vm, qapp, args.tab) diff --git a/qubesmanager/utils.py b/qubesmanager/utils.py new file mode 100644 index 0000000..e2a06a1 --- /dev/null +++ b/qubesmanager/utils.py @@ -0,0 +1,137 @@ +# +# The Qubes OS Project, https://www.qubes-os.org +# +# Copyright (C) 2012 Agnieszka Kostrzewa +# Copyright (C) 2012 Marek Marczykowski-Górecki +# +# Copyright (C) 2017 Wojtek Porczyk +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import functools +import os + +import qubesadmin + +from PyQt4.QtGui import QIcon + +def _filter_internal(vm): + return (not isinstance(vm, qubesadmin.vm.AdminVM) + and not vm.features.get('internal', False)) + +def prepare_choice(widget, holder, propname, choice, default, + filter_function=None, *, + icon_getter=None, allow_internal=None, allow_default=False, + allow_none=False): + + # for newly created vms, set propname to None + + debug( + 'prepare_choice(widget={widget!r}, ' + 'holder={holder!r}, ' + 'propname={propname!r}, ' + 'choice={choice!r}, ' + 'default={default!r}, ' + 'filter_function={filter_function!r}, ' + 'icon_getter={icon_getter!r}, ' + 'allow_internal={allow_internal!r}, ' + 'allow_default={allow_default!r}, ' + 'allow_none={allow_none!r})'.format(**locals())) + + if allow_internal is None: + allow_internal = propname is None or not propname.endswith('vm') + + if propname is not None: + oldvalue = getattr(holder, propname) + is_default = holder.property_is_default(propname) + else: + oldvalue = object() # won't match for identity + is_default = False + idx = 0 + + choice_list = list(choice)[:] + if not allow_internal: + choice_list = filter(_filter_internal, choice_list) + if filter_function is not None: + choice_list = filter(filter_function, choice_list) + choice_list = list(choice_list) + + if allow_default: + choice_list.insert(0, qubesadmin.DEFAULT) + if allow_none: + choice_list.append(None) + + for i, item in enumerate(choice_list): + debug('i={} item={}'.format(i, item)) + # 0: default (unset) + if item is qubesadmin.DEFAULT: + text = 'default ({})'.format( + str(default) if default is not None else 'none') + # N+1: explicit None + elif item is None: + text = 'none' + # 1..N: choices + else: + text = str(item) + + if item is qubesadmin.DEFAULT and is_default \ + or item is not qubesadmin.DEFAULT and item is oldvalue: + text += ' (current)' + idx = i + + widget.insertItem(i, text) + + if icon_getter is not None: + icon = icon_getter(item) + if icon is not None: + widget.setItemIcon(i, icon) + + widget.setCurrentIndex(idx) + + return choice_list, idx + +def prepare_kernel_choice(widget, holder, propname, default, *args, **kwargs): + # TODO get from storage API (pool 'linux-kernel') (suggested by @marmarta) + return prepare_choice(widget, holder, propname, + os.listdir('/var/lib/qubes/vm-kernels'), default, *args, **kwargs) + +def prepare_label_choice(widget, holder, propname, default, *args, **kwargs): + try: + app = holder.app + except AttributeError: + app = holder + + return prepare_choice(widget, holder, propname, + sorted(app.labels, key=lambda l: l.index), + default, *args, + icon_getter=(lambda label: QIcon.fromTheme(label.icon)), + **kwargs) + +def prepare_vm_choice(widget, holder, propname, default, *args, **kwargs): + try: + app = holder.app + except AttributeError: + app = holder + + return prepare_choice(widget, holder, propname, app.domains, default, + *args, **kwargs) + +def is_debug(): + return os.getenv('QUBES_MANAGER_DEBUG', '') not in ('', '0') + +def debug(*args, **kwargs): + if not is_debug(): + return + print(*args, **kwargs) diff --git a/rpm_spec/qmgr.spec b/rpm_spec/qmgr.spec index 9ed4950..67c0f31 100644 --- a/rpm_spec/qmgr.spec +++ b/rpm_spec/qmgr.spec @@ -85,7 +85,10 @@ rm -rf $RPM_BUILD_ROOT %{python3_sitelib}/qubesmanager/informationnotes.py %{python3_sitelib}/qubesmanager/create_new_vm.py %{python3_sitelib}/qubesmanager/thread_monitor.py +%{python3_sitelib}/qubesmanager/utils.py + %{python3_sitelib}/qubesmanager/resources_rc.py + %{python3_sitelib}/qubesmanager/ui_backupdlg.py %{python3_sitelib}/qubesmanager/ui_globalsettingsdlg.py %{python3_sitelib}/qubesmanager/ui_multiselectwidget.py