Add support for BIOS local boot for GPT label

Ironic doesn't support local booting of images in BIOS boot mode
when the disk_label is GPT. The current changes creates BIOS Boot
partition when the disk_label is chosen as gpt. The partition size
is a configurable parameter.

Change-Id: Iade60da0316b8f1c0bf8ce4c79931c1105d19cec
Closes-bug: #1563291
This commit is contained in:
vmud213 2016-04-29 08:43:55 +00:00
parent 9eaad70080
commit c6d4f0f463
5 changed files with 97 additions and 29 deletions

View File

@ -80,7 +80,7 @@ class DiskPartitioner(object):
use_standard_locale=True, run_as_root=True)
def add_partition(self, size, part_type='primary', fs_type='',
bootable=False):
boot_flag=None):
"""Add a partition.
:param size: The size of the partition in MiB.
@ -90,15 +90,16 @@ class DiskPartitioner(object):
fat16, HFS, linux-swap, NTFS, reiserfs, ufs.
If blank (''), it will create a Linux native
partition (83).
:param bootable: Boolean value; whether the partition is bootable
or not.
:param boot_flag: Boot flag that needs to be configured on the
partition. Ignored if None. It can take values
'bios_grub', 'boot'.
:returns: The partition number.
"""
self._partitions.append({'size': size,
'type': part_type,
'fs_type': fs_type,
'bootable': bootable})
'boot_flag': boot_flag})
return len(self._partitions)
def get_partitions(self):
@ -145,8 +146,8 @@ class DiskPartitioner(object):
end = start + part['size']
cmd_args.extend(['mkpart', part['type'], part['fs_type'],
str(start), str(end)])
if part['bootable']:
cmd_args.extend(['set', str(num), 'boot', 'on'])
if part['boot_flag']:
cmd_args.extend(['set', str(num), part['boot_flag'], 'on'])
start = end
self._exec(*cmd_args)

View File

