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:
parent
f65dcdbf10
commit
5e925b23d4
47
backupdlg.ui
47
backupdlg.ui
@ -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>
|
||||
|
@ -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 "
|
||||
|
@ -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
|
@ -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):
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user