summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Tantsur <dtantsur@protonmail.com>2021-11-15 19:40:03 +0100
committerDmitry Tantsur <dtantsur@protonmail.com>2021-11-15 20:09:20 +0100
commitdbc24610d907cb51d8b711edd1b6bd5413c85e69 (patch)
treea7a236dec81253091aadc257807dabbe9b3f8884
parent2ef65aa368842472946c7aa33749121f845ac336 (diff)
downloadironic-dbc24610d907cb51d8b711edd1b6bd5413c85e69.tar.gz
Add an option to create inspector-compatible boot.ipxe
Currently the default boot.ipxe is not suitable for ironic-inspector in a standalone configuration. This change adds a new option [pxe]ipxe_fallback_script that makes boot.ipxe fall back to the provided script. Story: #2009294 Task: #43982 Change-Id: Id5547885e75beafb4423e9e2056c79c54b286275
-rw-r--r--ironic/common/pxe_utils.py3
-rw-r--r--ironic/conf/pxe.py4
-rw-r--r--ironic/drivers/modules/boot.ipxe10
-rw-r--r--ironic/tests/unit/common/test_pxe_utils.py36
-rw-r--r--ironic/tests/unit/drivers/boot-fallback.ipxe31
-rw-r--r--ironic/tests/unit/drivers/modules/test_ipxe.py6
-rw-r--r--releasenotes/notes/ipxe-fallback-a10c8ce422caa429.yaml5
7 files changed, 90 insertions, 5 deletions
diff --git a/ironic/common/pxe_utils.py b/ironic/common/pxe_utils.py
index 08d6d3f98..c0e2ca6e0 100644
--- a/ironic/common/pxe_utils.py
+++ b/ironic/common/pxe_utils.py
@@ -379,7 +379,8 @@ def create_ipxe_boot_script():
"""Render the iPXE boot script into the HTTP root directory"""
boot_script = utils.render_template(
CONF.pxe.ipxe_boot_script,
- {'ipxe_for_mac_uri': PXE_CFG_DIR_NAME + '/'})
+ {'ipxe_for_mac_uri': PXE_CFG_DIR_NAME + '/',
+ 'ipxe_fallback_script': CONF.pxe.ipxe_fallback_script})
bootfile_path = os.path.join(
CONF.deploy.http_root,
os.path.basename(CONF.pxe.ipxe_boot_script))
diff --git a/ironic/conf/pxe.py b/ironic/conf/pxe.py
index 81aef8e74..08c554f87 100644
--- a/ironic/conf/pxe.py
+++ b/ironic/conf/pxe.py
@@ -144,6 +144,10 @@ opts = [
'$pybasedir', 'drivers/modules/boot.ipxe'),
help=_('On ironic-conductor node, the path to the main iPXE '
'script file.')),
+ cfg.StrOpt('ipxe_fallback_script',
+ help=_('File name (e.g. inspector.ipxe) of an iPXE script to '
+ 'fall back to when booting to a MAC-specific script '
+ 'fails. When not set, booting will fail in this case.')),
cfg.IntOpt('ipxe_timeout',
default=0,
help=_('Timeout value (in seconds) for downloading an image '
diff --git a/ironic/drivers/modules/boot.ipxe b/ironic/drivers/modules/boot.ipxe
index 4739695ba..4ed58497c 100644
--- a/ironic/drivers/modules/boot.ipxe
+++ b/ironic/drivers/modules/boot.ipxe
@@ -11,12 +11,22 @@ echo Attempting to boot from MAC ${net${netid}/mac:hexhyp}
chain {{ ipxe_for_mac_uri }}${net${netid}/mac:hexhyp} || goto loop
:loop_done
+{% if ipxe_fallback_script -%}
+chain {{ ipxe_fallback_script }} | goto boot_failed
+
+:boot_failed
+{% endif -%}
echo PXE boot failed! No configuration found for any of the present NICs.
echo Press any key to reboot...
prompt --timeout 180
reboot
:old_rom
+{% if ipxe_fallback_script -%}
+chain {{ ipxe_fallback_script }} | goto boot_failed_old_rom
+
+:boot_failed_old_rom
+{% endif -%}
echo PXE boot failed! No configuration found for NIC ${mac:hexhyp}.
echo Please update your iPXE ROM and retry.
echo Press any key to reboot...
diff --git a/ironic/tests/unit/common/test_pxe_utils.py b/ironic/tests/unit/common/test_pxe_utils.py
index 2c16e7eb7..4768b914a 100644
--- a/ironic/tests/unit/common/test_pxe_utils.py
+++ b/ironic/tests/unit/common/test_pxe_utils.py
@@ -154,6 +154,17 @@ class TestPXEUtils(db_base.DbTestCase):
self.assertEqual(str(expected_template), rendered_template)
+ def test_fallback_ipxe_boot_script(self):
+ rendered_template = utils.render_template(
+ CONF.pxe.ipxe_boot_script,
+ {'ipxe_for_mac_uri': 'pxelinux.cfg/',
+ 'ipxe_fallback_script': 'inspector.ipxe'})
+
+ with open('ironic/tests/unit/drivers/boot-fallback.ipxe') as f:
+ expected_template = f.read().rstrip()
+
+ self.assertEqual(str(expected_template), rendered_template)
+
def test_default_ipxe_config(self):
# NOTE(lucasagomes): iPXE is just an extension of the PXE driver,
# it doesn't have it's own configuration option for template.
@@ -714,7 +725,8 @@ class TestPXEUtils(db_base.DbTestCase):
'foo')
render_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
- {'ipxe_for_mac_uri': 'pxelinux.cfg/'})
+ {'ipxe_for_mac_uri': 'pxelinux.cfg/',
+ 'ipxe_fallback_script': None})
@mock.patch.object(os.path, 'isfile', lambda path: True)
@mock.patch('ironic.common.utils.file_has_content', autospec=True)
@@ -735,7 +747,27 @@ class TestPXEUtils(db_base.DbTestCase):
'foo')
render_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
- {'ipxe_for_mac_uri': 'pxelinux.cfg/'})
+ {'ipxe_for_mac_uri': 'pxelinux.cfg/',
+ 'ipxe_fallback_script': None})
+
+ @mock.patch.object(os.path, 'isfile', lambda path: False)
+ @mock.patch('ironic.common.utils.file_has_content', autospec=True)
+ @mock.patch('ironic.common.utils.write_to_file', autospec=True)
+ @mock.patch('ironic.common.utils.render_template', autospec=True)
+ def test_create_ipxe_boot_script_fallback(self, render_mock, write_mock,
+ file_has_content_mock):
+ self.config(ipxe_fallback_script='inspector.ipxe', group='pxe')
+ render_mock.return_value = 'foo'
+ pxe_utils.create_ipxe_boot_script()
+ self.assertFalse(file_has_content_mock.called)
+ write_mock.assert_called_once_with(
+ os.path.join(CONF.deploy.http_root,
+ os.path.basename(CONF.pxe.ipxe_boot_script)),
+ 'foo')
+ render_mock.assert_called_once_with(
+ CONF.pxe.ipxe_boot_script,
+ {'ipxe_for_mac_uri': 'pxelinux.cfg/',
+ 'ipxe_fallback_script': 'inspector.ipxe'})
@mock.patch.object(os.path, 'isfile', lambda path: True)
@mock.patch('ironic.common.utils.file_has_content', autospec=True)
diff --git a/ironic/tests/unit/drivers/boot-fallback.ipxe b/ironic/tests/unit/drivers/boot-fallback.ipxe
new file mode 100644
index 000000000..bf8ab414c
--- /dev/null
+++ b/ironic/tests/unit/drivers/boot-fallback.ipxe
@@ -0,0 +1,31 @@
+#!ipxe
+
+# NOTE(lucasagomes): Loop over all network devices and boot from
+# the first one capable of booting. For more information see:
+# https://bugs.launchpad.net/ironic/+bug/1504482
+set netid:int32 -1
+:loop
+inc netid || chain pxelinux.cfg/${mac:hexhyp} || goto old_rom
+isset ${net${netid}/mac} || goto loop_done
+echo Attempting to boot from MAC ${net${netid}/mac:hexhyp}
+chain pxelinux.cfg/${net${netid}/mac:hexhyp} || goto loop
+
+:loop_done
+chain inspector.ipxe | goto boot_failed
+
+:boot_failed
+echo PXE boot failed! No configuration found for any of the present NICs.
+echo Press any key to reboot...
+prompt --timeout 180
+reboot
+
+:old_rom
+chain inspector.ipxe | goto boot_failed_old_rom
+
+:boot_failed_old_rom
+echo PXE boot failed! No configuration found for NIC ${mac:hexhyp}.
+echo Please update your iPXE ROM and retry.
+echo Press any key to reboot...
+prompt --timeout 180
+reboot
+
diff --git a/ironic/tests/unit/drivers/modules/test_ipxe.py b/ironic/tests/unit/drivers/modules/test_ipxe.py
index 45a68eeb3..2254a2c72 100644
--- a/ironic/tests/unit/drivers/modules/test_ipxe.py
+++ b/ironic/tests/unit/drivers/modules/test_ipxe.py
@@ -395,7 +395,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
'foo')
render_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
- {'ipxe_for_mac_uri': 'pxelinux.cfg/'})
+ {'ipxe_for_mac_uri': 'pxelinux.cfg/',
+ 'ipxe_fallback_script': None})
@mock.patch.object(os.path, 'isfile', lambda path: False)
@mock.patch('ironic.common.utils.file_has_content', autospec=True)
@@ -415,7 +416,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
'foo')
render_mock.assert_called_once_with(
CONF.pxe.ipxe_boot_script,
- {'ipxe_for_mac_uri': 'pxelinux.cfg/'})
+ {'ipxe_for_mac_uri': 'pxelinux.cfg/',
+ 'ipxe_fallback_script': None})
@mock.patch.object(os.path, 'isfile', lambda path: True)
@mock.patch.object(common_utils, 'file_has_content', lambda *args: True)
diff --git a/releasenotes/notes/ipxe-fallback-a10c8ce422caa429.yaml b/releasenotes/notes/ipxe-fallback-a10c8ce422caa429.yaml
new file mode 100644
index 000000000..0f0d60236
--- /dev/null
+++ b/releasenotes/notes/ipxe-fallback-a10c8ce422caa429.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Adds a new configuration option ``[pxe]ipxe_fallback_script`` which allows
+ iPXE boot to fall back to e.g. ironic-inspector iPXE script.