From e3d66818fac8608a00629eb9b1c17e95aab4496d Mon Sep 17 00:00:00 2001
From: Ilya Etingof <etingof@gmail.com>
Date: Fri, 18 May 2018 11:50:15 +0200
Subject: [PATCH] Move boot-related code to boot_mode_utils.py

This change collects boot-relalated functions into
the `boot_mode_utils.py` module to improve code clarity.

Change-Id: I1a2225d503deb382ba6021a6073c81cd03ca3175
Story: 1734131
Task: 10640
---
 ironic/common/pxe_utils.py                    |   5 +-
 ironic/drivers/modules/agent.py               |   5 +-
 ironic/drivers/modules/boot_mode_utils.py     | 162 +++++++++++++++-
 ironic/drivers/modules/deploy_utils.py        | 175 ++----------------
 ironic/drivers/modules/ilo/boot.py            |   9 +-
 ironic/drivers/modules/ilo/common.py          |   3 +-
 ironic/drivers/modules/ipmitool.py            |   4 +-
 ironic/drivers/modules/irmc/boot.py           |   3 +-
 ironic/drivers/modules/iscsi_deploy.py        |   3 +-
 ironic/drivers/modules/pxe.py                 |   6 +-
 .../unit/drivers/modules/ilo/test_boot.py     |  13 +-
 .../unit/drivers/modules/irmc/test_boot.py    |  13 +-
 .../tests/unit/drivers/modules/test_agent.py  |   7 +-
 .../unit/drivers/modules/test_deploy_utils.py |  11 +-
 .../unit/drivers/modules/test_ipmitool.py     |   6 +-
 15 files changed, 224 insertions(+), 201 deletions(-)

diff --git a/ironic/common/pxe_utils.py b/ironic/common/pxe_utils.py
index 07529865f9..34f79ada84 100644
--- a/ironic/common/pxe_utils.py
+++ b/ironic/common/pxe_utils.py
@@ -25,6 +25,7 @@ from ironic.common import dhcp_factory
 from ironic.common import exception
 from ironic.common.i18n import _
 from ironic.common import utils
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 
 CONF = cfg.CONF
@@ -227,7 +228,7 @@ def create_pxe_config(task, pxe_options, template=None):
     _ensure_config_dirs_exist(task.node.uuid)
 
     pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
