summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/deploy/cleaning.rst9
-rw-r--r--doc/source/deploy/install-guide.rst64
-rw-r--r--doc/source/deploy/upgrade-guide.rst36
-rw-r--r--doc/source/deploy/user-guide.rst4
-rw-r--r--doc/source/drivers/amt.rst11
-rw-r--r--doc/source/drivers/ilo.rst146
-rw-r--r--doc/source/index.rst1
-rw-r--r--etc/ironic/ironic.conf.sample5
-rw-r--r--ironic/api/middleware/parsable_error.py2
-rw-r--r--ironic/common/grub_conf.template10
-rw-r--r--ironic/common/pxe_utils.py33
-rw-r--r--ironic/conductor/manager.py6
-rw-r--r--ironic/conductor/rpcapi.py16
-rw-r--r--ironic/drivers/modules/agent.py45
-rw-r--r--ironic/drivers/modules/boot.ipxe2
-rw-r--r--ironic/drivers/modules/deploy_utils.py19
-rw-r--r--ironic/drivers/modules/ilo/common.py2
-rw-r--r--ironic/drivers/modules/ilo/deploy.py4
-rw-r--r--ironic/drivers/modules/ipmitool.py29
-rw-r--r--ironic/drivers/modules/iscsi_deploy.py11
-rw-r--r--ironic/tests/conductor/test_manager.py16
-rw-r--r--ironic/tests/conductor/test_rpcapi.py4
-rw-r--r--ironic/tests/drivers/test_agent.py69
-rw-r--r--ironic/tests/drivers/test_ipmitool.py10
-rw-r--r--ironic/tests/drivers/test_iscsi_deploy.py19
-rw-r--r--ironic/tests/drivers/test_seamicro.py3
-rw-r--r--ironic/tests/test_images.py11
-rw-r--r--ironic/tests/test_pxe_utils.py37
28 files changed, 489 insertions, 135 deletions
diff --git a/doc/source/deploy/cleaning.rst b/doc/source/deploy/cleaning.rst
index 8897bf46b..0f1a42eda 100644
--- a/doc/source/deploy/cleaning.rst
+++ b/doc/source/deploy/cleaning.rst
@@ -34,6 +34,7 @@ This will enable the default set of steps, based on your hardware and Ironic
drivers. If you're using an agent_* driver, this includes, by default, erasing
all of the previous tenant's data.
+.. _InbandvsOutOfBandCleaning:
In-Band vs Out-of-Band
======================
@@ -58,6 +59,8 @@ Out-of-band are actions performed by your management controller, such as IPMI,
iLO, or DRAC. Out-of-band steps will be performed by Ironic using a Power or
Management driver. Which steps are performed depends on the driver and hardware.
+For Out-of-Band cleaning operations supported by iLO drivers, refer to
+:ref:`ilo_node_cleaning`.
FAQ
===
@@ -85,6 +88,12 @@ to disable erase_devices, you'd use the following config::
[agent]
agent_erase_devices_priority=0
+To enable/disable the in-band disk erase using ``agent_ilo`` driver, use the
+following config::
+
+ [ilo]
+ clean_priority_erase_devices=0
+
What cleaning step is running?
------------------------------
diff --git a/doc/source/deploy/install-guide.rst b/doc/source/deploy/install-guide.rst
index 2907ad54b..5e253746e 100644
--- a/doc/source/deploy/install-guide.rst
+++ b/doc/source/deploy/install-guide.rst
@@ -4,8 +4,9 @@
Bare Metal Service Installation Guide
=====================================
-This document pertains to the Juno (2014.2) release of OpenStack. Users of
-earlier releases may encounter some differences in configuration of services.
+This document pertains to the Kilo (2015.1) release of OpenStack Ironic. Users
+of earlier releases may encounter differences, and are encouraged to look at
+earlier versions of this document for guidance.
Service Overview
@@ -14,23 +15,33 @@ Service Overview
The Bare Metal Service is a collection of components that provides support to
manage and provision physical machines.
-Also known as the ``ironic`` project, the Bare Metal Service interacts with
-several other OpenStack services such as:
+Also known as the ``Ironic`` project, the Bare Metal Service may, depending
+upon configuration, interact with several other OpenStack services. This
+includes:
-- the Identity Service (keystone) for request authentication and to
+- the Telemetry (Ceilometer) for consuming the IPMI metrics
+- the Identity Service (Keystone) for request authentication and to
locate other OpenStack services
-- the Image Service (glance) from which to retrieve images
-- the Networking Service (neutron) for DHCP and network configuration
-- the Compute Service (nova), which leverages the Bare Metal Service to
- manage compute instances on bare metal.
+- the Image Service (Glance) from which to retrieve images and image meta-data
+- the Networking Service (Neutron) for DHCP and network configuration
+- the Compute Service (Nova) works with Ironic and acts as a user-facing API
+ for instance management, while Ironic provides the admin/operator API for
+ hardware management. Nova also provides scheduling facilities (matching
+ flavors <-> images <-> hardware), tenant quotas, IP assignment, and other
+ services which Ironic does not, in and of itself, provide.
+
+- the Block Storage (Cinder) will provide volumes, but the aspect is not yet available.
The Bare Metal Service includes the following components:
-- ironic-api. A RESTful API that processes application requests by sending
+- ironic-api: A RESTful API that processes application requests by sending
them to the ironic-conductor over RPC.
-- ironic-conductor. Adds/edits/deletes nodes; powers on/off nodes with
+- ironic-conductor: Adds/edits/deletes nodes; powers on/off nodes with
ipmi or ssh; provisions/deploys/decommissions bare metal nodes.
-- Ironic client. A command-line interface (CLI) for interacting with
+- ironic-python-agent: A python service which is run in a temporary ramdisk to
+ provide ironic-conductor service(s) with remote access and in-band hardware
+ control.
+- python-ironicclient: A command-line interface (CLI) for interacting with
the Bare Metal Service.
Additionally, the Bare Metal Service has certain external dependencies, which are
@@ -44,6 +55,20 @@ very similar to other OpenStack Services:
- A queue. A central hub for passing messages. It should use the same
implementation as that of the Compute Service (typically RabbitMQ).
+Optionally, one may wish to utilize the following associated projects for
+additional functionality:
+
+- ironic-discoverd_; An associated service which performs in-band hardware
+ introspection by PXE booting unregistered hardware into a "discovery ramdisk".
+- diskimage-builder_; May be used to customize machine images, create and
+ discovery deploy ramdisks, if necessary.
+.. _ironic-discoverd: https://github.com/stackforge/ironic-discoverd
+.. _diskimage-builder: https://github.com/openstack/diskimage-builder
+
+
+.. todo: include coreos-image-builder reference here, once the split is done
+
+
Install and Configure Prerequisites
===================================
@@ -676,21 +701,6 @@ steps on the Ironic conductor node to configure PXE UEFI environment.
ironic node-update <node-uuid> add properties/capabilities='boot_mode:uefi'
-#. For deploying signed images, update the Ironic node with ``secure_boot``
- capability in node's properties.
- field::
-
- ironic node-update <node-uuid> add properties/capabilities='secure_boot:true'
-
-#. Ensure the public key of the signed image is loaded into baremetal to deploy
- signed images.
- For HP Proliant Gen9 servers, one can enroll public key using iLO System
- Utilities UI. Please refer to section ``Accessing Secure Boot options`` in
- HP UEFI System Utilities User Guide http://www.hp.com/ctg/Manual/c04398276.pdf.
- Also, one can refer to white paper on Secure Boot on Linux for HP Proliant
- Servers at http://h20195.www2.hp.com/V2/getpdf.aspx/4AA5-4496ENW.pdf for
- more details.
-
#. Make sure that bare metal node is configured to boot in UEFI boot mode and
boot device is set to network/pxe.
diff --git a/doc/source/deploy/upgrade-guide.rst b/doc/source/deploy/upgrade-guide.rst
new file mode 100644
index 000000000..2ce9e0952
--- /dev/null
+++ b/doc/source/deploy/upgrade-guide.rst
@@ -0,0 +1,36 @@
+.. _upgrade-guide:
+
+=====================================
+Bare Metal Service Upgrade Guide
+=====================================
+
+This document outlines various steps and notes for operators to consider when
+upgrading their Ironic-driven clouds from previous versions of OpenStack.
+
+The Ironic service is tightly coupled with the Ironic driver that is shipped
+with Nova. Currently, some special considerations must be taken into account
+when upgrading your cloud from previous versions of OpenStack.
+
+Upgrading from Juno to Kilo
+===========================
+
+When upgrading a cloud from Juno to Kilo, users must ensure the Nova
+service is upgraded prior to upgrading the Ironic service. Additionally,
+users need to set a special config flag in Nova prior to upgrading to ensure
+the newer version of Nova is not attempting to take advantage of new Ironic
+features until the Ironic service has been upgraded. The steps for upgrading
+your Nova and Ironic services are as follows:
+
+- Edit nova.conf and ensure force_config_drive=False is set in the [DEFAULT]
+ group. Restart nova-compute if necessary.
+- Install new Nova code, run database migrations
+- Install new python-ironicclient code.
+- Restart Nova services.
+- Install new Ironic code, run database migrations, restart Ironic services.
+- Edit nova.conf and set force_config_drive to your liking, restaring
+ nova-compute if necessary.
+
+Note that during the period between Nova's upgrade and Ironic's upgrades,
+instances can still be provisioned to nodes, however, any attempt by users
+to specify a config drive for an instance will cause error until Ironic's
+upgrade has completed.
diff --git a/doc/source/deploy/user-guide.rst b/doc/source/deploy/user-guide.rst
index 59d290a4e..89d0fd272 100644
--- a/doc/source/deploy/user-guide.rst
+++ b/doc/source/deploy/user-guide.rst
@@ -47,8 +47,8 @@ Conceptual Architecture
=======================
The following diagram shows the relationships and how all services come into
-play during the provisioning of a physical server. (Note that Swift can be
-used with Ironic, but is missing from this diagram.)
+play during the provisioning of a physical server. (Note that Ceilometer and
+Swift can be used with Ironic, but are missing from this diagram.)
.. figure:: ../images/conceptual_architecture.png
diff --git a/doc/source/drivers/amt.rst b/doc/source/drivers/amt.rst
index 33e9701ac..ef6701a83 100644
--- a/doc/source/drivers/amt.rst
+++ b/doc/source/drivers/amt.rst
@@ -76,3 +76,14 @@ A detailed reference is available here, and a short guide follows below:
``amt_address``, and ``amt_username``
* Boot an instance
+
+.. note::
+ It is recommended that nodes using the pxe_amt driver be deployed with the
+ `local boot`_ option. This is because the AMT firmware currently has no
+ support for setting a persistent boot device. Nodes deployed without the
+ `local boot`_ option could fail to boot if they are restarted outside of
+ Ironic's control (I.E. rebooted by a local user) because the node will
+ not attempt to PXE / network boot the kernel, using `local boot`_ solves this
+ known issue.
+
+.. _`local boot`: http://docs.openstack.org/developer/ironic/deploy/install-guide.html#local-boot-with-partition-images
diff --git a/doc/source/drivers/ilo.rst b/doc/source/drivers/ilo.rst
index 0cfe092ed..3ea0c1929 100644
--- a/doc/source/drivers/ilo.rst
+++ b/doc/source/drivers/ilo.rst
@@ -80,9 +80,10 @@ This driver should work on HP Proliant Gen8 Servers and above with iLO 4.
It has been tested with the following servers:
* ProLiant DL380e Gen8
-* ProLiant DL380e Gen8
* ProLiant DL580 Gen8 UEFI
* ProLiant DL180 Gen9 UEFI
+* ProLiant DL380 Gen9 UEFI
+* ProLiant DL580 Gen9 UEFI
For more up-to-date information on server platform support info, refer
iLO driver wiki [6]_.
@@ -95,11 +96,13 @@ Features
by the nova flavor's extra spec.
* Always boot from network using Virtual Media.
* UEFI Boot Support
+* UEFI Secure Boot Support
* Passing authentication token via secure, encrypted management network
(Virtual Media). Provisioning is done using iSCSI over data network
(like PXE driver), so this driver has the benefit of security
enhancement with the same performance. Hence it segregates management info
from data channel.
+* Support for Out-Of-Band cleaning operations.
* Remote Console
* HW Sensors
* Works well for machines with resource constraints (lesser amount of memory).
@@ -247,7 +250,15 @@ node::
Boot modes
~~~~~~~~~~
-Refer boot_mode_support_ for more information.
+Refer to `Boot mode support`_ section for more information.
+
+UEFI Secure Boot
+~~~~~~~~~~~~~~~~
+Refer to `UEFI Secure Boot support`_ section for more information.
+
+Node cleaning
+~~~~~~~~~~~~~
+Refer to ilo_node_cleaning_ for more information.
agent_ilo driver
^^^^^^^^^^^^^^^^
@@ -271,7 +282,8 @@ This driver should work on HP Proliant Gen8 Servers and above with iLO 4.
It has been tested with the following servers:
* ProLiant DL380e Gen8
-* ProLiant DL380e Gen8
+* ProLiant DL380 Gen9 UEFI
+* ProLiant DL580 Gen9 UEFI
This driver supports only Gen 8 Class 0 systems (BIOS only). For
more up-to-date information, check the iLO driver wiki [6]_.
@@ -279,11 +291,16 @@ more up-to-date information, check the iLO driver wiki [6]_.
Features
~~~~~~~~
* PXE-less deploy with Virtual Media using Ironic Python Agent.
+* Support for out-of-band cleaning operations.
* Remote Console
* HW Sensors
* IPA runs on the baremetal node and pulls the image directly from Swift.
* IPA deployed instances always boots from local disk.
* Segregates management info from data channel.
+* UEFI Boot Support
+* UEFI Secure Boot Support
+* Support to use default in-band cleaning operations supported by
+ Ironic Python Agent. For more details, see :ref:`InbandvsOutOfBandCleaning`.
Requirements
~~~~~~~~~~~~
@@ -421,6 +438,18 @@ node::
ironic node-create -d agent_ilo -i ilo_address=<ilo-ip-address> -i ilo_username=<ilo-username> -i ilo_password=<ilo-password> -i ilo_deploy_iso=<glance-uuid-of-deploy-iso>
+Boot modes
+~~~~~~~~~~
+Refer to `Boot mode support`_ section for more information.
+
+UEFI Secure Boot
+~~~~~~~~~~~~~~~~
+Refer to `UEFI Secure Boot support`_ section for more information.
+
+Node Cleaning
+~~~~~~~~~~~~~
+Refer to ilo_node_cleaning_ for more information.
+
pxe_ilo driver
^^^^^^^^^^^^^^
@@ -456,6 +485,7 @@ Features
* Automatic detection of current boot mode.
* Automatic setting of the required boot mode if UEFI boot mode is requested
by the nova flavor's extra spec.
+* Support for Out-Of-Band cleaning operations.
Requirements
~~~~~~~~~~~~
@@ -510,13 +540,15 @@ node::
Boot modes
~~~~~~~~~~
-Refer boot_mode_support_ for more information.
+Refer to `Boot mode support`_ section for more information.
+
+Node Cleaning
+~~~~~~~~~~~~~
+Refer to ilo_node_cleaning_ for more information.
Functionalities across drivers
==============================
-.. _boot_mode_support:
-
Boot mode support
^^^^^^^^^^^^^^^^^
The following drivers support automatic detection and setting of boot
@@ -524,6 +556,7 @@ mode (Legacy BIOS or UEFI).
* ``pxe_ilo``
* ``iscsi_ilo``
+* ``agent_ilo``
The boot modes can be configured in Ironic in the following way:
@@ -570,6 +603,104 @@ diskimage-builder command to build the image. For example::
disk-image-create ubuntu baremetal iso
+UEFI Secure Boot support
+^^^^^^^^^^^^^^^^^^^^^^^^
+The following drivers support UEFI secure boot deploy:
+
+* ``iscsi_ilo``
+* ``agent_ilo``
+
+The UEFI secure boot mode can be configured in Ironic by adding
+``secure_boot`` parameter in the ``capabilities`` parameter within
+``properties`` field of an Ironic node.
+
+``secure_boot`` is a boolean parameter and takes value as ``true`` or
+``false``.
+
+To enable ``secure_boot`` on a node add it to ``capabilities`` as below::
+
+ ironic node-update <node-uuid> add properties/capabilities='secure_boot:true'
+
+Nodes having ``secure_boot`` set to ``true`` may be requested by adding an
+``extra_spec`` to the Nova flavor::
+
+ nova flavor-key ironic-test-3 set capabilities:secure_boot="true"
+ nova boot --flavor ironic-test-3 --image test-image instance-1
+
+If ``capabilities`` is used in ``extra_spec`` as above, Nova scheduler
+(``ComputeCapabilitiesFilter``) will match only Ironic nodes which have
+the ``secure_boot`` set appropriately in ``properties/capabilities``. It will
+filter out rest of the nodes.
+
+The above facility for matching in Nova can be used in heterogeneous
+environments where there is a mix of machines supporting and not supporting
+UEFI secure boot, and operator wants to provide a choice to the user
+regarding secure boot. If the flavor doesn't contain ``secure_boot`` then
+Nova scheduler will not consider secure boot mode as a placement criteria,
+hence user may get a secure boot capable machine that matches with user
+specified flavors but deployment would not use its secure boot capability.
+Secure boot deploy would happen only when it is explicitly specified through
+flavor.
+
+Ensure the public key of the signed image is loaded into baremetal to deploy
+signed images.
+For HP Proliant Gen9 servers, one can enroll public key using iLO System
+Utilities UI. Please refer to section ``Accessing Secure Boot options`` in
+HP UEFI System Utilities User Guide. [7]_
+One can also refer to white paper on Secure Boot for Linux on HP Proliant
+servers for additional details. [8]_
+
+.. _ilo_node_cleaning:
+
+Node Cleaning
+^^^^^^^^^^^^^
+The following iLO drivers support node cleaning -
+
+* ``pxe_ilo``
+* ``iscsi_ilo``
+* ``agent_ilo``
+
+Supported Cleaning Operations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* The cleaning operations supported are:
+
+ -``reset_ilo``:
+ Resets the iLO. By default, enabled with priority 1.
+ -``reset_bios_to_default``:
+ Resets BIOS Settings to default. By default, enabled with priority 10.
+ This clean step is supported only on Gen9 and above servers.
+ -``reset_secure_boot_keys_to_default``:
+ Resets secure boot keys to manufacturer's defaults. This step is supported
+ only on Gen9 and above servers. By default, enabled with priority 20 .
+ -``reset_ilo_credential``:
+ Resets the iLO password, if 'ilo_change_password' is specified as part of
+ node's driver_info. By default, enabled with priority 30.
+ -``clear_secure_boot_keys``:
+ Clears all secure boot keys. This step is supported only on Gen9 and above
+ servers. By default, this step is disabled.
+
+* For in-band cleaning operations supported by ``agent_ilo`` driver, see
+ :ref:`InbandvsOutOfBandCleaning`.
+
+* All the cleaning steps have an explicit configuration option for priority.
+ In order to disable or change the priority of the clean steps, respective
+ configuration option for priority should be updated in ironic.conf.
+
+* Updating clean step priority to 0, will disable that particular clean step
+ and will not run during cleaning.
+
+* Configuration Options for the clean steps are listed under [ilo] section in
+ ironic.conf ::
+
+ - clean_priority_reset_ilo=1
+ - clean_priority_reset_bios_to_default=10
+ - clean_priority_reset_secure_boot_keys_to_default=20
+ - clean_priority_clear_secure_boot_keys=0
+ - clean_priority_reset_ilo_credential=30
+ - clean_priority_erase_devices=10
+
+For more information on node cleaning, see [9]_.
References
==========
@@ -579,4 +710,7 @@ References
.. [4] http://docs.openstack.org/developer/glance/configuring.html#configuring-the-swift-storage-backend
.. [5] Ironic Python Agent - https://github.com/openstack/ironic-python-agent
.. [6] https://wiki.openstack.org/wiki/Ironic/Drivers/iLODrivers
+.. [7] HP UEFI System Utilities User Guide - http://www.hp.com/ctg/Manual/c04398276.pdf
+.. [8] Secure Boot for Linux on HP Proliant servers http://h20195.www2.hp.com/V2/getpdf.aspx/4AA5-4496ENW.pdf
+.. [9] http://docs.openstack.org/developer/ironic/deploy/cleaning.html
diff --git a/doc/source/index.rst b/doc/source/index.rst
index a3339207d..7ea533fdb 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -57,6 +57,7 @@ Overview
deploy/user-guide
deploy/install-guide
+ deploy/upgrade-guide
deploy/drivers
deploy/cleaning
diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index a8a1501a6..ccf368f0d 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -360,6 +360,11 @@
# set to 0, will not run during cleaning. (integer value)
#agent_erase_devices_priority=<None>
+# Whether Ironic will manage TFTP files for the deploy
+# ramdisks. If set to False, you will need to configure your
+# own TFTP server that allows booting the deploy ramdisks.
+# (boolean value)
+#manage_tftp=true
#
# Options defined in ironic.drivers.modules.agent_base_vendor
diff --git a/ironic/api/middleware/parsable_error.py b/ironic/api/middleware/parsable_error.py
index 1b86e14ff..42e2d6875 100644
--- a/ironic/api/middleware/parsable_error.py
+++ b/ironic/api/middleware/parsable_error.py
@@ -85,7 +85,7 @@ class ParsableErrorMiddleware(object):
else:
body = [json.dumps({'error_message': '\n'.join(app_iter)})]
state['headers'].append(('Content-Type', 'application/json'))
- state['headers'].append(('Content-Length', len(body[0])))
+ state['headers'].append(('Content-Length', str(len(body[0]))))
else:
body = app_iter
return body
diff --git a/ironic/common/grub_conf.template b/ironic/common/grub_conf.template
index 746a43d97..2a979d2d6 100644
--- a/ironic/common/grub_conf.template
+++ b/ironic/common/grub_conf.template
@@ -1,4 +1,8 @@
-menuentry "install" {
-linux {{ linux }} {{ kernel_params }} --
-initrd {{ initrd }}
+set default=0
+set timeout=5
+set hidden_timeout_quiet=false
+
+menuentry "boot_partition" {
+linuxefi {{ linux }} {{ kernel_params }} --
+initrdefi {{ initrd }}
}
diff --git a/ironic/common/pxe_utils.py b/ironic/common/pxe_utils.py
index a125da034..09528a218 100644
--- a/ironic/common/pxe_utils.py
+++ b/ironic/common/pxe_utils.py
@@ -80,12 +80,20 @@ def _link_mac_pxe_configs(task):
:param task: A TaskManager instance.
"""
- pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
- for mac in driver_utils.get_node_mac_addresses(task):
- mac_path = _get_pxe_mac_path(mac)
+
+ def create_link(mac_path):
utils.unlink_without_raise(mac_path)
utils.create_link_without_raise(pxe_config_file_path, mac_path)
+ pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
+ for mac in driver_utils.get_node_mac_addresses(task):
+ create_link(_get_pxe_mac_path(mac))
+ # TODO(lucasagomes): Backward compatibility with :hexraw,
+ # to be removed in M.
+ # see: https://bugs.launchpad.net/ironic/+bug/1441710
+ if CONF.pxe.ipxe_enabled:
+ create_link(_get_pxe_mac_path(mac, delimiter=''))
+
def _link_ip_address_pxe_configs(task):
"""Link each IP address with the PXE configuration file.
@@ -110,17 +118,20 @@ def _link_ip_address_pxe_configs(task):
ip_address_path)
-def _get_pxe_mac_path(mac):
+def _get_pxe_mac_path(mac, delimiter=None):
"""Convert a MAC address into a PXE config file name.
:param mac: A MAC address string in the format xx:xx:xx:xx:xx:xx.
+ :param delimiter: The MAC address delimiter. Defaults to dash ('-').
:returns: the path to the config file.
"""
- if CONF.pxe.ipxe_enabled:
- mac_file_name = mac.replace(':', '').lower()
- else:
- mac_file_name = "01-" + mac.replace(":", "-").lower()
+ if delimiter is None:
+ delimiter = '-'
+
+ mac_file_name = mac.replace(':', delimiter).lower()
+ if not CONF.pxe.ipxe_enabled:
+ mac_file_name = '01-' + mac_file_name
return os.path.join(get_root_dir(), PXE_CFG_DIR_NAME, mac_file_name)
@@ -221,6 +232,12 @@ def clean_up_pxe_config(task):
else:
for mac in driver_utils.get_node_mac_addresses(task):
utils.unlink_without_raise(_get_pxe_mac_path(mac))
+ # TODO(lucasagomes): Backward compatibility with :hexraw,
+ # to be removed in M.
+ # see: https://bugs.launchpad.net/ironic/+bug/1441710
+ if CONF.pxe.ipxe_enabled:
+ utils.unlink_without_raise(_get_pxe_mac_path(mac,
+ delimiter=''))
utils.rmtree_without_raise(os.path.join(get_root_dir(),
task.node.uuid))
diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py
index 3322741ab..c2b75bca0 100644
--- a/ironic/conductor/manager.py
+++ b/ironic/conductor/manager.py
@@ -204,7 +204,7 @@ class ConductorManager(periodic_task.PeriodicTasks):
"""Ironic Conductor manager main class."""
# NOTE(rloo): This must be in sync with rpcapi.ConductorAPI's.
- RPC_API_VERSION = '1.26'
+ RPC_API_VERSION = '1.27'
target = messaging.Target(version=RPC_API_VERSION)
@@ -824,10 +824,6 @@ class ConductorManager(periodic_task.PeriodicTasks):
state=node.provision_state)
self._do_node_clean(task)
- @messaging.expected_exceptions(exception.NoFreeConductorWorker,
- exception.NodeLocked,
- exception.InvalidStateRequested,
- exception.NodeNotFound)
def continue_node_clean(self, context, node_id):
"""RPC method to continue cleaning a node.
diff --git a/ironic/conductor/rpcapi.py b/ironic/conductor/rpcapi.py
index 06c7d434c..1ca0db5ee 100644
--- a/ironic/conductor/rpcapi.py
+++ b/ironic/conductor/rpcapi.py
@@ -69,11 +69,12 @@ class ConductorAPI(object):
| 1.24 - Added inspect_hardware method
| 1.25 - Added destroy_port
| 1.26 - Added continue_node_clean
+ | 1.27 - Convert continue_node_clean to cast
"""
# NOTE(rloo): This must be in sync with manager.ConductorManager's.
- RPC_API_VERSION = '1.26'
+ RPC_API_VERSION = '1.27'
def __init__(self, topic=None):
super(ConductorAPI, self).__init__()
@@ -329,18 +330,15 @@ class ConductorAPI(object):
def continue_node_clean(self, context, node_id, topic=None):
"""Signal to conductor service to start the next cleaning action.
+ NOTE(JoshNang) this is an RPC cast, there will be no response or
+ exception raised by the conductor for this RPC.
+
:param context: request context.
:param node_id: node id or uuid.
:param topic: RPC topic. Defaults to self.topic.
- :raises: NoFreeConductorWorker when there is no free worker to start
- async task.
- :raises: InvalidStateRequested if the requested action can not
- be performed.
- :raises: NodeLocked if node is locked by another conductor.
- :raises: NodeNotFound if the node no longer appears in the database
"""
- cctxt = self.client.prepare(topic=topic or self.topic, version='1.26')
- return cctxt.call(context, 'continue_node_clean',
+ cctxt = self.client.prepare(topic=topic or self.topic, version='1.27')
+ return cctxt.cast(context, 'continue_node_clean',
node_id=node_id)
def validate_driver_interfaces(self, context, node_id, topic=None):
diff --git a/ironic/drivers/modules/agent.py b/ironic/drivers/modules/agent.py
index c257d2c77..6a590c8c4 100644
--- a/ironic/drivers/modules/agent.py
+++ b/ironic/drivers/modules/agent.py
@@ -57,7 +57,14 @@ agent_opts = [
'Python Agent ramdisk. If unset, will use the priority '
'set in the ramdisk (defaults to 10 for the '
'GenericHardwareManager). If set to 0, will not run '
- 'during cleaning.')
+ 'during cleaning.'),
+ cfg.BoolOpt('manage_tftp',
+ default=True,
+ help='Whether Ironic will manage TFTP files for the deploy '
+ 'ramdisks. If set to False, you will need to configure '
+ 'your own TFTP server that allows booting the deploy '
+ 'ramdisks.'
+ ),
]
CONF = cfg.CONF
@@ -196,12 +203,13 @@ def build_instance_info_for_deploy(task):
def _prepare_pxe_boot(task):
"""Prepare the files required for PXE booting the agent."""
- pxe_info = _get_tftp_image_info(task.node)
- pxe_options = _build_pxe_config_options(task.node, pxe_info)
- pxe_utils.create_pxe_config(task,
- pxe_options,
- CONF.agent.agent_pxe_config_template)
- _cache_tftp_images(task.context, task.node, pxe_info)
+ if CONF.agent.manage_tftp:
+ pxe_info = _get_tftp_image_info(task.node)
+ pxe_options = _build_pxe_config_options(task.node, pxe_info)
+ pxe_utils.create_pxe_config(task,
+ pxe_options,
+ CONF.agent.agent_pxe_config_template)
+ _cache_tftp_images(task.context, task.node, pxe_info)
def _do_pxe_boot(task, ports=None):
@@ -220,13 +228,13 @@ def _do_pxe_boot(task, ports=None):
def _clean_up_pxe(task):
"""Clean up left over PXE and DHCP files."""
- pxe_info = _get_tftp_image_info(task.node)
- for label in pxe_info:
- path = pxe_info[label][1]
- utils.unlink_without_raise(path)
- AgentTFTPImageCache().clean_up()
-
- pxe_utils.clean_up_pxe_config(task)
+ if CONF.agent.manage_tftp:
+ pxe_info = _get_tftp_image_info(task.node)
+ for label in pxe_info:
+ path = pxe_info[label][1]
+ utils.unlink_without_raise(path)
+ AgentTFTPImageCache().clean_up()
+ pxe_utils.clean_up_pxe_config(task)
class AgentDeploy(base.DeployInterface):
@@ -251,10 +259,11 @@ class AgentDeploy(base.DeployInterface):
"""
node = task.node
params = {}
- params['driver_info.deploy_kernel'] = node.driver_info.get(
- 'deploy_kernel')
- params['driver_info.deploy_ramdisk'] = node.driver_info.get(
- 'deploy_ramdisk')
+ if CONF.agent.manage_tftp:
+ params['driver_info.deploy_kernel'] = node.driver_info.get(
+ 'deploy_kernel')
+ params['driver_info.deploy_ramdisk'] = node.driver_info.get(
+ 'deploy_ramdisk')
image_source = node.instance_info.get('image_source')
params['instance_info.image_source'] = image_source
error_msg = _('Node %s failed to validate deploy image info. Some '
diff --git a/ironic/drivers/modules/boot.ipxe b/ironic/drivers/modules/boot.ipxe
index 25a0ea8dc..3567dc029 100644
--- a/ironic/drivers/modules/boot.ipxe
+++ b/ironic/drivers/modules/boot.ipxe
@@ -1,7 +1,7 @@
#!ipxe
# load the MAC-specific file or fail if it's not found
-chain --autofree pxelinux.cfg/${mac:hexraw} || goto error_no_config
+chain --autofree pxelinux.cfg/${mac:hexhyp} || goto error_no_config
:error_no_config
echo PXE boot failed. No configuration found for MAC ${mac}
diff --git a/ironic/drivers/modules/deploy_utils.py b/ironic/drivers/modules/deploy_utils.py
index 79fcaecd3..e6c2c48ef 100644
--- a/ironic/drivers/modules/deploy_utils.py
+++ b/ironic/drivers/modules/deploy_utils.py
@@ -1046,24 +1046,22 @@ def is_secure_boot_requested(node):
def get_boot_mode_for_deploy(node):
"""Returns the boot mode that would be used for deploy.
- This method returns boot mode to used for deploy using following order:
+ This method returns boot mode to be used for deploy.
It returns 'uefi' if 'secure_boot' is set to 'true' in
'instance_info/capabilities' of node.
- It returns value of 'boot_mode' in 'properties/capabilities' of node.
- It returns boot mode specified in 'instance_info/deploy_boot_mode' 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
+ '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 'instance_info'.
+ node 'properties' nor in node's 'instance_info' (which could also be None).
:param node: an ironic node object.
:returns: 'bios', 'uefi' or None
"""
if is_secure_boot_requested(node):
- boot_mode = 'uefi'
- LOG.debug('Deploy boot mode is %(boot_mode)s for %(node)s.',
- {'boot_mode': boot_mode, 'node': node.uuid})
- return boot_mode
+ LOG.debug('Deploy boot mode is uefi for %s.', node.uuid)
+ return 'uefi'
boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
if boot_mode is None:
@@ -1072,4 +1070,5 @@ def get_boot_mode_for_deploy(node):
LOG.debug('Deploy boot mode is %(boot_mode)s for %(node)s.',
{'boot_mode': boot_mode, 'node': node.uuid})
- return boot_mode
+
+ return boot_mode.lower() if boot_mode else boot_mode
diff --git a/ironic/drivers/modules/ilo/common.py b/ironic/drivers/modules/ilo/common.py
index 02085ea5d..1d1363d0d 100644
--- a/ironic/drivers/modules/ilo/common.py
+++ b/ironic/drivers/modules/ilo/common.py
@@ -348,7 +348,7 @@ def update_boot_mode(task):
if boot_mode is not None:
LOG.debug("Node %(uuid)s boot mode is being set to %(boot_mode)s",
{'uuid': node.uuid, 'boot_mode': boot_mode})
- set_boot_mode(node, boot_mode.lower())
+ set_boot_mode(node, boot_mode)
return
LOG.debug("Check pending boot mode for node %s.", node.uuid)
diff --git a/ironic/drivers/modules/ilo/deploy.py b/ironic/drivers/modules/ilo/deploy.py
index dbd9d7340..86dc0c729 100644
--- a/ironic/drivers/modules/ilo/deploy.py
+++ b/ironic/drivers/modules/ilo/deploy.py
@@ -335,8 +335,8 @@ def _prepare_node_for_deploy(task):
if change_boot_mode:
ilo_common.update_boot_mode(task)
else:
- # Need to update boot mode that would used during deploy, if one is not
- # provided.
+ # 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:
instance_info = task.node.instance_info
diff --git a/ironic/drivers/modules/ipmitool.py b/ironic/drivers/modules/ipmitool.py
index 55fc1fa2d..ddebd68ef 100644
--- a/ironic/drivers/modules/ipmitool.py
+++ b/ironic/drivers/modules/ipmitool.py
@@ -111,7 +111,11 @@ ipmitool_command_options = {
'dual_bridge': ['ipmitool', '-m', '0', '-b', '0', '-t', '0',
'-B', '0', '-T', '0', '-h']}
-# Note(TheJulia): This string is hardcoded in ipmitool's lanplus driver.
+# Note(TheJulia): This string is hardcoded in ipmitool's lanplus driver
+# and is substituted in return for the error code received from the IPMI
+# controller. As of 1.8.15, no internationalization support appears to
+# be in ipmitool which means the string should always be returned in this
+# form regardless of locale.
IPMITOOL_RETRYABLE_FAILURES = ['insufficient resources for session']
@@ -348,14 +352,14 @@ def _exec_ipmitool(driver_info, command):
args.append('-N')
args.append(str(CONF.ipmi.min_command_interval))
- end_time = (_time() + CONF.ipmi.retry_timeout)
+ end_time = (time.time() + CONF.ipmi.retry_timeout)
while True:
num_tries = num_tries - 1
# NOTE(deva): ensure that no communications are sent to a BMC more
# often than once every min_command_interval seconds.
time_till_next_poll = CONF.ipmi.min_command_interval - (
- _time() - LAST_CMD_TIME.get(driver_info['address'], 0))
+ time.time() - LAST_CMD_TIME.get(driver_info['address'], 0))
if time_till_next_poll > 0:
time.sleep(time_till_next_poll)
# Resetting the list that will be utilized so the password arguments
@@ -377,21 +381,21 @@ def _exec_ipmitool(driver_info, command):
with excutils.save_and_reraise_exception() as ctxt:
err_list = [x for x in IPMITOOL_RETRYABLE_FAILURES
if x in e.message]
- if ((_time() > end_time) or
+ if ((time.time() > end_time) or
(num_tries == 0) or
not err_list):
- LOG.error(_LE('IPMI Error attempting to execute '
+ LOG.error(_LE('IPMI Error while attempting '
'"%(cmd)s" for node %(node)s. '
'Error: %(error)s'),
{
- 'node': driver_info['uuid'],
- 'cmd': e.cmd,
- 'error': e
+ 'node': driver_info['uuid'],
+ 'cmd': e.cmd,
+ 'error': e
})
else:
ctxt.reraise = False
LOG.warning(_LW('IPMI Error encountered, retrying '
- '"%(cmd)s" for node %(node)s '
+ '"%(cmd)s" for node %(node)s. '
'Error: %(error)s'),
{
'node': driver_info['uuid'],
@@ -399,7 +403,7 @@ def _exec_ipmitool(driver_info, command):
'error': e
})
finally:
- LAST_CMD_TIME[driver_info['address']] = _time()
+ LAST_CMD_TIME[driver_info['address']] = time.time()
def _sleep_time(iter):
@@ -626,11 +630,6 @@ def send_raw(task, raw_bytes):
raise exception.IPMIFailure(cmd=cmd)
-def _time():
- """Wrapper for time.time() enabling simplified unit testing."""
- return time.time()
-
-
class IPMIPower(base.PowerInterface):
def __init__(self):
diff --git a/ironic/drivers/modules/iscsi_deploy.py b/ironic/drivers/modules/iscsi_deploy.py
index 57df4a7d6..ac7315d33 100644
--- a/ironic/drivers/modules/iscsi_deploy.py
+++ b/ironic/drivers/modules/iscsi_deploy.py
@@ -422,7 +422,7 @@ def _get_boot_mode(node):
"""
boot_mode = deploy_utils.get_boot_mode_for_deploy(node)
if boot_mode:
- return boot_mode.lower()
+ return boot_mode
return "bios"
@@ -447,13 +447,20 @@ def build_deploy_ramdisk_options(node):
node.instance_info = i_info
node.save()
+ # XXX(jroll) DIB relies on boot_option=local to decide whether or not to
+ # lay down a bootloader. Hack this for now; fix it for real in Liberty.
+ # See also bug #1441556.
+ boot_option = get_boot_option(node)
+ if node.driver_internal_info.get('is_whole_disk_image'):
+ boot_option = 'netboot'
+
deploy_options = {
'deployment_id': node['uuid'],
'deployment_key': deploy_key,
'iscsi_target_iqn': "iqn-%s" % node.uuid,
'ironic_api_url': ironic_api,
'disk': CONF.pxe.disk_devices,
- 'boot_option': get_boot_option(node),
+ 'boot_option': boot_option,
'boot_mode': _get_boot_mode(node),
# NOTE: The below entry is a temporary workaround for bug/1433812
'coreos.configdrive': 0,
diff --git a/ironic/tests/conductor/test_manager.py b/ironic/tests/conductor/test_manager.py
index 47a6e77f7..df0078712 100644
--- a/ironic/tests/conductor/test_manager.py
+++ b/ironic/tests/conductor/test_manager.py
@@ -1606,11 +1606,9 @@ class DoNodeCleanTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
mock_spawn.side_effect = exception.NoFreeConductorWorker()
- exc = self.assertRaises(messaging.rpc.ExpectedException,
- self.service.continue_node_clean,
- self.context, node.uuid)
- # Compare true exception hidden by @messaging.expected_exceptions
- self.assertEqual(exception.NoFreeConductorWorker, exc.exc_info[0])
+ self.assertRaises(exception.NoFreeConductorWorker,
+ self.service.continue_node_clean,
+ self.context, node.uuid)
self.service._worker_pool.waitall()
node.refresh()
@@ -1630,11 +1628,9 @@ class DoNodeCleanTestCase(_ServiceSetUpMixin, tests_db_base.DbTestCase):
last_error=None)
self._start_service()
- exc = self.assertRaises(messaging.rpc.ExpectedException,
- self.service.continue_node_clean,
- self.context, node.uuid)
- # Compare true exception hidden by @messaging.expected_exceptions
- self.assertEqual(exception.InvalidStateRequested, exc.exc_info[0])
+ self.assertRaises(exception.InvalidStateRequested,
+ self.service.continue_node_clean,
+ self.context, node.uuid)
self.service._worker_pool.waitall()
node.refresh()
diff --git a/ironic/tests/conductor/test_rpcapi.py b/ironic/tests/conductor/test_rpcapi.py
index 1ec18db3f..e7d27627c 100644
--- a/ironic/tests/conductor/test_rpcapi.py
+++ b/ironic/tests/conductor/test_rpcapi.py
@@ -291,6 +291,6 @@ class RPCAPITestCase(base.DbTestCase):
def test_continue_node_clean(self):
self._test_rpcapi('continue_node_clean',
- 'call',
- version='1.26',
+ 'cast',
+ version='1.27',
node_id=self.fake_node['uuid'])
diff --git a/ironic/tests/drivers/test_agent.py b/ironic/tests/drivers/test_agent.py
index b9ea638e0..bb9675f3f 100644
--- a/ironic/tests/drivers/test_agent.py
+++ b/ironic/tests/drivers/test_agent.py
@@ -164,6 +164,14 @@ class TestAgentDeploy(db_base.DbTestCase):
self.assertIn('driver_info.deploy_ramdisk', str(e))
self.assertIn('driver_info.deploy_kernel', str(e))
+ def test_validate_driver_info_manage_tftp_false(self):
+ self.config(manage_tftp=False, group='agent')
+ self.node.driver_info = {}
+ self.node.save()
+ with task_manager.acquire(
+ self.context, self.node['uuid'], shared=False) as task:
+ self.driver.validate(task)
+
def test_validate_instance_info_missing_params(self):
self.node.instance_info = {}
self.node.save()
@@ -199,6 +207,37 @@ class TestAgentDeploy(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue,
task.driver.deploy.validate, task)
+ @mock.patch.object(agent, '_cache_tftp_images')
+ @mock.patch.object(pxe_utils, 'create_pxe_config')
+ @mock.patch.object(agent, '_build_pxe_config_options')
+ @mock.patch.object(agent, '_get_tftp_image_info')
+ def test__prepare_pxe_boot(self, pxe_info_mock, options_mock,
+ create_mock, cache_mock):
+ with task_manager.acquire(
+ self.context, self.node['uuid'], shared=False) as task:
+ agent._prepare_pxe_boot(task)
+ pxe_info_mock.assert_called_once_with(task.node)
+ options_mock.assert_called_once_with(task.node, mock.ANY)
+ create_mock.assert_called_once_with(
+ task, mock.ANY, CONF.agent.agent_pxe_config_template)
+ cache_mock.assert_called_once_with(task.context, task.node,
+ mock.ANY)
+
+ @mock.patch.object(agent, '_cache_tftp_images')
+ @mock.patch.object(pxe_utils, 'create_pxe_config')
+ @mock.patch.object(agent, '_build_pxe_config_options')
+ @mock.patch.object(agent, '_get_tftp_image_info')
+ def test__prepare_pxe_boot_manage_tftp_false(
+ self, pxe_info_mock, options_mock, create_mock, cache_mock):
+ self.config(manage_tftp=False, group='agent')
+ with task_manager.acquire(
+ self.context, self.node['uuid'], shared=False) as task:
+ agent._prepare_pxe_boot(task)
+ self.assertFalse(pxe_info_mock.called)
+ self.assertFalse(options_mock.called)
+ self.assertFalse(create_mock.called)
+ self.assertFalse(cache_mock.called)
+
@mock.patch.object(dhcp_factory.DHCPFactory, 'update_dhcp')
@mock.patch('ironic.conductor.utils.node_set_boot_device')
@mock.patch('ironic.conductor.utils.node_power_action')
@@ -221,6 +260,36 @@ class TestAgentDeploy(db_base.DbTestCase):
power_mock.assert_called_once_with(task, states.POWER_OFF)
self.assertEqual(driver_return, states.DELETED)
+ @mock.patch.object(pxe_utils, 'clean_up_pxe_config')
+ @mock.patch.object(agent, 'AgentTFTPImageCache')
+ @mock.patch('ironic.common.utils.unlink_without_raise')
+ @mock.patch.object(agent, '_get_tftp_image_info')
+ def test__clean_up_pxe(self, info_mock, unlink_mock, cache_mock,
+ clean_mock):
+ info_mock.return_value = {'label': ['fake1', 'fake2']}
+ with task_manager.acquire(
+ self.context, self.node['uuid'], shared=False) as task:
+ agent._clean_up_pxe(task)
+ info_mock.assert_called_once_with(task.node)
+ unlink_mock.assert_called_once_with('fake2')
+ clean_mock.assert_called_once_with(task)
+
+ @mock.patch.object(pxe_utils, 'clean_up_pxe_config')
+ @mock.patch.object(agent.AgentTFTPImageCache, 'clean_up')
+ @mock.patch('ironic.common.utils.unlink_without_raise')
+ @mock.patch.object(agent, '_get_tftp_image_info')
+ def test__clean_up_pxe_manage_tftp_false(
+ self, info_mock, unlink_mock, cache_mock, clean_mock):
+ self.config(manage_tftp=False, group='agent')
+ info_mock.return_value = {'label': ['fake1', 'fake2']}
+ with task_manager.acquire(
+ self.context, self.node['uuid'], shared=False) as task:
+ agent._clean_up_pxe(task)
+ self.assertFalse(info_mock.called)
+ self.assertFalse(unlink_mock.called)
+ self.assertFalse(cache_mock.called)
+ self.assertFalse(clean_mock.called)
+
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.delete_cleaning_ports')
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.create_cleaning_ports')
@mock.patch('ironic.drivers.modules.agent._do_pxe_boot')
diff --git a/ironic/tests/drivers/test_ipmitool.py b/ironic/tests/drivers/test_ipmitool.py
index 0828f2c60..2e0097fba 100644
--- a/ironic/tests/drivers/test_ipmitool.py
+++ b/ironic/tests/drivers/test_ipmitool.py
@@ -891,21 +891,21 @@ class IPMIToolPrivateMethodTestCase(db_base.DbTestCase):
@mock.patch.object(ipmi, '_is_option_supported', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
- def test__exec_ipmitool_exception_retry_failure_unhandable(self,
+ def test__exec_ipmitool_exception_non_retryable_failure(self,
mock_exec, mock_support, mock_sleep):
ipmi.LAST_CMD_TIME = {}
mock_support.return_value = False
- # Return a retryable error, then a error that cannot
- # be retryable thus resulting in a single retry
- # attempt by _exec_ipmitool that is successful.
+ # Return a retryable error, then an error that cannot
+ # be retried thus resulting in a single retry
+ # attempt by _exec_ipmitool.
mock_exec.side_effect = iter([
processutils.ProcessExecutionError(
stderr="insufficient resources for session"
),
processutils.ProcessExecutionError(
- "Unknown"
+ stderr="Unknown"
),
])
diff --git a/ironic/tests/drivers/test_iscsi_deploy.py b/ironic/tests/drivers/test_iscsi_deploy.py
index a0b1f40ba..4d3803542 100644
--- a/ironic/tests/drivers/test_iscsi_deploy.py
+++ b/ironic/tests/drivers/test_iscsi_deploy.py
@@ -487,6 +487,25 @@ class IscsiDeployMethodsTestCase(db_base.DbTestCase):
self._test_build_deploy_ramdisk_options(mock_alnum, fake_api_url,
expected_boot_option=expected)
+ @mock.patch.object(keystone, 'get_service_url', autospec=True)
+ @mock.patch.object(utils, 'random_alnum', autospec=True)
+ def test_build_deploy_ramdisk_options_whole_disk_image(self, mock_alnum,
+ mock_get_url):
+ """Tests a hack to boot_option for whole disk images.
+
+ This hack is in place to fix bug #1441556.
+ """
+ self.node.instance_info = {'capabilities': '{"boot_option": "local"}'}
+ dii = self.node.driver_internal_info
+ dii['is_whole_disk_image'] = True
+ self.node.driver_internal_info = dii
+ self.node.save()
+ expected = 'netboot'
+ fake_api_url = 'http://127.0.0.1:6385'
+ self.config(api_url=fake_api_url, group='conductor')
+ self._test_build_deploy_ramdisk_options(mock_alnum, fake_api_url,
+ expected_boot_option=expected)
+
def test_get_boot_option(self):
self.node.instance_info = {'capabilities': '{"boot_option": "local"}'}
result = iscsi_deploy.get_boot_option(self.node)
diff --git a/ironic/tests/drivers/test_seamicro.py b/ironic/tests/drivers/test_seamicro.py
index ad4586828..e477cddd4 100644
--- a/ironic/tests/drivers/test_seamicro.py
+++ b/ironic/tests/drivers/test_seamicro.py
@@ -129,6 +129,7 @@ class SeaMicroValidateParametersTestCase(db_base.DbTestCase):
node)
+@mock.patch('eventlet.greenthread.sleep', lambda n: None)
class SeaMicroPrivateMethodsTestCase(db_base.DbTestCase):
def setUp(self):
@@ -144,8 +145,6 @@ class SeaMicroPrivateMethodsTestCase(db_base.DbTestCase):
self.config(action_timeout=0, group='seamicro')
self.config(max_retry=2, group='seamicro')
- self.patcher = mock.patch('eventlet.greenthread.sleep')
- self.mock_sleep = self.patcher.start()
self.info = seamicro._parse_driver_info(self.node)
@mock.patch.object(seamicro_client, "Client")
diff --git a/ironic/tests/test_images.py b/ironic/tests/test_images.py
index fb1da9dbc..b610d4b0a 100644
--- a/ironic/tests/test_images.py
+++ b/ironic/tests/test_images.py
@@ -424,12 +424,15 @@ class FsImageTestCase(base.TestCase):
self.assertEqual(expected_cfg, cfg)
def test__generate_grub_cfg(self):
-
kernel_params = ['key1=value1', 'key2']
options = {'linux': '/vmlinuz', 'initrd': '/initrd'}
- expected_cfg = ("menuentry \"install\" {\n"
- "linux /vmlinuz key1=value1 key2 --\n"
- "initrd /initrd\n"
+ expected_cfg = ("set default=0\n"
+ "set timeout=5\n"
+ "set hidden_timeout_quiet=false\n"
+ "\n"
+ "menuentry \"boot_partition\" {\n"
+ "linuxefi /vmlinuz key1=value1 key2 --\n"
+ "initrdefi /initrd\n"
"}")
cfg = images._generate_cfg(kernel_params,
diff --git a/ironic/tests/test_pxe_utils.py b/ironic/tests/test_pxe_utils.py
index f16cd3e1b..1792d840a 100644
--- a/ironic/tests/test_pxe_utils.py
+++ b/ironic/tests/test_pxe_utils.py
@@ -144,7 +144,40 @@ class TestPXEUtils(db_base.DbTestCase):
]
unlink_calls = [
mock.call('/tftpboot/pxelinux.cfg/01-00-11-22-33-44-55-66'),
- mock.call('/tftpboot/pxelinux.cfg/01-00-11-22-33-44-55-67')
+ mock.call('/tftpboot/pxelinux.cfg/01-00-11-22-33-44-55-67'),
+ ]
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ pxe_utils._link_mac_pxe_configs(task)
+
+ unlink_mock.assert_has_calls(unlink_calls)
+ create_link_mock.assert_has_calls(create_link_calls)
+
+ @mock.patch('ironic.common.utils.create_link_without_raise', autospec=True)
+ @mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
+ @mock.patch('ironic.drivers.utils.get_node_mac_addresses', autospec=True)
+ def test__write_mac_ipxe_configs(self, get_macs_mock, unlink_mock,
+ create_link_mock):
+ self.config(ipxe_enabled=True, group='pxe')
+ macs = [
+ '00:11:22:33:44:55:66',
+ '00:11:22:33:44:55:67'
+ ]
+ get_macs_mock.return_value = macs
+ create_link_calls = [
+ mock.call(u'/httpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/config',
+ '/httpboot/pxelinux.cfg/00-11-22-33-44-55-66'),
+ mock.call(u'/httpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/config',
+ '/httpboot/pxelinux.cfg/00112233445566'),
+ mock.call(u'/httpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/config',
+ '/httpboot/pxelinux.cfg/00-11-22-33-44-55-67'),
+ mock.call(u'/httpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/config',
+ '/httpboot/pxelinux.cfg/00112233445567'),
+ ]
+ unlink_calls = [
+ mock.call('/httpboot/pxelinux.cfg/00-11-22-33-44-55-66'),
+ mock.call('/httpboot/pxelinux.cfg/00112233445566'),
+ mock.call('/httpboot/pxelinux.cfg/00-11-22-33-44-55-67'),
+ mock.call('/httpboot/pxelinux.cfg/00112233445567'),
]
with task_manager.acquire(self.context, self.node.uuid) as task:
pxe_utils._link_mac_pxe_configs(task)
@@ -218,7 +251,7 @@ class TestPXEUtils(db_base.DbTestCase):
self.config(ipxe_enabled=True, group='pxe')
self.config(http_root='/httpboot', group='pxe')
mac = '00:11:22:33:AA:BB:CC'
- self.assertEqual('/httpboot/pxelinux.cfg/00112233aabbcc',
+ self.assertEqual('/httpboot/pxelinux.cfg/00-11-22-33-aa-bb-cc',
pxe_utils._get_pxe_mac_path(mac))
def test__get_pxe_ip_address_path(self):