diff --git a/coriolis/osmorphing/redhat.py b/coriolis/osmorphing/redhat.py index 9308e088..b7ac1449 100644 --- a/coriolis/osmorphing/redhat.py +++ b/coriolis/osmorphing/redhat.py @@ -69,14 +69,23 @@ def get_update_grub2_command(self): return "grub2-mkconfig -o %s" % location def _get_grub2_cfg_location(self): + """Get GRUB2 config location for Red Hat-based distros. + + On RHEL 9.4+ and related distros (Rocky, Alma, etc.), the EFI grub.cfg + is a wrapper that loads from /boot/grub2/grub.cfg. grub2-mkconfig + refuses to overwrite the wrapper and requires output to + /boot/grub2/grub.cfg. Prefer the BIOS path when it exists. + """ self._exec_cmd_chroot("mount /boot || true") self._exec_cmd_chroot("mount /boot/efi || true") uefi_cfg = os.path.join(self.UEFI_GRUB_LOCATION, "grub.cfg") bios_cfg = os.path.join(self.BIOS_GRUB_LOCATION, "grub.cfg") - if self._test_path_chroot(uefi_cfg): - return uefi_cfg + # Prefer /boot/grub2/grub.cfg - on RHEL 9.4+ UEFI, the EFI file is a + # wrapper and grub2-mkconfig must write to /boot/grub2/grub.cfg if self._test_path_chroot(bios_cfg): return bios_cfg + if self._test_path_chroot(uefi_cfg): + return uefi_cfg raise Exception( "could not determine grub location." " boot partition not mounted?") diff --git a/coriolis/tests/osmorphing/test_redhat.py b/coriolis/tests/osmorphing/test_redhat.py index 7c9d3fd6..f3c1e459 100644 --- a/coriolis/tests/osmorphing/test_redhat.py +++ b/coriolis/tests/osmorphing/test_redhat.py @@ -70,35 +70,37 @@ def test_get_update_grub2_command(self, mock_get_grub2_cfg_location): @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot') @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path_chroot') - def test__get_grub2_cfg_location_uefi(self, mock_test_path_chroot, + def test__get_grub2_cfg_location_bios(self, mock_test_path_chroot, mock_exec_cmd_chroot): mock_test_path_chroot.return_value = True result = self.morphing_tools._get_grub2_cfg_location() - self.assertEqual(result, '/boot/efi/EFI/redhat/grub.cfg') + self.assertEqual(result, '/boot/grub2/grub.cfg') mock_exec_cmd_chroot.assert_has_calls([ mock.call("mount /boot || true"), mock.call("mount /boot/efi || true") ]) mock_test_path_chroot.assert_called_once_with( - '/boot/efi/EFI/redhat/grub.cfg') + '/boot/grub2/grub.cfg') @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot') @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path_chroot') - def test__get_grub2_cfg_location_bios(self, mock_test_path_chroot, + def test__get_grub2_cfg_location_uefi(self, mock_test_path_chroot, mock_exec_cmd_chroot): mock_test_path_chroot.side_effect = [False, True] result = self.morphing_tools._get_grub2_cfg_location() - mock_test_path_chroot.assert_called_with('/boot/grub2/grub.cfg') + self.assertEqual(result, '/boot/efi/EFI/redhat/grub.cfg') mock_exec_cmd_chroot.assert_has_calls([ mock.call("mount /boot || true"), mock.call("mount /boot/efi || true") ]) - - self.assertEqual(result, '/boot/grub2/grub.cfg') + mock_test_path_chroot.assert_has_calls([ + mock.call('/boot/grub2/grub.cfg'), + mock.call('/boot/efi/EFI/redhat/grub.cfg') + ]) @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot') @mock.patch.object(base.BaseLinuxOSMorphingTools, '_test_path_chroot') @@ -117,8 +119,8 @@ def test__get_grub2_cfg_location_unknown(self, mock_test_path_chroot, mock.call("mount /boot/efi || true") ]) mock_test_path_chroot.assert_has_calls([ - mock.call('/boot/efi/EFI/redhat/grub.cfg'), - mock.call('/boot/grub2/grub.cfg') + mock.call('/boot/grub2/grub.cfg'), + mock.call('/boot/efi/EFI/redhat/grub.cfg') ]) @mock.patch.object(base.BaseLinuxOSMorphingTools, '_exec_cmd_chroot')