From 3f76724dfb49df9501359e9b97acb1fc9c7689c8 Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Tue, 30 Nov 2021 11:25:43 +1300 Subject: [PATCH] Write initial grub config on startup This change removes the documentation to copy master_grub_cfg.txt to /tftpboot/grub/grub.cfg and instead writes it on conductor startup. This grub config is a simple redirect config requested by grub network boot. "master" has been renamed to "initial" as a more accurate label of its function. New configuration option [pxe]initial_grub_template allows the deployer to specify a different initial grub template. Change-Id: I71191dd399a6c49607f91d69b5b1673799a38624 --- devstack/lib/ironic | 14 ------ doc/source/install/configure-pxe.rst | 32 ------------- ironic/common/pxe_utils.py | 23 +++++++++ ironic/conf/pxe.py | 5 ++ .../drivers/modules/initial_grub_cfg.template | 7 +++ ironic/drivers/modules/master_grub_cfg.txt | 7 --- ironic/drivers/modules/pxe.py | 1 + ironic/tests/base.py | 1 + ironic/tests/unit/common/test_pxe_utils.py | 47 +++++++++++++++++++ ironic/tests/unit/conductor/mgr_utils.py | 7 ++- .../notes/initial_grub-566688b16f773fcf.yaml | 7 +++ 11 files changed, 96 insertions(+), 55 deletions(-) create mode 100644 ironic/drivers/modules/initial_grub_cfg.template delete mode 100644 ironic/drivers/modules/master_grub_cfg.txt create mode 100644 releasenotes/notes/initial_grub-566688b16f773fcf.yaml diff --git a/devstack/lib/ironic b/devstack/lib/ironic index 73087ae311..78b45f8122 100644 --- a/devstack/lib/ironic +++ b/devstack/lib/ironic @@ -2745,20 +2745,6 @@ function configure_tftpd { echo "re ^(^/) $IRONIC_TFTPBOOT_DIR/\1" >>$IRONIC_TFTPBOOT_DIR/map-file echo "re ^([^/]) $IRONIC_TFTPBOOT_DIR/\1" >>$IRONIC_TFTPBOOT_DIR/map-file - # Write a grub.cfg redirect for the ubuntu grub. The fedora grub - # will fetch the generated grub.cfg-01- directly - grub_dir=$IRONIC_TFTPBOOT_DIR/grub - mkdir -p $grub_dir - cat << EOF > $grub_dir/grub.cfg -set default=master -set timeout=1 -set hidden_timeout_quiet=false - -menuentry "master" { -configfile $IRONIC_TFTPBOOT_DIR/\$net_default_mac.conf -} -EOF - chmod 644 $grub_dir/grub.cfg else echo "r ^([^/]) $IRONIC_TFTPBOOT_DIR/\1" >$IRONIC_TFTPBOOT_DIR/map-file echo "r ^(/tftpboot/) $IRONIC_TFTPBOOT_DIR/\2" >>$IRONIC_TFTPBOOT_DIR/map-file diff --git a/doc/source/install/configure-pxe.rst b/doc/source/install/configure-pxe.rst index 0d084d839e..59346ce140 100644 --- a/doc/source/install/configure-pxe.rst +++ b/doc/source/install/configure-pxe.rst @@ -157,38 +157,6 @@ the PXE UEFI environment. sudo cp /usr/lib64/efi/shim.efi /tftpboot/bootx64.efi sudo cp /usr/lib/grub2/x86_64-efi/grub.efi /tftpboot/grubx64.efi -#. Create master grub.cfg: - - Ubuntu: Create grub.cfg under ``/tftpboot/grub`` directory:: - - GRUB_DIR=/tftpboot/grub - - Fedora: Create grub.cfg under ``/tftpboot/EFI/fedora`` directory:: - - GRUB_DIR=/tftpboot/EFI/fedora - - RHEL8/CentOS8: Create grub.cfg under ``/tftpboot/EFI/centos`` directory:: - - GRUB_DIR=/tftpboot/EFI/centos - - SUSE: Create grub.cfg under ``/tftpboot/boot/grub`` directory:: - - GRUB_DIR=/tftpboot/boot/grub - - Create directory ``GRUB_DIR``:: - - sudo mkdir -p $GRUB_DIR - - This file is used to redirect grub to baremetal node specific config file. - It redirects it to specific grub config file based on DHCP IP assigned to - baremetal node. - - .. literalinclude:: ../../../ironic/drivers/modules/master_grub_cfg.txt - - Change the permission of grub.cfg:: - - sudo chmod 644 $GRUB_DIR/grub.cfg - #. Update the bare metal node with ``boot_mode:uefi`` capability in node's properties field. See :ref:`boot_mode_support` for details. diff --git a/ironic/common/pxe_utils.py b/ironic/common/pxe_utils.py index 268c17846e..e87f731855 100644 --- a/ironic/common/pxe_utils.py +++ b/ironic/common/pxe_utils.py @@ -24,6 +24,7 @@ import jinja2 from oslo_concurrency import processutils from oslo_log import log as logging from oslo_utils import excutils +from oslo_utils import fileutils from ironic.common import dhcp_factory from ironic.common import exception @@ -1286,3 +1287,25 @@ def place_loaders_for_boot(base_path): 'the requested destination. %s' % e) LOG.error(msg) raise exception.IncorrectConfiguration(error=msg) + + +def place_common_config(): + """Place template generated config which is not node specific. + + Currently places the initial grub config for grub network boot. + """ + if not CONF.pxe.initial_grub_template: + return + + grub_dir_path = os.path.join(_get_root_dir(False), 'grub') + if not os.path.isdir(grub_dir_path): + fileutils.ensure_tree(grub_dir_path) + if CONF.pxe.dir_permission: + os.chmod(grub_dir_path, CONF.pxe.dir_permission) + + initial_grub = utils.render_template( + CONF.pxe.initial_grub_template, + {'tftp_root': _get_root_dir(False)}) + initial_grub_path = os.path.join(grub_dir_path, 'grub.cfg') + + utils.write_to_file(initial_grub_path, initial_grub) diff --git a/ironic/conf/pxe.py b/ironic/conf/pxe.py index 626c5ef6bd..d967128248 100644 --- a/ironic/conf/pxe.py +++ b/ironic/conf/pxe.py @@ -204,6 +204,11 @@ opts = [ 'for bootloaders. Use example: ' 'ipxe.efi:/usr/share/ipxe/ipxe-snponly-x86_64.efi,' 'undionly.kpxe:/usr/share/ipxe/undionly.kpxe')), + cfg.StrOpt('initial_grub_template', + default=os.path.join( + '$pybasedir', 'drivers/modules/initial_grub_cfg.template'), + help=_('On ironic-conductor node, the path to the initial grub' + 'configuration template for grub network boot.')), ] diff --git a/ironic/drivers/modules/initial_grub_cfg.template b/ironic/drivers/modules/initial_grub_cfg.template new file mode 100644 index 0000000000..3c1a2d76db --- /dev/null +++ b/ironic/drivers/modules/initial_grub_cfg.template @@ -0,0 +1,7 @@ +set default=initial +set timeout=5 +set hidden_timeout_quiet=false + +menuentry "initial" { +configfile {{ tftp_root }}/$net_default_mac.conf +} diff --git a/ironic/drivers/modules/master_grub_cfg.txt b/ironic/drivers/modules/master_grub_cfg.txt deleted file mode 100644 index 82847b81ee..0000000000 --- a/ironic/drivers/modules/master_grub_cfg.txt +++ /dev/null @@ -1,7 +0,0 @@ -set default=master -set timeout=5 -set hidden_timeout_quiet=false - -menuentry "master" { -configfile /tftpboot/$net_default_mac.conf -} diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py index 8cac287cae..50d962fcf6 100644 --- a/ironic/drivers/modules/pxe.py +++ b/ironic/drivers/modules/pxe.py @@ -39,6 +39,7 @@ class PXEBoot(pxe_base.PXEBaseMixin, base.BootInterface): capabilities = ['ramdisk_boot', 'pxe_boot'] def __init__(self): + pxe_utils.place_common_config() pxe_utils.place_loaders_for_boot(CONF.deploy.http_root) pxe_utils.place_loaders_for_boot(CONF.pxe.tftp_root) diff --git a/ironic/tests/base.py b/ironic/tests/base.py index 014581963c..1045b3d342 100644 --- a/ironic/tests/base.py +++ b/ironic/tests/base.py @@ -152,6 +152,7 @@ class TestCase(oslo_test_base.BaseTestCase): group='neutron') self.config(enabled_hardware_types=['fake-hardware', 'manual-management']) + self.config(initial_grub_template=None, group='pxe') for iface in drivers_base.ALL_INTERFACES: default = None diff --git a/ironic/tests/unit/common/test_pxe_utils.py b/ironic/tests/unit/common/test_pxe_utils.py index fbfe7f05b0..ef1e5d1f37 100644 --- a/ironic/tests/unit/common/test_pxe_utils.py +++ b/ironic/tests/unit/common/test_pxe_utils.py @@ -1035,6 +1035,53 @@ class TestPXEUtils(db_base.DbTestCase): next(actual)) self.assertEqual('/tftpboot-path/' + address + '.conf', next(actual)) + @mock.patch.object(os, 'makedirs', autospec=True) + @mock.patch.object(os.path, 'isdir', autospec=True) + @mock.patch.object(os, 'chmod', autospec=True) + def test_place_common_config(self, mock_chmod, mock_isdir, + mock_makedirs): + self.config(initial_grub_template=os.path.join( + '$pybasedir', + 'drivers/modules/initial_grub_cfg.template'), + group='pxe') + mock_isdir.return_value = False + self.config(group='pxe', dir_permission=0o777) + + def write_to_file(path, contents): + self.assertEqual('/tftpboot/grub/grub.cfg', path) + self.assertIn( + 'configfile /tftpboot/$net_default_mac.conf', + contents + ) + + with mock.patch('ironic.common.utils.write_to_file', + wraps=write_to_file): + pxe_utils.place_common_config() + + mock_isdir.assert_called_once_with('/tftpboot/grub') + mock_makedirs.assert_called_once_with('/tftpboot/grub', 511) + mock_chmod.assert_called_once_with('/tftpboot/grub', 0o777) + + @mock.patch.object(os, 'makedirs', autospec=True) + @mock.patch.object(os.path, 'isdir', autospec=True) + @mock.patch.object(os, 'chmod', autospec=True) + def test_place_common_config_existing_dirs(self, mock_chmod, mock_isdir, + mock_makedirs): + self.config(initial_grub_template=os.path.join( + '$pybasedir', + 'drivers/modules/initial_grub_cfg.template'), + group='pxe') + mock_isdir.return_value = True + + with mock.patch('ironic.common.utils.write_to_file', + autospec=True) as mock_write: + pxe_utils.place_common_config() + mock_write.assert_called_once() + + mock_isdir.assert_called_once_with('/tftpboot/grub') + mock_makedirs.assert_not_called() + mock_chmod.assert_not_called() + @mock.patch.object(ipxe.iPXEBoot, '__init__', lambda self: None) @mock.patch.object(pxe.PXEBoot, '__init__', lambda self: None) diff --git a/ironic/tests/unit/conductor/mgr_utils.py b/ironic/tests/unit/conductor/mgr_utils.py index 9baadaf42d..4451d7a159 100644 --- a/ironic/tests/unit/conductor/mgr_utils.py +++ b/ironic/tests/unit/conductor/mgr_utils.py @@ -24,6 +24,7 @@ from oslo_utils import strutils from oslo_utils import uuidutils from ironic.common import exception +from ironic.common import pxe_utils from ironic.common import states from ironic.conductor import manager from ironic import objects @@ -143,8 +144,10 @@ class ServiceSetUpMixin(object): self.service.init_host() else: with mock.patch.object(periodics, 'PeriodicWorker', autospec=True): - self.service.prepare_host() - self.service.init_host() + with mock.patch.object(pxe_utils, 'place_common_config', + autospec=True): + self.service.prepare_host() + self.service.init_host() self.addCleanup(self._stop_service) diff --git a/releasenotes/notes/initial_grub-566688b16f773fcf.yaml b/releasenotes/notes/initial_grub-566688b16f773fcf.yaml new file mode 100644 index 0000000000..0c87849491 --- /dev/null +++ b/releasenotes/notes/initial_grub-566688b16f773fcf.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Manually copying the initial grub config for grub network boot is no longer + necessary, as this file is now written to the TFTP root directory on + conductor startup. A custom template can be used to generate this file with + config option ``[pxe]initial_grub_template``.