Merge lp:~blake-rouse/maas/fix-power8 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: 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
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.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

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.

review: Needs Information
Revision history for this message
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.

Revision history for this message
Gavin Panella (allenap) :
review: Approve
Revision history for this message
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.

https://bugs.launchpad.net/curtin/+bug/1543263

Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (998.2 KiB)

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://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease [95.8 kB]
Hit:2 http://security.ubuntu.com/ubuntu xenial-security InRelease
Hit:3 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease
Hit:4 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Get:5 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/main Sources [1,108 kB]
Get:6 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe Sources [7,717 kB]
Get:7 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/main amd64 Packages [1,442 kB]
Get:8 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/main Translation-en [844 kB]
Get:9 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe amd64 Packages [7,352 kB]
Get:10 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial/universe Translation-en [4,889 kB]
Fetched 23.4 MB in 34s (688 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bind9 bind9utils build-essential bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm postgresql pxelinux python3-apt python3-bson python3-convoy python3-coverage python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-mock python3-netaddr python3-netifaces python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-seamicroclient python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
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.3.dfsg.P2-4).
bind9utils is already the newest version (1:9.10.3.dfsg.P2-4).
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.20160115ubuntu2).
dh-apport is already the newest version (2.20-0ubuntu3).
dh-systemd is already the newest version (1.28ubuntu2)....

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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"