Ejemplo n.º 1
0
def test_get_partition_alignment_msdos(monkeypatch):
    generateStandardMock(monkeypatch,
                         b"""Disk /dev/loop0: 7516 MB, 7516192768 bytes
255 heads, 63 sectors/track, 913 cylinders, total 14680064 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x524f0bf8

      Device Boot      Start         End      Blocks   Id  System
    """, b"", 0, "msdos")
    manager = device.DeviceManager("msdos.img")
    result = manager.get_partition_alignment()
    # Basically, this is testing if the physical partition is different then the logical partition. If so, then sectors will need to be aligned properly. That isn't the case here.
    assert result == 1
Ejemplo n.º 2
0
def test_get_partition_table_type_gpt(monkeypatch):
    generateStandardMock(monkeypatch, b"""Model:  (file)
Disk /media/Data/Documents/Programming/testing-super/gpt.img: 524MB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system  Name   Flags
 1      17.4kB  50.0MB  50.0MB               test
 2      50.3MB  74.4MB  24.1MB               cool
 3      74.4MB  200MB   126MB                nice
 4      200MB   350MB   150MB                great
 5      350MB   500MB   150MB                sweet
    6      500MB   524MB   24.1MB""", b"", 0, None)
    manager = device.DeviceManager("/dev/sda")
    result = manager.get_partition_table_type()
    assert "gpt" == result
Ejemplo n.º 3
0
def test_get_partition_code_newer_format(monkeypatch):
    generateStandardMock(monkeypatch, b"""Disk /dev/sdb: 5 GiB, 5368709120 bytes, 10485760 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0ee18f9a

Device     Boot  Start      End  Sectors  Size Id Type
/dev/sdb1  *      2048   350300   348253  170M 83 Linux
/dev/sdb2       352347 10485759 10133413  4,9G  5 Extended
/dev/sdb5       352349 10485759 10133411  4,9G 8e Linux LVM""",
                         b"", 0, "msdos")
    manager = device.DeviceManager("/dev/sdb")
    result = manager.get_partition_code(5)
    assert "8e" == result
Ejemplo n.º 4
0
def test_get_sector_alignment_number(monkeypatch):
    generateStandardMock(monkeypatch,
                         b"""Disk /dev/loop1: 512000 sectors, 250.0 MiB
Logical sector size: 512 bytes
Disk identifier (GUID): 4EB07926-DFE2-4D18-A2F4-75FB23616F71
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 511966
Partitions will be aligned on 2048-sector boundaries
Total free space is 5558 sectors (2.7 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          309247   150.0 MiB   8300  Linux filesystem
   2          309248          508422   97.3 MiB    8300  Linux filesystem
                      """, b"", 0)
    manager = device.DeviceManager("gpt.img")
    result = manager.get_partition_alignment()
    assert result == 2048
Ejemplo n.º 5
0
def test_get_partition_table_type_mbr(monkeypatch):
    generateStandardMock(monkeypatch, b"""Model:  (file)
Disk /media/Data/Documents/Programming/testing-super/mbr.img: 524MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start   End    Size    Type     File system  Flags
 1      1049kB  211MB  210MB   primary
 2      211MB   368MB  157MB   primary
 3      368MB   419MB  51.4MB  primary
 4      419MB   524MB  105MB   primary

    """, b"", 0, None)
    manager = device.DeviceManager("mbr.img")
    result = manager.get_partition_table_type()

    assert result == "msdos"
Ejemplo n.º 6
0
def test_get_partition_table_type_unsupported(monkeypatch):
    generateStandardMock(monkeypatch, b"""Model:  (file)
Disk /media/Data/Documents/Programming/testing-super/mbr.img: 524MB
Sector size (logical/physical): 512B/512B
Partition Table: blah

Number  Start   End    Size    Type     File system  Flags
 1      1049kB  211MB  210MB   primary
 2      211MB   368MB  157MB   primary
 3      368MB   419MB  51.4MB  primary
 4      419MB   524MB  105MB   primary

    """, b"", 0, None)
    manager = device.DeviceManager("blah.img")
    with pytest.raises(UnsupportedDeviceError) as execinfo:
        manager.get_partition_table_type()

    assert "blah" in str(execinfo)
Ejemplo n.º 7
0
def test_get_partition_code_mbr_invalid_value_passed(monkeypatch):
    generateStandardMock(monkeypatch,
                         b"""Disk /dev/loop0: 524 MB, 524288000 bytes
255 heads, 63 sectors/track, 63 cylinders, total 1024000 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x01517e72

      Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1 *          2048      411647      204800   83  Linux
/dev/loop0p2          411648      718847      153600   83  Linux
/dev/loop0p3 *        718848      819199       50176   83  Linux
/dev/loop0p4          819200     1023999      102400   83  Linux
    """, b"", 0, "msdos")
    with pytest.raises(ValueError) as execinfo:
        manager = device.DeviceManager("/dev/loop0", "{0}p{1}")
        manager.get_partition_code(5)
