Conditional creation of RAIDed ESP for UEFI Software RAID
Rebuilding an instance on a RAIDed ESPs will fail due to sgdisk
running against an non-clean disk and bailing out. Check if there
is a RAIDed ESP already and skip creation if it exists.
Change-Id: I13617ae77515a9d34bc4bb3caf9fae73d5e4e578
(cherry picked from commit 286d66709a)
			
			
This commit is contained in:
		@@ -12,6 +12,7 @@
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
import re
 | 
			
		||||
import shlex
 | 
			
		||||
 | 
			
		||||
from ironic_lib import disk_utils
 | 
			
		||||
from ironic_lib import utils as il_utils
 | 
			
		||||
@@ -342,6 +343,11 @@ def prepare_boot_partitions_for_softraid(device, holders, efi_part,
 | 
			
		||||
            if efi_part:
 | 
			
		||||
                efi_part = '{}p{}'.format(device, efi_part['number'])
 | 
			
		||||
 | 
			
		||||
        # check if we have a RAIDed ESP already
 | 
			
		||||
        md_device = find_esp_raid()
 | 
			
		||||
        if md_device:
 | 
			
		||||
            LOG.info("Found RAIDed ESP %s, skip creation", md_device)
 | 
			
		||||
        else:
 | 
			
		||||
            LOG.info("Creating EFI partitions on software RAID holder disks")
 | 
			
		||||
            # We know that we kept this space when configuring raid,see
 | 
			
		||||
            # hardware.GenericHardwareManager.create_configuration.
 | 
			
		||||
@@ -367,7 +373,8 @@ def prepare_boot_partitions_for_softraid(device, holders, efi_part,
 | 
			
		||||
                utils.execute("blkid")
 | 
			
		||||
 | 
			
		||||
                target_part, _u = utils.execute(
 | 
			
		||||
                "blkid", "-l", "-t", "PARTLABEL={}".format(partlabel), holder)
 | 
			
		||||
                    "blkid", "-l", "-t", "PARTLABEL={}".format(partlabel),
 | 
			
		||||
                    holder)
 | 
			
		||||
 | 
			
		||||
                target_part = target_part.splitlines()[-1].split(':', 1)[0]
 | 
			
		||||
                efi_partitions.append(target_part)
 | 
			
		||||
@@ -379,10 +386,12 @@ def prepare_boot_partitions_for_softraid(device, holders, efi_part,
 | 
			
		||||
            md_device = get_next_free_raid_device()
 | 
			
		||||
            LOG.debug("Creating md device %(md_device)s for the ESPs "
 | 
			
		||||
                      "on %(efi_partitions)s",
 | 
			
		||||
                  {'md_device': md_device, 'efi_partitions': efi_partitions})
 | 
			
		||||
                      {'md_device': md_device,
 | 
			
		||||
                       'efi_partitions': efi_partitions})
 | 
			
		||||
            utils.execute('mdadm', '--create', md_device, '--force',
 | 
			
		||||
                          '--run', '--metadata=1.0', '--level', '1',
 | 
			
		||||
                      '--name', 'esp', '--raid-devices', len(efi_partitions),
 | 
			
		||||
                          '--name', 'esp', '--raid-devices',
 | 
			
		||||
                          len(efi_partitions),
 | 
			
		||||
                          *efi_partitions)
 | 
			
		||||
 | 
			
		||||
            disk_utils.trigger_device_rescan(md_device)
 | 
			
		||||
@@ -420,3 +429,18 @@ def prepare_boot_partitions_for_softraid(device, holders, efi_part,
 | 
			
		||||
            # disk, as in virtual disk, where to load the data from.
 | 
			
		||||
            # Since there is a structural difference, this means it will
 | 
			
		||||
            # fail.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def find_esp_raid():
 | 
			
		||||
    """Find the ESP md device in case of a rebuild."""
 | 
			
		||||
 | 
			
		||||
    # find devices of type 'RAID1' and fstype 'VFAT'
 | 
			
		||||
    lsblk = utils.execute('lsblk', '-PbioNAME,TYPE,FSTYPE')
 | 
			
		||||
    report = lsblk[0]
 | 
			
		||||
    for line in report.split('\n'):
 | 
			
		||||
        dev = {}
 | 
			
		||||
        vals = shlex.split(line)
 | 
			
		||||
        for key, val in (v.split('=', 1) for v in vals):
 | 
			
		||||
            dev[key] = val.strip()
 | 
			
		||||
        if dev.get('TYPE') == 'raid1' and dev.get('FSTYPE') == 'vfat':
 | 
			
		||||
            return '/dev/' + dev.get('NAME')
 | 
			
		||||
 
 | 
			
		||||
@@ -1769,3 +1769,33 @@ MULTIPATH_LINKS_DM = (
 | 
			
		||||
    ' `-+- policy=\'service-time 0\' prio=1 status=active\n'
 | 
			
		||||
    '  `- 0:0:0:0 device s  8:0  active ready running\n'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
LSBLK_OUPUT = ("""
 | 
			
		||||
NAME="sda" TYPE="disk" FSTYPE=""
 | 
			
		||||
NAME="sdb" TYPE="disk" FSTYPE=""
 | 
			
		||||
""")
 | 
			
		||||
 | 
			
		||||
LSBLK_OUPUT_ESP_RAID = ("""
 | 
			
		||||
NAME="sda" TYPE="disk" FSTYPE=""
 | 
			
		||||
NAME="sda1" TYPE="part" FSTYPE="linux_raid_member"
 | 
			
		||||
NAME="md127" TYPE="raid1" FSTYPE=""
 | 
			
		||||
NAME="md127p1" TYPE="md" FSTYPE="xfs"
 | 
			
		||||
NAME="md127p2" TYPE="md" FSTYPE="iso9660"
 | 
			
		||||
NAME="md127p14" TYPE="md" FSTYPE=""
 | 
			
		||||
NAME="md127p15" TYPE="md" FSTYPE=""
 | 
			
		||||
NAME="sda2" TYPE="part" FSTYPE="linux_raid_member"
 | 
			
		||||
NAME="md126" TYPE="raid0" FSTYPE=""
 | 
			
		||||
NAME="sda3" TYPE="part" FSTYPE="linux_raid_member"
 | 
			
		||||
NAME="md125" TYPE="raid1" FSTYPE="vfat"
 | 
			
		||||
NAME="sdb" TYPE="disk" FSTYPE=""
 | 
			
		||||
NAME="sdb1" TYPE="part" FSTYPE="linux_raid_member"
 | 
			
		||||
NAME="md127" TYPE="raid1" FSTYPE=""
 | 
			
		||||
NAME="md127p1" TYPE="md" FSTYPE="xfs"
 | 
			
		||||
NAME="md127p2" TYPE="md" FSTYPE="iso9660"
 | 
			
		||||
NAME="md127p14" TYPE="md" FSTYPE=""
 | 
			
		||||
NAME="md127p15" TYPE="md" FSTYPE=""
 | 
			
		||||
NAME="sdb2" TYPE="part" FSTYPE="linux_raid_member"
 | 
			
		||||
NAME="md126" TYPE="raid0" FSTYPE=""
 | 
			
		||||
NAME="sdb3" TYPE="part" FSTYPE="linux_raid_member"
 | 
			
		||||
NAME="md125" TYPE="raid1" FSTYPE="vfat"
 | 
			
		||||
""")
 | 
			
		||||
 
 | 
			
		||||
@@ -153,6 +153,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
        volume_name = raid_utils.get_volume_name_of_raid_device('/dev/md0')
 | 
			
		||||
        self.assertIsNone(volume_name)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(raid_utils, 'find_esp_raid', autospec=True)
 | 
			
		||||
    @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
 | 
			
		||||
    @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
 | 
			
		||||
                       return_value='/dev/md42')
 | 
			
		||||
@@ -161,7 +162,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
    @mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
 | 
			
		||||
    def test_prepare_boot_partitions_for_softraid_uefi_gpt(
 | 
			
		||||
            self, mock_efi_part, mock_execute, mock_dispatch,
 | 
			
		||||
            mock_free_raid_device, mock_rescan):
 | 
			
		||||
            mock_free_raid_device, mock_rescan, mock_find_esp):
 | 
			
		||||
        mock_efi_part.return_value = {'number': '12'}
 | 
			
		||||
        mock_execute.side_effect = [
 | 
			
		||||
            ('451', None),  # sgdisk -F
 | 
			
		||||
@@ -178,6 +179,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
            (None, None),  # cp
 | 
			
		||||
            (None, None),  # wipefs
 | 
			
		||||
        ]
 | 
			
		||||
        mock_find_esp.return_value = None
 | 
			
		||||
 | 
			
		||||
        efi_part = raid_utils.prepare_boot_partitions_for_softraid(
 | 
			
		||||
            '/dev/md0', ['/dev/sda', '/dev/sdb'], None,
 | 
			
		||||
@@ -209,6 +211,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
        self.assertEqual(efi_part, '/dev/md42')
 | 
			
		||||
        mock_rescan.assert_called_once_with('/dev/md42')
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(raid_utils, 'find_esp_raid', autospec=True)
 | 
			
		||||
    @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
 | 
			
		||||
    @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
 | 
			
		||||
                       return_value='/dev/md42')
 | 
			
		||||
@@ -218,7 +221,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
    @mock.patch.object(ilib_utils, 'mkfs', autospec=True)
 | 
			
		||||
    def test_prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found(
 | 
			
		||||
            self, mock_mkfs, mock_efi_part, mock_execute, mock_dispatch,
 | 
			
		||||
            mock_free_raid_device, mock_rescan):
 | 
			
		||||
            mock_free_raid_device, mock_rescan, mock_find_esp):
 | 
			
		||||
        mock_efi_part.return_value = None
 | 
			
		||||
        mock_execute.side_effect = [
 | 
			
		||||
            ('451', None),  # sgdisk -F
 | 
			
		||||
@@ -233,6 +236,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
            ('/dev/sdb14: whatever', None),  # blkid
 | 
			
		||||
            (None, None),  # mdadm
 | 
			
		||||
        ]
 | 
			
		||||
        mock_find_esp.return_value = None
 | 
			
		||||
 | 
			
		||||
        efi_part = raid_utils.prepare_boot_partitions_for_softraid(
 | 
			
		||||
            '/dev/md0', ['/dev/sda', '/dev/sdb'], None,
 | 
			
		||||
@@ -262,6 +266,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
        self.assertEqual(efi_part, '/dev/md42')
 | 
			
		||||
        mock_rescan.assert_called_once_with('/dev/md42')
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(raid_utils, 'find_esp_raid', autospec=True)
 | 
			
		||||
    @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
 | 
			
		||||
    @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
 | 
			
		||||
                       return_value='/dev/md42')
 | 
			
		||||
