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
1=== modified file 'src/maasserver/models/partition.py'
2--- src/maasserver/models/partition.py 2015-09-28 17:43:23 +0000
3+++ src/maasserver/models/partition.py 2015-10-07 14:48:54 +0000
4@@ -44,6 +44,7 @@
5
6
7 MIN_PARTITION_SIZE = MIN_BLOCK_DEVICE_SIZE
8+MAX_PARTITION_SIZE_FOR_MBR = 2 * (1024 ** 4) # 2TiB
9
10
11 class PartitionManager(Manager):
12@@ -247,6 +248,24 @@
13 else:
14 self.size = adjusted_size
15
16+ # Check that the size is not larger than MBR allows.
17+ if (self.partition_table.table_type == PARTITION_TABLE_TYPE.MBR and
18+ self.size > MAX_PARTITION_SIZE_FOR_MBR):
19+ if self.id is not None:
20+ raise ValidationError({
21+ "size": [
22+ "Partition %s cannot be resized to fit on the "
23+ "block device; size is larger than the MBR "
24+ "2TiB maximum." % (
25+ self.id)],
26+ })
27+ else:
28+ raise ValidationError({
29+ "size": [
30+ "Partition cannot be saved; size is larger than "
31+ "the MBR 2TiB maximum."],
32+ })
33+
34 def delete(self):
35 """Delete the partition.
36
37
38=== modified file 'src/maasserver/models/partitiontable.py'
39--- src/maasserver/models/partitiontable.py 2015-09-24 16:22:12 +0000
40+++ src/maasserver/models/partitiontable.py 2015-10-07 14:48:54 +0000
41@@ -29,7 +29,10 @@
42 )
43 from maasserver.models.blockdevice import BlockDevice
44 from maasserver.models.cleansave import CleanSave
45-from maasserver.models.partition import Partition
46+from maasserver.models.partition import (
47+ MAX_PARTITION_SIZE_FOR_MBR,
48+ Partition,
49+)
50 from maasserver.models.timestampedmodel import TimestampedModel
51 from maasserver.utils.converters import round_size_to_nearest_block
52
53@@ -109,8 +112,9 @@
54 """
55 if size is None:
56 size = self.get_available_size()
57- else:
58- size = round_size_to_nearest_block(size, self.get_block_size())
59+ if self.table_type == PARTITION_TABLE_TYPE.MBR:
60+ size = min(size, MAX_PARTITION_SIZE_FOR_MBR)
61+ size = round_size_to_nearest_block(size, self.get_block_size())
62 return Partition.objects.create(
63 partition_table=self, size=size, uuid=uuid, bootable=bootable)
64
65
66=== modified file 'src/maasserver/models/tests/test_filesystemgroup.py'
67--- src/maasserver/models/tests/test_filesystemgroup.py 2015-09-24 16:22:12 +0000
68+++ src/maasserver/models/tests/test_filesystemgroup.py 2015-10-07 14:48:54 +0000
69@@ -29,6 +29,7 @@
70 FILESYSTEM_GROUP_TYPE,
71 FILESYSTEM_TYPE,
72 NODE_PERMISSION,
73+ PARTITION_TABLE_TYPE,
74 )
75 from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE
76 from maasserver.models.filesystem import Filesystem
77@@ -1927,10 +1928,11 @@
78 way as to make the RAID invalid (a 1-device RAID-0/1, a 2-device RAID-5
79 etc). The goal is to make sure we trigger the RAID internal validation.
80 """
81- node = factory.make_Node()
82+ node = factory.make_Node(bios_boot_method="uefi")
83 device_size = 10 * 1000 ** 4
84 partitions = [
85 factory.make_PartitionTable(
86+ table_type=PARTITION_TABLE_TYPE.GPT,
87 block_device=factory.make_PhysicalBlockDevice(
88 node=node, size=device_size)).add_partition()
89 for _ in range(4)
90@@ -1997,10 +1999,11 @@
91 self.assertEqual(6 * partition_size, raid.get_size())
92
93 def test_remove_invalid_partition_from_array_fails(self):
94- node = factory.make_Node()
95+ node = factory.make_Node(bios_boot_method="uefi")
96 device_size = 10 * 1000 ** 4
97 partitions = [
98 factory.make_PartitionTable(
99+ table_type=PARTITION_TABLE_TYPE.GPT,
100 block_device=factory.make_PhysicalBlockDevice(
101 node=node, size=device_size)).add_partition()
102 for _ in range(10)
103
104=== modified file 'src/maasserver/models/tests/test_partition.py'
105--- src/maasserver/models/tests/test_partition.py 2015-09-24 16:22:12 +0000
106+++ src/maasserver/models/tests/test_partition.py 2015-10-07 14:48:54 +0000
107@@ -243,6 +243,41 @@
108 "block device; not enough free space." % partition.id],
109 }, error.error_dict)
110
111+ def test_test_cannot_create_mbr_partition_larger_than_2TiB(self):
112+ block_device = factory.make_BlockDevice(size=3 * (1024 ** 4)) # 3TiB
113+ partition_table = factory.make_PartitionTable(
114+ block_device=block_device, table_type=PARTITION_TABLE_TYPE.MBR)
115+ error = self.assertRaises(
116+ ValidationError, factory.make_Partition,
117+ partition_table=partition_table,
118+ size=partition_table.get_available_size())
119+ self.assertEquals({
120+ "size": [
121+ "Partition cannot be saved; size is larger than "
122+ "the MBR 2TiB maximum."],
123+ }, error.error_dict)
124+
125+ def test_test_cannot_resize_mbr_partition_to_more_than_2TiB(self):
126+ block_device = factory.make_BlockDevice(size=3 * (1024 ** 4)) # 3TiB
127+ partition_table = factory.make_PartitionTable(
128+ block_device=block_device, table_type=PARTITION_TABLE_TYPE.MBR)
129+ partition = partition_table.add_partition(size=1 * (1024 ** 4))
130+ partition.size = 2.5 * (1024 ** 4)
131+ error = self.assertRaises(ValidationError, partition.save)
132+ self.assertEquals({
133+ "size": [
134+ "Partition %s cannot be resized to fit on the "
135+ "block device; size is larger than the MBR "
136+ "2TiB maximum." % partition.id],
137+ }, error.error_dict)
138+
139+ def test_validate_can_save_gpt_larger_than_2TiB(self):
140+ block_device = factory.make_BlockDevice(size=3 * (1024 ** 4)) # 3TiB
141+ partition_table = factory.make_PartitionTable(
142+ block_device=block_device, table_type=PARTITION_TABLE_TYPE.GPT)
143+ # Test is that an error is not raised.
144+ partition_table.add_partition()
145+
146 def test_validate_enough_space_will_round_down_a_block(self):
147 partition_table = factory.make_PartitionTable()
148 partition = partition_table.add_partition()
149
150=== modified file 'src/maasserver/models/tests/test_partitiontable.py'
151--- src/maasserver/models/tests/test_partitiontable.py 2015-08-14 13:48:59 +0000
152+++ src/maasserver/models/tests/test_partitiontable.py 2015-10-07 14:48:54 +0000
153@@ -17,6 +17,7 @@
154 from django.core.exceptions import ValidationError
155 from maasserver.enum import PARTITION_TABLE_TYPE
156 from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE
157+from maasserver.models.partition import MAX_PARTITION_SIZE_FOR_MBR
158 from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE
159 from maasserver.testing.factory import factory
160 from maasserver.testing.testcase import MAASServerTestCase
161@@ -70,6 +71,17 @@
162 self.assertEqual(
163 partition.size, MIN_BLOCK_DEVICE_SIZE * 2)
164
165+ def test_add_partition_no_size_sets_mbr_max(self):
166+ block_size = 4096
167+ device = factory.make_BlockDevice(
168+ size=3 * (1024 ** 4),
169+ block_size=block_size)
170+ partition_table = factory.make_PartitionTable(
171+ table_type=PARTITION_TABLE_TYPE.MBR, block_device=device)
172+ partition = partition_table.add_partition()
173+ self.assertEqual(
174+ partition.size, MAX_PARTITION_SIZE_FOR_MBR)
175+
176 def test_add_second_partition_no_size(self):
177 """Tests whether a second partition with no specified size starts from
178 the end of the previous partition and stretches to the end of the
179
180=== modified file 'src/maasserver/storage_layouts.py'
181--- src/maasserver/storage_layouts.py 2015-09-24 16:22:12 +0000
182+++ src/maasserver/storage_layouts.py 2015-10-07 14:48:54 +0000
183@@ -31,6 +31,10 @@
184 is_precentage,
185 )
186 from maasserver.models.cacheset import CacheSet
187+from maasserver.models.partition import (
188+ MAX_PARTITION_SIZE_FOR_MBR,
189+ MIN_PARTITION_SIZE,
190+)
191 from maasserver.utils.forms import (
192 compose_invalid_choice_text,
193 set_form_error,
194@@ -193,15 +197,29 @@
195 label="boot",
196 mount_point="/boot")
197 root_device = self.get_root_device()
198+ root_size = self.get_root_size()
199 if root_device is None or root_device == self.boot_disk:
200+ # Fix the maximum root_size for MBR.
201+ max_mbr_size = (
202+ MAX_PARTITION_SIZE_FOR_MBR - self.boot_disk.block_size)
203+ if (boot_partition_table.table_type == PARTITION_TABLE_TYPE.MBR and
204+ root_size is not None and root_size > max_mbr_size):
205+ root_size = max_mbr_size
206 root_partition = boot_partition_table.add_partition(
207- size=self.get_root_size())
208+ size=root_size)
209+ return root_partition, boot_partition_table
210 else:
211 root_partition_table = PartitionTable.objects.create(
212 block_device=root_device)
213+ # Fix the maximum root_size for MBR.
214+ max_mbr_size = (
215+ MAX_PARTITION_SIZE_FOR_MBR - root_device.block_size)
216+ if (root_partition_table.table_type == PARTITION_TABLE_TYPE.MBR and
217+ root_size is not None and root_size > max_mbr_size):
218+ root_size = max_mbr_size
219 root_partition = root_partition_table.add_partition(
220- size=self.get_root_size())
221- return root_partition
222+ size=root_size)
223+ return root_partition, root_partition_table
224
225 def configure(self, allow_fallback=True):
226 """Configure the storage for the node."""
227@@ -231,7 +249,7 @@
228 """Create the flat configuration."""
229 # Circular imports.
230 from maasserver.models.filesystem import Filesystem
231- root_partition = self.create_basic_layout()
232+ root_partition, _ = self.create_basic_layout()
233 Filesystem.objects.create(
234 partition=root_partition,
235 fstype=FILESYSTEM_TYPE.EXT4,
236@@ -320,9 +338,20 @@
237 # Circular imports.
238 from maasserver.models.filesystem import Filesystem
239 from maasserver.models.filesystemgroup import VolumeGroup
240- root_partition = self.create_basic_layout()
241+ root_partition, root_partition_table = self.create_basic_layout()
242+
243+ # Add extra partitions if MBR and extra space.
244+ partitions = [root_partition]
245+ if root_partition_table.table_type == PARTITION_TABLE_TYPE.MBR:
246+ available_size = root_partition_table.get_available_size()
247+ while available_size > MIN_PARTITION_SIZE:
248+ part = root_partition_table.add_partition()
249+ partitions.append(part)
250+ available_size -= part.size
251+
252+ # Create the volume group and logical volume.
253 volume_group = VolumeGroup.objects.create_volume_group(
254- self.get_vg_name(), block_devices=[], partitions=[root_partition])
255+ self.get_vg_name(), block_devices=[], partitions=partitions)
256 logical_volume = volume_group.create_logical_volume(
257 self.get_lv_name(), self.get_calculated_lv_size(volume_group))
258 Filesystem.objects.create(
259@@ -488,7 +517,7 @@
260 boot_size = self.get_boot_size()
261 if boot_size == 0:
262 boot_size = 1 * 1024 ** 3
263- root_partition = self.create_basic_layout(boot_size=boot_size)
264+ root_partition, _ = self.create_basic_layout(boot_size=boot_size)
265 cache_set = self.create_cache_set()
266 bcache = Bcache.objects.create_bcache(
267 cache_mode=self.get_cache_mode(), cache_set=cache_set,
268
269=== modified file 'src/maasserver/tests/test_storage_layouts.py'
270--- src/maasserver/tests/test_storage_layouts.py 2015-09-24 16:22:12 +0000
271+++ src/maasserver/tests/test_storage_layouts.py 2015-10-07 14:48:54 +0000
272@@ -25,6 +25,7 @@
273 )
274 from maasserver.models.blockdevice import MIN_BLOCK_DEVICE_SIZE
275 from maasserver.models.filesystemgroup import VolumeGroup
276+from maasserver.models.partition import MAX_PARTITION_SIZE_FOR_MBR
277 from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE
278 from maasserver.storage_layouts import (
279 BcacheStorageLayout,
280@@ -484,6 +485,34 @@
281 mount_point="/",
282 ))
283
284+ def test__creates_layout_with_maximum_mbr_partition_size(self):
285+ node = factory.make_Node()
286+ boot_disk = factory.make_PhysicalBlockDevice(
287+ node=node, size=3 * (1024 ** 4))
288+ layout = FlatStorageLayout(node)
289+ layout.configure()
290+
291+ # Validate partition table.
292+ partition_table = boot_disk.get_partitiontable()
293+ self.assertEquals(PARTITION_TABLE_TYPE.MBR, partition_table.table_type)
294+
295+ # Validate root partition.
296+ partitions = partition_table.partitions.order_by('id').all()
297+ root_partition = partitions[0]
298+ self.assertIsNotNone(root_partition)
299+ self.assertEquals(
300+ round_size_by_blocks(
301+ MAX_PARTITION_SIZE_FOR_MBR,
302+ boot_disk.block_size),
303+ root_partition.size)
304+ self.assertThat(
305+ root_partition.get_effective_filesystem(),
306+ MatchesStructure.byEquality(
307+ fstype=FILESYSTEM_TYPE.EXT4,
308+ label="root",
309+ mount_point="/",
310+ ))
311+
312 def test__creates_layout_with_uefi_defaults(self):
313 node = make_Node_with_uefi_boot_method()
314 boot_disk = factory.make_PhysicalBlockDevice(
315@@ -961,6 +990,25 @@
316 mount_point="/",
317 ))
318
319+ def test__creates_layout_with_multiple_mbr_partitions(self):
320+ node = factory.make_Node()
321+ boot_disk = factory.make_PhysicalBlockDevice(
322+ node=node, size=7 * (1024 ** 4))
323+ layout = LVMStorageLayout(node)
324+ layout.configure()
325+
326+ # Validate the volume group on root partition.
327+ partition_table = boot_disk.get_partitiontable()
328+ partitions = partition_table.partitions.order_by('id').all()
329+ root_partition = partitions[0]
330+ volume_group = VolumeGroup.objects.get(
331+ filesystems__partition=root_partition)
332+ self.assertIsNotNone(volume_group)
333+ self.assertEquals(
334+ 4, partition_table.partitions.count(),
335+ "Should have 4 partitions.")
336+ self.assertEquals(MAX_PARTITION_SIZE_FOR_MBR, root_partition.size)
337+
338
339 class TestBcacheStorageLayoutBase(MAASServerTestCase):
340