Merge lp:~blake-rouse/maas/fix-mbr-max-partition-size into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 4361
Proposed branch: lp:~blake-rouse/maas/fix-mbr-max-partition-size
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 339 lines (+162/-12)
7 files modified
src/maasserver/models/partition.py (+19/-0)
src/maasserver/models/partitiontable.py (+7/-3)
src/maasserver/models/tests/test_filesystemgroup.py (+5/-2)
src/maasserver/models/tests/test_partition.py (+35/-0)
src/maasserver/models/tests/test_partitiontable.py (+12/-0)
src/maasserver/storage_layouts.py (+36/-7)
src/maasserver/tests/test_storage_layouts.py (+48/-0)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-mbr-max-partition-size
Reviewer Review Type Date Requested Status
Ricardo Bánffy (community) Approve
Review via email: mp+273641@code.launchpad.net

Commit message

Don't allow creating partitions larger than 2TiB for MBR partition tables. When creating the LVM storage layout add extra partitions to fill the disks.

To post a comment you must log in.
Revision history for this message
Ricardo Bánffy (rbanffy) wrote :

Looks good. I'd rename a couple tests to make the expected result more explicit (I had to read the code to get what was being tested).

review: Approve
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Thanks for the review. I made the suggested changes.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/models/partition.py'
--- src/maasserver/models/partition.py 2015-09-28 17:43:23 +0000
+++ src/maasserver/models/partition.py 2015-10-07 14:48:54 +0000
@@ -44,6 +44,7 @@
4444
4545
46MIN_PARTITION_SIZE = MIN_BLOCK_DEVICE_SIZE46MIN_PARTITION_SIZE = MIN_BLOCK_DEVICE_SIZE
47MAX_PARTITION_SIZE_FOR_MBR = 2 * (1024 ** 4) # 2TiB
4748
4849
49class PartitionManager(Manager):50class PartitionManager(Manager):
@@ -247,6 +248,24 @@
247 else:248 else:
248 self.size = adjusted_size249 self.size = adjusted_size
249250
251 # Check that the size is not larger than MBR allows.
252 if (self.partition_table.table_type == PARTITION_TABLE_TYPE.MBR and
253 self.size > MAX_PARTITION_SIZE_FOR_MBR):
254 if self.id is not None:
255 raise ValidationError({
256 "size": [
257 "Partition %s cannot be resized to fit on the "
258 "block device; size is larger than the MBR "
259 "2TiB maximum." % (
260 self.id)],
261 })
262 else:
263 raise ValidationError({
264 "size": [
265 "Partition cannot be saved; size is larger than "
266 "the MBR 2TiB maximum."],
267 })
268
250 def delete(self):269 def delete(self):
251 """Delete the partition.270 """Delete the partition.
252271
253272
=== modified file 'src/maasserver/models/partitiontable.py'
--- src/maasserver/models/partitiontable.py 2015-09-24 16:22:12 +0000
+++ src/maasserver/models/partitiontable.py 2015-10-07 14:48:54 +0000
@@ -29,7 +29,10 @@
29)29)
30from maasserver.models.blockdevice import BlockDevice30from maasserver.models.blockdevice import BlockDevice
31from maasserver.models.cleansave import CleanSave31from maasserver.models.cleansave import CleanSave
32from maasserver.models.partition import Partition32from maasserver.models.partition import (
33 MAX_PARTITION_SIZE_FOR_MBR,
34 Partition,
35)
33from maasserver.models.timestampedmodel import TimestampedModel36from maasserver.models.timestampedmodel import TimestampedModel
34from maasserver.utils.converters import round_size_to_nearest_block37from maasserver.utils.converters import round_size_to_nearest_block
3538
@@ -109,8 +112,9 @@
109 """112 """
110 if size is None:113 if size is None:
111 size = self.get_available_size()114 size = self.get_available_size()
112 else:115 if self.table_type == PARTITION_TABLE_TYPE.MBR:
113 size = round_size_to_nearest_block(size, self.get_block_size())116 size = min(size, MAX_PARTITION_SIZE_FOR_MBR)
117 size = round_size_to_nearest_block(size, self.get_block_size())
114 return Partition.objects.create(118 return Partition.objects.create(
115 partition_table=self, size=size, uuid=uuid, bootable=bootable)119 partition_table=self, size=size, uuid=uuid, bootable=bootable)
116120
117121
=== modified file 'src/maasserver/models/tests/test_filesystemgroup.py'
--- src/maasserver/models/tests/test_filesystemgroup.py 2015-09-24 16:22:12 +0000
+++ src/maasserver/models/tests/test_filesystemgroup.py 2015-10-07 14:48:54 +0000
@@ -29,6 +29,7 @@
29 FILESYSTEM_GROUP_TYPE,29 FILESYSTEM_GROUP_TYPE,
30 FILESYSTEM_TYPE,30 FILESYSTEM_TYPE,
31 NODE_PERMISSION,31 NODE_PERMISSION,
32 PARTITION_TABLE_TYPE,
32)33)
33from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE34from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE
34from maasserver.models.filesystem import Filesystem35from maasserver.models.filesystem import Filesystem
@@ -1927,10 +1928,11 @@
1927 way as to make the RAID invalid (a 1-device RAID-0/1, a 2-device RAID-51928 way as to make the RAID invalid (a 1-device RAID-0/1, a 2-device RAID-5
1928 etc). The goal is to make sure we trigger the RAID internal validation.1929 etc). The goal is to make sure we trigger the RAID internal validation.
1929 """1930 """
1930 node = factory.make_Node()1931 node = factory.make_Node(bios_boot_method="uefi")
1931 device_size = 10 * 1000 ** 41932 device_size = 10 * 1000 ** 4
1932 partitions = [1933 partitions = [
1933 factory.make_PartitionTable(1934 factory.make_PartitionTable(
1935 table_type=PARTITION_TABLE_TYPE.GPT,
1934 block_device=factory.make_PhysicalBlockDevice(1936 block_device=factory.make_PhysicalBlockDevice(
1935 node=node, size=device_size)).add_partition()1937 node=node, size=device_size)).add_partition()
1936 for _ in range(4)1938 for _ in range(4)
@@ -1997,10 +1999,11 @@
1997 self.assertEqual(6 * partition_size, raid.get_size())1999 self.assertEqual(6 * partition_size, raid.get_size())
19982000
1999 def test_remove_invalid_partition_from_array_fails(self):2001 def test_remove_invalid_partition_from_array_fails(self):
2000 node = factory.make_Node()2002 node = factory.make_Node(bios_boot_method="uefi")
2001 device_size = 10 * 1000 ** 42003 device_size = 10 * 1000 ** 4
2002 partitions = [2004 partitions = [
2003 factory.make_PartitionTable(2005 factory.make_PartitionTable(
2006 table_type=PARTITION_TABLE_TYPE.GPT,
2004 block_device=factory.make_PhysicalBlockDevice(2007 block_device=factory.make_PhysicalBlockDevice(
2005 node=node, size=device_size)).add_partition()2008 node=node, size=device_size)).add_partition()
2006 for _ in range(10)2009 for _ in range(10)
20072010
=== modified file 'src/maasserver/models/tests/test_partition.py'
--- src/maasserver/models/tests/test_partition.py 2015-09-24 16:22:12 +0000
+++ src/maasserver/models/tests/test_partition.py 2015-10-07 14:48:54 +0000
@@ -243,6 +243,41 @@
243 "block device; not enough free space." % partition.id],243 "block device; not enough free space." % partition.id],
244 }, error.error_dict)244 }, error.error_dict)
245245
246 def test_test_cannot_create_mbr_partition_larger_than_2TiB(self):
247 block_device = factory.make_BlockDevice(size=3 * (1024 ** 4)) # 3TiB
248 partition_table = factory.make_PartitionTable(
249 block_device=block_device, table_type=PARTITION_TABLE_TYPE.MBR)
250 error = self.assertRaises(
251 ValidationError, factory.make_Partition,
252 partition_table=partition_table,
253 size=partition_table.get_available_size())
254 self.assertEquals({
255 "size": [
256 "Partition cannot be saved; size is larger than "
257 "the MBR 2TiB maximum."],
258 }, error.error_dict)
259
260 def test_test_cannot_resize_mbr_partition_to_more_than_2TiB(self):
261 block_device = factory.make_BlockDevice(size=3 * (1024 ** 4)) # 3TiB
262 partition_table = factory.make_PartitionTable(
263 block_device=block_device, table_type=PARTITION_TABLE_TYPE.MBR)
264 partition = partition_table.add_partition(size=1 * (1024 ** 4))
265 partition.size = 2.5 * (1024 ** 4)
266 error = self.assertRaises(ValidationError, partition.save)
267 self.assertEquals({
268 "size": [
269 "Partition %s cannot be resized to fit on the "
270 "block device; size is larger than the MBR "
271 "2TiB maximum." % partition.id],
272 }, error.error_dict)
273
274 def test_validate_can_save_gpt_larger_than_2TiB(self):
275 block_device = factory.make_BlockDevice(size=3 * (1024 ** 4)) # 3TiB
276 partition_table = factory.make_PartitionTable(
277 block_device=block_device, table_type=PARTITION_TABLE_TYPE.GPT)
278 # Test is that an error is not raised.
279 partition_table.add_partition()
280
246 def test_validate_enough_space_will_round_down_a_block(self):281 def test_validate_enough_space_will_round_down_a_block(self):
247 partition_table = factory.make_PartitionTable()282 partition_table = factory.make_PartitionTable()
248 partition = partition_table.add_partition()283 partition = partition_table.add_partition()
249284
=== modified file 'src/maasserver/models/tests/test_partitiontable.py'
--- src/maasserver/models/tests/test_partitiontable.py 2015-08-14 13:48:59 +0000
+++ src/maasserver/models/tests/test_partitiontable.py 2015-10-07 14:48:54 +0000
@@ -17,6 +17,7 @@
17from django.core.exceptions import ValidationError17from django.core.exceptions import ValidationError
18from maasserver.enum import PARTITION_TABLE_TYPE18from maasserver.enum import PARTITION_TABLE_TYPE
19from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE19from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE
20from maasserver.models.partition import MAX_PARTITION_SIZE_FOR_MBR
20from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE21from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE
21from maasserver.testing.factory import factory22from maasserver.testing.factory import factory
22from maasserver.testing.testcase import MAASServerTestCase23from maasserver.testing.testcase import MAASServerTestCase
@@ -70,6 +71,17 @@
70 self.assertEqual(71 self.assertEqual(
71 partition.size, MIN_BLOCK_DEVICE_SIZE * 2)72 partition.size, MIN_BLOCK_DEVICE_SIZE * 2)
7273
74 def test_add_partition_no_size_sets_mbr_max(self):
75 block_size = 4096
76 device = factory.make_BlockDevice(
77 size=3 * (1024 ** 4),
78 block_size=block_size)
79 partition_table = factory.make_PartitionTable(
80 table_type=PARTITION_TABLE_TYPE.MBR, block_device=device)
81 partition = partition_table.add_partition()
82 self.assertEqual(
83 partition.size, MAX_PARTITION_SIZE_FOR_MBR)
84
73 def test_add_second_partition_no_size(self):85 def test_add_second_partition_no_size(self):
74 """Tests whether a second partition with no specified size starts from86 """Tests whether a second partition with no specified size starts from
75 the end of the previous partition and stretches to the end of the87 the end of the previous partition and stretches to the end of the
7688
=== modified file 'src/maasserver/storage_layouts.py'
--- src/maasserver/storage_layouts.py 2015-09-24 16:22:12 +0000
+++ src/maasserver/storage_layouts.py 2015-10-07 14:48:54 +0000
@@ -31,6 +31,10 @@
31 is_precentage,31 is_precentage,
32)32)
33from maasserver.models.cacheset import CacheSet33from maasserver.models.cacheset import CacheSet
34from maasserver.models.partition import (
35 MAX_PARTITION_SIZE_FOR_MBR,
36 MIN_PARTITION_SIZE,
37)
34from maasserver.utils.forms import (38from maasserver.utils.forms import (
35 compose_invalid_choice_text,39 compose_invalid_choice_text,
36 set_form_error,40 set_form_error,
@@ -193,15 +197,29 @@
193 label="boot",197 label="boot",
194 mount_point="/boot")198 mount_point="/boot")
195 root_device = self.get_root_device()199 root_device = self.get_root_device()
200 root_size = self.get_root_size()
196 if root_device is None or root_device == self.boot_disk:201 if root_device is None or root_device == self.boot_disk:
202 # Fix the maximum root_size for MBR.
203 max_mbr_size = (
204 MAX_PARTITION_SIZE_FOR_MBR - self.boot_disk.block_size)
205 if (boot_partition_table.table_type == PARTITION_TABLE_TYPE.MBR and
206 root_size is not None and root_size > max_mbr_size):
207 root_size = max_mbr_size
197 root_partition = boot_partition_table.add_partition(208 root_partition = boot_partition_table.add_partition(
198 size=self.get_root_size())209 size=root_size)
210 return root_partition, boot_partition_table
199 else:211 else:
200 root_partition_table = PartitionTable.objects.create(212 root_partition_table = PartitionTable.objects.create(
201 block_device=root_device)213 block_device=root_device)
214 # Fix the maximum root_size for MBR.
215 max_mbr_size = (
216 MAX_PARTITION_SIZE_FOR_MBR - root_device.block_size)
217 if (root_partition_table.table_type == PARTITION_TABLE_TYPE.MBR and
218 root_size is not None and root_size > max_mbr_size):
219 root_size = max_mbr_size
202 root_partition = root_partition_table.add_partition(220 root_partition = root_partition_table.add_partition(
203 size=self.get_root_size())221 size=root_size)
204 return root_partition222 return root_partition, root_partition_table
205223
206 def configure(self, allow_fallback=True):224 def configure(self, allow_fallback=True):
207 """Configure the storage for the node."""225 """Configure the storage for the node."""
@@ -231,7 +249,7 @@
231 """Create the flat configuration."""249 """Create the flat configuration."""
232 # Circular imports.250 # Circular imports.
233 from maasserver.models.filesystem import Filesystem251 from maasserver.models.filesystem import Filesystem
234 root_partition = self.create_basic_layout()252 root_partition, _ = self.create_basic_layout()
235 Filesystem.objects.create(253 Filesystem.objects.create(
236 partition=root_partition,254 partition=root_partition,
237 fstype=FILESYSTEM_TYPE.EXT4,255 fstype=FILESYSTEM_TYPE.EXT4,
@@ -320,9 +338,20 @@
320 # Circular imports.338 # Circular imports.
321 from maasserver.models.filesystem import Filesystem339 from maasserver.models.filesystem import Filesystem
322 from maasserver.models.filesystemgroup import VolumeGroup340 from maasserver.models.filesystemgroup import VolumeGroup
323 root_partition = self.create_basic_layout()341 root_partition, root_partition_table = self.create_basic_layout()
342
343 # Add extra partitions if MBR and extra space.
344 partitions = [root_partition]
345 if root_partition_table.table_type == PARTITION_TABLE_TYPE.MBR:
346 available_size = root_partition_table.get_available_size()
347 while available_size > MIN_PARTITION_SIZE:
348 part = root_partition_table.add_partition()
349 partitions.append(part)
350 available_size -= part.size
351
352 # Create the volume group and logical volume.
324 volume_group = VolumeGroup.objects.create_volume_group(353 volume_group = VolumeGroup.objects.create_volume_group(
325 self.get_vg_name(), block_devices=[], partitions=[root_partition])354 self.get_vg_name(), block_devices=[], partitions=partitions)
326 logical_volume = volume_group.create_logical_volume(355 logical_volume = volume_group.create_logical_volume(
327 self.get_lv_name(), self.get_calculated_lv_size(volume_group))356 self.get_lv_name(), self.get_calculated_lv_size(volume_group))
328 Filesystem.objects.create(357 Filesystem.objects.create(
@@ -488,7 +517,7 @@
488 boot_size = self.get_boot_size()517 boot_size = self.get_boot_size()
489 if boot_size == 0:518 if boot_size == 0:
490 boot_size = 1 * 1024 ** 3519 boot_size = 1 * 1024 ** 3
491 root_partition = self.create_basic_layout(boot_size=boot_size)520 root_partition, _ = self.create_basic_layout(boot_size=boot_size)
492 cache_set = self.create_cache_set()521 cache_set = self.create_cache_set()
493 bcache = Bcache.objects.create_bcache(522 bcache = Bcache.objects.create_bcache(
494 cache_mode=self.get_cache_mode(), cache_set=cache_set,523 cache_mode=self.get_cache_mode(), cache_set=cache_set,
495524
=== modified file 'src/maasserver/tests/test_storage_layouts.py'
--- src/maasserver/tests/test_storage_layouts.py 2015-09-24 16:22:12 +0000
+++ src/maasserver/tests/test_storage_layouts.py 2015-10-07 14:48:54 +0000
@@ -25,6 +25,7 @@
25)25)
26from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE26from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE
27from maasserver.models.filesystemgroup import VolumeGroup27from maasserver.models.filesystemgroup import VolumeGroup
28from maasserver.models.partition import MAX_PARTITION_SIZE_FOR_MBR
28from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE29from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE
29from maasserver.storage_layouts import (30from maasserver.storage_layouts import (
30 BcacheStorageLayout,31 BcacheStorageLayout,
@@ -484,6 +485,34 @@
484 mount_point="/",485 mount_point="/",
485 ))486 ))
486487
488 def test__creates_layout_with_maximum_mbr_partition_size(self):
489 node = factory.make_Node()
490 boot_disk = factory.make_PhysicalBlockDevice(
491 node=node, size=3 * (1024 ** 4))
492 layout = FlatStorageLayout(node)
493 layout.configure()
494
495 # Validate partition table.
496 partition_table = boot_disk.get_partitiontable()
497 self.assertEquals(PARTITION_TABLE_TYPE.MBR, partition_table.table_type)
498
499 # Validate root partition.
500 partitions = partition_table.partitions.order_by('id').all()
501 root_partition = partitions[0]
502 self.assertIsNotNone(root_partition)
503 self.assertEquals(
504 round_size_by_blocks(
505 MAX_PARTITION_SIZE_FOR_MBR,
506 boot_disk.block_size),
507 root_partition.size)
508 self.assertThat(
509 root_partition.get_effective_filesystem(),
510 MatchesStructure.byEquality(
511 fstype=FILESYSTEM_TYPE.EXT4,
512 label="root",
513 mount_point="/",
514 ))
515
487 def test__creates_layout_with_uefi_defaults(self):516 def test__creates_layout_with_uefi_defaults(self):
488 node = make_Node_with_uefi_boot_method()517 node = make_Node_with_uefi_boot_method()
489 boot_disk = factory.make_PhysicalBlockDevice(518 boot_disk = factory.make_PhysicalBlockDevice(
@@ -961,6 +990,25 @@
961 mount_point="/",990 mount_point="/",
962 ))991 ))
963992
993 def test__creates_layout_with_multiple_mbr_partitions(self):
994 node = factory.make_Node()
995 boot_disk = factory.make_PhysicalBlockDevice(
996 node=node, size=7 * (1024 ** 4))
997 layout = LVMStorageLayout(node)
998 layout.configure()
999
1000 # Validate the volume group on root partition.
1001 partition_table = boot_disk.get_partitiontable()
1002 partitions = partition_table.partitions.order_by('id').all()
1003 root_partition = partitions[0]
1004 volume_group = VolumeGroup.objects.get(
1005 filesystems__partition=root_partition)
1006 self.assertIsNotNone(volume_group)
1007 self.assertEquals(
1008 4, partition_table.partitions.count(),
1009 "Should have 4 partitions.")
1010 self.assertEquals(MAX_PARTITION_SIZE_FOR_MBR, root_partition.size)
1011
9641012
965class TestBcacheStorageLayoutBase(MAASServerTestCase):1013class TestBcacheStorageLayoutBase(MAASServerTestCase):
9661014