From 99ca1086dbfc7b6e41cf800b0bd899565e2e8922 Mon Sep 17 00:00:00 2001 From: Julia Kreger Date: Fri, 25 Feb 2022 12:18:39 -0800 Subject: Create fstab entry with appropriate label Depending on the how the stars align with partition images being written to a remote system, we *may* end up with *either* a Partition UUID value, or a Partition's UUID value. Which are distinctly different. This is becasue the value, when collected as a result of writing an image to disk *falls* back and passes the value to enable partition discovery and matching. Later on, when we realized we ought to create an fstab entry, we blindly re-used the value thinking it was, indeed, always a Partition's UUID and not the Partition UUID. Obviously, the label type is quite explicit, either UUID or PARTUUID respectively, when initial ramdisk utilities such as dracut are searching and mounting filesystems. Adds capability to identify the correct label to utilize based upon the current state of the block devices on disk. Granted, we are likely only exposed to this because of IO race conditions under high concurrecy load operations. Normally this would only be seen on test VMs, but systems being backed by a Storage Area Network *can* exibit the same IO race conditions as virtual machines. Change-Id: I953c936cbf8fad889108cbf4e50b1a15f511b38c Resolves: rhbz#2058717 Story: #2009881 Task: 44623 --- .../tests/unit/extensions/test_image.py | 47 ++++++++++++++++++++-- .../tests/unit/samples/hardware_samples.py | 7 ++++ ironic_python_agent/tests/unit/test_hardware.py | 26 ++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) (limited to 'ironic_python_agent/tests') diff --git a/ironic_python_agent/tests/unit/extensions/test_image.py b/ironic_python_agent/tests/unit/extensions/test_image.py index 6c45c455..fe24a79f 100644 --- a/ironic_python_agent/tests/unit/extensions/test_image.py +++ b/ironic_python_agent/tests/unit/extensions/test_image.py @@ -751,6 +751,18 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 mock_is_md_device.return_value = False mock_md_get_raid_devices.return_value = {} mock_exists.side_effect = iter([False, True, False, True, True]) + partuuid_device = ('KNAME="sda" MODEL="DRIVE 0" SIZE="10240000" ' + 'ROTA="1" TYPE="disk" UUID="987654-3210" ' + 'PARTUUID=""\n' + 'KNAME="sda0" MODEL="DRIVE 0" SIZE="102400" ' + 'ROTA="1" TYPE="part" ' + 'UUID="' + self.fake_efi_system_part_uuid + '" ' + 'PARTUUID="1234-2918"\n') + exec_side_effect = [('', '')] * 16 + exec_side_effect.append((partuuid_device, '')) + exec_side_effect.extend([('', '')] * 8) + mock_execute.side_effect = exec_side_effect + with mock.patch('builtins.open', mock.mock_open()) as mock_open: image._install_grub2( self.fake_dev, root_uuid=self.fake_root_uuid, @@ -805,6 +817,9 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 'GRUB_DISABLE_OS_PROBER': 'true', 'GRUB_SAVEDEFAULT': 'true'}, use_standard_locale=True), + mock.call('udevadm', 'settle'), + mock.call('lsblk', '-Pbia', + '-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID'), mock.call('umount', self.fake_dir + '/boot/efi', attempts=3, delay_on_retry=True), mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' % @@ -849,8 +864,21 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 environ_mock.get.return_value = '/sbin' mock_is_md_device.return_value = False mock_md_get_raid_devices.return_value = {} + partuuid_device = ('KNAME="sda" MODEL="DRIVE 0" SIZE="10240000" ' + 'ROTA="1" TYPE="disk" UUID="987654-3210" ' + 'PARTUUID=""\n' + 'KNAME="sda0" MODEL="DRIVE 0" SIZE="102400" ' + 'ROTA="1" TYPE="part" UUID="987654-3210" ' + 'PARTUUID="' + self.fake_efi_system_part_uuid + + '"\n') + exec_side_effect = [('', '')] * 16 + exec_side_effect.append((partuuid_device, '')) + exec_side_effect.extend([('', '')] * 8) + mock_execute.side_effect = exec_side_effect + # Validates the complete opposite path *and* no-write behavior + # occurs if the entry already exists. fstab_data = ( - 'UUID=%s\tpath vfat option' % self.fake_efi_system_part_uuid) + 'PARTUUID=%s\tpath vfat option' % self.fake_efi_system_part_uuid) mock_exists.side_effect = [True, False, True, True, True, False, True, True] with mock.patch('builtins.open', @@ -916,6 +944,9 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 'GRUB_DISABLE_OS_PROBER': 'true', 'GRUB_SAVEDEFAULT': 'true'}, use_standard_locale=True), + mock.call('udevadm', 'settle'), + mock.call('lsblk', '-Pbia', + '-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID'), mock.call('umount', self.fake_dir + '/boot/efi', attempts=3, delay_on_retry=True), mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' % @@ -1026,6 +1057,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 @mock.patch.object(os, 'listdir', lambda *_: ['file1', 'file2']) @mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False) + @mock.patch.object(image, '_append_uefi_to_fstab', autospec=True) @mock.patch.object(shutil, 'copy2', autospec=True) @mock.patch.object(os.path, 'isfile', autospec=True) @mock.patch.object(image, '_efi_boot_setup', autospec=True) @@ -1042,7 +1074,8 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 mock_is_md_device, mock_exists, mock_copytree, mock_efi_setup, mock_isfile, mock_copy2, - mock_execute, mock_dispatch): + mock_fstab_append, mock_execute, + mock_dispatch): mock_exists.return_value = True mock_efi_setup.return_value = True mock_get_part_uuid.side_effect = [self.fake_root_part, @@ -1106,6 +1139,9 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 uuid=self.fake_root_uuid) mock_get_part_uuid.assert_any_call(self.fake_dev, uuid=self.fake_efi_system_part_uuid) + mock_fstab_append.assert_called_once_with( + self.fake_dir, + self.fake_efi_system_part_uuid) self.assertFalse(mock_dispatch.called) @mock.patch.object(os.path, 'ismount', lambda *_: False) @@ -1344,6 +1380,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 self.fake_efi_system_part_uuid) @mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False) + @mock.patch.object(image, '_append_uefi_to_fstab', autospec=True) @mock.patch.object(os, 'listdir', autospec=True) @mock.patch.object(shutil, 'copy2', autospec=True) @mock.patch.object(os.path, 'isfile', autospec=True) @@ -1361,8 +1398,8 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 mock_is_md_device, mock_exists, mock_copytree, mock_efi_setup, mock_isfile, mock_copy2, - mock_oslistdir, mock_execute, - mock_dispatch): + mock_oslistdir, mock_append_to_fstab, + mock_execute, mock_dispatch): mock_exists.return_value = True mock_efi_setup.return_value = True mock_get_part_uuid.side_effect = [self.fake_root_part, @@ -1431,6 +1468,8 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640 uuid=self.fake_efi_system_part_uuid) self.assertFalse(mock_dispatch.called) self.assertEqual(2, mock_oslistdir.call_count) + mock_append_to_fstab.assert_called_with(self.fake_dir, + self.fake_efi_system_part_uuid) @mock.patch.object(os.path, 'ismount', lambda *_: False) @mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False) diff --git a/ironic_python_agent/tests/unit/samples/hardware_samples.py b/ironic_python_agent/tests/unit/samples/hardware_samples.py index 3ab54153..8cc9b1ea 100644 --- a/ironic_python_agent/tests/unit/samples/hardware_samples.py +++ b/ironic_python_agent/tests/unit/samples/hardware_samples.py @@ -160,6 +160,13 @@ RAID_BLK_DEVICE_TEMPLATE = ( 'PARTUUID=""' ) +PARTUUID_DEVICE_TEMPLATE = ( + 'KNAME="sda" MODEL="DRIVE 0" SIZE="1765517033472" ' + 'ROTA="1" TYPE="disk" UUID="" PARTUUID=""\n' + 'KNAME="sda1" MODEL="DRIVE 0" SIZE="107373133824" ' + 'ROTA="1" TYPE="part" UUID="987654-3210" PARTUUID="1234-5678"\n' +) + SHRED_OUTPUT_0_ITERATIONS_ZERO_FALSE = () SHRED_OUTPUT_1_ITERATION_ZERO_TRUE = ( diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py index 7b19931b..8a05f35b 100644 --- a/ironic_python_agent/tests/unit/test_hardware.py +++ b/ironic_python_agent/tests/unit/test_hardware.py @@ -66,6 +66,13 @@ RAID_BLK_DEVICE_TEMPLATE_DEVICES = [ vendor="FooTastic", uuid=""), ] +BLK_DEVICE_TEMPLATE_PARTUUID_DEVICE = [ + hardware.BlockDevice(name='/dev/sda1', model='DRIVE 0', + size=107373133824, rotational=True, + vendor="FooTastic", uuid="987654-3210", + partuuid="1234-5678"), +] + class FakeHardwareManager(hardware.GenericHardwareManager): def __init__(self, hardware_support): @@ -4291,6 +4298,25 @@ class TestModuleFunctions(base.IronicAgentTest): self.assertEqual(RAID_BLK_DEVICE_TEMPLATE_DEVICES, result) mocked_udev.assert_called_once_with() + @mock.patch.object(os, 'readlink', autospec=True) + @mock.patch.object(hardware, '_get_device_info', + lambda x, y, z: 'FooTastic') + @mock.patch.object(hardware, '_udev_settle', autospec=True) + @mock.patch.object(hardware.pyudev.Devices, "from_device_file", + autospec=False) + def test_list_all_block_devices_partuuid_success( + self, mocked_fromdevfile, + mocked_udev, mocked_readlink, + mocked_execute): + mocked_readlink.return_value = '../../sda' + mocked_fromdevfile.return_value = {} + mocked_execute.return_value = (hws.PARTUUID_DEVICE_TEMPLATE, '') + result = hardware.list_all_block_devices(block_type='part') + mocked_execute.assert_called_once_with( + 'lsblk', '-Pbia', '-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID') + self.assertEqual(BLK_DEVICE_TEMPLATE_PARTUUID_DEVICE, result) + mocked_udev.assert_called_once_with() + @mock.patch.object(hardware, '_get_device_info', lambda x, y: "FooTastic") @mock.patch.object(hardware, '_udev_settle', autospec=True) -- cgit v1.2.1