@ -47,6 +47,10 @@ opts = [
help='Size of EFI system partition in MiB when configuring '
'UEFI systems for local boot.',
deprecated_group='deploy'),
cfg.IntOpt('bios_boot_partition_size',
default=1,
help='Size of BIOS Boot partition in MiB when configuring '
'GPT partitioned systems for local boot in BIOS.'),
cfg.StrOpt('dd_block_size',
default='1M',
help='Block size to use when writing to the nodes disk.',
@ -180,9 +184,14 @@ def make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
if boot_mode == "uefi" and boot_option == "local":
part_num = dp.add_partition(CONF.disk_utils.efi_system_partition_size,
fs_type='fat32',
bootable=True)
boot_flag='boot')
part_dict['efi system partition'] = part_template % part_num
if boot_mode == "bios" and boot_option == "local" and disk_label == "gpt":
part_num = dp.add_partition(CONF.disk_utils.bios_boot_partition_size,
boot_flag='bios_grub')
part_dict['BIOS Boot partition'] = part_template % part_num
if ephemeral_mb:
LOG.debug("Add ephemeral partition (%(size)d MB) to device: %(dev)s "
"for node %(node)s",
@ -208,8 +217,14 @@ def make_partitions(dev, root_mb, swap_mb, ephemeral_mb,
LOG.debug("Add root partition (%(size)d MB) to device: %(dev)s "
"for node %(node)s",
{'dev': dev, 'size': root_mb, 'node': node_uuid})
part_num = dp.add_partition(root_mb, bootable=(boot_option == "local" and
boot_mode == "bios"))
boot_val = None
if (boot_mode == "bios" and boot_option == "local" and
disk_label == "msdos"):
boot_val = 'boot'
part_num = dp.add_partition(root_mb, boot_flag=boot_val)
part_dict['root'] = part_template % part_num
if commit:

View File

@ -30,21 +30,26 @@ class DiskPartitionerTestCase(test_base.BaseTestCase):
dp = disk_partitioner.DiskPartitioner('/dev/fake')
dp.add_partition(1024)
dp.add_partition(512, fs_type='linux-swap')
dp.add_partition(2048, bootable=True)
expected = [(1, {'bootable': False,
dp.add_partition(2048, boot_flag='boot')
dp.add_partition(2048, boot_flag='bios_grub')
expected = [(1, {'boot_flag': None,
'fs_type': '',
'type': 'primary',
'size': 1024}),
(2, {'bootable': False,
(2, {'boot_flag': None,
'fs_type': 'linux-swap',
'type': 'primary',
'size': 512}),
(3, {'bootable': True,
(3, {'boot_flag': 'boot',
'fs_type': '',
'type': 'primary',
'size': 2048}),
(4, {'boot_flag': 'bios_grub',
'fs_type': '',
'type': 'primary',
'size': 2048})]
partitions = [(n, p) for n, p in dp.get_partitions()]
self.assertThat(partitions, HasLength(3))
self.assertThat(partitions, HasLength(4))
self.assertEqual(expected, partitions)
@mock.patch.object(disk_partitioner.DiskPartitioner, '_exec',
@ -52,11 +57,15 @@ class DiskPartitionerTestCase(test_base.BaseTestCase):
@mock.patch.object(utils, 'execute', autospec=True)
def test_commit(self, mock_utils_exc, mock_disk_partitioner_exec):
dp = disk_partitioner.DiskPartitioner('/dev/fake')
fake_parts = [(1, {'bootable': False,
fake_parts = [(1, {'boot_flag': None,
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1}),
(2, {'bootable': True,
(2, {'boot_flag': 'boot',
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1}),
(3, {'boot_flag': 'bios_grub',
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1})]
@ -69,7 +78,9 @@ class DiskPartitionerTestCase(test_base.BaseTestCase):
mock.ANY, 'mklabel', 'msdos',
'mkpart', 'fake-type', 'fake-fs-type', '1', '2',
'mkpart', 'fake-type', 'fake-fs-type', '2', '3',
'set', '2', 'boot', 'on')
'set', '2', 'boot', 'on',
'mkpart', 'fake-type', 'fake-fs-type', '3', '4',
'set', '3', 'bios_grub', 'on')
mock_utils_exc.assert_called_once_with(
'fuser', '/dev/fake', run_as_root=True, check_exit_code=[0, 1])
@ -80,11 +91,11 @@ class DiskPartitionerTestCase(test_base.BaseTestCase):
def test_commit_with_device_is_busy_once(self, mock_utils_exc,
mock_disk_partitioner_exec):
dp = disk_partitioner.DiskPartitioner('/dev/fake')
fake_parts = [(1, {'bootable': False,
fake_parts = [(1, {'boot_flag': None,
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1}),
(2, {'bootable': True,
(2, {'boot_flag': 'boot',
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1})]
@ -111,11 +122,11 @@ class DiskPartitionerTestCase(test_base.BaseTestCase):
def test_commit_with_device_is_always_busy(self, mock_utils_exc,
mock_disk_partitioner_exec):
dp = disk_partitioner.DiskPartitioner('/dev/fake')
fake_parts = [(1, {'bootable': False,
fake_parts = [(1, {'boot_flag': None,
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1}),
(2, {'bootable': True,
(2, {'boot_flag': 'boot',
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1})]
@ -142,11 +153,11 @@ class DiskPartitionerTestCase(test_base.BaseTestCase):
def test_commit_with_device_disconnected(self, mock_utils_exc,
mock_disk_partitioner_exec):
dp = disk_partitioner.DiskPartitioner('/dev/fake')
fake_parts = [(1, {'bootable': False,
fake_parts = [(1, {'boot_flag': None,
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1}),
(2, {'bootable': True,
(2, {'boot_flag': 'boot',
'fs_type': 'fake-fs-type',
'type': 'fake-type',
'size': 1})]

View File

@ -240,6 +240,8 @@ class MakePartitionsTestCase(test_base.BaseTestCase):
self.ephemeral_mb = 0
self.configdrive_mb = 0
self.node_uuid = "12345678-1234-1234-1234-1234567890abcxyz"
self.efi_size = CONF.disk_utils.efi_system_partition_size
self.bios_size = CONF.disk_utils.bios_boot_partition_size
def _get_parted_cmd(self, dev, label=None):
if label is None:
@ -248,17 +250,44 @@ class MakePartitionsTestCase(test_base.BaseTestCase):
return ['parted', '-a', 'optimal', '-s', dev,
'--', 'unit', 'MiB', 'mklabel', label]
def _test_make_partitions(self, mock_exc, boot_option, disk_label=None):
def _test_make_partitions(self, mock_exc, boot_option, boot_mode='bios',
disk_label=None):
mock_exc.return_value = (None, None)
disk_utils.make_partitions(self.dev, self.root_mb, self.swap_mb,
self.ephemeral_mb, self.configdrive_mb,
self.node_uuid, boot_option=boot_option,
disk_label=disk_label)
boot_mode=boot_mode, disk_label=disk_label)
expected_mkpart = ['mkpart', 'primary', 'linux-swap', '1', '513',
'mkpart', 'primary', '', '513', '1537']
if boot_option == "local":
expected_mkpart.extend(['set', '2', 'boot', 'on'])
_s = lambda x, sz: x + sz
if boot_option == "local" and boot_mode == "uefi":
add_efi_sz = lambda x: str(_s(x, self.efi_size))
expected_mkpart = ['mkpart', 'primary', 'fat32', '1',
add_efi_sz(1),
'set', '1', 'boot', 'on',
'mkpart', 'primary', 'linux-swap',
add_efi_sz(1), add_efi_sz(513), 'mkpart',
'primary', '', add_efi_sz(513),
add_efi_sz(1537)]
else:
if boot_option == "local":
if disk_label == "gpt":
add_bios_sz = lambda x: str(_s(x, self.bios_size))
expected_mkpart = ['mkpart', 'primary', '', '1',
add_bios_sz(1),
'set', '1', 'bios_grub', 'on',
'mkpart', 'primary', 'linux-swap',
add_bios_sz(1), add_bios_sz(513),
'mkpart', 'primary', '',
add_bios_sz(513), add_bios_sz(1537)]
else:
expected_mkpart = ['mkpart', 'primary', 'linux-swap', '1',
'513', 'mkpart', 'primary', '', '513',
'1537', 'set', '2', 'boot', 'on']
else:
expected_mkpart = ['mkpart', 'primary', 'linux-swap', '1',
'513', 'mkpart', 'primary', '', '513',
'1537']
self.dev = 'fake-dev'
parted_cmd = (self._get_parted_cmd(self.dev, disk_label) +
expected_mkpart)
@ -275,6 +304,14 @@ class MakePartitionsTestCase(test_base.BaseTestCase):
def test_make_partitions_local_boot(self, mock_exc):
self._test_make_partitions(mock_exc, boot_option="local")
def test_make_partitions_local_boot_uefi(self, mock_exc):
self._test_make_partitions(mock_exc, boot_option="local",
boot_mode="uefi", disk_label="gpt")
def test_make_partitions_local_boot_gpt_bios(self, mock_exc):
self._test_make_partitions(mock_exc, boot_option="local",
disk_label="gpt")
def test_make_partitions_disk_label_gpt(self, mock_exc):
self._test_make_partitions(mock_exc, boot_option="netboot",
disk_label="gpt")

View File

@ -0,0 +1,4 @@
---
fixes:
- Fixes a problem where nodes being configured in BIOS mode to boot
from the local disk with a GPT partition table would fail at boot time.