-    is_uefi_boot_mode = (deploy_utils.get_boot_mode_for_deploy(task.node)
+    is_uefi_boot_mode = (boot_mode_utils.get_boot_mode_for_deploy(task.node)
                          == 'uefi')
 
     # grub bootloader panics with '{}' around any of its tags in its
@@ -287,7 +288,7 @@ def clean_up_pxe_config(task):
     """
     LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)
 
-    is_uefi_boot_mode = (deploy_utils.get_boot_mode_for_deploy(task.node)
+    is_uefi_boot_mode = (boot_mode_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
diff --git a/ironic/drivers/modules/agent.py b/ironic/drivers/modules/agent.py
index f78c744d71..c8c861f8b9 100644
--- a/ironic/drivers/modules/agent.py
+++ b/ironic/drivers/modules/agent.py
@@ -32,6 +32,7 @@ from ironic.conductor import utils as manager_utils
 from ironic.conf import CONF
 from ironic.drivers import base
 from ironic.drivers.modules import agent_base_vendor
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 
 
@@ -215,7 +216,7 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin):
             for label in PARTITION_IMAGE_LABELS:
                 image_info[label] = node.instance_info.get(label)
             boot_option = deploy_utils.get_boot_option(node)
-            boot_mode = deploy_utils.get_boot_mode_for_deploy(node)
+            boot_mode = boot_mode_utils.get_boot_mode_for_deploy(node)
             if boot_mode:
                 image_info['deploy_boot_mode'] = boot_mode
             else:
@@ -310,7 +311,7 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin):
 
         efi_sys_uuid = None
         if not iwdi:
-            if deploy_utils.get_boot_mode_for_deploy(node) == 'uefi':
+            if boot_mode_utils.get_boot_mode_for_deploy(node) == 'uefi':
                 efi_sys_uuid = (
                     self._get_uuid_from_result(task,
                                                'efi_system_partition_uuid'))
diff --git a/ironic/drivers/modules/boot_mode_utils.py b/ironic/drivers/modules/boot_mode_utils.py
index acddf898ec..aab53f63e0 100644
--- a/ironic/drivers/modules/boot_mode_utils.py
+++ b/ironic/drivers/modules/boot_mode_utils.py
@@ -14,12 +14,14 @@
 #    under the License.
 
 from oslo_log import log as logging
+from oslo_serialization import jsonutils
+import six
 
 from ironic.common import exception
 from ironic.common.i18n import _
 from ironic.conductor import utils as manager_utils
 from ironic.conf import CONF
-from ironic.drivers.modules import deploy_utils
+from ironic.drivers import utils as driver_utils
 
 LOG = logging.getLogger(__name__)
 
@@ -98,7 +100,7 @@ def sync_boot_mode(task):
         LOG.debug("Cannot determine node %(uuid)s boot mode: %(error)s",
                   {'uuid': node.uuid, 'error': ex})
 
-    ironic_boot_mode = deploy_utils.get_boot_mode_for_deploy(node)
+    ironic_boot_mode = get_boot_mode_for_deploy(node)
 
     # NOTE(etingof): the outcome of the branching that follows is that
     # the new boot mode may be set in 'driver_internal_info/deploy_boot_mode'
@@ -146,3 +148,159 @@ def sync_boot_mode(task):
         # underlying hardware type does not support setting boot mode as
         # it seems to be a hopeless misconfiguration
         _set_boot_mode_on_bm(task, ironic_boot_mode, fail_if_unsupported=True)
+
+
+def parse_instance_info_capabilities(node):
+    """Parse the instance_info capabilities.
+
+    One way of having these capabilities set is via Nova, where the
+    capabilities are defined in the Flavor extra_spec and passed to
+    Ironic by the Nova Ironic driver.
+
+    NOTE: Although our API fully supports JSON fields, to maintain the
+    backward compatibility with Juno the Nova Ironic driver is sending
+    it as a string.
+
+    :param node: a single Node.
+    :raises: InvalidParameterValue if the capabilities string is not a
+             dictionary or is malformed.
+    :returns: A dictionary with the capabilities if found, otherwise an
+              empty dictionary.
+    """
+
+    def parse_error():
+        error_msg = (_('Error parsing capabilities from Node %s instance_info '
+                       'field. A dictionary or a "jsonified" dictionary is '
+                       'expected.') % node.uuid)
+        raise exception.InvalidParameterValue(error_msg)
+
+    capabilities = node.instance_info.get('capabilities', {})
+    if isinstance(capabilities, six.string_types):
+        try:
+            capabilities = jsonutils.loads(capabilities)
+        except (ValueError, TypeError):
+            parse_error()
+
+    if not isinstance(capabilities, dict):
+        parse_error()
+
+    return capabilities
+
+
+def is_secure_boot_requested(node):
+    """Returns True if secure_boot is requested for deploy.
+
+    This method checks node property for secure_boot and returns True
+    if it is requested.
+
+    :param node: a single Node.
+    :raises: InvalidParameterValue if the capabilities string is not a
+             dictionary or is malformed.
+    :returns: True if secure_boot is requested.
+    """
+
+    capabilities = parse_instance_info_capabilities(node)
+    sec_boot = capabilities.get('secure_boot', 'false').lower()
+
+    return sec_boot == 'true'
+
+
+def is_trusted_boot_requested(node):
+    """Returns True if trusted_boot is requested for deploy.
+
+    This method checks instance property for trusted_boot and returns True
+    if it is requested.
+
+    :param node: a single Node.
+    :raises: InvalidParameterValue if the capabilities string is not a
+             dictionary or is malformed.
+    :returns: True if trusted_boot is requested.
+    """
+
+    capabilities = parse_instance_info_capabilities(node)
+    trusted_boot = capabilities.get('trusted_boot', 'false').lower()
+
+    return trusted_boot == 'true'
+
+
+def get_boot_mode_for_deploy(node):
+    """Returns the boot mode that would be used for deploy.
+
+    This method returns boot mode to be used for deploy.
+    It returns 'uefi' if 'secure_boot' is set to 'true' or returns 'bios' if
+    'trusted_boot' is set to 'true' in 'instance_info/capabilities' of node.
+    Otherwise it returns value of 'boot_mode' in 'properties/capabilities'
+    of node if set. If that is not set, it returns boot mode in
+    'internal_driver_info/deploy_boot_mode' for the node.
+    If that is not set, it returns boot mode in
+    'instance_info/deploy_boot_mode' for the node.
+    It would return None if boot mode is present neither in 'capabilities' of
+    node 'properties' nor in node's 'internal_driver_info' nor in node's
+    'instance_info' (which could also be None).
+
+    :param node: an ironic node object.
+    :returns: 'bios', 'uefi' or None
+    :raises: InvalidParameterValue, if the node boot mode disagrees with
+        the boot mode set to node properties/capabilities
+    """
+
+    if is_secure_boot_requested(node):
+        LOG.debug('Deploy boot mode is uefi for %s.', node.uuid)
+        return 'uefi'
+
+    if is_trusted_boot_requested(node):
+        # TODO(lintan) Trusted boot also supports uefi, but at the moment,
+        # it should only boot with bios.
+        LOG.debug('Deploy boot mode is bios for %s.', node.uuid)
+        return 'bios'
+
+    # NOTE(etingof):
+    # The search for a boot mode should be in the priority order:
+    #
+    # 1) instance_info
+    # 2) properties.capabilities
+    # 3) driver_internal_info
+    #
+    # Because:
+    #
+    # (1) can be deleted before teardown
+    # (3) will never be touched if node properties/capabilities
+    #     are still present.
+    # (2) becomes operational default as the last resort
+
+    instance_info = node.instance_info
+
+    cap_boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
+
+    boot_mode = instance_info.get('deploy_boot_mode')
+    if boot_mode is None:
+        boot_mode = cap_boot_mode
+        if cap_boot_mode is None:
+            driver_internal_info = node.driver_internal_info
+            boot_mode = driver_internal_info.get('deploy_boot_mode')
+
+    if not boot_mode:
+        return
+
+    boot_mode = boot_mode.lower()
+
+    # NOTE(etingof):
+    # Make sure that the ultimate boot_mode agrees with the one set to
+    # node properties/capabilities. This locks down node to use only
+    # boot mode specified in properties/capabilities.
+    # TODO(etingof): this logic will have to go away when we switch to traits
+    if cap_boot_mode:
+        cap_boot_mode = cap_boot_mode.lower()
+        if cap_boot_mode != boot_mode:
+            msg = (_("Node %(uuid)s boot mode %(boot_mode)s violates "
+                     "node properties/capabilities %(caps)s") %
+                   {'uuid': node.uuid,
+                    'boot_mode': boot_mode,
+                    'caps': cap_boot_mode})
+            LOG.error(msg)
+            raise exception.InvalidParameterValue(msg)
+
+    LOG.debug('Deploy boot mode is %(boot_mode)s for %(node)s.',
+              {'boot_mode': boot_mode, 'node': node.uuid})
+
+    return boot_mode
diff --git a/ironic/drivers/modules/deploy_utils.py b/ironic/drivers/modules/deploy_utils.py
index 911d73fa07..a4315f188c 100644
--- a/ironic/drivers/modules/deploy_utils.py
+++ b/ironic/drivers/modules/deploy_utils.py
@@ -24,7 +24,6 @@ from ironic_lib import disk_utils
 from ironic_lib import metrics_utils
 from oslo_concurrency import processutils
 from oslo_log import log as logging
-from oslo_serialization import jsonutils
 from oslo_utils import excutils
 from oslo_utils import netutils
 from oslo_utils import strutils
@@ -40,15 +39,16 @@ from ironic.common import utils
 from ironic.conductor import utils as manager_utils
 from ironic.conf import CONF
 from ironic.drivers.modules import agent_client
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import image_cache
 from ironic.drivers import utils as driver_utils
 from ironic import objects
 
-
 # TODO(Faizan): Move this logic to common/utils.py and deprecate
 # rootwrap_config.
 # This is required to set the default value of ironic_lib option
 # only if rootwrap_config does not contain the default value.
+
 if CONF.rootwrap_config != '/etc/ironic/rootwrap.conf':
     root_helper = 'sudo ironic-rootwrap %s' % CONF.rootwrap_config
     CONF.set_default('root_helper', root_helper, 'ironic_lib')
@@ -560,43 +560,6 @@ def get_single_nic_with_vif_port_id(task):
             return port.address
 
 
-def parse_instance_info_capabilities(node):
-    """Parse the instance_info capabilities.
-
-    One way of having these capabilities set is via Nova, where the
-    capabilities are defined in the Flavor extra_spec and passed to
-    Ironic by the Nova Ironic driver.
-
-    NOTE: Although our API fully supports JSON fields, to maintain the
-    backward compatibility with Juno the Nova Ironic driver is sending
-    it as a string.
-
-    :param node: a single Node.
-    :raises: InvalidParameterValue if the capabilities string is not a
-             dictionary or is malformed.
-    :returns: A dictionary with the capabilities if found, otherwise an
-              empty dictionary.
-    """
-
-    def parse_error():
-        error_msg = (_('Error parsing capabilities from Node %s instance_info '
-                       'field. A dictionary or a "jsonified" dictionary is '
-                       'expected.') % node.uuid)
-        raise exception.InvalidParameterValue(error_msg)
-
-    capabilities = node.instance_info.get('capabilities', {})
-    if isinstance(capabilities, six.string_types):
-        try:
-            capabilities = jsonutils.loads(capabilities)
-        except (ValueError, TypeError):
-            parse_error()
-
-    if not isinstance(capabilities, dict):
-        parse_error()
-
-    return capabilities
-
-
 def agent_get_clean_steps(task, interface=None, override_priorities=None):
     """Get the list of cached clean steps from the agent.
 
@@ -713,42 +676,6 @@ def try_set_boot_device(task, device, persistent=True):
                             "the boot device manually.", task.node.uuid)
 
 
