summaryrefslogtreecommitdiff
path: root/ironic_python_agent
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2019-10-28 11:43:04 +0000
committerGerrit Code Review <review@openstack.org>2019-10-28 11:43:04 +0000
commita158b862eaf99f0f3018496adf076b538ae5a64d (patch)
treeef35d85ee46b795468ff3ed240efe0ced8a775a0 /ironic_python_agent
parent945133ee8d9d9fe673e2dc55d9725133f933608a (diff)
parent19754780971f2316021ac0d8459d31fa77d239eb (diff)
downloadironic-python-agent-a158b862eaf99f0f3018496adf076b538ae5a64d.tar.gz
Merge "Set rd.md.uuid kernel parameter when deploying on software raid"
Diffstat (limited to 'ironic_python_agent')
-rw-r--r--ironic_python_agent/extensions/image.py27
-rw-r--r--ironic_python_agent/hardware.py18
-rw-r--r--ironic_python_agent/tests/unit/extensions/test_image.py44
3 files changed, 74 insertions, 15 deletions
diff --git a/ironic_python_agent/extensions/image.py b/ironic_python_agent/extensions/image.py
index ce5d59cd..ba4710d2 100644
--- a/ironic_python_agent/extensions/image.py
+++ b/ironic_python_agent/extensions/image.py
@@ -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 '
diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py
index 9126abcc..412f6110 100644
--- a/ironic_python_agent/hardware.py
+++ b/ironic_python_agent/hardware.py
@@ -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.
diff --git a/ironic_python_agent/tests/unit/extensions/test_image.py b/ironic_python_agent/tests/unit/extensions/test_image.py
index 9cf76492..3b6cf9fb 100644
--- a/ironic_python_agent/tests/unit/extensions/test_image.py
+++ b/ironic_python_agent/tests/unit/extensions/test_image.py
@@ -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)