[PATCH 4/5] Account for partition alignment in PartitionDevice min/max size. (#1120964)

David Lehman dlehman at redhat.com
Thu Oct 9 21:53:56 UTC 2014


Ensure that end sectors are always aligned to avoid surprises since we will
align them in _computeResize.
---
 blivet/devices/partition.py | 38 +++++++++++++++++++++--
 tests/devices_test.py       | 75 +++++++++++++++++++++++++++++++++++++++++++++
 tests/storagetestcase.py    |  1 +
 3 files changed, 111 insertions(+), 3 deletions(-)

diff --git a/blivet/devices/partition.py b/blivet/devices/partition.py
index 6ad107a..b86eabd 100644
--- a/blivet/devices/partition.py
+++ b/blivet/devices/partition.py
@@ -571,13 +571,16 @@ class PartitionDevice(StorageDevice):
         else:
             self._postCreate()
 
-    def _computeResize(self, partition):
+    def _computeResize(self, partition, newsize=None):
         log_method_call(self, self.name, status=self.status)
 
+        if newsize is None:
+            newsize = self.targetSize
+
         # compute new size for partition
         currentGeom = partition.geometry
         currentDev = currentGeom.device
-        newLen = int(self.targetSize) / currentDev.sectorSize
+        newLen = int(newsize) / currentDev.sectorSize
         newGeometry = parted.Geometry(device=currentDev,
                                       start=currentGeom.start,
                                       length=newLen)
@@ -714,6 +717,28 @@ class PartitionDevice(StorageDevice):
     disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d))
 
     @property
+    def minSize(self):
+        min_size = super(PartitionDevice, self).minSize
+        if self.resizable:
+            # Adjust the min size as needed so that aligning the end sector
+            # won't drive the actual size below the formatting's minimum.
+            # This has the added benefit that we're always returning an aligned
+            # size.
+            (_constraint, geometry) = self._computeResize(self.partedPartition,
+                                                          newsize=min_size)
+            if Size(geometry.getLength(unit="B")) < min_size:
+                disk_device = self.disk.partedDevice
+                disk_alignment = self.disk.format.endAlignment
+                disk_geometry = parted.Geometry(device=disk_device,
+                                                start=0,
+                                                end=disk_device.length - 1)
+                geometry.end = disk_alignment.alignUp(disk_geometry,
+                                                      geometry.end + 1)
+                min_size = Size(geometry.getLength(unit="B"))
+
+        return min_size
+
+    @property
     def maxSize(self):
         """ The maximum size this partition can be. """
         # XXX Only allow growth up to the amount of free space following this
@@ -728,7 +753,14 @@ class PartitionDevice(StorageDevice):
             pass
         else:
             if partition.type == parted.PARTITION_FREESPACE:
-                maxPartSize = self.size + Size(partition.getLength(unit="B"))
+                # align the end sector
+                geometry = partition.geometry
+                free_start = geometry.start
+                free_end = self.disk.format.endAlignment.alignDown(geometry,
+                                                                   geometry.end)
+                free_sectors = free_end - free_start + 1
+                sector_size = self.disk.partedDevice.sectorSize
+                maxPartSize = self.size + Size(free_sectors * sector_size)
 
         maxFormatSize = self.format.maxSize
         return min(maxFormatSize, maxPartSize) if maxFormatSize else maxPartSize
diff --git a/tests/devices_test.py b/tests/devices_test.py
index 78cdc0f..ed361e4 100644
--- a/tests/devices_test.py
+++ b/tests/devices_test.py
@@ -1,6 +1,7 @@
 #!/usr/bin/python
 # vim:set fileencoding=utf-8
 
+import os
 import unittest
 
 from mock import Mock
@@ -22,10 +23,13 @@ from blivet.devices import LVMThinSnapShotDevice
 from blivet.devices import LVMVolumeGroupDevice
 from blivet.devices import MDRaidArrayDevice
 from blivet.devices import OpticalDevice
+from blivet.devices import PartitionDevice
 from blivet.devices import StorageDevice
 from blivet.devices import ParentList