-def is_secure_boot_requested(node):
-    """Returns True if secure_boot is requested for deploy.
-
-    This method checks node property for secure_boot and returns True
-    if it is requested.
-
-    :param node: a single Node.
-    :raises: InvalidParameterValue if the capabilities string is not a
-             dictionary or is malformed.
-    :returns: True if secure_boot is requested.
-    """
-
-    capabilities = parse_instance_info_capabilities(node)
-    sec_boot = capabilities.get('secure_boot', 'false').lower()
-
-    return sec_boot == 'true'
-
-
-def is_trusted_boot_requested(node):
-    """Returns True if trusted_boot is requested for deploy.
-
-    This method checks instance property for trusted_boot and returns True
-    if it is requested.
-
-    :param node: a single Node.
-    :raises: InvalidParameterValue if the capabilities string is not a
-             dictionary or is malformed.
-    :returns: True if trusted_boot is requested.
-    """
-
-    capabilities = parse_instance_info_capabilities(node)
-    trusted_boot = capabilities.get('trusted_boot', 'false').lower()
-
-    return trusted_boot == 'true'
-
-
 def get_disk_label(node):
     """Return the disk label requested for deploy, if any.
 
@@ -757,93 +684,10 @@ def get_disk_label(node):
              dictionary or is malformed.
     :returns: the disk label or None if no disk label was specified.
     """
