Permalink
Browse files

Fixed bugs in disk.py were methods failed when working with a fresh d…

…isk, added custom exceptions, allow to enter sector_size manually on Size class.
  • Loading branch information...
1 parent 41c0009 commit 0248dbf73c9bee06fc2fdaac52ac860a279fa762 @xzased committed May 1, 2012
Showing with 241 additions and 71 deletions.
  1. +20 −21 README
  2. +21 −1 reparted/__init__.py
  3. +15 −10 reparted/conversion.py
  4. +20 −2 reparted/device.py
  5. +64 −29 reparted/disk.py
  6. +74 −0 reparted/exception.py
  7. +26 −7 reparted/size.py
  8. +1 −1 setup.py
View
41 README
@@ -1,25 +1,27 @@
-Ok, here is a quickie introduction -if u know what I mean-, so this is how it goes for now,
-you can set a device and add partitions to it using the following code:
+Reparted is my attempt at learning python ctypes module to create bindings for c api's so use it
+at your own risk. I was a bit confused on the way parted and pyparted interface works, my aim was
+to create a simple interface. It does not have the full set of features parted has to offer, only
+enough to create and delete partitions and set the labels. Feel free to check the code and point
+out enhancements. For any questions my email is rq.sysadmin@gmail.com, have fun!
-############################
+Quick Introduction to add partitions:
from reparted import *
-dev = device.Device("/dev/sdb") # or whatever your preferred dev is. If you call it without any
- # arguments it will probe all standard devices and default to the
- # first one it finds
+dev = device.Device("/dev/sdb") # or whatever your preferred dev is. If you call it without any
+ # arguments it will probe all standard devices and default to the
+ # first one it finds
mydisk = disk.Disk(dev)
sz = size.Size(dev, 200, "MB")
-new = disk.Partition(disk, sz) # this defaults the filesystem to ext3 and partition type "NORMAL"
-#new = Partition(disk, sz, fs="ext4") # set the filesystem blocks to ext4
-#new = Partition(disk, sz, fs="ext4", name="hire_ruben") # set a name
-#new = Partition(disk, sz, align='minimal') # sets the partition alignment to minimal.
+new = disk.Partition(mydisk, sz) # this defaults the filesystem to ext3 and partition type "NORMAL"
+#new = disk.Partition(mydisk, sz, fs="ext4") # set the filesystem blocks to ext4
+#new = disk.Partition(mydisk, sz, fs="ext4", name="myname") # set a name
+#new = disk.Partition(mydisk, sz, align='minimal') # sets the partition alignment to minimal.
mydisk.add_partition(new)
mydisk.commit()
-############################
-Partition Stuff
+Partition
Now you can access partition properties and methods from the partition instance "new":
@@ -39,15 +41,13 @@ alignment -Returns either 'minimal' or 'optimal' depending on the partitio
new partition, this may return optimal depending if the partition start
sector turns to be aligned.
-############################
-
-Disk Stuff
+Disk
You can create an instance of a disk by providing a device:
- dev = Device("/dev/sdb") # or whatever your preferred dev is.
- disk = Disk(dev)
+ dev = device.Device("/dev/sdb") # or whatever your preferred dev is.
+ mydisk = disk.Disk(dev)
These are the properties and methods for disk instances:
@@ -69,17 +69,16 @@ _ped_device -Returns the ctypes device structure.
get_partition -Returns a Partition instance given a partition number.
set_label -Takes a string (gpt or msdos) and sets the disk partition table.
-############################
-Device Stuff
+Device
You can create an instance of a device by providing the path (ie. /dev/sda). If the path
is not provided, it will probe all standard devices until it finds one and default to it.
- dev = Device("/dev/sdb") # or whatever your preferred dev is.
+ dev = device.Device("/dev/sdb") # or whatever your preferred dev is.
OR
- dev = Device()
+ dev = device.Device()
These are the properties and methods for device instances:
View
22 reparted/__init__.py
@@ -1 +1,21 @@
-__all__ = ['conversion', 'size', 'device', 'disk']
+#This file is part of reparted.
+
+#reparted is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#reparted is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with reparted. If not, see <http://www.gnu.org/licenses/>.
+
+__all__ = ['exception', 'conversion', 'size', 'device', 'disk']
+
+from exception import *
+from size import *
+from device import *
+from disk import *
View
25 reparted/conversion.py
@@ -1,3 +1,18 @@
+#This file is part of reparted.
+
+#reparted is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#reparted is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with reparted. If not, see <http://www.gnu.org/licenses/>.
+
from ctypes.util import find_library
from ctypes import *
@@ -46,15 +61,13 @@ class PedDevice(Structure):
('arch_specific', c_void_p),
]
-# Geometry stuff starts here
class PedGeometry(Structure):
_fields_ = [
('dev', POINTER(PedDevice)),
('start', PedSector),
('length', PedSector),
('end', PedSector),
]
-# Geometry stuff ends here
class PedPartition(Structure):
pass
@@ -134,8 +147,6 @@ class PedDiskOps(Structure):
('disk_specific', c_void_p),
]
-# Start of Alignment/Constraint stuff
-
class PedConstraint(Structure):
_fields_ = [
('start_align', POINTER(PedAlignment)),
@@ -145,7 +156,6 @@ class PedConstraint(Structure):
('min_size', PedSector),
('max_size', PedSector),
]
-# End of Alignment/Constraint stuff
# Device Function conversions
device_get = parted.ped_device_get
@@ -167,7 +177,6 @@ class PedConstraint(Structure):
device_get__constraint = parted.ped_device_get_constraint
device_get_constraint.argtypes = [POINTER(PedDevice)]
device_get_constraint.restype = POINTER(PedConstraint)
-#devices = POINTER(PedDevice)
# Disk Function conversions
disk_probe = parted.ped_disk_probe
@@ -191,17 +200,13 @@ class PedConstraint(Structure):
disk_delete_partition.argtypes = [POINTER(PedDisk), POINTER(PedPartition)]
disk_delete_all = parted.ped_disk_delete_all
disk_delete_all.argtypes = [POINTER(PedDisk)]
-# I would rather call commit_to_dev and commit_to_os manually
-#parted.ped_disk_commit.argtypes = [POINTER(PedDisk)]
disk_commit_to_os = parted.ped_disk_commit_to_os
disk_commit_to_os.argtypes = [POINTER(PedDisk)]
disk_commit_to_dev = parted.ped_disk_commit_to_dev
disk_commit_to_dev.argtypes = [POINTER(PedDisk)]
disk_destroy = parted.ped_disk_destroy
disk_destroy.argtypes = [POINTER(PedDisk)]
disk_destroy.restype = None
-#disk_print = parted.ped_disk_print
-#disk_print.argtypes = [POINTER(PedDisk)]
disk_get_type = parted.ped_disk_type_get
disk_get_type.restype = POINTER(PedDiskType)
disk_remove_partition = parted.ped_disk_remove_partition
View
22 reparted/device.py
@@ -1,5 +1,21 @@
+#This file is part of reparted.
+
+#reparted is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#reparted is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with reparted. If not, see <http://www.gnu.org/licenses/>.
+
from conversion import *
from disk import *
+from exception import DeviceError
import os
device_type = {
@@ -57,6 +73,8 @@ def __init__(self, path=None, dev=None):
self.__device = dev
else:
self.__device = self._probe_ped_device()
+ if not bool(self.__device):
+ raise DeviceError(500)
@property
def _ped_device(self):
@@ -131,9 +149,9 @@ def did(self):
def _probe_ped_device(self):
for path in standard_devices:
dev = device_probe(path)
- if dev:
+ if bool(dev):
return dev
- raise Exception("No devices found.")
+ raise DeviceError(500)
def probe_standard_devices():
devices = []
View
93 reparted/disk.py
@@ -1,4 +1,20 @@
+#This file is part of reparted.
+
+#reparted is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#reparted is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with reparted. If not, see <http://www.gnu.org/licenses/>.
+
from conversion import *
+from exception import *
import os
disk_features = {
@@ -40,8 +56,16 @@
"LEGACY_BOOT" : 15
}
-class DiskError(Exception):
- pass
+def diskDecorator(error=False):
+ def wrap(fn):
+ def wrapped(self):
+ if bool(self._ped_disk):
+ return fn(self)
+ if error:
+ raise DiskError(606)
+ return None
+ return wrapped
+ return wrap
class Disk(object):
def __init__(self, device=None, disk=None):
@@ -52,7 +76,7 @@ def __init__(self, device=None, disk=None):
self.__disk = disk
self.__device = self.__disk.contents.dev
else:
- raise Exception("Dude WTF?")
+ raise DiskError(600)
@property
def _ped_device(self):
@@ -63,34 +87,40 @@ def _ped_disk(self):
return self.__disk
@property
+ @diskDecorator()
def type_name(self):
return self.__disk.contents.type.contents.name
@property
+ @diskDecorator()
def device(self):
return self.__device
@property
+ @diskDecorator()
def type_features(self):
feat = self.__disk.contents.type.contents.features
return disk_features[feat]
@property
+ @diskDecorator()
def block_sizes(self):
return self.__disk.contents.block_sizes
@property
+ @diskDecorator()
def needs_clobber(self):
return bool(self.__disk.contents.needs_clobber)
@property
+ @diskDecorator()
def update_mode(self):
return bool(self.__disk.contents.update_mode)
+ @diskDecorator()
def partitions(self):
partitions = []
part = disk_next_partition(self.__disk, None)
-
while part:
if part.contents.type > 2:
part = disk_next_partition(self.__disk, part)
@@ -100,13 +130,12 @@ def partitions(self):
part = disk_next_partition(self.__disk, part)
return partitions
+ @diskDecorator(error=True)
def add_partition(self, part):
try:
p = self.get_partition(part.num)
if p.geom == part.geom:
- raise DiskError
- except DiskError:
- raise ValueError("This partition already exists.")
+ raise AddPartitionError(701)
except ValueError:
pass
partition = part._Partition__partition
@@ -116,63 +145,68 @@ def add_partition(self, part):
range_end = geometry_new(self.__device, end, 1)
user_constraint = constraint_new(alignment_any, alignment_any, range_start, range_end, 1, disk.contents.dev.contents.length)
if not bool(user_constraint):
- raise Exception("Could not set user defined constraint.")
+ raise AddPartitionError(702)
if part.alignment == 'optimal':
dev_constraint = device_get_optimal_aligned_constraint(self.__device)
elif part.alignment == 'minimal':
dev_constraint = device_get_minimal_aligned_constraint(self.__device)
else:
dev_constraint = device_get_constraint(self.__device)
if not bool(dev_constraint):
- raise Exception("Could not set user defined constraint.")
+ raise AddPartitionError(702)
final_constraint = constraint_intersect(user_constraint, dev_constraint)
constraint_destroy(user_constraint)
constraint_destroy(dev_constraint)
if not bool(final_constraint):
- raise Exception("Could not set device constraint.")
+ raise AddPartitionError(703)
added = disk_add_partition(disk, partition, final_constraint)
constraint_destroy(final_constraint)
if not added:
disk_remove_partition(disk, partition)
- raise Exception("Failed to add partition")
+ raise AddPartitionError(701)
if part.name:
set_name = partition_set_name(partition, part.name)
if not set_name:
disk_remove_partition(disk, partition)
- raise Exception("Failed to set partition name.")
+ raise AddPartitionError(704)
+ @diskDecorator(error=True)
def delete_partition(self, part):
if part and isinstance(part, Partition):
partition = part._Partition__partition
elif type(part) is int:
partition = self._get_ped_partition(part)
else:
- raise ValueError("You must specify a Partition instance or a partition number.")
+ raise DeletePartitionError(705)
if partition_is_busy(partition):
- raise Exception("Partition is busy, no sexy time for you!")
+ raise DeletePartitionError(706)
disk_delete_partition(self.__disk, partition)
self.commit()
disk_destroy(self.__disk)
self.__disk = disk_new(self.__device)
+ @diskDecorator()
def delete_all(self):
disk_delete_all(self.__disk)
return
+ @diskDecorator(error=True)
def commit(self):
to_dev = disk_commit_to_dev(self.__disk)
if not to_dev:
- raise Exception("Failed to commit disk changes to device.")
+ raise DiskCommitError(601)
to_os = disk_commit_to_os(self.__disk)
if not to_os:
- raise Exception("Failed to commit disk changes to OS.")
+ raise DiskCommitError(602)
+ @diskDecorator(error=True)
def _get_ped_partition(self, part_num):
partition = disk_get_partition(self.__disk, part_num)
if not bool(partition):
- raise ValueError("Partition number %i does not exist." % part_num)
+ raise PartitionError(705)
return partition
+ @diskDecorator(error=True)
def get_partition(self, part_num):
partition = Partition(part=self._get_ped_partition(part_num))
return partition
@@ -185,18 +219,19 @@ def _destroy_disk(self, disk=None):
disk_destroy(self._ped_disk)
self.__disk = None
else:
- raise Exception("Disk is not initialized biatch!")
+ raise DiskError(600)
def set_label(self, label):
if label not in disk_labels:
- raise ValueError("Disk label '%s' not supported." % label)
+ raise DiskError(603)
disk_type = disk_get_type(label)
if not bool(disk_type):
- raise Exception("Failed to get disk type.")
- self._destroy_disk()
+ raise DiskError(604)
+ if bool(self._ped_disk):
+ self._destroy_disk()
new_disk = disk_new_fresh(self._ped_device, disk_type)
if not bool(new_disk):
- raise Exception("Failed to create new disk.")
+ raise DiskError(605)
self.__disk = new_disk
self.commit()
self._destroy_disk(disk=new_disk)
@@ -213,7 +248,7 @@ def __init__(self, disk=None, size=None, type='NORMAL', fs='ext3', align='optima
elif disk and size:
self.__align = align
if not type in partition_type.values():
- raise ValueError("Invalid partition type '%s'" % type)
+ raise PartitionError(707)
part_type = [key for key,val in partition_type.iteritems() if val == type][0]
if type != 'EXTENDED' and fs != None:
filesystem = file_system_type_get(fs)
@@ -223,12 +258,12 @@ def __init__(self, disk=None, size=None, type='NORMAL', fs='ext3', align='optima
dev = disk._ped_device
a_start, a_end = self._get_alignment(dev, align, start, end, size)
else:
- raise ValueError("Alignment option '%s' does not exist." % align)
+ raise PartitionError(708)
self.__partition = partition_new(self.__ped_disk, part_type, filesystem, a_start, a_end)
if name:
self.set_name(name)
else:
- raise Exception("Dude WTF?")
+ raise PartitionError(700)
@property
def disk(self):
@@ -286,15 +321,15 @@ def set_name(self, name):
raise NotImplementedError("The disk does not support partition names.")
new_name = partition_set_name(self.__partition, name)
if not new_name:
- raise Exception("Failed to set name %s to partition." % name)
+ raise PartitionError(704)
return
def _snap_sectors(self, start, end, size):
if start:
if not end:
end = start + size.sectors - 1
if (end - start) != (size.sectors - 1):
- raise ValueError("Dude, your geometry is messed up.")
+ raise PartitionError(709)
else:
last_part_num = disk_get_last_partition_num(self.disk._ped_disk)
last_part = disk_get_partition(self.disk._ped_disk, last_part_num)
@@ -326,10 +361,10 @@ def _get_percent_size(self, length):
def _check_flag(self, flag):
if not flag in partition_flag.keys():
- raise ValueError("Flag '%s' not supported." % flag)
+ raise PartitionError(710)
check = partition_is_flag_available(self.__partition, partition_flag[flag])
if not check:
- raise ValueError("Flag '%s' not available for this partition." % flag)
+ raise PartitionError(710)
def set_flag(self, flag, state):
self._check_flag(flag)
View
74 reparted/exception.py
@@ -0,0 +1,74 @@
+class RepartedError(Exception):
+ """
+ Base Exception
+ """
+ def __init__(self, code):
+ self._register(code, code_dict=None)
+
+ def _register(self, code, code_dict):
+ self.code_dict = code_dict
+ self.code = code
+
+ def __str__(self):
+ return repr(self.code_dict.get(self.code, None))
+
+size_error_code = {
+ 400: "Device instance required for this operation.",
+ 401: "Invalid length type."
+}
+
+device_error_code = {
+ 500: "No device found."
+}
+
+disk_error_code = {
+ 600: "Failed to initialize disk.",
+ 601: "Failed to commit to device.",
+ 602: "Failed to commit to OS.",
+ 603: "Unsupported disk label.",
+ 604: "Failed to get disk type.",
+ 605: "Failed to create new disk.",
+ 606: "Method unavailable for initialized disk."
+}
+
+partition_error_code = {
+ 700: "Failed to initialize partition.",
+ 701: "Failed to add partition to disk.",
+ 702: "Failed to set user-defined constraint to disk.",
+ 703: "Failed to set device constraint to disk.",
+ 704: "Failed to set partition name to disk.",
+ 705: "Invalid partition instance or partition number.",
+ 706: "Partition is busy.",
+ 707: "Invalid partition type.",
+ 708: "Invalid alignment option.",
+ 709: "Invalid geometry.",
+ 710: "Unsupported flag.",
+}
+
+class SizeError(RepartedError):
+ def __init__(self, code):
+ self._register(code, size_error_code)
+
+class DeviceError(RepartedError):
+ def __init__(self, code):
+ self._register(code, device_error_code)
+
+class DiskError(RepartedError):
+ def __init__(self, code):
+ self._register(code, disk_error_code)
+
+class DiskCommitError(RepartedError):
+ def __init__(self, code):
+ self._register(code, disk_error_code)
+
+class PartitionError(RepartedError):
+ def __init__(self, code):
+ self._register(code, partition_error_code)
+
+class AddPartitionError(RepartedError):
+ def __init__(self, code):
+ self._register(code, partition_error_code)
+
+class DeletePartitionError(RepartedError):
+ def __init__(self, code):
+ self._register(code, partition_error_code)
View
33 reparted/size.py
@@ -1,3 +1,20 @@
+#This file is part of reparted.
+
+#reparted is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#reparted is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with reparted. If not, see <http://www.gnu.org/licenses/>.
+
+from exception import SizeError
+
size_units = {
"B": 1, # byte
"KB": 1000**1, # kilobyte
@@ -20,16 +37,18 @@
}
class Size(object):
- def __init__(self, dev, length, units="MB"):
+ def __init__(self, length, units="MB", sector_size=512, dev=None):
self.__length = length
self.units = units
- self.sector_size = dev.sector_size
- if units == "%":
- if not (0 < length <= 100) or type(length) is float:
- raise ValueError("%i%% is invalid" % length)
- self.sectors = (dev.length / 100) * length
- else:
+ self.sector_size = getattr(dev, "sector_size", sector_size)
+ if units != "%":
self.sectors = (size_units[units] * length) / self.sector_size
+ elif not dev:
+ raise SizeError(400)
+ elif not (0 < length <= 100) or type(length) is float:
+ raise SizeError(401)
+ else:
+ self.sectors = (dev.length / 100) * length
def to(self, units):
return size_units[units] * self.sectors
View
2 setup.py
@@ -3,7 +3,7 @@
from distutils.core import setup
setup(name='reparted',
- version='1.0',
+ version='1.1',
description='Python libparted bindings',
author='Ruben Quinones',
author_email='rq.sysadmin@gmail.com',

0 comments on commit 0248dbf

Please sign in to comment.