API Documentation

weresync.interface

This modules has easy, one function interfaces with the DeviceCopier and DeviceManager.

weresync.interface.DEFAULT_LOG_LOCATION = '/var/log/weresync/weresync.log'

The default location for WereSync's log files.

weresync.interface.LANGUAGES = ['en']

Currently translated languages. See here for more information.

weresync.interface.check_python_version()[source]

This method tests if the running version of python supports WereSync. If it does not, it raises a InvalidVersionException

weresync.interface.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='-aAXxH --delete', lvm_source=None, lvm_target=None, bootloader='uuid_copy', part_callback=None, copy_callback=None, boot_callback=None)[source]

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.

Parameters:
  • source -- The drive identifier ("/dev/sda" or the like) of the source drive.
  • target -- The drive identifier ("/dev/sda" or the like) of the target drive.
  • 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.
  • 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}".
  • target_part_mask -- Same as source_part_mask, but applied to target drive. Defaults to "{0}{1}"
  • excluded_partitions -- Partitions to not copy or test for boot capability.
  • 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.
  • root_partition -- If not None, this is an int that determines which partition grub should be installed to. Defaults to None.
  • boot_partition -- If not None, this is an int that represents the partition to mount at /boot when installing grub.
  • efi_partition -- If not None, this is an int that represents the partition to mount at /boot/efi when installing grub.
  • 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.
  • lvm -- the Logical Volume Group to copy to the new drive.
  • 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.
  • 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
  • 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.
  • 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.

weresync.interface.copy_partitions(copier, part_callback=None, lvm=False)[source]

Checks if partitions are valid and copies if they aren't.

Parameters:
weresync.interface.create_new_vg_if_not_exists(lvm, name, target)[source]

Creates a new Logical Volume Group with the name lvm + "copy" and all of the partitions of the target with type "lvm" added to it.

This is not a conclusive function and misses several uses of LVM drives, if your situation is not covered, please feel free to open a pull request fixing it.

Parameters:
  • lvm -- a string representing the name of the source lvm
  • target -- a DeviceManager representing the device whose partitions to add to the LVM.
weresync.interface.enable_localization()[source]

Activates the gettext module to start internalization and enable translation.

weresync.interface.main()[source]

The entry point for the command line function. This uses argparse to parse arguments to call call copy_drive() with. For help use "weresync -h" in a commandline after installation.

weresync.interface.mount_loop_device(image_file)[source]

Mounts an image file as a loop device and returns the device name of the mounted loop. This mounts on first free loop device. This accepts relative paths.

Params image_file:
 Path pointing to the image file to mount.
Returns:A string containing device identifier (/dev/sda or such)

weresync.device

This module contains the classes used to modify block devices and copy data.

weresync.device.DEFAULT_RSYNC_ARGS = '-aAXxH --delete'

Default arguments passed to rsync. See rsync documentation for what they do.

class weresync.device.DeviceCopier(source, target, lvm_source=None, lvm_target=None)[source]

DeviceCopiers transfer data from a source drive to a target drive.

Parameters:
  • source -- the drive identifier (/dev/sda or such) of the source drive.
  • target -- the drive identifier (/dev/sdb or such) of the target drive.
  • lvm_source -- the name or LVMDeviceManager object representing the source LVM. If None no LVM is copied.
  • lvm_target -- the name or LVMDeviceManager object representing the target LVM. If None no LVM is copied.
copy_files(mnt_source, mnt_target, excluded_partitions=[], ignore_failures=True, rsync_args='-aAXxH --delete', callback=None)[source]

Copies all files from source to target drive, doing one partition at a time. This assumes that the two drives have equivalent partition mappings, i.e. that the data on partition 1 of the source drive should be on partition 1 of the target drive.

Parameters:
  • mnt_source -- The directory to mount partitions from the source drive on.
  • mnt_target -- The directory to mount partitions from the target drive on.
  • excluded_partitions -- A list containing the partitions to not copy. Defaults to empty.
  • ignore_failures -- If True, errors encountered for a partition will not cause the function to exit, but we instead cause a warning to be logged. Defaults to true.
  • callaback -- If not None, a function with the signature callback(int, float), where the int represents partition number and float represents progress. If an error occurs, the float will be negative. If the float should pulse, it wil return True.
