summaryrefslogtreecommitdiff
path: root/ironic/drivers
diff options
context:
space:
mode:
authorFaizan Barmawer <faizan.barmawer@gmail.com>2014-08-14 16:55:44 -0400
committerJim Rollenhagen <jim@jimrollenhagen.com>2014-09-03 07:52:42 -0700
commit1718a5f32ead9f2f10e2045a3080a3c4074236d1 (patch)
tree588eb140b0d7c8f3c78e60b4e3a6dbb87ecb992a /ironic/drivers
parentb56db42aa39e855e558a52eb71e656ea14380f8a (diff)
downloadironic-1718a5f32ead9f2f10e2045a3080a3c4074236d1.tar.gz
Add UEFI based deployment support in Ironic
Most of the new hardware comes with UEFI boot mode, which has several technical advantages over the traditional BIOS system. Operator inform the boot mode to ironic using the "capabilities" property of the node. The operator may add a new capability "boot_mode=uefi" or "boot_mode=bios" in "capabilities" within "properties" of the node. Add new pxe config options: - "uefi_pxe_bootfile_name": specify the efi bootloader to be used. - "uefi_pxe_config_template": specify the respective efi bootloader config template. As of now only elilo.efi bootloader is supported. elilo.efi bootloader requires the configuration file to be named after the ip-address assigned by the DHCP server. Implements: blueprint uefi-boot-for-ironic Co-Authored-By: Jim Rollenhagen <jim@jimrollenhagen.com> Change-Id: I0ad399b2207d7c66f6887e56470ba553b3c87b53
Diffstat (limited to 'ironic/drivers')
-rw-r--r--ironic/drivers/modules/agent.py2
-rw-r--r--ironic/drivers/modules/deploy_utils.py14
-rw-r--r--ironic/drivers/modules/elilo_efi_pxe_config.template11
-rw-r--r--ironic/drivers/modules/pxe.py55
4 files changed, 72 insertions, 10 deletions
diff --git a/ironic/drivers/modules/agent.py b/ironic/drivers/modules/agent.py
index ba60a3d57..08cfdfd80 100644
--- a/ironic/drivers/modules/agent.py
+++ b/ironic/drivers/modules/agent.py
@@ -217,7 +217,7 @@ class AgentDeploy(base.DeployInterface):
:param task: a TaskManager instance.
:returns: status of the deploy. One of ironic.common.states.
"""
- dhcp_opts = pxe_utils.dhcp_options_for_instance()
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
provider = dhcp_factory.DHCPFactory(token=task.context.auth_token)
provider.update_dhcp(task, dhcp_opts)
manager_utils.node_set_boot_device(task, 'pxe', persistent=True)
diff --git a/ironic/drivers/modules/deploy_utils.py b/ironic/drivers/modules/deploy_utils.py
index 898d21110..7e84f885c 100644
--- a/ironic/drivers/modules/deploy_utils.py
+++ b/ironic/drivers/modules/deploy_utils.py
@@ -160,15 +160,21 @@ def block_uuid(dev):
return out.strip()
-def switch_pxe_config(path, root_uuid):
+def switch_pxe_config(path, root_uuid, boot_mode):
"""Switch a pxe config from deployment mode to service mode."""
with open(path) as f:
lines = f.readlines()
root = 'UUID=%s' % root_uuid
- pxe_cmd = 'goto' if CONF.pxe.ipxe_enabled else 'default'
rre = re.compile(r'\{\{ ROOT \}\}')
- dre = re.compile('^%s .*$' % pxe_cmd)
- boot_line = '%s boot' % pxe_cmd
+
+ if boot_mode == 'uefi':
+ dre = re.compile('^default=.*$')
+ boot_line = 'default=boot'
+ else:
+ pxe_cmd = 'goto' if CONF.pxe.ipxe_enabled else 'default'
+ dre = re.compile('^%s .*$' % pxe_cmd)
+ boot_line = '%s boot' % pxe_cmd
+
with open(path, 'w') as f:
for line in lines:
line = rre.sub(root, line)
diff --git a/ironic/drivers/modules/elilo_efi_pxe_config.template b/ironic/drivers/modules/elilo_efi_pxe_config.template
new file mode 100644
index 000000000..b4c78d46d
--- /dev/null
+++ b/ironic/drivers/modules/elilo_efi_pxe_config.template
@@ -0,0 +1,11 @@
+default=deploy
+
+image={{pxe_options.deployment_aki_path}}
+ label=deploy
+ initrd={{pxe_options.deployment_ari_path}}
+ append="rootfstype=ramfs 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) }} ip=%I:{{pxe_options.tftp_server}}:%G:%M:%H::on"
+
+image={{pxe_options.aki_path}}
+ label=boot
+ initrd={{pxe_options.ari_path}}
+ append="root={{ ROOT }} ro text {{ pxe_options.pxe_append_params|default("", true) }} ip=%I:{{pxe_options.tftp_server}}:%G:%M:%H::on"
diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py
index 60501a2c5..73eff77df 100644
--- a/ironic/drivers/modules/pxe.py
+++ b/ironic/drivers/modules/pxe.py
@@ -41,11 +41,19 @@ from ironic.openstack.common import fileutils
from ironic.openstack.common import log as logging
+_LE = i18n._LE
+_LW = i18n._LW
+
pxe_opts = [
cfg.StrOpt('pxe_config_template',
default=paths.basedir_def(
'drivers/modules/pxe_config.template'),
help='Template file for PXE configuration.'),
+ cfg.StrOpt('uefi_pxe_config_template',
+ default=paths.basedir_def(
+ 'drivers/modules/elilo_efi_pxe_config.template'),
+ help='Template file for PXE configuration for UEFI boot'
+ ' loader.'),
cfg.StrOpt('tftp_server',
default='$my_ip',
help='IP address of Ironic compute node\'s tftp server.'),
@@ -60,6 +68,9 @@ pxe_opts = [
cfg.StrOpt('pxe_bootfile_name',
default='pxelinux.0',
help='Bootfile DHCP parameter.'),
+ cfg.StrOpt('uefi_pxe_bootfile_name',
+ default='elilo.efi',
+ help='Bootfile DHCP parameter for UEFI boot mode.'),
cfg.StrOpt('http_url',
help='Ironic compute node\'s HTTP server URL. '
'Example: http://192.1.2.3:8080'),
@@ -168,6 +179,7 @@ def _build_pxe_config_options(node, pxe_info, ctx):
'aki_path': kernel,
'ari_path': ramdisk,
'pxe_append_params': CONF.pxe.pxe_append_params,
+ 'tftp_server': CONF.pxe.tftp_server
}
deploy_ramdisk_options = iscsi_deploy.build_deploy_ramdisk_options(node,
@@ -265,11 +277,22 @@ class PXEDeploy(base.DeployInterface):
:raises: InvalidParameterValue.
:raises: MissingParameterValue
"""
+ # Check the boot_mode capability parameter value.
+ pxe_utils.validate_boot_mode_capability(task.node)
+
if CONF.pxe.ipxe_enabled:
if not CONF.pxe.http_url or not CONF.pxe.http_root:
raise exception.MissingParameterValue(_(
"iPXE boot is enabled but no HTTP URL or HTTP "
"root was specified."))
+ # iPXE and UEFI should not be configured together.
+ if pxe_utils.get_node_capability(task.node, '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': task.node.uuid})
d_info = _parse_deploy_info(task.node)
@@ -299,10 +322,25 @@ class PXEDeploy(base.DeployInterface):
# TODO(yuriyz): more secure way needed for pass auth token
# to deploy ramdisk
_create_token_file(task)
- dhcp_opts = pxe_utils.dhcp_options_for_instance()
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
provider = dhcp_factory.DHCPFactory(token=task.context.auth_token)
provider.update_dhcp(task, dhcp_opts)
- manager_utils.node_set_boot_device(task, 'pxe', persistent=True)
+
+ # NOTE(faizan): Under UEFI boot mode, setting of boot device may differ
+ # between different machines. IPMI does not work for setting boot
+ # devices in UEFI mode for certain machines.
+ # Expected IPMI failure for uefi boot mode. Logging a message to
+ # set the boot device manually and continue with deploy.
+ try:
+ manager_utils.node_set_boot_device(task, 'pxe', persistent=True)
+ except exception.IPMIFailure:
+ if pxe_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
+ LOG.warning(_LW("ipmitool is unable to set boot device while "
+ "the node is in UEFI boot mode."
+ "Please set the boot device manually."))
+ else:
+ raise
+
manager_utils.node_power_action(task, states.REBOOT)
return states.DEPLOYWAIT
@@ -338,8 +376,14 @@ class PXEDeploy(base.DeployInterface):
pxe_info = _get_image_info(task.node, task.context)
pxe_options = _build_pxe_config_options(task.node, pxe_info,
task.context)
+
+ if pxe_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
+ pxe_config_template = CONF.pxe.uefi_pxe_config_template
+ else:
+ pxe_config_template = CONF.pxe.pxe_config_template
+
pxe_utils.create_pxe_config(task, pxe_options,
- CONF.pxe.pxe_config_template)
+ pxe_config_template)
_cache_ramdisk_kernel(task.context, task.node, pxe_info)
def clean_up(self, task):
@@ -364,7 +408,7 @@ class PXEDeploy(base.DeployInterface):
_destroy_token_file(node)
def take_over(self, task):
- dhcp_opts = pxe_utils.dhcp_options_for_instance()
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
provider = dhcp_factory.DHCPFactory(token=task.context.auth_token)
provider.update_dhcp(task, dhcp_opts)
@@ -420,7 +464,8 @@ class VendorPassthru(base.VendorInterface):
try:
pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
- deploy_utils.switch_pxe_config(pxe_config_path, root_uuid)
+ deploy_utils.switch_pxe_config(pxe_config_path, root_uuid,
+ pxe_utils.get_node_capability(node, 'boot_mode'))
deploy_utils.notify_deploy_complete(kwargs['address'])