backup+restore: remove an option to attach disk from a VM to dom0

This is (intentionally) not possible in R3. The user can choose to
backup directly to the VM, encrypting the backup in dom0 first.
This commit is contained in:
Marek Marczykowski-Górecki 2015-03-31 05:40:53 +02:00
parent f65dcdbf10
commit 5e925b23d4
5 changed files with 16 additions and 299 deletions

View File

@ -155,64 +155,27 @@
<string>Backup destination directory</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Device:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="dev_combobox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>dev1</string>
</property>
</item>
<item>
<property name="text">
<string>longdeviceblablabla</string>
</property>
</item>
<item>
<property name="text">
<string>dev2</string>
</property>
</item>
<item>
<property name="text">
<string>dev3</string>
</property>
</item>
</widget>
</item>
<item row="4" column="2">
<item row="3" column="2">
<widget class="QLineEdit" name="dir_line_edit"/>
</item>
<item row="4" column="3">
<item row="3" column="3">
<widget class="QToolButton" name="select_path_button">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="2">
<item row="1" column="2">
<widget class="QComboBox" name="appvm_combobox"/>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Target AppVM:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Backup directory or VM command:</string>

View File

@ -66,7 +66,6 @@ class BackupVMsWindow(Ui_Backup, QWizard):
self.blk_manager = blk_manager
self.shutdown_vm_func = shutdown_vm_func
self.dev_mount_path = None
self.func_output = []
self.selected_vms = []
self.tmpdir_to_remove = None
@ -92,13 +91,8 @@ class BackupVMsWindow(Ui_Backup, QWizard):
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.dev_combobox, SIGNAL("activated(int)"), self.dev_combobox_activated)
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.dev_combobox, SIGNAL("activated(int)"),
self.update_device_appvm_enabled)
self.connect(self.appvm_combobox, SIGNAL("activated(int)"),
self.update_device_appvm_enabled)
self.select_vms_page.isComplete = self.has_selected_vms
self.select_dir_page.isComplete = self.has_selected_dir_and_pass
@ -119,7 +113,6 @@ class BackupVMsWindow(Ui_Backup, QWizard):
self.total_size = 0
self.__fill_vms_list__()
fill_devs_list(self)
fill_appvms_list(self)
self.load_settings()
@ -130,12 +123,6 @@ class BackupVMsWindow(Ui_Backup, QWizard):
dest_vm_idx = self.appvm_combobox.findText(dest_vm_name.toString())
if dest_vm_idx > -1:
self.appvm_combobox.setCurrentIndex(dest_vm_idx)
else:
dest_blk_name = main.manager_window.manager_settings.value(
'backup/device', defaultValue="")
dest_blk_idx = self.dev_combobox.findText(dest_blk_name.toString())
if dest_blk_idx > -1:
self.dev_combobox.setCurrentIndex(dest_blk_idx)
if main.manager_window.manager_settings.contains('backup/path'):
dest_path = main.manager_window.manager_settings.value(
@ -150,8 +137,6 @@ class BackupVMsWindow(Ui_Backup, QWizard):
def save_settings(self):
main.manager_window.manager_settings.setValue(
'backup/vmname', self.appvm_combobox.currentText())
main.manager_window.manager_settings.setValue(
'backup/device', self.dev_combobox.currentText())
main.manager_window.manager_settings.setValue(
'backup/path', self.dir_line_edit.text())
main.manager_window.manager_settings.setValue(
@ -274,12 +259,6 @@ class BackupVMsWindow(Ui_Backup, QWizard):
vms.append(item.vm)
return (names, vms)
def dev_combobox_activated(self, idx):
dev_combobox_activated(self, idx)
def update_device_appvm_enabled(self, idx):
update_device_appvm_enabled(self, idx)
@pyqtSlot(name='on_select_path_button_clicked')
def select_path_button_clicked(self):
select_path_button_clicked(self)
@ -402,18 +381,7 @@ class BackupVMsWindow(Ui_Backup, QWizard):
else:
self.progress_bar.setValue(100)
self.progress_status.setText("Backup finished.")
if self.dev_mount_path is not None:
self.progress_status.setText(
"Backup finished. You can disconnect your backup "
"device")
else:
self.progress_status.setText("Backup finished.")
if self.dev_mount_path is not None:
umount_device(self.dev_mount_path)
detach_device(self, str(self.dev_combobox.itemData(
self.dev_combobox.currentIndex()).toString()))
self.dev_mount_path = None
elif self.showFileDialog.isChecked():
if self.showFileDialog.isChecked():
orig_text = self.progress_status.text
self.progress_status.setText(
orig_text + " Please unmount your backup volume and cancel "

View File

@ -36,61 +36,6 @@ from string import replace
path_re = re.compile(r"[a-zA-Z0-9/:.,_+=() -]*")
path_max_len = 512
mount_for_backup_path = '/usr/libexec/qubes-manager/mount_for_backup.sh'
def check_if_mounted(dev_path):
mounts_file = open("/proc/mounts")
for m in list(mounts_file):
if m.startswith(dev_path):
return m.split(" ")[1]
return None
def mount_device(dev_path):
try:
mount_dir_name = "backup" + replace(str(datetime.now()),' ', '-').split(".")[0]
pmount_cmd = [mount_for_backup_path, dev_path, mount_dir_name]
res = subprocess.check_call(pmount_cmd)
except Exception as ex:
QMessageBox.warning (None, "Error mounting selected device!", "<b>Could not mount {0}.</b><br><br>ERROR: {1}".format(dev_path, ex))
return None
if res == 0:
dev_mount_path = "/media/"+mount_dir_name
return dev_mount_path
return None
def umount_device(dev_mount_path):
while True:
try:
pumount_cmd = ["/usr/bin/sudo", "/usr/bin/pumount", "--luks-force", dev_mount_path]
res = subprocess.check_call(pumount_cmd)
if res == 0:
dev_mount_path = None
return dev_mount_path
except Exception as ex:
title = "Error unmounting backup device!"
text = "<b>Could not unmount {0}.</b><br>\
<b>Please retry or unmount it manually using</b><br> pumount {0}.<br><br>\
ERROR: {1}".format(dev_mount_path, ex)
button = QMessageBox.warning (None, title, text, QMessageBox.Ok | QMessageBox.Retry, QMessageBox.Retry)
if button == QMessageBox.Ok:
return dev_mount_path
def detach_device(dialog, dev_name):
""" Detach device from dom0, if device was attached from some VM"""
if not dev_name.startswith(dialog.vm.name+":"):
with dialog.blk_manager.blk_lock:
dialog.blk_manager.detach_device(dialog.vm, dev_name)
dialog.blk_manager.update()
else:
# umount/LUKS remove do not trigger udev event on underlying device,
# so trigger it manually - to publish back as available device
subprocess.call(["/usr/bin/sudo", "/bin/udevadm", "trigger", "--action=change",
"--subsystem-match=block",
"--sysname-match=%s" % dev_name.split(":")[1]])
with dialog.blk_manager.blk_lock:
dialog.blk_manager.update()
def fill_appvms_list(dialog):
dialog.appvm_combobox.clear()
@ -107,100 +52,10 @@ def fill_appvms_list(dialog):
if vm.is_running() and vm.qid != 0:
dialog.appvm_combobox.addItem(vm.name)
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']
dialog.dev_combobox.addItem(att, QVariant(a))
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()
enable_dir_line_edit(dialog, True)
def enable_dir_line_edit(dialog, boolean):
dialog.dir_line_edit.setEnabled(boolean)
dialog.select_path_button.setEnabled(boolean)
def update_device_appvm_enabled(dialog, idx):
# Only one of those can be used
dialog.dev_combobox.setEnabled(dialog.appvm_combobox.currentIndex() == 0)
dialog.appvm_combobox.setEnabled(dialog.dev_combobox.currentIndex() == 0)
def dev_combobox_activated(dialog, idx):
if idx == dialog.prev_dev_idx: #nothing has changed
return
#there was a change
dialog.dir_line_edit.setText("")
# umount old device if any
if dialog.dev_mount_path != None:
dialog.dev_mount_path = umount_device(dialog.dev_mount_path)
if dialog.dev_mount_path != None:
dialog.dev_combobox.setCurrentIndex(dialog.prev_dev_idx)
return
else:
detach_device(dialog,
str(dialog.dev_combobox.itemData(dialog.prev_dev_idx).toString()))
# then attach new one
if dialog.dev_combobox.currentIndex() != 0: #An existing device chosen
dev_name = str(dialog.dev_combobox.itemData(idx).toString())
if dev_name.startswith(dialog.vm.name+":"):
# originally attached to dom0
dev_path = "/dev/"+dev_name.split(":")[1]
else:
try:
with dialog.blk_manager.blk_lock:
if dev_name in dialog.blk_manager.free_devs:
#attach it to dom0, then treat it as an attached device
dialog.blk_manager.attach_device(dialog.vm, dev_name)
dialog.blk_manager.update()
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']
else:
raise QubesException("device not attached?!")
except QubesException as ex:
QMessageBox.warning (None, "Error attaching selected device!",
"<b>Could not attach {0}.</b><br><br>ERROR: {1}".format(dev_name, ex))
dialog.dev_combobox.setCurrentIndex(0) #if couldn't mount - set current device to "None"
dialog.prev_dev_idx = 0
return
#check if device mounted
dialog.dev_mount_path = check_if_mounted(dev_path)
if dialog.dev_mount_path == None:
dialog.dev_mount_path = mount_device(dev_path)
if dialog.dev_mount_path == None:
dialog.dev_combobox.setCurrentIndex(0) #if couldn't mount - set current device to "None"
dialog.prev_dev_idx = 0
detach_device(dialog,
str(dialog.dev_combobox.itemData(idx).toString()))
return
dialog.prev_dev_idx = idx
if hasattr(dialog, 'selected_vms'):
# backup window
if dialog.dev_mount_path != None:
# Initialize path with root of mounted device
dialog.dir_line_edit.setText(dialog.dev_mount_path)
dialog.select_dir_page.emit(SIGNAL("completeChanged()"))
def get_path_for_vm(vm, service_name):
if not vm:
return None
@ -234,8 +89,6 @@ def select_path_button_clicked(dialog, select_file = False):
if vm:
new_path = get_path_for_vm(vm, "qubes.SelectFile" if select_file
else "qubes.SelectDirectory")
elif dialog.dev_mount_path != None:
new_path = file_dialog_function(dialog, "Select backup location.", dialog.dev_mount_path)
else:
new_path = file_dialog_function(dialog, "Select backup location.",
backup_location if backup_location
@ -258,5 +111,4 @@ def simulate_long_lasting_proces(period, progress_callback):
time.sleep(1)
progress_callback(100)
return 0
return 0

View File

@ -62,7 +62,6 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
self.qvm_collection = qvm_collection
self.blk_manager = blk_manager
self.dev_mount_path = None
self.restore_options = None
self.vms_to_restore = None
self.func_output = []
@ -83,14 +82,9 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
self.select_vms_layout.insertWidget(1, self.select_vms_widget)
self.connect(self, SIGNAL("currentIdChanged(int)"), self.current_page_changed)
self.connect(self.dev_combobox, SIGNAL("activated(int)"), self.dev_combobox_activated)
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.dev_combobox, SIGNAL("activated(int)"),
self.update_device_appvm_enabled)
self.connect(self.appvm_combobox, SIGNAL("activated(int)"),
self.update_device_appvm_enabled)
self.connect(self.verify_only, SIGNAL("stateChanged(int)"),
self.on_verify_only_toogled)
@ -101,17 +95,9 @@ class RestoreVMsWindow(Ui_Restore, 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()"))
fill_devs_list(self)
fill_appvms_list(self)
self.__init_restore_options__()
def dev_combobox_activated(self, idx):
dev_combobox_activated(self, idx)
def update_device_appvm_enabled(self, idx):
update_device_appvm_enabled(self, idx)
@pyqtSlot(name='on_select_path_button_clicked')
def select_path_button_clicked(self):
select_path_button_clicked(self, True)
@ -288,12 +274,7 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
QMessageBox.warning (None, "Backup error!", "ERROR: {1}"
.format(self.vm.name, self.thread_monitor.error_msg))
if self.dev_mount_path != None:
umount_device(self.dev_mount_path)
self.dev_mount_path = None
detach_device(self, str(self.dev_combobox.itemData(
self.dev_combobox.currentIndex()).toString()))
elif self.showFileDialog.isChecked():
if self.showFileDialog.isChecked():
self.emit(SIGNAL("restore_progress(QString)"),
'<b><font color="black">{0}</font></b>'.format(
"Please unmount your backup volume and cancel "
@ -304,9 +285,9 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
else:
file_dialog = QFileDialog()
file_dialog.setReadOnly(True)
file_dialog.getExistingDirectory(self,
"Detach backup device",
self.dev_mount_path)
file_dialog.getExistingDirectory(
self, "Detach backup device",
os.path.dirname(unicode(self.dir_line_edit.text())))
self.progress_bar.setValue(100)
self.button(self.FinishButton).setEnabled(True)
self.button(self.CancelButton).setEnabled(False)
@ -330,10 +311,6 @@ class RestoreVMsWindow(Ui_Restore, QWizard):
.format("Aborting the operation..."))
self.button(self.CancelButton).setDisabled(True)
else:
if self.dev_mount_path != None:
umount_device(self.dev_mount_path)
detach_device(self, str(self.dev_combobox.itemData(
self.dev_combobox.currentIndex()).toString()))
self.done(0)
def has_selected_dir(self):

View File

@ -94,47 +94,17 @@
<string>Backup source location</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QComboBox" name="dev_combobox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>dev1</string>
</property>
</item>
<item>
<property name="text">
<string>longdeviceblablabla</string>
</property>
</item>
<item>
<property name="text">
<string>dev2</string>
</property>
</item>
<item>
<property name="text">
<string>dev3</string>
</property>
</item>
</widget>
</item>
<item row="6" column="1">
<item row="5" column="1">
<widget class="QLineEdit" name="dir_line_edit"/>
</item>
<item row="6" column="2">
<item row="5" column="2">
<widget class="QToolButton" name="select_path_button">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
@ -153,23 +123,10 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Device:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="0" column="1">
<widget class="QComboBox" name="appvm_combobox"/>
</item>
<item row="1" column="0">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>AppVM:</string>