-    capabilities = parse_instance_info_capabilities(node)
+    capabilities = boot_mode_utils.parse_instance_info_capabilities(node)
     return capabilities.get('disk_label')
 
 
-def get_boot_mode_for_deploy(node):
-    """Returns the boot mode that would be used for deploy.
-
-    This method returns boot mode to be used for deploy.
-    It returns 'uefi' if 'secure_boot' is set to 'true' or returns 'bios' if
-    'trusted_boot' is set to 'true' in 'instance_info/capabilities' of node.
-    Otherwise it returns value of 'boot_mode' in 'properties/capabilities'
-    of node if set. If that is not set, it returns boot mode in
-    'driver_internal_info/deploy_boot_mode' for the node.
-    If that is not set, it returns boot mode in
-    'instance_info/deploy_boot_mode' for the node.
-    It would return None if boot mode is present neither in 'capabilities' of
-    node 'properties' nor in node's 'driver_internal_info' nor in node's
-    'instance_info' (which could also be None).
-
-    :param node: an ironic node object.
-    :returns: 'bios', 'uefi' or None
-    :raises: InvalidParameterValue, if the node boot mode disagrees with
-        the boot mode set to node properties/capabilities
-    """
-
-    if is_secure_boot_requested(node):
-        LOG.debug('Deploy boot mode is uefi for %s.', node.uuid)
-        return 'uefi'
-
-    if is_trusted_boot_requested(node):
-        # TODO(lintan) Trusted boot also supports uefi, but at the moment,
-        # it should only boot with bios.
-        LOG.debug('Deploy boot mode is bios for %s.', node.uuid)
-        return 'bios'
-
-    # NOTE(etingof):
-    # The search for a boot mode should be in the priority order:
-    #
-    # 1) instance_info
-    # 2) properties.capabilities
-    # 3) driver_internal_info
-    #
-    # Because:
-    #
-    # (1) can be deleted before teardown
-    # (3) will never be touched if node properties/capabilities
-    #     are still present.
-    # (2) becomes operational default as the last resort
-
-    instance_info = node.instance_info
-
-    cap_boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
-
-    boot_mode = instance_info.get('deploy_boot_mode')
-    if boot_mode is None:
-        boot_mode = cap_boot_mode
-        if cap_boot_mode is None:
-            driver_internal_info = node.driver_internal_info
-            boot_mode = driver_internal_info.get('deploy_boot_mode')
-
-    if not boot_mode:
-        return
-
-    boot_mode = boot_mode.lower()
-
-    # NOTE(etingof):
-    # Make sure that the ultimate boot_mode agrees with the one set to
-    # node properties/capabilities. This locks down node to use only
-    # boot mode specified in properties/capabilities.
-    # TODO(etingof): this logic will have to go away when we switch to traits
-    if cap_boot_mode:
-        cap_boot_mode = cap_boot_mode.lower()
-        if cap_boot_mode != boot_mode:
-            msg = (_("Node %(uuid)s boot mode %(boot_mode)s violates "
-                     "node properties/capabilities %(caps)s") %
-                   {'uuid': node.uuid,
-                    'boot_mode': boot_mode,
-                    'caps': cap_boot_mode})
-            LOG.error(msg)
-            raise exception.InvalidParameterValue(msg)
-
-    LOG.debug('Deploy boot mode is %(boot_mode)s for %(node)s.',
-              {'boot_mode': boot_mode, 'node': node.uuid})
-
-    return boot_mode
-
-
 def get_pxe_boot_file(node):
     """Return the PXE boot file name requested for deploy.
 
@@ -916,7 +760,7 @@ def validate_capabilities(node):
                  'value': value, 'valid_values': ', '.join(valid_values)})
 
         # Validate capability_name in node's instance_info/['capabilities']
-        capabilities = parse_instance_info_capabilities(node)
+        capabilities = boot_mode_utils.parse_instance_info_capabilities(node)
         value = capabilities.get(capability_name)
 
         if value and (value not in valid_values):
@@ -988,7 +832,7 @@ def get_boot_option(node):
     :returns: A string representing the boot option type. Defaults to
         'netboot'.
     """