@@ -269,7 +274,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
    @mock.patch.object(ilib_utils, 'execute', autospec=True)
 | 
			
		||||
    def test_prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided(
 | 
			
		||||
            self, mock_execute, mock_dispatch, mock_free_raid_device,
 | 
			
		||||
            mock_rescan):
 | 
			
		||||
            mock_rescan, mock_find_esp):
 | 
			
		||||
        mock_execute.side_effect = [
 | 
			
		||||
            ('451', None),  # sgdisk -F
 | 
			
		||||
            (None, None),  # sgdisk create part
 | 
			
		||||
@@ -285,6 +290,7 @@ class TestRaidUtils(base.IronicAgentTest):
 | 
			
		||||
            (None, None),  # cp
 | 
			
		||||
            (None, None),  # wipefs
 | 
			
		||||
        ]
 | 
			
		||||
        mock_find_esp.return_value = None
 | 
			
		||||
 | 
			
		||||
        efi_part = raid_utils.prepare_boot_partitions_for_softraid(
 | 
			
		||||
            '/dev/md0', ['/dev/sda', '/dev/sdb'], '/dev/md0p15',
 | 
			
		||||
@@ -389,3 +395,17 @@ class TestGetNextFreeRaidDevice(base.IronicAgentTest):
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertRaises(errors.SoftwareRAIDError,
 | 
			
		||||
                          raid_utils.get_next_free_raid_device)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock.patch.object(utils, 'execute', autospec=True)
 | 
			
		||||
class TestFindESPRAID(base.IronicAgentTest):
 | 
			
		||||
 | 
			
		||||
    def test_no_esp_raid(self, mock_execute):
 | 
			
		||||
        mock_execute.side_effect = [(hws.LSBLK_OUPUT, '')]
 | 
			
		||||
        result = raid_utils.find_esp_raid()
 | 
			
		||||
        self.assertIsNone(result)
 | 
			
		||||
 | 
			
		||||
    def test_esp_raid(self, mock_execute):
 | 
			
		||||
        mock_execute.side_effect = [(hws.LSBLK_OUPUT_ESP_RAID, '')]
 | 
			
		||||
        result = raid_utils.find_esp_raid()
 | 
			
		||||
        self.assertEqual('/dev/md125', result)
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
fixes:
 | 
			
		||||
  - |
 | 
			
		||||
    Fixes an issue with rebuilding instances on Software RAID with
 | 
			
		||||
    RAIDed ESP partitions.
 | 
			
		||||
		Reference in New Issue
	
	Block a user