Place bootloader onto /boot partition-less disks

Sometimes, there's no separate /boot fs image. Therefore,
a bootloader should have been installed onto disk where
rootfs image lands.

It seems that Nailgun driver didn't support such cases and then
an expection regarding missing /boot partition was thrown.

That was totally wrong approach. Nailgun driver should work fine
without /boot partition if there's no /boot image.

In addition, this change removes ugly hacks from Ironic driver.
Apparently Ironic driver explicitly sets self._boot_done and
self._boot_partition_done flags to True, hence enables to bypass
that exception. Since now, Nailgun can handle such a case, so
there're no any needs to keep that hack in Ironic driver.

Change-Id: I2d97fec7810643865abf0414f2cb80da1c591398
Closes-Bug: #1529066
This commit is contained in:
Alexander Gordeev 2015-12-25 19:45:13 +03:00
parent a05b6e49da
commit 95fe335f24
2 changed files with 65 additions and 12 deletions

View File

@ -147,6 +147,12 @@ class Nailgun(BaseDataDriver):
disk for disk in disks
if ('nvme' not in disk['name'] and self._is_boot_disk(disk))
]
# NOTE(agordeev) sometimes, there's no separate /boot fs image.
# Therefore bootloader should be installed into
# the disk where rootfs image lands. Ironic's case.
if not suitable_disks and not self._have_boot_partition(disks):
return [d for d in disks
if self._is_root_disk(d) and 'nvme' not in d['name']]
# FIXME(agordeev): if we have rootfs on fake raid, then /boot should
# land on it too. We can't proceed with grub-install otherwise.
md_boot_disks = [
@ -156,11 +162,19 @@ class Nailgun(BaseDataDriver):
else:
return suitable_disks
def _have_boot_partition(self, disks):
return any(self._is_boot_disk(d) for d in disks)
def _is_boot_disk(self, disk):
return any(v["type"] in ('partition', 'raid') and
v.get("mount") == "/boot"
for v in disk["volumes"])
def _is_root_disk(self, disk):
return any(v["type"] in ('partition', 'raid') and
v.get("mount") == "/"
for v in disk["volumes"])
def _is_os_volume(self, vol):
return vol['size'] > 0 and vol['type'] == 'pv' and vol['vg'] == 'os'
@ -467,8 +481,9 @@ class Nailgun(BaseDataDriver):
disk['name'])
parted.add_partition(size=20, configdrive=True)
# checking if /boot is created
if not self._boot_partition_done or not self._boot_done:
# checking if /boot is expected to be created
if self._have_boot_partition(self.ks_disks) and \
(not self._boot_partition_done or not self._boot_done):
raise errors.WrongPartitionSchemeError(
'/boot partition has not been created for some reasons')
@ -648,14 +663,6 @@ class Ironic(Nailgun):
def parse_configdrive_scheme(self):
pass
def parse_partition_scheme(self):
# FIXME(yuriyz): Using of internal attributes of base class is very
# fragile. This code acts only as temporary solution. Ironic should
# use own driver, based on simple driver.
self._boot_partition_done = True
self._boot_done = True
return super(Ironic, self).parse_partition_scheme()
class NailgunBuildImage(BaseDataDriver):

View File

@ -634,6 +634,25 @@ FIRST_DISK_HUGE_KS_SPACES = [
}
]
ONLY_ROOTFS_IMAGE_SPACES = [
{
"name": "sda",
"extra": [],
"free_space": 11000,
"volumes": [
{
"mount": "/",
"type": "partition",
"file_system": "ext4",
"size": 10000
}
],
"size": 11000,
"type": "disk",
"id": "sda",
}
]
FIRST_DISK_NVME_KS_SPACES = [
{
"name": "nvme0n1",
@ -1433,8 +1452,13 @@ class TestNailgunMockedMeta(unittest2.TestCase):
data = copy.deepcopy(PROVISION_SAMPLE_DATA)
data['ks_meta']['pm_data']['ks_spaces'] = NO_BOOT_KS_SPACES
mock_lbd.return_value = LIST_BLOCK_DEVICES_SAMPLE
self.assertRaises(errors.WrongPartitionSchemeError,
nailgun.Nailgun, data)
drv = nailgun.Nailgun(data)
self.assertEqual(
drv.partition_scheme.fs_by_mount('/').device,
'/dev/sda3')
# there's no boot partition is scheme.
# It is not expected to be created
self.assertIsNone(drv.partition_scheme.fs_by_mount('/boot'))
def test_boot_partition_no_boot_nvme(self, mock_lbd, mock_image_meta):
data = copy.deepcopy(PROVISION_SAMPLE_DATA)
@ -1454,6 +1478,28 @@ class TestNailgunMockedMeta(unittest2.TestCase):
drv.partition_scheme.fs_by_mount('/boot').device,
'/dev/sda3')
def test_boot_partition_is_on_rootfs_nailgun(self, mock_lbd,
mock_image_meta):
data = copy.deepcopy(PROVISION_SAMPLE_DATA)
data['ks_meta']['pm_data']['ks_spaces'] = ONLY_ROOTFS_IMAGE_SPACES
mock_lbd.return_value = LIST_BLOCK_DEVICES_SAMPLE_NVME
drv = nailgun.Nailgun(data)
self.assertEqual(
drv.partition_scheme.fs_by_mount('/').device,
'/dev/sda3')
self.assertIsNone(drv.partition_scheme.fs_by_mount('/boot'))
def test_boot_partition_is_on_rootfs_ironic(self, mock_lbd,
mock_image_meta):
data = copy.deepcopy(PROVISION_SAMPLE_DATA)
data['ks_meta']['pm_data']['ks_spaces'] = ONLY_ROOTFS_IMAGE_SPACES
mock_lbd.return_value = LIST_BLOCK_DEVICES_SAMPLE_NVME
drv = nailgun.Ironic(data)
self.assertEqual(
drv.partition_scheme.fs_by_mount('/').device,
'/dev/sda3')
self.assertIsNone(drv.partition_scheme.fs_by_mount('/boot'))
def test_md_metadata_centos(self, mock_lbd, mock_image_meta):
data = copy.deepcopy(PROVISION_SAMPLE_DATA)
data['profile'] = 'base-centos-x86_64'