-    capabilities = parse_instance_info_capabilities(node)
+    capabilities = boot_mode_utils.parse_instance_info_capabilities(node)
     return capabilities.get('boot_option', get_default_boot_option()).lower()
 
 
@@ -1385,3 +1229,12 @@ def is_iscsi_boot(task):
         except exception.VolumeTargetNotFound:
             return False
     return False
+
+
+# NOTE(etingof): retain original location of these funcs for compatibility
+is_secure_boot_requested = boot_mode_utils.is_secure_boot_requested
+is_trusted_boot_requested = boot_mode_utils.is_trusted_boot_requested
+get_boot_mode_for_deploy = boot_mode_utils.get_boot_mode_for_deploy
+parse_instance_info_capabilities = (
+    boot_mode_utils.parse_instance_info_capabilities
+)
diff --git a/ironic/drivers/modules/ilo/boot.py b/ironic/drivers/modules/ilo/boot.py
index 9be7a06c8b..4ef880f478 100644
--- a/ironic/drivers/modules/ilo/boot.py
+++ b/ironic/drivers/modules/ilo/boot.py
@@ -35,6 +35,7 @@ from ironic.common import states
 from ironic.common import swift
 from ironic.conductor import utils as manager_utils
 from ironic.drivers import base
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 from ironic.drivers.modules.ilo import common as ilo_common
 from ironic.drivers.modules import pxe
@@ -184,7 +185,7 @@ def _get_boot_iso(task, root_uuid):
     # Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift
     # or web server and provide its name.
     deploy_iso_uuid = deploy_info['ilo_deploy_iso']
-    boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
+    boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
     boot_iso_object_name = _get_boot_iso_object_name(task.node)
     kernel_params = CONF.pxe.pxe_append_params
     with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as fileobj:
@@ -368,7 +369,7 @@ def prepare_node_for_deploy(task):
         # Need to update boot mode that will be used during deploy, if one is
         # not provided.
         # Since secure boot was disabled, we are in 'uefi' boot mode.
-        if deploy_utils.get_boot_mode_for_deploy(task.node) is None:
+        if boot_mode_utils.get_boot_mode_for_deploy(task.node) is None:
             instance_info = task.node.instance_info
             instance_info['deploy_boot_mode'] = 'uefi'
             task.node.instance_info = instance_info
@@ -503,7 +504,7 @@ class IloVirtualMediaBoot(base.BootInterface):
 
         ilo_common.cleanup_vmedia_boot(task)
 
-        boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
+        boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
 
         if deploy_utils.is_iscsi_boot(task):
             # It will set iSCSI info onto iLO
@@ -671,7 +672,7 @@ class IloPXEBoot(pxe.PXEBoot):
         # Need to enable secure boot, if being requested
         ilo_common.update_secure_boot_mode(task, True)
 
-        boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
+        boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
 
         if deploy_utils.is_iscsi_boot(task) and boot_mode == 'uefi':
             # Need to set 'ilo_uefi_iscsi_boot' param for clean up
diff --git a/ironic/drivers/modules/ilo/common.py b/ironic/drivers/modules/ilo/common.py
index bc78e189d7..a851cf2bb2 100644
--- a/ironic/drivers/modules/ilo/common.py
+++ b/ironic/drivers/modules/ilo/common.py
@@ -36,6 +36,7 @@ from ironic.common import swift
 from ironic.common import utils
 from ironic.conductor import utils as manager_utils
 from ironic.conf import CONF
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 
 ilo_client = importutils.try_import('proliantutils.ilo.client')