Ejemplo n.º 8
0
def test_partition_code(monkeypatch):
    generateStandardMock(monkeypatch,
                         b"""Disk /dev/nbd0: 16777216 sectors, 8.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 13E1C95B-5AC6-412B-930B-8F119760B86E
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 4029 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1          976896        11718655   5.1 GiB     8300
   2        11718656        14452735   1.3 GiB     8300
   3        14452736        16775167   1.1 GiB     8200
   4            2048          976895   476.0 MiB   EF02
                      """, b"", 0)
    manager = device.DeviceManager("gpt.img")
    result = manager.get_partition_code(3)
    assert "8200" == result
Ejemplo n.º 9
0
def test_get_drive_empty_space(monkeypatch):
    generateStandardMock(monkeypatch,
                         b"""Disk gpt.img: 1024000 sectors, 500.0 MiB
Logical sector size: 512 bytes
Disk identifier (GUID): 6FCE9962-D7B0-4BF3-B7BC-5E5CE8A5B0B0
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 1023966
Partitions will be aligned on 2-sector boundaries
Total free space is 647 sectors (323.5 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1              34           97656   47.7 MiB    8300  test
   2           98304          145407   23.0 MiB    8300  cool
   3          145408          391167   120.0 MiB   8300  nice
   4          391168          684031   143.0 MiB   8300  great
   5          684032          976895   143.0 MiB   8300  sweet
   6          976896         1023966   23.0 MiB    8300
                      """, b"", 0)
    manager = device.DeviceManager("gpt.img")
    result = manager.get_empty_space()
    assert result == 34
Ejemplo n.º 10
0
def test_get_partition_size(monkeypatch):
    generateStandardMock(monkeypatch,
                         b"""Disk /dev/loop0: 1024000 sectors, 500.0 MiB
Logical sector size: 512 bytes
Disk identifier (GUID): 4EB07926-DFE2-4D18-A2F4-75FB23616F71
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 1023966
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          309247   150.0 MiB   8300  Linux filesystem
   2          309248          821247   250.0 MiB   8300  Linux filesystem
   3          821248          972799   74.0 MiB    8300  Linux filesystem
   4          972800         1019903   23.0 MiB    8300  Linux filesystem
   5         1019904         1023966   2.0 MiB     8300  Linux filesystem
""", None, 0)
    monkeypatch.setattr(
        "weresync.device.DeviceManager.get_partition_table_type",
        lambda x: "gpt")
    manager = device.DeviceManager("gpt.img")
    result = manager.get_partition_size(5)
    assert 4062 == result
Ejemplo n.º 11
0
def test_mount_partition(monkeypatch):
    generateStandardMock(monkeypatch, b"", None, 0)
    manager = device.DeviceManager("/dev/sda")
    manager.mount_partition(3, "/mnt")
Ejemplo n.º 12
0
def test_get_partition_size_mbr(monkeypatch):
    generateStandardMock(monkeypatch, b"204800", b"", 0, "msdos")
    manager = device.DeviceManager("mbr.img")
    result = manager.get_partition_size(4)
    assert result == 204800
Ejemplo n.º 13
0
def test_get_partition_file_system_empty_return(monkeypatch):
    generateStandardMock(monkeypatch, b"", b"", 0)
    manager = device.DeviceManager("gpt.img")
    result = manager.get_partition_file_system(4)
    assert result == None
Ejemplo n.º 14
0
def test_get_partition_file_system_unsupported_type(monkeypatch):
    generateStandardMock(monkeypatch, b"completelyimpossiblefilesystemtype",
                         b"", 0)
    manager = device.DeviceManager("gpt.img")
    result = manager.get_partition_file_system(4)
    assert result == None
Ejemplo n.º 15
0
def test_get_partition_alignment_msdos_non_zero_return_code(monkeypatch):
    generateStandardMock(monkeypatch, b"", b"Error msdos", 1, "msdos")
    manager = device.DeviceManager("msdos.img")
    with pytest.raises(DeviceError) as exceinfo:
        result = manager.get_partition_alignment()
Ejemplo n.º 16
0
def test_unmount_partition(monkeypatch):
    generateStandardMock(monkeypatch, b"", b"", 0)
    manager = device.DeviceManager("/dev/sda")
    manager.unmount_partition(5)
Ejemplo n.º 17
0
def test_get_sector_alignment_number_invalid_return(monkeypatch):
    generateStandardMock(monkeypatch, b"No alignment", b"", 0)
    manager = device.DeviceManager("gpt.img")
    with pytest.raises(DeviceError) as execinfo:
        result = manager.get_partition_alignment()
Ejemplo n.º 18
0
def test_get_drive_size_bytes(monkeypatch):
    generateStandardMock(monkeypatch, b"190", b"", 0)
    manager = device.DeviceManager("/dev/sda")
    result = manager.get_drive_size_bytes()

    assert 190 == result
Ejemplo n.º 19
0
def test_mount_point_no_mount_point(monkeypatch):
    generateStandardMock(monkeypatch, b"", None,
                         1)  # findmnt returns 1 when there is no mount point
    manager = device.DeviceManager("/dev/sda")
    result = manager.mount_point(5)
    assert result == None
Ejemplo n.º 20
0
def test_get_partitions_no_partitions(monkeypatch):
    generateStandardMock(monkeypatch, b"Nope\nvery\nvery\nbad\ndata", None, 0)
    manager = device.DeviceManager("/dev/sda")
    result = manager.get_partitions()
    assert result == []
Ejemplo n.º 21
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)