Browse Source

Set rd.md.uuid kernel parameter when deploying on software raid

When deploying an image to a software raid array, it is currently
required that the deployed image assembles the md arrays automatically
so that the rootfs can be mounted. In order to remove this
requirement/limitation on the deployed image we can add rd.md.uuid to
the kernel command line with the raid array's uuid.

Story: 2006648
Task: 36884
Change-Id: I42cb198753ecd84b7eaef6b5aa7c2064535bfe0e
(cherry picked from commit 19754780971f2316021ac0d8459d31fa77d239eb)
tags/5.0.1
Andrei Nistor Iury Gregory Melo Ferreira 4 months ago
parent
commit
72b85c539d
3 changed files with 74 additions and 15 deletions
  1. +27
    -0
      ironic_python_agent/extensions/image.py
  2. +18
    -0
      ironic_python_agent/hardware.py
  3. +29
    -15
      ironic_python_agent/tests/unit/extensions/test_image.py

+ 27
- 0
ironic_python_agent/extensions/image.py View File

@@ -14,6 +14,7 @@
# under the License.

import os
import re
import shlex
import shutil
import stat
@@ -123,6 +124,16 @@ def _get_partition(device, uuid):
raise errors.CommandExecutionError(error_msg)


def _has_dracut(root):
try:
utils.execute('chroot %(path)s /bin/sh -c '
'"which dracut"' %
{'path': root}, shell=True)
except processutils.ProcessExecutionError:
return False
return True


def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
prep_boot_part_uuid=None):
"""Install GRUB2 bootloader on a given device."""
@@ -202,6 +213,22 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
{'path': path, 'bin': binary_name, 'dev': device},
shell=True, env_variables={'PATH': path_variable})

# If the image has dracut installed, set the rd.md.uuid kernel
# parameter for discovered md devices.
if hardware.is_md_device(device) and _has_dracut(path):
rd_md_uuids = ["rd.md.uuid=%s" % x['UUID']
for x in hardware.md_get_raid_devices().values()]

LOG.debug("Setting rd.md.uuid kernel parameters: %s", rd_md_uuids)
with open('%s/etc/default/grub' % path, 'r') as g:
contents = g.read()
with open('%s/etc/default/grub' % path, 'w') as g:
g.write(
re.sub(r'GRUB_CMDLINE_LINUX="(.*)"',
r'GRUB_CMDLINE_LINUX="\1 %s"'
% " ".join(rd_md_uuids),
contents))

