Add UEFI support for iPXE
This patch adds UEFI support for iPXE, the changes made are: * Remove conditional preventing iPXE to be configured with UEFI * Add the boot_mode= kernel parameter to the iPXE template * Add initrd=deploy_ramdisk kernel parameter to the iPXE template. The UEFI support in iPXE requires the kernel argument to match what the initrd expects. For more information see [0] [0] http://forum.ipxe.org/showthread.php?tid=7589&pid=11843#pid11843 Closes-Bug: #1525989 Change-Id: I6e74bc6332c5aba92ef0de8694fd4259c596cf03
This commit is contained in:
@@ -985,20 +985,20 @@ on the Bare Metal service node(s) where ``ironic-conductor`` is running.
|
|||||||
Fedora 22 or higher:
|
Fedora 22 or higher:
|
||||||
dnf install ipxe-bootimgs
|
dnf install ipxe-bootimgs
|
||||||
|
|
||||||
#. Copy the iPXE boot image (undionly.kpxe) to ``/tftpboot``. The binary
|
#. Copy the iPXE boot image (``undionly.kpxe`` for **BIOS** and
|
||||||
might be found at::
|
``ipxe.efi`` for **UEFI**) to ``/tftpboot``. The binary might
|
||||||
|
be found at::
|
||||||
|
|
||||||
Ubuntu:
|
Ubuntu:
|
||||||
cp /usr/lib/ipxe/undionly.kpxe /tftpboot
|
cp /usr/lib/ipxe/{undionly.kpxe,ipxe.efi} /tftpboot
|
||||||
|
|
||||||
Fedora/RHEL7/CentOS7:
|
Fedora/RHEL7/CentOS7:
|
||||||
cp /usr/share/ipxe/undionly.kpxe /tftpboot
|
cp /usr/share/ipxe/{undionly.kpxe,ipxe.efi} /tftpboot
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
If the packaged version of the iPXE boot image doesn't work, you
|
If the packaged version of the iPXE boot image doesn't work, you can
|
||||||
can download a prebuilt one from http://boot.ipxe.org/undionly.kpxe
|
download a prebuilt one from http://boot.ipxe.org or build one image
|
||||||
or build one image from source, see http://ipxe.org/download for
|
from source, see http://ipxe.org/download for more information.
|
||||||
more information.
|
|
||||||
|
|
||||||
#. Enable/Configure iPXE in the Bare Metal Service's configuration file
|
#. Enable/Configure iPXE in the Bare Metal Service's configuration file
|
||||||
(/etc/ironic/ironic.conf)::
|
(/etc/ironic/ironic.conf)::
|
||||||
@@ -1011,9 +1011,16 @@ on the Bare Metal service node(s) where ``ironic-conductor`` is running.
|
|||||||
# Neutron bootfile DHCP parameter. (string value)
|
# Neutron bootfile DHCP parameter. (string value)
|
||||||
pxe_bootfile_name=undionly.kpxe
|
pxe_bootfile_name=undionly.kpxe
|
||||||
|
|
||||||
|
# Bootfile DHCP parameter for UEFI boot mode. (string value)
|
||||||
|
uefi_pxe_bootfile_name=ipxe.efi
|
||||||
|
|
||||||
# Template file for PXE configuration. (string value)
|
# Template file for PXE configuration. (string value)
|
||||||
pxe_config_template=$pybasedir/drivers/modules/ipxe_config.template
|
pxe_config_template=$pybasedir/drivers/modules/ipxe_config.template
|
||||||
|
|
||||||
|
# Template file for PXE configuration for UEFI boot loader.
|
||||||
|
# (string value)
|
||||||
|
uefi_pxe_config_template=$pybasedir/drivers/modules/ipxe_config.template
|
||||||
|
|
||||||
#. Restart the ``ironic-conductor`` process::
|
#. Restart the ``ironic-conductor`` process::
|
||||||
|
|
||||||
Fedora/RHEL7/CentOS7:
|
Fedora/RHEL7/CentOS7:
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ def create_pxe_config(task, pxe_options, template=None):
|
|||||||
pxe_config_disk_ident)
|
pxe_config_disk_ident)
|
||||||
utils.write_to_file(pxe_config_file_path, pxe_config)
|
utils.write_to_file(pxe_config_file_path, pxe_config)
|
||||||
|
|
||||||
if is_uefi_boot_mode:
|
if is_uefi_boot_mode and not CONF.pxe.ipxe_enabled:
|
||||||
_link_ip_address_pxe_configs(task, hex_form)
|
_link_ip_address_pxe_configs(task, hex_form)
|
||||||
else:
|
else:
|
||||||
_link_mac_pxe_configs(task)
|
_link_mac_pxe_configs(task)
|
||||||
@@ -257,7 +257,9 @@ def clean_up_pxe_config(task):
|
|||||||
"""
|
"""
|
||||||
LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)
|
LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)
|
||||||
|
|
||||||
if deploy_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
|
is_uefi_boot_mode = (deploy_utils.get_boot_mode_for_deploy(task.node) ==
|
||||||
|
'uefi')
|
||||||
|
if is_uefi_boot_mode and not CONF.pxe.ipxe_enabled:
|
||||||
api = dhcp_factory.DHCPFactory().provider
|
api = dhcp_factory.DHCPFactory().provider
|
||||||
ip_addresses = api.get_ip_addresses(task)
|
ip_addresses = api.get_ip_addresses(task)
|
||||||
if not ip_addresses:
|
if not ip_addresses:
|
||||||
@@ -297,6 +299,12 @@ def dhcp_options_for_instance(task):
|
|||||||
:param task: A TaskManager instance.
|
:param task: A TaskManager instance.
|
||||||
"""
|
"""
|
||||||
dhcp_opts = []
|
dhcp_opts = []
|
||||||
|
|
||||||
|
if deploy_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
|
||||||
|
boot_file = CONF.pxe.uefi_pxe_bootfile_name
|
||||||
|
else:
|
||||||
|
boot_file = CONF.pxe.pxe_bootfile_name
|
||||||
|
|
||||||
if CONF.pxe.ipxe_enabled:
|
if CONF.pxe.ipxe_enabled:
|
||||||
script_name = os.path.basename(CONF.pxe.ipxe_boot_script)
|
script_name = os.path.basename(CONF.pxe.ipxe_boot_script)
|
||||||
ipxe_script_url = '/'.join([CONF.deploy.http_url, script_name])
|
ipxe_script_url = '/'.join([CONF.deploy.http_url, script_name])
|
||||||
@@ -307,22 +315,17 @@ def dhcp_options_for_instance(task):
|
|||||||
# Neutron use dnsmasq as default DHCP agent, add extra config
|
# Neutron use dnsmasq as default DHCP agent, add extra config
|
||||||
# to neutron "dhcp-match=set:ipxe,175" and use below option
|
# to neutron "dhcp-match=set:ipxe,175" and use below option
|
||||||
dhcp_opts.append({'opt_name': 'tag:!ipxe,bootfile-name',
|
dhcp_opts.append({'opt_name': 'tag:!ipxe,bootfile-name',
|
||||||
'opt_value': CONF.pxe.pxe_bootfile_name})
|
'opt_value': boot_file})
|
||||||
dhcp_opts.append({'opt_name': 'tag:ipxe,bootfile-name',
|
dhcp_opts.append({'opt_name': 'tag:ipxe,bootfile-name',
|
||||||
'opt_value': ipxe_script_url})
|
'opt_value': ipxe_script_url})
|
||||||
else:
|
else:
|
||||||
# !175 == non-iPXE.
|
# !175 == non-iPXE.
|
||||||
# http://ipxe.org/howto/dhcpd#ipxe-specific_options
|
# http://ipxe.org/howto/dhcpd#ipxe-specific_options
|
||||||
dhcp_opts.append({'opt_name': '!175,bootfile-name',
|
dhcp_opts.append({'opt_name': '!175,bootfile-name',
|
||||||
'opt_value': CONF.pxe.pxe_bootfile_name})
|
'opt_value': boot_file})
|
||||||
dhcp_opts.append({'opt_name': 'bootfile-name',
|
dhcp_opts.append({'opt_name': 'bootfile-name',
|
||||||
'opt_value': ipxe_script_url})
|
'opt_value': ipxe_script_url})
|
||||||
else:
|
else:
|
||||||
if deploy_utils.get_boot_mode_for_deploy(task.node) == 'uefi':
|
|
||||||
boot_file = CONF.pxe.uefi_pxe_bootfile_name
|
|
||||||
else:
|
|
||||||
boot_file = CONF.pxe.pxe_bootfile_name
|
|
||||||
|
|
||||||
dhcp_opts.append({'opt_name': 'bootfile-name',
|
dhcp_opts.append({'opt_name': 'bootfile-name',
|
||||||
'opt_value': boot_file})
|
'opt_value': boot_file})
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ dhcp
|
|||||||
goto deploy
|
goto deploy
|
||||||
|
|
||||||
:deploy
|
:deploy
|
||||||
kernel {{ pxe_options.deployment_aki_path }} selinux=0 disk={{ pxe_options.disk }} iscsi_target_iqn={{ pxe_options.iscsi_target_iqn }} deployment_id={{ pxe_options.deployment_id }} deployment_key={{ pxe_options.deployment_key }} ironic_api_url={{ pxe_options.ironic_api_url }} troubleshoot=0 text {{ pxe_options.pxe_append_params|default("", true) }} boot_option={{ pxe_options.boot_option }} ip=${ip}:${next-server}:${gateway}:${netmask} BOOTIF=${mac} {% if pxe_options.root_device %}root_device={{ pxe_options.root_device }}{% endif %} ipa-api-url={{ pxe_options['ipa-api-url'] }} ipa-driver-name={{ pxe_options['ipa-driver-name'] }} coreos.configdrive=0
|
kernel {{ pxe_options.deployment_aki_path }} selinux=0 disk={{ pxe_options.disk }} iscsi_target_iqn={{ pxe_options.iscsi_target_iqn }} deployment_id={{ pxe_options.deployment_id }} deployment_key={{ pxe_options.deployment_key }} ironic_api_url={{ pxe_options.ironic_api_url }} troubleshoot=0 text {{ pxe_options.pxe_append_params|default("", true) }} boot_option={{ pxe_options.boot_option }} ip=${ip}:${next-server}:${gateway}:${netmask} BOOTIF=${mac} {% if pxe_options.root_device %}root_device={{ pxe_options.root_device }}{% endif %} ipa-api-url={{ pxe_options['ipa-api-url'] }} ipa-driver-name={{ pxe_options['ipa-driver-name'] }} boot_mode={{ pxe_options['boot_mode'] }} initrd=deploy_ramdisk coreos.configdrive=0
|
||||||
|
|
||||||
initrd {{ pxe_options.deployment_ari_path }}
|
initrd {{ pxe_options.deployment_ari_path }}
|
||||||
boot
|
boot
|
||||||
|
|||||||
@@ -404,14 +404,6 @@ class PXEBoot(base.BootInterface):
|
|||||||
raise exception.MissingParameterValue(_(
|
raise exception.MissingParameterValue(_(
|
||||||
"iPXE boot is enabled but no HTTP URL or HTTP "
|
"iPXE boot is enabled but no HTTP URL or HTTP "
|
||||||
"root was specified."))
|
"root was specified."))
|
||||||
# iPXE and UEFI should not be configured together.
|
|
||||||
if boot_mode == 'uefi':
|
|
||||||
LOG.error(_LE("UEFI boot mode is not supported with "
|
|
||||||
"iPXE boot enabled."))
|
|
||||||
raise exception.InvalidParameterValue(_(
|
|
||||||
"Conflict: iPXE is enabled, but cannot be used with node"
|
|
||||||
"%(node_uuid)s configured to use UEFI boot") %
|
|
||||||
{'node_uuid': node.uuid})
|
|
||||||
|
|
||||||
if boot_mode == 'uefi':
|
if boot_mode == 'uefi':
|
||||||
validate_boot_option_for_uefi(node)
|
validate_boot_option_for_uefi(node)
|
||||||
|
|||||||
@@ -84,6 +84,16 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
'ari_path': 'http://1.2.3.4:1234/ramdisk',
|
'ari_path': 'http://1.2.3.4:1234/ramdisk',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.ipxe_options_bios = {
|
||||||
|
'boot_mode': 'bios',
|
||||||
|
}
|
||||||
|
self.ipxe_options_bios.update(self.ipxe_options)
|
||||||
|
|
||||||
|
self.ipxe_options_uefi = {
|
||||||
|
'boot_mode': 'uefi',
|
||||||
|
}
|
||||||
|
self.ipxe_options_uefi.update(self.ipxe_options)
|
||||||
|
|
||||||
self.node = object_utils.create_test_node(self.context)
|
self.node = object_utils.create_test_node(self.context)
|
||||||
|
|
||||||
def test__build_pxe_config(self):
|
def test__build_pxe_config(self):
|
||||||
@@ -108,7 +118,7 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
|
|
||||||
self.assertEqual(six.text_type(expected_template), rendered_template)
|
self.assertEqual(six.text_type(expected_template), rendered_template)
|
||||||
|
|
||||||
def test__build_ipxe_config(self):
|
def test__build_ipxe_bios_config(self):
|
||||||
# NOTE(lucasagomes): iPXE is just an extension of the PXE driver,
|
# NOTE(lucasagomes): iPXE is just an extension of the PXE driver,
|
||||||
# it doesn't have it's own configuration option for template.
|
# it doesn't have it's own configuration option for template.
|
||||||
# More info:
|
# More info:
|
||||||
@@ -119,7 +129,7 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
)
|
)
|
||||||
self.config(http_url='http://1.2.3.4:1234', group='deploy')
|
self.config(http_url='http://1.2.3.4:1234', group='deploy')
|
||||||
rendered_template = pxe_utils._build_pxe_config(
|
rendered_template = pxe_utils._build_pxe_config(
|
||||||
self.ipxe_options, CONF.pxe.pxe_config_template,
|
self.ipxe_options_bios, CONF.pxe.pxe_config_template,
|
||||||
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
|
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
|
||||||
|
|
||||||
expected_template = open(
|
expected_template = open(
|
||||||
@@ -127,6 +137,26 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
|
|
||||||
self.assertEqual(six.text_type(expected_template), rendered_template)
|
self.assertEqual(six.text_type(expected_template), rendered_template)
|
||||||
|
|
||||||
|
def test__build_ipxe_uefi_config(self):
|
||||||
|
# NOTE(lucasagomes): iPXE is just an extension of the PXE driver,
|
||||||
|
# it doesn't have it's own configuration option for template.
|
||||||
|
# More info:
|
||||||
|
# http://docs.openstack.org/developer/ironic/deploy/install-guide.html
|
||||||
|
self.config(
|
||||||
|
pxe_config_template='ironic/drivers/modules/ipxe_config.template',
|
||||||
|
group='pxe'
|
||||||
|
)
|
||||||
|
self.config(http_url='http://1.2.3.4:1234', group='deploy')
|
||||||
|
rendered_template = pxe_utils._build_pxe_config(
|
||||||
|
self.ipxe_options_uefi, CONF.pxe.pxe_config_template,
|
||||||
|
'{{ ROOT }}', '{{ DISK_IDENTIFIER }}')
|
||||||
|
|
||||||
|
expected_template = open(
|
||||||
|
'ironic/tests/unit/drivers/'
|
||||||
|
'ipxe_uefi_config.template').read().rstrip()
|
||||||
|
|
||||||
|
self.assertEqual(six.text_type(expected_template), rendered_template)
|
||||||
|
|
||||||
def test__build_elilo_config(self):
|
def test__build_elilo_config(self):
|
||||||
pxe_opts = self.pxe_options
|
pxe_opts = self.pxe_options
|
||||||
pxe_opts['boot_mode'] = 'uefi'
|
pxe_opts['boot_mode'] = 'uefi'
|
||||||
@@ -311,6 +341,36 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
|
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
|
||||||
write_mock.assert_called_with(pxe_cfg_file_path, self.pxe_options_uefi)
|
write_mock.assert_called_with(pxe_cfg_file_path, self.pxe_options_uefi)
|
||||||
|
|
||||||
|
@mock.patch('ironic.common.pxe_utils._link_mac_pxe_configs',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
|
||||||
|
@mock.patch('ironic.common.pxe_utils._build_pxe_config', autospec=True)
|
||||||
|
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
|
||||||
|
def test_create_pxe_config_uefi_ipxe(self, ensure_tree_mock, build_mock,
|
||||||
|
write_mock, link_mac_pxe_mock):
|
||||||
|
self.config(ipxe_enabled=True, group='pxe')
|
||||||
|
build_mock.return_value = self.ipxe_options_uefi
|
||||||
|
ipxe_template = "ironic/drivers/modules/ipxe_config.template"
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
|
task.node.properties['capabilities'] = 'boot_mode:uefi'
|
||||||
|
pxe_utils.create_pxe_config(task, self.ipxe_options_uefi,
|
||||||
|
ipxe_template)
|
||||||
|
|
||||||
|
ensure_calls = [
|
||||||
|
mock.call(os.path.join(CONF.deploy.http_root, self.node.uuid)),
|
||||||
|
mock.call(os.path.join(CONF.deploy.http_root, 'pxelinux.cfg'))
|
||||||
|
]
|
||||||
|
ensure_tree_mock.assert_has_calls(ensure_calls)
|
||||||
|
build_mock.assert_called_with(self.ipxe_options_uefi,
|
||||||
|
ipxe_template,
|
||||||
|
'{{ ROOT }}',
|
||||||
|
'{{ DISK_IDENTIFIER }}')
|
||||||
|
link_mac_pxe_mock.assert_called_once_with(task)
|
||||||
|
|
||||||
|
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
|
||||||
|
write_mock.assert_called_with(pxe_cfg_file_path,
|
||||||
|
self.ipxe_options_uefi)
|
||||||
|
|
||||||
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
|
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
|
||||||
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
|
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
|
||||||
def test_clean_up_pxe_config(self, unlink_mock, rmtree_mock):
|
def test_clean_up_pxe_config(self, unlink_mock, rmtree_mock):
|
||||||
@@ -422,9 +482,8 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
node_uuid,
|
node_uuid,
|
||||||
driver_info)
|
driver_info)
|
||||||
|
|
||||||
def test_dhcp_options_for_instance_ipxe(self):
|
def _dhcp_options_for_instance_ipxe(self, task, boot_file):
|
||||||
self.config(tftp_server='192.0.2.1', group='pxe')
|
self.config(tftp_server='192.0.2.1', group='pxe')
|
||||||
self.config(pxe_bootfile_name='fake-bootfile', group='pxe')
|
|
||||||
self.config(ipxe_enabled=True, group='pxe')
|
self.config(ipxe_enabled=True, group='pxe')
|
||||||
self.config(http_url='http://192.0.3.2:1234', group='deploy')
|
self.config(http_url='http://192.0.3.2:1234', group='deploy')
|
||||||
self.config(ipxe_boot_script='/test/boot.ipxe', group='pxe')
|
self.config(ipxe_boot_script='/test/boot.ipxe', group='pxe')
|
||||||
@@ -432,7 +491,7 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
self.config(dhcp_provider='isc', group='dhcp')
|
self.config(dhcp_provider='isc', group='dhcp')
|
||||||
expected_boot_script_url = 'http://192.0.3.2:1234/boot.ipxe'
|
expected_boot_script_url = 'http://192.0.3.2:1234/boot.ipxe'
|
||||||
expected_info = [{'opt_name': '!175,bootfile-name',
|
expected_info = [{'opt_name': '!175,bootfile-name',
|
||||||
'opt_value': 'fake-bootfile',
|
'opt_value': boot_file,
|
||||||
'ip_version': 4},
|
'ip_version': 4},
|
||||||
{'opt_name': 'server-ip-address',
|
{'opt_name': 'server-ip-address',
|
||||||
'opt_value': '192.0.2.1',
|
'opt_value': '192.0.2.1',
|
||||||
@@ -443,14 +502,14 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
{'opt_name': 'bootfile-name',
|
{'opt_name': 'bootfile-name',
|
||||||
'opt_value': expected_boot_script_url,
|
'opt_value': expected_boot_script_url,
|
||||||
'ip_version': 4}]
|
'ip_version': 4}]
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
|
||||||
self.assertItemsEqual(expected_info,
|
self.assertItemsEqual(expected_info,
|
||||||
pxe_utils.dhcp_options_for_instance(task))
|
pxe_utils.dhcp_options_for_instance(task))
|
||||||
|
|
||||||
self.config(dhcp_provider='neutron', group='dhcp')
|
self.config(dhcp_provider='neutron', group='dhcp')
|
||||||
expected_boot_script_url = 'http://192.0.3.2:1234/boot.ipxe'
|
expected_boot_script_url = 'http://192.0.3.2:1234/boot.ipxe'
|
||||||
expected_info = [{'opt_name': 'tag:!ipxe,bootfile-name',
|
expected_info = [{'opt_name': 'tag:!ipxe,bootfile-name',
|
||||||
'opt_value': 'fake-bootfile',
|
'opt_value': boot_file,
|
||||||
'ip_version': 4},
|
'ip_version': 4},
|
||||||
{'opt_name': 'server-ip-address',
|
{'opt_name': 'server-ip-address',
|
||||||
'opt_value': '192.0.2.1',
|
'opt_value': '192.0.2.1',
|
||||||
@@ -461,9 +520,22 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
{'opt_name': 'tag:ipxe,bootfile-name',
|
{'opt_name': 'tag:ipxe,bootfile-name',
|
||||||
'opt_value': expected_boot_script_url,
|
'opt_value': expected_boot_script_url,
|
||||||
'ip_version': 4}]
|
'ip_version': 4}]
|
||||||
|
|
||||||
|
self.assertItemsEqual(expected_info,
|
||||||
|
pxe_utils.dhcp_options_for_instance(task))
|
||||||
|
|
||||||
|
def test_dhcp_options_for_instance_ipxe_bios(self):
|
||||||
|
boot_file = 'fake-bootfile-bios'
|
||||||
|
self.config(pxe_bootfile_name=boot_file, group='pxe')
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
self.assertItemsEqual(expected_info,
|
self._dhcp_options_for_instance_ipxe(task, boot_file)
|
||||||
pxe_utils.dhcp_options_for_instance(task))
|
|
||||||
|
def test_dhcp_options_for_instance_ipxe_uefi(self):
|
||||||
|
boot_file = 'fake-bootfile-uefi'
|
||||||
|
self.config(uefi_pxe_bootfile_name=boot_file, group='pxe')
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
|
task.node.properties['capabilities'] = 'boot_mode:uefi'
|
||||||
|
self._dhcp_options_for_instance_ipxe(task, boot_file)
|
||||||
|
|
||||||
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
|
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
|
||||||
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
|
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
|
||||||
@@ -514,3 +586,24 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
unlink_mock.assert_has_calls(unlink_calls)
|
unlink_mock.assert_has_calls(unlink_calls)
|
||||||
rmtree_mock.assert_called_once_with(
|
rmtree_mock.assert_called_once_with(
|
||||||
os.path.join(CONF.pxe.tftp_root, self.node.uuid))
|
os.path.join(CONF.pxe.tftp_root, self.node.uuid))
|
||||||
|
|
||||||
|
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
|
||||||
|
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
|
||||||
|
def test_clean_up_ipxe_config_uefi(self, unlink_mock, rmtree_mock):
|
||||||
|
self.config(ipxe_enabled=True, group='pxe')
|
||||||
|
address = "aa:aa:aa:aa:aa:aa"
|
||||||
|
properties = {'capabilities': 'boot_mode:uefi'}
|
||||||
|
object_utils.create_test_port(self.context, node_id=self.node.id,
|
||||||
|
address=address)
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
|
task.node.properties = properties
|
||||||
|
pxe_utils.clean_up_pxe_config(task)
|
||||||
|
|
||||||
|
unlink_calls = [
|
||||||
|
mock.call('/httpboot/pxelinux.cfg/aa-aa-aa-aa-aa-aa'),
|
||||||
|
mock.call('/httpboot/pxelinux.cfg/aaaaaaaaaaaa')
|
||||||
|
]
|
||||||
|
unlink_mock.assert_has_calls(unlink_calls)
|
||||||
|
rmtree_mock.assert_called_once_with(
|
||||||
|
os.path.join(CONF.deploy.http_root, self.node.uuid))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ dhcp
|
|||||||
goto deploy
|
goto deploy
|
||||||
|
|
||||||
:deploy
|
:deploy
|
||||||
kernel http://1.2.3.4:1234/deploy_kernel selinux=0 disk=cciss/c0d0,sda,hda,vda iscsi_target_iqn=iqn-1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_id=1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_key=0123456789ABCDEFGHIJKLMNOPQRSTUV ironic_api_url=http://192.168.122.184:6385 troubleshoot=0 text test_param boot_option=netboot ip=${ip}:${next-server}:${gateway}:${netmask} BOOTIF=${mac} root_device=vendor=fake,size=123 ipa-api-url=http://192.168.122.184:6385 ipa-driver-name=pxe_ssh coreos.configdrive=0
|
kernel http://1.2.3.4:1234/deploy_kernel selinux=0 disk=cciss/c0d0,sda,hda,vda iscsi_target_iqn=iqn-1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_id=1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_key=0123456789ABCDEFGHIJKLMNOPQRSTUV ironic_api_url=http://192.168.122.184:6385 troubleshoot=0 text test_param boot_option=netboot ip=${ip}:${next-server}:${gateway}:${netmask} BOOTIF=${mac} root_device=vendor=fake,size=123 ipa-api-url=http://192.168.122.184:6385 ipa-driver-name=pxe_ssh boot_mode=bios initrd=deploy_ramdisk coreos.configdrive=0
|
||||||
|
|
||||||
initrd http://1.2.3.4:1234/deploy_ramdisk
|
initrd http://1.2.3.4:1234/deploy_ramdisk
|
||||||
boot
|
boot
|
||||||
|
|||||||
19
ironic/tests/unit/drivers/ipxe_uefi_config.template
Normal file
19
ironic/tests/unit/drivers/ipxe_uefi_config.template
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!ipxe
|
||||||
|
|
||||||
|
dhcp
|
||||||
|
|
||||||
|
goto deploy
|
||||||
|
|
||||||
|
:deploy
|
||||||
|
kernel http://1.2.3.4:1234/deploy_kernel selinux=0 disk=cciss/c0d0,sda,hda,vda iscsi_target_iqn=iqn-1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_id=1be26c0b-03f2-4d2e-ae87-c02d7f33c123 deployment_key=0123456789ABCDEFGHIJKLMNOPQRSTUV ironic_api_url=http://192.168.122.184:6385 troubleshoot=0 text test_param boot_option=netboot ip=${ip}:${next-server}:${gateway}:${netmask} BOOTIF=${mac} root_device=vendor=fake,size=123 ipa-api-url=http://192.168.122.184:6385 ipa-driver-name=pxe_ssh boot_mode=uefi initrd=deploy_ramdisk coreos.configdrive=0
|
||||||
|
|
||||||
|
initrd http://1.2.3.4:1234/deploy_ramdisk
|
||||||
|
boot
|
||||||
|
|
||||||
|
:boot_partition
|
||||||
|
kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param
|
||||||
|
initrd http://1.2.3.4:1234/ramdisk
|
||||||
|
boot
|
||||||
|
|
||||||
|
:boot_whole_disk
|
||||||
|
sanboot --no-describe
|
||||||
@@ -582,20 +582,6 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
self.assertRaises(exception.MissingParameterValue,
|
self.assertRaises(exception.MissingParameterValue,
|
||||||
task.driver.boot.validate, task)
|
task.driver.boot.validate, task)
|
||||||
|
|
||||||
@mock.patch.object(base_image_service.BaseImageService, '_show',
|
|
||||||
autospec=True)
|
|
||||||
def test_validate_fail_invalid_config_uefi_ipxe(self, mock_glance):
|
|
||||||
properties = {'capabilities': 'boot_mode:uefi,cap2:value2'}
|
|
||||||
mock_glance.return_value = {'properties': {'kernel_id': 'fake-kernel',
|
|
||||||
'ramdisk_id': 'fake-initr'}}
|
|
||||||
self.config(ipxe_enabled=True, group='pxe')
|
|
||||||
self.config(http_url='dummy_url', group='deploy')
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
|
||||||
shared=True) as task:
|
|
||||||
task.node.properties = properties
|
|
||||||
self.assertRaises(exception.InvalidParameterValue,
|
|
||||||
task.driver.boot.validate, task)
|
|
||||||
|
|
||||||
def test_validate_fail_invalid_config_uefi_whole_disk_image(self):
|
def test_validate_fail_invalid_config_uefi_whole_disk_image(self):
|
||||||
properties = {'capabilities': 'boot_mode:uefi,boot_option:netboot'}
|
properties = {'capabilities': 'boot_mode:uefi,boot_option:netboot'}
|
||||||
instance_info = {"boot_option": "netboot"}
|
instance_info = {"boot_option": "netboot"}
|
||||||
|
|||||||
3
releasenotes/notes/ipxe-and-uefi-7722bd5db71df02c.yaml
Normal file
3
releasenotes/notes/ipxe-and-uefi-7722bd5db71df02c.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Adds support for using iPXE in UEFI mode.
|
||||||
Reference in New Issue
Block a user