Merge branch 'master' into virt-mode-select-fixes
This commit is contained in:
commit
48c3bfa798
14
.travis.yml
14
.travis.yml
@ -1,7 +1,13 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: generic
|
||||
install: git clone https://github.com/QubesOS/qubes-builder ~/qubes-builder
|
||||
script: ~/qubes-builder/scripts/travis-build
|
||||
language: python
|
||||
python:
|
||||
- '3.5'
|
||||
install:
|
||||
- pip install --quiet -r ci/requirements.txt
|
||||
- git clone https://github.com/QubesOS/qubes-builder ~/qubes-builder
|
||||
script:
|
||||
- PYTHONPATH=test-packages pylint --rcfile=ci/pylintrc qubesmanager
|
||||
- ~/qubes-builder/scripts/travis-build
|
||||
env:
|
||||
- DIST_DOM0=fc23 USE_QUBES_REPO_VERSION=3.2 USE_QUBES_REPO_TESTING=1
|
||||
- DIST_DOM0=fc25 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
|
||||
|
211
ci/pylintrc
Normal file
211
ci/pylintrc
Normal file
@ -0,0 +1,211 @@
|
||||
[MASTER]
|
||||
persistent=no
|
||||
ignore=tests,
|
||||
ui_about.py,
|
||||
ui_backupdlg.py,
|
||||
ui_bootfromdevice.py,
|
||||
ui_globalsettingsdlg.py,
|
||||
ui_informationnotes.py,
|
||||
ui_logdlg.py,
|
||||
ui_multiselectwidget.py,
|
||||
ui_newappvmdlg.py,
|
||||
ui_newfwruledlg.py,
|
||||
ui_releasenotes.py,
|
||||
ui_restoredlg.py,
|
||||
ui_settingsdlg.py,
|
||||
resources_rc.py
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
# abstract-class-little-used: see http://www.logilab.org/ticket/111138
|
||||
# deprecated-method:
|
||||
# enable again after disabling py-3.4.3 asyncio.ensure_future compat hack
|
||||
disable=
|
||||
abstract-class-little-used,
|
||||
bad-continuation,
|
||||
cyclic-import,
|
||||
deprecated-method,
|
||||
duplicate-code,
|
||||
file-ignored,
|
||||
fixme,
|
||||
inconsistent-return-statements,
|
||||
locally-disabled,
|
||||
locally-enabled,
|
||||
logging-format-interpolation,
|
||||
missing-docstring,
|
||||
star-args,
|
||||
useless-super-delegation,
|
||||
wrong-import-order
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||
# (visual studio) and html
|
||||
output-format=colorized
|
||||
|
||||
#files-output=no
|
||||
reports=yes
|
||||
|
||||
[TYPECHECK]
|
||||
#ignored-classes=
|
||||
|
||||
ignore-mixin-members=yes
|
||||
generated-members=
|
||||
iter_entry_points,
|
||||
Element,ElementTree,QName,SubElement,fromstring,parse,tostring,
|
||||
|
||||
[BASIC]
|
||||
|
||||
# List of builtins function names that should not be used, separated by a comma
|
||||
bad-functions=
|
||||
|
||||
# Regular expression which should only match correct module names
|
||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||
|
||||
# Regular expression which should only match correct module level names
|
||||
const-rgx=(([A-Za-z_][A-Za-z0-9_]*)|(__.*__))$
|
||||
|
||||
# Regular expression which should only match correct class names
|
||||
class-rgx=([A-Z_][a-zA-Z0-9]+|TC_\d\d_[a-zA-Z0-9_]+)$
|
||||
|
||||
# Regular expression which should only match correct function names
|
||||
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct method names
|
||||
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct instance attribute names
|
||||
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct argument names
|
||||
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct variable names
|
||||
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||
|
||||
# Regular expression which should only match correct list comprehension /
|
||||
# generator expression variable names
|
||||
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma
|
||||
good-names=e,i,j,k,m,p,v,ex,Run,_,log,vm,ok,ip
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma
|
||||
bad-names=foo,bar,baz,toto,tutu,tata
|
||||
|
||||
# Regular expression which should only match functions or classes name which do
|
||||
# not require a docstring
|
||||
no-docstring-rgx=__.*__
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,FIX,XXX,TODO
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=80
|
||||
|
||||
# Maximum number of lines in a module
|
||||
max-module-lines=3000
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# A regular expression matching the beginning of the name of dummy variables
|
||||
# (i.e. not used).
|
||||
dummy-variables-rgx=_|dummy
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=regsub,TERMIOS,Bastion,rexec
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled)
|
||||
import-graph=
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
int-import-graph=
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method
|
||||
max-args=35
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore
|
||||
ignored-argument-names=_.*
|
||||
|
||||
# Maximum number of locals for function / method body
|
||||
# Let's have max-args + 5
|
||||
max-locals=40
|
||||
|
||||
# Maximum number of return / yield for function / method body
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of branch for function / method body
|
||||
# 4x the default value
|
||||
max-branches=48
|
||||
|
||||
# Maximum number of statements in function / method body
|
||||
# Double default
|
||||
max-statements=100
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=15
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=100
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "Exception"
|
||||
overgeneral-exceptions=Exception,EnvironmentError
|
||||
|
||||
# vim: ft=conf
|
11
ci/requirements.txt
Normal file
11
ci/requirements.txt
Normal file
@ -0,0 +1,11 @@
|
||||
# WARNING: those requirements are used only for travis-ci.org
|
||||
# they SHOULD NOT be used under normal conditions; use system package manager
|
||||
coverage
|
||||
codecov
|
||||
docutils
|
||||
jinja2
|
||||
lxml
|
||||
pylint
|
||||
sphinx
|
||||
pydbus
|
||||
PyYAML
|
9
qubes-backup-restore.desktop
Normal file
9
qubes-backup-restore.desktop
Normal file
@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Exec=qubes-backup-restore
|
||||
Icon=qubes-manager
|
||||
Terminal=false
|
||||
Name=Restore Backup
|
||||
GenericName=Restore Backup
|
||||
StartupNotify=false
|
||||
Categories=System;
|
9
qubes-backup.desktop
Normal file
9
qubes-backup.desktop
Normal file
@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Exec=qubes-backup
|
||||
Icon=qubes-manager
|
||||
Terminal=false
|
||||
Name=Backup Qubes
|
||||
GenericName=Backup Qubes
|
||||
StartupNotify=false
|
||||
Categories=System;
|
@ -1,7 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Exec=qubes-global-settings
|
||||
Path=/user/bin
|
||||
Icon=qubes-manager
|
||||
Terminal=false
|
||||
Name=Qubes Global Settings
|
||||
|
9
qubes-qube-manager.desktop
Normal file
9
qubes-qube-manager.desktop
Normal file
@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Exec=qubes-qube-manager
|
||||
Icon=qubes-manager
|
||||
Terminal=false
|
||||
Name=Qube Manager
|
||||
GenericName=Qube Manager
|
||||
StartupNotify=false
|
||||
Categories=System;
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# coding=utf-8
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
@ -16,21 +16,19 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
from PyQt4.QtCore import SIGNAL, SLOT
|
||||
from PyQt4.QtGui import QDialog, QIcon
|
||||
from PyQt4.QtCore import SIGNAL, SLOT # pylint: disable=import-error
|
||||
from PyQt4.QtGui import QDialog, QIcon # pylint: disable=import-error
|
||||
from qubesmanager.releasenotes import ReleaseNotesDialog
|
||||
from qubesmanager.informationnotes import InformationNotesDialog
|
||||
|
||||
from .ui_about import *
|
||||
from . import ui_about # pylint: disable=no-name-in-module
|
||||
|
||||
|
||||
|
||||
class AboutDialog(Ui_AboutDialog, QDialog):
|
||||
class AboutDialog(ui_about.Ui_AboutDialog, QDialog):
|
||||
def __init__(self):
|
||||
super(AboutDialog, self).__init__()
|
||||
|
||||
|
@ -14,28 +14,19 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
from operator import itemgetter
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
|
||||
|
||||
import qubesmanager.resources_rc
|
||||
import PyQt4.QtGui # pylint: disable=import-error
|
||||
|
||||
# TODO description in tooltip
|
||||
# TODO icon
|
||||
class AppListWidgetItem(QListWidgetItem):
|
||||
# pylint: disable=too-few-public-methods
|
||||
class AppListWidgetItem(PyQt4.QtGui.QListWidgetItem):
|
||||
def __init__(self, name, ident, parent=None):
|
||||
super(AppListWidgetItem, self).__init__(name, parent)
|
||||
# self.setToolTip(command)
|
||||
@ -43,12 +34,12 @@ class AppListWidgetItem(QListWidgetItem):
|
||||
|
||||
@classmethod
|
||||
def from_line(cls, line):
|
||||
ident, icon_name, name = line.strip().split(maxsplit=2)
|
||||
ident, _icon_name, name = line.strip().split(maxsplit=2)
|
||||
return cls(name=name, ident=ident)
|
||||
|
||||
|
||||
class AppmenuSelectManager:
|
||||
def __init__(self, vm, apps_multiselect, parent=None):
|
||||
def __init__(self, vm, apps_multiselect):
|
||||
self.vm = vm
|
||||
self.app_list = apps_multiselect # this is a multiselect wiget
|
||||
self.whitelisted = None
|
||||
@ -60,7 +51,9 @@ class AppmenuSelectManager:
|
||||
).decode().strip().split('\n') if line]
|
||||
|
||||
# Check if appmenu entry is really installed
|
||||
# whitelisted = [a for a in whitelisted if os.path.exists('%s/apps/%s-%s' % (self.vm.dir_path, self.vm.name, a))]
|
||||
# whitelisted = [a for a in whitelisted
|
||||
# if os.path.exists('%s/apps/%s-%s' %
|
||||
# (self.vm.dir_path, self.vm.name, a))]
|
||||
|
||||
self.app_list.clear()
|
||||
|
||||
@ -69,11 +62,11 @@ class AppmenuSelectManager:
|
||||
'--get-available', '--i-understand-format-is-unstable',
|
||||
self.vm.name]).decode().splitlines()]
|
||||
|
||||
for a in available_appmenus:
|
||||
if a.ident in self.whitelisted:
|
||||
self.app_list.selected_list.addItem(a)
|
||||
for app in available_appmenus:
|
||||
if app.ident in self.whitelisted:
|
||||
self.app_list.selected_list.addItem(app)
|
||||
else:
|
||||
self.app_list.available_list.addItem(a)
|
||||
self.app_list.available_list.addItem(app)
|
||||
|
||||
self.app_list.available_list.sortItems()
|
||||
self.app_list.selected_list.sortItems()
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
@ -15,84 +15,67 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import traceback
|
||||
|
||||
import signal
|
||||
|
||||
from qubesadmin import Qubes, exc
|
||||
from qubesadmin import utils as admin_utils
|
||||
from qubes.storage.file import get_disk_usage
|
||||
|
||||
from PyQt4 import QtCore # pylint: disable=import-error
|
||||
from PyQt4 import QtGui # pylint: disable=import-error
|
||||
from . import ui_backupdlg # pylint: disable=no-name-in-module
|
||||
from . import multiselectwidget
|
||||
|
||||
from . import backup_utils
|
||||
from . import utils
|
||||
import grp
|
||||
import pwd
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
import shutil
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from qubes.qubes import QubesDaemonPidfile
|
||||
from qubes.qubes import QubesHost
|
||||
from qubes import backup
|
||||
from qubes import qubesutils
|
||||
|
||||
import qubesmanager.resources_rc
|
||||
|
||||
from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
|
||||
|
||||
from . import thread_monitor
|
||||
import threading
|
||||
import time
|
||||
from .thread_monitor import *
|
||||
from operator import itemgetter
|
||||
|
||||
from datetime import datetime
|
||||
from string import replace
|
||||
|
||||
from .ui_backupdlg import *
|
||||
from .multiselectwidget import *
|
||||
|
||||
from .backup_utils import *
|
||||
import main
|
||||
import grp,pwd
|
||||
|
||||
|
||||
class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
class BackupVMsWindow(ui_backupdlg.Ui_Backup, multiselectwidget.QtGui.QWizard):
|
||||
|
||||
__pyqtSignals__ = ("backup_progress(int)",)
|
||||
|
||||
def __init__(self, app, qvm_collection, blk_manager, shutdown_vm_func, parent=None):
|
||||
def __init__(self, qt_app, qubes_app, parent=None):
|
||||
super(BackupVMsWindow, self).__init__(parent)
|
||||
|
||||
self.app = app
|
||||
self.qvm_collection = qvm_collection
|
||||
self.blk_manager = blk_manager
|
||||
self.shutdown_vm_func = shutdown_vm_func
|
||||
self.qt_app = qt_app
|
||||
self.qubes_app = qubes_app
|
||||
self.backup_settings = QtCore.QSettings()
|
||||
|
||||
self.func_output = []
|
||||
self.selected_vms = []
|
||||
self.tmpdir_to_remove = None
|
||||
self.canceled = False
|
||||
|
||||
self.vm = self.qvm_collection[0]
|
||||
self.files_to_backup = None
|
||||
|
||||
assert self.vm != None
|
||||
self.thread_monitor = None
|
||||
|
||||
self.setupUi(self)
|
||||
|
||||
self.progress_status.text = self.tr("Backup in progress...")
|
||||
self.show_running_vms_warning(False)
|
||||
self.dir_line_edit.setReadOnly(False)
|
||||
|
||||
self.select_vms_widget = MultiSelectWidget(self)
|
||||
self.select_vms_widget = multiselectwidget.MultiSelectWidget(self)
|
||||
self.verticalLayout.insertWidget(1, self.select_vms_widget)
|
||||
|
||||
self.connect(self, SIGNAL("currentIdChanged(int)"), self.current_page_changed)
|
||||
self.connect(self.select_vms_widget, SIGNAL("selected_changed()"), self.check_running)
|
||||
self.connect(self.select_vms_widget, SIGNAL("items_removed(PyQt_PyObject)"), self.vms_removed)
|
||||
self.connect(self.select_vms_widget, SIGNAL("items_added(PyQt_PyObject)"), self.vms_added)
|
||||
self.refresh_button.clicked.connect(self.check_running)
|
||||
self.shutdown_running_vms_button.clicked.connect(self.shutdown_all_running_selected)
|
||||
self.connect(self, SIGNAL("backup_progress(int)"), self.progress_bar.setValue)
|
||||
self.dir_line_edit.connect(self.dir_line_edit, SIGNAL("textChanged(QString)"), self.backup_location_changed)
|
||||
self.connect(self, QtCore.SIGNAL("currentIdChanged(int)"),
|
||||
self.current_page_changed)
|
||||
self.connect(self.select_vms_widget,
|
||||
QtCore.SIGNAL("items_removed(PyQt_PyObject)"),
|
||||
self.vms_removed)
|
||||
self.connect(self.select_vms_widget,
|
||||
QtCore.SIGNAL("items_added(PyQt_PyObject)"),
|
||||
self.vms_added)
|
||||
self.dir_line_edit.connect(self.dir_line_edit,
|
||||
QtCore.SIGNAL("textChanged(QString)"),
|
||||
self.backup_location_changed)
|
||||
|
||||
self.select_vms_page.isComplete = self.has_selected_vms
|
||||
self.select_dir_page.isComplete = self.has_selected_dir_and_pass
|
||||
@ -100,300 +83,256 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
# this causes to run isComplete() twice, I don't know why
|
||||
self.select_vms_page.connect(
|
||||
self.select_vms_widget,
|
||||
SIGNAL("selected_changed()"),
|
||||
SIGNAL("completeChanged()"))
|
||||
QtCore.SIGNAL("selected_changed()"),
|
||||
QtCore.SIGNAL("completeChanged()"))
|
||||
self.passphrase_line_edit.connect(
|
||||
self.passphrase_line_edit,
|
||||
SIGNAL("textChanged(QString)"),
|
||||
QtCore.SIGNAL("textChanged(QString)"),
|
||||
self.backup_location_changed)
|
||||
self.passphrase_line_edit_verify.connect(
|
||||
self.passphrase_line_edit_verify,
|
||||
SIGNAL("textChanged(QString)"),
|
||||
QtCore.SIGNAL("textChanged(QString)"),
|
||||
self.backup_location_changed)
|
||||
|
||||
self.total_size = 0
|
||||
self.__fill_vms_list__()
|
||||
|
||||
fill_appvms_list(self)
|
||||
self.load_settings()
|
||||
self.target_vm_list, self.target_vm_idx = utils.prepare_vm_choice(
|
||||
self.appvm_combobox,
|
||||
self.qubes_app,
|
||||
None,
|
||||
self.qubes_app.domains['dom0'],
|
||||
(lambda vm: vm.klass != 'TemplateVM' and vm.is_running()),
|
||||
allow_internal=False,
|
||||
allow_default=False,
|
||||
allow_none=False
|
||||
)
|
||||
|
||||
selected = self.load_settings()
|
||||
self.__fill_vms_list__(selected)
|
||||
|
||||
def load_settings(self):
|
||||
dest_vm_name = main.manager_window.manager_settings.value(
|
||||
'backup/vmname', defaultValue="")
|
||||
dest_vm_idx = self.appvm_combobox.findText(dest_vm_name.toString())
|
||||
"""
|
||||
Helper function that tries to load existing backup profile
|
||||
(default path: /etc/qubes/backup/qubes-manager-backup.conf )
|
||||
and then apply its contents to the Backup window.
|
||||
:return: list of vms to include in backup, if it exists in the profile,
|
||||
or None if it does not
|
||||
"""
|
||||
try:
|
||||
profile_data = backup_utils.load_backup_profile()
|
||||
except FileNotFoundError:
|
||||
return
|
||||
except exc.QubesException:
|
||||
QtGui.QMessageBox.information(
|
||||
None, self.tr("Error loading backup profile"),
|
||||
self.tr("Unable to load saved backup profile."))
|
||||
return
|
||||
if not profile_data:
|
||||
return
|
||||
|
||||
if 'destination_vm' in profile_data:
|
||||
dest_vm_name = profile_data['destination_vm']
|
||||
dest_vm_idx = self.appvm_combobox.findText(dest_vm_name)
|
||||
if dest_vm_idx > -1:
|
||||
self.appvm_combobox.setCurrentIndex(dest_vm_idx)
|
||||
|
||||
if main.manager_window.manager_settings.contains('backup/path'):
|
||||
dest_path = main.manager_window.manager_settings.value(
|
||||
'backup/path', defaultValue=None)
|
||||
self.dir_line_edit.setText(dest_path.toString())
|
||||
if 'destination_path' in profile_data:
|
||||
dest_path = profile_data['destination_path']
|
||||
self.dir_line_edit.setText(dest_path)
|
||||
|
||||
if main.manager_window.manager_settings.contains('backup/encrypt'):
|
||||
encrypt = main.manager_window.manager_settings.value(
|
||||
'backup/encrypt', defaultValue=None)
|
||||
self.encryption_checkbox.setChecked(encrypt.toBool())
|
||||
if 'passphrase_text' in profile_data:
|
||||
self.passphrase_line_edit.setText(profile_data['passphrase_text'])
|
||||
self.passphrase_line_edit_verify.setText(
|
||||
profile_data['passphrase_text'])
|
||||
|
||||
def save_settings(self):
|
||||
main.manager_window.manager_settings.setValue(
|
||||
'backup/vmname', self.appvm_combobox.currentText())
|
||||
main.manager_window.manager_settings.setValue(
|
||||
'backup/path', self.dir_line_edit.text())
|
||||
main.manager_window.manager_settings.setValue(
|
||||
'backup/encrypt', self.encryption_checkbox.isChecked())
|
||||
if 'compression' in profile_data:
|
||||
self.compress_checkbox.setChecked(profile_data['compression'])
|
||||
|
||||
def show_running_vms_warning(self, show):
|
||||
self.running_vms_warning.setVisible(show)
|
||||
self.shutdown_running_vms_button.setVisible(show)
|
||||
self.refresh_button.setVisible(show)
|
||||
if 'include' in profile_data:
|
||||
return profile_data['include']
|
||||
|
||||
class VmListItem(QListWidgetItem):
|
||||
return None
|
||||
|
||||
def save_settings(self, use_temp):
|
||||
"""
|
||||
Helper function that saves backup profile to either
|
||||
/etc/qubes/backup/qubes-manager-backup.conf or
|
||||
/etc/qubes/backup/qubes-manager-backup-tmp.conf
|
||||
:param use_temp: whether to use temporary profile (True) or the default
|
||||
backup profile (False)
|
||||
"""
|
||||
settings = {'destination_vm': self.appvm_combobox.currentText(),
|
||||
'destination_path': self.dir_line_edit.text(),
|
||||
'include': [vm.name for vm in self.selected_vms],
|
||||
'passphrase_text': self.passphrase_line_edit.text(),
|
||||
'compression': self.compress_checkbox.isChecked()}
|
||||
|
||||
backup_utils.write_backup_profile(settings, use_temp)
|
||||
|
||||
class VmListItem(QtGui.QListWidgetItem):
|
||||
# pylint: disable=too-few-public-methods
|
||||
def __init__(self, vm):
|
||||
self.vm = vm
|
||||
if vm.qid == 0:
|
||||
local_user = grp.getgrnam('qubes').gr_mem[0]
|
||||
home_dir = pwd.getpwnam(local_user).pw_dir
|
||||
self.size = qubesutils.get_disk_usage(home_dir)
|
||||
self.size = get_disk_usage(home_dir)
|
||||
else:
|
||||
self.size = self.get_vm_size(vm)
|
||||
super(BackupVMsWindow.VmListItem, self).__init__(vm.name+ " (" + qubesutils.size_to_human(self.size) + ")")
|
||||
self.size = vm.get_disk_utilization()
|
||||
super(BackupVMsWindow.VmListItem, self).__init__(
|
||||
vm.name + " (" + admin_utils.size_to_human(self.size) + ")")
|
||||
|
||||
def get_vm_size(self, vm):
|
||||
size = 0
|
||||
if vm.private_img is not None:
|
||||
size += qubesutils.get_disk_usage (vm.private_img)
|
||||
|
||||
if vm.updateable:
|
||||
size += qubesutils.get_disk_usage(vm.root_img)
|
||||
|
||||
return size
|
||||
|
||||
|
||||
def __fill_vms_list__(self):
|
||||
for vm in self.qvm_collection.values():
|
||||
if vm.internal:
|
||||
def __fill_vms_list__(self, selected=None):
|
||||
for vm in self.qubes_app.domains:
|
||||
if vm.features.get('internal', False):
|
||||
continue
|
||||
|
||||
item = BackupVMsWindow.VmListItem(vm)
|
||||
if vm.include_in_backups == True:
|
||||
if (selected is None and
|
||||
getattr(vm, 'include_in_backups', True)) \
|
||||
or (selected and vm.name in selected):
|
||||
self.select_vms_widget.selected_list.addItem(item)
|
||||
self.total_size += item.size
|
||||
else:
|
||||
self.select_vms_widget.available_list.addItem(item)
|
||||
self.select_vms_widget.available_list.sortItems()
|
||||
self.select_vms_widget.selected_list.sortItems()
|
||||
self.check_running()
|
||||
self.total_size_label.setText(qubesutils.size_to_human(self.total_size))
|
||||
|
||||
self.unrecognized_config_label.setVisible(
|
||||
selected is not None and
|
||||
len(selected) != len(self.select_vms_widget.selected_list))
|
||||
self.total_size_label.setText(
|
||||
admin_utils.size_to_human(self.total_size))
|
||||
|
||||
def vms_added(self, items):
|
||||
for i in items:
|
||||
self.total_size += i.size
|
||||
self.total_size_label.setText(qubesutils.size_to_human(self.total_size))
|
||||
self.total_size_label.setText(
|
||||
admin_utils.size_to_human(self.total_size))
|
||||
|
||||
def vms_removed(self, items):
|
||||
for i in items:
|
||||
self.total_size -= i.size
|
||||
self.total_size_label.setText(qubesutils.size_to_human(self.total_size))
|
||||
self.total_size_label.setText(
|
||||
admin_utils.size_to_human(self.total_size))
|
||||
|
||||
def check_running(self):
|
||||
some_selected_vms_running = False
|
||||
for i in range(self.select_vms_widget.selected_list.count()):
|
||||
item = self.select_vms_widget.selected_list.item(i)
|
||||
if item.vm.is_running() and item.vm.qid != 0:
|
||||
item.setForeground(QBrush(QColor(255, 0, 0)))
|
||||
some_selected_vms_running = True
|
||||
else:
|
||||
item.setForeground(QBrush(QColor(0, 0, 0)))
|
||||
|
||||
self.show_running_vms_warning(some_selected_vms_running)
|
||||
|
||||
for i in range(self.select_vms_widget.available_list.count()):
|
||||
item = self.select_vms_widget.available_list.item(i)
|
||||
if item.vm.is_running() and item.vm.qid != 0:
|
||||
item.setForeground(QBrush(QColor(255, 0, 0)))
|
||||
else:
|
||||
item.setForeground(QBrush(QColor(0, 0, 0)))
|
||||
|
||||
return some_selected_vms_running
|
||||
|
||||
def shutdown_all_running_selected(self):
|
||||
(names, vms) = self.get_running_vms()
|
||||
if len(vms) == 0:
|
||||
return
|
||||
|
||||
for vm in vms:
|
||||
self.blk_manager.check_if_serves_as_backend(vm)
|
||||
|
||||
reply = QMessageBox.question(None, self.tr("VM Shutdown Confirmation"),
|
||||
self.tr(
|
||||
"Are you sure you want to power down the following VMs: "
|
||||
"<b>{0}</b>?<br/>"
|
||||
"<small>This will shutdown all the running applications "
|
||||
"within them.</small>").format(', '.join(names)),
|
||||
QMessageBox.Yes | QMessageBox.Cancel)
|
||||
|
||||
self.app.processEvents()
|
||||
|
||||
if reply == QMessageBox.Yes:
|
||||
|
||||
wait_time = 60.0
|
||||
for vm in vms:
|
||||
self.shutdown_vm_func(vm, wait_time*1000)
|
||||
|
||||
progress = QProgressDialog ("Shutting down VMs <b>{0}</b>...".format(', '.join(names)), "", 0, 0)
|
||||
progress.setModal(True)
|
||||
progress.show()
|
||||
|
||||
wait_for = wait_time
|
||||
while self.check_running() and wait_for > 0:
|
||||
self.app.processEvents()
|
||||
time.sleep (0.5)
|
||||
wait_for -= 0.5
|
||||
|
||||
progress.hide()
|
||||
|
||||
def get_running_vms(self):
|
||||
names = []
|
||||
vms = []
|
||||
for i in range(self.select_vms_widget.selected_list.count()):
|
||||
item = self.select_vms_widget.selected_list.item(i)
|
||||
if item.vm.is_running() and item.vm.qid != 0:
|
||||
names.append(item.vm.name)
|
||||
vms.append(item.vm)
|
||||
return (names, vms)
|
||||
|
||||
@pyqtSlot(name='on_select_path_button_clicked')
|
||||
@QtCore.pyqtSlot(name='on_select_path_button_clicked')
|
||||
def select_path_button_clicked(self):
|
||||
select_path_button_clicked(self)
|
||||
backup_utils.select_path_button_clicked(self)
|
||||
|
||||
def validateCurrentPage(self):
|
||||
# pylint: disable=invalid-name
|
||||
if self.currentPage() is self.select_vms_page:
|
||||
if self.check_running():
|
||||
QMessageBox.information(None,
|
||||
self.tr("Wait!"),
|
||||
self.tr("Some selected VMs are running. "
|
||||
"Running VMs can not be backuped. "
|
||||
"Please shut them down or remove them from the list."))
|
||||
return False
|
||||
|
||||
self.selected_vms = []
|
||||
for i in range(self.select_vms_widget.selected_list.count()):
|
||||
self.selected_vms.append(self.select_vms_widget.selected_list.item(i).vm)
|
||||
self.selected_vms.append(
|
||||
self.select_vms_widget.selected_list.item(i).vm)
|
||||
|
||||
elif self.currentPage() is self.select_dir_page:
|
||||
backup_location = str(self.dir_line_edit.text())
|
||||
if not backup_location:
|
||||
QMessageBox.information(None, self.tr("Wait!"),
|
||||
QtGui.QMessageBox.information(
|
||||
None, self.tr("Wait!"),
|
||||
self.tr("Enter backup target location first."))
|
||||
return False
|
||||
if self.appvm_combobox.currentIndex() == 0 and \
|
||||
not os.path.isdir(backup_location):
|
||||
QMessageBox.information(None, self.tr("Wait!"),
|
||||
if self.appvm_combobox.currentIndex() == 0 \
|
||||
and not os.path.isdir(backup_location):
|
||||
QtGui.QMessageBox.information(
|
||||
None, self.tr("Wait!"),
|
||||
self.tr("Selected directory do not exists or "
|
||||
"not a directory (%s).") % backup_location)
|
||||
return False
|
||||
if not len(self.passphrase_line_edit.text()):
|
||||
QMessageBox.information(None, self.tr("Wait!"),
|
||||
self.tr("Enter passphrase for backup encryption/verification first."))
|
||||
if not self.passphrase_line_edit.text():
|
||||
QtGui.QMessageBox.information(
|
||||
None, self.tr("Wait!"),
|
||||
self.tr("Enter passphrase for backup "
|
||||
"encryption/verification first."))
|
||||
return False
|
||||
if self.passphrase_line_edit.text() != self.passphrase_line_edit_verify.text():
|
||||
QMessageBox.information(None,
|
||||
self.tr("Wait!"),
|
||||
if self.passphrase_line_edit.text() !=\
|
||||
self.passphrase_line_edit_verify.text():
|
||||
QtGui.QMessageBox.information(
|
||||
None, self.tr("Wait!"),
|
||||
self.tr("Enter the same passphrase in both fields."))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def gather_output(self, s):
|
||||
self.func_output.append(s)
|
||||
|
||||
def update_progress_bar(self, value):
|
||||
self.emit(SIGNAL("backup_progress(int)"), value)
|
||||
|
||||
def __do_backup__(self, thread_monitor):
|
||||
def __do_backup__(self, t_monitor):
|
||||
msg = []
|
||||
|
||||
try:
|
||||
backup.backup_do(self.dir_line_edit.text(),
|
||||
self.files_to_backup,
|
||||
self.passphrase_line_edit.text(),
|
||||
progress_callback=self.update_progress_bar,
|
||||
encrypted=self.encryption_checkbox.isChecked(),
|
||||
appvm=self.target_appvm)
|
||||
#simulate_long_lasting_proces(10, self.update_progress_bar)
|
||||
except backup.BackupCanceledError as ex:
|
||||
msg.append(str(ex))
|
||||
self.canceled = True
|
||||
if ex.tmpdir:
|
||||
self.tmpdir_to_remove = ex.tmpdir
|
||||
except Exception as ex:
|
||||
print("Exception:", ex)
|
||||
vm = self.qubes_app.domains[
|
||||
self.appvm_combobox.currentText()]
|
||||
if not vm.is_running():
|
||||
vm.start()
|
||||
self.qubes_app.qubesd_call(
|
||||
'dom0', 'admin.backup.Execute',
|
||||
backup_utils.get_profile_name(True))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
msg.append(str(ex))
|
||||
|
||||
if len(msg) > 0 :
|
||||
thread_monitor.set_error_msg('\n'.join(msg))
|
||||
if msg:
|
||||
t_monitor.set_error_msg('\n'.join(msg))
|
||||
|
||||
thread_monitor.set_finished()
|
||||
t_monitor.set_finished()
|
||||
|
||||
@staticmethod
|
||||
def cleanup_temporary_files():
|
||||
try:
|
||||
os.remove(backup_utils.get_profile_path(use_temp=True))
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
def current_page_changed(self, id):
|
||||
def current_page_changed(self, page_id): # pylint: disable=unused-argument
|
||||
old_sigchld_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
if self.currentPage() is self.confirm_page:
|
||||
|
||||
self.target_appvm = None
|
||||
if self.appvm_combobox.currentIndex() != 0: #An existing appvm chosen
|
||||
self.target_appvm = self.qvm_collection.get_vm_by_name(
|
||||
self.appvm_combobox.currentText())
|
||||
|
||||
del self.func_output[:]
|
||||
try:
|
||||
self.files_to_backup = backup.backup_prepare(
|
||||
self.selected_vms,
|
||||
print_callback = self.gather_output,
|
||||
hide_vm_names=self.encryption_checkbox.isChecked())
|
||||
except Exception as ex:
|
||||
print("Exception:", ex)
|
||||
QMessageBox.critical(None,
|
||||
self.tr("Error while preparing backup."),
|
||||
self.tr("ERROR: {0}").format(ex))
|
||||
self.save_settings(use_temp=True)
|
||||
backup_summary = self.qubes_app.qubesd_call(
|
||||
'dom0', 'admin.backup.Info',
|
||||
backup_utils.get_profile_name(True))
|
||||
|
||||
self.textEdit.setReadOnly(True)
|
||||
self.textEdit.setFontFamily("Monospace")
|
||||
self.textEdit.setText("\n".join(self.func_output))
|
||||
self.save_settings()
|
||||
self.textEdit.setText(backup_summary.decode())
|
||||
|
||||
elif self.currentPage() is self.commit_page:
|
||||
|
||||
if self.save_profile_checkbox.isChecked():
|
||||
self.save_settings(use_temp=False)
|
||||
|
||||
self.button(self.FinishButton).setDisabled(True)
|
||||
self.showFileDialog.setEnabled(
|
||||
self.appvm_combobox.currentIndex() != 0)
|
||||
self.showFileDialog.setChecked(self.showFileDialog.isEnabled()
|
||||
and str(self.dir_line_edit.text())
|
||||
.count("media/") > 0)
|
||||
self.thread_monitor = ThreadMonitor()
|
||||
thread = threading.Thread (target= self.__do_backup__ , args=(self.thread_monitor,))
|
||||
self.thread_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(
|
||||
target=self.__do_backup__,
|
||||
args=(self.thread_monitor,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
counter = 0
|
||||
while not self.thread_monitor.is_finished():
|
||||
self.app.processEvents()
|
||||
self.qt_app.processEvents()
|
||||
time.sleep(0.1)
|
||||
|
||||
if not self.thread_monitor.success:
|
||||
if self.canceled:
|
||||
self.progress_status.setText(self.tr("Backup aborted."))
|
||||
if self.tmpdir_to_remove:
|
||||
if QMessageBox.warning(None, self.tr("Backup aborted"),
|
||||
self.tr("Do you want to remove temporary files from "
|
||||
"%s?") % self.tmpdir_to_remove,
|
||||
QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes:
|
||||
shutil.rmtree(self.tmpdir_to_remove)
|
||||
self.progress_status.setText(
|
||||
self.tr(
|
||||
"Backup aborted. "
|
||||
"Temporary file may be left at backup location."))
|
||||
else:
|
||||
self.progress_status.setText(self.tr("Backup error."))
|
||||
QMessageBox.warning(self, self.tr("Backup error!"),
|
||||
QtGui.QMessageBox.warning(
|
||||
self, self.tr("Backup error!"),
|
||||
self.tr("ERROR: {}").format(
|
||||
self.thread_monitor.error_msg))
|
||||
else:
|
||||
self.progress_bar.setMaximum(100)
|
||||
self.progress_bar.setValue(100)
|
||||
self.progress_status.setText(self.tr("Backup finished."))
|
||||
if self.showFileDialog.isChecked():
|
||||
@ -402,84 +341,78 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
orig_text + self.tr(
|
||||
" Please unmount your backup volume and cancel "
|
||||
"the file selection dialog."))
|
||||
if self.target_appvm:
|
||||
self.target_appvm.run("QUBESRPC %s dom0" % "qubes"
|
||||
".SelectDirectory")
|
||||
backup_utils.select_path_button_clicked(self, False, True)
|
||||
self.button(self.CancelButton).setEnabled(False)
|
||||
self.button(self.FinishButton).setEnabled(True)
|
||||
self.showFileDialog.setEnabled(False)
|
||||
self.cleanup_temporary_files()
|
||||
signal.signal(signal.SIGCHLD, old_sigchld_handler)
|
||||
|
||||
def reject(self):
|
||||
#cancell clicked while the backup is in progress.
|
||||
#calling kill on tar.
|
||||
if self.currentPage() is self.commit_page:
|
||||
if backup.backup_cancel():
|
||||
self.canceled = True
|
||||
self.qubes_app.qubesd_call(
|
||||
'dom0', 'admin.backup.Cancel',
|
||||
backup_utils.get_profile_name(True))
|
||||
self.progress_bar.setMaximum(100)
|
||||
self.progress_bar.setValue(0)
|
||||
self.button(self.CancelButton).setDisabled(True)
|
||||
self.cleanup_temporary_files()
|
||||
else:
|
||||
self.cleanup_temporary_files()
|
||||
self.done(0)
|
||||
|
||||
def has_selected_vms(self):
|
||||
return self.select_vms_widget.selected_list.count() > 0
|
||||
|
||||
def has_selected_dir_and_pass(self):
|
||||
if not len(self.passphrase_line_edit.text()):
|
||||
if not self.passphrase_line_edit.text():
|
||||
return False
|
||||
if self.passphrase_line_edit.text() != self.passphrase_line_edit_verify.text():
|
||||
if self.passphrase_line_edit.text() != \
|
||||
self.passphrase_line_edit_verify.text():
|
||||
return False
|
||||
return len(self.dir_line_edit.text()) > 0
|
||||
|
||||
def backup_location_changed(self, new_dir=None):
|
||||
self.select_dir_page.emit(SIGNAL("completeChanged()"))
|
||||
# pylint: disable=unused-argument
|
||||
self.select_dir_page.emit(QtCore.SIGNAL("completeChanged()"))
|
||||
|
||||
|
||||
# Bases on the original code by:
|
||||
# Copyright (c) 2002-2007 Pascal Varet <p.varet@gmail.com>
|
||||
|
||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
import sys
|
||||
import os.path
|
||||
import traceback
|
||||
|
||||
filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop()
|
||||
filename = os.path.basename(filename)
|
||||
error = "%s: %s" % (exc_type.__name__, exc_value)
|
||||
|
||||
QMessageBox.critical(None, "Houston, we have a problem...",
|
||||
QtGui.QMessageBox.critical(
|
||||
None,
|
||||
"Houston, we have a problem...",
|
||||
"Whoops. A critical error has occured. This is most likely a bug "
|
||||
"in Qubes Restore VMs application.<br><br>"
|
||||
"<b><i>%s</i></b>" % error +
|
||||
"at <b>line %d</b> of file <b>%s</b>.<br/><br/>"
|
||||
"in Qubes Global Settings application.<br><br><b><i>%s</i></b>" %
|
||||
error + "at <b>line %d</b> of file <b>%s</b>.<br/><br/>"
|
||||
% (line, filename))
|
||||
|
||||
|
||||
def app_main():
|
||||
def main():
|
||||
|
||||
global qubes_host
|
||||
qubes_host = QubesHost()
|
||||
|
||||
global app
|
||||
app = QApplication(sys.argv)
|
||||
app.setOrganizationName("The Qubes Project")
|
||||
app.setOrganizationDomain("http://qubes-os.org")
|
||||
app.setApplicationName("Qubes Backup VMs")
|
||||
qt_app = QtGui.QApplication(sys.argv)
|
||||
qt_app.setOrganizationName("The Qubes Project")
|
||||
qt_app.setOrganizationDomain("http://qubes-os.org")
|
||||
qt_app.setApplicationName("Qubes Backup VMs")
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
app = Qubes()
|
||||
|
||||
global backup_window
|
||||
backup_window = BackupVMsWindow()
|
||||
backup_window = BackupVMsWindow(qt_app, app)
|
||||
|
||||
backup_window.show()
|
||||
|
||||
app.exec_()
|
||||
app.exit()
|
||||
|
||||
qt_app.exec_()
|
||||
qt_app.exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app_main()
|
||||
main()
|
||||
|
@ -14,96 +14,117 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4 import QtGui # pylint: disable=import-error
|
||||
from PyQt4 import QtCore # pylint: disable=import-error
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from .thread_monitor import *
|
||||
from . import utils
|
||||
import yaml
|
||||
|
||||
path_re = re.compile(r"[a-zA-Z0-9/:.,_+=() -]*")
|
||||
path_max_len = 512
|
||||
|
||||
|
||||
def fill_appvms_list(dialog):
|
||||
"""
|
||||
Helper function, designed to fill the destination vm combobox in both backup
|
||||
and restore GUI tools.
|
||||
:param dialog: QtGui.QWizard with a combobox called appvm_combobox
|
||||
"""
|
||||
dialog.appvm_combobox.clear()
|
||||
dialog.appvm_combobox.addItem("dom0")
|
||||
|
||||
dialog.appvm_combobox.setCurrentIndex(0) # current selected is null ""
|
||||
|
||||
for vm in dialog.qvm_collection.values():
|
||||
if vm.is_appvm() and vm.internal:
|
||||
continue
|
||||
if vm.is_template() and vm.installed_by_rpm:
|
||||
for vm in dialog.qubes_app.domains:
|
||||
if vm.features.get('internal', False) or vm.klass == 'TemplateVM':
|
||||
continue
|
||||
|
||||
if vm.is_running() and vm.qid != 0:
|
||||
dialog.appvm_combobox.addItem(vm.name)
|
||||
|
||||
|
||||
def enable_dir_line_edit(dialog, boolean):
|
||||
dialog.dir_line_edit.setEnabled(boolean)
|
||||
dialog.select_path_button.setEnabled(boolean)
|
||||
|
||||
def get_path_for_vm(vm, service_name):
|
||||
if not vm:
|
||||
return None
|
||||
proc = vm.run("QUBESRPC %s dom0" % service_name, passio_popen=True)
|
||||
proc.stdin.close()
|
||||
untrusted_path = proc.stdout.readline(path_max_len)
|
||||
if len(untrusted_path) == 0:
|
||||
return None
|
||||
if path_re.match(untrusted_path):
|
||||
assert '../' not in untrusted_path
|
||||
assert '\0' not in untrusted_path
|
||||
return untrusted_path.strip()
|
||||
else:
|
||||
return None
|
||||
|
||||
def select_path_button_clicked(dialog, select_file = False):
|
||||
def select_path_button_clicked(dialog, select_file=False, read_only=False):
|
||||
"""
|
||||
Helper function that displays a file/directory selection wizard. Used by
|
||||
backup and restore GUI tools.
|
||||
:param dialog: QtGui.QWizard with a dir_line_edit text box that wants to
|
||||
receive a file/directory path and appvm_combobox with VM to use
|
||||
:param select_file: True: select file dialog; False: select directory
|
||||
dialog
|
||||
:param read_only: should the dir_line_edit be changed after selecting a file
|
||||
or directory
|
||||
:return:
|
||||
"""
|
||||
backup_location = str(dialog.dir_line_edit.text())
|
||||
file_dialog = QFileDialog()
|
||||
file_dialog = QtGui.QFileDialog()
|
||||
file_dialog.setReadOnly(True)
|
||||
|
||||
if select_file:
|
||||
file_dialog_function = file_dialog.getOpenFileName
|
||||
else:
|
||||
file_dialog_function = file_dialog.getExistingDirectory
|
||||
|
||||
new_appvm = None
|
||||
new_path = None
|
||||
if dialog.appvm_combobox.currentIndex() != 0: #An existing appvm chosen
|
||||
|
||||
new_appvm = str(dialog.appvm_combobox.currentText())
|
||||
vm = dialog.qvm_collection.get_vm_by_name(new_appvm)
|
||||
if vm:
|
||||
new_path = get_path_for_vm(vm, "qubes.SelectFile" if select_file
|
||||
vm = dialog.qubes_app.domains[new_appvm]
|
||||
try:
|
||||
new_path = utils.get_path_from_vm(
|
||||
vm,
|
||||
"qubes.SelectFile" if select_file
|
||||
else "qubes.SelectDirectory")
|
||||
except subprocess.CalledProcessError:
|
||||
if not read_only:
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
dialog.tr("Nothing selected!"),
|
||||
dialog.tr("No file or directory selected."))
|
||||
else:
|
||||
new_path = file_dialog_function(dialog,
|
||||
dialog.tr("Select backup location."),
|
||||
backup_location if backup_location else '/')
|
||||
return
|
||||
|
||||
if new_path != None:
|
||||
if os.path.basename(new_path) == 'qubes.xml':
|
||||
backup_location = os.path.dirname(new_path)
|
||||
else:
|
||||
backup_location = new_path
|
||||
dialog.dir_line_edit.setText(backup_location)
|
||||
if new_path and not read_only:
|
||||
dialog.dir_line_edit.setText(new_path)
|
||||
|
||||
if (new_path or new_appvm) and len(backup_location) > 0:
|
||||
dialog.select_dir_page.emit(SIGNAL("completeChanged()"))
|
||||
if new_path and backup_location and not read_only:
|
||||
dialog.select_dir_page.emit(QtCore.SIGNAL("completeChanged()"))
|
||||
|
||||
def simulate_long_lasting_proces(period, progress_callback):
|
||||
for i in range(period):
|
||||
progress_callback((i*100)/period)
|
||||
time.sleep(1)
|
||||
|
||||
progress_callback(100)
|
||||
return 0
|
||||
def get_profile_name(use_temp):
|
||||
backup_profile_name = 'qubes-manager-backup'
|
||||
temp_backup_profile_name = 'qubes-manager-backup-tmp'
|
||||
|
||||
return temp_backup_profile_name if use_temp else backup_profile_name
|
||||
|
||||
|
||||
def get_profile_path(use_temp):
|
||||
path = '/etc/qubes/backup/' + get_profile_name(use_temp) + '.conf'
|
||||
return path
|
||||
|
||||
|
||||
def load_backup_profile(use_temp=False):
|
||||
|
||||
path = get_profile_path(use_temp)
|
||||
|
||||
with open(path) as profile_file:
|
||||
profile_data = yaml.safe_load(profile_file)
|
||||
return profile_data
|
||||
|
||||
|
||||
def write_backup_profile(args, use_temp=False):
|
||||
|
||||
acceptable_fields = ['include', 'passphrase_text', 'compression',
|
||||
'destination_vm', 'destination_path']
|
||||
|
||||
profile_data = {key: value for key, value in args.items()
|
||||
if key in acceptable_fields}
|
||||
|
||||
path = get_profile_path(use_temp)
|
||||
|
||||
with open(path, 'w') as profile_file:
|
||||
yaml.safe_dump(profile_data, profile_file)
|
||||
|
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- coding: utf8 -*-
|
||||
# pylint: skip-file
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
@ -15,9 +16,8 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import threading
|
||||
import time
|
||||
|
@ -12,20 +12,21 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
from . import utils
|
||||
from .firewall import *
|
||||
from .ui_bootfromdevice import *
|
||||
import qubesadmin.tools.qvm_start as qvm_start
|
||||
from . import ui_bootfromdevice # pylint: disable=no-name-in-module
|
||||
from PyQt4 import QtGui, QtCore # pylint: disable=import-error
|
||||
from qubesadmin import tools
|
||||
from qubesadmin.tools import qvm_start
|
||||
|
||||
|
||||
class VMBootFromDeviceWindow(Ui_BootDialog, QDialog):
|
||||
class VMBootFromDeviceWindow(ui_bootfromdevice.Ui_BootDialog, QtGui.QDialog):
|
||||
def __init__(self, vm, qapp, parent=None):
|
||||
super(VMBootFromDeviceWindow, self).__init__(parent)
|
||||
|
||||
@ -33,14 +34,19 @@ class VMBootFromDeviceWindow(Ui_BootDialog, QDialog):
|
||||
self.qapp = qapp
|
||||
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle(self.tr("Boot {vm} from device").format(vm=self.vm.name))
|
||||
self.setWindowTitle(
|
||||
self.tr("Boot {vm} from device").format(vm=self.vm.name))
|
||||
|
||||
self.connect(self.buttonBox, SIGNAL("accepted()"), self.save_and_apply)
|
||||
self.connect(self.buttonBox, SIGNAL("rejected()"), self.reject)
|
||||
self.connect(
|
||||
self.buttonBox,
|
||||
QtCore.SIGNAL("accepted()"),
|
||||
self.save_and_apply)
|
||||
self.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject)
|
||||
|
||||
# populate buttons and such
|
||||
self.__init_buttons__()
|
||||
|
||||
# warn user if the VM is currently running
|
||||
self.__warn_if_running__()
|
||||
|
||||
def reject(self):
|
||||
self.done(0)
|
||||
@ -49,15 +55,30 @@ class VMBootFromDeviceWindow(Ui_BootDialog, QDialog):
|
||||
if self.blockDeviceRadioButton.isChecked():
|
||||
cdrom_location = self.blockDeviceComboBox.currentText()
|
||||
elif self.fileRadioButton.isChecked():
|
||||
cdrom_location = str(self.vm_list[self.fileVM.currentIndex()]) + ":" + self.pathText.text()
|
||||
cdrom_location = str(
|
||||
self.vm_list[self.fileVM.currentIndex()]) + \
|
||||
":" + self.pathText.text()
|
||||
else:
|
||||
QMessageBox.warning(None,
|
||||
self.tr(
|
||||
"ERROR!"),
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("ERROR!"),
|
||||
self.tr("No file or block device selected; please select one."))
|
||||
return
|
||||
|
||||
# warn user if the VM is currently running
|
||||
self.__warn_if_running__()
|
||||
|
||||
qvm_start.main(['--cdrom', cdrom_location, self.vm.name])
|
||||
|
||||
def __warn_if_running__(self):
|
||||
if self.vm.is_running():
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Warning!"),
|
||||
self.tr("Qube must be turned off before booting it from"
|
||||
"device. Please turn off the qube.")
|
||||
)
|
||||
|
||||
def __init_buttons__(self):
|
||||
self.fileVM.setEnabled(False)
|
||||
self.selectFileButton.setEnabled(False)
|
||||
@ -86,7 +107,8 @@ class VMBootFromDeviceWindow(Ui_BootDialog, QDialog):
|
||||
)
|
||||
|
||||
def radio_button_clicked(self):
|
||||
self.blockDeviceComboBox.setEnabled(self.blockDeviceRadioButton.isChecked())
|
||||
self.blockDeviceComboBox.setEnabled(
|
||||
self.blockDeviceRadioButton.isChecked())
|
||||
self.fileVM.setEnabled(self.fileRadioButton.isChecked())
|
||||
self.selectFileButton.setEnabled(self.fileRadioButton.isChecked())
|
||||
self.pathText.setEnabled(self.fileRadioButton.isChecked())
|
||||
@ -103,18 +125,17 @@ class VMBootFromDeviceWindow(Ui_BootDialog, QDialog):
|
||||
self.pathText.setText(new_path)
|
||||
|
||||
|
||||
parser = qubesadmin.tools.QubesArgumentParser(vmname_nargs=1)
|
||||
parser = tools.QubesArgumentParser(vmname_nargs=1)
|
||||
|
||||
|
||||
def main(args=None):
|
||||
global bootfromdevice_window
|
||||
|
||||
args = parser.parse_args(args)
|
||||
vm = args.domains.pop()
|
||||
|
||||
qapp = QApplication(sys.argv)
|
||||
qapp = QtGui.QApplication(sys.argv)
|
||||
qapp.setOrganizationName('Invisible Things Lab')
|
||||
qapp.setOrganizationDomain("https://www.qubes-os.org/")
|
||||
qapp.setApplicationName("Qubes VM Settings")
|
||||
qapp.setApplicationName("Boot Qube From Device")
|
||||
|
||||
# if not utils.is_debug(): #FIXME
|
||||
# sys.excepthook = handle_exception
|
||||
|
@ -1,4 +1,5 @@
|
||||
#!/usr/bin/python2
|
||||
# pylint: skip-file
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
@ -16,9 +17,8 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
|
@ -16,33 +16,29 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4 import QtCore, QtGui # pylint: disable=import-error
|
||||
|
||||
import qubesadmin
|
||||
import qubesadmin.tools
|
||||
|
||||
import qubesmanager.resources_rc
|
||||
import qubesadmin.exc
|
||||
|
||||
from . import utils
|
||||
|
||||
from .ui_newappvmdlg import Ui_NewVMDlg
|
||||
from .ui_newappvmdlg import Ui_NewVMDlg # pylint: disable=import-error
|
||||
from .thread_monitor import ThreadMonitor
|
||||
|
||||
|
||||
class NewVmDlg(QDialog, Ui_NewVMDlg):
|
||||
class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
|
||||
def __init__(self, qtapp, app, parent=None):
|
||||
super(NewVmDlg, self).__init__(parent)
|
||||
self.setupUi(self)
|
||||
@ -51,7 +47,8 @@ class NewVmDlg(QDialog, Ui_NewVMDlg):
|
||||
self.app = app
|
||||
|
||||
# Theoretically we should be locking for writing here and unlock
|
||||
# only after the VM creation finished. But the code would be more messy...
|
||||
# only after the VM creation finished. But the code would be
|
||||
# more messy...
|
||||
# Instead we lock for writing in the actual worker thread
|
||||
self.label_list, self.label_idx = utils.prepare_label_choice(
|
||||
self.label,
|
||||
@ -73,13 +70,13 @@ class NewVmDlg(QDialog, Ui_NewVMDlg):
|
||||
(lambda vm: vm.provides_network),
|
||||
allow_internal=False, allow_default=True, allow_none=True)
|
||||
|
||||
self.name.setValidator(QRegExpValidator(
|
||||
QRegExp("[a-zA-Z0-9-]*", Qt.CaseInsensitive), None))
|
||||
self.name.setValidator(QtGui.QRegExpValidator(
|
||||
QtCore.QRegExp("[a-zA-Z0-9-]*", QtCore.Qt.CaseInsensitive), None))
|
||||
self.name.selectAll()
|
||||
self.name.setFocus()
|
||||
|
||||
if len(self.template_list) == 0:
|
||||
QMessageBox.warning(None,
|
||||
if not self.template_list:
|
||||
QtGui.QMessageBox.warning(None,
|
||||
self.tr('No template available!'),
|
||||
self.tr('Cannot create a qube when no template exists.'))
|
||||
|
||||
@ -99,7 +96,8 @@ class NewVmDlg(QDialog, Ui_NewVMDlg):
|
||||
self.done(0)
|
||||
|
||||
def accept(self):
|
||||
vmclass = ('AppVM' if self.vm_type.currentIndex() == 0 else 'StandaloneVM')
|
||||
vmclass = ('AppVM' if self.vm_type.currentIndex() == 0
|
||||
else 'StandaloneVM')
|
||||
|
||||
name = str(self.name.text())
|
||||
try:
|
||||
@ -107,7 +105,7 @@ class NewVmDlg(QDialog, Ui_NewVMDlg):
|
||||
except LookupError:
|
||||
pass
|
||||
else:
|
||||
QMessageBox.warning(None,
|
||||
QtGui.QMessageBox.warning(None,
|
||||
self.tr('Incorrect qube name!'),
|
||||
self.tr('A qube with the name <b>{}</b> already exists in the '
|
||||
'system!').format(name))
|
||||
@ -132,7 +130,7 @@ class NewVmDlg(QDialog, Ui_NewVMDlg):
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
progress = QProgressDialog(
|
||||
progress = QtGui.QProgressDialog(
|
||||
self.tr("Creating new qube <b>{}</b>...").format(name), "", 0, 0)
|
||||
progress.setCancelButton(None)
|
||||
progress.setModal(True)
|
||||
@ -145,7 +143,7 @@ class NewVmDlg(QDialog, Ui_NewVMDlg):
|
||||
progress.hide()
|
||||
|
||||
if not thread_monitor.success:
|
||||
QMessageBox.warning(None,
|
||||
QtGui.QMessageBox.warning(None,
|
||||
self.tr("Error creating the qube!"),
|
||||
self.tr("ERROR: {}").format(thread_monitor.error_msg))
|
||||
|
||||
@ -177,8 +175,10 @@ class NewVmDlg(QDialog, Ui_NewVMDlg):
|
||||
for k, v in properties.items():
|
||||
setattr(vm, k, v)
|
||||
|
||||
except Exception as ex:
|
||||
thread_monitor.set_error_msg(str(ex))
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
thread_monitor.set_error_msg(str(qex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
thread_monitor.set_error_msg(repr(ex))
|
||||
|
||||
thread_monitor.set_finished()
|
||||
|
||||
@ -218,7 +218,7 @@ parser = qubesadmin.tools.QubesArgumentParser()
|
||||
def main(args=None):
|
||||
args = parser.parse_args(args)
|
||||
|
||||
qtapp = QApplication(sys.argv)
|
||||
qtapp = QtGui.QApplication(sys.argv)
|
||||
qtapp.setOrganizationName('Invisible Things Lab')
|
||||
qtapp.setOrganizationDomain('https://www.qubes-os.org/')
|
||||
qtapp.setApplicationName('Create qube')
|
||||
|
@ -13,91 +13,90 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import datetime
|
||||
import ipaddress
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import xml.etree.ElementTree
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
|
||||
from PyQt4 import QtCore, QtGui # pylint: disable=import-error
|
||||
import qubesadmin.firewall
|
||||
|
||||
from . import ui_newfwruledlg
|
||||
from . import ui_newfwruledlg # pylint: disable=no-name-in-module
|
||||
|
||||
|
||||
class FirewallModifiedOutsideError(ValueError):
|
||||
pass
|
||||
|
||||
class QIPAddressValidator(QValidator):
|
||||
class QIPAddressValidator(QtGui.QValidator):
|
||||
# pylint: disable=too-few-public-methods
|
||||
def __init__(self, parent=None):
|
||||
super(QIPAddressValidator, self).__init__(parent)
|
||||
|
||||
def validate(self, input, pos):
|
||||
hostname = str(input)
|
||||
def validate(self, input_string, pos):
|
||||
# pylint: disable=too-many-return-statements,no-self-use
|
||||
hostname = str(input_string)
|
||||
|
||||
if len(hostname) > 255 or len(hostname) == 0:
|
||||
return (QValidator.Intermediate, input, pos)
|
||||
if len(hostname) > 255 or not hostname:
|
||||
return (QtGui.QValidator.Intermediate, input_string, pos)
|
||||
|
||||
if hostname == "*":
|
||||
return (QValidator.Acceptable, input, pos)
|
||||
return (QtGui.QValidator.Acceptable, input_string, pos)
|
||||
|
||||
unmask = hostname.split("/", 1)
|
||||
if len(unmask) == 2:
|
||||
hostname = unmask[0]
|
||||
mask = unmask[1]
|
||||
if mask.isdigit() or mask == "":
|
||||
if re.match("^([0-9]{1,3}\.){3}[0-9]{1,3}$", hostname) is None:
|
||||
return (QValidator.Invalid, input, pos)
|
||||
if re.match(r"^([0-9]{1,3}\.){3}[0-9]{1,3}$", hostname) is None:
|
||||
return (QtGui.QValidator.Invalid, input_string, pos)
|
||||
if mask != "":
|
||||
mask = int(unmask[1])
|
||||
if mask < 0 or mask > 32:
|
||||
return (QValidator.Invalid, input, pos)
|
||||
return (QtGui.QValidator.Invalid, input_string, pos)
|
||||
else:
|
||||
return (QValidator.Invalid, input, pos)
|
||||
return (QtGui.QValidator.Invalid, input_string, pos)
|
||||
|
||||
if hostname[-1:] == ".":
|
||||
hostname = hostname[:-1]
|
||||
|
||||
if hostname[-1:] == "-":
|
||||
return (QValidator.Intermediate, input, pos)
|
||||
return (QtGui.QValidator.Intermediate, input_string, pos)
|
||||
|
||||
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||
allowed = re.compile(r"(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||
if all(allowed.match(x) for x in hostname.split(".")):
|
||||
return (QValidator.Acceptable, input, pos)
|
||||
return (QtGui.QValidator.Acceptable, input_string, pos)
|
||||
|
||||
return (QValidator.Invalid, input, pos)
|
||||
return (QtGui.QValidator.Invalid, input_string, pos)
|
||||
|
||||
class NewFwRuleDlg (QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
|
||||
class NewFwRuleDlg(QtGui.QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
|
||||
def __init__(self, parent=None):
|
||||
super(NewFwRuleDlg, self).__init__(parent)
|
||||
self.setupUi(self)
|
||||
|
||||
self.set_ok_enabled(False)
|
||||
self.set_ok_state(False)
|
||||
self.addressComboBox.setValidator(QIPAddressValidator())
|
||||
self.addressComboBox.editTextChanged.connect(self.address_editing_finished)
|
||||
self.serviceComboBox.setValidator(QRegExpValidator(QRegExp("[a-z][a-z0-9-]+|[0-9]+(-[0-9]+)?", Qt.CaseInsensitive), None))
|
||||
self.addressComboBox.editTextChanged.connect(
|
||||
self.address_editing_finished)
|
||||
self.serviceComboBox.setValidator(QtGui.QRegExpValidator(
|
||||
QtCore.QRegExp("[a-z][a-z0-9-]+|[0-9]+(-[0-9]+)?",
|
||||
QtCore.Qt.CaseInsensitive), None))
|
||||
self.serviceComboBox.setEnabled(False)
|
||||
self.serviceComboBox.setInsertPolicy(QComboBox.InsertAtBottom)
|
||||
self.serviceComboBox.setInsertPolicy(QtGui.QComboBox.InsertAtBottom)
|
||||
self.populate_combos()
|
||||
self.serviceComboBox.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
self.serviceComboBox.setInsertPolicy(QtGui.QComboBox.InsertAtTop)
|
||||
|
||||
def accept(self):
|
||||
if self.tcp_radio.isChecked() or self.udp_radio.isChecked():
|
||||
if len(self.serviceComboBox.currentText()) == 0:
|
||||
msg = QMessageBox()
|
||||
if not self.serviceComboBox.currentText():
|
||||
msg = QtGui.QMessageBox()
|
||||
msg.warning(self, self.tr("Firewall rule"),
|
||||
self.tr("You need to fill service name/port for TCP/UDP rule"))
|
||||
self.tr("You need to fill service "
|
||||
"name/port for TCP/UDP rule"))
|
||||
return
|
||||
QDialog.accept(self)
|
||||
QtGui.QDialog.accept(self)
|
||||
|
||||
def populate_combos(self):
|
||||
example_addresses = [
|
||||
@ -120,12 +119,12 @@ class NewFwRuleDlg (QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
|
||||
self.serviceComboBox.addItem(service)
|
||||
|
||||
def address_editing_finished(self):
|
||||
self.set_ok_enabled(True)
|
||||
self.set_ok_state(True)
|
||||
|
||||
def set_ok_enabled(self, on):
|
||||
ok_button = self.buttonBox.button(QDialogButtonBox.Ok)
|
||||
def set_ok_state(self, ok_state):
|
||||
ok_button = self.buttonBox.button(QtGui.QDialogButtonBox.Ok)
|
||||
if ok_button is not None:
|
||||
ok_button.setEnabled(on)
|
||||
ok_button.setEnabled(ok_state)
|
||||
|
||||
def on_tcp_radio_toggled(self, checked):
|
||||
if checked:
|
||||
@ -139,55 +138,43 @@ class NewFwRuleDlg (QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
|
||||
if checked:
|
||||
self.serviceComboBox.setEnabled(False)
|
||||
|
||||
class QubesFirewallRulesModel(QAbstractItemModel):
|
||||
class QubesFirewallRulesModel(QtCore.QAbstractItemModel):
|
||||
def __init__(self, parent=None):
|
||||
QAbstractItemModel.__init__(self, parent)
|
||||
QtCore.QAbstractItemModel.__init__(self, parent)
|
||||
|
||||
self.__columnValues = {
|
||||
0: lambda x: "*" if self.children[x]["address"] == "0.0.0.0" and
|
||||
self.children[x]["netmask"] == 0 else
|
||||
self.children[x]["address"] + ("" if self.children[x][ "netmask"] == 32 else
|
||||
" /{0}".format(self.children[x][
|
||||
"netmask"])),
|
||||
1: lambda x: "any" if self.children[x]["portBegin"] == 0 else
|
||||
"{0}-{1}".format(self.children[x]["portBegin"], self.children[x][
|
||||
"portEnd"]) if self.children[x]["portEnd"] is not None else \
|
||||
self.get_service_name(self.children[x]["portBegin"]),
|
||||
2: lambda x: self.children[x]["proto"], }
|
||||
self.__columnNames = {0: "Address", 1: "Service", 2: "Protocol", }
|
||||
self.__column_names = {0: "Address", 1: "Service", 2: "Protocol", }
|
||||
self.__services = list()
|
||||
pattern = re.compile("(?P<name>[a-z][a-z0-9-]+)\s+(?P<port>[0-9]+)/(?P<protocol>[a-z]+)", re.IGNORECASE)
|
||||
f = open('/etc/services', 'r')
|
||||
for line in f:
|
||||
pattern = re.compile(
|
||||
r"(?P<name>[a-z][a-z0-9-]+)\s+(?P<port>[0-9]+)/"
|
||||
r"(?P<protocol>[a-z]+)",
|
||||
re.IGNORECASE)
|
||||
with open('/etc/services', 'r') as file:
|
||||
for line in file:
|
||||
match = pattern.match(line)
|
||||
if match is not None:
|
||||
service = match.groupdict()
|
||||
self.__services.append( (service["name"], int(service["port"]),) )
|
||||
f.close()
|
||||
self.__services.append(
|
||||
(service["name"], int(service["port"]),))
|
||||
|
||||
self.fw_changed = False
|
||||
self.allow = None # is the default policy allow or deny
|
||||
self.temp_full_access_expire_time = None # temporary full access time
|
||||
self.__vm = None # VM that the model applies to
|
||||
self.__children = None # list of rules in the FW
|
||||
|
||||
def sort(self, idx, order):
|
||||
from operator import attrgetter
|
||||
|
||||
rev = (order == Qt.AscendingOrder)
|
||||
if idx==0:
|
||||
self.children.sort(key=lambda x: x['address'], reverse = rev)
|
||||
if idx==1:
|
||||
self.children.sort(key=lambda x: self.get_service_name(x[
|
||||
"portBegin"]) if x["portEnd"] == None else x["portBegin"],
|
||||
rev = (order == QtCore.Qt.AscendingOrder)
|
||||
self.children.sort(key=lambda x: self.get_column_string(idx, x),
|
||||
reverse=rev)
|
||||
if idx==2:
|
||||
self.children.sort(key=lambda x: x['proto'], reverse
|
||||
= rev)
|
||||
|
||||
index1 = self.createIndex(0, 0)
|
||||
index2 = self.createIndex(len(self)-1, len(self.__columnValues)-1)
|
||||
index2 = self.createIndex(len(self) - 1, len(self.__column_names) - 1)
|
||||
self.dataChanged.emit(index1, index2)
|
||||
|
||||
|
||||
def get_service_name(self, port):
|
||||
for service in self.__services:
|
||||
if service[1] == port:
|
||||
if str(service[1]) == str(port):
|
||||
return service[0]
|
||||
return str(port)
|
||||
|
||||
@ -197,256 +184,292 @@ class QubesFirewallRulesModel(QAbstractItemModel):
|
||||
return service[1]
|
||||
return None
|
||||
|
||||
def get_column_string(self, col, row):
|
||||
return self.__columnValues[col](row)
|
||||
|
||||
|
||||
def rule_to_dict(self, rule):
|
||||
def get_column_string(self, col, rule):
|
||||
# pylint: disable=too-many-return-statements
|
||||
# Address
|
||||
if col == 0:
|
||||
if rule.dsthost is None:
|
||||
raise FirewallModifiedOutsideError('no dsthost')
|
||||
return "*"
|
||||
if rule.dsthost.type == 'dst4' and rule.dsthost.prefixlen == '32':
|
||||
return str(rule.dsthost)[:-3]
|
||||
if rule.dsthost.type == 'dst6' and rule.dsthost.prefixlen == '128':
|
||||
return str(rule.dsthost)[:-4]
|
||||
return str(rule.dsthost)
|
||||
|
||||
d = {}
|
||||
|
||||
if not rule.proto:
|
||||
d['proto'] = 'any'
|
||||
d['portBegin'] = 'any'
|
||||
d['portEnd'] = None
|
||||
|
||||
else:
|
||||
d['proto'] = rule.proto
|
||||
# Service
|
||||
if col == 1:
|
||||
if rule.dstports is None:
|
||||
raise FirewallModifiedOutsideError('no dstport')
|
||||
d['portBegin'] = rule.dstports.range[0]
|
||||
d['portEnd'] = rule.dstports.range[1] \
|
||||
if rule.dstports.range[0] != rule.dstports.range[1] \
|
||||
else None
|
||||
return "any"
|
||||
if rule.dstports.range[0] != rule.dstports.range[1]:
|
||||
return str(rule.dstports)
|
||||
return self.get_service_name(rule.dstports)
|
||||
|
||||
if rule.dsthost.type == 'dsthost':
|
||||
d['address'] = str(rule.dsthost)
|
||||
d['netmask'] = 32
|
||||
elif rule.dsthost.type == 'dst4':
|
||||
network = ipaddress.IPv4Network(rule.dsthost)
|
||||
d['address'] = str(network.network_address)
|
||||
d['netmask'] = int(network.prefixlen)
|
||||
else:
|
||||
raise FirewallModifiedOutsideError(
|
||||
'cannot map dsthost.type={!s}'.format(rule.dsthost))
|
||||
# Protocol
|
||||
if col == 2:
|
||||
if rule.proto is None:
|
||||
return "any"
|
||||
return str(rule.proto)
|
||||
return "unknown"
|
||||
|
||||
if rule.expire is not None:
|
||||
d['expire'] = int(rule.expire)
|
||||
|
||||
return d
|
||||
|
||||
def get_firewall_conf(self, vm):
|
||||
@staticmethod
|
||||
def get_firewall_conf(vm):
|
||||
conf = {
|
||||
'allow': None,
|
||||
'allowDns': False,
|
||||
'allowIcmp': False,
|
||||
'allowYumProxy': False,
|
||||
'expire': 0,
|
||||
'rules': [],
|
||||
}
|
||||
|
||||
allow_dns = False
|
||||
allow_icmp = False
|
||||
common_action = None
|
||||
tentative_action = None
|
||||
|
||||
reversed_rules = list(reversed(vm.firewall.rules))
|
||||
last_rule = reversed_rules.pop(0)
|
||||
|
||||
if last_rule == qubesadmin.firewall.Rule('action=accept') \
|
||||
or last_rule == qubesadmin.firewall.Rule('action=drop'):
|
||||
common_action = last_rule.action
|
||||
else:
|
||||
FirewallModifiedOutsideError('Last rule must be either '
|
||||
'drop all or accept all.')
|
||||
|
||||
dns_rule = qubesadmin.firewall.Rule(None,
|
||||
action='accept', specialtarget='dns')
|
||||
icmp_rule = qubesadmin.firewall.Rule(None,
|
||||
action='accept', proto='icmp')
|
||||
while reversed_rules:
|
||||
rule = reversed_rules[0]
|
||||
if rule.dsthost is not None or rule.proto is not None:
|
||||
break
|
||||
tentative_action = reversed_rules.pop(0).action
|
||||
rule = reversed_rules.pop(0)
|
||||
|
||||
if not reversed_rules:
|
||||
conf['allow'] = tentative_action == 'accept'
|
||||
return conf
|
||||
|
||||
for rule in reversed_rules:
|
||||
if rule.specialtarget == 'dns':
|
||||
conf['allowDns'] = (rule.action == 'accept')
|
||||
if rule == dns_rule:
|
||||
allow_dns = True
|
||||
continue
|
||||
|
||||
if rule.proto == 'icmp':
|
||||
if rule.icmptype is not None:
|
||||
raise FirewallModifiedOutsideError(
|
||||
'cannot map icmptype != None')
|
||||
conf['allowIcmp'] = (rule.action == 'accept')
|
||||
if rule == icmp_rule:
|
||||
allow_icmp = True
|
||||
continue
|
||||
|
||||
if common_action is None:
|
||||
common_action = rule.action
|
||||
elif common_action != rule.action:
|
||||
raise FirewallModifiedOutsideError('incoherent action')
|
||||
if rule.specialtarget is not None or rule.icmptype is not None:
|
||||
raise FirewallModifiedOutsideError("Rule type unknown!")
|
||||
|
||||
conf['rules'].insert(0, self.rule_to_dict(rule))
|
||||
if (rule.dsthost is not None or rule.proto is not None) \
|
||||
and rule.expire is None:
|
||||
if rule.action == 'accept':
|
||||
conf['rules'].insert(0, rule)
|
||||
continue
|
||||
else:
|
||||
raise FirewallModifiedOutsideError('No blacklist support.')
|
||||
|
||||
if rule.expire is not None and rule.dsthost is None \
|
||||
and rule.proto is None:
|
||||
conf['expire'] = int(str(rule.expire))
|
||||
continue
|
||||
|
||||
raise FirewallModifiedOutsideError('it does not add up.')
|
||||
|
||||
conf['allow'] = (common_action == 'accept')
|
||||
|
||||
if not allow_icmp and not conf['allow']:
|
||||
raise FirewallModifiedOutsideError('ICMP must be allowed.')
|
||||
|
||||
if not allow_dns and not conf['allow']:
|
||||
raise FirewallModifiedOutsideError('DNS must be allowed')
|
||||
|
||||
if common_action is None or common_action != tentative_action:
|
||||
# we've got only specialtarget and/or icmp
|
||||
conf['allow'] = tentative_action == 'accept'
|
||||
return conf
|
||||
|
||||
raise FirewallModifiedOutsideError('it does not add up')
|
||||
|
||||
def write_firewall_conf(self, vm, conf):
|
||||
common_action = qubesadmin.firewall.Action(
|
||||
'drop' if conf['allow'] else 'accept')
|
||||
|
||||
@staticmethod
|
||||
def write_firewall_conf(vm, conf):
|
||||
rules = []
|
||||
|
||||
for rule in conf['rules']:
|
||||
kwargs = {}
|
||||
if rule['proto'] != 'any':
|
||||
kwargs['proto'] = rule['proto']
|
||||
if rule['portBegin'] != 'any':
|
||||
kwargs['dstports'] = '-'.join(map(str, filter((lambda x: x),
|
||||
(rule['portBegin'], rule['portEnd']))))
|
||||
rules.append(rule)
|
||||
|
||||
netmask = str(rule['netmask']) if rule['netmask'] != 32 else None
|
||||
|
||||
rules.append(qubesadmin.firewall.Rule(None,
|
||||
action=common_action,
|
||||
dsthost='/'.join(map(str, filter((lambda x: x),
|
||||
(rule['address'], netmask)))),
|
||||
**kwargs))
|
||||
|
||||
if conf['allowDns']:
|
||||
if not conf['allow']:
|
||||
rules.append(qubesadmin.firewall.Rule(None,
|
||||
action='accept', specialtarget='dns'))
|
||||
|
||||
if conf['allowIcmp']:
|
||||
if not conf['allow']:
|
||||
rules.append(qubesadmin.firewall.Rule(None,
|
||||
action='accept', proto='icmp'))
|
||||
|
||||
if common_action == 'drop':
|
||||
if conf['allow']:
|
||||
rules.append(qubesadmin.firewall.Rule(None,
|
||||
action='accept'))
|
||||
else:
|
||||
rules.append(qubesadmin.firewall.Rule(None,
|
||||
action='drop'))
|
||||
|
||||
vm.firewall.rules = rules
|
||||
|
||||
def set_vm(self, vm):
|
||||
self.__vm = vm
|
||||
|
||||
self.clearChildren()
|
||||
self.clear_children()
|
||||
|
||||
conf = self.get_firewall_conf(vm)
|
||||
|
||||
self.allow = conf["allow"]
|
||||
self.allowDns = conf["allowDns"]
|
||||
self.allowIcmp = conf["allowIcmp"]
|
||||
self.allowYumProxy = conf["allowYumProxy"]
|
||||
self.tempFullAccessExpireTime = 0
|
||||
|
||||
self.temp_full_access_expire_time = conf['expire']
|
||||
|
||||
for rule in conf["rules"]:
|
||||
self.appendChild(rule)
|
||||
if "expire" in rule and rule["address"] == "0.0.0.0":
|
||||
self.tempFullAccessExpireTime = rule["expire"]
|
||||
self.append_child(rule)
|
||||
|
||||
def get_vm_name(self):
|
||||
return self.__vm.name
|
||||
|
||||
def apply_rules(self, allow, dns, icmp, yumproxy, tempFullAccess=False,
|
||||
tempFullAccessTime=None):
|
||||
def apply_rules(self, allow, temp_full_access=False,
|
||||
temp_full_access_time=None):
|
||||
assert self.__vm is not None
|
||||
|
||||
if self.allow != allow or self.allowDns != dns or \
|
||||
self.allowIcmp != icmp or self.allowYumProxy != yumproxy or \
|
||||
(self.tempFullAccessExpireTime != 0) != tempFullAccess:
|
||||
if self.allow != allow or \
|
||||
(self.temp_full_access_expire_time != 0) != temp_full_access:
|
||||
self.fw_changed = True
|
||||
|
||||
conf = {"allow": allow,
|
||||
"allowDns": dns,
|
||||
"allowIcmp": icmp,
|
||||
"allowYumProxy": yumproxy,
|
||||
"rules": list()
|
||||
}
|
||||
|
||||
for rule in self.children:
|
||||
if "expire" in rule and rule["address"] == "0.0.0.0" and \
|
||||
rule["netmask"] == 0 and rule["proto"] == "any":
|
||||
# rule already present, update its time
|
||||
if tempFullAccess:
|
||||
rule["expire"] = \
|
||||
int(datetime.datetime.now().strftime("%s")) + \
|
||||
tempFullAccessTime*60
|
||||
tempFullAccess = False
|
||||
conf["rules"].append(rule)
|
||||
conf['rules'].extend(self.children)
|
||||
|
||||
if tempFullAccess and not allow:
|
||||
conf["rules"].append({"address": "0.0.0.0",
|
||||
"netmask": 0,
|
||||
"proto": "any",
|
||||
"expire": int(
|
||||
datetime.datetime.now().strftime("%s"))+\
|
||||
tempFullAccessTime*60
|
||||
})
|
||||
if temp_full_access and not allow:
|
||||
conf["rules"].append(qubesadmin.firewall.Rule(
|
||||
None,
|
||||
action='accept',
|
||||
expire=int(datetime.datetime.now().strftime("%s")) +
|
||||
temp_full_access_time * 60))
|
||||
|
||||
if self.fw_changed:
|
||||
self.write_firewall_conf(self.__vm, conf)
|
||||
|
||||
def index(self, row, column, parent=QModelIndex()):
|
||||
def populate_edit_dialog(self, dialog, row):
|
||||
address = self.get_column_string(0, self.children[row])
|
||||
dialog.addressComboBox.setItemText(0, address)
|
||||
dialog.addressComboBox.setCurrentIndex(0)
|
||||
service = self.get_column_string(1, self.children[row])
|
||||
if service == "any":
|
||||
service = ""
|
||||
dialog.serviceComboBox.setItemText(0, service)
|
||||
dialog.serviceComboBox.setCurrentIndex(0)
|
||||
protocol = self.get_column_string(2, self.children[row])
|
||||
if protocol == "tcp":
|
||||
dialog.tcp_radio.setChecked(True)
|
||||
elif protocol == "udp":
|
||||
dialog.udp_radio.setChecked(True)
|
||||
else:
|
||||
dialog.any_radio.setChecked(True)
|
||||
|
||||
def run_rule_dialog(self, dialog, row=None):
|
||||
if dialog.exec_():
|
||||
|
||||
address = str(dialog.addressComboBox.currentText())
|
||||
service = str(dialog.serviceComboBox.currentText())
|
||||
|
||||
rule = qubesadmin.firewall.Rule(None, action='accept')
|
||||
|
||||
if address is not None and address != "*":
|
||||
try:
|
||||
rule.dsthost = address
|
||||
except ValueError:
|
||||
QtGui.QMessageBox.warning(None, self.tr("Invalid address"),
|
||||
self.tr("Address '{0}' is invalid.").format(address))
|
||||
|
||||
if dialog.tcp_radio.isChecked():
|
||||
rule.proto = 'tcp'
|
||||
elif dialog.udp_radio.isChecked():
|
||||
rule.proto = 'udp'
|
||||
|
||||
if '-' in service:
|
||||
try:
|
||||
rule.dstports = service
|
||||
except ValueError:
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Invalid port or service"),
|
||||
self.tr("Port number or service '{0}' is invalid.")
|
||||
.format(service))
|
||||
elif service:
|
||||
try:
|
||||
rule.dstports = service
|
||||
except (TypeError, ValueError):
|
||||
if self.get_service_port(service) is not None:
|
||||
rule.dstports = self.get_service_port(service)
|
||||
else:
|
||||
QtGui.QMessageBox.warning(None,
|
||||
self.tr("Invalid port or service"),
|
||||
self.tr("Port number or service '{0}' is invalid.")
|
||||
.format(service))
|
||||
|
||||
if row is not None:
|
||||
self.set_child(row, rule)
|
||||
else:
|
||||
self.append_child(rule)
|
||||
|
||||
def index(self, row, column, parent=QtCore.QModelIndex()):
|
||||
if not self.hasIndex(row, column, parent):
|
||||
return QModelIndex()
|
||||
return QtCore.QModelIndex()
|
||||
|
||||
return self.createIndex(row, column, self.children[row])
|
||||
|
||||
def parent(self, child):
|
||||
return QModelIndex()
|
||||
def parent(self, child): # pylint: disable=unused-argument,no-self-use
|
||||
return QtCore.QModelIndex()
|
||||
|
||||
def rowCount(self, parent=QModelIndex()):
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
return len(self)
|
||||
|
||||
def columnCount(self, parent=QModelIndex()):
|
||||
return len(self.__columnValues)
|
||||
# pylint: disable=invalid-name,unused-argument
|
||||
def columnCount(self, parent=QtCore.QModelIndex()):
|
||||
return len(self.__column_names)
|
||||
|
||||
def hasChildren(self, index=QModelIndex()):
|
||||
parentItem = index.internalPointer()
|
||||
if parentItem is not None:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
# pylint: disable=invalid-name,no-self-use
|
||||
def hasChildren(self, index=QtCore.QModelIndex()):
|
||||
parent_item = index.internalPointer()
|
||||
return parent_item is None
|
||||
|
||||
def data(self, index, role=Qt.DisplayRole):
|
||||
if index.isValid() and role == Qt.DisplayRole:
|
||||
return self.__columnValues[index.column()](index.row())
|
||||
def data(self, index, role=QtCore.Qt.DisplayRole):
|
||||
if index.isValid() and role == QtCore.Qt.DisplayRole:
|
||||
return self.get_column_string(index.column(),
|
||||
self.children[index.row()])
|
||||
|
||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
||||
if section < len(self.__columnNames) \
|
||||
and orientation == Qt.Horizontal and role == Qt.DisplayRole:
|
||||
return self.__columnNames[section]
|
||||
# pylint: disable=invalid-name
|
||||
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
|
||||
if section < len(self.__column_names) \
|
||||
and orientation == QtCore.Qt.Horizontal \
|
||||
and role == QtCore.Qt.DisplayRole:
|
||||
return self.__column_names[section]
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
return self.__children
|
||||
|
||||
def appendChild(self, child):
|
||||
def append_child(self, child):
|
||||
row = len(self)
|
||||
self.beginInsertRows(QModelIndex(), row, row)
|
||||
self.beginInsertRows(QtCore.QModelIndex(), row, row)
|
||||
self.children.append(child)
|
||||
self.endInsertRows()
|
||||
index = self.createIndex(row, 0, child)
|
||||
self.dataChanged.emit(index, index)
|
||||
self.fw_changed = True
|
||||
|
||||
def removeChild(self, i):
|
||||
def remove_child(self, i):
|
||||
if i >= len(self):
|
||||
return
|
||||
|
||||
self.beginRemoveRows(QModelIndex(), i, i)
|
||||
self.beginRemoveRows(QtCore.QModelIndex(), i, i)
|
||||
del self.children[i]
|
||||
self.endRemoveRows()
|
||||
index = self.createIndex(i, 0)
|
||||
self.dataChanged.emit(index, index)
|
||||
self.fw_changed = True
|
||||
|
||||
def setChild(self, i, child):
|
||||
def set_child(self, i, child):
|
||||
self.children[i] = child
|
||||
index = self.createIndex(i, 0, child)
|
||||
self.dataChanged.emit(index, index)
|
||||
self.fw_changed = True
|
||||
|
||||
def clearChildren(self):
|
||||
def clear_children(self):
|
||||
self.__children = list()
|
||||
|
||||
def __len__(self):
|
||||
return len(self.children)
|
||||
|
||||
|
@ -15,28 +15,29 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
import os.path
|
||||
import traceback
|
||||
from PyQt4 import QtCore, QtGui # pylint: disable=import-error
|
||||
|
||||
from qubesadmin import Qubes
|
||||
from qubesadmin.utils import parse_size, updates_vms_status
|
||||
|
||||
from .ui_globalsettingsdlg import *
|
||||
from . import ui_globalsettingsdlg # pylint: disable=no-name-in-module
|
||||
|
||||
from configparser import ConfigParser
|
||||
from qubesadmin.utils import parse_size, updates_vms_status
|
||||
|
||||
qmemman_config_path = '/etc/qubes/qmemman.conf'
|
||||
|
||||
|
||||
class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
class GlobalSettingsWindow(ui_globalsettingsdlg.Ui_GlobalSettings,
|
||||
QtGui.QDialog):
|
||||
|
||||
def __init__(self, app, qvm_collection, parent=None):
|
||||
super(GlobalSettingsWindow, self).__init__(parent)
|
||||
@ -46,8 +47,11 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
|
||||
self.setupUi(self)
|
||||
|
||||
self.connect(self.buttonBox, SIGNAL("accepted()"), self.save_and_apply)
|
||||
self.connect(self.buttonBox, SIGNAL("rejected()"), self.reject)
|
||||
self.connect(
|
||||
self.buttonBox,
|
||||
QtCore.SIGNAL("accepted()"),
|
||||
self.save_and_apply)
|
||||
self.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject)
|
||||
|
||||
self.__init_system_defaults__()
|
||||
self.__init_kernel_defaults__()
|
||||
@ -89,7 +93,8 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
self.clock_vm_combo.setCurrentIndex(self.clockvm_idx)
|
||||
|
||||
# default netvm
|
||||
netvms = [vm for vm in all_vms if getattr(vm, 'provides_network', False)]
|
||||
netvms = [vm for vm in all_vms
|
||||
if getattr(vm, 'provides_network', False)]
|
||||
self.netvm_idx = -1
|
||||
|
||||
current_netvm = self.qvm_collection.default_netvm
|
||||
@ -152,7 +157,8 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
|
||||
def __init_kernel_defaults__(self):
|
||||
kernel_list = []
|
||||
# TODO system_path["qubes_kernels_base_dir"] idea: qubes.pulls['linux-kernel'].volumes
|
||||
# TODO system_path["qubes_kernels_base_dir"]
|
||||
# idea: qubes.pools['linux-kernel'].volumes
|
||||
for k in os.listdir('/var/lib/qubes/vm-kernels'):
|
||||
kernel_list.append(k)
|
||||
|
||||
@ -175,7 +181,6 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
|
||||
|
||||
def __init_mem_defaults__(self):
|
||||
|
||||
#qmemman settings
|
||||
self.qmemman_config = ConfigParser()
|
||||
self.vm_min_mem_val = '200MiB' #str(qmemman_algo.MIN_PREFMEM)
|
||||
@ -183,8 +188,10 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
|
||||
self.qmemman_config.read(qmemman_config_path)
|
||||
if self.qmemman_config.has_section('global'):
|
||||
self.vm_min_mem_val = self.qmemman_config.get('global', 'vm-min-mem')
|
||||
self.dom0_mem_boost_val = self.qmemman_config.get('global', 'dom0-mem-boost')
|
||||
self.vm_min_mem_val = \
|
||||
self.qmemman_config.get('global', 'vm-min-mem')
|
||||
self.dom0_mem_boost_val = \
|
||||
self.qmemman_config.get('global', 'dom0-mem-boost')
|
||||
|
||||
self.vm_min_mem_val = parse_size(self.vm_min_mem_val)
|
||||
self.dom0_mem_boost_val = parse_size(self.dom0_mem_boost_val)
|
||||
@ -199,7 +206,8 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
current_min_vm_mem = self.min_vm_mem.value()
|
||||
current_dom0_mem_boost = self.dom0_mem_boost.value()
|
||||
|
||||
if current_min_vm_mem*1024*1024 != self.vm_min_mem_val or current_dom0_mem_boost*1024*1024 != self.dom0_mem_boost_val:
|
||||
if current_min_vm_mem*1024*1024 != self.vm_min_mem_val \
|
||||
or current_dom0_mem_boost*1024*1024 != self.dom0_mem_boost_val:
|
||||
|
||||
current_min_vm_mem = str(current_min_vm_mem)+'M'
|
||||
current_dom0_mem_boost = str(current_dom0_mem_boost)+'M'
|
||||
@ -207,38 +215,46 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
if not self.qmemman_config.has_section('global'):
|
||||
#add the whole section
|
||||
self.qmemman_config.add_section('global')
|
||||
self.qmemman_config.set('global', 'vm-min-mem', current_min_vm_mem)
|
||||
self.qmemman_config.set('global', 'dom0-mem-boost', current_dom0_mem_boost)
|
||||
self.qmemman_config.set('global', 'cache-margin-factor', str(1.3)) # removed qmemman_algo.CACHE_FACTOR
|
||||
self.qmemman_config.set(
|
||||
'global', 'vm-min-mem', current_min_vm_mem)
|
||||
self.qmemman_config.set(
|
||||
'global', 'dom0-mem-boost', current_dom0_mem_boost)
|
||||
self.qmemman_config.set(
|
||||
'global', 'cache-margin-factor', str(1.3))
|
||||
# removed qmemman_algo.CACHE_FACTOR
|
||||
|
||||
qmemman_config_file = open(qmemman_config_path, 'a')
|
||||
self.qmemman_config.write(qmemman_config_file)
|
||||
qmemman_config_file.close()
|
||||
|
||||
else:
|
||||
#If there already is a 'global' section, we don't use SafeConfigParser.write() - it would get rid of all the comments...
|
||||
#If there already is a 'global' section, we don't use
|
||||
# SafeConfigParser.write() - it would get rid of
|
||||
# all the comments...
|
||||
|
||||
lines_to_add = {}
|
||||
lines_to_add['vm-min-mem'] = "vm-min-mem = " + current_min_vm_mem + "\n"
|
||||
lines_to_add['dom0-mem-boost'] = "dom0-mem-boost = " + current_dom0_mem_boost +"\n"
|
||||
lines_to_add['vm-min-mem'] = \
|
||||
"vm-min-mem = " + current_min_vm_mem + "\n"
|
||||
lines_to_add['dom0-mem-boost'] = \
|
||||
"dom0-mem-boost = " + current_dom0_mem_boost +"\n"
|
||||
|
||||
config_lines = []
|
||||
|
||||
qmemman_config_file = open(qmemman_config_path, 'r')
|
||||
for l in qmemman_config_file:
|
||||
if l.strip().startswith('vm-min-mem'):
|
||||
for line in qmemman_config_file:
|
||||
if line.strip().startswith('vm-min-mem'):
|
||||
config_lines.append(lines_to_add['vm-min-mem'])
|
||||
del lines_to_add['vm-min-mem']
|
||||
elif l.strip().startswith('dom0-mem-boost'):
|
||||
elif line.strip().startswith('dom0-mem-boost'):
|
||||
config_lines.append(lines_to_add['dom0-mem-boost'])
|
||||
del lines_to_add['dom0-mem-boost']
|
||||
else:
|
||||
config_lines.append(l)
|
||||
config_lines.append(line)
|
||||
|
||||
qmemman_config_file.close()
|
||||
|
||||
for l in lines_to_add:
|
||||
config_lines.append(l)
|
||||
for line in lines_to_add:
|
||||
config_lines.append(line)
|
||||
|
||||
qmemman_config_file = open(qmemman_config_path, 'w')
|
||||
qmemman_config_file.writelines(config_lines)
|
||||
@ -252,17 +268,19 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
self.updates_dom0.setChecked(self.updates_dom0_val)
|
||||
updates_vms = updates_vms_status(self.qvm_collection)
|
||||
if updates_vms is None:
|
||||
self.updates_vm.setCheckState(Qt.PartiallyChecked)
|
||||
self.updates_vm.setCheckState(QtCore.Qt.PartiallyChecked)
|
||||
else:
|
||||
self.updates_vm.setCheckState(updates_vms)
|
||||
|
||||
def __apply_updates__(self):
|
||||
if self.updates_dom0.isChecked() != self.updates_dom0_val:
|
||||
# TODO updates_dom0_toggle(self.qvm_collection, self.updates_dom0.isChecked())
|
||||
# TODO updates_dom0_toggle(
|
||||
# self.qvm_collection, self.updates_dom0.isChecked())
|
||||
raise NotImplementedError('Toggle dom0 updates not implemented')
|
||||
if self.updates_vm.checkState() != Qt.PartiallyChecked:
|
||||
if self.updates_vm.checkState() != QtCore.Qt.PartiallyChecked:
|
||||
for vm in self.qvm_collection.domains:
|
||||
vm.features['check-updates'] = bool(self.updates_vm.checkState())
|
||||
vm.features['check-updates'] = \
|
||||
bool(self.updates_vm.checkState())
|
||||
|
||||
def reject(self):
|
||||
self.done(0)
|
||||
@ -280,25 +298,21 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
# Copyright (c) 2002-2007 Pascal Varet <p.varet@gmail.com>
|
||||
|
||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
import os.path
|
||||
import traceback
|
||||
|
||||
filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop()
|
||||
filename = os.path.basename(filename)
|
||||
error = "%s: %s" % (exc_type.__name__, exc_value)
|
||||
|
||||
QMessageBox.critical(None, "Houston, we have a problem...",
|
||||
QtGui.QMessageBox.critical(
|
||||
None,
|
||||
"Houston, we have a problem...",
|
||||
"Whoops. A critical error has occured. This is most likely a bug "
|
||||
"in Qubes Global Settings application.<br><br>"
|
||||
"<b><i>%s</i></b>" % error +
|
||||
"at <b>line %d</b> of file <b>%s</b>.<br/><br/>"
|
||||
"in Qubes Global Settings application.<br><br><b><i>%s</i></b>" %
|
||||
error + "at <b>line %d</b> of file <b>%s</b>.<br/><br/>"
|
||||
% (line, filename))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
global qtapp
|
||||
qtapp = QApplication(sys.argv)
|
||||
qtapp = QtGui.QApplication(sys.argv)
|
||||
qtapp.setOrganizationName("The Qubes Project")
|
||||
qtapp.setOrganizationDomain("http://qubes-os.org")
|
||||
qtapp.setApplicationName("Qubes Global Settings")
|
||||
@ -307,7 +321,6 @@ def main():
|
||||
|
||||
app = Qubes()
|
||||
|
||||
global global_window
|
||||
global_window = GlobalSettingsWindow(qtapp, app)
|
||||
|
||||
global_window.show()
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# coding=utf-8
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
@ -16,22 +16,23 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
from PyQt4.QtCore import SIGNAL
|
||||
from PyQt4.QtGui import QDialog, QIcon
|
||||
from PyQt4.QtGui import QDialog # pylint: disable=import-error
|
||||
|
||||
from .ui_informationnotes import *
|
||||
from . import ui_informationnotes # pylint: disable=no-name-in-module
|
||||
import subprocess
|
||||
|
||||
|
||||
class InformationNotesDialog(Ui_InformationNotesDialog, QDialog):
|
||||
class InformationNotesDialog(ui_informationnotes.Ui_InformationNotesDialog,
|
||||
QDialog):
|
||||
# pylint: disable=too-few-public-methods
|
||||
def __init__(self):
|
||||
super(InformationNotesDialog, self).__init__()
|
||||
|
||||
self.setupUi(self)
|
||||
details = subprocess.check_output(['/usr/libexec/qubes-manager/qvm_about.sh'])
|
||||
self.informationNotes.setText(details)
|
||||
details = subprocess.check_output(
|
||||
['/usr/libexec/qubes-manager/qvm_about.sh'])
|
||||
self.informationNotes.setText(details.decode())
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
@ -15,28 +15,24 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import sys
|
||||
from PyQt4 import QtCore # pylint: disable=import-error
|
||||
from PyQt4 import QtGui # pylint: disable=import-error
|
||||
|
||||
from . import ui_logdlg # pylint: disable=no-name-in-module
|
||||
from . import clipboard
|
||||
import os
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
|
||||
from qubes.qubes import QubesException
|
||||
|
||||
import qubesmanager.resources_rc
|
||||
|
||||
from .ui_logdlg import *
|
||||
from .clipboard import *
|
||||
|
||||
# Display only this size of log
|
||||
LOG_DISPLAY_SIZE = 1024*1024
|
||||
|
||||
class LogDialog(Ui_LogDialog, QDialog):
|
||||
|
||||
class LogDialog(ui_logdlg.Ui_LogDialog, QtGui.QDialog):
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
def __init__(self, app, log_path, parent=None):
|
||||
super(LogDialog, self).__init__(parent)
|
||||
@ -47,7 +43,9 @@ class LogDialog(Ui_LogDialog, QDialog):
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle(log_path)
|
||||
|
||||
self.connect(self.copy_to_qubes_clipboard, SIGNAL("clicked()"), self.copy_to_qubes_clipboard_triggered)
|
||||
self.connect(self.copy_to_qubes_clipboard,
|
||||
QtCore.SIGNAL("clicked()"),
|
||||
self.copy_to_clipboard_triggered)
|
||||
|
||||
self.__init_log_text__()
|
||||
|
||||
@ -56,7 +54,8 @@ class LogDialog(Ui_LogDialog, QDialog):
|
||||
log = open(self.log_path)
|
||||
log.seek(0, os.SEEK_END)
|
||||
if log.tell() > LOG_DISPLAY_SIZE:
|
||||
self.displayed_text = self.tr("(Showing only last %d bytes of file)\n") % LOG_DISPLAY_SIZE
|
||||
self.displayed_text = self.tr(
|
||||
"(Showing only last %d bytes of file)\n") % LOG_DISPLAY_SIZE
|
||||
log.seek(-LOG_DISPLAY_SIZE, os.SEEK_END)
|
||||
else:
|
||||
log.seek(0, os.SEEK_SET)
|
||||
@ -64,6 +63,5 @@ class LogDialog(Ui_LogDialog, QDialog):
|
||||
log.close()
|
||||
self.log_text.setPlainText(self.displayed_text)
|
||||
|
||||
|
||||
def copy_to_qubes_clipboard_triggered(self):
|
||||
copy_text_to_qubes_clipboard(self.displayed_text)
|
||||
def copy_to_clipboard_triggered(self):
|
||||
clipboard.copy_text_to_qubes_clipboard(self.displayed_text)
|
||||
|
@ -1,39 +1,40 @@
|
||||
import sys
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from .ui_multiselectwidget import *
|
||||
from PyQt4 import QtCore, QtGui # pylint: disable=import-error
|
||||
from . import ui_multiselectwidget # pylint: disable=no-name-in-module
|
||||
|
||||
class MultiSelectWidget(Ui_MultiSelectWidget, QWidget):
|
||||
class MultiSelectWidget(
|
||||
ui_multiselectwidget.Ui_MultiSelectWidget, QtGui.QWidget):
|
||||
|
||||
__pyqtSignals__ = ("selected_changed()",)
|
||||
__pyqtSignals__ = ("items_added(PyQt_PyObject)",)
|
||||
__pyqtSignals__ = ("items_removed(PyQt_PyObject)",)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(MultiSelectWidget, self).__init__()
|
||||
super(MultiSelectWidget, self).__init__(parent)
|
||||
self.setupUi(self)
|
||||
self.add_selected_button.clicked.connect(self.add_selected)
|
||||
self.add_all_button.clicked.connect(self.add_all)
|
||||
self.remove_selected_button.clicked.connect(self.remove_selected)
|
||||
self.remove_all_button.clicked.connect(self.remove_all)
|
||||
self.available_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.selected_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.available_list.setSelectionMode(
|
||||
QtGui.QAbstractItemView.ExtendedSelection)
|
||||
self.selected_list.setSelectionMode(
|
||||
QtGui.QAbstractItemView.ExtendedSelection)
|
||||
|
||||
def switch_selected(self, src, dst):
|
||||
selected = src.selectedItems()
|
||||
items = []
|
||||
|
||||
for s in selected:
|
||||
row = src.indexFromItem(s).row()
|
||||
for selected_item in selected:
|
||||
row = src.indexFromItem(selected_item).row()
|
||||
item = src.takeItem(row)
|
||||
dst.addItem(item)
|
||||
items.append(item)
|
||||
dst.sortItems()
|
||||
self.emit(SIGNAL("selected_changed()"))
|
||||
self.emit(QtCore.SIGNAL("selected_changed()"))
|
||||
if src is self.selected_list:
|
||||
self.emit(SIGNAL("items_removed(PyQt_PyObject)"), items)
|
||||
self.emit(QtCore.SIGNAL("items_removed(PyQt_PyObject)"), items)
|
||||
else:
|
||||
self.emit(SIGNAL("items_added(PyQt_PyObject)"), items)
|
||||
self.emit(QtCore.SIGNAL("items_added(PyQt_PyObject)"), items)
|
||||
|
||||
def add_selected(self):
|
||||
self.switch_selected(self.available_list, self.selected_list)
|
||||
@ -48,11 +49,11 @@ class MultiSelectWidget(Ui_MultiSelectWidget, QWidget):
|
||||
dst.addItem(item)
|
||||
items.append(item)
|
||||
dst.sortItems()
|
||||
self.emit(SIGNAL("selected_changed()"))
|
||||
self.emit(QtCore.SIGNAL("selected_changed()"))
|
||||
if src is self.selected_list:
|
||||
self.emit(SIGNAL("items_removed(PyQt_PyObject)"), items)
|
||||
self.emit(QtCore.SIGNAL("items_removed(PyQt_PyObject)"), items)
|
||||
else:
|
||||
self.emit(SIGNAL("items_added(PyQt_PyObject)"), items)
|
||||
self.emit(QtCore.SIGNAL("items_added(PyQt_PyObject)"), items)
|
||||
|
||||
|
||||
def add_all(self):
|
||||
@ -64,4 +65,3 @@ class MultiSelectWidget(Ui_MultiSelectWidget, QWidget):
|
||||
def clear(self):
|
||||
self.available_list.clear()
|
||||
self.selected_list.clear()
|
||||
|
||||
|
1188
qubesmanager/qube_manager.py
Executable file
1188
qubesmanager/qube_manager.py
Executable file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# coding=utf-8
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
@ -16,18 +16,17 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
from PyQt4.QtCore import SIGNAL
|
||||
from PyQt4.QtGui import QDialog, QIcon
|
||||
from PyQt4.QtGui import QDialog # pylint: disable=import-error
|
||||
|
||||
from .ui_releasenotes import *
|
||||
from . import ui_releasenotes # pylint: disable=no-name-in-module
|
||||
|
||||
|
||||
class ReleaseNotesDialog(Ui_ReleaseNotesDialog, QDialog):
|
||||
class ReleaseNotesDialog(ui_releasenotes.Ui_ReleaseNotesDialog, QDialog):
|
||||
# pylint: disable=too-few-public-methods
|
||||
def __init__(self):
|
||||
super(ReleaseNotesDialog, self).__init__()
|
||||
|
||||
|
@ -15,104 +15,90 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
import sys
|
||||
from PyQt4 import QtCore # pylint: disable=import-error
|
||||
from PyQt4 import QtGui # pylint: disable=import-error
|
||||
import threading
|
||||
import time
|
||||
import os
|
||||
import shutil
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
import os.path
|
||||
import traceback
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from qubes.qubes import QubesDaemonPidfile
|
||||
from qubes.qubes import QubesHost
|
||||
from qubes.qubes import qubes_base_dir
|
||||
import qubesmanager.resources_rc
|
||||
import signal
|
||||
|
||||
from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
|
||||
|
||||
import time
|
||||
from operator import itemgetter
|
||||
from .thread_monitor import *
|
||||
|
||||
from qubes import backup
|
||||
from qubes import qubesutils
|
||||
|
||||
from .ui_restoredlg import *
|
||||
from .multiselectwidget import *
|
||||
from . import ui_restoredlg # pylint: disable=no-name-in-module
|
||||
from . import multiselectwidget
|
||||
from . import backup_utils
|
||||
from . import thread_monitor
|
||||
|
||||
from .backup_utils import *
|
||||
from multiprocessing import Queue, Event
|
||||
from multiprocessing.queues import Empty
|
||||
from qubesadmin import Qubes, exc
|
||||
from qubesadmin.backup import restore
|
||||
|
||||
class RestoreVMsWindow(Ui_Restore, QWizard):
|
||||
|
||||
__pyqtSignals__ = ("restore_progress(int)","backup_progress(int)")
|
||||
class RestoreVMsWindow(ui_restoredlg.Ui_Restore, QtGui.QWizard):
|
||||
|
||||
def __init__(self, app, qvm_collection, blk_manager, parent=None):
|
||||
def __init__(self, qt_app, qubes_app, parent=None):
|
||||
super(RestoreVMsWindow, self).__init__(parent)
|
||||
|
||||
self.app = app
|
||||
self.qvm_collection = qvm_collection
|
||||
self.blk_manager = blk_manager
|
||||
self.qt_app = qt_app
|
||||
self.qubes_app = qubes_app
|
||||
|
||||
self.restore_options = None
|
||||
self.vms_to_restore = None
|
||||
self.func_output = []
|
||||
|
||||
# Set up logging
|
||||
self.feedback_queue = Queue()
|
||||
handler = logging.handlers.QueueHandler(self.feedback_queue)
|
||||
logger = logging.getLogger('qubesadmin.backup')
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
self.canceled = False
|
||||
self.tmpdir_to_remove = None
|
||||
self.error_detected = Event()
|
||||
|
||||
self.excluded = {}
|
||||
|
||||
self.vm = self.qvm_collection[0]
|
||||
|
||||
assert self.vm != None
|
||||
self.thread_monitor = None
|
||||
self.backup_restore = None
|
||||
self.target_appvm = None
|
||||
|
||||
self.setupUi(self)
|
||||
|
||||
self.select_vms_widget = MultiSelectWidget(self)
|
||||
self.select_vms_widget = multiselectwidget.MultiSelectWidget(self)
|
||||
self.select_vms_layout.insertWidget(1, self.select_vms_widget)
|
||||
|
||||
self.connect(self, SIGNAL("currentIdChanged(int)"), self.current_page_changed)
|
||||
self.connect(self, SIGNAL("restore_progress(QString)"), self.commit_text_edit.append)
|
||||
self.connect(self, SIGNAL("backup_progress(int)"), self.progress_bar.setValue)
|
||||
self.dir_line_edit.connect(self.dir_line_edit, SIGNAL("textChanged(QString)"), self.backup_location_changed)
|
||||
self.connect(self.verify_only, SIGNAL("stateChanged(int)"),
|
||||
self.on_verify_only_toogled)
|
||||
self.connect(self,
|
||||
QtCore.SIGNAL("currentIdChanged(int)"),
|
||||
self.current_page_changed)
|
||||
self.dir_line_edit.connect(self.dir_line_edit,
|
||||
QtCore.SIGNAL("textChanged(QString)"),
|
||||
self.backup_location_changed)
|
||||
|
||||
self.select_dir_page.isComplete = self.has_selected_dir
|
||||
self.select_vms_page.isComplete = self.has_selected_vms
|
||||
self.confirm_page.isComplete = self.all_vms_good
|
||||
# FIXME
|
||||
# this causes to run isComplete() twice, I don't know why
|
||||
self.select_vms_page.connect(self.select_vms_widget, SIGNAL("selected_changed()"), SIGNAL("completeChanged()"))
|
||||
self.select_vms_page.connect(
|
||||
self.select_vms_widget,
|
||||
QtCore.SIGNAL("selected_changed()"),
|
||||
QtCore.SIGNAL("completeChanged()"))
|
||||
|
||||
fill_appvms_list(self)
|
||||
self.__init_restore_options__()
|
||||
backup_utils.fill_appvms_list(self)
|
||||
|
||||
@pyqtSlot(name='on_select_path_button_clicked')
|
||||
@QtCore.pyqtSlot(name='on_select_path_button_clicked')
|
||||
def select_path_button_clicked(self):
|
||||
select_path_button_clicked(self, True)
|
||||
backup_utils.select_path_button_clicked(self, True)
|
||||
|
||||
def on_ignore_missing_toggled(self, checked):
|
||||
self.restore_options['use-default-template'] = checked
|
||||
self.restore_options['use-default-netvm'] = checked
|
||||
|
||||
def on_ignore_uname_mismatch_toggled(self, checked):
|
||||
self.restore_options['ignore-username-mismatch'] = checked
|
||||
|
||||
def on_verify_only_toogled(self, checked):
|
||||
self.restore_options['verify-only'] = bool(checked)
|
||||
|
||||
def cleanupPage(self, p_int):
|
||||
def cleanupPage(self, p_int): # pylint: disable=invalid-name
|
||||
if self.page(p_int) is self.select_vms_page:
|
||||
self.vms_to_restore = None
|
||||
else:
|
||||
@ -127,118 +113,94 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
|
||||
|
||||
self.target_appvm = None
|
||||
if self.appvm_combobox.currentIndex() != 0: # An existing appvm chosen
|
||||
self.target_appvm = self.qvm_collection.get_vm_by_name(
|
||||
str(self.appvm_combobox.currentText()))
|
||||
self.target_appvm = self.qubes_app.domains[
|
||||
str(self.appvm_combobox.currentText())]
|
||||
|
||||
try:
|
||||
self.vms_to_restore = backup.backup_restore_prepare(
|
||||
self.backup_restore = restore.BackupRestore(
|
||||
self.qubes_app,
|
||||
self.dir_line_edit.text(),
|
||||
self.passphrase_line_edit.text(),
|
||||
options=self.restore_options,
|
||||
host_collection=self.qvm_collection,
|
||||
encrypted=self.encryption_checkbox.isChecked(),
|
||||
appvm=self.target_appvm)
|
||||
self.target_appvm,
|
||||
self.passphrase_line_edit.text()
|
||||
)
|
||||
|
||||
if self.ignore_missing.isChecked():
|
||||
self.backup_restore.options.use_default_template = True
|
||||
self.backup_restore.options.use_default_netvm = True
|
||||
|
||||
if self.ignore_uname_mismatch.isChecked():
|
||||
self.backup_restore.options.ignore_username_mismatch = True
|
||||
|
||||
if self.verify_only.isChecked():
|
||||
self.backup_restore.options.verify_only = True
|
||||
|
||||
self.vms_to_restore = self.backup_restore.get_restore_info()
|
||||
|
||||
for vmname in self.vms_to_restore:
|
||||
if vmname.startswith('$'):
|
||||
# Internal info
|
||||
continue
|
||||
self.select_vms_widget.available_list.addItem(vmname)
|
||||
except QubesException as ex:
|
||||
QMessageBox.warning (None, self.tr("Restore error!"), str(ex))
|
||||
except exc.QubesException as ex:
|
||||
QtGui.QMessageBox.warning(None, self.tr("Restore error!"), str(ex))
|
||||
|
||||
def __init_restore_options__(self):
|
||||
if not self.restore_options:
|
||||
self.restore_options = {}
|
||||
backup.backup_restore_set_defaults(self.restore_options)
|
||||
def append_output(self, text):
|
||||
self.commit_text_edit.append(text)
|
||||
|
||||
if 'use-default-template' in self.restore_options and 'use-default-netvm' in self.restore_options:
|
||||
val = self.restore_options['use-default-template'] and self.restore_options['use-default-netvm']
|
||||
self.ignore_missing.setChecked(val)
|
||||
else:
|
||||
self.ignore_missing.setChecked(False)
|
||||
|
||||
if 'ignore-username-mismatch' in self.restore_options:
|
||||
self.ignore_uname_mismatch.setChecked(self.restore_options['ignore-username-mismatch'])
|
||||
|
||||
def gather_output(self, s):
|
||||
self.func_output.append(s)
|
||||
|
||||
def restore_error_output(self, s):
|
||||
self.error_detected.set()
|
||||
self.feedback_queue.put((SIGNAL("restore_progress(QString)"),
|
||||
u'<font color="red">{0}</font>'.format(s)))
|
||||
|
||||
def restore_output(self, s):
|
||||
self.feedback_queue.put((SIGNAL("restore_progress(QString)"),
|
||||
u'<font color="black">{0}</font>'.format(s)))
|
||||
|
||||
def update_progress_bar(self, value):
|
||||
self.feedback_queue.put((SIGNAL("backup_progress(int)"), value))
|
||||
|
||||
def __do_restore__(self, thread_monitor):
|
||||
def __do_restore__(self, t_monitor):
|
||||
err_msg = []
|
||||
self.qvm_collection.lock_db_for_writing()
|
||||
try:
|
||||
backup.backup_restore_do(self.vms_to_restore,
|
||||
self.qvm_collection,
|
||||
print_callback=self.restore_output,
|
||||
error_callback=self.restore_error_output,
|
||||
progress_callback=self.update_progress_bar)
|
||||
self.backup_restore.restore_do(self.vms_to_restore)
|
||||
|
||||
except backup.BackupCanceledError as ex:
|
||||
self.canceled = True
|
||||
self.tmpdir_to_remove = ex.tmpdir
|
||||
err_msg.append(str(ex))
|
||||
except Exception as ex:
|
||||
print ("Exception:", ex)
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
err_msg.append(str(ex))
|
||||
err_msg.append(
|
||||
self.tr("Partially restored files left in "
|
||||
"/var/tmp/restore_*, investigate them and/or clean them up"))
|
||||
self.tr("Partially restored files left in /var/tmp/restore_*, "
|
||||
"investigate them and/or clean them up"))
|
||||
|
||||
self.qvm_collection.unlock_db()
|
||||
if self.canceled:
|
||||
self.emit(SIGNAL("restore_progress(QString)"),
|
||||
'<b><font color="red">{0}</font></b>'
|
||||
.format(self.tr("Restore aborted!")))
|
||||
elif len(err_msg) > 0 or self.error_detected.is_set():
|
||||
if len(err_msg) > 0:
|
||||
thread_monitor.set_error_msg('\n'.join(err_msg))
|
||||
self.emit(SIGNAL("restore_progress(QString)"),
|
||||
'<b><font color="red">{0}</font></b>'
|
||||
.format(self.tr("Finished with errors!")))
|
||||
self.append_output('<b><font color="red">{0}</font></b>'.format(
|
||||
self.tr("Restore aborted!")))
|
||||
elif err_msg or self.error_detected.is_set():
|
||||
if err_msg:
|
||||
t_monitor.set_error_msg('\n'.join(err_msg))
|
||||
self.append_output('<b><font color="red">{0}</font></b>'.format(
|
||||
self.tr("Finished with errors!")))
|
||||
else:
|
||||
self.emit(SIGNAL("restore_progress(QString)"),
|
||||
'<font color="green">{0}</font>'
|
||||
.format(self.tr("Finished successfully!")))
|
||||
self.append_output('<font color="green">{0}</font>'.format(
|
||||
self.tr("Finished successfully!")))
|
||||
|
||||
thread_monitor.set_finished()
|
||||
t_monitor.set_finished()
|
||||
|
||||
def current_page_changed(self, id):
|
||||
def current_page_changed(self, page_id): # pylint: disable=unused-argument
|
||||
|
||||
old_sigchld_handler = signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
||||
if self.currentPage() is self.select_vms_page:
|
||||
self.__fill_vms_list__()
|
||||
|
||||
elif self.currentPage() is self.confirm_page:
|
||||
for v in self.excluded:
|
||||
self.vms_to_restore[v] = self.excluded[v]
|
||||
self.excluded = {}
|
||||
|
||||
self.vms_to_restore = self.backup_restore.get_restore_info()
|
||||
|
||||
for i in range(self.select_vms_widget.available_list.count()):
|
||||
vmname = self.select_vms_widget.available_list.item(i).text()
|
||||
self.excluded[str(vmname)] = self.vms_to_restore[str(vmname)]
|
||||
del self.vms_to_restore[str(vmname)]
|
||||
|
||||
del self.func_output[:]
|
||||
self.vms_to_restore = backup.restore_info_verify(self.vms_to_restore,
|
||||
self.qvm_collection)
|
||||
backup.backup_restore_print_summary(
|
||||
self.vms_to_restore, print_callback = self.gather_output)
|
||||
self.vms_to_restore = self.backup_restore.restore_info_verify(
|
||||
self.vms_to_restore)
|
||||
|
||||
self.func_output = self.backup_restore.get_restore_summary(
|
||||
self.vms_to_restore
|
||||
)
|
||||
|
||||
self.confirm_text_edit.setReadOnly(True)
|
||||
self.confirm_text_edit.setFontFamily("Monospace")
|
||||
self.confirm_text_edit.setText("\n".join(self.func_output))
|
||||
self.confirm_text_edit.setText(self.func_output)
|
||||
|
||||
self.confirm_page.emit(SIGNAL("completeChanged()"))
|
||||
self.confirm_page.emit(QtCore.SIGNAL("completeChanged()"))
|
||||
|
||||
elif self.currentPage() is self.commit_page:
|
||||
self.button(self.FinishButton).setDisabled(True)
|
||||
@ -247,50 +209,46 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
|
||||
and str(self.dir_line_edit.text())
|
||||
.count("media/") > 0)
|
||||
|
||||
self.thread_monitor = ThreadMonitor()
|
||||
thread = threading.Thread (target= self.__do_restore__ , args=(self.thread_monitor,))
|
||||
self.thread_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(target=self.__do_restore__,
|
||||
args=(self.thread_monitor,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
while not self.thread_monitor.is_finished():
|
||||
self.app.processEvents()
|
||||
self.qt_app.processEvents()
|
||||
time.sleep(0.1)
|
||||
try:
|
||||
for (signal_to_emit,data) in iter(self.feedback_queue.get_nowait,None):
|
||||
self.emit(signal_to_emit,data)
|
||||
log_record = self.feedback_queue.get_nowait()
|
||||
while log_record:
|
||||
if log_record.levelno == logging.ERROR or\
|
||||
log_record.levelno == logging.CRITICAL:
|
||||
output = '<font color="red">{0}</font>'.format(
|
||||
log_record.getMessage())
|
||||
else:
|
||||
output = log_record.getMessage()
|
||||
self.append_output(output)
|
||||
log_record = self.feedback_queue.get_nowait()
|
||||
except Empty:
|
||||
pass
|
||||
|
||||
if not self.thread_monitor.success:
|
||||
if self.canceled:
|
||||
if self.tmpdir_to_remove and \
|
||||
QMessageBox.warning(None, self.tr("Restore aborted"),
|
||||
self.tr("Do you want to remove temporary files "
|
||||
"from %s?") % self.tmpdir_to_remove,
|
||||
QMessageBox.Yes, QMessageBox.No) == \
|
||||
QMessageBox.Yes:
|
||||
shutil.rmtree(self.tmpdir_to_remove)
|
||||
else:
|
||||
QMessageBox.warning(None,
|
||||
self.tr("Backup error!"), self.tr("ERROR: {0}")
|
||||
.format(self.thread_monitor.error_msg))
|
||||
if not self.canceled:
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Backup error!"),
|
||||
self.tr("ERROR: {0}").format(
|
||||
self.thread_monitor.error_msg))
|
||||
self.progress_bar.setMaximum(100)
|
||||
self.progress_bar.setValue(100)
|
||||
|
||||
if self.showFileDialog.isChecked():
|
||||
self.emit(SIGNAL("restore_progress(QString)"),
|
||||
self.append_output(
|
||||
'<b><font color="black">{0}</font></b>'.format(
|
||||
self.tr(
|
||||
"Please unmount your backup volume and cancel"
|
||||
self.tr("Please unmount your backup volume and cancel "
|
||||
"the file selection dialog.")))
|
||||
if self.target_appvm:
|
||||
self.target_appvm.run("QUBESRPC %s dom0" %
|
||||
"qubes.SelectDirectory")
|
||||
else:
|
||||
file_dialog = QFileDialog()
|
||||
file_dialog.setReadOnly(True)
|
||||
file_dialog.getExistingDirectory(
|
||||
self, self.tr("Detach backup device"),
|
||||
os.path.dirname(self.dir_line_edit.text()))
|
||||
self.progress_bar.setValue(100)
|
||||
self.qt_app.processEvents()
|
||||
backup_utils.select_path_button_clicked(self, False, True)
|
||||
|
||||
self.button(self.FinishButton).setEnabled(True)
|
||||
self.button(self.CancelButton).setEnabled(False)
|
||||
self.showFileDialog.setEnabled(False)
|
||||
@ -298,19 +256,18 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
|
||||
signal.signal(signal.SIGCHLD, old_sigchld_handler)
|
||||
|
||||
def all_vms_good(self):
|
||||
for vminfo in self.vms_to_restore.values():
|
||||
if not vminfo.has_key('vm'):
|
||||
for vm_info in self.vms_to_restore.values():
|
||||
if not vm_info.vm:
|
||||
continue
|
||||
if not vminfo['good-to-go']:
|
||||
if not vm_info.good_to_go:
|
||||
return False
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
if self.currentPage() is self.commit_page:
|
||||
if backup.backup_cancel():
|
||||
self.emit(SIGNAL("restore_progress(QString)"),
|
||||
'<font color="red">{0}</font>'
|
||||
.format(self.tr("Aborting the operation...")))
|
||||
self.backup_restore.canceled = True
|
||||
self.append_output('<font color="red">{0}</font>'.format(
|
||||
self.tr("Aborting the operation...")))
|
||||
self.button(self.CancelButton).setDisabled(True)
|
||||
else:
|
||||
self.done(0)
|
||||
@ -332,57 +289,45 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
|
||||
return self.select_vms_widget.selected_list.count() > 0
|
||||
|
||||
def backup_location_changed(self, new_dir=None):
|
||||
self.select_dir_page.emit(SIGNAL("completeChanged()"))
|
||||
# pylint: disable=unused-argument
|
||||
self.select_dir_page.emit(QtCore.SIGNAL("completeChanged()"))
|
||||
|
||||
|
||||
# Bases on the original code by:
|
||||
# Copyright (c) 2002-2007 Pascal Varet <p.varet@gmail.com>
|
||||
|
||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
import sys
|
||||
import os.path
|
||||
import traceback
|
||||
|
||||
filename, line, dummy, dummy = traceback.extract_tb(exc_traceback).pop()
|
||||
filename = os.path.basename(filename)
|
||||
error = "%s: %s" % (exc_type.__name__, exc_value)
|
||||
|
||||
QMessageBox.critical(None, "Houston, we have a problem...",
|
||||
"Whoops. A critical error has occured. This is most likely a bug "
|
||||
QtGui.QMessageBox.critical(None, "Houston, we have a problem...",
|
||||
"Whoops. A critical error has occured. "
|
||||
"This is most likely a bug "
|
||||
"in Qubes Restore VMs application.<br><br>"
|
||||
"<b><i>%s</i></b>" % error +
|
||||
"at <b>line %d</b> of file <b>%s</b>.<br/><br/>"
|
||||
% (line, filename))
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
global qubes_host
|
||||
qubes_host = QubesHost()
|
||||
|
||||
global app
|
||||
app = QApplication(sys.argv)
|
||||
app.setOrganizationName("The Qubes Project")
|
||||
app.setOrganizationDomain("http://qubes-os.org")
|
||||
app.setApplicationName("Qubes Restore VMs")
|
||||
qt_app = QtGui.QApplication(sys.argv)
|
||||
qt_app.setOrganizationName("The Qubes Project")
|
||||
qt_app.setOrganizationDomain("http://qubes-os.org")
|
||||
qt_app.setApplicationName("Qubes Restore VMs")
|
||||
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
qubes_app = Qubes()
|
||||
|
||||
global restore_window
|
||||
restore_window = RestoreVMsWindow()
|
||||
restore_window = RestoreVMsWindow(qt_app, qubes_app)
|
||||
|
||||
restore_window.show()
|
||||
|
||||
app.exec_()
|
||||
app.exit()
|
||||
|
||||
qt_app.exec_()
|
||||
qt_app.exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -17,39 +17,37 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
import collections
|
||||
import copy
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import qubesadmin
|
||||
import qubesadmin.tools
|
||||
import os
|
||||
import sys
|
||||
from qubesadmin.tools import QubesArgumentParser
|
||||
from qubesadmin import devices
|
||||
import qubesadmin.exc
|
||||
|
||||
from . import utils
|
||||
from . import multiselectwidget
|
||||
from . import thread_monitor
|
||||
|
||||
from .appmenu_select import AppmenuSelectManager
|
||||
from .backup_utils import get_path_for_vm
|
||||
from .firewall import *
|
||||
from . import firewall
|
||||
from PyQt4 import QtCore, QtGui # pylint: disable=import-error
|
||||
|
||||
from .ui_settingsdlg import *
|
||||
from .bootfromdevice import main as bootfromdevice
|
||||
from . import ui_settingsdlg #pylint: disable=no-name-in-module
|
||||
|
||||
class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class VMSettingsWindow(ui_settingsdlg.Ui_SettingsDialog, QtGui.QDialog):
|
||||
tabs_indices = collections.OrderedDict((
|
||||
('basic', 0),
|
||||
('advanced', 1),
|
||||
@ -73,42 +71,59 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
self.setWindowTitle(self.tr("Settings: {vm}").format(vm=self.vm.name))
|
||||
if init_page in self.tabs_indices:
|
||||
idx = self.tabs_indices[init_page]
|
||||
assert (idx in range(self.tabWidget.count()))
|
||||
assert idx in range(self.tabWidget.count())
|
||||
self.tabWidget.setCurrentIndex(idx)
|
||||
|
||||
self.connect(self.buttonBox, SIGNAL("accepted()"), self.save_and_apply)
|
||||
self.connect(self.buttonBox, SIGNAL("rejected()"), self.reject)
|
||||
self.connect(self.buttonBox,
|
||||
QtCore.SIGNAL("accepted()"),
|
||||
self.save_and_apply)
|
||||
self.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject)
|
||||
|
||||
self.tabWidget.currentChanged.connect(self.current_tab_changed)
|
||||
|
||||
# self.tabWidget.setTabEnabled(self.tabs_indices["firewall"], vm.is_networked() and not vm.provides_network)
|
||||
|
||||
###### basic tab
|
||||
self.__init_basic_tab__()
|
||||
self.rename_vm_button.clicked.connect(self.rename_vm)
|
||||
self.delete_vm_button.clicked.connect(self.remove_vm)
|
||||
self.clone_vm_button.clicked.connect(self.clone_vm)
|
||||
|
||||
###### advanced tab
|
||||
self.__init_advanced_tab__()
|
||||
self.include_in_balancing.stateChanged.connect(self.include_in_balancing_state_changed)
|
||||
self.connect(self.init_mem, SIGNAL("editingFinished()"), self.check_mem_changes)
|
||||
self.connect(self.max_mem_size, SIGNAL("editingFinished()"), self.check_mem_changes)
|
||||
self.bootFromDeviceButton.clicked.connect(self.boot_from_cdrom_button_pressed)
|
||||
self.include_in_balancing.stateChanged.connect(
|
||||
self.include_in_balancing_changed)
|
||||
self.connect(self.init_mem,
|
||||
QtCore.SIGNAL("editingFinished()"),
|
||||
self.check_mem_changes)
|
||||
self.connect(self.max_mem_size,
|
||||
QtCore.SIGNAL("editingFinished()"),
|
||||
self.check_mem_changes)
|
||||
self.boot_from_device_button.clicked.connect(
|
||||
self.boot_from_cdrom_button_pressed)
|
||||
|
||||
###### firewall tab
|
||||
if self.tabWidget.isTabEnabled(self.tabs_indices['firewall']):
|
||||
model = QubesFirewallRulesModel()
|
||||
model = firewall.QubesFirewallRulesModel()
|
||||
try:
|
||||
model.set_vm(vm)
|
||||
self.set_fw_model(model)
|
||||
self.firewall_modified_outside_label.setVisible(False)
|
||||
except firewall.FirewallModifiedOutsideError:
|
||||
self.disable_all_fw_conf()
|
||||
|
||||
self.newRuleButton.clicked.connect(self.new_rule_button_pressed)
|
||||
self.editRuleButton.clicked.connect(self.edit_rule_button_pressed)
|
||||
self.deleteRuleButton.clicked.connect(self.delete_rule_button_pressed)
|
||||
self.policyDenyRadioButton.clicked.connect(self.policy_changed)
|
||||
self.policyAllowRadioButton.clicked.connect(self.policy_changed)
|
||||
self.new_rule_button.clicked.connect(self.new_rule_button_pressed)
|
||||
self.edit_rule_button.clicked.connect(self.edit_rule_button_pressed)
|
||||
self.delete_rule_button.clicked.connect(
|
||||
self.delete_rule_button_pressed)
|
||||
self.policy_deny_radio_button.clicked.connect(self.policy_changed)
|
||||
self.policy_allow_radio_button.clicked.connect(self.policy_changed)
|
||||
if init_page == 'firewall':
|
||||
self.check_network_availability()
|
||||
|
||||
####### devices tab
|
||||
self.__init_devices_tab__()
|
||||
self.connect(self.dev_list, SIGNAL("selected_changed()"), self.devices_selection_changed)
|
||||
self.connect(self.dev_list,
|
||||
QtCore.SIGNAL("selected_changed()"),
|
||||
self.devices_selection_changed)
|
||||
|
||||
####### services tab
|
||||
self.__init_services_tab__()
|
||||
@ -119,8 +134,9 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if self.tabWidget.isTabEnabled(self.tabs_indices["applications"]):
|
||||
self.app_list = multiselectwidget.MultiSelectWidget(self)
|
||||
self.apps_layout.addWidget(self.app_list)
|
||||
self.AppListManager = AppmenuSelectManager(self.vm, self.app_list)
|
||||
self.refresh_apps_button.clicked.connect(self.refresh_apps_button_pressed)
|
||||
self.app_list_manager = AppmenuSelectManager(self.vm, self.app_list)
|
||||
self.refresh_apps_button.clicked.connect(
|
||||
self.refresh_apps_button_pressed)
|
||||
|
||||
def reject(self):
|
||||
self.done(0)
|
||||
@ -131,12 +147,14 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
|
||||
def save_and_apply(self):
|
||||
t_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(target=self.__save_changes__, args=(t_monitor,))
|
||||
thread = threading.Thread(target=self.__save_changes__,
|
||||
args=(t_monitor,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
progress = QProgressDialog(
|
||||
self.tr("Applying settings to <b>{0}</b>...").format(self.vm.name), "", 0, 0)
|
||||
progress = QtGui.QProgressDialog(
|
||||
self.tr("Applying settings to <b>{0}</b>...").format(self.vm.name),
|
||||
"", 0, 0)
|
||||
progress.setCancelButton(None)
|
||||
progress.setModal(True)
|
||||
progress.show()
|
||||
@ -148,67 +166,83 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
progress.hide()
|
||||
|
||||
if not t_monitor.success:
|
||||
QMessageBox.warning(None,
|
||||
self.tr("Error while changing settings for {0}!").format(self.vm.name),
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Error while changing settings for {0}!"
|
||||
).format(self.vm.name),
|
||||
self.tr("ERROR: {0}").format(t_monitor.error_msg))
|
||||
|
||||
self.done(0)
|
||||
|
||||
def __save_changes__(self, t_monitor):
|
||||
|
||||
self.anything_changed = False
|
||||
|
||||
ret = []
|
||||
try:
|
||||
ret_tmp = self.__apply_basic_tab__()
|
||||
if len(ret_tmp) > 0:
|
||||
if ret_tmp:
|
||||
ret += ["Basic tab:"] + ret_tmp
|
||||
ret_tmp = self.__apply_advanced_tab__()
|
||||
if len(ret_tmp) > 0:
|
||||
if ret_tmp:
|
||||
ret += ["Advanced tab:"] + ret_tmp
|
||||
ret_tmp = self.__apply_devices_tab__()
|
||||
if len(ret_tmp) > 0:
|
||||
if ret_tmp:
|
||||
ret += ["Devices tab:"] + ret_tmp
|
||||
ret_tmp = self.__apply_services_tab__()
|
||||
if len(ret_tmp) > 0:
|
||||
if ret_tmp:
|
||||
ret += ["Sevices tab:"] + ret_tmp
|
||||
except Exception as ex:
|
||||
ret.append(self.tr('Error while saving changes: ') + str(ex))
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
ret.append(self.tr('Error while saving changes: ') + str(qex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
ret.append(repr(ex))
|
||||
|
||||
try:
|
||||
if self.tabWidget.isTabEnabled(self.tabs_indices["firewall"]):
|
||||
self.fw_model.apply_rules(self.policyAllowRadioButton.isChecked(),
|
||||
self.dnsCheckBox.isChecked(),
|
||||
self.icmpCheckBox.isChecked(),
|
||||
self.yumproxyCheckBox.isChecked(),
|
||||
self.tempFullAccess.isChecked(),
|
||||
self.tempFullAccessTime.value())
|
||||
if self.fw_model.fw_changed:
|
||||
# might modified vm.services
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
ret += [self.tr("Firewall tab:"), str(ex)]
|
||||
if self.policy_allow_radio_button.isEnabled():
|
||||
self.fw_model.apply_rules(
|
||||
self.policy_allow_radio_button.isChecked(),
|
||||
self.temp_full_access.isChecked(),
|
||||
self.temp_full_access_time.value())
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
ret += [self.tr("Firewall tab:"), str(qex)]
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
ret += [self.tr("Firewall tab:"), repr(ex)]
|
||||
|
||||
try:
|
||||
if self.tabWidget.isTabEnabled(self.tabs_indices["applications"]):
|
||||
self.AppListManager.save_appmenu_select_changes()
|
||||
except Exception as ex:
|
||||
ret += [self.tr("Applications tab:"), str(ex)]
|
||||
self.app_list_manager.save_appmenu_select_changes()
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
ret += [self.tr("Applications tab:"), str(qex)]
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
ret += [self.tr("Applications tab:"), repr(ex)]
|
||||
|
||||
if len(ret) > 0 :
|
||||
if ret:
|
||||
t_monitor.set_error_msg('\n'.join(ret))
|
||||
|
||||
utils.debug('\n'.join(ret))
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
def current_tab_changed(self, idx):
|
||||
if idx == self.tabs_indices["firewall"]:
|
||||
def check_network_availability(self):
|
||||
netvm = self.vm.netvm
|
||||
self.no_netvm_label.setVisible(netvm is None)
|
||||
self.netvm_no_firewall_label.setVisible(
|
||||
netvm is not None and
|
||||
not netvm.features.check_with_template('qubes-firewall', False))
|
||||
if netvm is None:
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Qube configuration problem!"),
|
||||
self.tr('This qube has networking disabled '
|
||||
'(Basic -> Networking) - network will be disabled. '
|
||||
'If you want to use firewall, '
|
||||
'please enable networking.')
|
||||
)
|
||||
if netvm is not None and \
|
||||
not netvm.features.check_with_template('qubes-firewall', False):
|
||||
QMessageBox.warning(None,
|
||||
self.tr("VM configuration problem!"),
|
||||
not netvm.features.check_with_template(
|
||||
'qubes-firewall',
|
||||
False):
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Qube configuration problem!"),
|
||||
self.tr("The '{vm}' AppVM is network connected to "
|
||||
"'{netvm}', which does not support firewall!<br/>"
|
||||
"You may edit the '{vm}' VM firewall rules, but these "
|
||||
@ -216,6 +250,9 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
"a working Firewall VM.").format(
|
||||
vm=self.vm.name, netvm=netvm.name))
|
||||
|
||||
def current_tab_changed(self, idx):
|
||||
if idx == self.tabs_indices["firewall"]:
|
||||
self.check_network_availability()
|
||||
|
||||
######### basic tab
|
||||
|
||||
@ -233,9 +270,17 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
|
||||
def __init_basic_tab__(self):
|
||||
self.vmname.setText(self.vm.name)
|
||||
self.vmname.setValidator(QRegExpValidator(QRegExp("[a-zA-Z0-9-]*", Qt.CaseInsensitive), None))
|
||||
self.vmname.setValidator(
|
||||
QtGui.QRegExpValidator(
|
||||
QtCore.QRegExp("[a-zA-Z0-9-]*",
|
||||
QtCore.Qt.CaseInsensitive), None))
|
||||
self.vmname.setEnabled(False)
|
||||
self.rename_vm_button.setEnabled(not self.vm.is_running())
|
||||
self.delete_vm_button.setEnabled(not self.vm.is_running())
|
||||
|
||||
if self.vm.is_running():
|
||||
self.delete_vm_button.setText(self.tr('Delete VM '
|
||||
'(cannot delete a running VM)'))
|
||||
|
||||
if self.vm.qid == 0:
|
||||
self.vmlabel.setVisible(False)
|
||||
@ -282,7 +327,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
self.autostart_vm.setVisible(False)
|
||||
|
||||
#type
|
||||
self.type_label.setText(type(self.vm).__name__)
|
||||
self.type_label.setText(self.vm.klass)
|
||||
|
||||
#installed by rpm
|
||||
self.rpm_label.setText('Yes' if self.vm.installed_by_rpm else 'No')
|
||||
@ -316,8 +361,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if self.vmlabel.currentIndex() != self.label_idx:
|
||||
label = self.label_list[self.vmlabel.currentIndex()]
|
||||
self.vm.label = label
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#vm template changed
|
||||
@ -325,24 +369,22 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if self.template_name.currentIndex() != self.template_idx:
|
||||
self.vm.template = \
|
||||
self.template_list[self.template_name.currentIndex()]
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#vm netvm changed
|
||||
try:
|
||||
if self.netVM.currentIndex() != self.netvm_idx:
|
||||
self.vm.netvm = self.netvm_list[self.netVM.currentIndex()]
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#include in backups
|
||||
try:
|
||||
if self.vm.include_in_backups != self.include_in_backups.isChecked():
|
||||
if self.vm.include_in_backups != \
|
||||
self.include_in_backups.isChecked():
|
||||
self.vm.include_in_backups = self.include_in_backups.isChecked()
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#run_in_debug_mode
|
||||
@ -350,8 +392,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if self.run_in_debug_mode.isVisible():
|
||||
if self.vm.debug != self.run_in_debug_mode.isChecked():
|
||||
self.vm.debug = self.run_in_debug_mode.isChecked()
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#autostart_vm
|
||||
@ -359,8 +400,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if self.autostart_vm.isVisible():
|
||||
if self.vm.autostart != self.autostart_vm.isChecked():
|
||||
self.vm.autostart = self.autostart_vm.isChecked()
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#max priv storage
|
||||
@ -368,8 +408,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if self.priv_img_size != priv_size:
|
||||
try:
|
||||
self.vm.volumes['private'].resize(priv_size * 1024**2)
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#max sys storage
|
||||
@ -377,8 +416,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if self.root_img_size != sys_size:
|
||||
try:
|
||||
self.vm.volumes['root'].resize(sys_size * 1024**2)
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
return msg
|
||||
@ -386,39 +424,27 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
|
||||
def check_mem_changes(self):
|
||||
if self.max_mem_size.value() < self.init_mem.value():
|
||||
QMessageBox.warning(None,
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Warning!"),
|
||||
self.tr("Max memory can not be less than initial memory.<br>"
|
||||
"Setting max memory to equal initial memory."))
|
||||
self.max_mem_size.setValue(self.init_mem.value())
|
||||
# Linux specific limit: init memory must not be below max_mem_size/10.79 in order to allow scaling up to max_mem_size (or else "add_memory() failed: -17" problem)
|
||||
# Linux specific limit: init memory must not be below
|
||||
# max_mem_size/10.79 in order to allow scaling up to
|
||||
# max_mem_size (or else "add_memory() failed: -17" problem)
|
||||
if self.init_mem.value() * 10 < self.max_mem_size.value():
|
||||
QMessageBox.warning(None,
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Warning!"),
|
||||
self.tr("Initial memory can not be less than one tenth "
|
||||
"Max memory.<br>Setting initial memory to the minimum "
|
||||
"allowed value."))
|
||||
self.init_mem.setValue(self.max_mem_size.value() / 10)
|
||||
|
||||
def _rename_vm(self, t_monitor, name):
|
||||
try:
|
||||
self.vm.app.clone_vm(self.vm, name)
|
||||
del self.vm.app.domains[self.vm.name]
|
||||
|
||||
except Exception as ex:
|
||||
t_monitor.set_error_msg(str(ex))
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
|
||||
def rename_vm(self):
|
||||
|
||||
new_vm_name, ok = QInputDialog.getText(self, self.tr('Rename VM'), self.tr('New name: (WARNING: all other changes will be discarded)'))
|
||||
|
||||
if ok:
|
||||
|
||||
def _run_in_thread(self, func, *args):
|
||||
t_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(target=self._rename_vm, args=(t_monitor, new_vm_name,))
|
||||
thread = threading.Thread(target=func, args=(t_monitor, *args,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
@ -427,13 +453,93 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
time.sleep(0.1)
|
||||
|
||||
if not t_monitor.success:
|
||||
QMessageBox.warning(None,
|
||||
self.tr("Error renaming the VM!"),
|
||||
QtGui.QMessageBox.warning(None,
|
||||
self.tr("Error!"),
|
||||
self.tr("ERROR: {}").format(
|
||||
t_monitor.error_msg))
|
||||
|
||||
|
||||
def _rename_vm(self, t_monitor, name):
|
||||
try:
|
||||
self.vm.app.clone_vm(self.vm, name)
|
||||
del self.vm.app.domains[self.vm.name]
|
||||
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
t_monitor.set_error_msg(str(qex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
t_monitor.set_error_msg(repr(ex))
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
|
||||
def rename_vm(self):
|
||||
|
||||
new_vm_name, ok = QtGui.QInputDialog.getText(
|
||||
self,
|
||||
self.tr('Rename VM'),
|
||||
self.tr('New name: (WARNING: all other changes will be discarded)'))
|
||||
|
||||
if ok:
|
||||
self._run_in_thread(self._rename_vm, new_vm_name)
|
||||
self.done(0)
|
||||
|
||||
def _remove_vm(self, t_monitor):
|
||||
try:
|
||||
del self.vm.app.domains[self.vm.name]
|
||||
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
t_monitor.set_error_msg(str(qex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
t_monitor.set_error_msg(repr(ex))
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
def remove_vm(self):
|
||||
|
||||
answer, ok = QtGui.QInputDialog.getText(
|
||||
self,
|
||||
self.tr('Delete VM'),
|
||||
self.tr('Are you absolutely sure you want to delete this VM? '
|
||||
'<br/> All VM settings and data will be irrevocably'
|
||||
' deleted. <br/> If you are sure, please enter this '
|
||||
'VM\'s name below.'))
|
||||
|
||||
|
||||
if ok and answer == self.vm.name:
|
||||
self._run_in_thread(self._remove_vm)
|
||||
self.done(0)
|
||||
|
||||
elif ok:
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Removal cancelled"),
|
||||
self.tr("The VM will not be removed."))
|
||||
|
||||
def _clone_vm(self, t_monitor, name):
|
||||
try:
|
||||
self.vm.app.clone_vm(self.vm, name)
|
||||
|
||||
except qubesadmin.exc.QubesException as qex:
|
||||
t_monitor.set_error_msg(str(qex))
|
||||
except Exception as ex: # pylint: disable=broad-except
|
||||
t_monitor.set_error_msg(repr(ex))
|
||||
|
||||
t_monitor.set_finished()
|
||||
|
||||
def clone_vm(self):
|
||||
|
||||
cloned_vm_name, ok = QtGui.QInputDialog.getText(
|
||||
self,
|
||||
self.tr('Clone VM'),
|
||||
self.tr('Name for the cloned VM:'))
|
||||
|
||||
if ok:
|
||||
self._run_in_thread(self._clone_vm, cloned_vm_name)
|
||||
QtGui.QMessageBox.warning(
|
||||
None,
|
||||
self.tr("Success"),
|
||||
self.tr("The VM was cloned successfully."))
|
||||
|
||||
######### advanced tab
|
||||
|
||||
def __init_advanced_tab__(self):
|
||||
@ -453,7 +559,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
|
||||
self.include_in_balancing.setEnabled(True)
|
||||
self.include_in_balancing.setChecked(
|
||||
self.vm.features.get('services.meminfo-writer', True))
|
||||
bool(self.vm.features.get('service.meminfo-writer', True)))
|
||||
self.max_mem_size.setEnabled(self.include_in_balancing.isChecked())
|
||||
|
||||
#in case VM is HVM
|
||||
@ -490,16 +596,13 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
try:
|
||||
if self.init_mem.value() != int(self.vm.memory):
|
||||
self.vm.memory = self.init_mem.value()
|
||||
self.anything_changed = True
|
||||
|
||||
if self.max_mem_size.value() != int(self.vm.maxmem):
|
||||
self.vm.maxmem = self.max_mem_size.value()
|
||||
self.anything_changed = True
|
||||
|
||||
if self.vcpus.value() != int(self.vm.vcpus):
|
||||
self.vm.vcpus = self.vcpus.value()
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#include_in_memory_balancing applied in services tab
|
||||
@ -508,9 +611,9 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if hasattr(self.vm, "kernel") and self.kernel_groupbox.isVisible():
|
||||
try:
|
||||
if self.kernel.currentIndex() != self.kernel_idx:
|
||||
self.vm.kernel = self.kernel_list[self.kernel.currentIndex()]
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
self.vm.kernel = self.kernel_list[
|
||||
self.kernel.currentIndex()]
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
#vm default_dispvm changed
|
||||
@ -518,8 +621,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if self.default_dispvm.currentIndex() != self.default_dispvm_idx:
|
||||
self.vm.default_dispvm = \
|
||||
self.default_dispvm_list[self.default_dispvm.currentIndex()]
|
||||
self.anything_changed = True
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
try:
|
||||
@ -632,11 +734,11 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
for dev in lspci.splitlines():
|
||||
devs.append((dev.rstrip(), dev.split(' ')[0]))
|
||||
|
||||
class DevListWidgetItem(QListWidgetItem):
|
||||
# pylint: disable=too-few-public-methods
|
||||
class DevListWidgetItem(QtGui.QListWidgetItem):
|
||||
def __init__(self, name, ident, parent=None):
|
||||
super(DevListWidgetItem, self).__init__(name, parent)
|
||||
self.ident = ident
|
||||
self.Type
|
||||
|
||||
persistent = [ass.ident.replace('_', ':')
|
||||
for ass in self.vm.devices['pci'].persistent()]
|
||||
@ -649,7 +751,8 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
self.dev_list.available_list.addItem(
|
||||
DevListWidgetItem(name, ident))
|
||||
|
||||
if self.dev_list.selected_list.count() > 0 and self.include_in_balancing.isChecked():
|
||||
if self.dev_list.selected_list.count() > 0\
|
||||
and self.include_in_balancing.isChecked():
|
||||
self.dmm_warning_adv.show()
|
||||
self.dmm_warning_dev.show()
|
||||
else:
|
||||
@ -677,7 +780,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
for i in range(self.dev_list.selected_list.count())]
|
||||
for ident in new:
|
||||
if ident not in old:
|
||||
ass = qubesadmin.devices.DeviceAssignment(
|
||||
ass = devices.DeviceAssignment(
|
||||
self.vm.app.domains['dom0'],
|
||||
ident.replace(':', '_'),
|
||||
persistent=True)
|
||||
@ -686,24 +789,22 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if ass.ident.replace('_', ':') not in new:
|
||||
self.vm.devices['pci'].detach(ass)
|
||||
|
||||
self.anything_changed = True
|
||||
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
if utils.is_debug():
|
||||
traceback.print_exc()
|
||||
msg.append(str(ex))
|
||||
|
||||
return msg
|
||||
|
||||
def include_in_balancing_state_changed(self, state):
|
||||
for r in range (self.services_list.count()):
|
||||
item = self.services_list.item(r)
|
||||
def include_in_balancing_changed(self, state):
|
||||
for i in range(self.services_list.count()):
|
||||
item = self.services_list.item(i)
|
||||
if str(item.text()) == 'meminfo-writer':
|
||||
item.setCheckState(state)
|
||||
break
|
||||
|
||||
if self.dev_list.selected_list.count() > 0:
|
||||
if state == QtCore.Qt.Checked:
|
||||
if state == ui_settingsdlg.QtCore.Qt.Checked:
|
||||
self.dmm_warning_adv.show()
|
||||
self.dmm_warning_dev.show()
|
||||
else:
|
||||
@ -757,7 +858,9 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
self.refresh_apps_button.setText(self.tr('Refresh in progress...'))
|
||||
|
||||
t_monitor = thread_monitor.ThreadMonitor()
|
||||
thread = threading.Thread(target=self.refresh_apps_in_vm, args=(t_monitor,))
|
||||
thread = threading.Thread(
|
||||
target=self.refresh_apps_in_vm,
|
||||
args=(t_monitor,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
@ -765,7 +868,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
self.qapp.processEvents()
|
||||
time.sleep(0.1)
|
||||
|
||||
self.AppListManager = AppmenuSelectManager(self.vm, self.app_list)
|
||||
self.app_list_manager = AppmenuSelectManager(self.vm, self.app_list)
|
||||
|
||||
self.refresh_apps_button.setEnabled(True)
|
||||
self.refresh_apps_button.setText(self.tr('Refresh Applications'))
|
||||
@ -778,23 +881,29 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if not feature.startswith('service.'):
|
||||
continue
|
||||
service = feature[len('service.'):]
|
||||
item = QListWidgetItem(service)
|
||||
item.setCheckState(QtCore.Qt.Checked
|
||||
if self.vm.features[feature] else QtCore.Qt.Unchecked)
|
||||
item = QtGui.QListWidgetItem(service)
|
||||
item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked
|
||||
if self.vm.features[feature]
|
||||
else ui_settingsdlg.QtCore.Qt.Unchecked)
|
||||
self.services_list.addItem(item)
|
||||
self.new_srv_dict[service] = self.vm.features[feature]
|
||||
|
||||
self.connect(self.services_list, SIGNAL("itemClicked(QListWidgetItem *)"), self.services_item_clicked)
|
||||
self.connect(
|
||||
self.services_list,
|
||||
QtCore.SIGNAL("itemClicked(QListWidgetItem *)"),
|
||||
self.services_item_clicked)
|
||||
|
||||
def __add_service__(self):
|
||||
srv = str(self.service_line_edit.text()).strip()
|
||||
if srv != "":
|
||||
if srv in self.new_srv_dict:
|
||||
QMessageBox.information(None, '',
|
||||
QtGui.QMessageBox.information(
|
||||
None,
|
||||
'',
|
||||
self.tr('Service already on the list!'))
|
||||
else:
|
||||
item = QListWidgetItem(srv)
|
||||
item.setCheckState(QtCore.Qt.Checked)
|
||||
item = QtGui.QListWidgetItem(srv)
|
||||
item.setCheckState(ui_settingsdlg.QtCore.Qt.Checked)
|
||||
self.services_list.addItem(item)
|
||||
self.new_srv_dict[srv] = True
|
||||
|
||||
@ -804,9 +913,11 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
if not item:
|
||||
return
|
||||
if str(item.text()) == 'meminfo-writer':
|
||||
QMessageBox.information(None,
|
||||
QtGui.QMessageBox.information(
|
||||
None,
|
||||
self.tr('Service can not be removed'),
|
||||
self.tr('Service meminfo-writer can not be removed from the list.'))
|
||||
self.tr('Service meminfo-writer can not '
|
||||
'be removed from the list.'))
|
||||
return
|
||||
|
||||
row = self.services_list.currentRow()
|
||||
@ -816,10 +927,10 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
|
||||
def services_item_clicked(self, item):
|
||||
if str(item.text()) == 'meminfo-writer':
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
if item.checkState() == ui_settingsdlg.QtCore.Qt.Checked:
|
||||
if not self.include_in_balancing.isChecked():
|
||||
self.include_in_balancing.setChecked(True)
|
||||
elif item.checkState() == QtCore.Qt.Unchecked:
|
||||
elif item.checkState() == ui_settingsdlg.QtCore.Qt.Unchecked:
|
||||
if self.include_in_balancing.isChecked():
|
||||
self.include_in_balancing.setChecked(False)
|
||||
|
||||
@ -828,13 +939,16 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
msg = []
|
||||
|
||||
try:
|
||||
for r in range(self.services_list.count()):
|
||||
item = self.services_list.item(r)
|
||||
self.new_srv_dict[str(item.text())] = (item.checkState() == QtCore.Qt.Checked)
|
||||
for i in range(self.services_list.count()):
|
||||
item = self.services_list.item(i)
|
||||
self.new_srv_dict[str(item.text())] = \
|
||||
(item.checkState() == ui_settingsdlg.QtCore.Qt.Checked)
|
||||
|
||||
balancing_was_checked = self.vm.features.get('service.meminfo-writer', True)
|
||||
balancing_was_checked = self.vm.features.get(
|
||||
'service.meminfo-writer', True)
|
||||
balancing_is_checked = self.include_in_balancing.isChecked()
|
||||
meminfo_writer_checked = self.new_srv_dict.get('meminfo-writer', True)
|
||||
meminfo_writer_checked = self.new_srv_dict.get(
|
||||
'meminfo-writer', True)
|
||||
|
||||
if balancing_is_checked != meminfo_writer_checked:
|
||||
if balancing_is_checked != balancing_was_checked:
|
||||
@ -844,7 +958,6 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
feature = 'service.' + service
|
||||
if v != self.vm.features.get(feature, object()):
|
||||
self.vm.features[feature] = v
|
||||
self.anything_changed = True
|
||||
|
||||
for feature in self.vm.features:
|
||||
if not feature.startswith('service.'):
|
||||
@ -852,7 +965,7 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
service = feature[len('service.'):]
|
||||
if service not in self.new_srv_dict:
|
||||
del self.vm.features[feature]
|
||||
except Exception as ex:
|
||||
except qubesadmin.exc.QubesException as ex:
|
||||
msg.append(str(ex))
|
||||
|
||||
return msg
|
||||
@ -863,116 +976,65 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
def set_fw_model(self, model):
|
||||
self.fw_model = model
|
||||
self.rulesTreeView.setModel(model)
|
||||
self.rulesTreeView.header().setResizeMode(QHeaderView.ResizeToContents)
|
||||
self.rulesTreeView.header().setResizeMode(0, QHeaderView.Stretch)
|
||||
self.rulesTreeView.header().setResizeMode(
|
||||
QtGui.QHeaderView.ResizeToContents)
|
||||
self.rulesTreeView.header().setResizeMode(0, QtGui.QHeaderView.Stretch)
|
||||
self.set_allow(model.allow)
|
||||
self.dnsCheckBox.setChecked(model.allowDns)
|
||||
self.icmpCheckBox.setChecked(model.allowIcmp)
|
||||
self.yumproxyCheckBox.setChecked(model.allowYumProxy)
|
||||
if model.tempFullAccessExpireTime:
|
||||
self.tempFullAccess.setChecked(True)
|
||||
self.tempFullAccessTime.setValue(
|
||||
(model.tempFullAccessExpireTime -
|
||||
int(datetime.datetime.now().strftime("%s")))/60)
|
||||
if model.temp_full_access_expire_time:
|
||||
self.temp_full_access.setChecked(True)
|
||||
self.temp_full_access_time.setValue(
|
||||
(model.temp_full_access_expire_time -
|
||||
int(firewall.datetime.datetime.now().strftime("%s"))) / 60)
|
||||
|
||||
def disable_all_fw_conf(self):
|
||||
self.firewall_modified_outside_label.setVisible(True)
|
||||
self.policy_allow_radio_button.setEnabled(False)
|
||||
self.policy_deny_radio_button.setEnabled(False)
|
||||
self.rulesTreeView.setEnabled(False)
|
||||
self.new_rule_button.setEnabled(False)
|
||||
self.edit_rule_button.setEnabled(False)
|
||||
self.delete_rule_button.setEnabled(False)
|
||||
self.firewal_rules_label.setEnabled(False)
|
||||
self.tempFullAccessWidget.setEnabled(False)
|
||||
|
||||
def set_allow(self, allow):
|
||||
self.policyAllowRadioButton.setChecked(allow)
|
||||
self.policyDenyRadioButton.setChecked(not allow)
|
||||
self.policy_changed(allow)
|
||||
self.policy_allow_radio_button.setChecked(allow)
|
||||
self.policy_deny_radio_button.setChecked(not allow)
|
||||
self.policy_changed()
|
||||
|
||||
def policy_changed(self, checked):
|
||||
self.tempFullAccessWidget.setEnabled(self.policyDenyRadioButton.isChecked())
|
||||
def policy_changed(self):
|
||||
self.rulesTreeView.setEnabled(
|
||||
self.policy_deny_radio_button.isChecked())
|
||||
self.new_rule_button.setEnabled(
|
||||
self.policy_deny_radio_button.isChecked())
|
||||
self.edit_rule_button.setEnabled(
|
||||
self.policy_deny_radio_button.isChecked())
|
||||
self.delete_rule_button.setEnabled(
|
||||
self.policy_deny_radio_button.isChecked())
|
||||
self.firewal_rules_label.setEnabled(
|
||||
self.policy_deny_radio_button.isChecked())
|
||||
self.tempFullAccessWidget.setEnabled(
|
||||
self.policy_deny_radio_button.isChecked())
|
||||
|
||||
def new_rule_button_pressed(self):
|
||||
dialog = NewFwRuleDlg()
|
||||
self.run_rule_dialog(dialog)
|
||||
dialog = firewall.NewFwRuleDlg()
|
||||
self.fw_model.run_rule_dialog(dialog)
|
||||
|
||||
def edit_rule_button_pressed(self):
|
||||
dialog = NewFwRuleDlg()
|
||||
dialog.set_ok_enabled(True)
|
||||
selected = self.rulesTreeView.selectedIndexes()
|
||||
if len(selected) > 0:
|
||||
row = self.rulesTreeView.selectedIndexes().pop().row()
|
||||
address = self.fw_model.get_column_string(0, row).replace(' ', '')
|
||||
dialog.addressComboBox.setItemText(0, address)
|
||||
dialog.addressComboBox.setCurrentIndex(0)
|
||||
service = self.fw_model.get_column_string(1, row)
|
||||
if service == "any":
|
||||
service = ""
|
||||
dialog.serviceComboBox.setItemText(0, service)
|
||||
dialog.serviceComboBox.setCurrentIndex(0)
|
||||
protocol = self.fw_model.get_column_string(2, row)
|
||||
if protocol == "tcp":
|
||||
dialog.tcp_radio.setChecked(True)
|
||||
elif protocol == "udp":
|
||||
dialog.udp_radio.setChecked(True)
|
||||
else:
|
||||
dialog.any_radio.setChecked(True)
|
||||
|
||||
self.run_rule_dialog(dialog, row)
|
||||
selected = self.rulesTreeView.selectedIndexes()
|
||||
|
||||
if selected:
|
||||
dialog = firewall.NewFwRuleDlg()
|
||||
dialog.set_ok_state(True)
|
||||
row = self.rulesTreeView.selectedIndexes().pop().row()
|
||||
self.fw_model.populate_edit_dialog(dialog, row)
|
||||
self.fw_model.run_rule_dialog(dialog, row)
|
||||
|
||||
def delete_rule_button_pressed(self):
|
||||
for i in set([index.row() for index in self.rulesTreeView.selectedIndexes()]):
|
||||
self.fw_model.removeChild(i)
|
||||
|
||||
def run_rule_dialog(self, dialog, row = None):
|
||||
if dialog.exec_():
|
||||
address = str(dialog.addressComboBox.currentText())
|
||||
service = str(dialog.serviceComboBox.currentText())
|
||||
port = None
|
||||
port2 = None
|
||||
|
||||
unmask = address.split("/", 1)
|
||||
if len(unmask) == 2:
|
||||
address = unmask[0]
|
||||
netmask = int(unmask[1])
|
||||
else:
|
||||
netmask = 32
|
||||
|
||||
if address == "*":
|
||||
address = "0.0.0.0"
|
||||
netmask = 0
|
||||
|
||||
if dialog.any_radio.isChecked():
|
||||
protocol = "any"
|
||||
port = 0
|
||||
else:
|
||||
if dialog.tcp_radio.isChecked():
|
||||
protocol = "tcp"
|
||||
elif dialog.udp_radio.isChecked():
|
||||
protocol = "udp"
|
||||
else:
|
||||
protocol = "any"
|
||||
|
||||
try:
|
||||
range = service.split("-", 1)
|
||||
if len(range) == 2:
|
||||
port = int(range[0])
|
||||
port2 = int(range[1])
|
||||
else:
|
||||
port = int(service)
|
||||
except (TypeError, ValueError) as ex:
|
||||
port = self.fw_model.get_service_port(service)
|
||||
|
||||
if port is not None:
|
||||
if port2 is not None and port2 <= port:
|
||||
QMessageBox.warning(None, self.tr("Invalid service ports range"),
|
||||
self.tr("Port {0} is lower than port {1}.").format(
|
||||
port2, port))
|
||||
else:
|
||||
item = {"address": address,
|
||||
"netmask": netmask,
|
||||
"portBegin": port,
|
||||
"portEnd": port2,
|
||||
"proto": protocol,
|
||||
}
|
||||
if row is not None:
|
||||
self.fw_model.setChild(row, item)
|
||||
else:
|
||||
self.fw_model.appendChild(item)
|
||||
else:
|
||||
QMessageBox.warning(None, self.tr("Invalid service name"),
|
||||
self.tr("Service '{0}' is unknown.").format(service))
|
||||
for i in set([index.row() for index
|
||||
in self.rulesTreeView.selectedIndexes()]):
|
||||
self.fw_model.remove_child(i)
|
||||
|
||||
|
||||
# Bases on the original code by:
|
||||
@ -986,7 +1048,7 @@ def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
|
||||
strace = ""
|
||||
stacktrace = traceback.extract_tb(exc_traceback)
|
||||
while len(stacktrace) > 0:
|
||||
while stacktrace:
|
||||
(filename, line, func, txt) = stacktrace.pop()
|
||||
strace += "----\n"
|
||||
strace += "line: %s\n" %txt
|
||||
@ -994,12 +1056,12 @@ def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
strace += "line no.: %d\n" %line
|
||||
strace += "file: %s\n" %filename
|
||||
|
||||
msg_box = QMessageBox()
|
||||
msg_box = QtGui.QMessageBox()
|
||||
msg_box.setDetailedText(strace)
|
||||
msg_box.setIcon(QMessageBox.Critical)
|
||||
msg_box.setIcon(QtGui.QMessageBox.Critical)
|
||||
msg_box.setWindowTitle("Houston, we have a problem...")
|
||||
msg_box.setText("Whoops. A critical error has occured. This is most likely a bug "
|
||||
"in Qubes Manager.<br><br>"
|
||||
msg_box.setText("Whoops. A critical error has occured. "
|
||||
"This is most likely a bug in Qubes Manager.<br><br>"
|
||||
"<b><i>%s</i></b>" % error +
|
||||
"<br/>at line <b>%d</b><br/>of file %s.<br/><br/>"
|
||||
% (line, filename))
|
||||
@ -1007,7 +1069,7 @@ def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
msg_box.exec_()
|
||||
|
||||
|
||||
parser = qubesadmin.tools.QubesArgumentParser(vmname_nargs=1)
|
||||
parser = QubesArgumentParser(vmname_nargs=1)
|
||||
|
||||
parser.add_argument('--tab', metavar='TAB',
|
||||
action='store',
|
||||
@ -1018,12 +1080,10 @@ parser.set_defaults(
|
||||
)
|
||||
|
||||
def main(args=None):
|
||||
global settings_window
|
||||
|
||||
args = parser.parse_args(args)
|
||||
vm = args.domains.pop()
|
||||
|
||||
qapp = QApplication(sys.argv)
|
||||
qapp = QtGui.QApplication(sys.argv)
|
||||
qapp.setOrganizationName('Invisible Things Lab')
|
||||
qapp.setOrganizationDomain("https://www.qubes-os.org/")
|
||||
qapp.setApplicationName("Qubes VM Settings")
|
||||
|
@ -1,9 +1,10 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
||||
# Copyright (C) 2014 Marek Marczykowski-Górecki
|
||||
# <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@ -15,61 +16,54 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
from PyQt4 import QtGui # pylint: disable=import-error
|
||||
from PyQt4 import QtCore # pylint: disable=import-error
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from PyQt4.QtCore import QSize, Qt
|
||||
|
||||
from PyQt4.QtGui import QTableWidgetItem, QHBoxLayout, QIcon, QLabel, QWidget, \
|
||||
QSizePolicy, QSpacerItem, QFont, QColor, QProgressBar, QPainter, QPen
|
||||
import time
|
||||
from qubes.qubes import vm_files
|
||||
import main
|
||||
|
||||
qubes_dom0_updates_stat_file = '/var/lib/qubes/updates/dom0-updates-available'
|
||||
power_order = Qt.DescendingOrder
|
||||
update_order = Qt.AscendingOrder
|
||||
power_order = QtCore.Qt.DescendingOrder
|
||||
update_order = QtCore.Qt.AscendingOrder
|
||||
|
||||
|
||||
row_height = 30
|
||||
|
||||
|
||||
class VmIconWidget (QWidget):
|
||||
class VmIconWidget(QtGui.QWidget):
|
||||
def __init__(self, icon_path, enabled=True, size_multiplier=0.7,
|
||||
tooltip=None, parent=None, icon_sz=(32, 32)):
|
||||
super(VmIconWidget, self).__init__(parent)
|
||||
|
||||
self.label_icon = QLabel()
|
||||
self.label_icon = QtGui.QLabel()
|
||||
if icon_path[0] in ':/':
|
||||
icon = QIcon (icon_path)
|
||||
icon = QtGui.QIcon(icon_path)
|
||||
else:
|
||||
icon = QIcon.fromTheme(icon_path)
|
||||
icon_sz = QSize (row_height * size_multiplier, row_height * size_multiplier)
|
||||
icon_pixmap = icon.pixmap(icon_sz, QIcon.Disabled if not enabled else QIcon.Normal)
|
||||
icon = QtGui.QIcon.fromTheme(icon_path)
|
||||
icon_sz = QtCore.QSize(row_height * size_multiplier,
|
||||
row_height * size_multiplier)
|
||||
icon_pixmap = icon.pixmap(
|
||||
icon_sz,
|
||||
QtGui.QIcon.Disabled if not enabled else QtGui.QIcon.Normal)
|
||||
self.label_icon.setPixmap(icon_pixmap)
|
||||
self.label_icon.setFixedSize(icon_sz)
|
||||
if tooltip != None:
|
||||
if tooltip is not None:
|
||||
self.label_icon.setToolTip(tooltip)
|
||||
|
||||
layout = QHBoxLayout()
|
||||
layout = QtGui.QHBoxLayout()
|
||||
layout.addWidget(self.label_icon)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.setLayout(layout)
|
||||
|
||||
def setToolTip(self, tooltip):
|
||||
def setToolTip(self, tooltip): # pylint: disable=invalid-name
|
||||
if tooltip is not None:
|
||||
self.label_icon.setToolTip(tooltip)
|
||||
else:
|
||||
self.label_icon.setToolTip('')
|
||||
|
||||
class VmTypeWidget(VmIconWidget):
|
||||
|
||||
class VmTypeItem(QTableWidgetItem):
|
||||
class VmTypeWidget(VmIconWidget):
|
||||
class VmTypeItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, value, vm):
|
||||
super(VmTypeWidget.VmTypeItem, self).__init__()
|
||||
self.value = value
|
||||
@ -84,40 +78,39 @@ class VmTypeWidget(VmIconWidget):
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.value == other.value:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return self.value < other.value
|
||||
|
||||
def __init__(self, vm, parent=None):
|
||||
(icon_path, tooltip) = self.get_vm_icon(vm)
|
||||
super (VmTypeWidget, self).__init__(icon_path, True, 0.8, tooltip, parent)
|
||||
super(VmTypeWidget, self).__init__(
|
||||
icon_path, True, 0.8, tooltip, parent)
|
||||
self.vm = vm
|
||||
self.tableItem = self.VmTypeItem(self.value, vm)
|
||||
self.table_item = self.VmTypeItem(self.value, vm)
|
||||
self.value = None
|
||||
|
||||
# TODO: add "provides network" column
|
||||
|
||||
def get_vm_icon(self, vm):
|
||||
if vm.qid == 0:
|
||||
if vm.klass == 'AdminVM':
|
||||
self.value = 0
|
||||
return (":/dom0.png", "Dom0")
|
||||
elif vm.is_netvm() and not vm.is_proxyvm():
|
||||
self.value = 1
|
||||
return (":/netvm.png", "NetVM")
|
||||
elif vm.is_proxyvm():
|
||||
self.value = 2
|
||||
return (":/proxyvm.png", "ProxyVM")
|
||||
elif vm.is_appvm() and vm.template is None:
|
||||
self.value = 4
|
||||
return (":/standalonevm.png", "StandaloneVM")
|
||||
elif vm.is_template():
|
||||
icon_name = "dom0"
|
||||
elif vm.klass == 'TemplateVM':
|
||||
self.value = 3
|
||||
return (":/templatevm.png", "TemplateVM")
|
||||
elif vm.is_appvm() or vm.is_disposablevm():
|
||||
icon_name = "templatevm"
|
||||
elif vm.klass == 'StandaloneVM':
|
||||
self.value = 4
|
||||
icon_name = "standalonevm"
|
||||
else:
|
||||
self.value = 5 + vm.label.index
|
||||
return (":/appvm.png", "AppVM")
|
||||
icon_name = "appvm"
|
||||
|
||||
return ":/" + icon_name + ".png", vm.klass
|
||||
|
||||
|
||||
class VmLabelWidget(VmIconWidget):
|
||||
|
||||
class VmLabelItem(QTableWidgetItem):
|
||||
class VmLabelItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, value, vm):
|
||||
super(VmLabelWidget.VmLabelItem, self).__init__()
|
||||
self.value = value
|
||||
@ -132,32 +125,27 @@ class VmLabelWidget(VmIconWidget):
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.value == other.value:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return self.value < other.value
|
||||
|
||||
def __init__(self, vm, parent=None):
|
||||
icon_path = self.get_vm_icon_path(vm)
|
||||
super(VmLabelWidget, self).__init__(icon_path, True, 0.8, None, parent)
|
||||
self.vm = vm
|
||||
self.tableItem = self.VmLabelItem(self.value, vm)
|
||||
self.table_item = self.VmLabelItem(self.value, vm)
|
||||
self.value = None
|
||||
|
||||
def get_vm_icon_path(self, vm):
|
||||
if vm.qid == 0:
|
||||
self.value = 100
|
||||
return ":/off.png"
|
||||
else:
|
||||
self.value = vm.label.index
|
||||
return vm.label.icon
|
||||
|
||||
|
||||
|
||||
class VmNameItem (QTableWidgetItem):
|
||||
class VmNameItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(VmNameItem, self).__init__()
|
||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.setText(vm.name)
|
||||
self.setTextAlignment(Qt.AlignVCenter)
|
||||
self.setTextAlignment(QtCore.Qt.AlignVCenter)
|
||||
self.qid = vm.qid
|
||||
|
||||
def __lt__(self, other):
|
||||
@ -168,44 +156,43 @@ class VmNameItem (QTableWidgetItem):
|
||||
return super(VmNameItem, self).__lt__(other)
|
||||
|
||||
|
||||
class VmStatusIcon(QLabel):
|
||||
class VmStatusIcon(QtGui.QLabel):
|
||||
def __init__(self, vm, parent=None):
|
||||
super(VmStatusIcon, self).__init__(parent)
|
||||
self.vm = vm
|
||||
self.set_on_icon()
|
||||
self.previous_power_state = vm.last_power_state
|
||||
self.previous_power_state = self.vm.get_power_state()
|
||||
|
||||
def update(self):
|
||||
if self.previous_power_state != self.vm.last_power_state:
|
||||
if self.previous_power_state != self.vm.get_power_state():
|
||||
self.set_on_icon()
|
||||
self.previous_power_state = self.vm.last_power_state
|
||||
self.previous_power_state = self.vm.get_power_state()
|
||||
|
||||
def set_on_icon(self):
|
||||
if self.vm.last_power_state == "Running":
|
||||
icon = QIcon (":/on.png")
|
||||
elif self.vm.last_power_state in ["Paused", "Suspended"]:
|
||||
icon = QIcon (":/paused.png")
|
||||
elif self.vm.last_power_state in ["Transient", "Halting", "Dying"]:
|
||||
icon = QIcon (":/transient.png")
|
||||
if self.vm.get_power_state() == "Running":
|
||||
icon = QtGui.QIcon(":/on.png")
|
||||
elif self.vm.get_power_state() in ["Paused", "Suspended"]:
|
||||
icon = QtGui.QIcon(":/paused.png")
|
||||
elif self.vm.get_power_state() in ["Transient", "Halting", "Dying"]:
|
||||
icon = QtGui.QIcon(":/transient.png")
|
||||
else:
|
||||
icon = QIcon (":/off.png")
|
||||
icon = QtGui.QIcon(":/off.png")
|
||||
|
||||
icon_sz = QSize (row_height * 0.5, row_height *0.5)
|
||||
icon_sz = QtCore.QSize(row_height * 0.5, row_height * 0.5)
|
||||
icon_pixmap = icon.pixmap(icon_sz)
|
||||
self.setPixmap(icon_pixmap)
|
||||
self.setFixedSize(icon_sz)
|
||||
|
||||
|
||||
|
||||
class VmInfoWidget (QWidget):
|
||||
|
||||
class VmInfoItem (QTableWidgetItem):
|
||||
class VmInfoWidget(QtGui.QWidget):
|
||||
class VmInfoItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, upd_info_item, vm):
|
||||
super(VmInfoWidget.VmInfoItem, self).__init__()
|
||||
self.upd_info_item = upd_info_item
|
||||
self.vm = vm
|
||||
|
||||
def __lt__(self, other):
|
||||
# pylint: disable=too-many-return-statements
|
||||
if self.vm.qid == 0:
|
||||
return True
|
||||
elif other.vm.qid == 0:
|
||||
@ -213,21 +200,25 @@ class VmInfoWidget (QWidget):
|
||||
|
||||
self_val = self.upd_info_item.value
|
||||
other_val = other.upd_info_item.value
|
||||
if self.tableWidget().horizontalHeader().sortIndicatorOrder() == update_order:
|
||||
|
||||
if self.tableWidget().\
|
||||
horizontalHeader().sortIndicatorOrder() == update_order:
|
||||
# the result will be sorted by upd, sorting order: Ascending
|
||||
self_val += 1 if self.vm.is_running() else 0
|
||||
other_val += 1 if other.vm.is_running() else 0
|
||||
if self_val == other_val:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return self_val > other_val
|
||||
elif self.tableWidget().horizontalHeader().sortIndicatorOrder() == power_order:
|
||||
#the result will be sorted by power state, sorting order: Descending
|
||||
self_val = -(self_val/10 + 10*(1 if self.vm.is_running() else 0))
|
||||
other_val = -(other_val/10 + 10*(1 if other.vm.is_running() else 0))
|
||||
elif self.tableWidget().\
|
||||
horizontalHeader().sortIndicatorOrder() == power_order:
|
||||
# the result will be sorted by power state,
|
||||
# sorting order: Descending
|
||||
self_val = -(self_val/10 +
|
||||
10*(1 if self.vm.is_running() else 0))
|
||||
other_val = -(other_val/10 +
|
||||
10*(1 if other.vm.is_running() else 0))
|
||||
if self_val == other_val:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return self_val > other_val
|
||||
else:
|
||||
# it would be strange if this happened
|
||||
@ -236,7 +227,7 @@ class VmInfoWidget (QWidget):
|
||||
def __init__(self, vm, parent=None):
|
||||
super(VmInfoWidget, self).__init__(parent)
|
||||
self.vm = vm
|
||||
layout = QHBoxLayout ()
|
||||
layout = QtGui.QHBoxLayout()
|
||||
|
||||
self.on_icon = VmStatusIcon(vm)
|
||||
self.upd_info = VmUpdateInfoWidget(vm, show_text=False)
|
||||
@ -247,7 +238,9 @@ class VmInfoWidget (QWidget):
|
||||
layout.addWidget(self.on_icon)
|
||||
layout.addWidget(self.upd_info)
|
||||
layout.addWidget(self.error_icon)
|
||||
layout.addItem(QSpacerItem(0, 10, QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding))
|
||||
layout.addItem(QtGui.QSpacerItem(0, 10,
|
||||
QtGui.QSizePolicy.Expanding,
|
||||
QtGui.QSizePolicy.Expanding))
|
||||
layout.addWidget(self.blk_icon)
|
||||
layout.addWidget(self.rec_icon)
|
||||
|
||||
@ -258,47 +251,30 @@ class VmInfoWidget (QWidget):
|
||||
self.blk_icon.setVisible(False)
|
||||
self.error_icon.setVisible(False)
|
||||
|
||||
self.tableItem = self.VmInfoItem(self.upd_info.tableItem, vm)
|
||||
self.table_item = self.VmInfoItem(self.upd_info.table_item, vm)
|
||||
|
||||
def update_vm_state(self, vm, blk_visible, rec_visible=None):
|
||||
def update_vm_state(self, vm):
|
||||
self.on_icon.update()
|
||||
self.upd_info.update_outdated(vm)
|
||||
if blk_visible != None:
|
||||
self.blk_icon.setVisible(blk_visible)
|
||||
if rec_visible != None:
|
||||
self.rec_icon.setVisible(rec_visible)
|
||||
self.error_icon.setToolTip(vm.qubes_manager_state[main.QMVmState
|
||||
.ErrorMsg])
|
||||
self.error_icon.setVisible(vm.qubes_manager_state[main.QMVmState
|
||||
.ErrorMsg] is not None)
|
||||
|
||||
|
||||
class VmTemplateItem (QTableWidgetItem):
|
||||
class VmTemplateItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(VmTemplateItem, self).__init__()
|
||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.vm = vm
|
||||
|
||||
if vm.template is not None:
|
||||
if getattr(vm, 'template', None) is not None:
|
||||
self.setText(vm.template.name)
|
||||
else:
|
||||
font = QFont()
|
||||
font.setStyle(QFont.StyleItalic)
|
||||
font = QtGui.QFont()
|
||||
font.setStyle(QtGui.QFont.StyleItalic)
|
||||
self.setFont(font)
|
||||
self.setTextColor(QColor("gray"))
|
||||
self.setTextColor(QtGui.QColor("gray"))
|
||||
|
||||
if vm.is_appvm(): # and vm.template is None
|
||||
self.setText("StandaloneVM")
|
||||
elif vm.is_template():
|
||||
self.setText("TemplateVM")
|
||||
elif vm.qid == 0:
|
||||
self.setText("AdminVM")
|
||||
elif vm.is_netvm():
|
||||
self.setText("NetVM")
|
||||
else:
|
||||
self.setText("---")
|
||||
self.setText(vm.klass)
|
||||
|
||||
self.setTextAlignment(Qt.AlignVCenter)
|
||||
self.setTextAlignment(QtCore.Qt.AlignVCenter)
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.vm.qid == 0:
|
||||
@ -306,27 +282,22 @@ class VmTemplateItem (QTableWidgetItem):
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.text() == other.text():
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return super(VmTemplateItem, self).__lt__(other)
|
||||
|
||||
|
||||
|
||||
|
||||
class VmNetvmItem (QTableWidgetItem):
|
||||
class VmNetvmItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(VmNetvmItem, self).__init__()
|
||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.vm = vm
|
||||
|
||||
if vm.is_netvm() and not vm.is_proxyvm():
|
||||
if getattr(vm, 'netvm', None) is None:
|
||||
self.setText("n/a")
|
||||
elif vm.netvm is not None:
|
||||
self.setText(vm.netvm.name)
|
||||
else:
|
||||
self.setText("---")
|
||||
self.setText(vm.netvm.name)
|
||||
|
||||
self.setTextAlignment(Qt.AlignVCenter)
|
||||
self.setTextAlignment(QtCore.Qt.AlignVCenter)
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.vm.qid == 0:
|
||||
@ -334,22 +305,19 @@ class VmNetvmItem (QTableWidgetItem):
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.text() == other.text():
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return super(VmNetvmItem, self).__lt__(other)
|
||||
|
||||
class VmInternalItem(QTableWidgetItem):
|
||||
|
||||
class VmInternalItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(VmInternalItem, self).__init__()
|
||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
|
||||
self.vm = vm
|
||||
self.internal = self.vm.internal
|
||||
self.internal = vm.features.get('internal', False)
|
||||
|
||||
if self.internal:
|
||||
self.setText("Yes")
|
||||
else:
|
||||
self.setText("")
|
||||
self.setText("Yes" if self.internal else "")
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.vm.qid == 0:
|
||||
@ -359,144 +327,10 @@ class VmInternalItem(QTableWidgetItem):
|
||||
return super(VmInternalItem, self).__lt__(other)
|
||||
|
||||
|
||||
class VmUsageBarWidget (QWidget):
|
||||
# features man qvm-features
|
||||
class VmUpdateInfoWidget(QtGui.QWidget):
|
||||
|
||||
class VmUsageBarItem (QTableWidgetItem):
|
||||
def __init__(self, value, vm):
|
||||
super(VmUsageBarWidget.VmUsageBarItem, self).__init__()
|
||||
self.value = value
|
||||
self.vm = vm
|
||||
|
||||
def set_value(self, value):
|
||||
self.value = value
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.vm.qid == 0:
|
||||
return True
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.value == other.value:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return int(self.value) < int(other.value)
|
||||
|
||||
def __init__(self, min, max, format, update_func, vm, load, hue=210, parent = None):
|
||||
super (VmUsageBarWidget, self).__init__(parent)
|
||||
|
||||
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.update_func = update_func
|
||||
self.value = min
|
||||
|
||||
self.widget = QProgressBar()
|
||||
self.widget.setMinimum(min)
|
||||
self.widget.setMaximum(max)
|
||||
self.widget.setFormat(format)
|
||||
|
||||
self.widget.setStyleSheet(
|
||||
"QProgressBar:horizontal{" +\
|
||||
"border: 1px solid hsv({0}, 100, 250);".format(hue) +\
|
||||
"border-radius: 4px;\
|
||||
background: transparent;\
|
||||
text-align: center;\
|
||||
}\
|
||||
QProgressBar::chunk:horizontal {\
|
||||
background: qlineargradient(x1: 1, y1: 0.5, x2: 1, y2: 0.5, " +\
|
||||
"stop: 0 hsv({0}, 170, 207),".format(hue) +
|
||||
" stop: 1 white); \
|
||||
}"
|
||||
)
|
||||
|
||||
layout = QHBoxLayout()
|
||||
layout.addWidget(self.widget)
|
||||
|
||||
self.setLayout(layout)
|
||||
self.tableItem = self.VmUsageBarItem(min, vm)
|
||||
|
||||
self.update_load(vm, load)
|
||||
|
||||
|
||||
|
||||
def update_load(self, vm, load):
|
||||
self.value = self.update_func(vm, load)
|
||||
self.widget.setValue(self.value)
|
||||
self.tableItem.set_value(self.value)
|
||||
|
||||
class ChartWidget (QWidget):
|
||||
|
||||
class ChartItem (QTableWidgetItem):
|
||||
def __init__(self, value, vm):
|
||||
super(ChartWidget.ChartItem, self).__init__()
|
||||
self.value = value
|
||||
self.vm = vm
|
||||
|
||||
def set_value(self, value):
|
||||
self.value = value
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.vm.qid == 0:
|
||||
return True
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.value == other.value:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.value < other.value
|
||||
|
||||
def __init__(self, vm, update_func, hue, load = 0, parent = None):
|
||||
super (ChartWidget, self).__init__(parent)
|
||||
self.update_func = update_func
|
||||
self.hue = hue
|
||||
if hue < 0 or hue > 255:
|
||||
self.hue = 255
|
||||
self.load = load
|
||||
assert self.load >= 0 and self.load <= 100, "load = {0}".format(self.load)
|
||||
self.load_history = [self.load]
|
||||
self.tableItem = ChartWidget.ChartItem(self.load, vm)
|
||||
|
||||
def update_load (self, vm, load):
|
||||
self.load = self.update_func(vm, load)
|
||||
|
||||
assert self.load >= 0, "load = {0}".format(self.load)
|
||||
# assert self.load >= 0 and self.load <= 100, "load = {0}".format(self.load)
|
||||
if self.load > 100:
|
||||
# FIXME: This is an ugly workaround for cpu_load:/
|
||||
self.load = 100
|
||||
|
||||
self.load_history.append (self.load)
|
||||
self.tableItem.set_value(self.load)
|
||||
self.repaint()
|
||||
|
||||
def paintEvent (self, Event = None):
|
||||
p = QPainter (self)
|
||||
dx = 4
|
||||
|
||||
W = self.width()
|
||||
H = self.height() - 5
|
||||
N = len(self.load_history)
|
||||
if N > W/dx:
|
||||
tail = N - W/dx
|
||||
N = W/dx
|
||||
self.load_history = self.load_history[tail:]
|
||||
|
||||
assert len(self.load_history) == N
|
||||
|
||||
for i in range (0, N-1):
|
||||
val = self.load_history[N- i - 1]
|
||||
sat = 70 + val*(255-70)/100
|
||||
color = QColor.fromHsv (self.hue, sat, 255)
|
||||
pen = QPen (color)
|
||||
pen.setWidth(dx-1)
|
||||
p.setPen(pen)
|
||||
if val > 0:
|
||||
p.drawLine (W - i*dx - dx, H , W - i*dx - dx, H - (H - 5) * val/100)
|
||||
|
||||
|
||||
|
||||
class VmUpdateInfoWidget(QWidget):
|
||||
|
||||
class VmUpdateInfoItem (QTableWidgetItem):
|
||||
class VmUpdateInfoItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, value, vm):
|
||||
super(VmUpdateInfoWidget.VmUpdateInfoItem, self).__init__()
|
||||
self.value = 0
|
||||
@ -517,91 +351,53 @@ class VmUpdateInfoWidget(QWidget):
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.value == other.value:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return self.value < other.value
|
||||
|
||||
def __init__(self, vm, show_text=True, parent=None):
|
||||
super(VmUpdateInfoWidget, self).__init__(parent)
|
||||
layout = QHBoxLayout ()
|
||||
layout = QtGui.QHBoxLayout()
|
||||
self.show_text = show_text
|
||||
if self.show_text:
|
||||
self.label=QLabel("")
|
||||
layout.addWidget(self.label, alignment=Qt.AlignCenter)
|
||||
self.label = QtGui.QLabel("")
|
||||
layout.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
|
||||
else:
|
||||
self.icon = QLabel("")
|
||||
layout.addWidget(self.icon, alignment=Qt.AlignCenter)
|
||||
self.icon = QtGui.QLabel("")
|
||||
layout.addWidget(self.icon, alignment=QtCore.Qt.AlignCenter)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.previous_outdated_state = None
|
||||
self.previous_update_recommended = None
|
||||
self.value = None
|
||||
self.tableItem = VmUpdateInfoWidget.VmUpdateInfoItem(self.value, vm)
|
||||
self.table_item = VmUpdateInfoWidget.VmUpdateInfoItem(self.value, vm)
|
||||
|
||||
def update_outdated(self, vm):
|
||||
if vm.type == "HVM":
|
||||
return
|
||||
|
||||
if vm.is_outdated():
|
||||
outdated_state = False
|
||||
|
||||
try:
|
||||
for vol in vm.volumes:
|
||||
if vol.is_outdated():
|
||||
outdated_state = "outdated"
|
||||
# During TemplateVM shutdown, there's an interval of a few seconds
|
||||
# during which vm.template.is_running() returns false but
|
||||
# vm.is_outdated() does not yet return true, so the icon disappears.
|
||||
# This looks goofy, but we've decided not to fix it at this time
|
||||
# (2015-02-09).
|
||||
elif vm.template and vm.template.is_running():
|
||||
outdated_state = "to-be-outdated"
|
||||
else:
|
||||
outdated_state = None
|
||||
break
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if not outdated_state and getattr(vm, 'template', None)\
|
||||
and vm.template.is_running():
|
||||
outdated_state = "to-be-outdated"
|
||||
if outdated_state != self.previous_outdated_state:
|
||||
self.update_status_widget(outdated_state)
|
||||
|
||||
self.previous_outdated_state = outdated_state
|
||||
|
||||
if not vm.is_updateable():
|
||||
return
|
||||
|
||||
if vm.qid == 0:
|
||||
update_recommended = self.previous_update_recommended
|
||||
if os.path.exists(qubes_dom0_updates_stat_file):
|
||||
update_recommended = True
|
||||
else:
|
||||
update_recommended = False
|
||||
|
||||
else:
|
||||
update_recommended = self.previous_update_recommended
|
||||
stat_file_path = vm.dir_path + '/' + vm_files["updates_stat_file"]
|
||||
if not os.path.exists(stat_file_path):
|
||||
update_recommended = False
|
||||
else:
|
||||
if (not hasattr(vm, "updates_stat_file_read_time")) or vm.updates_stat_file_read_time <= os.path.getmtime(stat_file_path):
|
||||
|
||||
stat_file = open(stat_file_path, "r")
|
||||
updates = stat_file.read().strip()
|
||||
stat_file.close()
|
||||
if updates.isdigit():
|
||||
updates = int(updates)
|
||||
else:
|
||||
updates = 0
|
||||
|
||||
if updates == 0:
|
||||
update_recommended = False
|
||||
else:
|
||||
update_recommended = True
|
||||
vm.updates_stat_file_read_time = time.time()
|
||||
|
||||
if update_recommended and not self.previous_update_recommended:
|
||||
self.update_status_widget("update")
|
||||
elif self.previous_update_recommended and not update_recommended:
|
||||
self.update_status_widget(None)
|
||||
|
||||
self.previous_update_recommended = update_recommended
|
||||
|
||||
updates_available = vm.features.get('updates-available', False)
|
||||
if updates_available != self.previous_update_recommended:
|
||||
self.update_status_widget("update" if updates_available else None)
|
||||
self.previous_update_recommended = updates_available
|
||||
|
||||
def update_status_widget(self, state):
|
||||
self.value = state
|
||||
self.tableItem.set_value(state)
|
||||
self.table_item.set_value(state)
|
||||
if state == "update":
|
||||
label_text = "<font color=\"#CCCC00\">Check updates</font>"
|
||||
icon_path = ":/update-recommended.png"
|
||||
@ -618,7 +414,7 @@ class VmUpdateInfoWidget(QWidget):
|
||||
tooltip_text = self.tr(
|
||||
"The TemplateVM must be stopped before changes from its "
|
||||
"current session can be picked up by this VM.")
|
||||
elif state is None:
|
||||
else:
|
||||
label_text = ""
|
||||
icon_path = None
|
||||
tooltip_text = None
|
||||
@ -632,25 +428,26 @@ class VmUpdateInfoWidget(QWidget):
|
||||
self.icon = VmIconWidget(icon_path, True, 0.7)
|
||||
self.icon.setToolTip(tooltip_text)
|
||||
else:
|
||||
self.icon = QLabel(label_text)
|
||||
self.layout().addWidget(self.icon, alignment=Qt.AlignCenter)
|
||||
self.icon = QtGui.QLabel(label_text)
|
||||
self.layout().addWidget(self.icon, alignment=QtCore.Qt.AlignCenter)
|
||||
|
||||
|
||||
class VmSizeOnDiskItem (QTableWidgetItem):
|
||||
class VmSizeOnDiskItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(VmSizeOnDiskItem, self).__init__()
|
||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
|
||||
self.vm = vm
|
||||
self.value = 0
|
||||
self.update()
|
||||
self.setTextAlignment(Qt.AlignVCenter)
|
||||
self.setTextAlignment(QtCore.Qt.AlignVCenter)
|
||||
|
||||
def update(self):
|
||||
if self.vm.qid == 0:
|
||||
self.setText("n/a")
|
||||
else:
|
||||
self.value = self.vm.get_disk_utilization()/(1024*1024)
|
||||
self.value = 10
|
||||
self.value = round(self.vm.get_disk_utilization()/(1024*1024), 2)
|
||||
self.setText(str(self.value) + " MiB")
|
||||
|
||||
def __lt__(self, other):
|
||||
@ -659,21 +456,18 @@ class VmSizeOnDiskItem (QTableWidgetItem):
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.value == other.value:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return self.value < other.value
|
||||
|
||||
class VmIPItem(QTableWidgetItem):
|
||||
|
||||
class VmIPItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(VmIPItem, self).__init__()
|
||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
|
||||
self.vm = vm
|
||||
self.ip = self.vm.ip
|
||||
if self.ip:
|
||||
self.setText(self.ip)
|
||||
else:
|
||||
self.setText("n/a")
|
||||
self.ip = getattr(self.vm, 'ip', None)
|
||||
self.setText(self.ip if self.ip is not None else 'n/a')
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.vm.qid == 0:
|
||||
@ -682,13 +476,14 @@ class VmIPItem(QTableWidgetItem):
|
||||
return False
|
||||
return super(VmIPItem, self).__lt__(other)
|
||||
|
||||
class VmIncludeInBackupsItem(QTableWidgetItem):
|
||||
|
||||
class VmIncludeInBackupsItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(VmIncludeInBackupsItem, self).__init__()
|
||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
|
||||
self.vm = vm
|
||||
if self.vm.include_in_backups:
|
||||
if getattr(self.vm, 'include_in_backups', None):
|
||||
self.setText("Yes")
|
||||
else:
|
||||
self.setText("")
|
||||
@ -699,18 +494,18 @@ class VmIncludeInBackupsItem(QTableWidgetItem):
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.vm.include_in_backups == other.vm.include_in_backups:
|
||||
return self.vm.qid < other.vm.qid
|
||||
else:
|
||||
return self.vm.name < other.vm.name
|
||||
return self.vm.include_in_backups < other.vm.include_in_backups
|
||||
|
||||
class VmLastBackupItem(QTableWidgetItem):
|
||||
|
||||
class VmLastBackupItem(QtGui.QTableWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(VmLastBackupItem, self).__init__()
|
||||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
|
||||
self.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
|
||||
self.vm = vm
|
||||
if self.vm.backup_timestamp:
|
||||
self.setText(str(self.vm.backup_timestamp.date()))
|
||||
if getattr(self.vm, 'backup_timestamp', None):
|
||||
self.setText(self.vm.backup_timestamp)
|
||||
else:
|
||||
self.setText("")
|
||||
|
||||
@ -720,10 +515,9 @@ class VmLastBackupItem(QTableWidgetItem):
|
||||
elif other.vm.qid == 0:
|
||||
return False
|
||||
elif self.vm.backup_timestamp == other.vm.backup_timestamp:
|
||||
return self.vm.qid < other.vm.qid
|
||||
return self.vm.name < other.vm.name
|
||||
elif not self.vm.backup_timestamp:
|
||||
return False
|
||||
elif not other.vm.backup_timestamp:
|
||||
return True
|
||||
else:
|
||||
return self.vm.backup_timestamp < other.vm.backup_timestamp
|
||||
|
@ -14,18 +14,17 @@
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
import PyQt4.QtCore # pylint: disable=import-error
|
||||
|
||||
import threading
|
||||
|
||||
class ThreadMonitor(QObject):
|
||||
class ThreadMonitor(PyQt4.QtCore.QObject):
|
||||
def __init__(self):
|
||||
self.success = True
|
||||
self.error_msg = None
|
||||
@ -41,4 +40,3 @@ class ThreadMonitor(QObject):
|
||||
|
||||
def set_finished(self):
|
||||
self.event_finished.set()
|
||||
|
||||
|
@ -20,12 +20,11 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import qubesadmin
|
||||
|
||||
from PyQt4.QtGui import QIcon
|
||||
from PyQt4.QtGui import QIcon # pylint: disable=import-error
|
||||
|
||||
def _filter_internal(vm):
|
||||
return (not vm.klass == 'AdminVM'
|
||||
@ -153,11 +152,11 @@ def get_path_from_vm(vm, service_name):
|
||||
|
||||
if not vm:
|
||||
return None
|
||||
stdout, stderr = vm.run_service_for_stdio(service_name)
|
||||
stdout, _stderr = vm.run_service_for_stdio(service_name)
|
||||
|
||||
untrusted_path = stdout.decode(encoding='ascii')[:path_max_len]
|
||||
|
||||
if len(untrusted_path) == 0:
|
||||
if not untrusted_path:
|
||||
return None
|
||||
if path_re.match(untrusted_path):
|
||||
assert '../' not in untrusted_path
|
||||
|
@ -43,7 +43,9 @@ cp qubesmanager/qvm_about.sh $RPM_BUILD_ROOT/usr/libexec/qubes-manager/
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/share/applications
|
||||
cp qubes-global-settings.desktop $RPM_BUILD_ROOT/usr/share/applications/
|
||||
cp qubes-vm-create.desktop $RPM_BUILD_ROOT/usr/share/applications/
|
||||
|
||||
cp qubes-backup.desktop $RPM_BUILD_ROOT/usr/share/applications/
|
||||
cp qubes-backup-restore.desktop $RPM_BUILD_ROOT/usr/share/applications/
|
||||
cp qubes-qube-manager.desktop $RPM_BUILD_ROOT/usr/share/applications/
|
||||
|
||||
%post
|
||||
update-desktop-database &> /dev/null || :
|
||||
@ -60,6 +62,9 @@ rm -rf $RPM_BUILD_ROOT
|
||||
/usr/bin/qubes-vm-settings
|
||||
/usr/bin/qubes-vm-create
|
||||
/usr/bin/qubes-vm-boot-from-device
|
||||
/usr/bin/qubes-backup
|
||||
/usr/bin/qubes-backup-restore
|
||||
/usr/bin/qubes-qube-manager
|
||||
/usr/libexec/qubes-manager/mount_for_backup.sh
|
||||
/usr/libexec/qubes-manager/qvm_about.sh
|
||||
|
||||
@ -83,6 +88,7 @@ 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/qube_manager.py
|
||||
%{python3_sitelib}/qubesmanager/utils.py
|
||||
%{python3_sitelib}/qubesmanager/bootfromdevice.py
|
||||
|
||||
@ -100,6 +106,7 @@ rm -rf $RPM_BUILD_ROOT
|
||||
%{python3_sitelib}/qubesmanager/ui_about.py
|
||||
%{python3_sitelib}/qubesmanager/ui_releasenotes.py
|
||||
%{python3_sitelib}/qubesmanager/ui_informationnotes.py
|
||||
%{python3_sitelib}/qubesmanager/ui_qubemanager.py
|
||||
%{python3_sitelib}/qubesmanager/i18n/qubesmanager_*.qm
|
||||
%{python3_sitelib}/qubesmanager/i18n/qubesmanager_*.ts
|
||||
|
||||
@ -108,3 +115,6 @@ rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
/usr/share/applications/qubes-global-settings.desktop
|
||||
/usr/share/applications/qubes-vm-create.desktop
|
||||
/usr/share/applications/qubes-backup.desktop
|
||||
/usr/share/applications/qubes-backup-restore.desktop
|
||||
/usr/share/applications/qubes-qube-manager.desktop
|
||||
|
5
setup.py
5
setup.py
@ -21,6 +21,9 @@ if __name__ == '__main__':
|
||||
'qubes-global-settings = qubesmanager.global_settings:main',
|
||||
'qubes-vm-settings = qubesmanager.settings:main',
|
||||
'qubes-vm-create = qubesmanager.create_new_vm:main',
|
||||
'qubes-vm-boot-from-device = qubesmanager.bootfromdevice:main'
|
||||
'qubes-vm-boot-from-device = qubesmanager.bootfromdevice:main',
|
||||
'qubes-backup = qubesmanager.backup:main',
|
||||
'qubes-backup-restore = qubesmanager.restore:main',
|
||||
'qubes-qube-manager = qubesmanager.qube_manager:main'
|
||||
],
|
||||
})
|
||||
|
5
test-packages/qubes/backup/__init__.py
Normal file
5
test-packages/qubes/backup/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
class BackupCanceledError(BaseException):
|
||||
|
||||
tmpdir = None
|
||||
|
||||
pass
|
1
test-packages/qubes/storage/__init__.py
Normal file
1
test-packages/qubes/storage/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
pass
|
2
test-packages/qubes/storage/file.py
Normal file
2
test-packages/qubes/storage/file.py
Normal file
@ -0,0 +1,2 @@
|
||||
def get_disk_usage(*args):
|
||||
return 0
|
4
test-packages/qubesadmin/__init__.py
Normal file
4
test-packages/qubesadmin/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
class Qubes(object):
|
||||
pass
|
||||
|
||||
DEFAULT = object()
|
1
test-packages/qubesadmin/backup/__init__.py
Normal file
1
test-packages/qubesadmin/backup/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
pass
|
19
test-packages/qubesadmin/backup/restore/__init__.py
Normal file
19
test-packages/qubesadmin/backup/restore/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
class BackupRestore(object):
|
||||
|
||||
options = object()
|
||||
canceled = None
|
||||
|
||||
def get_restore_info(self, *args):
|
||||
pass
|
||||
|
||||
def restore_do(self, *args):
|
||||
pass
|
||||
|
||||
def get_restore_info(self, *args):
|
||||
pass
|
||||
|
||||
def restore_info_verify(self, *args):
|
||||
pass
|
||||
|
||||
def get_restore_summary(self, *args):
|
||||
pass
|
4
test-packages/qubesadmin/devices.py
Normal file
4
test-packages/qubesadmin/devices.py
Normal file
@ -0,0 +1,4 @@
|
||||
### mock qubesadmin.devices module
|
||||
|
||||
class DeviceAssignment(object):
|
||||
pass
|
5
test-packages/qubesadmin/events.py
Normal file
5
test-packages/qubesadmin/events.py
Normal file
@ -0,0 +1,5 @@
|
||||
class EventsDispatcher(object):
|
||||
|
||||
def add_handler(self, *args):
|
||||
pass
|
||||
pass
|
6
test-packages/qubesadmin/exc.py
Normal file
6
test-packages/qubesadmin/exc.py
Normal file
@ -0,0 +1,6 @@
|
||||
### mock qubesadmin.exc module
|
||||
# pylint: disable=unused-variable
|
||||
|
||||
|
||||
class QubesException(BaseException):
|
||||
pass
|
4
test-packages/qubesadmin/firewall.py
Normal file
4
test-packages/qubesadmin/firewall.py
Normal file
@ -0,0 +1,4 @@
|
||||
### mock qubesadmin.firewall module
|
||||
|
||||
class Rule(object):
|
||||
pass
|
9
test-packages/qubesadmin/tools/__init__.py
Normal file
9
test-packages/qubesadmin/tools/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
### mock qubesadmin.tools module
|
||||
|
||||
class QubesArgumentParser(object):
|
||||
|
||||
def add_argument(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def set_defaults(self, *args, **kwargs):
|
||||
pass
|
4
test-packages/qubesadmin/tools/qvm_start.py
Normal file
4
test-packages/qubesadmin/tools/qvm_start.py
Normal file
@ -0,0 +1,4 @@
|
||||
### mock qvm_start module
|
||||
|
||||
def main(*args, **kwargs):
|
||||
pass
|
10
test-packages/qubesadmin/utils.py
Normal file
10
test-packages/qubesadmin/utils.py
Normal file
@ -0,0 +1,10 @@
|
||||
### mock qubesadmin.utils module
|
||||
|
||||
def parse_size(*args, **kwargs):
|
||||
return args[0]
|
||||
|
||||
def updates_vms_status(*args, **kwargs):
|
||||
return args[0]
|
||||
|
||||
def size_to_human(*args, **kwargs):
|
||||
return args[0]
|
242
ui/backupdlg.ui
242
ui/backupdlg.ui
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>650</width>
|
||||
<height>399</height>
|
||||
<width>737</width>
|
||||
<height>420</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -23,73 +23,8 @@
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="shutdown_running_vms_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Shutdown all running selected VMs</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/shutdownvm.png</normaloff>:/shutdownvm.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="refresh_button">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh running states.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="running_vms_warning">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color:rgb(255, 0, 0)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Some of the selected VMs are running (red). Running VMs cannot be backed up!</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
<weight>50</weight>
|
||||
<italic>false</italic>
|
||||
<bold>false</bold>
|
||||
<underline>false</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select VMs to backup:</string>
|
||||
</property>
|
||||
@ -145,12 +80,79 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="compress_checkbox">
|
||||
<property name="text">
|
||||
<string>Compress the backup</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="unrecognized_config_label">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>139</red>
|
||||
<green>142</green>
|
||||
<blue>142</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Warning: unrecognized data found in configuration files. </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWizardPage" name="select_dir_page">
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Backup destination directory</string>
|
||||
</property>
|
||||
@ -188,30 +190,38 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<item row="3" column="0">
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Backup security</string>
|
||||
<string>Save backup profile</string>
|
||||
</property>
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>30</y>
|
||||
<width>267</width>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Encryption / Verification<br/>passphrase:</p></body></html></string>
|
||||
<string>Save settings as default backup profile:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Encrypt backup:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="encryption_checkbox">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
<widget class="QCheckBox" name="save_profile_checkbox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>270</x>
|
||||
<y>30</y>
|
||||
<width>539</width>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
@ -220,39 +230,64 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Backup security</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Reenter passphrase:</p></body></html></string>
|
||||
<string><html><head/><body><p>Encryption / Verification<br/>passphrase:</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="passphrase_line_edit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Reenter passphrase:</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="passphrase_line_edit_verify">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><b>Note:</b> Even if you choose not to encrypt your backup, a password is still required in order to provide authenticated verification of the data. See the emergency restore instructions for more info.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>NOTE: Only running VMs are listed.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@ -281,8 +316,8 @@
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string>
|
||||
</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -324,6 +359,9 @@ p, li { white-space: pre-wrap; }
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progress_bar">
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -31,10 +31,22 @@
|
||||
<property name="verticalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<item row="2" column="2">
|
||||
<widget class="QRadioButton" name="udp_radio">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Protocol</string>
|
||||
<string>UDP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -45,13 +57,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="serviceComboBox">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
@ -59,6 +64,31 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QRadioButton" name="any_radio">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>71</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Any </string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="addressComboBox">
|
||||
<property name="editable">
|
||||
@ -66,6 +96,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="serviceComboBox">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QRadioButton" name="tcp_radio">
|
||||
<property name="sizePolicy">
|
||||
@ -85,44 +122,10 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QRadioButton" name="udp_radio">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>UDP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QRadioButton" name="any_radio">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>71</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Any </string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<string>Protocol</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
841
ui/qubemanager.ui
Normal file
841
ui/qubemanager.ui
Normal file
@ -0,0 +1,841 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VmManagerWindow</class>
|
||||
<widget class="QMainWindow" name="VmManagerWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>769</width>
|
||||
<height>385</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Qube Manager</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset theme="qubes-manager">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="locale">
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="locale">
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="searchContainer">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Search:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QTableWidget" name="table">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="sizeIncrement">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="gridStyle">
|
||||
<enum>Qt::NoPen</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="rowCount">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>150</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>150</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<row>
|
||||
<property name="text">
|
||||
<string>Nowy wiersz</string>
|
||||
</property>
|
||||
</row>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<row/>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>VM name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>State</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Update info</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Template</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>VM's template</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>NetVM</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>VM's netVM</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Size</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Internal</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>IP</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Backups</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Last backup</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>769</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_system">
|
||||
<property name="title">
|
||||
<string>&System</string>
|
||||
</property>
|
||||
<addaction name="action_global_settings"/>
|
||||
<addaction name="action_show_network"/>
|
||||
<addaction name="action_backup"/>
|
||||
<addaction name="action_restore"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_view">
|
||||
<property name="title">
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<addaction name="action_vm_type"/>
|
||||
<addaction name="action_label"/>
|
||||
<addaction name="action_name"/>
|
||||
<addaction name="action_state"/>
|
||||
<addaction name="action_template"/>
|
||||
<addaction name="action_netvm"/>
|
||||
<addaction name="action_size_on_disk"/>
|
||||
<addaction name="action_internal"/>
|
||||
<addaction name="action_ip"/>
|
||||
<addaction name="action_backups"/>
|
||||
<addaction name="action_last_backup"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_toolbar"/>
|
||||
<addaction name="action_menubar"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_search"/>
|
||||
<addaction name="action_refresh_list"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_vm">
|
||||
<property name="title">
|
||||
<string>&Qube</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="logs_menu">
|
||||
<property name="title">
|
||||
<string>&Logs</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/log.png</normaloff>:/log.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="blk_menu">
|
||||
<property name="title">
|
||||
<string>Attach/detach &block devices</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/mount.png</normaloff>:/mount.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="action_createvm"/>
|
||||
<addaction name="action_removevm"/>
|
||||
<addaction name="action_clonevm"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_resumevm"/>
|
||||
<addaction name="action_pausevm"/>
|
||||
<addaction name="action_shutdownvm"/>
|
||||
<addaction name="action_restartvm"/>
|
||||
<addaction name="action_killvm"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_settings"/>
|
||||
<addaction name="action_editfwrules"/>
|
||||
<addaction name="action_appmenus"/>
|
||||
<addaction name="action_updatevm"/>
|
||||
<addaction name="action_run_command_in_vm"/>
|
||||
<addaction name="action_set_keyboard_layout"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="logs_menu"/>
|
||||
<addaction name="blk_menu"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_about">
|
||||
<property name="title">
|
||||
<string>&About</string>
|
||||
</property>
|
||||
<addaction name="action_about_qubes"/>
|
||||
</widget>
|
||||
<addaction name="menu_system"/>
|
||||
<addaction name="menu_vm"/>
|
||||
<addaction name="menu_view"/>
|
||||
<addaction name="menu_about"/>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="toolbar">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<property name="allowedAreas">
|
||||
<set>Qt::BottomToolBarArea|Qt::TopToolBarArea</set>
|
||||
</property>
|
||||
<property name="floatable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="action_createvm"/>
|
||||
<addaction name="action_removevm"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_resumevm"/>
|
||||
<addaction name="action_pausevm"/>
|
||||
<addaction name="action_shutdownvm"/>
|
||||
<addaction name="action_restartvm"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_settings"/>
|
||||
<addaction name="action_editfwrules"/>
|
||||
<addaction name="action_appmenus"/>
|
||||
<addaction name="action_updatevm"/>
|
||||
<addaction name="action_set_keyboard_layout"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_global_settings"/>
|
||||
<addaction name="action_backup"/>
|
||||
<addaction name="action_restore"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_refresh_list"/>
|
||||
</widget>
|
||||
<action name="action_createvm">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/createvm.png</normaloff>:/createvm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create &New Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create a new Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_removevm">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/removevm.png</normaloff>:/removevm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Delete Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove an existing Qube (must be stopped first)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_resumevm">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/resumevm.png</normaloff>:/resumevm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start/Resu&me Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Start/Resume selected Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_pausevm">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/pausevm.png</normaloff>:/pausevm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Pause Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Pause selected Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_shutdownvm">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/shutdownvm.png</normaloff>:/shutdownvm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Shutdown Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Shutdown selected Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_restartvm">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/restartvm.png</normaloff>:/restartvm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Restar&t Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Restart selected Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_appmenus">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/apps.png</normaloff>:/apps.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add/remove app s&hortcuts</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add/remove app shortcuts for this Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_updatevm">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/updateable.png</normaloff>:/updateable.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Update Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Update Qube system</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_editfwrules">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/firewall.png</normaloff>:/firewall.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit Qube &firewall rules</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Edit Qube firewall rules</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_showgraphs">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/showcpuload.png</normaloff>:/showcpuload.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show graphs</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show Graphs</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_options">
|
||||
<property name="text">
|
||||
<string>Options</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_view">
|
||||
<property name="text">
|
||||
<string>View</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_template">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Template</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_netvm">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&NetVM</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_settings">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/settings.png</normaloff>:/settings.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Qube s&ettings</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Qube Settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_restore">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/restore.png</normaloff>:/restore.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Restore Qubes from backup</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Restore Qubes from backup</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_backup">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/backup.png</normaloff>:/backup.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Backup Qubes</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Backup Qubes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_global_settings">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/global-settings.png</normaloff>:/global-settings.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Global settings</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_show_network">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/networking.png</normaloff>:/networking.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Qubes Network</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_state">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&State</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_killvm">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/killvm.png</normaloff>:/killvm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Kill Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Kill selected Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_set_keyboard_layout">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/kbd-layout.png</normaloff>:/kbd-layout.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set keyboard la&yout</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Set keyboard layout per Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_vm_type">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>T&ype</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Qube Type</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_label">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Label</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_name">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>N&ame</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_toolbar">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show tool bar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_menubar">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show menu bar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_about_qubes">
|
||||
<property name="icon">
|
||||
<iconset theme="qubes-manager">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Qubes OS</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_size_on_disk">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Si&ze</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Size on Disk</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_run_command_in_vm">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/run-command.png</normaloff>:/run-command.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Run command in Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Run command in the specified Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_clonevm">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/templatevm.png</normaloff>:/templatevm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Clone Qube</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Clone Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_internal">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Inte&rnal</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Is an internal Qube</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_startvm_tools_install">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/resumevm.png</normaloff>:/resumevm.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start Qube for Window Tools installation</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Start Qube for Window Tools installation</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_ip">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&IP</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_backups">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Include in &backups</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_last_backup">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Last back&up</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_search">
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_refresh_list">
|
||||
<property name="icon">
|
||||
<iconset resource="../resources.qrc">
|
||||
<normaloff>:/outdated.png</normaloff>:/outdated.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh Qube List</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Refresh Qube List</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
@ -52,7 +52,7 @@
|
||||
<string>Ignore missing templates or netvms, restore VMs anyway.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ignore missing</string>
|
||||
<string>ignore missing templates and net VMs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -147,6 +147,9 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
@ -211,8 +214,8 @@
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:9pt;"><br /></p></body></html></string>
|
||||
</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -241,6 +244,9 @@ p, li { white-space: pre-wrap; }
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progress_bar">
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="basic_tab">
|
||||
<property name="locale">
|
||||
@ -170,7 +170,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@ -373,6 +373,26 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QPushButton" name="delete_vm_button">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: qlineargradient(spread:pad, x1:1, y1:1, x2:1, y2:0, stop:0 rgba(255, 179, 179, 255), stop:1 rgba(255, 108, 108, 255));
|
||||
border-color: rgb(170, 0, 0);
|
||||
border-style: solid;
|
||||
border-width: 1px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete VM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="clone_vm_button">
|
||||
<property name="text">
|
||||
<string>Clone VM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="advanced_tab">
|
||||
@ -580,7 +600,7 @@
|
||||
<widget class="QComboBox" name="default_dispvm"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="bootFromDeviceButton">
|
||||
<widget class="QPushButton" name="boot_from_device_button">
|
||||
<property name="text">
|
||||
<string>Boot qube from CDROM</string>
|
||||
</property>
|
||||
@ -769,47 +789,198 @@
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="policyAllowRadioButton">
|
||||
<property name="text">
|
||||
<string>Allow network access except...</string>
|
||||
<widget class="QLabel" name="no_netvm_label">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>139</red>
|
||||
<green>142</green>
|
||||
<blue>142</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="icmpCheckBox">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>323</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Allow ICMP traffic</string>
|
||||
<string>This qube has no networking - it will not have any network access anyway.</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="policyDenyRadioButton">
|
||||
<widget class="QLabel" name="netvm_no_firewall_label">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>139</red>
|
||||
<green>142</green>
|
||||
<blue>142</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Deny network access except...</string>
|
||||
<string>Networking qube does not support 'qubes-firewall' - firewall restrictions will not be applied.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="dnsCheckBox">
|
||||
<property name="text">
|
||||
<string>Allow DNS queries</string>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="firewall_modified_outside_label">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>139</red>
|
||||
<green>142</green>
|
||||
<blue>142</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Firewall has been modified manually - please use qvm-firewall for any further configuration.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="policy_allow_radio_button">
|
||||
<property name="text">
|
||||
<string>Allow all outgoing Internet connections</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="policy_deny_radio_button">
|
||||
<property name="toolTip">
|
||||
<string>Changing firewall settings does NOT affect existing connections.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Limit outgoing Internet connections to ...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="firewal_rules_label">
|
||||
<property name="text">
|
||||
<string>List of allowed (whitelisted) addresses:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="firewallRulesLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMaximumSize</enum>
|
||||
</property>
|
||||
@ -851,7 +1022,7 @@
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QPushButton" name="newRuleButton">
|
||||
<widget class="QPushButton" name="new_rule_button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
@ -868,7 +1039,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="editRuleButton">
|
||||
<widget class="QPushButton" name="edit_rule_button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
@ -885,7 +1056,10 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="deleteRuleButton">
|
||||
<widget class="QPushButton" name="delete_rule_button">
|
||||
<property name="toolTip">
|
||||
<string>Changing firewall settings does NOT affect existing connections.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
@ -918,14 +1092,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="yumproxyCheckBox">
|
||||
<property name="text">
|
||||
<string>Allow connections to Updates Proxy</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="8" column="0">
|
||||
<widget class="QWidget" name="tempFullAccessWidget" native="true">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
@ -940,15 +1107,15 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="tempFullAccess">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="temp_full_access">
|
||||
<property name="text">
|
||||
<string>Allow full access for </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="tempFullAccessTime">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="temp_full_access_time">
|
||||
<property name="suffix">
|
||||
<string> min</string>
|
||||
</property>
|
||||
@ -957,9 +1124,35 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_22">
|
||||
<property name="text">
|
||||
<string>NOTE: To block all network access, set Networking to (none) on the Basic settings tab. This tab provides a very simplified firewall configuration. All DNS requests and ICMP (pings) will be allowed. For more granular control, use the command line tool qvm-firewall.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="devices_tab">
|
||||
@ -1162,15 +1355,10 @@
|
||||
<tabstop>vcpus</tabstop>
|
||||
<tabstop>include_in_balancing</tabstop>
|
||||
<tabstop>kernel</tabstop>
|
||||
<tabstop>policyAllowRadioButton</tabstop>
|
||||
<tabstop>policyDenyRadioButton</tabstop>
|
||||
<tabstop>icmpCheckBox</tabstop>
|
||||
<tabstop>dnsCheckBox</tabstop>
|
||||
<tabstop>yumproxyCheckBox</tabstop>
|
||||
<tabstop>newRuleButton</tabstop>
|
||||
<tabstop>new_rule_button</tabstop>
|
||||
<tabstop>rulesTreeView</tabstop>
|
||||
<tabstop>editRuleButton</tabstop>
|
||||
<tabstop>deleteRuleButton</tabstop>
|
||||
<tabstop>edit_rule_button</tabstop>
|
||||
<tabstop>delete_rule_button</tabstop>
|
||||
<tabstop>service_line_edit</tabstop>
|
||||
<tabstop>add_srv_button</tabstop>
|
||||
<tabstop>services_list</tabstop>
|
||||
|
Loading…
Reference in New Issue
Block a user