[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