@@ -483,7 +484,7 @@ def update_boot_mode(task):
     """
 
     node = task.node
-    boot_mode = deploy_utils.get_boot_mode_for_deploy(node)
+    boot_mode = boot_mode_utils.get_boot_mode_for_deploy(node)
 
     # No boot mode found. Check if default_boot_mode is defined
     if not boot_mode and (CONF.ilo.default_boot_mode in ['bios', 'uefi']):
diff --git a/ironic/drivers/modules/ipmitool.py b/ironic/drivers/modules/ipmitool.py
index 51cea29ff3..5c50ef82ea 100644
--- a/ironic/drivers/modules/ipmitool.py
+++ b/ironic/drivers/modules/ipmitool.py
@@ -53,8 +53,8 @@ from ironic.conductor import task_manager
 from ironic.conductor import utils as cond_utils
 from ironic.conf import CONF
 from ironic.drivers import base
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import console_utils
-from ironic.drivers.modules import deploy_utils
 from ironic.drivers import utils as driver_utils
 
 
@@ -944,7 +944,7 @@ class IPMIManagement(base.ManagementInterface):
         # uefi mode, this will work with newer and older versions of the
         # ipmitool utility. Also see:
         # https://bugs.launchpad.net/ironic/+bug/1611306
-        boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
+        boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
         if persistent and boot_mode == 'uefi':
             raw_cmd = ('0x00 0x08 0x05 0xe0 %s 0x00 0x00 0x00' %
                        BOOT_DEVICE_HEXA_MAP[device])
diff --git a/ironic/drivers/modules/irmc/boot.py b/ironic/drivers/modules/irmc/boot.py
index 5350e8f0e6..4c385e9c9f 100644
--- a/ironic/drivers/modules/irmc/boot.py
+++ b/ironic/drivers/modules/irmc/boot.py
@@ -34,6 +34,7 @@ from ironic.common import states
 from ironic.conductor import utils as manager_utils
 from ironic.conf import CONF
 from ironic.drivers import base
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 from ironic.drivers.modules.irmc import common as irmc_common
 from ironic.drivers.modules.irmc import management as irmc_management
@@ -289,7 +290,7 @@ def _prepare_boot_iso(task, root_uuid):
         deploy_iso_filename = _get_iso_name(task.node, label='deploy')
         deploy_iso = ('file://' + os.path.join(
             CONF.irmc.remote_image_share_root, deploy_iso_filename))
-        boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
+        boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
         kernel_params = CONF.pxe.pxe_append_params
 
         boot_iso_filename = _get_iso_name(task.node, label='boot')
diff --git a/ironic/drivers/modules/iscsi_deploy.py b/ironic/drivers/modules/iscsi_deploy.py
index 392837022c..05fac5029b 100644
--- a/ironic/drivers/modules/iscsi_deploy.py
+++ b/ironic/drivers/modules/iscsi_deploy.py
@@ -33,6 +33,7 @@ from ironic.conductor import utils as manager_utils
 from ironic.conf import CONF
 from ironic.drivers import base
 from ironic.drivers.modules import agent_base_vendor
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 from ironic.drivers.modules import image_cache
 
@@ -349,7 +350,7 @@ def _get_boot_mode(node):
     :param node: A single Node.
     :returns: A string representing the boot mode type. Defaults to 'bios'.
     """
-    boot_mode = deploy_utils.get_boot_mode_for_deploy(node)
+    boot_mode = boot_mode_utils.get_boot_mode_for_deploy(node)
     if boot_mode:
         return boot_mode
     return "bios"
diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py
index f2310c64e2..6dc9478821 100644
--- a/ironic/drivers/modules/pxe.py
+++ b/ironic/drivers/modules/pxe.py
@@ -281,7 +281,7 @@ def _build_service_pxe_config(task, instance_image_info,
     iwdi = node.driver_internal_info.get('is_whole_disk_image')
     deploy_utils.switch_pxe_config(
         pxe_config_path, root_uuid_or_disk_id,
-        deploy_utils.get_boot_mode_for_deploy(node),
+        boot_mode_utils.get_boot_mode_for_deploy(node),
         iwdi, deploy_utils.is_trusted_boot_requested(node),
         deploy_utils.is_iscsi_boot(task))
 
@@ -354,7 +354,7 @@ def _get_volume_pxe_options(task):
 
 def validate_boot_parameters_for_trusted_boot(node):
     """Check if boot parameters are valid for trusted boot."""
-    boot_mode = deploy_utils.get_boot_mode_for_deploy(node)
+    boot_mode = boot_mode_utils.get_boot_mode_for_deploy(node)
     boot_option = deploy_utils.get_boot_option(node)
     is_whole_disk_image = node.driver_internal_info.get('is_whole_disk_image')
     # 'is_whole_disk_image' is not supported by trusted boot, because there is
@@ -614,7 +614,7 @@ class PXEBoot(base.BootInterface):
                     task, pxe_options, pxe_config_template)
             deploy_utils.switch_pxe_config(
                 pxe_config_path, None,
-                deploy_utils.get_boot_mode_for_deploy(node), False,
+                boot_mode_utils.get_boot_mode_for_deploy(node), False,
                 iscsi_boot=True)
             boot_device = boot_devices.PXE
 
diff --git a/ironic/tests/unit/drivers/modules/ilo/test_boot.py b/ironic/tests/unit/drivers/modules/ilo/test_boot.py
index 2eab8f044d..c94cf6c383 100644
--- a/ironic/tests/unit/drivers/modules/ilo/test_boot.py
+++ b/ironic/tests/unit/drivers/modules/ilo/test_boot.py
@@ -31,6 +31,7 @@ from ironic.common import states
 from ironic.common import swift
 from ironic.conductor import task_manager
 from ironic.conductor import utils as manager_utils
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 from ironic.drivers.modules.ilo import boot as ilo_boot
 from ironic.drivers.modules.ilo import common as ilo_common
