示例#1
0
def test_get_empty_space_non_zero_return_code(monkeypatch):
    generateStandardMock(monkeypatch, b"Error.", b"", 1, "lvm")
    manager = device.LVMDeviceManager("/dev/fileserver")
    with pytest.raises(DeviceError) as execinfo:
        manager.get_empty_space()

    assert "Error." in str(execinfo)
示例#2
0
def test_lvm_get_partition_size_bad_return_code(monkeypatch):
    generateStandardMock(monkeypatch, b"Didn't work.", b"", 1, "lvm")
    manager = device.LVMDeviceManager("/dev/fileserver")
    with pytest.raises(DeviceError) as execinfo:
        manager.get_partition_size("media")

    assert "Didn't work." in str(execinfo)
示例#3
0
def test_lvm_get_drive_size_bytes_bad_return_code(monkeypatch):
    generateStandardMock(monkeypatch, b"Test error", b"", 1, "lvm")
    manager = device.LVMDeviceManager("/dev/fileserver")
    with pytest.raises(DeviceError) as execinfo:
        manager.get_drive_size_bytes()

    assert "Test error" in str(execinfo)
示例#4
0
def test_lvm_get_partition_size(monkeypatch):
    generateStandardMock(
        monkeypatch,
        b"/dev/fileserver/media:fileserver:3:1:-1:0:2097152:256:-1:0:-1:252:3",
        b"", 0, "lvm")
    manager = device.LVMDeviceManager("/dev/fileserver")
    result = manager.get_partition_size("media")
    assert result == 2097152
示例#5
0
    def _generate_progress_grid(self):
        """Generates the grid for the screen showing progress. Sets
        `self.progress_grid` as the grid.`"""

        self.progress_grid = Gtk.Grid()
        part_label = Gtk.Label(label=_("Checking partitions and copying: "),
                               halign=Gtk.Align.START,
                               xpad=DEFAULT_HORIZONTAL_PADDING,
                               ypad=DEFAULT_VERTICAL_PADDING)
        self.progress_grid.attach(part_label, 1, 1, 1, 1)
        self.part_progress = Gtk.ProgressBar()
        set_margin(self.part_progress)
        self.progress_grid.attach_next_to(self.part_progress, part_label,
                                          Gtk.PositionType.RIGHT, 1, 1)
        self.copy_progresses = {}

        def create_partitions(source_manager, start_label):
            previous_label = start_label
            partitions = source_manager.get_partitions()
            for val in partitions:
                copy_label = Gtk.Label(
                    label=_("Copying partition {0}: ").format(val),
                    halign=Gtk.Align.START,
                    xpad=DEFAULT_HORIZONTAL_PADDING,
                    ypad=DEFAULT_VERTICAL_PADDING)
                copy_progress = Gtk.ProgressBar()
                set_margin(copy_progress)
                self.progress_grid.attach_next_to(copy_label, previous_label,
                                                  Gtk.PositionType.BOTTOM, 1,
                                                  1)
                self.progress_grid.attach_next_to(copy_progress, copy_label,
                                                  Gtk.PositionType.RIGHT, 1, 1)
                self.copy_progresses[val] = copy_progress
                previous_label = copy_label

            return previous_label

        final_label = create_partitions(
            device.DeviceManager(self.source, self.source_part_mask),
            part_label)
        if self.lvm_button.get_active():
            final_label = create_partitions(
                device.LVMDeviceManager(self.lvm_source), final_label)

        boot_label = Gtk.Label(label=_("Making bootable: "),
                               halign=Gtk.Align.START,
                               xpad=DEFAULT_HORIZONTAL_PADDING,
                               ypad=DEFAULT_VERTICAL_PADDING)
        self.progress_grid.attach_next_to(boot_label, final_label,
                                          Gtk.PositionType.BOTTOM, 1, 1)
        self.boot_progress = Gtk.ProgressBar()
        set_margin(self.boot_progress)
        self.progress_grid.attach_next_to(self.boot_progress, boot_label,
                                          Gtk.PositionType.RIGHT, 1, 1)
        self.cancel_btn = Gtk.Button(label="Cancel")
        set_margin(self.cancel_btn)
        self.progress_grid.attach_next_to(self.cancel_btn, self.boot_progress,
                                          Gtk.PositionType.BOTTOM, 1, 1)
