Browse Source

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.
Marek Marczykowski-Górecki 9 years ago
parent
commit
5e925b23d4
5 changed files with 16 additions and 299 deletions
  1. 5 42
      backupdlg.ui
  2. 1 33
      qubesmanager/backup.py
  3. 1 149
      qubesmanager/backup_utils.py
  4. 4 27
      qubesmanager/restore.py
  5. 5 48
      restoredlg.ui

+ 5 - 42
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>

+ 1 - 33
qubesmanager/backup.py

@@ -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 "

+ 1 - 149
qubesmanager/backup_utils.py

@@ -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

+ 4 - 27
qubesmanager/restore.py

@@ -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):

+ 5 - 48
restoredlg.ui

@@ -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>