@@ -141,7 +142,7 @@ class IloBootPrivateMethodsTestCase(db_base.DbTestCase):
             boot_iso_expected = u'glance://uui\u0111'
             self.assertEqual(boot_iso_expected, boot_iso_actual)
 
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy',
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
                        spec_set=True, autospec=True)
     @mock.patch.object(ilo_boot.LOG, 'error', spec_set=True, autospec=True)
     @mock.patch.object(images, 'get_image_properties', spec_set=True,
@@ -1056,7 +1057,7 @@ class IloVirtualMediaBootTestCase(db_base.DbTestCase):
                        autospec=True)
     @mock.patch.object(deploy_utils, 'is_iscsi_boot',
                        spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy',
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
                        spec_set=True, autospec=True)
     @mock.patch.object(ilo_management.IloManagement, 'set_iscsi_boot_target',
                        spec_set=True, autospec=True)
@@ -1093,7 +1094,7 @@ class IloVirtualMediaBootTestCase(db_base.DbTestCase):
                        autospec=True)
     @mock.patch.object(deploy_utils, 'is_iscsi_boot',
                        spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy',
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
                        spec_set=True, autospec=True)
     def test_prepare_instance_boot_from_volume_bios(
             self, get_boot_mode_mock,
@@ -1226,7 +1227,7 @@ class IloPXEBootTestCase(db_base.DbTestCase):
 
     @mock.patch.object(deploy_utils, 'is_iscsi_boot',
                        spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy',
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
                        spec_set=True, autospec=True)
     @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True,
                        autospec=True)
@@ -1252,7 +1253,7 @@ class IloPXEBootTestCase(db_base.DbTestCase):
 
     @mock.patch.object(deploy_utils, 'is_iscsi_boot',
                        spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy',
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
                        spec_set=True, autospec=True)
     @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True,
                        autospec=True)
@@ -1278,7 +1279,7 @@ class IloPXEBootTestCase(db_base.DbTestCase):
 
     @mock.patch.object(deploy_utils, 'is_iscsi_boot',
                        spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy',
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
                        spec_set=True, autospec=True)
     @mock.patch.object(ilo_management.IloManagement, 'set_iscsi_boot_target',
                        spec_set=True, autospec=True)
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_boot.py b/ironic/tests/unit/drivers/modules/irmc/test_boot.py
index 1382624828..9c10f671d7 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_boot.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_boot.py
@@ -34,6 +34,7 @@ from ironic.common import images
 from ironic.common import states
 from ironic.conductor import task_manager
 from ironic.conductor import utils as manager_utils
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 from ironic.drivers.modules.irmc import boot as irmc_boot
 from ironic.drivers.modules.irmc import common as irmc_common
@@ -391,8 +392,8 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
         self.assertEqual(expected, actual)
 
     @mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy', spec_set=True,
-                       autospec=True)
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
+                       spec_set=True, autospec=True)
     @mock.patch.object(images, 'get_image_properties', spec_set=True,
                        autospec=True)
     @mock.patch.object(images, 'fetch', spec_set=True,
@@ -419,8 +420,8 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
                              task.node.driver_internal_info['irmc_boot_iso'])
 
     @mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy', spec_set=True,
-                       autospec=True)
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
+                       spec_set=True, autospec=True)
     @mock.patch.object(images, 'get_image_properties', spec_set=True,
                        autospec=True)
     @mock.patch.object(images, 'fetch', spec_set=True,
@@ -460,8 +461,8 @@ class IRMCDeployPrivateMethodsTestCase(db_base.DbTestCase):
                              task.node.driver_internal_info['irmc_boot_iso'])
 
     @mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy', spec_set=True,
-                       autospec=True)
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
+                       spec_set=True, autospec=True)
     @mock.patch.object(images, 'get_image_properties', spec_set=True,
                        autospec=True)
     @mock.patch.object(images, 'fetch', spec_set=True,
diff --git a/ironic/tests/unit/drivers/modules/test_agent.py b/ironic/tests/unit/drivers/modules/test_agent.py
index 22db83147c..91b81527ce 100644
--- a/ironic/tests/unit/drivers/modules/test_agent.py
+++ b/ironic/tests/unit/drivers/modules/test_agent.py
@@ -29,6 +29,7 @@ from ironic.drivers import base as drivers_base
 from ironic.drivers.modules import agent
 from ironic.drivers.modules import agent_base_vendor
 from ironic.drivers.modules import agent_client
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils
 from ironic.drivers.modules import fake
 from ironic.drivers.modules.network import flat as flat_network
@@ -1034,7 +1035,8 @@ class TestAgentDeploy(db_base.DbTestCase):
             self.assertEqual(states.NOSTATE, task.node.target_provision_state)
 
     @mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy', autospec=True)
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
+                       autospec=True)
     @mock.patch.object(agent.AgentDeployMixin, '_get_uuid_from_result',
                        autospec=True)
     @mock.patch.object(manager_utils, 'node_power_action', autospec=True)