+from blivet.devices import DiskFile
 from blivet.devicelibs import btrfs
 from blivet.size import Size
+from blivet.util import sparsetmpfile
 
 from blivet.formats import getFormat
 
@@ -736,6 +740,77 @@ class LVMDeviceTest(unittest.TestCase):
         snap1.exists = True
         self.assertEqual(snap1.dependsOn(thinlv), False)
 
+class PartitionDeviceTestCase(unittest.TestCase):
+
+    def testMinMaxSizeAlignment(self):
+        with sparsetmpfile("minsizetest", Size("10 MiB")) as disk_file:
+            disk = DiskFile(disk_file)
+            disk.format = getFormat("disklabel", device=disk.path)
+            grain_size = disk.format.alignment.grainSize
+            sector_size = disk.format.partedDevice.sectorSize
+            start = grain_size
+            end = start + (Size("6 MiB") / sector_size)
+            disk.format.addPartition(start, end)
+            partition = disk.format.partedDisk.getPartitionBySector(start)
+            self.assertNotEqual(partition, None)
+
+            device = PartitionDevice(os.path.basename(partition.path))
+            device.disk = disk
+            device.exists = True
+            device.partedPartition = partition
+
+            # Typical sector size is 512 B.
+            # Default optimum alignment grain size is 2048 sectors, or 1 MiB.
+            device.format = getFormat("ext4", device=device.path)
+            device.format.exists = True
+            device.format._minInstanceSize = Size("2 MiB") + (grain_size / 2)
+
+            ##
+            ## minSize
+            ##
+
+            # The end sector based only on format min size should be unaligned.
+            min_sectors = int(device.format.minSize / sector_size)
+            min_end_sector = partition.geometry.start + min_sectors - 1
+            self.assertEqual(
+                disk.format.endAlignment.isAligned(partition.geometry,
+                                                   min_end_sector),
+                False)
+
+            # The end sector based on device min size should be aligned.
+            min_sectors = int(device.minSize / sector_size)
+            min_end_sector = partition.geometry.start + min_sectors - 1
+            self.assertEqual(
+                disk.format.endAlignment.isAligned(partition.geometry,
+                                                   min_end_sector),
+                True)
+
+            ##
+            ## maxSize
+            ##
+
+            # Add a partition starting three sectors past an aligned sector and
+            # extending to the end of the disk so that there's a free region
+            # immediately following the first partition with an unaligned end
+            # sector.
+            free = disk.format.partedDisk.getFreeSpaceRegions()[-1]
+            raw_start = int(Size("9 MiB") / sector_size)
+            start = disk.format.alignment.alignUp(free, raw_start) + 3
+            disk.format.addPartition(start, disk.partedDevice.length - 1)
+
+            # Verify the end of the free region immediately following the first
+            # partition is unaligned.
+            free = disk.format.partedDisk.getFreeSpaceRegions()[1]
+            self.assertEqual(disk.format.endAlignment.isAligned(free, free.end),
+                             False)
+
+            # The end sector based on device min size should be aligned.
+            max_sectors = int(device.maxSize / sector_size)
+            max_end_sector = partition.geometry.start + max_sectors - 1
+            self.assertEqual(
+                disk.format.endAlignment.isAligned(free, max_end_sector),
+                True)
+
 class DeviceNameTestCase(unittest.TestCase):
     """Test device name validation"""
 
diff --git a/tests/storagetestcase.py b/tests/storagetestcase.py
index 11de630..a5fb5a6 100644
--- a/tests/storagetestcase.py
+++ b/tests/storagetestcase.py
@@ -37,6 +37,7 @@ class StorageTestCase(unittest.TestCase):
         # geometry
         blivet.devices.PartitionDevice._setTargetSize = StorageDevice._setTargetSize
         blivet.devices.PartitionDevice.maxSize = StorageDevice.maxSize
+        blivet.devices.PartitionDevice.minSize = StorageDevice.minSize
 
         def partition_probe(device):
             if isinstance(device._partedPartition, Mock):
-- 
1.9.3



More information about the anaconda-patches mailing list