示例#6
0
def test_lvm_get_partitions_standard(monkeypatch):
    generateStandardMock(
        monkeypatch,
        b"""LV:VG:Attr:LSize:Pool:Origin:Data%:Meta%:Move:Log:Cpy%Sync:Convert
  backup:fileserver:-wi-a-----:5,00g::::::::
  media:fileserver:-wi-a-----:1,00g::::::::
  share:fileserver:-wi-a-----:50,00g::::::::
  root:ubuntu-vg:-wi-ao----:6,52g::::::::
  swap_1:ubuntu-vg:-wi-ao----:1,00g::::::::
    """, b"", 0, "lvm")
    manager = device.LVMDeviceManager("/dev/fileserver")
    parts = manager.get_partitions()
    assert ["backup", "media", "share"] == parts
示例#7
0
def test_get_empty_space(monkeypatch):
    generateStandardMock(monkeypatch, b"  70921486336B", b"", 0, "lvm")
    manager = device.LVMDeviceManager("/dev/fileserver")
    result = manager.get_empty_space()
    assert result == 70921486336
示例#8
0
def test_lvm_get_partition_alignment_unssported():
    manager = device.LVMDeviceManager("/dev/fileserver")
    with pytest.raises(UnsupportedDeviceError) as execinfo:
        manager.get_partition_alignment()
示例#9
0
def test_lvm_get_partition_code_unsupported():
    manager = device.LVMDeviceManager("/dev/fileserver")
    with pytest.raises(UnsupportedDeviceError) as execinfo:
        manager.get_partition_code("media")
示例#10
0
def test_lvm_get_drive_size_bytes_standard(monkeypatch):
    generateStandardMock(monkeypatch, b"  6434062336B", b"", 0, "lvm")
    manager = device.LVMDeviceManager("/dev/fileserver")
    result = manager.get_drive_size_bytes()
    assert 6434062336 == result
示例#11
0
def test_lvm_get_drive_size_standard(monkeypatch):
    generateStandardMock(monkeypatch, b"  12566528S", b"", 0, "lvm")
    manager = device.LVMDeviceManager("/dev/fileserver")
    result = manager.get_drive_size()
    assert 12566528 == result