@@ -1119,7 +1121,8 @@ class TestAgentDeploy(db_base.DbTestCase):
             self.assertEqual(states.ACTIVE, task.node.target_provision_state)
 
     @mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True)
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy', autospec=True)
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy',
+                       autospec=True)
     @mock.patch.object(agent.AgentDeployMixin, '_get_uuid_from_result',
                        autospec=True)
     @mock.patch.object(manager_utils, 'node_power_action', autospec=True)
diff --git a/ironic/tests/unit/drivers/modules/test_deploy_utils.py b/ironic/tests/unit/drivers/modules/test_deploy_utils.py
index 12e5135cfd..8f3fbe26db 100644
--- a/ironic/tests/unit/drivers/modules/test_deploy_utils.py
+++ b/ironic/tests/unit/drivers/modules/test_deploy_utils.py
@@ -35,6 +35,7 @@ from ironic.common import utils as common_utils
 from ironic.conductor import task_manager
 from ironic.conductor import utils as manager_utils
 from ironic.drivers.modules import agent_client
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import deploy_utils as utils
 from ironic.drivers.modules import fake
 from ironic.drivers.modules import image_cache
@@ -1357,34 +1358,34 @@ class ParseInstanceInfoCapabilitiesTestCase(tests_base.TestCase):
         properties = {'capabilities': 'boot_mode:uefi,cap2:value2'}
         self.node.properties = properties
 
-        result = utils.get_boot_mode_for_deploy(self.node)
+        result = boot_mode_utils.get_boot_mode_for_deploy(self.node)
         self.assertEqual('uefi', result)
 
     def test_get_boot_mode_for_deploy_using_instance_info_cap(self):
         instance_info = {'capabilities': {'secure_boot': 'True'}}
         self.node.instance_info = instance_info
 
-        result = utils.get_boot_mode_for_deploy(self.node)
+        result = boot_mode_utils.get_boot_mode_for_deploy(self.node)
         self.assertEqual('uefi', result)
 
         instance_info = {'capabilities': {'trusted_boot': 'True'}}
         self.node.instance_info = instance_info
 
-        result = utils.get_boot_mode_for_deploy(self.node)
+        result = boot_mode_utils.get_boot_mode_for_deploy(self.node)
         self.assertEqual('bios', result)
 
         instance_info = {'capabilities': {'trusted_boot': 'True'},
                          'capabilities': {'secure_boot': 'True'}}
         self.node.instance_info = instance_info
 
-        result = utils.get_boot_mode_for_deploy(self.node)
+        result = boot_mode_utils.get_boot_mode_for_deploy(self.node)
         self.assertEqual('uefi', result)
 
     def test_get_boot_mode_for_deploy_using_instance_info(self):
         instance_info = {'deploy_boot_mode': 'bios'}
         self.node.instance_info = instance_info
 
-        result = utils.get_boot_mode_for_deploy(self.node)
+        result = boot_mode_utils.get_boot_mode_for_deploy(self.node)
         self.assertEqual('bios', result)
 
     def test_validate_boot_mode_capability(self):
diff --git a/ironic/tests/unit/drivers/modules/test_ipmitool.py b/ironic/tests/unit/drivers/modules/test_ipmitool.py
index b607bcd274..009f87c404 100644
--- a/ironic/tests/unit/drivers/modules/test_ipmitool.py
+++ b/ironic/tests/unit/drivers/modules/test_ipmitool.py
@@ -42,8 +42,8 @@ from ironic.common import states
 from ironic.common import utils
 from ironic.conductor import task_manager
 import ironic.conf
+from ironic.drivers.modules import boot_mode_utils
 from ironic.drivers.modules import console_utils
-from ironic.drivers.modules import deploy_utils
 from ironic.drivers.modules import ipmitool as ipmi
 from ironic.drivers import utils as driver_utils
 from ironic.tests import base
@@ -1974,7 +1974,7 @@ class IPMIToolDriverTestCase(Base):
                               self.management.set_boot_device,
                               task, boot_devices.PXE)
 
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy')
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy')
     @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
     def test_management_interface_set_boot_device_uefi(self, mock_exec,
                                                        mock_boot_mode):
@@ -1990,7 +1990,7 @@ class IPMIToolDriverTestCase(Base):
         ]
         mock_exec.assert_has_calls(mock_calls)
 
-    @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy')
+    @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy')
     @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
     def test_management_interface_set_boot_device_uefi_and_persistent(
             self, mock_exec, mock_boot_mode):