Merge remote-tracking branch 'qubesos/pr/120'
This commit is contained in:
commit
ee9862d010
@ -35,6 +35,7 @@ from pydbus import SessionBus
|
|||||||
|
|
||||||
from qubesadmin import Qubes
|
from qubesadmin import Qubes
|
||||||
from qubesadmin import exc
|
from qubesadmin import exc
|
||||||
|
from qubesadmin import utils
|
||||||
|
|
||||||
from PyQt4 import QtGui # pylint: disable=import-error
|
from PyQt4 import QtGui # pylint: disable=import-error
|
||||||
from PyQt4 import QtCore # pylint: disable=import-error
|
from PyQt4 import QtCore # pylint: disable=import-error
|
||||||
@ -49,6 +50,7 @@ from . import global_settings
|
|||||||
from . import restore
|
from . import restore
|
||||||
from . import backup
|
from . import backup
|
||||||
from . import log_dialog
|
from . import log_dialog
|
||||||
|
from . import utils as manager_utils
|
||||||
|
|
||||||
|
|
||||||
class SearchBox(QtGui.QLineEdit):
|
class SearchBox(QtGui.QLineEdit):
|
||||||
@ -412,7 +414,11 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
|||||||
def check_updates(self):
|
def check_updates(self):
|
||||||
for vm in self.qubes_app.domains:
|
for vm in self.qubes_app.domains:
|
||||||
if vm.klass in {'TemplateVM', 'StandaloneVM'}:
|
if vm.klass in {'TemplateVM', 'StandaloneVM'}:
|
||||||
|
try:
|
||||||
self.vms_in_table[vm.qid].update()
|
self.vms_in_table[vm.qid].update()
|
||||||
|
except exc.QubesException:
|
||||||
|
# the VM might have vanished in the meantime
|
||||||
|
pass
|
||||||
|
|
||||||
def on_domain_added(self, _, domain):
|
def on_domain_added(self, _, domain):
|
||||||
#needs to clear cache
|
#needs to clear cache
|
||||||
@ -685,20 +691,24 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
|||||||
|
|
||||||
vm = self.get_selected_vm()
|
vm = self.get_selected_vm()
|
||||||
|
|
||||||
if vm.klass == 'TemplateVM':
|
dependencies = utils.vm_dependencies(self.qubes_app, vm)
|
||||||
dependent_vms = 0
|
|
||||||
for single_vm in self.qubes_app.domains:
|
if dependencies:
|
||||||
if getattr(single_vm, 'template', None) == vm:
|
list_text = "<br>" + \
|
||||||
dependent_vms += 1
|
manager_utils.format_dependencies_list(dependencies) + \
|
||||||
if dependent_vms > 0:
|
"<br>"
|
||||||
QtGui.QMessageBox.warning(
|
|
||||||
None, self.tr("Warning!"),
|
info_dialog = QtGui.QMessageBox(self)
|
||||||
self.tr("This Template Qube cannot be removed, "
|
info_dialog.setWindowTitle(self.tr("Warning!"))
|
||||||
"because there is at least one Qube that is based "
|
info_dialog.setText(
|
||||||
"on it.<br><small>If you want to remove this "
|
self.tr("This qube cannot be removed. It is used as:"
|
||||||
"Template Qube and all the Qubes based on it, you "
|
" <br> {} <small>If you want to remove this qube, "
|
||||||
"should first remove each individual Qube that "
|
"you should remove or change settings of each qube "
|
||||||
"uses this template.</small>"))
|
"or setting that uses it.</small>").format(list_text))
|
||||||
|
info_dialog.setModal(False)
|
||||||
|
info_dialog.show()
|
||||||
|
self.qt_app.processEvents()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
(requested_name, ok) = QtGui.QInputDialog.getText(
|
(requested_name, ok) = QtGui.QInputDialog.getText(
|
||||||
@ -990,8 +1000,24 @@ class VmManagerWindow(ui_qubemanager.Ui_VmManagerWindow, QtGui.QMainWindow):
|
|||||||
settings_window = settings.VMSettingsWindow(
|
settings_window = settings.VMSettingsWindow(
|
||||||
vm, self.qt_app, "basic")
|
vm, self.qt_app, "basic")
|
||||||
settings_window.exec_()
|
settings_window.exec_()
|
||||||
self.vms_in_table[vm.qid].update()
|
|
||||||
|
|
||||||
|
vm_deleted = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# the VM might not exist after running Settings - it might
|
||||||
|
# have been cloned or removed
|
||||||
|
self.vms_in_table[vm.qid].update()
|
||||||
|
except exc.QubesException:
|
||||||
|
# TODO: this will be replaced by proper signal handling once
|
||||||
|
# settings are migrated to AdminAPI
|
||||||
|
vm_deleted = True
|
||||||
|
|
||||||
|
if vm_deleted:
|
||||||
|
for row in self.vms_in_table:
|
||||||
|
try:
|
||||||
|
self.vms_in_table[row].update()
|
||||||
|
except exc.QubesException:
|
||||||
|
pass
|
||||||
|
|
||||||
# noinspection PyArgumentList
|
# noinspection PyArgumentList
|
||||||
@QtCore.pyqtSlot(name='on_action_appmenus_triggered')
|
@QtCore.pyqtSlot(name='on_action_appmenus_triggered')
|
||||||
|
@ -134,6 +134,7 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
|||||||
if self.verify_only.isChecked():
|
if self.verify_only.isChecked():
|
||||||
self.backup_restore.options.verify_only = True
|
self.backup_restore.options.verify_only = True
|
||||||
|
|
||||||
|
# pylint: disable=assignment-from-no-return
|
||||||
self.vms_to_restore = self.backup_restore.get_restore_info()
|
self.vms_to_restore = self.backup_restore.get_restore_info()
|
||||||
|
|
||||||
for vmname in self.vms_to_restore:
|
for vmname in self.vms_to_restore:
|
||||||
@ -182,7 +183,7 @@ class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
|||||||
self.__fill_vms_list__()
|
self.__fill_vms_list__()
|
||||||
|
|
||||||
elif self.currentPage() is self.confirm_page:
|
elif self.currentPage() is self.confirm_page:
|
||||||
|
# pylint: disable=assignment-from-no-return
|
||||||
self.vms_to_restore = self.backup_restore.get_restore_info()
|
self.vms_to_restore = self.backup_restore.get_restore_info()
|
||||||
|
|
||||||
for i in range(self.select_vms_widget.available_list.count()):
|
for i in range(self.select_vms_widget.available_list.count()):
|
||||||
|
@ -34,6 +34,7 @@ import traceback
|
|||||||
import sys
|
import sys
|
||||||
from qubesadmin.tools import QubesArgumentParser
|
from qubesadmin.tools import QubesArgumentParser
|
||||||
from qubesadmin import devices
|
from qubesadmin import devices
|
||||||
|
from qubesadmin import utils as admin_utils
|
||||||
import qubesadmin.exc
|
import qubesadmin.exc
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
@ -481,10 +482,35 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _rename_vm(self, t_monitor, name):
|
def _rename_vm(self, t_monitor, name, dependencies):
|
||||||
try:
|
try:
|
||||||
self.vm.app.clone_vm(self.vm, name)
|
new_vm = self.vm.app.clone_vm(self.vm, name)
|
||||||
|
|
||||||
|
failed_props = []
|
||||||
|
|
||||||
|
for (holder, prop) in dependencies:
|
||||||
|
try:
|
||||||
|
if holder is None:
|
||||||
|
setattr(self.vm.app, prop, new_vm)
|
||||||
|
else:
|
||||||
|
setattr(holder, prop, new_vm)
|
||||||
|
except qubesadmin.exc.QubesException as qex:
|
||||||
|
failed_props += [(holder, prop)]
|
||||||
|
|
||||||
|
if not failed_props:
|
||||||
del self.vm.app.domains[self.vm.name]
|
del self.vm.app.domains[self.vm.name]
|
||||||
|
else:
|
||||||
|
list_text = utils.format_dependencies_list(failed_props)
|
||||||
|
|
||||||
|
QtGui.QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
self.tr("Warning: rename partially unsuccessful"),
|
||||||
|
self.tr("Some properties could not be changed to the new "
|
||||||
|
"name. The system has now both {} and {} qubes. "
|
||||||
|
"To resolve this, please check and change the "
|
||||||
|
"following properties and remove the qube {} "
|
||||||
|
"manually.<br> ").format(
|
||||||
|
self.vm.name, name, self.vm.name) + list_text)
|
||||||
|
|
||||||
except qubesadmin.exc.QubesException as qex:
|
except qubesadmin.exc.QubesException as qex:
|
||||||
t_monitor.set_error_msg(str(qex))
|
t_monitor.set_error_msg(str(qex))
|
||||||
@ -494,13 +520,31 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
|||||||
t_monitor.set_finished()
|
t_monitor.set_finished()
|
||||||
|
|
||||||
def rename_vm(self):
|
def rename_vm(self):
|
||||||
|
|
||||||
|
dependencies = admin_utils.vm_dependencies(self.vm.app, self.vm)
|
||||||
|
|
||||||
|
running_dependencies = [vm.name for (vm, prop) in dependencies
|
||||||
|
if vm and prop == 'template'
|
||||||
|
and vm.is_running()]
|
||||||
|
|
||||||
|
if running_dependencies:
|
||||||
|
QtGui.QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
self.tr("Qube cannot be renamed!"),
|
||||||
|
self.tr(
|
||||||
|
"The following qubes using this qube as a template are "
|
||||||
|
"running: <br> {}. <br> In order to rename this qube, you "
|
||||||
|
"must first shut them down.").format(
|
||||||
|
", ".join(running_dependencies)))
|
||||||
|
return
|
||||||
|
|
||||||
new_vm_name, ok = QtGui.QInputDialog.getText(
|
new_vm_name, ok = QtGui.QInputDialog.getText(
|
||||||
self,
|
self,
|
||||||
self.tr('Rename qube'),
|
self.tr('Rename qube'),
|
||||||
self.tr('New name: (WARNING: all other changes will be discarded)'))
|
self.tr('New name: (WARNING: all other changes will be discarded)'))
|
||||||
|
|
||||||
if ok:
|
if ok:
|
||||||
if self._run_in_thread(self._rename_vm, new_vm_name):
|
if self._run_in_thread(self._rename_vm, new_vm_name, dependencies):
|
||||||
self.done(0)
|
self.done(0)
|
||||||
|
|
||||||
def _remove_vm(self, t_monitor):
|
def _remove_vm(self, t_monitor):
|
||||||
@ -516,6 +560,21 @@ class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
|||||||
|
|
||||||
def remove_vm(self):
|
def remove_vm(self):
|
||||||
|
|
||||||
|
dependencies = admin_utils.vm_dependencies(self.vm.app, self.vm)
|
||||||
|
|
||||||
|
if dependencies:
|
||||||
|
list_text = utils.format_dependencies_list(dependencies)
|
||||||
|
QtGui.QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
self.tr("Qube cannot be removed!"),
|
||||||
|
self.tr("This qube cannot be removed. It is used as:"
|
||||||
|
" <br> {} <small>If you want to remove this qube, "
|
||||||
|
"you should remove or change settings of each qube "
|
||||||
|
"or setting that uses it.</small>").format(list_text))
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
answer, ok = QtGui.QInputDialog.getText(
|
answer, ok = QtGui.QInputDialog.getText(
|
||||||
self,
|
self,
|
||||||
self.tr('Delete qube'),
|
self.tr('Delete qube'),
|
||||||
|
@ -172,3 +172,18 @@ def get_path_from_vm(vm, service_name):
|
|||||||
assert '\0' not in untrusted_path
|
assert '\0' not in untrusted_path
|
||||||
return untrusted_path.strip()
|
return untrusted_path.strip()
|
||||||
raise ValueError('Unexpected characters in path.')
|
raise ValueError('Unexpected characters in path.')
|
||||||
|
|
||||||
|
|
||||||
|
def format_dependencies_list(dependencies):
|
||||||
|
"""Given a list of tuples representing properties, formats them in
|
||||||
|
a readable list."""
|
||||||
|
|
||||||
|
list_text = ""
|
||||||
|
for (holder, prop) in dependencies:
|
||||||
|
if holder is None:
|
||||||
|
list_text += "- Global property <b>{}</b> <br>".format(prop)
|
||||||
|
else:
|
||||||
|
list_text += "- <b>{}</b> for qube <b>{}</b> <br>".format(
|
||||||
|
prop, holder.name)
|
||||||
|
|
||||||
|
return list_text
|
||||||
|
@ -8,3 +8,6 @@ def updates_vms_status(*args, **kwargs):
|
|||||||
|
|
||||||
def size_to_human(*args, **kwargs):
|
def size_to_human(*args, **kwargs):
|
||||||
return args[0]
|
return args[0]
|
||||||
|
|
||||||
|
def vm_dependencies(*args):
|
||||||
|
return args[0]
|
||||||
|
Loading…
Reference in New Issue
Block a user