format_partitions(ignore_errors=True, callback=None, lvm=False)[source]

Goes through each partition in the source drive and formats the corresponding partition in the target drive to the same thing.

Parameters:
  • ignore_errrors -- whether or not errors (such as a file-system not recongized by mkfs) should be ignored. If True such errors will be logged. If false the exceptions will be propogated. Defaults to True.
  • callback -- If not None, this function should be a function that accepts a float between 0 and 1 to update the progress.
  • lvm -- Whether or not to use the lvm managers.
get_uuid_dict()[source]

Generates a dictionary that relates the partitions of the source drive to the partitions of the target drive.

This function requires that both drives have the same number of partitions with the same numbers.

This function caches its result in self.uuid_dict. After changing drive uuids, self.uuid_dict should be set to None.

Returns:A dictionary where the keys are the source drive partition UUIDs, and the values are the corresponding target drive UUIDs.
make_bootable(plugin_name, source_mnt, target_mnt, excluded_partitions=[], root_partition=None, boot_partition=None, efi_partition=None, callback=None)[source]

Calls the appropriate plugin to make the target drive bootable.

Parameters:
  • plugin_name -- the name, not pretty name, of the plugin to use. If None, so bootloading occurs, other than fstab copying.
  • source_mnt -- a string representing the directory where partitions from the source drive may be mounted.
  • target_mnt -- a string representing the directory where partitions from the target drive may be mounted.
  • copier -- an instance of DeviceCopier which represents the source and target drives.
  • excluded_partitions -- these partitions should not be searched or included in the boot installation.
  • boot_partition -- this is the partition that should be mounted on /boot of the root_partition.
  • root_partition -- this is the root partition of the drive, where the bootloader should be installed.
  • efi_partition -- this is the partition of the Efi System Partition. Should be None if not a UEFI system.
partitions_valid(lvm=False)[source]

Tests if the partitions on the target drive can support copying files from the source drive.

Returns:True if no errors found.
Raises:a CopyError if any part invalid.
transfer_partition_table(resize=True, callback=None)[source]

Transfers the partition table from one drive to another. Afterwards, it formats the partitions on the target drive to be the same as those on the source drive.

Parameters:
  • resize -- if true (default) then the program will attempt to resize the partition tables to fit on a smaller drive. This will not expand partition tables at any time.
  • callback -- If not none, a function to update progress completed. See py:func:~.format_partitions()
class weresync.device.DeviceManager(device, partition_mask='{0}{1}')[source]

A class that allows various operations on a device.

Most methods in this class raise DeviceError if the subprocess they initiate has an error.

Parameters:
  • device -- a string containing the device identifier (/dev/sda or the like)
  • partition_mask -- a string as the first parameter of a the expression: "partion_mask.format(device, partition)" resolving to a string. Defaults to "{0}{1}".
get_drive_size()[source]

Returns the maximum size of the drive, in sectors.

get_drive_size_bytes()[source]

Returns the maximum size of the drive, in bytes.

get_empty_space()[source]

Returns the amount of empty space after the last partition. This does not include space hidden between partitions.

Returns:An integer representing the number of sectors free after the last partition.
Raises:DeviceError -- If the command has a non-zero return code
get_part_uuid(partition_num)[source]

Gets the PARTUUID for a given partition, if it exists. This is not the filesystem UUID and is different from py:func:~.DeviceManager.get_partition_uuid.

Parameters:partition_num -- the number of the partition whose PARTUUID to get.
Returns:a string containing the partition's PARTUUID.
get_partition_alignment()[source]

Returns the number of sectors the drive must be aligned to. For GPT disk this is found using sgdisk's output. This function is only supported by GPT disks.

Returns:An integer that represents the partition alignment of the device.
Raises:DeviceError -- If the command returns a non-zero return code
get_partition_code(partition_num)[source]

For GPT disks: gets the partition code (as defined by sgdisk) for the passed partition number.

For MBR disks: gets the partition code (as defined by sfdisk) for the passed partition number.

Parameters:

partition_num -- the number of the partition whose code to find.