# Generate the grub configuration file
utils.execute('chroot %(path)s /bin/sh -c '
'"%(bin)s-mkconfig -o '


+ 18
- 0
ironic_python_agent/hardware.py View File

@@ -224,6 +224,24 @@ def md_restart(raid_device):
raise errors.CommandExecutionError(error_msg)


def md_get_raid_devices():
"""Get all discovered Software RAID (md) devices

:return: A python dict containing details about the discovered RAID
devices
"""
report = utils.execute('mdadm', '--examine', '--scan')[0]
lines = report.splitlines()
result = {}
for line in lines:
vals = shlex.split(line)
device = vals[1]
result[device] = {}
for key, val in (v.split('=', 1) for v in vals[2:]):
result[device][key] = val.strip()
return result


def _md_scan_and_assemble():
"""Scan all md devices and assemble RAID arrays from them.



+ 29
- 15
ironic_python_agent/tests/unit/extensions/test_image.py View File

@@ -92,14 +92,16 @@ class TestImageExtension(base.IronicAgentTest):
mock_iscsi_clean.assert_called_once_with(self.fake_dev)

@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
@mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
def test__install_grub2(self, mock_get_part_uuid, environ_mock,
mock_is_md_device, mock_execute,
mock_dispatch):
mock_md_get_raid_devices, mock_is_md_device,
mock_execute, mock_dispatch):
mock_get_part_uuid.return_value = self.fake_root_part
environ_mock.get.return_value = '/sbin'
mock_is_md_device.side_effect = [False]
mock_is_md_device.side_effect = [False, False]
mock_md_get_raid_devices.return_value = {}
image._install_grub2(self.fake_dev, self.fake_root_uuid)

expected = [mock.call('mount', '/dev/fake2', self.fake_dir),
@@ -136,15 +138,18 @@ class TestImageExtension(base.IronicAgentTest):
self.assertFalse(mock_dispatch.called)

@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
@mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
def test__install_grub2_prep(self, mock_get_part_uuid, environ_mock,
mock_is_md_device, mock_execute,
mock_dispatch):
mock_md_get_raid_devices, mock_is_md_device,
mock_execute, mock_dispatch):
mock_get_part_uuid.side_effect = [self.fake_root_part,
self.fake_prep_boot_part]
environ_mock.get.return_value = '/sbin'
mock_is_md_device.side_effect = [False]
mock_is_md_device.side_effect = [False, False]
mock_md_get_raid_devices.return_value = {}

image._install_grub2(self.fake_dev, self.fake_root_uuid,
prep_boot_part_uuid=self.fake_prep_boot_part_uuid)

@@ -185,16 +190,19 @@ class TestImageExtension(base.IronicAgentTest):
self.assertFalse(mock_dispatch.called)

@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
@mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
def test__install_grub2_uefi(self, mock_get_part_uuid, mkdir_mock,
environ_mock, mock_is_md_device,
mock_execute, mock_dispatch):
environ_mock, mock_md_get_raid_devices,
mock_is_md_device, mock_execute,
mock_dispatch):
mock_get_part_uuid.side_effect = [self.fake_root_part,
self.fake_efi_system_part]
environ_mock.get.return_value = '/sbin'
mock_is_md_device.return_value = False
mock_md_get_raid_devices.return_value = {}

image._install_grub2(
self.fake_dev, root_uuid=self.fake_root_uuid,
@@ -245,15 +253,18 @@ class TestImageExtension(base.IronicAgentTest):
self.assertFalse(mock_dispatch.called)

@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
@mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
def test__install_grub2_uefi_umount_fails(
self, mock_get_part_uuid, mkdir_mock, environ_mock,
mock_is_md_device, mock_execute, mock_dispatch):
mock_md_get_raid_devices, mock_is_md_device, mock_execute,
mock_dispatch):
mock_get_part_uuid.side_effect = [self.fake_root_part,
self.fake_efi_system_part]
mock_is_md_device.return_value = False
mock_md_get_raid_devices.return_value = {}

def umount_raise_func(*args, **kwargs):
if args[0] == 'umount':
@@ -295,15 +306,18 @@ class TestImageExtension(base.IronicAgentTest):
mock_execute.assert_has_calls(expected)

@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(hardware, 'md_get_raid_devices', autospec=True)
@mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
@mock.patch.object(image, '_get_partition', autospec=True)
def test__install_grub2_uefi_mount_fails(
self, mock_get_part_uuid, mkdir_mock, environ_mock,
mock_is_md_device, mock_execute, mock_dispatch):
mock_is_md_device, mock_md_get_raid_devices, mock_execute,
mock_dispatch):
mock_get_part_uuid.side_effect = [self.fake_root_part,
self.fake_efi_system_part]
mock_is_md_device.side_effect = [False]
mock_is_md_device.side_effect = [False, False]
mock_md_get_raid_devices.return_value = {}

def mount_raise_func(*args, **kwargs):
if args[0] == 'mount':
@@ -345,7 +359,7 @@ class TestImageExtension(base.IronicAgentTest):
@mock.patch.object(hardware, 'is_md_device', autospec=True)
def test__get_partition(self, mock_is_md_device, mock_execute,
mock_dispatch):
mock_is_md_device.side_effect = [False]
mock_is_md_device.side_effect = [False, False]
lsblk_output = ('''KNAME="test" UUID="" TYPE="disk"
KNAME="test1" UUID="256a39e3-ca3c-4fb8-9cc2-b32eec441f47" TYPE="part"
KNAME="test2" UUID="%s" TYPE="part"''' % self.fake_root_uuid)
@@ -364,7 +378,7 @@ class TestImageExtension(base.IronicAgentTest):
@mock.patch.object(hardware, 'is_md_device', autospec=True)
def test__get_partition_no_device_found(self, mock_is_md_device,
mock_execute, mock_dispatch):
mock_is_md_device.side_effect = [False]
mock_is_md_device.side_effect = [False, False]
lsblk_output = ('''KNAME="test" UUID="" TYPE="disk"
KNAME="test1" UUID="256a39e3-ca3c-4fb8-9cc2-b32eec441f47" TYPE="part"
KNAME="test2" UUID="" TYPE="part"''')
@@ -412,7 +426,7 @@ class TestImageExtension(base.IronicAgentTest):
@mock.patch.object(hardware, 'is_md_device', autospec=True)
def test__get_partition_command_fail(self, mock_is_md_device,
mock_execute, mock_dispatch):
mock_is_md_device.side_effect = [False]
mock_is_md_device.side_effect = [False, False]
mock_execute.side_effect = (None, None,
processutils.ProcessExecutionError('boom'))
self.assertRaises(errors.CommandExecutionError,
@@ -430,7 +444,7 @@ class TestImageExtension(base.IronicAgentTest):
@mock.patch.object(hardware, 'is_md_device', autospec=True)
def test__get_partition_partuuid(self, mock_is_md_device, mock_execute,
mock_dispatch):
mock_is_md_device.side_effect = [False]
mock_is_md_device.side_effect = [False, False]
lsblk_output = ('''KNAME="test" UUID="" TYPE="disk"
KNAME="test1" UUID="256a39e3-ca3c-4fb8-9cc2-b32eec441f47" TYPE="part"
KNAME="test2" PARTUUID="%s" TYPE="part"''' % self.fake_root_uuid)


Loading…
Cancel
Save