Merge branch 'master' of git://git.qubes-os.org/aga/qubes-manager
48
backupdlg.ui
@ -97,6 +97,54 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<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>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Total size:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="total_size_label">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<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>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWizardPage" name="select_dir_page">
|
||||
|
@ -90,7 +90,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Minimal VM's memory:</string>
|
||||
@ -100,36 +100,36 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="min_vm_mem">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
<string> MiB</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999999999</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>10</number>
|
||||
<number>50</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>dom0 memory margin:</string>
|
||||
<string>dom0 memory boost:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="dom0_mem_margin">
|
||||
<widget class="QSpinBox" name="dom0_mem_boost">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
<string> MiB</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999999999</number>
|
||||
|
BIN
icons/apps.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
icons/backup.png
Normal file
After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 2.8 KiB |
BIN
icons/global-settings.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
icons/restore.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
icons/running.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
icons/settings.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
icons/show-all-running.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
icons/wall.png
Normal file
After Width: | Height: | Size: 23 KiB |
@ -273,6 +273,10 @@
|
||||
<addaction name="action_appmenus"/>
|
||||
<addaction name="action_updatevm"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_global_settings"/>
|
||||
<addaction name="action_backup"/>
|
||||
<addaction name="action_restore"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_showallvms"/>
|
||||
</widget>
|
||||
<action name="action_createvm">
|
||||
@ -353,7 +357,7 @@
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/appsprefs.png</normaloff>:/appsprefs.png</iconset>
|
||||
<normaloff>:/apps.png</normaloff>:/apps.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select VM applications</string>
|
||||
@ -386,8 +390,8 @@
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/showallvms.png</normaloff>
|
||||
<selectedoff>:/showallvms.png</selectedoff>:/showallvms.png</iconset>
|
||||
<normaloff>:/show-all-running.png</normaloff>
|
||||
<selectedoff>:/showallvms.png</selectedoff>:/show-all-running.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show/Hide inactive VMs</string>
|
||||
@ -399,7 +403,7 @@
|
||||
<action name="action_editfwrules">
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/redfirewall.png</normaloff>:/redfirewall.png</iconset>
|
||||
<normaloff>:/firewall.png</normaloff>:/firewall.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit VM Firewall rules</string>
|
||||
@ -502,7 +506,7 @@
|
||||
<action name="action_settings">
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/root.png</normaloff>:/root.png</iconset>
|
||||
<normaloff>:/settings.png</normaloff>:/settings.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
@ -512,16 +516,28 @@
|
||||
</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 VMs 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 VMs</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>
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
|
||||
@ -48,6 +49,7 @@ from ui_backupdlg import *
|
||||
from multiselectwidget import *
|
||||
|
||||
from backup_utils import *
|
||||
import grp,pwd
|
||||
|
||||
|
||||
class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
@ -84,6 +86,8 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
|
||||
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(list)"), self.vms_removed)
|
||||
self.connect(self.select_vms_widget, SIGNAL("items_added(list)"), 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.dev_combobox, SIGNAL("activated(int)"), self.dev_combobox_activated)
|
||||
@ -95,6 +99,7 @@ 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()"))
|
||||
|
||||
self.total_size = 0
|
||||
self.__fill_vms_list__()
|
||||
fill_devs_list(self)
|
||||
|
||||
@ -105,8 +110,25 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
|
||||
class VmListItem(QListWidgetItem):
|
||||
def __init__(self, vm):
|
||||
super(BackupVMsWindow.VmListItem, self).__init__(vm.name)
|
||||
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)
|
||||
else:
|
||||
self.size = self.get_vm_size(vm)
|
||||
super(BackupVMsWindow.VmListItem, self).__init__(vm.name+ " (" + qubesutils.size_to_human(self.size) + ")")
|
||||
|
||||
def get_vm_size(self, vm):
|
||||
size = 0
|
||||
if vm.private_img is not None:
|
||||
size += vm.get_disk_usage (vm.private_img)
|
||||
|
||||
if vm.updateable:
|
||||
size += vm.get_disk_usage(vm.root_img)
|
||||
|
||||
return size
|
||||
|
||||
|
||||
def __fill_vms_list__(self):
|
||||
for vm in self.qvm_collection.values():
|
||||
@ -118,9 +140,21 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
item = BackupVMsWindow.VmListItem(vm)
|
||||
if vm.include_in_backups == True:
|
||||
self.select_vms_widget.selected_list.addItem(item)
|
||||
self.total_size += item.size
|
||||
else:
|
||||
self.select_vms_widget.available_list.addItem(item)
|
||||
self.check_running()
|
||||
self.total_size_label.setText(qubesutils.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))
|
||||
|
||||
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))
|
||||
|
||||
def check_running(self):
|
||||
some_selected_vms_running = False
|
||||
@ -201,7 +235,7 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
|
||||
del self.excluded[:]
|
||||
for i in range(self.select_vms_widget.available_list.count()):
|
||||
vmname = str(self.select_vms_widget.available_list.item(i).text())
|
||||
vmname = self.select_vms_widget.available_list.item(i).vm.name
|
||||
self.excluded.append(vmname)
|
||||
|
||||
return True
|
||||
@ -210,8 +244,14 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
self.func_output.append(s)
|
||||
|
||||
def update_progress_bar(self, value):
|
||||
self.emit(SIGNAL("backup_progress(int)"), value)
|
||||
if value == 100:
|
||||
self.emit(SIGNAL("backup_progress(int)"), value)
|
||||
|
||||
def check_backup_progress(self, initial_usage, total_backup_size):
|
||||
du = qubesutils.get_disk_usage(self.backup_dir)
|
||||
done = du - initial_usage
|
||||
percent = int((float(done)/total_backup_size)*100)
|
||||
return percent
|
||||
|
||||
def __do_backup__(self, thread_monitor):
|
||||
msg = []
|
||||
@ -240,16 +280,24 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
self.textEdit.setText("\n".join(self.func_output))
|
||||
|
||||
elif self.currentPage() is self.commit_page:
|
||||
self.button(self.CancelButton).setDisabled(True)
|
||||
self.button(self.FinishButton).setDisabled(True)
|
||||
self.button(self.CancelButton).setDisabled(True)
|
||||
self.thread_monitor = ThreadMonitor()
|
||||
initial_usage = qubesutils.get_disk_usage(self.backup_dir)
|
||||
thread = threading.Thread (target= self.__do_backup__ , args=(self.thread_monitor,))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
self.button(self.CancelButton).setDisabled(False)
|
||||
|
||||
counter = 0
|
||||
while not self.thread_monitor.is_finished():
|
||||
self.app.processEvents()
|
||||
time.sleep (0.1)
|
||||
counter += 1
|
||||
if counter == 20:
|
||||
progress = self.check_backup_progress(initial_usage, self.total_size)
|
||||
self.progress_bar.setValue(progress)
|
||||
counter = 0
|
||||
|
||||
if not self.thread_monitor.success:
|
||||
QMessageBox.warning (None, "Backup error!", "ERROR: {1}".format(self.vm.name, self.thread_monitor.error_msg))
|
||||
@ -260,6 +308,25 @@ class BackupVMsWindow(Ui_Backup, QWizard):
|
||||
|
||||
|
||||
def reject(self):
|
||||
#cancell clicked while the backup is in progress.
|
||||
#calling kill on cp.
|
||||
if self.currentPage() is self.commit_page:
|
||||
manager_pid = os.getpid()
|
||||
cp_pid_cmd = ["ps" ,"--ppid", str(manager_pid)]
|
||||
pid = None
|
||||
|
||||
while not self.thread_monitor.is_finished():
|
||||
cp_pid = subprocess.Popen(cp_pid_cmd, stdout = subprocess.PIPE)
|
||||
output = cp_pid.stdout.read().split("\n")
|
||||
|
||||
for l in output:
|
||||
if l.endswith("cp"):
|
||||
pid = l.split(" ")[1]
|
||||
break
|
||||
if pid != None:
|
||||
os.kill(int(pid), signal.SIGTERM)
|
||||
break
|
||||
|
||||
if self.dev_mount_path != None:
|
||||
umount_device(self.dev_mount_path)
|
||||
self.done(0)
|
||||
|
@ -72,6 +72,8 @@ def umount_device(dev_mount_path):
|
||||
def fill_devs_list(dialog):
|
||||
dialog.dev_combobox.clear()
|
||||
dialog.dev_combobox.addItem("None")
|
||||
|
||||
dialog.blk_manager.blk_lock.acquire()
|
||||
for a in dialog.blk_manager.attached_devs:
|
||||
if dialog.blk_manager.attached_devs[a]['attached_to']['vm'] == dialog.vm.name :
|
||||
att = a + " " + unicode(dialog.blk_manager.attached_devs[a]['size']) + " " + dialog.blk_manager.attached_devs[a]['desc']
|
||||
@ -79,6 +81,8 @@ def fill_devs_list(dialog):
|
||||
for a in dialog.blk_manager.free_devs:
|
||||
att = a + " " + unicode(dialog.blk_manager.free_devs[a]['size']) + " " + dialog.blk_manager.free_devs[a]['desc']
|
||||
dialog.dev_combobox.addItem(att, QVariant(a))
|
||||
dialog.blk_manager.blk_lock.release()
|
||||
|
||||
dialog.dev_combobox.setCurrentIndex(0) #current selected is null ""
|
||||
dialog.prev_dev_idx = 0
|
||||
dialog.dir_line_edit.clear()
|
||||
@ -107,6 +111,7 @@ def dev_combobox_activated(dialog, idx):
|
||||
if dialog.dev_combobox.currentText() != "None": #An existing device chosen
|
||||
dev_name = str(dialog.dev_combobox.itemData(idx).toString())
|
||||
|
||||
dialog.blk_manager.blk_lock.acquire()
|
||||
if dev_name in dialog.blk_manager.free_devs:
|
||||
if dev_name.startswith(dialog.vm.name): # originally attached to dom0
|
||||
dev_path = "/dev/"+dev_name.split(":")[1]
|
||||
@ -118,6 +123,7 @@ def dev_combobox_activated(dialog, idx):
|
||||
if dev_name in dialog.blk_manager.attached_devs: #is attached to dom0
|
||||
assert dialog.blk_manager.attached_devs[dev_name]['attached_to']['vm'] == dialog.vm.name
|
||||
dev_path = "/dev/" + dialog.blk_manager.attached_devs[dev_name]['attached_to']['frontend']
|
||||
dialog.blk_manager.blk_lock.release()
|
||||
|
||||
#check if device mounted
|
||||
dialog.dev_mount_path = check_if_mounted(dev_path)
|
||||
|
@ -43,7 +43,12 @@ from operator import itemgetter
|
||||
|
||||
from ui_globalsettingsdlg import *
|
||||
|
||||
from ConfigParser import SafeConfigParser
|
||||
from qubes.qubesutils import parse_size
|
||||
from qubes import qmemman_algo
|
||||
|
||||
dont_keep_dvm_in_memory_path = '/var/lib/qubes/dvmdata/dont_use_shm'
|
||||
qmemman_config_path = '/etc/qubes/qmemman.conf'
|
||||
|
||||
|
||||
class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
@ -188,6 +193,22 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
|
||||
def __init_mem_defaults__(self):
|
||||
|
||||
#qmemman settings
|
||||
self.qmemman_config = SafeConfigParser()
|
||||
self.vm_min_mem_val = str(qmemman_algo.MIN_PREFMEM)
|
||||
self.dom0_mem_boost_val = str(qmemman_algo.DOM0_MEM_BOOST)
|
||||
|
||||
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 = parse_size(self.vm_min_mem_val)
|
||||
self.dom0_mem_boost_val = parse_size(self.dom0_mem_boost_val)
|
||||
|
||||
self.min_vm_mem.setValue(self.vm_min_mem_val/1024/1024)
|
||||
self.dom0_mem_boost.setValue(self.dom0_mem_boost_val/1024/1024)
|
||||
|
||||
#keep dispvm in memory
|
||||
exists = os.path.exists(dont_keep_dvm_in_memory_path)
|
||||
self.dispvm_in_memory.setChecked( not exists)
|
||||
@ -195,6 +216,57 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
|
||||
def __apply_mem_defaults__(self):
|
||||
|
||||
#qmemman settings
|
||||
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:
|
||||
|
||||
current_min_vm_mem = str(current_min_vm_mem)+'M'
|
||||
current_dom0_mem_boost = str(current_dom0_mem_boost)+'M'
|
||||
|
||||
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(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...
|
||||
|
||||
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"
|
||||
|
||||
config_lines = []
|
||||
|
||||
qmemman_config_file = open(qmemman_config_path, 'r')
|
||||
for l in qmemman_config_file:
|
||||
if l.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'):
|
||||
config_lines.append(lines_to_add['dom0-mem-boost'])
|
||||
del lines_to_add['dom0-mem-boost']
|
||||
else:
|
||||
config_lines.append(l)
|
||||
|
||||
qmemman_config_file.close()
|
||||
|
||||
for l in lines_to_add:
|
||||
config_lines.append(l)
|
||||
|
||||
qmemman_config_file = open(qmemman_config_path, 'w')
|
||||
qmemman_config_file.writelines(config_lines)
|
||||
qmemman_config_file.close()
|
||||
|
||||
self.anything_changed = True
|
||||
|
||||
#keep dispvm in memory
|
||||
was_checked = not os.path.exists(dont_keep_dvm_in_memory_path)
|
||||
if was_checked != self.dispvm_in_memory.isChecked():
|
||||
@ -206,6 +278,7 @@ class GlobalSettingsWindow(Ui_GlobalSettings, QDialog):
|
||||
os.remove(dont_keep_dvm_in_memory_path)
|
||||
self.anything_changed = True
|
||||
|
||||
|
||||
def reject(self):
|
||||
self.done(0)
|
||||
|
||||
|
@ -557,8 +557,13 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
||||
self.setupUi(self)
|
||||
self.toolbar = self.toolBar
|
||||
|
||||
self.qubes_watch = qubesutils.QubesWatch()
|
||||
self.qvm_collection = QubesVmCollection()
|
||||
self.blk_manager = QubesBlockDevicesManager(self.qvm_collection)
|
||||
self.qubes_watch.setup_block_watch(self.blk_manager.block_devs_event)
|
||||
self.blk_watch_thread = threading.Thread(target=self.qubes_watch.watch_loop)
|
||||
self.blk_watch_thread.daemon = True
|
||||
self.blk_watch_thread.start()
|
||||
|
||||
self.connect(self.table, SIGNAL("itemSelectionChanged()"), self.table_selection_changed)
|
||||
|
||||
@ -794,8 +799,10 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
||||
rows_with_blk = None
|
||||
if update_devs == True:
|
||||
rows_with_blk = []
|
||||
self.blk_manager.blk_lock.acquire()
|
||||
for d in self.blk_manager.attached_devs:
|
||||
rows_with_blk.append( self.blk_manager.attached_devs[d]['attached_to']['vm'])
|
||||
self.blk_manager.blk_lock.release()
|
||||
|
||||
if self.counter % 3 == 0 or out_of_schedule:
|
||||
(self.last_measure_time, self.last_measure_results) = \
|
||||
@ -843,7 +850,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
||||
|
||||
|
||||
def update_block_devices(self):
|
||||
res, msg = self.blk_manager.update()
|
||||
res, msg = self.blk_manager.check_for_updates()
|
||||
if msg != None and len(msg) > 0:
|
||||
str = "\n".join(msg)
|
||||
trayIcon.showMessage ("Qubes Manager", str, msecs=5000)
|
||||
@ -1304,6 +1311,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
||||
else:
|
||||
self.blk_menu.clear()
|
||||
self.blk_menu.setEnabled(True)
|
||||
|
||||
self.blk_manager.blk_lock.acquire()
|
||||
if len(self.blk_manager.attached_devs) > 0 :
|
||||
for d in self.blk_manager.attached_devs:
|
||||
if self.blk_manager.attached_devs[d]['attached_to']['vm'] == vm.name:
|
||||
@ -1319,6 +1328,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
||||
action = self.blk_menu.addAction(QIcon(":/add.png"), str)
|
||||
action.setData(QVariant(d))
|
||||
|
||||
self.blk_manager.blk_lock.release()
|
||||
|
||||
if self.blk_menu.isEmpty():
|
||||
self.blk_menu.setEnabled(False)
|
||||
|
||||
@ -1328,10 +1339,13 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
||||
def attach_dettach_device_triggered(self, action):
|
||||
dev = str(action.data().toString())
|
||||
vm = self.get_selected_vm()
|
||||
|
||||
self.blk_manager.blk_lock.acquire()
|
||||
if dev in self.blk_manager.attached_devs:
|
||||
self.blk_manager.detach_device(vm, dev)
|
||||
else:
|
||||
self.blk_manager.attach_device(vm, dev)
|
||||
self.blk_manager.blk_lock.release()
|
||||
|
||||
|
||||
class QubesBlockDevicesManager():
|
||||
@ -1344,43 +1358,75 @@ class QubesBlockDevicesManager():
|
||||
self.current_attached = {}
|
||||
self.devs_changed = False
|
||||
|
||||
self.last_update_time = time.time()
|
||||
self.blk_state_changed = True
|
||||
self.msg = []
|
||||
self.check_counter = 0
|
||||
self.blk_lock = threading.Lock()
|
||||
|
||||
self.update()
|
||||
|
||||
def block_devs_event(self, xid):
|
||||
now = time.time()
|
||||
#don't update more often than 1/10 s
|
||||
if now - self.last_update_time >= 0.1:
|
||||
self.last_update_time = now
|
||||
|
||||
self.blk_lock.acquire()
|
||||
|
||||
self.blk_state_changed = True
|
||||
|
||||
self.blk_lock.release()
|
||||
|
||||
def check_for_updates(self):
|
||||
self.blk_lock.acquire()
|
||||
|
||||
ret = (self.blk_state_changed, self.msg)
|
||||
|
||||
if self.blk_state_changed == True:
|
||||
self.check_counter += 1
|
||||
|
||||
self.update()
|
||||
ret = (self.blk_state_changed, self.msg)
|
||||
|
||||
#let the update last for 3 manager-update cycles
|
||||
if self.check_counter == 3:
|
||||
self.check_counter = 0
|
||||
self.blk_state_changed = False
|
||||
self.msg = []
|
||||
|
||||
self.blk_lock.release()
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def update(self):
|
||||
blk = qubesutils.block_list()
|
||||
msg = []
|
||||
for b in blk:
|
||||
att = qubesutils.block_check_attached(None, blk[b]['device'], backend_xid = blk[b]['xid'])
|
||||
if b in self.current_blk:
|
||||
if blk[b] == self.current_blk[b]:
|
||||
if self.current_attached[b] != att: #devices the same, sth with attaching changed
|
||||
self.current_attached[b] = att
|
||||
self.devs_changed = True
|
||||
else: #device changed ?!
|
||||
self.current_blk[b] = blk[b]
|
||||
self.current_attached[b] = att
|
||||
self.devs_changed = True
|
||||
else: #new device
|
||||
self.current_blk[b] = blk[b]
|
||||
self.current_attached[b] = att
|
||||
self.devs_changed = True
|
||||
msg.append("Attached new device: {0}".format(blk[b]['device']))
|
||||
self.msg.append("Attached new device: {0}".format(blk[b]['device']))
|
||||
|
||||
to_delete = []
|
||||
for b in self.current_blk: #remove devices that are not there anymore
|
||||
if b not in blk:
|
||||
to_delete.append(b)
|
||||
self.devs_changed = True
|
||||
msg.append("Detached device: {0}".format(self.current_blk[b]['device']))
|
||||
self.msg.append("Detached device: {0}".format(self.current_blk[b]['device']))
|
||||
|
||||
for d in to_delete:
|
||||
del self.current_blk[d]
|
||||
del self.current_attached[d]
|
||||
|
||||
if self.devs_changed == True:
|
||||
self.devs_changed = False
|
||||
self.__update_blk_entries__()
|
||||
return True, msg
|
||||
else:
|
||||
return False, None
|
||||
self.__update_blk_entries__()
|
||||
|
||||
|
||||
def __update_blk_entries__(self):
|
||||
@ -1408,16 +1454,12 @@ class QubesBlockDevicesManager():
|
||||
backend_vm = self.qvm_collection.get_vm_by_name(backend_vm_name)
|
||||
trayIcon.showMessage ("Qubes Manager", "{0} - attaching {1}".format(vm.name, dev), msecs=3000)
|
||||
qubesutils.block_attach(vm, backend_vm, dev_id)
|
||||
self.devs_changed = True
|
||||
|
||||
def detach_device(self, vm, dev_name):
|
||||
dev_id = self.attached_devs[dev_name]['attached_to']['devid']
|
||||
vm_xid = self.attached_devs[dev_name]['attached_to']['xid']
|
||||
trayIcon.showMessage ("Qubes Manager", "{0} - detaching {1}".format(vm.name, dev_name), msecs=3000)
|
||||
qubesutils.block_detach(None, dev_id, vm_xid)
|
||||
self.devs_changed = True
|
||||
|
||||
|
||||
|
||||
|
||||
class QubesTrayIcon(QSystemTrayIcon):
|
||||
|
@ -6,6 +6,8 @@ from ui_multiselectwidget import *
|
||||
class MultiSelectWidget(Ui_MultiSelectWidget, QWidget):
|
||||
|
||||
__pyqtSignals__ = ("selected_changed()",)
|
||||
__pyqtSignals__ = ("items_added(list)",)
|
||||
__pyqtSignals__ = ("items_removed(list)",)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(MultiSelectWidget, self).__init__()
|
||||
@ -19,13 +21,19 @@ class MultiSelectWidget(Ui_MultiSelectWidget, QWidget):
|
||||
|
||||
def switch_selected(self, src, dst):
|
||||
selected = src.selectedItems()
|
||||
items = []
|
||||
|
||||
for s in selected:
|
||||
row = src.indexFromItem(s).row()
|
||||
item = src.takeItem(row)
|
||||
dst.addItem(item)
|
||||
items.append(item)
|
||||
dst.sortItems()
|
||||
self.emit(SIGNAL("selected_changed()"))
|
||||
if src is self.selected_list:
|
||||
self.emit(SIGNAL("items_removed(list)"), items)
|
||||
else:
|
||||
self.emit(SIGNAL("items_added(list)"), items)
|
||||
|
||||
def add_selected(self):
|
||||
self.switch_selected(self.available_list, self.selected_list)
|
||||
@ -34,11 +42,18 @@ class MultiSelectWidget(Ui_MultiSelectWidget, QWidget):
|
||||
self.switch_selected(self.selected_list, self.available_list)
|
||||
|
||||
def move_all(self, src, dst):
|
||||
items = []
|
||||
while src.count() > 0:
|
||||
item = src.takeItem(0)
|
||||
dst.addItem(item)
|
||||
items.append(item)
|
||||
dst.sortItems()
|
||||
self.emit(SIGNAL("selected_changed()"))
|
||||
if src is self.selected_list:
|
||||
self.emit(SIGNAL("items_removed(list)"), items)
|
||||
else:
|
||||
self.emit(SIGNAL("items_added(list)"), items)
|
||||
|
||||
|
||||
def add_all(self):
|
||||
self.move_all(self.available_list, self.selected_list)
|
||||
|
@ -463,6 +463,13 @@ class VMSettingsWindow(Ui_SettingsDialog, QDialog):
|
||||
self.dmm_warning_adv.hide()
|
||||
self.dmm_warning_dev.hide()
|
||||
|
||||
if self.vm.is_running():
|
||||
self.dev_list.setEnabled(False)
|
||||
self.turn_off_vm_to_modify_devs.setVisible(True)
|
||||
else:
|
||||
self.dev_list.setEnabled(True)
|
||||
self.turn_off_vm_to_modify_devs.setVisible(False)
|
||||
|
||||
|
||||
def __apply_devices_tab__(self):
|
||||
sth_changed = False
|
||||
|
@ -1,19 +1,26 @@
|
||||
<RCC>
|
||||
<qresource>
|
||||
<file alias="apps.png">icons/apps.png</file>
|
||||
<file alias="settings.png">icons/settings.png</file>
|
||||
<file alias="firewall.png">icons/wall.png</file>
|
||||
<file alias="restore.png">icons/restore.png</file>
|
||||
<file alias="backup.png">icons/backup.png</file>
|
||||
<file alias="global-settings.png">icons/global-settings.png</file>
|
||||
<file alias="off.png">icons/on-icon/off.png</file>
|
||||
<file alias="on.png">icons/on-icon/on.png</file>
|
||||
<file alias="on-old.png">icons/on-icon/on.png</file>
|
||||
<file alias="show-all-running.png">icons/on-icon/show-all-running.png</file>
|
||||
<file alias="mount.png">icons/mount.png</file>
|
||||
<file alias="pencil.png">icons/pencil.png</file>
|
||||
<file alias="redfirewall.png">icons/redfirewall.png</file>
|
||||
<file alias="edit.png">icons/edit.png</file>
|
||||
<file alias="add.png">icons/add.png</file>
|
||||
<file alias="flag-blue.png">icons/flag-blue.png</file>
|
||||
<file alias="on.png">icons/running.png</file>
|
||||
<file alias="flag-green.png">icons/flag-green.png</file>
|
||||
<file alias="flag-red.png">icons/flag-red.png</file>
|
||||
<file alias="flag-yellow.png">icons/flag-yellow.png</file>
|
||||
<file alias="remove.png">icons/remove.png</file>
|
||||
<file alias="appsprefs.png">icons/appsprefs.png</file>
|
||||
<file alias="newfirewall.png">icons/newfirewall.png</file>
|
||||
<file alias="qubes.png">icons/qubes.png</file>
|
||||
<file alias="appvm.png">icons/appvm.png</file>
|
||||
<file alias="netvm.png">icons/netvm.png</file>
|
||||
@ -31,6 +38,6 @@
|
||||
<file alias="pausevm.png">icons/pausevm.png</file>
|
||||
<file alias="showallvms.png">icons/showallvms.png</file>
|
||||
<file alias="showcpuload.png">icons/showcpuload.png</file>
|
||||
<file alias="firewall.png">icons/firewall.png</file>
|
||||
</qresource>
|
||||
<qresource/>
|
||||
</RCC>
|
||||
|
@ -29,7 +29,7 @@
|
||||
<locale language="English" country="UnitedStates"/>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>3</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="basic_tab">
|
||||
<property name="locale">
|
||||
@ -586,6 +586,10 @@
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="firewall_tab">
|
||||
<attribute name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/firewall.png</normaloff>:/firewall.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Firewall rules</string>
|
||||
</attribute>
|
||||
@ -746,11 +750,15 @@
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/storagevm.png</normaloff>:/storagevm.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Devices</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="devices_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="dmm_warning_dev">
|
||||
@ -769,6 +777,20 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="turn_off_vm_to_modify_devs">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<italic>true</italic>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>To modify PCI devices you have to turn off the VM.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@ -776,7 +798,7 @@
|
||||
<widget class="QWidget" name="apps_tab">
|
||||
<attribute name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/appsprefs.png</normaloff>:/appsprefs.png</iconset>
|
||||
<normaloff>:/apps.png</normaloff>:/apps.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Applications</string>
|
||||
|