Raises:
  • DeviceError -- If the command returns a non-zero return code.
  • ValueError -- If an invalid partition number (one that doesn't exist) is passed.
Returns:

a string containing the partition code for the appropriate disk type.

get_partition_file_system(part_num)[source]

Returns the file system (ext4, ntfs, etc.) of the partition. If a partition system that can't be created by this system is found, None is return.

Parameters:part_num -- the partition number whose filesystem to get.
Returns:A string containing the file system type if a type found, otherwise None. If this is a swap partition this returns 'swap'
get_partition_size(partition_num)[source]

Gets the size of a partition in 512B sectors.

Parameters:

partition_num -- An int representing the partition whose size to get.

Returns:

An int representing the number of 512B blocks in the partition

Raises:
  • DeviceError -- if the commands getting the size of the partition fail.
  • ValueError -- if the drive has an unsupported partition table type.
get_partition_table_type()[source]

Gets the type of partition table on the device. Usually "gpt" for GPT disks or "msdos" for MBR disks.

Returns:

A string containing the name of the partition table.

Raises:
get_partition_used(partition_num)[source]

Returns the space available in a drive in 512B blocks

Parameters:partition_num -- the number of the partition to check
get_partition_uuid(partition_num)[source]

Gets the UUID for a given partition. This is not the filesystem UUID.

Parameters:partition_num -- the number of the partition whose UUID to get.
Returns:a string containing the partition's UUID
get_partitions()[source]

Returns a list with all the partitions in the drive. The partitions will be listed in the order they appear on the disk. So if partition 4 has a start sector of 500, it will appear before partition 1 that has a start sectro of 2000

Returns:A list of integers representing the partition numbers
mount_partition(partition_num, mount_loc)[source]

Mounts the specified partition at the specified location.

Parameters:
  • partition_num -- the number of the partition to mount
  • mount_loc -- the location at which to mount the drive
Raises:

DeviceError if there is an error mounting the device.

mount_point(partition_num)[source]

Returnds an absolute path to the mountpoint of the specific partition. Returns None if no mountpoint found (probably because partion not mounted). The return will not have a trailing slash.

Parameters:partition_num -- The partition of whose mount to find.
set_partition_file_system(part_num, system_type)[source]

Creates a new file system on the passed partition number. This is essentially the same as formatting the partition. If the passed partition is currently mounted, this will unmount the system, and remount it when finished.

Parameters:
  • part_num -- the partition number to format
  • system_type -- a file system (ex. ntfs) supported by mkfs on the current system.
unmount_partition(partition_num)[source]

Unmounts a device.

Parameters:partition_num -- the number of the partition to unmount.
Raises:DeviceError if the partition is busy or the partition is not mounted.
class weresync.device.LVMDeviceManager(device, partition_mask='{0}/{1}')[source]

This is an extension of the DeviceManager class which handles Logical Volume groups. All method names referring to "partition" in fact refer to logical volumes, but the names remain the same for compatibility reasons.

Parameters:source -- The name of the logical volume group.
get_drive_size()[source]

Normally this function returns the drive size in sectors.

Returns:an integer representing the size of the drive in sectors
Raises:DeviceError -- if the command returns a non-zero return code
get_drive_size_bytes()[source]

Returns the of a logical volume group in bytes.

get_empty_space()[source]

Gets the amount of empty space left in the logical volume group. Due to a lack of strict ordering in lvm, this is considered to be the total space, not simply the space after the last partition.

Returns:An integer representing the number of sectors free in the volume group.
Raises:DeviceError -- If the command has a non-zero return value.
get_partition_alignment()[source]

Not valid for LVM drives.

Raises:UnsupportedDeviceError --
get_partition_code(partition_num)[source]

Not valid for LVM drives.

Raises:UnsupportedDeviceError --
get_partition_size(partition_name)[source]

Gets the size, in sectors, of a logical volume.

Parameters:partition_name -- the name of the logical volume whose size to get.
Returns:an int representing the number of bytes on the logical volume.
Raises:DeviceError -- if the commands for getting the size of the partition fail to return a 0 return code.
get_partitions()[source]

Returns the names of the logical volumes in the group, as a list of strings.

weresync.device.SUPPORTED_PARTITION_TABLE_TYPES = ['gpt', 'msdos']

The names, as reported by parted of the partition table types supported by this program.

weresync.device.multireplace(string, replacements)[source]

Given a string and a replacement map, it returns the replaced string.

Credit goes to bgusach

Parameters:
  • string (str) -- string to execute replacements on
  • replacements (dict) -- replacement dictionary {value to find: value to replace}
Returns:

a string with the replaced text.

weresync.exception

Exceptions used by WereSync

exception weresync.exception.CopyError(message, errors=None)[source]

Exception thrown to show errors caused by an issue copying data, usually both devices face the issue.

exception weresync.exception.DeviceError(device, message, errors=None)[source]

Exception thrown to show errors caused by an issue with a specific device.

exception weresync.exception.InvalidVersionError[source]

Exception thrown when the version of python being used does not support the feature.

exception weresync.exception.PluginNotFoundError[source]

Exception thrown when the passed plugin is not found.

exception weresync.exception.UnsupportedDeviceError[source]

Exception thrown to show that action is not supported on the partition table type of the device.

weresync.plugins

This package contains code for bootloader plugins.

Bootloader plugin is any class extending IBootPlugin inside a file name following the regex pattern "^weresync_.*.py$". These plugins can be installed to the site-packages directory (for example, using pip) or to /usr/local/weresync/plugins.

For an example plugin see the GrubPlugin.

class weresync.plugins.IBootPlugin(name, prettyName=None)[source]

An interface class for bootloader plugins. Plugins implementing this class must implement the install_bootloader() method.

The name of a plugin should simply be its filename, without the "weresync_" prefix or a file extension. So "weresync_grub2.py"'s name would be "grub2".

Parameters:
  • prettyName -- a human readable name for display. Can contain any character.
  • name -- A unique identifying name that should be easy to type on a terminal. Used by users to tell WereSync which plugin to use. See above for exact definition.
activate()[source]

Called at plugin activation,right before bootloader is installed.

This will be called before /etc/fstab has been updated.

deactivate()[source]

Called at plugin deactivation, after bootloader has been installed.

get_help()[source]

Returns the help message for this plugin.

It is optional to override this.

Returns:a string representing the help message for this plugin.
install_bootloader(source_mnt, target_mnt, copier, excluded_partitions=[], boot_partition=None, root_partition=None, efi_partition=None)[source]

Called to make the drive bootable.

This will be called after /etc/fstab has been updated.

Parameters:
  • source_mnt -- a string representing the directory where partitions from the source drive may be mounted.
  • target_mnt -- a string representing the directory where partitions from the target drive may be mounted.
  • copier -- an instance of DeviceCopier which represents the source and target drives.
  • excluded_partitions -- these partitions should not be searched or included in the boot installation.
  • boot_partition -- this is the partition that should be mounted on /boot of the root_partition.
  • root_partition -- this is the root partition of the drive, where the bootloader should be installed.
  • efi_partition -- this is the partition of the Efi System Partition. Should be None if not a UEFI system.
Raises:

DeviceError -- if a bootloader installation command has an error.

weresync.plugins.get_manager()[source]

Returns the PluginManager for this instance of WereSync

weresync.plugins.mount_partition(manager, lvm_manager, part, mount_point)[source]

Mounts a partition and figures out whether or not the partition is in the LVM drive. It assumes a numerical partition is not a logical volume.

Parameters:
  • manager -- a DeviceManager object representing a possible mount.
  • lvm_manager -- a LVMDeviceManager object representing a possible host for the mount.
  • part -- the name or number of the partition to mount.
  • mount_point -- the location to mount the partition.
weresync.plugins.search_for_boot_part(target_mnt, target_manager, search_folder, exlcuded_partitions=[])[source]

Finds the partition that is the boot partition, by searching for a specific folder name. The first partition that contains this name or /boot/<name> will be returned.

Parameters:
  • target_mnt -- the folder to mount partitions
  • target_manager -- The DeviceManager class representing the drive to search.
  • search_folder -- The name of the folder to search for
  • excluded_partitions -- A list containing a list of partitions which should not be searched.
weresync.plugins.translate_uuid(copier, partition, path, target_mnt)[source]

Translates all uuids of the files in the given partition at path. This will not affect files which are not UTF-8 or ASCII, and it will not affect files which are greater than 200 MB.

Parameters:
  • copier -- the object with the DeviceManager instances.
  • partition -- the partition number of the partition to translate.
  • path -- the path of the file to change relative to the mount of the partition. Should start with "/".
  • target_mnt -- the path to the folder where the partitions should be mounted.