示例#12
0
def copy_drive(source,
               target,
               check_if_valid_and_copy=False,
               source_part_mask="{0}{1}",
               target_part_mask="{0}{1}",
               excluded_partitions=[],
               ignore_copy_failures=True,
               root_partition=None,
               boot_partition=None,
               efi_partition=None,
               mount_points=None,
               rsync_args=device.DEFAULT_RSYNC_ARGS,
               lvm_source=None,
               lvm_target=None,
               bootloader="uuid_copy",
               part_callback=None,
               copy_callback=None,
               boot_callback=None):
    """Uses a DeviceCopier to clone the source drive to the target drive.

    **Note:** if using LVM, any uses of "partition" in the documentation
    actually refer to logical volumes.

    It is recommended to set ``check_if_valid_and_copy`` to True if the the
    two drives are not the same size with the same partitions.

    If either source or target ends in ".img" copy_drives will assume it is an
    image file, and mount if accordingly.

    :param source: The drive identifier ("/dev/sda" or the like) of the source
                   drive.
    :param target: The drive identifier ("/dev/sda" or the like) of the target
                   drive.
    :param check_if_valid=False: If true, the function checks if the target
                                 drive is compatible to receive the source
                                 drive's data. If it is not, erase the target
                                 drive and make a proper partition table.
                                 Defaults to False.
    :param source_part_mask: A string to be passed to the "format" method that
                             expects to arguments, the drive name and the
                             partition number. Applied to the source drive.
                             Defaults to "{0}{1}".
    :param target_part_mask: Same as source_part_mask, but applied to target
                             drive. Defaults to "{0}{1}"
    :param excluded_partitions: Partitions to not copy or test for boot
                                capability.
    :param ignore_copy_failures: If True, errors during copying will be
                                 ignored and copying will continue. It is
                                 recommended that this be left to true,
                                 because errors frequently occur with swap
                                 partitions or other strange partitions.
    :param root_partition: If not None, this is an int that determines which
                           partition grub should be installed to. Defaults to
                           None.
    :param boot_partition: If not None, this is an int that represents the
                           partition to mount at /boot when installing grub.
    :param efi_partition: If not None, this is an int that represents the
                          partition to mount at /boot/efi when installing grub.
    :param mount_points: Expects a tuple containing two strings pointing to
                         the directories where partitions should be mounted in
                         case of testing. If None, the function will generate
                         two random directories in the /tmp folder. Defaults
                         to None.
    :param lvm: the Logical Volume Group to copy to the new drive.
    :param part_callback: a function that can be called to pass the progress
                          of the partition function. The function should
                          expect on float between 0 and 1, a negative value
                          denoting an error, or a boolean True to indicate the
                          progress is indeterminate.
    :param copy_callback: a function that can be called to pass the progress
                          of copying partitions. The function should expect
                          two arguments: an integer showing partition number
                          and a float showing progress between 0 and 1
    :param boot_callback: a function that can be called to pass the progress
                          of making the clone bootable. The function should
                          expect one argument: a boolean indicating whether or
                          not the process has finished.

    :raises DeviceError: If there is an error reading data from one device or
                         another.
    :raises CopyError: If there is an error copying the data between the two
                       devices.

    :returns: True on success and an error message or exception on failure.
    """
    try:
        source_loop = None
        target_loop = None
        if source.endswith(".img"):
            source_loop = mount_loop_device(source)
            source = source_loop
            source_part_mask = "{0}p{1}"

        if target.endswith(".img"):
            target_loop = mount_loop_device(target)
            target = target_loop
            target_part_mask = "{0}p{1}"
            LOGGER.warning(
                "Right now, WereSync does not properly install bootloaders on "
                "image files. You will have to handle that yourself if you "
                "want your image to be bootable.")

        source_manager = device.DeviceManager(source, source_part_mask)
        target_manager = device.DeviceManager(target, target_part_mask)

        try:
            target_manager.get_partition_table_type()
        except (DeviceError, UnsupportedDeviceError) as ex:
            # Since we're erasing the target drive anyway, we can just create
            # a new disk label
            proc = subprocess.Popen(["sgdisk", "-o", target_manager.device])
            proc.communicate()

        copier = device.DeviceCopier(source_manager, target_manager)
        partitions_remade = False
        if check_if_valid_and_copy:
            copy_partitions(copier, part_callback)
            partitions_remade = True

        if lvm_source is not None:
            create_new_vg_if_not_exists(lvm_source, lvm_source + "-copy",
                                        target_manager)
            lvm_source = device.LVMDeviceManager(lvm_source)
            lvm_target = device.LVMDeviceManager(lvm_source.device + "-copy")
            copier.lvm_source = lvm_source
            copier.lvm_target = lvm_target
            if partitions_remade and check_if_valid_and_copy:
                copy_partitions(copier, part_callback, lvm=True)

        if mount_points is None or len(
                mount_points) < 2 or mount_points[0] == mount_points[1]:
            source_dir = "/tmp/" + str(random.randint(0, 100000))
            target_dir = "/tmp/" + str(random.randint(-100000, -1))
            os.makedirs(source_dir, exist_ok=True)
            os.makedirs(target_dir, exist_ok=True)
            mount_points = (source_dir, target_dir)

        print(_("Beginning to copy files."))
        copier.copy_files(mount_points[0],
                          mount_points[1],
                          excluded_partitions,
                          ignore_copy_failures,
                          rsync_args,
                          callback=copy_callback)
        print(_("Finished copying files."))

        print(_("Making bootable"))
        try:
            copier.make_bootable(bootloader, mount_points[0], mount_points[1],
                                 excluded_partitions, root_partition,
                                 boot_partition, efi_partition, boot_callback)
        except DeviceError as ex:
            print(_("Error making drive bootable. All files should be fine."))
            return ex
        print(_("All done, enjoy your drive!"))
        return True
    finally:

        def delete_loop(loop_name):
            subprocess.call(["losetup", "-d", loop_name])

        if source_loop is not None:
            delete_loop(source_loop)
        if target_loop is not None:
            delete_loop(target_loop)