Merge lp:~blake-rouse/maas/fix-power8 into lp:~maas-committers/maas/trunk
- fix-power8
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Blake Rouse | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 4680 | ||||
Proposed branch: | lp:~blake-rouse/maas/fix-power8 | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
913 lines (+563/-21) 15 files modified
docs/changelog.rst (+20/-0) src/maasserver/models/node.py (+1/-1) src/maasserver/models/partition.py (+11/-1) src/maasserver/models/partitiontable.py (+20/-4) src/maasserver/models/tests/test_node.py (+8/-0) src/maasserver/models/tests/test_partition.py (+25/-1) src/maasserver/models/tests/test_partitiontable.py (+49/-1) src/maasserver/preseed_storage.py (+66/-8) src/maasserver/static/js/angular/controllers/node_details_storage.js (+11/-0) src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js (+51/-0) src/maasserver/storage_layouts.py (+6/-1) src/maasserver/tests/test_preseed_storage.py (+215/-1) src/maasserver/tests/test_storage_layouts.py (+78/-1) src/provisioningserver/boot/powerkvm.py (+1/-1) src/provisioningserver/boot/powernv.py (+1/-1) |
||||
To merge this branch: | bzr merge lp:~blake-rouse/maas/fix-power8 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+285234@code.launchpad.net |
Commit message
Fix storage configuration so it adds a prep partition at the beginning of the disk for deploying PowerNV. Fix PowerNV and PowerKVM to not create the EFI partition when the storage layout is generated.
Description of the change
Blake Rouse (blake-rouse) wrote : | # |
Thanks for the review.
"slope towards spaghetti", totally agree! But this is one branch that could not be avoided, due to the current state of MAAS in the release process. Because of Django 1.8 and migrations we cannot release any new migrations in MAAS 1.9 and this branch has a requirement to be in MAAS 1.9. The only benefit we get out of this is we get to hide this stupid partition from the user. This is also fallout on not having every architecture in our CI. If this would have existed in our CI we would have caught this very early in the MAAS 1.9 cycle and I would not be doing it now and it could have been done properly and not as some magic if case throughout the storage generation.
See inline comments for responses. Also you reviewed an earlier version of the branch, some things have already changed.
Gavin Panella (allenap) : | # |
Blake Rouse (blake-rouse) wrote : | # |
I am waiting to land this once curtin is fixed to work with the way we are creating partitions.
Once this bug is resolved and I can QA this I will land this and backport to 1.9.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~blake-rouse/maas/fix-power8 into lp:maas failed. Below is the output from the failed tests.
Get:1 http://
Hit:2 http://
Hit:3 http://
Hit:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Get:8 http://
Get:9 http://
Get:10 http://
Fetched 23.4 MB in 34s (688 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-1ubuntu1).
archdetect-deb is already the newest version (1.114ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
bind9 is already the newest version (1:9.10.
bind9utils is already the newest version (1:9.10.
build-essential is already the newest version (12.1ubuntu2).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubun
dh-apport is already the newest version (2.20-0ubuntu3).
dh-systemd is already the newest version (1.28ubuntu2)....
Preview Diff
1 | === modified file 'docs/changelog.rst' |
2 | --- docs/changelog.rst 2016-02-17 16:12:30 +0000 |
3 | +++ docs/changelog.rst 2016-02-24 16:12:28 +0000 |
4 | @@ -3,6 +3,26 @@ |
5 | ========= |
6 | |
7 | |
8 | +1.9.1 |
9 | +===== |
10 | + |
11 | +See https://launchpad.net/maas/+milestone/1.9.1 for full details. |
12 | + |
13 | +Bug Fix Update |
14 | +-------------- |
15 | + |
16 | +#1523779 Fix grub-install error on deploying power8 machines. |
17 | + |
18 | +#1526542 Skip block devices with duplicate serial numbers to fix multipath issue. |
19 | + |
20 | +#1536754 Upgrade from 1.8 to 1.9 lost connected macs in all but one network. |
21 | + |
22 | +#1532262 Fix failure to power query requests for SM15K servers. |
23 | + |
24 | +#1484696 Fix bug in apache2 maas config where it will reuse websocket connections |
25 | + to work around a bug in apache2 itself. |
26 | + |
27 | + |
28 | 1.9.0 |
29 | ===== |
30 | |
31 | |
32 | === modified file 'src/maasserver/models/node.py' |
33 | --- src/maasserver/models/node.py 2016-02-19 01:46:23 +0000 |
34 | +++ src/maasserver/models/node.py 2016-02-24 16:12:28 +0000 |
35 | @@ -158,7 +158,7 @@ |
36 | |
37 | # Holds the known `bios_boot_methods`. If `bios_boot_method` is not in this |
38 | # list then it will fallback to `DEFAULT_BIOS_BOOT_METHOD`. |
39 | -KNOWN_BIOS_BOOT_METHODS = ["pxe", "uefi"] |
40 | +KNOWN_BIOS_BOOT_METHODS = ["pxe", "uefi", "powernv", "powerkvm"] |
41 | |
42 | # Default `bios_boot_method`. See `KNOWN_BIOS_BOOT_METHOD` above for usage. |
43 | DEFAULT_BIOS_BOOT_METHOD = "pxe" |
44 | |
45 | === modified file 'src/maasserver/models/partition.py' |
46 | --- src/maasserver/models/partition.py 2015-12-01 18:12:59 +0000 |
47 | +++ src/maasserver/models/partition.py 2016-02-24 16:12:28 +0000 |
48 | @@ -180,7 +180,17 @@ |
49 | partitions_in_table = sorted(partitions_in_table, key=attrgetter('id')) |
50 | idx = partitions_in_table.index(self) |
51 | if self.partition_table.table_type == PARTITION_TABLE_TYPE.GPT: |
52 | - return idx + 1 |
53 | + # ppc64el machines get part1 skipped when this partition is on |
54 | + # the boot disk. This is because the prep partition is part1 and |
55 | + # is added when the preseed for storage is generated. |
56 | + node = self.get_node() |
57 | + arch, _ = node.split_arch() |
58 | + boot_disk = node.get_boot_disk() |
59 | + if (arch == "ppc64el" and |
60 | + self.partition_table.block_device.id == boot_disk.id): |
61 | + return idx + 2 |
62 | + else: |
63 | + return idx + 1 |
64 | elif self.partition_table.table_type == PARTITION_TABLE_TYPE.MBR: |
65 | # If more than 4 partitions then the 4th partition number is |
66 | # skipped because that is used for the extended partition. |
67 | |
68 | === modified file 'src/maasserver/models/partitiontable.py' |
69 | --- src/maasserver/models/partitiontable.py 2015-12-01 18:12:59 +0000 |
70 | +++ src/maasserver/models/partitiontable.py 2016-02-24 16:12:28 +0000 |
71 | @@ -40,6 +40,13 @@ |
72 | PARTITION_TABLE_EXTRA_SPACE = ( |
73 | INITIAL_PARTITION_OFFSET + END_OF_PARTITION_TABLE_SPACE) |
74 | |
75 | +# The amount of space required to be reserved for the prep partition. Prep |
76 | +# partition is required by all ppc64el architectures. Because of the way the |
77 | +# system boots it requires that a 8MiB prep partition exist with grub installed |
78 | +# on that partition. Without this partition the installation of grub will fail |
79 | +# on ppc64el and will fail to boot. |
80 | +PREP_PARTITION_SIZE = 8 * 1024 * 1024 # 8MiB |
81 | + |
82 | |
83 | class PartitionTable(CleanSave, TimestampedModel): |
84 | """A partition table on a block device. |
85 | @@ -64,7 +71,7 @@ |
86 | def get_size(self): |
87 | """Total usable size of partition table.""" |
88 | return round_size_to_nearest_block( |
89 | - self.block_device.size - PARTITION_TABLE_EXTRA_SPACE, |
90 | + self.block_device.size - self.get_overhead_size(), |
91 | PARTITION_ALIGNMENT_SIZE, |
92 | False) |
93 | |
94 | @@ -72,6 +79,15 @@ |
95 | """Block size of partition table.""" |
96 | return self.block_device.block_size |
97 | |
98 | + def get_overhead_size(self): |
99 | + """Return the total amount of extra space this partition table |
100 | + requires.""" |
101 | + extra_space = PARTITION_TABLE_EXTRA_SPACE |
102 | + node_arch, _ = self.block_device.node.split_arch() |
103 | + if node_arch == "ppc64el": |
104 | + extra_space += PREP_PARTITION_SIZE |
105 | + return extra_space |
106 | + |
107 | def get_used_size(self, ignore_partitions=[]): |
108 | """Return the used size of partitions on the table.""" |
109 | ignore_ids = [ |
110 | @@ -84,7 +100,7 @@ |
111 | if used_size is None: |
112 | used_size = 0 |
113 | # The extra space taken by the partition table header is used space. |
114 | - return used_size + PARTITION_TABLE_EXTRA_SPACE |
115 | + return used_size + self.get_overhead_size() |
116 | |
117 | def get_available_size(self, ignore_partitions=[]): |
118 | """Return the remaining size available for partitions.""" |
119 | @@ -133,8 +149,8 @@ |
120 | # placed on the boot disk. |
121 | if boot_disk is not None and self.block_device.id == boot_disk.id: |
122 | bios_boot_method = node.get_bios_boot_method() |
123 | - if bios_boot_method == "uefi": |
124 | - # UEFI must always use a GPT table. |
125 | + if bios_boot_method in ["uefi", "powernv", "powerkvm"]: |
126 | + # UEFI, PowerNV, or PowerKVM must always use a GPT table. |
127 | if not self.table_type: |
128 | self.table_type = PARTITION_TABLE_TYPE.GPT |
129 | elif self.table_type != PARTITION_TABLE_TYPE.GPT: |
130 | |
131 | === modified file 'src/maasserver/models/tests/test_node.py' |
132 | --- src/maasserver/models/tests/test_node.py 2016-02-19 01:46:23 +0000 |
133 | +++ src/maasserver/models/tests/test_node.py 2016-02-24 16:12:28 +0000 |
134 | @@ -390,6 +390,14 @@ |
135 | node = factory.make_Node(bios_boot_method="uefi") |
136 | self.assertEqual("uefi", node.get_bios_boot_method()) |
137 | |
138 | + def test_get_bios_boot_method_returns_powernv(self): |
139 | + node = factory.make_Node(bios_boot_method="powernv") |
140 | + self.assertEqual("powernv", node.get_bios_boot_method()) |
141 | + |
142 | + def test_get_bios_boot_method_returns_powerkvm(self): |
143 | + node = factory.make_Node(bios_boot_method="powerkvm") |
144 | + self.assertEqual("powerkvm", node.get_bios_boot_method()) |
145 | + |
146 | def test_get_bios_boot_method_fallback_to_pxe(self): |
147 | node = factory.make_Node(bios_boot_method=factory.make_name("boot")) |
148 | self.assertEqual("pxe", node.get_bios_boot_method()) |
149 | |
150 | === modified file 'src/maasserver/models/tests/test_partition.py' |
151 | --- src/maasserver/models/tests/test_partition.py 2015-12-01 18:12:59 +0000 |
152 | +++ src/maasserver/models/tests/test_partition.py 2016-02-24 16:12:28 +0000 |
153 | @@ -22,7 +22,10 @@ |
154 | Partition, |
155 | PARTITION_ALIGNMENT_SIZE, |
156 | ) |
157 | -from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE |
158 | +from maasserver.models.partitiontable import ( |
159 | + PARTITION_TABLE_EXTRA_SPACE, |
160 | + PREP_PARTITION_SIZE, |
161 | +) |
162 | from maasserver.testing.factory import factory |
163 | from maasserver.testing.orm import reload_object |
164 | from maasserver.testing.testcase import MAASServerTestCase |
165 | @@ -302,6 +305,27 @@ |
166 | self.expectThat(idx, Equals(partition.get_partition_number())) |
167 | idx += 1 |
168 | |
169 | + def test_get_partition_number_returns_starting_at_2_for_ppc64el(self): |
170 | + node = factory.make_Node( |
171 | + architecture="ppc64el/generic", bios_boot_method="uefi") |
172 | + block_device = factory.make_PhysicalBlockDevice( |
173 | + node=node, |
174 | + size=( |
175 | + (MIN_PARTITION_SIZE * 4) + PARTITION_TABLE_EXTRA_SPACE + |
176 | + PREP_PARTITION_SIZE)) |
177 | + node.boot_disk = block_device |
178 | + node.save() |
179 | + partition_table = factory.make_PartitionTable( |
180 | + block_device=block_device, table_type=PARTITION_TABLE_TYPE.GPT) |
181 | + partitions = [ |
182 | + partition_table.add_partition(size=MIN_BLOCK_DEVICE_SIZE) |
183 | + for _ in range(4) |
184 | + ] |
185 | + idx = 2 |
186 | + for partition in partitions: |
187 | + self.expectThat(idx, Equals(partition.get_partition_number())) |
188 | + idx += 1 |
189 | + |
190 | def test_get_partition_number_returns_correct_numbering_for_mbr(self): |
191 | block_device = factory.make_PhysicalBlockDevice( |
192 | size=(MIN_BLOCK_DEVICE_SIZE * 6) + PARTITION_TABLE_EXTRA_SPACE) |
193 | |
194 | === modified file 'src/maasserver/models/tests/test_partitiontable.py' |
195 | --- src/maasserver/models/tests/test_partitiontable.py 2015-12-01 18:12:59 +0000 |
196 | +++ src/maasserver/models/tests/test_partitiontable.py 2016-02-24 16:12:28 +0000 |
197 | @@ -19,7 +19,10 @@ |
198 | MIN_PARTITION_SIZE, |
199 | PARTITION_ALIGNMENT_SIZE, |
200 | ) |
201 | -from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE |
202 | +from maasserver.models.partitiontable import ( |
203 | + PARTITION_TABLE_EXTRA_SPACE, |
204 | + PREP_PARTITION_SIZE, |
205 | +) |
206 | from maasserver.testing.factory import factory |
207 | from maasserver.testing.testcase import MAASServerTestCase |
208 | from maasserver.utils.converters import round_size_to_nearest_block |
209 | @@ -43,6 +46,19 @@ |
210 | False), |
211 | partition_table.get_size()) |
212 | |
213 | + def test_get_size_returns_block_device_size_minus_ppc64el(self): |
214 | + node = factory.make_Node(architecture="ppc64el/generic") |
215 | + block_device = factory.make_PhysicalBlockDevice(node=node) |
216 | + partition_table = factory.make_PartitionTable( |
217 | + block_device=block_device) |
218 | + self.assertEqual( |
219 | + round_size_to_nearest_block( |
220 | + partition_table.block_device.size - |
221 | + PARTITION_TABLE_EXTRA_SPACE - PREP_PARTITION_SIZE, |
222 | + PARTITION_ALIGNMENT_SIZE, |
223 | + False), |
224 | + partition_table.get_size()) |
225 | + |
226 | def test_get_block_size_returns_block_device_block_size(self): |
227 | partition_table = factory.make_PartitionTable() |
228 | self.assertEqual( |
229 | @@ -114,6 +130,24 @@ |
230 | self.assertRaises( |
231 | ValidationError, partition_table.add_partition) |
232 | |
233 | + def test_get_overhead_size(self): |
234 | + node = factory.make_Node(bios_boot_method="pxe") |
235 | + block_device = factory.make_PhysicalBlockDevice(node=node) |
236 | + partition_table = factory.make_PartitionTable( |
237 | + block_device=block_device) |
238 | + self.assertEquals( |
239 | + PARTITION_TABLE_EXTRA_SPACE, |
240 | + partition_table.get_overhead_size()) |
241 | + |
242 | + def test_get_overhead_size_for_ppc64el(self): |
243 | + node = factory.make_Node(architecture="ppc64el/generic") |
244 | + block_device = factory.make_PhysicalBlockDevice(node=node) |
245 | + partition_table = factory.make_PartitionTable( |
246 | + block_device=block_device) |
247 | + self.assertEquals( |
248 | + PARTITION_TABLE_EXTRA_SPACE + PREP_PARTITION_SIZE, |
249 | + partition_table.get_overhead_size()) |
250 | + |
251 | def test_get_available_size(self): |
252 | block_size = 4096 |
253 | device = factory.make_BlockDevice( |
254 | @@ -153,6 +187,20 @@ |
255 | partition_table = factory.make_PartitionTable(block_device=boot_disk) |
256 | self.assertEqual(PARTITION_TABLE_TYPE.GPT, partition_table.table_type) |
257 | |
258 | + def test_save_sets_table_type_to_gpt_for_powernv_boot(self): |
259 | + node = factory.make_Node( |
260 | + with_boot_disk=False, bios_boot_method="powernv") |
261 | + boot_disk = factory.make_PhysicalBlockDevice(node=node) |
262 | + partition_table = factory.make_PartitionTable(block_device=boot_disk) |
263 | + self.assertEqual(PARTITION_TABLE_TYPE.GPT, partition_table.table_type) |
264 | + |
265 | + def test_save_sets_table_type_to_gpt_for_powerkvm_boot(self): |
266 | + node = factory.make_Node( |
267 | + with_boot_disk=False, bios_boot_method="powerkvm") |
268 | + boot_disk = factory.make_PhysicalBlockDevice(node=node) |
269 | + partition_table = factory.make_PartitionTable(block_device=boot_disk) |
270 | + self.assertEqual(PARTITION_TABLE_TYPE.GPT, partition_table.table_type) |
271 | + |
272 | def test_save_sets_table_type_to_gpt_for_none_boot_disk(self): |
273 | node = factory.make_Node(with_boot_disk=False, bios_boot_method="pxe") |
274 | factory.make_PhysicalBlockDevice(node=node) |
275 | |
276 | === modified file 'src/maasserver/preseed_storage.py' |
277 | --- src/maasserver/preseed_storage.py 2016-02-23 12:13:09 +0000 |
278 | +++ src/maasserver/preseed_storage.py 2016-02-24 16:12:28 +0000 |
279 | @@ -14,7 +14,10 @@ |
280 | FILESYSTEM_TYPE, |
281 | PARTITION_TABLE_TYPE, |
282 | ) |
283 | -from maasserver.models.partitiontable import INITIAL_PARTITION_OFFSET |
284 | +from maasserver.models.partitiontable import ( |
285 | + INITIAL_PARTITION_OFFSET, |
286 | + PREP_PARTITION_SIZE, |
287 | +) |
288 | from maasserver.models.physicalblockdevice import PhysicalBlockDevice |
289 | from maasserver.models.virtualblockdevice import VirtualBlockDevice |
290 | import yaml |
291 | @@ -26,6 +29,7 @@ |
292 | def __init__(self, node): |
293 | self.node = node |
294 | self.boot_disk = node.get_boot_disk() |
295 | + self.boot_disk_first_partition = None |
296 | self.operations = { |
297 | "disk": [], |
298 | "partition": [], |
299 | @@ -103,6 +107,13 @@ |
300 | raise ValueError("Unknown block device instance: %s" % ( |
301 | block_device.__class__.__name__)) |
302 | |
303 | + def _requires_prep_partition(self, block_device): |
304 | + """Return True if block device requires the prep partition.""" |
305 | + arch, _ = self.node.split_arch() |
306 | + return ( |
307 | + self.boot_disk.id == block_device.id and |
308 | + arch == "ppc64el") |
309 | + |
310 | def _add_partition_operations(self): |
311 | """Add all the partition operations. |
312 | |
313 | @@ -110,9 +121,16 @@ |
314 | attached to the node. |
315 | """ |
316 | for block_device in self.node.blockdevice_set.order_by('id'): |
317 | + requires_prep = self._requires_prep_partition(block_device) |
318 | partition_table = block_device.get_partitiontable() |
319 | if partition_table is not None: |
320 | - for partition in partition_table.partitions.order_by('id'): |
321 | + partitions = list(partition_table.partitions.order_by('id')) |
322 | + for idx, partition in enumerate(partitions): |
323 | + # If this is the last partition and prep partition is |
324 | + # required then set boot_disk_first_partition so extra |
325 | + # space can be removed. |
326 | + if requires_prep and idx == 0: |
327 | + self.boot_disk_first_partition = partition |
328 | self.operations["partition"].append(partition) |
329 | |
330 | def _add_format_and_mount_operations(self): |
331 | @@ -172,21 +190,35 @@ |
332 | |
333 | # Set the partition table type if a partition table exists or if this |
334 | # is the boot disk. |
335 | + add_prep_partition = False |
336 | partition_table = block_device.get_partitiontable() |
337 | if partition_table is not None: |
338 | disk_operation["ptable"] = self._get_ptable_type( |
339 | partition_table) |
340 | elif block_device.id == self.boot_disk.id: |
341 | - if self.node.get_bios_boot_method() == "uefi": |
342 | + bios_boot_method = self.node.get_bios_boot_method() |
343 | + node_arch, _ = self.node.split_arch() |
344 | + if bios_boot_method in [ |
345 | + "uefi", "powernv", "powerkvm"]: |
346 | disk_operation["ptable"] = "gpt" |
347 | + if node_arch == "ppc64el": |
348 | + add_prep_partition = True |
349 | else: |
350 | disk_operation["ptable"] = "msdos" |
351 | |
352 | - # Set this disk to be the grub device if its the boot disk. |
353 | - if self.boot_disk == block_device: |
354 | + # Set this disk to be the grub device if it's the boot disk and doesn't |
355 | + # require a prep partition. When a prep partition is required grub |
356 | + # must be installed on that partition and not in the partition header |
357 | + # of that disk. |
358 | + requires_prep = self._requires_prep_partition(block_device) |
359 | + if self.boot_disk.id == block_device.id and not requires_prep: |
360 | disk_operation["grub_device"] = True |
361 | self.storage_config.append(disk_operation) |
362 | |
363 | + # Add the prep partition at the end of the disk when it is required. |
364 | + if add_prep_partition: |
365 | + self._generate_prep_partition(block_device.get_name()) |
366 | + |
367 | def _get_ptable_type(self, partition_table): |
368 | """Return the value for the "ptable" entry in the physical operation. |
369 | """ |
370 | @@ -199,12 +231,38 @@ |
371 | "Unknown partition table type: %s" % ( |
372 | partition_table.table_type)) |
373 | |
374 | + def _generate_prep_partition(self, device_name): |
375 | + """Generate the prep partition at the beginning of the block device.""" |
376 | + prep_part_name = "%s-part1" % (device_name) |
377 | + partition_operation = { |
378 | + "id": prep_part_name, |
379 | + "name": prep_part_name, |
380 | + "type": "partition", |
381 | + "number": 1, |
382 | + "offset": "%dB" % INITIAL_PARTITION_OFFSET, |
383 | + "size": "%dB" % PREP_PARTITION_SIZE, |
384 | + "device": device_name, |
385 | + "wipe": "zero", |
386 | + "flag": "prep", |
387 | + "grub_device": True, |
388 | + } |
389 | + self.storage_config.append(partition_operation) |
390 | + |
391 | def _generate_partition_operations(self): |
392 | """Generate all partition operations.""" |
393 | for partition in self.operations["partition"]: |
394 | - self._generate_partition_operation(partition) |
395 | + if partition == self.boot_disk_first_partition: |
396 | + # This is the first partition in the boot disk and add prep |
397 | + # partition at the beginning of the partition table. |
398 | + device_name = partition.partition_table.block_device.get_name() |
399 | + self._generate_prep_partition(device_name) |
400 | + self._generate_partition_operation( |
401 | + partition, include_initial=False) |
402 | + else: |
403 | + self._generate_partition_operation( |
404 | + partition, include_initial=True) |
405 | |
406 | - def _generate_partition_operation(self, partition): |
407 | + def _generate_partition_operation(self, partition, include_initial): |
408 | """Generate partition operation for `partition` and place in |
409 | `storage_config`.""" |
410 | partition_table = partition.partition_table |
411 | @@ -221,7 +279,7 @@ |
412 | "wipe": "superblock", |
413 | } |
414 | # First partition always sets the initial offset. |
415 | - if partition_number == 1: |
416 | + if partition_number == 1 and include_initial: |
417 | partition_operation["offset"] = "%sB" % INITIAL_PARTITION_OFFSET |
418 | if partition.bootable: |
419 | partition_operation["flag"] = "boot" |
420 | |
421 | === modified file 'src/maasserver/static/js/angular/controllers/node_details_storage.js' |
422 | --- src/maasserver/static/js/angular/controllers/node_details_storage.js 2016-02-02 14:20:45 +0000 |
423 | +++ src/maasserver/static/js/angular/controllers/node_details_storage.js 2016-02-24 16:12:28 +0000 |
424 | @@ -51,6 +51,7 @@ |
425 | var END_OF_PARTITION_TABLE_SPACE = 1024 * 1024; |
426 | var PARTITION_TABLE_EXTRA_SPACE = INITIAL_PARTITION_OFFSET + |
427 | END_OF_PARTITION_TABLE_SPACE; |
428 | + var PREP_PARTITION_SIZE = 8 * 1024 * 1024; |
429 | |
430 | // From models/partition.py - must be kept in sync. |
431 | var PARTITION_ALIGNMENT_SIZE = 4 * 1024 * 1024; |
432 | @@ -855,6 +856,11 @@ |
433 | || disk.original.partition_table_type === "") { |
434 | // Disk has no partition table, so reserve space for it. |
435 | space_to_reserve = PARTITION_TABLE_EXTRA_SPACE; |
436 | + // ppc64el node requires that space be saved for the prep |
437 | + // partition. |
438 | + if($scope.node.architecture.indexOf("ppc64el") === 0) { |
439 | + space_to_reserve += PREP_PARTITION_SIZE; |
440 | + } |
441 | } |
442 | return ConverterService.roundByBlockSize( |
443 | disk.original.available_size - space_to_reserve, |
444 | @@ -1130,6 +1136,11 @@ |
445 | if(disk.original.partition_table_type === "mbr" && |
446 | length > 2) { |
447 | return disk.name + "-part" + (length + 2); |
448 | + } else if($scope.node.architecture.indexOf("ppc64el") === 0 && |
449 | + disk.original.is_boot) { |
450 | + // Boot disk on ppc64el machines skip the first partition as |
451 | + // its reserved for the prep partition. |
452 | + return disk.name + "-part" + (length + 2); |
453 | } else { |
454 | return disk.name + "-part" + (length + 1); |
455 | } |
456 | |
457 | === modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js' |
458 | --- src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js 2016-02-02 14:20:45 +0000 |
459 | +++ src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js 2016-02-24 16:12:28 +0000 |
460 | @@ -101,6 +101,7 @@ |
461 | var node, updateNodeSpy, canEditSpy; |
462 | beforeEach(function() { |
463 | node = { |
464 | + architecture: "amd64/generic", |
465 | disks: [] |
466 | }; |
467 | updateNodeSpy = jasmine.createSpy("updateNode"); |
468 | @@ -1455,6 +1456,25 @@ |
469 | expect($scope.canAddPartition(disk)).toBe(false); |
470 | }); |
471 | |
472 | + it("returns false if available_size is less than partition size " + |
473 | + "when node is ppc64el architecture", |
474 | + function() { |
475 | + var controller = makeController(); |
476 | + var disk = { |
477 | + type: "physical", |
478 | + fstype: "", |
479 | + original: { |
480 | + partition_table_type: null, |
481 | + available_size: (2.5 * 1024 * 1024) + (8 * 1024 * 1024), |
482 | + block_size: 1024 |
483 | + } |
484 | + }; |
485 | + node.architecture = "ppc64el/generic"; |
486 | + spyOn($scope, "isAllStorageDisabled").and.returnValue(false); |
487 | + $scope.isSuperUser = function() { return true; }; |
488 | + expect($scope.canAddPartition(disk)).toBe(false); |
489 | + }); |
490 | + |
491 | it("returns false if not super user", function() { |
492 | var controller = makeController(); |
493 | var disk = { |
494 | @@ -2350,6 +2370,37 @@ |
495 | expect($scope.getAddPartitionName(disk)).toBe(name + "-part3"); |
496 | }); |
497 | |
498 | + it("returns disk.name with -part2 for ppc64el", function() { |
499 | + node.architecture = "ppc64el/generic"; |
500 | + var controller = makeController(); |
501 | + var name = makeName("sda"); |
502 | + var disk = { |
503 | + name: name, |
504 | + original: { |
505 | + is_boot: true, |
506 | + partition_table_type: "gpt" |
507 | + } |
508 | + }; |
509 | + |
510 | + expect($scope.getAddPartitionName(disk)).toBe(name + "-part2"); |
511 | + }); |
512 | + |
513 | + it("returns disk.name with -part4 for ppc64el", function() { |
514 | + node.architecture = "ppc64el/generic"; |
515 | + var controller = makeController(); |
516 | + var name = makeName("sda"); |
517 | + var disk = { |
518 | + name: name, |
519 | + original: { |
520 | + is_boot: true, |
521 | + partition_table_type: "gpt", |
522 | + partitions: [{}, {}] |
523 | + } |
524 | + }; |
525 | + |
526 | + expect($scope.getAddPartitionName(disk)).toBe(name + "-part4"); |
527 | + }); |
528 | + |
529 | it("returns disk.name with -part3 for MBR", function() { |
530 | var controller = makeController(); |
531 | var name = makeName("sda"); |
532 | |
533 | === modified file 'src/maasserver/storage_layouts.py' |
534 | --- src/maasserver/storage_layouts.py 2015-12-01 18:12:59 +0000 |
535 | +++ src/maasserver/storage_layouts.py 2016-02-24 16:12:28 +0000 |
536 | @@ -169,7 +169,12 @@ |
537 | from maasserver.models.partitiontable import PartitionTable |
538 | boot_partition_table = PartitionTable.objects.create( |
539 | block_device=self.boot_disk) |
540 | - if boot_partition_table.table_type == PARTITION_TABLE_TYPE.GPT: |
541 | + bios_boot_method = self.node.get_bios_boot_method() |
542 | + node_arch, _ = self.node.split_arch() |
543 | + if (boot_partition_table.table_type == PARTITION_TABLE_TYPE.GPT and |
544 | + bios_boot_method == "uefi" and node_arch != "ppc64el"): |
545 | + # Add EFI partition only if booting UEFI and not a ppc64el |
546 | + # architecture. |
547 | efi_partition = boot_partition_table.add_partition( |
548 | size=EFI_PARTITION_SIZE, bootable=True) |
549 | Filesystem.objects.create( |
550 | |
551 | === modified file 'src/maasserver/tests/test_preseed_storage.py' |
552 | --- src/maasserver/tests/test_preseed_storage.py 2016-02-23 12:13:09 +0000 |
553 | +++ src/maasserver/tests/test_preseed_storage.py 2016-02-24 16:12:28 +0000 |
554 | @@ -20,7 +20,10 @@ |
555 | RAID, |
556 | VolumeGroup, |
557 | ) |
558 | -from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE |
559 | +from maasserver.models.partitiontable import ( |
560 | + PARTITION_TABLE_EXTRA_SPACE, |
561 | + PREP_PARTITION_SIZE, |
562 | +) |
563 | from maasserver.preseed_storage import compose_curtin_storage_config |
564 | from maasserver.testing.factory import factory |
565 | from maasserver.testing.testcase import MAASServerTestCase |
566 | @@ -770,6 +773,217 @@ |
567 | self.assertStorageConfig(self.STORAGE_CONFIG, config) |
568 | |
569 | |
570 | +class TestSimplePower8Layout(MAASServerTestCase, AssertStorageConfigMixin): |
571 | + |
572 | + STORAGE_CONFIG = dedent("""\ |
573 | + config: |
574 | + - id: sda |
575 | + name: sda |
576 | + type: disk |
577 | + wipe: superblock |
578 | + ptable: gpt |
579 | + model: QEMU HARDDISK |
580 | + serial: QM00001 |
581 | + - id: sda-part1 |
582 | + name: sda-part1 |
583 | + type: partition |
584 | + number: 1 |
585 | + offset: 4194304B |
586 | + size: 8388608B |
587 | + device: sda |
588 | + wipe: zero |
589 | + flag: prep |
590 | + grub_device: True |
591 | + - id: sda-part2 |
592 | + name: sda-part2 |
593 | + type: partition |
594 | + number: 2 |
595 | + uuid: f74ff260-2a5b-4a36-b1b8-37f746b946bf |
596 | + size: 8573157376B |
597 | + wipe: superblock |
598 | + device: sda |
599 | + - id: sda-part2_format |
600 | + type: format |
601 | + fstype: ext4 |
602 | + label: root |
603 | + uuid: 90a69b22-e281-4c5b-8df9-b09514f27ba1 |
604 | + volume: sda-part2 |
605 | + - id: sda-part2_mount |
606 | + type: mount |
607 | + path: / |
608 | + device: sda-part2_format |
609 | + """) |
610 | + |
611 | + def test__renders_expected_output(self): |
612 | + node = factory.make_Node( |
613 | + status=NODE_STATUS.ALLOCATED, architecture="ppc64el/generic", |
614 | + bios_boot_method="uefi", with_boot_disk=False) |
615 | + boot_disk = factory.make_PhysicalBlockDevice( |
616 | + node=node, size=8 * 1024 ** 3, name="sda", |
617 | + model="QEMU HARDDISK", serial="QM00001") # 8 GiB |
618 | + partition_table = factory.make_PartitionTable( |
619 | + table_type=PARTITION_TABLE_TYPE.GPT, block_device=boot_disk) |
620 | + root_partition = factory.make_Partition( |
621 | + partition_table=partition_table, |
622 | + uuid="f74ff260-2a5b-4a36-b1b8-37f746b946bf", |
623 | + size=( |
624 | + (8 * 1024 ** 3) - PARTITION_TABLE_EXTRA_SPACE - |
625 | + PREP_PARTITION_SIZE), |
626 | + bootable=False) |
627 | + factory.make_Filesystem( |
628 | + partition=root_partition, fstype=FILESYSTEM_TYPE.EXT4, |
629 | + uuid="90a69b22-e281-4c5b-8df9-b09514f27ba1", label="root", |
630 | + mount_point="/", mount_options=None) |
631 | + node._create_acquired_filesystems() |
632 | + config = compose_curtin_storage_config(node) |
633 | + self.assertStorageConfig(self.STORAGE_CONFIG, config) |
634 | + |
635 | + |
636 | +class TestPower8ExtraSpaceLayout( |
637 | + MAASServerTestCase, AssertStorageConfigMixin): |
638 | + |
639 | + STORAGE_CONFIG = dedent("""\ |
640 | + config: |
641 | + - id: sda |
642 | + name: sda |
643 | + type: disk |
644 | + wipe: superblock |
645 | + ptable: gpt |
646 | + model: QEMU HARDDISK |
647 | + serial: QM00001 |
648 | + - id: sda-part1 |
649 | + name: sda-part1 |
650 | + type: partition |
651 | + number: 1 |
652 | + offset: 4194304B |
653 | + size: 8388608B |
654 | + device: sda |
655 | + wipe: zero |
656 | + flag: prep |
657 | + grub_device: True |
658 | + - id: sda-part2 |
659 | + name: sda-part2 |
660 | + type: partition |
661 | + number: 2 |
662 | + uuid: f74ff260-2a5b-4a36-b1b8-37f746b946bf |
663 | + size: 7507804160B |
664 | + wipe: superblock |
665 | + device: sda |
666 | + - id: sda-part2_format |
667 | + type: format |
668 | + fstype: ext4 |
669 | + label: root |
670 | + uuid: 90a69b22-e281-4c5b-8df9-b09514f27ba1 |
671 | + volume: sda-part2 |
672 | + - id: sda-part2_mount |
673 | + type: mount |
674 | + path: / |
675 | + device: sda-part2_format |
676 | + """) |
677 | + |
678 | + def test__renders_expected_output(self): |
679 | + node = factory.make_Node( |
680 | + status=NODE_STATUS.ALLOCATED, architecture="ppc64el/generic", |
681 | + bios_boot_method="uefi", with_boot_disk=False) |
682 | + boot_disk = factory.make_PhysicalBlockDevice( |
683 | + node=node, size=8 * 1024 ** 3, name="sda", |
684 | + model="QEMU HARDDISK", serial="QM00001") # 8 GiB |
685 | + partition_table = factory.make_PartitionTable( |
686 | + table_type=PARTITION_TABLE_TYPE.GPT, block_device=boot_disk) |
687 | + root_partition = factory.make_Partition( |
688 | + partition_table=partition_table, |
689 | + uuid="f74ff260-2a5b-4a36-b1b8-37f746b946bf", |
690 | + size=(7 * 1024 ** 3) - PARTITION_TABLE_EXTRA_SPACE, |
691 | + bootable=False) |
692 | + factory.make_Filesystem( |
693 | + partition=root_partition, fstype=FILESYSTEM_TYPE.EXT4, |
694 | + uuid="90a69b22-e281-4c5b-8df9-b09514f27ba1", label="root", |
695 | + mount_point="/", mount_options=None) |
696 | + node._create_acquired_filesystems() |
697 | + config = compose_curtin_storage_config(node) |
698 | + self.assertStorageConfig(self.STORAGE_CONFIG, config) |
699 | + |
700 | + |
701 | +class TestPower8NoPartitionTableLayout( |
702 | + MAASServerTestCase, AssertStorageConfigMixin): |
703 | + |
704 | + STORAGE_CONFIG = dedent("""\ |
705 | + config: |
706 | + - id: sda |
707 | + name: sda |
708 | + type: disk |
709 | + wipe: superblock |
710 | + ptable: gpt |
711 | + model: QEMU HARDDISK |
712 | + serial: QM00001 |
713 | + - id: sdb |
714 | + name: sdb |
715 | + type: disk |
716 | + wipe: superblock |
717 | + ptable: gpt |
718 | + model: QEMU HARDDISK |
719 | + serial: QM00002 |
720 | + - id: sdb-part1 |
721 | + name: sdb-part1 |
722 | + type: partition |
723 | + number: 1 |
724 | + offset: 4194304B |
725 | + size: 8388608B |
726 | + device: sdb |
727 | + wipe: zero |
728 | + flag: prep |
729 | + grub_device: True |
730 | + - id: sda-part1 |
731 | + name: sda-part1 |
732 | + type: partition |
733 | + number: 1 |
734 | + uuid: f74ff260-2a5b-4a36-b1b8-37f746b946bf |
735 | + offset: 4194304B |
736 | + size: 8573157376B |
737 | + wipe: superblock |
738 | + device: sda |
739 | + - id: sda-part1_format |
740 | + type: format |
741 | + fstype: ext4 |
742 | + label: root |
743 | + uuid: 90a69b22-e281-4c5b-8df9-b09514f27ba1 |
744 | + volume: sda-part1 |
745 | + - id: sda-part1_mount |
746 | + type: mount |
747 | + path: / |
748 | + device: sda-part1_format |
749 | + """) |
750 | + |
751 | + def test__renders_expected_output(self): |
752 | + node = factory.make_Node( |
753 | + status=NODE_STATUS.ALLOCATED, architecture="ppc64el/generic", |
754 | + bios_boot_method="uefi", with_boot_disk=False) |
755 | + root_disk = factory.make_PhysicalBlockDevice( |
756 | + node=node, size=8 * 1024 ** 3, name="sda", |
757 | + model="QEMU HARDDISK", serial="QM00001") # 8 GiB |
758 | + partition_table = factory.make_PartitionTable( |
759 | + table_type=PARTITION_TABLE_TYPE.GPT, block_device=root_disk) |
760 | + boot_disk = factory.make_PhysicalBlockDevice( |
761 | + node=node, size=8 * 1024 ** 3, name="sdb", |
762 | + model="QEMU HARDDISK", serial="QM00002") # 8 GiB |
763 | + node.boot_disk = boot_disk |
764 | + node.save() |
765 | + root_partition = factory.make_Partition( |
766 | + partition_table=partition_table, |
767 | + uuid="f74ff260-2a5b-4a36-b1b8-37f746b946bf", |
768 | + size=( |
769 | + (8 * 1024 ** 3) - PARTITION_TABLE_EXTRA_SPACE - |
770 | + PREP_PARTITION_SIZE), |
771 | + bootable=False) |
772 | + factory.make_Filesystem( |
773 | + partition=root_partition, fstype=FILESYSTEM_TYPE.EXT4, |
774 | + uuid="90a69b22-e281-4c5b-8df9-b09514f27ba1", label="root", |
775 | + mount_point="/", mount_options=None) |
776 | + node._create_acquired_filesystems() |
777 | + config = compose_curtin_storage_config(node) |
778 | + self.assertStorageConfig(self.STORAGE_CONFIG, config) |
779 | + |
780 | + |
781 | def shuffled(things): |
782 | things = list(things) |
783 | random.shuffle(things) |
784 | |
785 | === modified file 'src/maasserver/tests/test_storage_layouts.py' |
786 | --- src/maasserver/tests/test_storage_layouts.py 2016-01-21 17:03:28 +0000 |
787 | +++ src/maasserver/tests/test_storage_layouts.py 2016-02-24 16:12:28 +0000 |
788 | @@ -20,7 +20,10 @@ |
789 | MAX_PARTITION_SIZE_FOR_MBR, |
790 | PARTITION_ALIGNMENT_SIZE, |
791 | ) |
792 | -from maasserver.models.partitiontable import PARTITION_TABLE_EXTRA_SPACE |
793 | +from maasserver.models.partitiontable import ( |
794 | + PARTITION_TABLE_EXTRA_SPACE, |
795 | + PREP_PARTITION_SIZE, |
796 | +) |
797 | from maasserver.storage_layouts import ( |
798 | BcacheStorageLayout, |
799 | BcacheStorageLayoutBase, |
800 | @@ -54,6 +57,20 @@ |
801 | return factory.make_Node(*args, **kwargs) |
802 | |
803 | |
804 | +def make_ppc64el_Node_with_powernv_boot_method(*args, **kwargs): |
805 | + kwargs['bios_boot_method'] = "powernv" |
806 | + kwargs['with_boot_disk'] = False |
807 | + kwargs['architecture'] = "ppc64el/generic" |
808 | + return factory.make_Node(*args, **kwargs) |
809 | + |
810 | + |
811 | +def make_ppc64el_Node_with_uefi_boot_method(*args, **kwargs): |
812 | + kwargs['bios_boot_method'] = "powerkvm" |
813 | + kwargs['with_boot_disk'] = False |
814 | + kwargs['architecture'] = "ppc64el/generic" |
815 | + return factory.make_Node(*args, **kwargs) |
816 | + |
817 | + |
818 | class TestFormHelpers(MAASServerTestCase): |
819 | |
820 | def test_get_storage_layout_choices(self): |
821 | @@ -505,6 +522,66 @@ |
822 | mount_point="/", |
823 | )) |
824 | |
825 | + def test__creates_layout_for_powernv(self): |
826 | + node = make_ppc64el_Node_with_powernv_boot_method() |
827 | + boot_disk = factory.make_PhysicalBlockDevice( |
828 | + node=node, size=LARGE_BLOCK_DEVICE) |
829 | + layout = FlatStorageLayout(node) |
830 | + layout.configure() |
831 | + |
832 | + # Validate partition table. |
833 | + partition_table = boot_disk.get_partitiontable() |
834 | + self.assertEqual(PARTITION_TABLE_TYPE.GPT, partition_table.table_type) |
835 | + |
836 | + # Validate root partition. |
837 | + partitions = partition_table.partitions.order_by('id').all() |
838 | + root_partition = partitions[0] |
839 | + self.assertIsNotNone(root_partition) |
840 | + self.assertEqual( |
841 | + round_size_to_nearest_block( |
842 | + boot_disk.size - PARTITION_TABLE_EXTRA_SPACE - |
843 | + PREP_PARTITION_SIZE, |
844 | + PARTITION_ALIGNMENT_SIZE, |
845 | + False), |
846 | + root_partition.size) |
847 | + self.assertThat( |
848 | + root_partition.get_effective_filesystem(), |
849 | + MatchesStructure.byEquality( |
850 | + fstype=FILESYSTEM_TYPE.EXT4, |
851 | + label="root", |
852 | + mount_point="/", |
853 | + )) |
854 | + |
855 | + def test__creates_layout_for_powerkvm(self): |
856 | + node = make_ppc64el_Node_with_uefi_boot_method() |
857 | + boot_disk = factory.make_PhysicalBlockDevice( |
858 | + node=node, size=LARGE_BLOCK_DEVICE) |
859 | + layout = FlatStorageLayout(node) |
860 | + layout.configure() |
861 | + |
862 | + # Validate partition table. |
863 | + partition_table = boot_disk.get_partitiontable() |
864 | + self.assertEqual(PARTITION_TABLE_TYPE.GPT, partition_table.table_type) |
865 | + |
866 | + # Validate root partition. |
867 | + partitions = partition_table.partitions.order_by('id').all() |
868 | + root_partition = partitions[0] |
869 | + self.assertIsNotNone(root_partition) |
870 | + self.assertEqual( |
871 | + round_size_to_nearest_block( |
872 | + boot_disk.size - PARTITION_TABLE_EXTRA_SPACE - |
873 | + PREP_PARTITION_SIZE, |
874 | + PARTITION_ALIGNMENT_SIZE, |
875 | + False), |
876 | + root_partition.size) |
877 | + self.assertThat( |
878 | + root_partition.get_effective_filesystem(), |
879 | + MatchesStructure.byEquality( |
880 | + fstype=FILESYSTEM_TYPE.EXT4, |
881 | + label="root", |
882 | + mount_point="/", |
883 | + )) |
884 | + |
885 | def test__creates_layout_with_uefi_defaults(self): |
886 | node = make_Node_with_uefi_boot_method() |
887 | boot_disk = factory.make_PhysicalBlockDevice( |
888 | |
889 | === modified file 'src/provisioningserver/boot/powerkvm.py' |
890 | --- src/provisioningserver/boot/powerkvm.py 2015-12-01 18:12:59 +0000 |
891 | +++ src/provisioningserver/boot/powerkvm.py 2016-02-24 16:12:28 +0000 |
892 | @@ -33,7 +33,7 @@ |
893 | class PowerKVMBootMethod(BootMethod): |
894 | |
895 | name = "powerkvm" |
896 | - bios_boot_method = "pxe" |
897 | + bios_boot_method = "powerkvm" |
898 | template_subdir = None |
899 | bootloader_path = "bootppc64.bin" |
900 | bootloader_arches = ['ppc64el'] |
901 | |
902 | === modified file 'src/provisioningserver/boot/powernv.py' |
903 | --- src/provisioningserver/boot/powernv.py 2015-12-01 18:12:59 +0000 |
904 | +++ src/provisioningserver/boot/powernv.py 2016-02-24 16:12:28 +0000 |
905 | @@ -61,7 +61,7 @@ |
906 | class PowerNVBootMethod(BootMethod): |
907 | |
908 | name = "powernv" |
909 | - bios_boot_method = "pxe" |
910 | + bios_boot_method = "powernv" |
911 | template_subdir = "pxe" |
912 | bootloader_path = "pxelinux.0" |
913 | arch_octet = "00:0E" |
The abundant special-casing puts this on the slope towards spaghetti, but additional tests and documentation (chiefly in the form of comments) will alleviate that and make it easier to unravel later on.
I have several questions but no reservations other than the above, so Needs Information.