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)
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)
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)
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
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)
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
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
def test_lvm_get_partition_alignment_unssported(): manager = device.LVMDeviceManager("/dev/fileserver") with pytest.raises(UnsupportedDeviceError) as execinfo: manager.get_partition_alignment()
def test_lvm_get_partition_code_unsupported(): manager = device.LVMDeviceManager("/dev/fileserver") with pytest.raises(UnsupportedDeviceError) as execinfo: manager.get_partition_code("media")
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
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
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)