Merge remote-tracking branch 'origin/pr/273'
* origin/pr/273: (24 commits) Fix coherence in network menu when adding/removing domains Fix too long line Add warning if trying to change template VM Disable network menu for templates Display default netvm Fix possible 'None' default error Added default option for network change Wrap warnings message in self.tr() Added wait argument to start_vm Better dialog creation Added try/except for starting netvm Added error message to dialogs Added proper error handling and Check netvm_name is not None Changed checkboxes to icons Added change network confirmation Added Template Change Confirmation Moved change_* funcs after __init__() Added QMessageBox if netvm is halted and user wants to start it Added network_menu updates Added try/except for change_network ...
This commit is contained in:
commit
d253b50545
@ -709,6 +709,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
self.frame_width = 0
|
||||
self.frame_height = 0
|
||||
|
||||
self.init_template_menu()
|
||||
self.init_network_menu()
|
||||
self.__init_context_menu()
|
||||
|
||||
self.tools_context_menu = QMenu(self)
|
||||
@ -740,6 +742,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
self.proxy.setFilterKeyColumn(2)
|
||||
self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive)
|
||||
self.proxy.layoutChanged.connect(self.save_sorting)
|
||||
self.proxy.layoutChanged.connect(self.update_template_menu)
|
||||
self.proxy.layoutChanged.connect(self.update_network_menu)
|
||||
|
||||
self.show_running.stateChanged.connect(self.invalidate)
|
||||
self.show_halted.stateChanged.connect(self.invalidate)
|
||||
@ -762,7 +766,6 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
column = self.qubes_model.columns_indices[col_no]
|
||||
action = self.menu_view.addAction(column)
|
||||
action.setData(column)
|
||||
action.setCheckable(True)
|
||||
action.toggled.connect(partial(self.showhide_column, col_no))
|
||||
|
||||
self.menu_view.addSeparator()
|
||||
@ -822,9 +825,77 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
|
||||
self.check_updates()
|
||||
|
||||
def change_template(self, template):
|
||||
selected_vms = self.get_selected_vms()
|
||||
reply = QMessageBox.question(
|
||||
self, self.tr("Template Change Confirmation"),
|
||||
self.tr("Do you want to change '{0}'<br>"
|
||||
"to Template <b>'{1}'</b>?").format(
|
||||
', '.join(vm.name for vm in selected_vms), template),
|
||||
QMessageBox.Yes | QMessageBox.Cancel)
|
||||
|
||||
if reply == QMessageBox.Yes:
|
||||
errors = []
|
||||
for info in selected_vms:
|
||||
try:
|
||||
info.vm.template = template
|
||||
except exc.QubesValueError as ex:
|
||||
errors.append((info.name, str(ex)))
|
||||
|
||||
for error in errors:
|
||||
QMessageBox.warning(self, self.tr("{0} template change failed!")
|
||||
.format(error[0]), error[1])
|
||||
|
||||
|
||||
def change_network(self, netvm_name):
|
||||
selected_vms = self.get_selected_vms()
|
||||
reply = QMessageBox.question(
|
||||
self, self.tr("Network Change Confirmation"),
|
||||
self.tr("Do you want to change '{0}'<br>"
|
||||
"to Network <b>'{1}'</b>?").format(
|
||||
', '.join(vm.name for vm in selected_vms), netvm_name),
|
||||
QMessageBox.Yes | QMessageBox.Cancel)
|
||||
|
||||
if reply != QMessageBox.Yes:
|
||||
return
|
||||
|
||||
if netvm_name not in [None, 'default']:
|
||||
check_power = any(info.state['power'] == 'Running' for info
|
||||
in self.get_selected_vms())
|
||||
netvm = self.qubes_cache.get_vm(name=netvm_name)
|
||||
if check_power and netvm.state['power'] != 'Running':
|
||||
reply = QMessageBox.question(
|
||||
self, self.tr("Qube Start Confirmation"),
|
||||
self.tr("<br>Can not change netvm to a halted Qube.<br>"
|
||||
"Do you want to start the Qube <b>'{0}'</b>?").format(
|
||||
netvm_name),
|
||||
QMessageBox.Yes | QMessageBox.Cancel)
|
||||
|
||||
if reply == QMessageBox.Yes:
|
||||
self.start_vm(netvm.vm, True)
|
||||
else:
|
||||
return
|
||||
|
||||
errors = []
|
||||
for info in self.get_selected_vms():
|
||||
try:
|
||||
if netvm_name == 'default':
|
||||
delattr(info.vm, 'netvm')
|
||||
else:
|
||||
info.vm.netvm = netvm_name
|
||||
except exc.QubesValueError as ex:
|
||||
errors.append((info.name, str(ex)))
|
||||
|
||||
for error in errors:
|
||||
QMessageBox.warning(self, self.tr("{0} network change failed!")
|
||||
.format(error[0]), error[1])
|
||||
|
||||
|
||||
def __init_context_menu(self):
|
||||
self.context_menu = QMenu(self)
|
||||
self.context_menu.addAction(self.action_settings)
|
||||
self.context_menu.addAction(self.template_menu.menuAction())
|
||||
self.context_menu.addAction(self.network_menu.menuAction())
|
||||
self.context_menu.addAction(self.action_editfwrules)
|
||||
self.context_menu.addAction(self.action_appmenus)
|
||||
self.context_menu.addAction(self.action_set_keyboard_layout)
|
||||
@ -885,6 +956,33 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
|
||||
progress.setValue(row_no)
|
||||
|
||||
def init_template_menu(self):
|
||||
self.template_menu.clear()
|
||||
for vm in self.qubes_app.domains:
|
||||
if vm.klass == 'TemplateVM':
|
||||
action = self.template_menu.addAction(vm.name)
|
||||
action.setData(vm.name)
|
||||
action.triggered.connect(partial(self.change_template, vm.name))
|
||||
|
||||
def _get_default_netvm(self):
|
||||
for vm in self.qubes_app.domains:
|
||||
if vm.klass == 'AppVM':
|
||||
return vm.property_get_default('netvm')
|
||||
|
||||
def init_network_menu(self):
|
||||
default = self._get_default_netvm()
|
||||
self.network_menu.clear()
|
||||
action = self.network_menu.addAction("None")
|
||||
action.triggered.connect(partial(self.change_network, None))
|
||||
action = self.network_menu.addAction("default ({0})".format(default))
|
||||
action.triggered.connect(partial(self.change_network, 'default'))
|
||||
|
||||
for vm in self.qubes_app.domains:
|
||||
if vm.qid != 0 and vm.provides_network:
|
||||
action = self.network_menu.addAction(vm.name)
|
||||
action.setData(vm.name)
|
||||
action.triggered.connect(partial(self.change_network, vm.name))
|
||||
|
||||
def setup_application(self):
|
||||
self.qt_app.setApplicationName(self.tr("Qube Manager"))
|
||||
self.qt_app.setWindowIcon(QIcon.fromTheme("qubes-manager"))
|
||||
@ -942,12 +1040,16 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
domain = self.qubes_app.domains[vm]
|
||||
self.qubes_cache.add_vm(domain)
|
||||
self.proxy.invalidate()
|
||||
if domain.klass == 'TemplateVM':
|
||||
self.init_template_menu()
|
||||
except (exc.QubesException, KeyError):
|
||||
pass
|
||||
|
||||
def on_domain_removed(self, _submitter, _event, **kwargs):
|
||||
self.qubes_cache.remove_vm(name=kwargs['vm'])
|
||||
self.proxy.invalidate()
|
||||
self.init_template_menu()
|
||||
self.init_network_menu()
|
||||
|
||||
def on_domain_status_changed(self, vm, event, **_kwargs):
|
||||
try:
|
||||
@ -977,6 +1079,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
return
|
||||
|
||||
try:
|
||||
if event.endswith(':provides_network'):
|
||||
self.init_network_menu()
|
||||
self.qubes_cache.get_vm(qid=vm.qid).update(event=event)
|
||||
self.proxy.invalidate()
|
||||
except exc.QubesDaemonAccessError:
|
||||
@ -1061,6 +1165,8 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
def table_selection_changed(self):
|
||||
# Since selection could have multiple domains
|
||||
# enable all first and then filter them
|
||||
self.template_menu.setEnabled(True)
|
||||
self.network_menu.setEnabled(True)
|
||||
for action in self.toolbar.actions() + self.context_menu.actions():
|
||||
action.setEnabled(True)
|
||||
|
||||
@ -1071,17 +1177,20 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
['Running', 'Transient', 'Halting', 'Dying']:
|
||||
self.action_resumevm.setEnabled(False)
|
||||
self.action_removevm.setEnabled(False)
|
||||
self.template_menu.setEnabled(False)
|
||||
elif vm.state['power'] == 'Paused':
|
||||
self.action_removevm.setEnabled(False)
|
||||
self.action_pausevm.setEnabled(False)
|
||||
self.action_set_keyboard_layout.setEnabled(False)
|
||||
self.action_restartvm.setEnabled(False)
|
||||
self.action_open_console.setEnabled(False)
|
||||
self.template_menu.setEnabled(False)
|
||||
elif vm.state['power'] == 'Suspend':
|
||||
self.action_set_keyboard_layout.setEnabled(False)
|
||||
self.action_removevm.setEnabled(False)
|
||||
self.action_pausevm.setEnabled(False)
|
||||
self.action_open_console.setEnabled(False)
|
||||
self.template_menu.setEnabled(False)
|
||||
elif vm.state['power'] == 'Halted':
|
||||
self.action_set_keyboard_layout.setEnabled(False)
|
||||
self.action_pausevm.setEnabled(False)
|
||||
@ -1104,9 +1213,15 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
self.action_editfwrules.setEnabled(False)
|
||||
self.action_set_keyboard_layout.setEnabled(False)
|
||||
self.action_run_command_in_vm.setEnabled(False)
|
||||
self.template_menu.setEnabled(False)
|
||||
self.network_menu.setEnabled(False)
|
||||
elif vm.klass == 'DispVM':
|
||||
self.action_appmenus.setEnabled(False)
|
||||
self.action_restartvm.setEnabled(False)
|
||||
self.template_menu.setEnabled(False)
|
||||
elif vm.klass == 'TemplateVM':
|
||||
self.template_menu.setEnabled(False)
|
||||
self.network_menu.setEnabled(False)
|
||||
|
||||
if vm.vm.features.get('internal', False):
|
||||
self.action_appmenus.setEnabled(False)
|
||||
@ -1114,6 +1229,47 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
if not vm.updateable and vm.klass != 'AdminVM':
|
||||
self.action_updatevm.setEnabled(False)
|
||||
|
||||
self.update_template_menu()
|
||||
self.update_network_menu()
|
||||
|
||||
def update_template_menu(self):
|
||||
if not self.template_menu.isEnabled():
|
||||
return
|
||||
|
||||
for entry in self.template_menu.actions():
|
||||
entry.setIcon(QIcon())
|
||||
|
||||
vms = self.get_selected_vms()
|
||||
for vm in vms:
|
||||
for entry in self.template_menu.actions():
|
||||
if entry.data() == vm.template:
|
||||
if len(vms) == 1:
|
||||
entry.setIcon(QIcon(":/on.png"))
|
||||
else:
|
||||
entry.setIcon(QIcon(":/transient.png"))
|
||||
|
||||
def update_network_menu(self):
|
||||
if not self.network_menu.isEnabled():
|
||||
return
|
||||
|
||||
for entry in self.network_menu.actions():
|
||||
entry.setIcon(QIcon())
|
||||
|
||||
if len(self.get_selected_vms()) == 1:
|
||||
icon = QIcon(":/on.png")
|
||||
else:
|
||||
icon = QIcon(":/transient.png")
|
||||
|
||||
for vm in self.get_selected_vms():
|
||||
if vm.netvm == "n/a":
|
||||
self.network_menu.actions()[0].setIcon(QIcon(icon))
|
||||
elif vm.vm.property_is_default("netvm"):
|
||||
self.network_menu.actions()[1].setIcon(QIcon(icon))
|
||||
else:
|
||||
for entry in self.network_menu.actions():
|
||||
if entry.data() == vm.netvm:
|
||||
entry.setIcon(icon)
|
||||
|
||||
# noinspection PyArgumentList
|
||||
@pyqtSlot(name='on_action_createvm_triggered')
|
||||
def action_createvm_triggered(self):
|
||||
@ -1202,7 +1358,7 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
|
||||
self.start_vm(vm)
|
||||
|
||||
def start_vm(self, vm):
|
||||
def start_vm(self, vm, wait=False):
|
||||
if manager_utils.is_running(vm, False):
|
||||
return
|
||||
|
||||
@ -1211,6 +1367,10 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QMainWindow):
|
||||
thread.finished.connect(self.clear_threads)
|
||||
thread.start()
|
||||
|
||||
if wait:
|
||||
with common_threads.busy_cursor():
|
||||
thread.wait()
|
||||
|
||||
# noinspection PyArgumentList
|
||||
@pyqtSlot(name='on_action_startvm_tools_install_triggered')
|
||||
# TODO: replace with boot from device
|
||||
|
@ -432,6 +432,7 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
|
||||
self.netVM.setCurrentIndex(-1)
|
||||
|
||||
self.netVM.currentIndexChanged.connect(self.check_warn_dispvmnetvm)
|
||||
self.netVM.currentIndexChanged.connect(self.check_warn_templatenetvm)
|
||||
|
||||
try:
|
||||
self.include_in_backups.setChecked(self.vm.include_in_backups)
|
||||
@ -625,6 +626,16 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtWidgets.QDialog):
|
||||
self.init_mem.value() * 10 < self.max_mem_size.value():
|
||||
self.warn_too_much_mem_label.setVisible(True)
|
||||
|
||||
def check_warn_templatenetvm(self):
|
||||
if self.vm.klass == 'TemplateVM':
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self,
|
||||
self.tr("Warning!"),
|
||||
self.tr("Connecting a TemplateVM directly to a network is higly"
|
||||
" discouraged! <br> <small>You are breaking a basic par"
|
||||
"t of Qubes security and there is probably no real need"
|
||||
" to do so. Continue at your own risk.</small>"))
|
||||
|
||||
def check_warn_dispvmnetvm(self):
|
||||
if not hasattr(self.vm, 'default_dispvm'):
|
||||
self.warn_netvm_dispvm.setVisible(False)
|
||||
|
@ -350,6 +350,24 @@ Template</string>
|
||||
<property name="title">
|
||||
<string>&Qube</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="template_menu">
|
||||
<property name="title">
|
||||
<string>Template</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/templatevm.png</normaloff>:/templatevm.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="network_menu">
|
||||
<property name="title">
|
||||
<string>Network</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/netvm.png</normaloff>:/netvm.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="action_createvm"/>
|
||||
<addaction name="action_removevm"/>
|
||||
<addaction name="action_clonevm"/>
|
||||
@ -361,6 +379,8 @@ Template</string>
|
||||
<addaction name="action_killvm"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_settings"/>
|
||||
<addaction name="template_menu"/>
|
||||
<addaction name="network_menu"/>
|
||||
<addaction name="action_editfwrules"/>
|
||||
<addaction name="action_appmenus"/>
|
||||
<addaction name="action_updatevm"/>
|
||||
|
Loading…
Reference in New Issue
Block a user