Adds ilo-uefi-https boot interface to ilo5
Change-Id: I224eca4d8b331711369b17903098daa9fec27d7d Story: #2008073 Task: #40761
This commit is contained in:
parent
f1ea2ee6d1
commit
6d36b0b785
@ -62,6 +62,7 @@ features:
|
|||||||
* `Out of Band RAID Support`_
|
* `Out of Band RAID Support`_
|
||||||
* `Out of Band Sanitize Disk Erase Support`_
|
* `Out of Band Sanitize Disk Erase Support`_
|
||||||
* `Out of Band One Button Secure Erase Support`_
|
* `Out of Band One Button Secure Erase Support`_
|
||||||
|
* `UEFI-HTTPS Boot support`_
|
||||||
|
|
||||||
Hardware interfaces
|
Hardware interfaces
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
@ -191,7 +192,8 @@ The ``ilo`` hardware type supports following hardware interfaces:
|
|||||||
|
|
||||||
|
|
||||||
The ``ilo5`` hardware type supports all the ``ilo`` interfaces described above,
|
The ``ilo5`` hardware type supports all the ``ilo`` interfaces described above,
|
||||||
except for ``raid`` interface. The details of ``raid`` interface is as under:
|
except for ``boot`` and ``raid`` interfaces. The details of ``boot`` and
|
||||||
|
``raid`` interfaces is as under:
|
||||||
|
|
||||||
* raid
|
* raid
|
||||||
Supports ``ilo5`` and ``no-raid``. The default is ``ilo5``.
|
Supports ``ilo5`` and ``no-raid``. The default is ``ilo5``.
|
||||||
@ -204,6 +206,19 @@ except for ``raid`` interface. The details of ``raid`` interface is as under:
|
|||||||
enabled_hardware_types = ilo5
|
enabled_hardware_types = ilo5
|
||||||
enabled_raid_interfaces = ilo5,no-raid
|
enabled_raid_interfaces = ilo5,no-raid
|
||||||
|
|
||||||
|
* boot
|
||||||
|
Supports ``ilo-uefi-https`` apart from the other boot interfaces supported
|
||||||
|
by ``ilo`` hardware type.
|
||||||
|
This can be enabled by using the ``[DEFAULT]enabled_boot_interfaces``
|
||||||
|
option in ``ironic.conf`` as given below:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[DEFAULT]
|
||||||
|
enabled_hardware_types = ilo5
|
||||||
|
enabled_boot_interfaces = ilo-uefi-https,ilo-virtual-media
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The ``ilo`` and ``ilo5`` hardware type support all standard ``deploy`` and
|
The ``ilo`` and ``ilo5`` hardware type support all standard ``deploy`` and
|
||||||
``network`` interface implementations, see :ref:`enable-hardware-interfaces`
|
``network`` interface implementations, see :ref:`enable-hardware-interfaces`
|
||||||
@ -290,6 +305,27 @@ Node configuration
|
|||||||
This is optional property and is used when ``rescue`` interface is set to
|
This is optional property and is used when ``rescue`` interface is set to
|
||||||
``agent``.
|
``agent``.
|
||||||
|
|
||||||
|
* The following properties are also required in node object's
|
||||||
|
``driver_info`` if ``ilo-uefi-https`` boot interface is used for ``ilo5``
|
||||||
|
hardware type:
|
||||||
|
|
||||||
|
- ``ilo_deploy_kernel``: The glance UUID or a HTTPS URL of the deployment kernel.
|
||||||
|
- ``ilo_deploy_ramdisk``: The glance UUID or a HTTPS URL of the deployment ramdisk.
|
||||||
|
- ``ilo_bootloader``: The glance UUID or a HTTPS URL of the bootloader.
|
||||||
|
- ``ilo_rescue_kernel``: The glance UUID or a HTTPS URL of the rescue kernel.
|
||||||
|
This is optional property and is used when ``rescue`` interface is set to
|
||||||
|
``agent``.
|
||||||
|
- ``ilo_rescue_ramdisk``: The glance UUID or a HTTP(S) URL of the rescue ramdisk.
|
||||||
|
This is optional property and is used when ``rescue`` interface is set to
|
||||||
|
``agent``.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
``ilo-uefi-https`` boot interface is supported by only ``ilo5`` hardware
|
||||||
|
type. If the images are not hosted in glance, the references
|
||||||
|
must be HTTPS URLs hosted by secure webserver. This boot interface can
|
||||||
|
be used only when the current boot mode is ``UEFI``.
|
||||||
|
|
||||||
|
|
||||||
* The following parameters are mandatory in ``driver_info``
|
* The following parameters are mandatory in ``driver_info``
|
||||||
if ``ilo-inspect`` inspect inteface is used and SNMPv3 inspection
|
if ``ilo-inspect`` inspect inteface is used and SNMPv3 inspection
|
||||||
(`SNMPv3 Authentication` in `HPE iLO4 User Guide`_) is desired:
|
(`SNMPv3 Authentication` in `HPE iLO4 User Guide`_) is desired:
|
||||||
@ -438,7 +474,9 @@ the intermediate floppy image and the boot ISO.
|
|||||||
.. note::
|
.. note::
|
||||||
HTTPS is strongly recommended over HTTP web server configuration for security
|
HTTPS is strongly recommended over HTTP web server configuration for security
|
||||||
enhancement. The ``ilo-virtual-media`` boot interface will send the instance's
|
enhancement. The ``ilo-virtual-media`` boot interface will send the instance's
|
||||||
configdrive over an encrypted channel if web server is HTTPS enabled.
|
configdrive over an encrypted channel if web server is HTTPS enabled. However
|
||||||
|
for ``ilo-uefi-https`` boot interface HTTPS webserver is mandatory as this
|
||||||
|
interface only supports HTTPS URLs.
|
||||||
|
|
||||||
Enable driver
|
Enable driver
|
||||||
=============
|
=============
|
||||||
@ -2081,6 +2119,45 @@ Below are the steps to perform this clean step:
|
|||||||
.. note::
|
.. note::
|
||||||
Do not perform any iLO 5 configuration changes until this process is completed.
|
Do not perform any iLO 5 configuration changes until this process is completed.
|
||||||
|
|
||||||
|
UEFI-HTTPS Boot support
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
The UEFI firmware on Gen10 HPE Proliant servers supports booting from secured URLs.
|
||||||
|
With this capability ``ilo5`` hardware with ``ilo-uefi-https`` boot interface supports
|
||||||
|
deploy/rescue features in more secured environments.
|
||||||
|
|
||||||
|
If swift is used as glance backend and ironic is configured to use swift to store
|
||||||
|
temporary images, it is required that swift is configured on HTTPS so that the tempurl
|
||||||
|
generated is HTTPS URL.
|
||||||
|
|
||||||
|
If the webserver is used for hosting the temporary images, then the webserver is required
|
||||||
|
to serve requests on HTTPS.
|
||||||
|
|
||||||
|
If the images are hosted on a HTTPS webserver or swift configured with HTTPS with
|
||||||
|
custom certificates, the user is required to export SSL certificates into iLO.
|
||||||
|
Refer to `HPE Integrated Lights-Out Security Technology Brief`_ for more information.
|
||||||
|
|
||||||
|
The following command can be used to enroll a ProLiant node with ``ilo5`` hardware type
|
||||||
|
and ``ilo-uefi-https`` boot interface:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
openstack baremetal node create \
|
||||||
|
--driver ilo5 \
|
||||||
|
--boot-interface ilo-uefi-https \
|
||||||
|
--deploy-interface direct \
|
||||||
|
--raid-interface ilo5 \
|
||||||
|
--rescue-interface agent \
|
||||||
|
--driver-info ilo_address=<ilo-ip-address> \
|
||||||
|
--driver-info ilo_username=<ilo-username> \
|
||||||
|
--driver-info ilo_password=<ilo-password> \
|
||||||
|
--driver-info ilo_deploy_kernel=<glance-uuid-of-deploy-kernel> \
|
||||||
|
--driver-info ilo_deploy_ramdisk=<glance-uuid-of-rescue-ramdisk> \
|
||||||
|
--driver-info ilo_bootloader=<glance-uuid-of-bootloader>
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
UEFI secure boot is not supported with ``ilo-uefi-https`` boot interface.
|
||||||
|
|
||||||
|
|
||||||
.. _`ssacli documentation`: https://support.hpe.com/hpsc/doc/public/display?docId=c03909334
|
.. _`ssacli documentation`: https://support.hpe.com/hpsc/doc/public/display?docId=c03909334
|
||||||
.. _`proliant-tools`: https://docs.openstack.org/diskimage-builder/latest/elements/proliant-tools/README.html
|
.. _`proliant-tools`: https://docs.openstack.org/diskimage-builder/latest/elements/proliant-tools/README.html
|
||||||
.. _`HPE iLO4 User Guide`: https://h20566.www2.hpe.com/hpsc/doc/public/display?docId=c03334051
|
.. _`HPE iLO4 User Guide`: https://h20566.www2.hpe.com/hpsc/doc/public/display?docId=c03334051
|
||||||
@ -2093,3 +2170,4 @@ Below are the steps to perform this clean step:
|
|||||||
.. _`SUM`: https://h17007.www1.hpe.com/us/en/enterprise/servers/products/service_pack/hpsum/index.aspx
|
.. _`SUM`: https://h17007.www1.hpe.com/us/en/enterprise/servers/products/service_pack/hpsum/index.aspx
|
||||||
.. _`SUM User Guide`: https://h20565.www2.hpe.com/hpsc/doc/public/display?docId=c05210448
|
.. _`SUM User Guide`: https://h20565.www2.hpe.com/hpsc/doc/public/display?docId=c05210448
|
||||||
.. [1] `ironic-python-agent-builder`: https://docs.openstack.org/ironic-python-agent-builder/latest/install/index.html
|
.. [1] `ironic-python-agent-builder`: https://docs.openstack.org/ironic-python-agent-builder/latest/install/index.html
|
||||||
|
.. _`HPE Integrated Lights-Out Security Technology Brief`: http://h20564.www2.hpe.com/hpsc/doc/public/display?docId=c04530504
|
||||||
|
@ -103,6 +103,14 @@ opts = [
|
|||||||
'"auto" for backward compatibility. When "auto" is '
|
'"auto" for backward compatibility. When "auto" is '
|
||||||
'specified, default boot mode will be selected based '
|
'specified, default boot mode will be selected based '
|
||||||
'on boot mode settings on the system.')),
|
'on boot mode settings on the system.')),
|
||||||
|
cfg.IntOpt('file_permission',
|
||||||
|
default=0o644,
|
||||||
|
help=_('File permission for swift-less image hosting with the '
|
||||||
|
'octal permission representation of file access '
|
||||||
|
'permissions. This setting defaults to ``644``, '
|
||||||
|
'or as the octal number ``0o644`` in Python. '
|
||||||
|
'This setting must be set to the octal number '
|
||||||
|
'representation, meaning starting with ``0o``.')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,6 +77,12 @@ class Ilo5Hardware(IloHardware):
|
|||||||
iLO5 hardware type is targeted for iLO5 based Proliant Gen10 servers.
|
iLO5 hardware type is targeted for iLO5 based Proliant Gen10 servers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_boot_interfaces(self):
|
||||||
|
"""List of supported boot interfaces."""
|
||||||
|
return super(Ilo5Hardware,
|
||||||
|
self).supported_boot_interfaces + [boot.IloUefiHttpsBoot]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_raid_interfaces(self):
|
def supported_raid_interfaces(self):
|
||||||
"""List of supported raid interfaces."""
|
"""List of supported raid interfaces."""
|
||||||
|
@ -38,6 +38,7 @@ from ironic.drivers import base
|
|||||||
from ironic.drivers.modules import boot_mode_utils
|
from ironic.drivers.modules import boot_mode_utils
|
||||||
from ironic.drivers.modules import deploy_utils
|
from ironic.drivers.modules import deploy_utils
|
||||||
from ironic.drivers.modules.ilo import common as ilo_common
|
from ironic.drivers.modules.ilo import common as ilo_common
|
||||||
|
from ironic.drivers.modules import image_utils
|
||||||
from ironic.drivers.modules import ipxe
|
from ironic.drivers.modules import ipxe
|
||||||
from ironic.drivers.modules import pxe
|
from ironic.drivers.modules import pxe
|
||||||
|
|
||||||
@ -56,6 +57,29 @@ RESCUE_PROPERTIES = {
|
|||||||
"required if rescue mode is being used and ironic is "
|
"required if rescue mode is being used and ironic is "
|
||||||
"managing booting the rescue ramdisk.")
|
"managing booting the rescue ramdisk.")
|
||||||
}
|
}
|
||||||
|
REQUIRED_PROPERTIES_UEFI_HTTPS_BOOT = {
|
||||||
|
'ilo_deploy_kernel': _("URL or Glance UUID of the deployment kernel. "
|
||||||
|
"Required."),
|
||||||
|
'ilo_deploy_ramdisk': _("URL or Glance UUID of the ramdisk that is "
|
||||||
|
"mounted at boot time. Required."),
|
||||||
|
'ilo_bootloader': _("URL or Glance UUID of the EFI system partition "
|
||||||
|
"image containing EFI boot loader. This image will "
|
||||||
|
"be used by ironic when building UEFI-bootable ISO "
|
||||||
|
"out of kernel and ramdisk. Required for UEFI "
|
||||||
|
"boot from partition images.")
|
||||||
|
}
|
||||||
|
RESCUE_PROPERTIES_UEFI_HTTPS_BOOT = {
|
||||||
|
'ilo_rescue_kernel': _('URL or Glance UUID of the rescue kernel. This '
|
||||||
|
'value is required for rescue mode.'),
|
||||||
|
'ilo_rescue_ramdisk': _('URL or Glance UUID of the rescue ramdisk with '
|
||||||
|
'agent that is used at node rescue time. '
|
||||||
|
'The value is required for rescue mode.'),
|
||||||
|
'ilo_bootloader': _("URL or Glance UUID of the EFI system partition "
|
||||||
|
"image containing EFI boot loader. This image will "
|
||||||
|
"be used by ironic when building UEFI-bootable ISO "
|
||||||
|
"out of kernel and ramdisk. Required for UEFI "
|
||||||
|
"boot from partition images.")
|
||||||
|
}
|
||||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES
|
COMMON_PROPERTIES = REQUIRED_PROPERTIES
|
||||||
|
|
||||||
|
|
||||||
@ -871,3 +895,363 @@ class IloiPXEBoot(ipxe.iPXEBoot):
|
|||||||
# Volume boot in BIOS boot mode is handled using
|
# Volume boot in BIOS boot mode is handled using
|
||||||
# PXE boot interface
|
# PXE boot interface
|
||||||
super(IloiPXEBoot, self).clean_up_instance(task)
|
super(IloiPXEBoot, self).clean_up_instance(task)
|
||||||
|
|
||||||
|
|
||||||
|
class IloUefiHttpsBoot(base.BootInterface):
|
||||||
|
|
||||||
|
capabilities = ['ramdisk_boot']
|
||||||
|
|
||||||
|
def get_properties(self):
|
||||||
|
"""Return the properties of the interface.
|
||||||
|
|
||||||
|
:returns: dictionary of <property name>:<property description> entries.
|
||||||
|
"""
|
||||||
|
return REQUIRED_PROPERTIES_UEFI_HTTPS_BOOT
|
||||||
|
|
||||||
|
def _validate_hrefs(self, image_dict):
|
||||||
|
"""Validates if the given URLs are secured URLs.
|
||||||
|
|
||||||
|
If the given URLs are not glance images then validates if the URLs
|
||||||
|
are secured.
|
||||||
|
|
||||||
|
:param image_dict: a dictionary containing property/URL pair.
|
||||||
|
:returns: None
|
||||||
|
:raises: InvalidParameterValue, if any of URLs provided are insecure.
|
||||||
|
"""
|
||||||
|
insecure_props = []
|
||||||
|
|
||||||
|
for prop in image_dict:
|
||||||
|
image_ref = image_dict.get(prop)
|
||||||
|
if not service_utils.is_glance_image(image_ref):
|
||||||
|
prefix = urlparse.urlparse(image_ref).scheme.lower()
|
||||||
|
if prefix == 'http':
|
||||||
|
insecure_props.append(prop)
|
||||||
|
|
||||||
|
if len(insecure_props) > 0:
|
||||||
|
error = (_('Secure URLs exposed over HTTPS are expected. '
|
||||||
|
'Insecure URLs are provided for %s') % insecure_props)
|
||||||
|
raise exception.InvalidParameterValue(error)
|
||||||
|
|
||||||
|
def _parse_deploy_info(self, node):
|
||||||
|
"""Gets the instance and driver specific Node deployment info.
|
||||||
|
|
||||||
|
This method validates whether the 'instance_info' and 'driver_info'
|
||||||
|
property of the supplied node contains the required information for
|
||||||
|
this driver to deploy images to the node.
|
||||||
|
|
||||||
|
:param node: a target node of the deployment
|
||||||
|
:returns: a dict with the instance_info and driver_info values.
|
||||||
|
:raises: MissingParameterValue, if any of the required parameters are
|
||||||
|
missing.
|
||||||
|
:raises: InvalidParameterValue, if any of the parameters have invalid
|
||||||
|
value.
|
||||||
|
"""
|
||||||
|
deploy_info = {}
|
||||||
|
deploy_info.update(deploy_utils.get_image_instance_info(node))
|
||||||
|
deploy_info.update(self._parse_driver_info(node))
|
||||||
|
|
||||||
|
return deploy_info
|
||||||
|
|
||||||
|
def _parse_driver_info(self, node, mode='deploy'):
|
||||||
|
"""Gets the node specific deploy/rescue info.
|
||||||
|
|
||||||
|
This method validates whether the 'driver_info' property of the
|
||||||
|
supplied node contains the required information for this driver to
|
||||||
|
deploy images to the node.
|
||||||
|
|
||||||
|
:param node: a single Node.
|
||||||
|
:param mode: Label indicating a deploy or rescue operation being
|
||||||
|
carried out on the node. Supported values are 'deploy' and
|
||||||
|
'rescue'. Defaults to 'deploy', indicating deploy operation
|
||||||
|
is being carried out.
|
||||||
|
:returns: A dict with the driver_info values.
|
||||||
|
:raises: MissingParameterValue, if any of the required parameters are
|
||||||
|
missing.
|
||||||
|
"""
|
||||||
|
info = node.driver_info
|
||||||
|
|
||||||
|
if mode == 'rescue':
|
||||||
|
params_to_check = RESCUE_PROPERTIES_UEFI_HTTPS_BOOT.keys()
|
||||||
|
else:
|
||||||
|
params_to_check = REQUIRED_PROPERTIES_UEFI_HTTPS_BOOT.keys()
|
||||||
|
|
||||||
|
deploy_info = {option: info.get(option)
|
||||||
|
for option in params_to_check}
|
||||||
|
|
||||||
|
self._validate_hrefs(deploy_info)
|
||||||
|
|
||||||
|
error_msg = (_("Error validating %s for iLO UEFI HTTPS boot. Some "
|
||||||
|
"parameters were missing in node's driver_info") % mode)
|
||||||
|
deploy_utils.check_for_missing_params(deploy_info, error_msg)
|
||||||
|
|
||||||
|
deploy_info.update(ilo_common.parse_driver_info(node))
|
||||||
|
|
||||||
|
return deploy_info
|
||||||
|
|
||||||
|
def _validate_driver_info(self, task):
|
||||||
|
"""Validates the prerequisites for ilo-uefi-https boot interface.
|
||||||
|
|
||||||
|
This method validates whether the 'driver_info' property of the
|
||||||
|
supplied node contains the required information for this driver.
|
||||||
|
|
||||||
|
:param task: a TaskManager instance containing the node to act on.
|
||||||
|
:raises: InvalidParameterValue if any parameters are incorrect
|
||||||
|
:raises: MissingParameterValue if some mandatory information
|
||||||
|
is missing on the node
|
||||||
|
"""
|
||||||
|
node = task.node
|
||||||
|
|
||||||
|
self._parse_driver_info(node)
|
||||||
|
|
||||||
|
def _validate_instance_image_info(self, task):
|
||||||
|
"""Validate instance image information for the task's node.
|
||||||
|
|
||||||
|
:param task: a TaskManager instance containing the node to act on.
|
||||||
|
:raises: InvalidParameterValue, if some information is invalid.
|
||||||
|
:raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are
|
||||||
|
missing in the Glance image or 'kernel' and 'ramdisk' not provided
|
||||||
|
in instance_info for non-Glance image.
|
||||||
|
"""
|
||||||
|
node = task.node
|
||||||
|
|
||||||
|
d_info = deploy_utils.get_image_instance_info(node)
|
||||||
|
|
||||||
|
self._validate_hrefs(d_info)
|
||||||
|
|
||||||
|
if node.driver_internal_info.get('is_whole_disk_image'):
|
||||||
|
props = []
|
||||||
|
elif service_utils.is_glance_image(d_info['image_source']):
|
||||||
|
props = ['kernel_id', 'ramdisk_id']
|
||||||
|
else:
|
||||||
|
props = ['kernel', 'ramdisk']
|
||||||
|
deploy_utils.validate_image_properties(task.context, d_info, props)
|
||||||
|
|
||||||
|
@METRICS.timer('IloUefiHttpsBoot.validate')
|
||||||
|
def validate(self, task):
|
||||||
|
"""Validate the deployment information for the task's node.
|
||||||
|
|
||||||
|
This method validates whether the 'driver_info' and/or 'instance_info'
|
||||||
|
properties of the task's node contains the required information for
|
||||||
|
this interface to function.
|
||||||
|
|
||||||
|
:param task: A TaskManager instance containing the node to act on.
|
||||||
|
:raises: InvalidParameterValue on malformed parameter(s)
|
||||||
|
:raises: MissingParameterValue on missing parameter(s)
|
||||||
|
"""
|
||||||
|
node = task.node
|
||||||
|
boot_option = deploy_utils.get_boot_option(node)
|
||||||
|
try:
|
||||||
|
boot_mode = ilo_common.get_current_boot_mode(task.node)
|
||||||
|
except exception.IloOperationError:
|
||||||
|
error = _("Validation for 'ilo-uefi-https' boot interface failed. "
|
||||||
|
"Could not determine current boot mode for node "
|
||||||
|
"%(node)s.") % node.uuid
|
||||||
|
raise exception.InvalidParameterValue(error)
|
||||||
|
|
||||||
|
if boot_mode.lower() != 'uefi':
|
||||||
|
error = _("Validation for 'ilo-uefi-https' boot interface failed. "
|
||||||
|
"The node is required to be in 'UEFI' boot mode.")
|
||||||
|
raise exception.InvalidParameterValue(error)
|
||||||
|
|
||||||
|
boot_iso = node.instance_info.get('ilo_boot_iso')
|
||||||
|
if (boot_option == "ramdisk" and boot_iso):
|
||||||
|
if not service_utils.is_glance_image(boot_iso):
|
||||||
|
try:
|
||||||
|
image_service.HttpImageService().validate_href(boot_iso)
|
||||||
|
except exception.ImageRefValidationFailed:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error("UEFI-HTTPS boot with 'ramdisk' "
|
||||||
|
"boot_option accepts only Glance images or "
|
||||||
|
"HTTPS URLs as "
|
||||||
|
"instance_info['ilo_boot_iso']. Either %s "
|
||||||
|
"is not a valid HTTPS URL or is not "
|
||||||
|
"reachable.", boot_iso)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._validate_driver_info(task)
|
||||||
|
|
||||||
|
if task.driver.storage.should_write_image(task):
|
||||||
|
self._validate_instance_image_info(task)
|
||||||
|
|
||||||
|
def validate_inspection(self, task):
|
||||||
|
"""Validate that the node has required properties for inspection.
|
||||||
|
|
||||||
|
:param task: A TaskManager instance with the node being checked
|
||||||
|
:raises: MissingParameterValue if node is missing one or more required
|
||||||
|
parameters
|
||||||
|
:raises: UnsupportedDriverExtension
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._validate_driver_info(task)
|
||||||
|
except exception.MissingParameterValue:
|
||||||
|
# Fall back to non-managed in-band inspection
|
||||||
|
raise exception.UnsupportedDriverExtension(
|
||||||
|
driver=task.node.driver, extension='inspection')
|
||||||
|
|
||||||
|
@METRICS.timer('IloUefiHttpsBoot.prepare_ramdisk')
|
||||||
|
def prepare_ramdisk(self, task, ramdisk_params):
|
||||||
|
"""Prepares the boot of deploy ramdisk using UEFI-HTTPS boot.
|
||||||
|
|
||||||
|
This method prepares the boot of the deploy or rescue ramdisk after
|
||||||
|
reading relevant information from the node's driver_info and
|
||||||
|
instance_info.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager.
|
||||||
|
:param ramdisk_params: the parameters to be passed to the ramdisk.
|
||||||
|
:returns: None
|
||||||
|
:raises: MissingParameterValue, if some information is missing in
|
||||||
|
node's driver_info or instance_info.
|
||||||
|
:raises: InvalidParameterValue, if some information provided is
|
||||||
|
invalid.
|
||||||
|
:raises: IronicException, if some power or set boot boot device
|
||||||
|
operation failed on the node.
|
||||||
|
:raises: IloOperationError, if some operation on iLO failed.
|
||||||
|
"""
|
||||||
|
node = task.node
|
||||||
|
# NOTE(TheJulia): If this method is being called by something
|
||||||
|
# aside from deployment, clean and rescue, such as conductor takeover,
|
||||||
|
# we should treat this as a no-op and move on otherwise we would
|
||||||
|
# modify the state of the node due to virtual media operations.
|
||||||
|
if node.provision_state not in (states.DEPLOYING,
|
||||||
|
states.CLEANING,
|
||||||
|
states.RESCUING,
|
||||||
|
states.INSPECTING):
|
||||||
|
return
|
||||||
|
|
||||||
|
prepare_node_for_deploy(task)
|
||||||
|
|
||||||
|
# Clear ilo_boot_iso if it's a glance image to force recreate
|
||||||
|
# another one again (or use existing one in glance).
|
||||||
|
# This is mainly for rebuild and rescue scenario.
|
||||||
|
if service_utils.is_glance_image(
|
||||||
|
node.instance_info.get('image_source')):
|
||||||
|
instance_info = node.instance_info
|
||||||
|
instance_info.pop('ilo_boot_iso', None)
|
||||||
|
node.instance_info = instance_info
|
||||||
|
node.save()
|
||||||
|
|
||||||
|
# NOTE(TheJulia): Since we're deploying, cleaning, or rescuing,
|
||||||
|
# with virtual media boot, we should generate a token!
|
||||||
|
manager_utils.add_secret_token(node, pregenerated=True)
|
||||||
|
ramdisk_params['ipa-agent-token'] = \
|
||||||
|
task.node.driver_internal_info['agent_secret_token']
|
||||||
|
task.node.save()
|
||||||
|
|
||||||
|
deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
|
||||||
|
ramdisk_params['BOOTIF'] = deploy_nic_mac
|
||||||
|
|
||||||
|
mode = 'deploy'
|
||||||
|
if node.provision_state == states.RESCUING:
|
||||||
|
mode = 'rescue'
|
||||||
|
|
||||||
|
d_info = self._parse_driver_info(node, mode)
|
||||||
|
|
||||||
|
iso_ref = image_utils.prepare_deploy_iso(task, ramdisk_params,
|
||||||
|
mode, d_info)
|
||||||
|
|
||||||
|
LOG.debug("Set 'UEFIHTTP' as one time boot option on the node "
|
||||||
|
"%(node)s to boot from URL %(iso_ref)s.",
|
||||||
|
{'node': node.uuid, 'iso_ref': iso_ref})
|
||||||
|
|
||||||
|
ilo_common.setup_uefi_https(task, iso_ref)
|
||||||
|
|
||||||
|
@METRICS.timer('IloUefiHttpsBoot.clean_up_ramdisk')
|
||||||
|
def clean_up_ramdisk(self, task):
|
||||||
|
"""Cleans up the boot of ironic ramdisk.
|
||||||
|
|
||||||
|
This method cleans up the environment that was setup for booting the
|
||||||
|
deploy ramdisk.
|
||||||
|
|
||||||
|
:param task: A task from TaskManager.
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
LOG.debug("Cleaning up deploy boot for "
|
||||||
|
"%(node)s", {'node': task.node.uuid})
|
||||||
|
|
||||||
|
image_utils.cleanup_iso_image(task)
|
||||||
|
|
||||||
|
@METRICS.timer('IloUefiHttpsBoot.prepare_instance')
|
||||||
|
def prepare_instance(self, task):
|
||||||
|
"""Prepares the boot of instance.
|
||||||
|
|
||||||
|
This method prepares the boot of the instance after reading
|
||||||
|
relevant information from the node's instance_info.
|
||||||
|
It does the following depending on boot_option for deploy:
|
||||||
|
|
||||||
|
- If the boot_option requested for this deploy is 'local' or image is
|
||||||
|
a whole disk image, then it sets the node to boot from disk.
|
||||||
|
- Otherwise it finds/creates the boot ISO, sets the node boot option
|
||||||
|
to UEFIHTTP and sets the URL as the boot ISO to boot the instance
|
||||||
|
image.
|
||||||
|
|
||||||
|
:param task: a task from TaskManager.
|
||||||
|
:returns: None
|
||||||
|
:raises: IloOperationError, if some operation on iLO failed.
|
||||||
|
:raises: InstanceDeployFailure, if its try to boot iSCSI volume in
|
||||||
|
'BIOS' boot mode.
|
||||||
|
"""
|
||||||
|
node = task.node
|
||||||
|
image_utils.cleanup_iso_image(task)
|
||||||
|
boot_option = deploy_utils.get_boot_option(task.node)
|
||||||
|
|
||||||
|
iwdi = node.driver_internal_info.get('is_whole_disk_image')
|
||||||
|
if boot_option == "local" or iwdi:
|
||||||
|
manager_utils.node_set_boot_device(task, boot_devices.DISK,
|
||||||
|
persistent=True)
|
||||||
|
LOG.debug("Node %(node)s is set to permanently boot from local "
|
||||||
|
"%(device)s", {'node': task.node.uuid,
|
||||||
|
'device': boot_devices.DISK})
|
||||||
|
return
|
||||||
|
|
||||||
|
params = {}
|
||||||
|
|
||||||
|
if boot_option != 'ramdisk':
|
||||||
|
root_uuid = node.driver_internal_info.get('root_uuid_or_disk_id')
|
||||||
|
if not root_uuid and task.driver.storage.should_write_image(task):
|
||||||
|
LOG.warning(
|
||||||
|
"The UUID of the root partition could not be found for "
|
||||||
|
"node %s. Booting instance from disk anyway.", node.uuid)
|
||||||
|
manager_utils.node_set_boot_device(task, boot_devices.DISK,
|
||||||
|
persistent=True)
|
||||||
|
|
||||||
|
return
|
||||||
|
params.update(root_uuid=root_uuid)
|
||||||
|
|
||||||
|
d_info = self._parse_deploy_info(node)
|
||||||
|
iso_ref = image_utils.prepare_boot_iso(task, d_info, **params)
|
||||||
|
|
||||||
|
if boot_option != 'ramdisk':
|
||||||
|
i_info = node.instance_info
|
||||||
|
i_info['ilo_boot_iso'] = iso_ref
|
||||||
|
node.instance_info = i_info
|
||||||
|
node.save()
|
||||||
|
|
||||||
|
ilo_common.setup_uefi_https(task, iso_ref, persistent=True)
|
||||||
|
|
||||||
|
LOG.debug("Node %(node)s is set to boot from UEFIHTTP "
|
||||||
|
"boot option", {'node': task.node.uuid})
|
||||||
|
|
||||||
|
@METRICS.timer('IloUefiHttpsBoot.clean_up_instance')
|
||||||
|
def clean_up_instance(self, task):
|
||||||
|
"""Cleans up the boot of instance.
|
||||||
|
|
||||||
|
This method cleans up the environment that was setup for booting
|
||||||
|
the instance.
|
||||||
|
|
||||||
|
:param task: A task from TaskManager.
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
LOG.debug("Cleaning up instance boot for "
|
||||||
|
"%(node)s", {'node': task.node.uuid})
|
||||||
|
|
||||||
|
image_utils.cleanup_iso_image(task)
|
||||||
|
|
||||||
|
@METRICS.timer('IloUefiHttpsBoot.validate_rescue')
|
||||||
|
def validate_rescue(self, task):
|
||||||
|
"""Validate that the node has required properties for rescue.
|
||||||
|
|
||||||
|
:param task: a TaskManager instance with the node being checked
|
||||||
|
:raises: MissingParameterValue if node is missing one or more required
|
||||||
|
parameters
|
||||||
|
"""
|
||||||
|
self._parse_driver_info(task.node, mode='rescue')
|
||||||
|
@ -922,3 +922,49 @@ def get_server_post_state(node):
|
|||||||
except ilo_error.IloError as ilo_exception:
|
except ilo_error.IloError as ilo_exception:
|
||||||
raise exception.IloOperationError(operation=operation,
|
raise exception.IloOperationError(operation=operation,
|
||||||
error=ilo_exception)
|
error=ilo_exception)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_uefi_https(task, iso, persistent=False):
|
||||||
|
"""Sets up system to boot from UEFIHTTP boot device.
|
||||||
|
|
||||||
|
Sets the one-time/persistent boot device to UEFIHTTP based
|
||||||
|
on the argument supplied.
|
||||||
|
|
||||||
|
:param task: a TaskManager instance containing the node to act on.
|
||||||
|
:param iso: ISO URL to be set to boot from.
|
||||||
|
:param persistent: Indicates whether the system should be set to boot
|
||||||
|
from the given device one-time or each time.
|
||||||
|
:raises: IloOperationError on an error from IloClient library.
|
||||||
|
:raises: IloOperationNotSupported if retrieving post state is not
|
||||||
|
supported on the server.
|
||||||
|
"""
|
||||||
|
node = task.node
|
||||||
|
ilo_object = get_ilo_object(node)
|
||||||
|
scheme = urlparse.urlparse(iso).scheme.lower()
|
||||||
|
|
||||||
|
operation = (_("Setting up node %(node)s to boot from URL %(iso)s.") %
|
||||||
|
{'iso': iso, 'node': node.uuid})
|
||||||
|
|
||||||
|
if scheme != 'https':
|
||||||
|
msg = (_('Error setting up node %(node)s to boot from '
|
||||||
|
'URL %(iso)s. A secure URL is expected that is exposed '
|
||||||
|
'over HTTPS.') %
|
||||||
|
{'node': node.uuid, 'iso': iso})
|
||||||
|
raise exception.IloOperationNotSupported(operation=operation,
|
||||||
|
error=msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ilo_object.set_http_boot_url(iso)
|
||||||
|
LOG.info("Set the node %(node)s to boot from URL %(iso)s "
|
||||||
|
"successfully.", {'node': node.uuid, 'iso': iso})
|
||||||
|
if not persistent:
|
||||||
|
ilo_object.set_one_time_boot('UEFIHTTP')
|
||||||
|
else:
|
||||||
|
ilo_object.update_persistent_boot(['UEFIHTTP'])
|
||||||
|
|
||||||
|
except ilo_error.IloCommandNotSupportedInBiosError as ilo_exception:
|
||||||
|
raise exception.IloOperationNotSupported(operation=operation,
|
||||||
|
error=ilo_exception)
|
||||||
|
except ilo_error.IloError as ilo_exception:
|
||||||
|
raise exception.IloOperationError(operation=operation,
|
||||||
|
error=ilo_exception)
|
||||||
|
@ -44,7 +44,14 @@ class ImageHandler(object):
|
|||||||
"timeout": CONF.redfish.swift_object_expiry_timeout,
|
"timeout": CONF.redfish.swift_object_expiry_timeout,
|
||||||
"image_subdir": "redfish",
|
"image_subdir": "redfish",
|
||||||
"file_permission": CONF.redfish.file_permission
|
"file_permission": CONF.redfish.file_permission
|
||||||
}
|
},
|
||||||
|
"ilo5": {
|
||||||
|
"swift_enabled": not CONF.ilo.use_web_server_for_images,
|
||||||
|
"container": CONF.ilo.swift_ilo_container,
|
||||||
|
"timeout": CONF.ilo.swift_object_expiry_timeout,
|
||||||
|
"image_subdir": "ilo",
|
||||||
|
"file_permission": CONF.ilo.file_permission
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, driver):
|
def __init__(self, driver):
|
||||||
@ -380,6 +387,14 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href,
|
|||||||
return image_url
|
return image_url
|
||||||
|
|
||||||
|
|
||||||
|
def _find_param(param_str, param_dict):
|
||||||
|
val = None
|
||||||
|
for param_key in param_dict:
|
||||||
|
if param_str in param_key:
|
||||||
|
val = param_dict.get(param_key)
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
def prepare_deploy_iso(task, params, mode, d_info):
|
def prepare_deploy_iso(task, params, mode, d_info):
|
||||||
"""Prepare deploy or rescue ISO image
|
"""Prepare deploy or rescue ISO image
|
||||||
|
|
||||||
@ -406,9 +421,13 @@ def prepare_deploy_iso(task, params, mode, d_info):
|
|||||||
:raises: ImageCreationFailed, if creating ISO image failed.
|
:raises: ImageCreationFailed, if creating ISO image failed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kernel_href = d_info.get('%s_kernel' % mode)
|
kernel_str = '%s_kernel' % mode
|
||||||
ramdisk_href = d_info.get('%s_ramdisk' % mode)
|
ramdisk_str = '%s_ramdisk' % mode
|
||||||
bootloader_href = d_info.get('bootloader')
|
bootloader_str = 'bootloader'
|
||||||
|
|
||||||
|
kernel_href = _find_param(kernel_str, d_info)
|
||||||
|
ramdisk_href = _find_param(ramdisk_str, d_info)
|
||||||
|
bootloader_href = _find_param(bootloader_str, d_info)
|
||||||
|
|
||||||
# TODO(TheJulia): At some point we should support something like
|
# TODO(TheJulia): At some point we should support something like
|
||||||
# boot_iso for the deploy interface, perhaps when we support config
|
# boot_iso for the deploy interface, perhaps when we support config
|
||||||
@ -494,7 +513,8 @@ def prepare_boot_iso(task, d_info, root_uuid=None):
|
|||||||
"to generate boot ISO for %(node)s") %
|
"to generate boot ISO for %(node)s") %
|
||||||
{'node': task.node.uuid})
|
{'node': task.node.uuid})
|
||||||
|
|
||||||
bootloader_href = d_info.get('bootloader')
|
bootloader_str = 'bootloader'
|
||||||
|
bootloader_href = _find_param(bootloader_str, d_info)
|
||||||
|
|
||||||
return _prepare_iso_image(
|
return _prepare_iso_image(
|
||||||
task, kernel_href, ramdisk_href, bootloader_href,
|
task, kernel_href, ramdisk_href, bootloader_href,
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import io
|
import io
|
||||||
import tempfile
|
import tempfile
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
from urllib import parse as urlparse
|
||||||
|
|
||||||
from ironic_lib import utils as ironic_utils
|
from ironic_lib import utils as ironic_utils
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -36,14 +37,19 @@ from ironic.drivers.modules import deploy_utils
|
|||||||
from ironic.drivers.modules.ilo import boot as ilo_boot
|
from ironic.drivers.modules.ilo import boot as ilo_boot
|
||||||
from ironic.drivers.modules.ilo import common as ilo_common
|
from ironic.drivers.modules.ilo import common as ilo_common
|
||||||
from ironic.drivers.modules.ilo import management as ilo_management
|
from ironic.drivers.modules.ilo import management as ilo_management
|
||||||
|
from ironic.drivers.modules import image_utils
|
||||||
from ironic.drivers.modules import ipxe
|
from ironic.drivers.modules import ipxe
|
||||||
from ironic.drivers.modules import pxe
|
from ironic.drivers.modules import pxe
|
||||||
from ironic.drivers.modules.storage import noop as noop_storage
|
from ironic.drivers.modules.storage import noop as noop_storage
|
||||||
from ironic.drivers import utils as driver_utils
|
from ironic.drivers import utils as driver_utils
|
||||||
|
from ironic.tests.unit.db import base as db_base
|
||||||
|
from ironic.tests.unit.db import utils as db_utils
|
||||||
from ironic.tests.unit.drivers.modules.ilo import test_common
|
from ironic.tests.unit.drivers.modules.ilo import test_common
|
||||||
|
from ironic.tests.unit.objects import utils as obj_utils
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
INFO_DICT = db_utils.get_test_ilo_info()
|
||||||
|
|
||||||
|
|
||||||
class IloBootCommonMethodsTestCase(test_common.BaseIloTest):
|
class IloBootCommonMethodsTestCase(test_common.BaseIloTest):
|
||||||
@ -1591,3 +1597,746 @@ class IloiPXEBootTestCase(test_common.BaseIloTest):
|
|||||||
update_secure_boot_mode_mock.assert_called_once_with(task, True)
|
update_secure_boot_mode_mock.assert_called_once_with(task, True)
|
||||||
self.assertTrue(task.node.driver_internal_info.get(
|
self.assertTrue(task.node.driver_internal_info.get(
|
||||||
'ilo_uefi_iscsi_boot'))
|
'ilo_uefi_iscsi_boot'))
|
||||||
|
|
||||||
|
|
||||||
|
class IloUefiHttpsBootTestCase(db_base.DbTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(IloUefiHttpsBootTestCase, self).setUp()
|
||||||
|
self.driver = mock.Mock(boot=ilo_boot.IloUefiHttpsBoot())
|
||||||
|
n = {
|
||||||
|
'driver': 'ilo5',
|
||||||
|
'driver_info': INFO_DICT
|
||||||
|
}
|
||||||
|
self.config(enabled_hardware_types=['ilo5'],
|
||||||
|
enabled_boot_interfaces=['ilo-uefi-https'],
|
||||||
|
enabled_console_interfaces=['ilo'],
|
||||||
|
enabled_deploy_interfaces=['iscsi'],
|
||||||
|
enabled_inspect_interfaces=['ilo'],
|
||||||
|
enabled_management_interfaces=['ilo5'],
|
||||||
|
enabled_power_interfaces=['ilo'],
|
||||||
|
enabled_raid_interfaces=['ilo5'])
|
||||||
|
self.node = obj_utils.create_test_node(self.context, **n)
|
||||||
|
|
||||||
|
@mock.patch.object(urlparse, 'urlparse', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__validate_hrefs_https_image(self, is_glance_mock, urlparse_mock):
|
||||||
|
is_glance_mock.return_value = False
|
||||||
|
urlparse_mock.return_value.scheme = 'https'
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
data = {
|
||||||
|
'ilo_deploy_kernel': 'https://a.b.c.d/kernel',
|
||||||
|
'ilo_deploy_ramdisk': 'https://a.b.c.d/ramdisk',
|
||||||
|
'ilo_bootloader': 'https://a.b.c.d/bootloader'
|
||||||
|
}
|
||||||
|
task.driver.boot._validate_hrefs(data)
|
||||||
|
|
||||||
|
glance_calls = [
|
||||||
|
mock.call('https://a.b.c.d/kernel'),
|
||||||
|
mock.call('https://a.b.c.d/ramdisk'),
|
||||||
|
mock.call('https://a.b.c.d/bootloader')
|
||||||
|
]
|
||||||
|
|
||||||
|
is_glance_mock.assert_has_calls(glance_calls)
|
||||||
|
urlparse_mock.assert_has_calls(glance_calls)
|
||||||
|
|
||||||
|
@mock.patch.object(urlparse, 'urlparse', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__validate_hrefs_http_image(self, is_glance_mock, urlparse_mock):
|
||||||
|
is_glance_mock.return_value = False
|
||||||
|
scheme_mock = mock.PropertyMock(
|
||||||
|
side_effect=['http', 'https', 'http'])
|
||||||
|
type(urlparse_mock.return_value).scheme = scheme_mock
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
data = {
|
||||||
|
'ilo_deploy_kernel': 'http://a.b.c.d/kernel',
|
||||||
|
'ilo_deploy_ramdisk': 'https://a.b.c.d/ramdisk',
|
||||||
|
'ilo_bootloader': 'http://a.b.c.d/bootloader'
|
||||||
|
}
|
||||||
|
|
||||||
|
glance_calls = [
|
||||||
|
mock.call('http://a.b.c.d/kernel'),
|
||||||
|
mock.call('https://a.b.c.d/ramdisk'),
|
||||||
|
mock.call('http://a.b.c.d/bootloader')
|
||||||
|
]
|
||||||
|
self.assertRaisesRegex(exception.InvalidParameterValue,
|
||||||
|
"Secure URLs exposed over HTTPS are .*"
|
||||||
|
"['ilo_deploy_kernel', 'ilo_bootloader']",
|
||||||
|
task.driver.boot._validate_hrefs, data)
|
||||||
|
is_glance_mock.assert_has_calls(glance_calls)
|
||||||
|
urlparse_mock.assert_has_calls(glance_calls)
|
||||||
|
|
||||||
|
@mock.patch.object(urlparse, 'urlparse', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__validate_hrefs_glance_image(self, is_glance_mock, urlparse_mock):
|
||||||
|
is_glance_mock.return_value = True
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
data = {
|
||||||
|
'ilo_deploy_kernel': 'https://a.b.c.d/kernel',
|
||||||
|
'ilo_deploy_ramdisk': 'https://a.b.c.d/ramdisk',
|
||||||
|
'ilo_bootloader': 'https://a.b.c.d/bootloader'
|
||||||
|
}
|
||||||
|
|
||||||
|
task.driver.boot._validate_hrefs(data)
|
||||||
|
|
||||||
|
glance_calls = [
|
||||||
|
mock.call('https://a.b.c.d/kernel'),
|
||||||
|
mock.call('https://a.b.c.d/ramdisk'),
|
||||||
|
mock.call('https://a.b.c.d/bootloader')
|
||||||
|
]
|
||||||
|
|
||||||
|
is_glance_mock.assert_has_calls(glance_calls)
|
||||||
|
urlparse_mock.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_parse_driver_info',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'get_image_instance_info',
|
||||||
|
autospec=True)
|
||||||
|
def test__parse_deploy_info(self, get_img_inst_mock,
|
||||||
|
parse_driver_mock):
|
||||||
|
parse_driver_mock.return_value = {
|
||||||
|
'ilo_deploy_kernel': 'deploy-kernel',
|
||||||
|
'ilo_deploy_ramdisk': 'deploy-ramdisk',
|
||||||
|
'ilo_bootloader': 'bootloader'
|
||||||
|
}
|
||||||
|
get_img_inst_mock.return_value = {
|
||||||
|
'ilo_boot_iso': 'boot-iso',
|
||||||
|
'image_source': '6b2f0c0c-79e8-4db6-842e-43c9764204af'
|
||||||
|
}
|
||||||
|
instance_info = self.node.instance_info
|
||||||
|
driver_info = self.node.driver_info
|
||||||
|
|
||||||
|
instance_info['ilo_boot_iso'] = 'boot-iso'
|
||||||
|
instance_info['image_source'] = '6b2f0c0c-79e8-4db6-842e-43c9764204af'
|
||||||
|
self.node.instance_info = instance_info
|
||||||
|
|
||||||
|
driver_info['ilo_deploy_kernel'] = 'deploy-kernel'
|
||||||
|
driver_info['ilo_deploy_ramdisk'] = 'deploy-ramdisk'
|
||||||
|
driver_info['ilo_bootloader'] = 'bootloader'
|
||||||
|
self.node.driver_info = driver_info
|
||||||
|
self.node.save()
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
expected_info = {
|
||||||
|
'ilo_deploy_kernel': 'deploy-kernel',
|
||||||
|
'ilo_deploy_ramdisk': 'deploy-ramdisk',
|
||||||
|
'ilo_bootloader': 'bootloader',
|
||||||
|
'ilo_boot_iso': 'boot-iso',
|
||||||
|
'image_source': '6b2f0c0c-79e8-4db6-842e-43c9764204af'
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_info = task.driver.boot._parse_deploy_info(task.node)
|
||||||
|
get_img_inst_mock.assert_called_once_with(task.node)
|
||||||
|
parse_driver_mock.assert_called_once_with(mock.ANY, task.node)
|
||||||
|
self.assertEqual(expected_info, actual_info)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_hrefs',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'check_for_missing_params',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'parse_driver_info', autospec=True)
|
||||||
|
def test__parse_driver_info_default_mode(
|
||||||
|
self, parse_driver_mock, check_missing_mock, validate_href_mock):
|
||||||
|
parse_driver_mock.return_value = {
|
||||||
|
'ilo_username': 'admin',
|
||||||
|
'ilo_password': 'admin'
|
||||||
|
}
|
||||||
|
driver_info = self.node.driver_info
|
||||||
|
driver_info['ilo_deploy_kernel'] = 'deploy-kernel'
|
||||||
|
driver_info['ilo_rescue_kernel'] = 'rescue-kernel'
|
||||||
|
driver_info['ilo_deploy_ramdisk'] = 'deploy-ramdisk'
|
||||||
|
driver_info['ilo_rescue_ramdisk'] = 'rescue-ramdisk'
|
||||||
|
driver_info['ilo_bootloader'] = 'bootloader'
|
||||||
|
driver_info['dummy_key'] = 'dummy-value'
|
||||||
|
self.node.driver_info = driver_info
|
||||||
|
self.node.save()
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
deploy_info = {
|
||||||
|
'ilo_deploy_kernel': 'deploy-kernel',
|
||||||
|
'ilo_deploy_ramdisk': 'deploy-ramdisk',
|
||||||
|
'ilo_bootloader': 'bootloader'
|
||||||
|
}
|
||||||
|
actual_info = deploy_info
|
||||||
|
actual_info.update({'ilo_username': 'admin',
|
||||||
|
'ilo_password': 'admin'})
|
||||||
|
|
||||||
|
expected_info = task.driver.boot._parse_driver_info(task.node)
|
||||||
|
validate_href_mock.assert_called_once_with(mock.ANY, deploy_info)
|
||||||
|
check_missing_mock.assert_called_once_with(deploy_info, mock.ANY)
|
||||||
|
parse_driver_mock.assert_called_once_with(task.node)
|
||||||
|
self.assertEqual(actual_info, expected_info)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_hrefs',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'check_for_missing_params',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'parse_driver_info', autospec=True)
|
||||||
|
def test__parse_driver_info_rescue_mode(
|
||||||
|
self, parse_driver_mock, check_missing_mock, validate_href_mock):
|
||||||
|
parse_driver_mock.return_value = {
|
||||||
|
'ilo_username': 'admin',
|
||||||
|
'ilo_password': 'admin'
|
||||||
|
}
|
||||||
|
mode = 'rescue'
|
||||||
|
driver_info = self.node.driver_info
|
||||||
|
driver_info['ilo_deploy_kernel'] = 'deploy-kernel'
|
||||||
|
driver_info['ilo_rescue_kernel'] = 'rescue-kernel'
|
||||||
|
driver_info['ilo_deploy_ramdisk'] = 'deploy-ramdisk'
|
||||||
|
driver_info['ilo_rescue_ramdisk'] = 'rescue-ramdisk'
|
||||||
|
driver_info['ilo_bootloader'] = 'bootloader'
|
||||||
|
driver_info['dummy_key'] = 'dummy-value'
|
||||||
|
self.node.driver_info = driver_info
|
||||||
|
self.node.save()
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
deploy_info = {
|
||||||
|
'ilo_rescue_kernel': 'rescue-kernel',
|
||||||
|
'ilo_rescue_ramdisk': 'rescue-ramdisk',
|
||||||
|
'ilo_bootloader': 'bootloader'
|
||||||
|
}
|
||||||
|
actual_info = deploy_info
|
||||||
|
actual_info.update({'ilo_username': 'admin',
|
||||||
|
'ilo_password': 'admin'})
|
||||||
|
|
||||||
|
expected_info = task.driver.boot._parse_driver_info(
|
||||||
|
task.node, mode)
|
||||||
|
check_missing_mock.assert_called_once_with(deploy_info, mock.ANY)
|
||||||
|
validate_href_mock.assert_called_once_with(mock.ANY, deploy_info)
|
||||||
|
parse_driver_mock.assert_called_once_with(task.node)
|
||||||
|
self.assertEqual(actual_info, expected_info)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_hrefs',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'validate_image_properties',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'get_image_instance_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__validate_instance_image_info_not_iwdi(
|
||||||
|
self, glance_mock, get_image_inst_mock, validate_image_mock,
|
||||||
|
validate_href_mock):
|
||||||
|
instance_info = {
|
||||||
|
'ilo_boot_iso': 'boot-iso',
|
||||||
|
'image_source': '6b2f0c0c-79e8-4db6-842e-43c9764204af'
|
||||||
|
}
|
||||||
|
driver_internal_info = self.node.driver_internal_info
|
||||||
|
driver_internal_info.pop('is_whole_disk_image', None)
|
||||||
|
self.node.driver_internal_info = driver_internal_info
|
||||||
|
self.node.instance_info = instance_info
|
||||||
|
self.node.save()
|
||||||
|
get_image_inst_mock.return_value = instance_info
|
||||||
|
glance_mock.return_value = True
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
|
||||||
|
task.driver.boot._validate_instance_image_info(task)
|
||||||
|
get_image_inst_mock.assert_called_once_with(task.node)
|
||||||
|
glance_mock.assert_called_once_with(
|
||||||
|
'6b2f0c0c-79e8-4db6-842e-43c9764204af')
|
||||||
|
validate_image_mock.assert_called_once_with(task.context,
|
||||||
|
instance_info,
|
||||||
|
['kernel_id',
|
||||||
|
'ramdisk_id'])
|
||||||
|
validate_href_mock.assert_called_once_with(mock.ANY, instance_info)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_hrefs',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'validate_image_properties',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'get_image_instance_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__validate_instance_image_info_neither_iwdi_nor_glance(
|
||||||
|
self, glance_mock, get_image_inst_mock, validate_image_mock,
|
||||||
|
validate_href_mock):
|
||||||
|
|
||||||
|
instance_info = {
|
||||||
|
'ilo_boot_iso': 'boot-iso',
|
||||||
|
'image_source': '6b2f0c0c-79e8-4db6-842e-43c9764204af'
|
||||||
|
}
|
||||||
|
driver_internal_info = self.node.driver_internal_info
|
||||||
|
driver_internal_info.pop('is_whole_disk_image', None)
|
||||||
|
self.node.driver_internal_info = driver_internal_info
|
||||||
|
self.node.instance_info = instance_info
|
||||||
|
self.node.save()
|
||||||
|
get_image_inst_mock.return_value = instance_info
|
||||||
|
glance_mock.return_value = False
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
|
||||||
|
task.driver.boot._validate_instance_image_info(task)
|
||||||
|
get_image_inst_mock.assert_called_once_with(task.node)
|
||||||
|
glance_mock.assert_called_once_with(
|
||||||
|
'6b2f0c0c-79e8-4db6-842e-43c9764204af')
|
||||||
|
validate_image_mock.assert_called_once_with(task.context,
|
||||||
|
instance_info,
|
||||||
|
['kernel',
|
||||||
|
'ramdisk'])
|
||||||
|
validate_href_mock.assert_called_once_with(mock.ANY, instance_info)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_hrefs',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'validate_image_properties',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'get_image_instance_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test__validate_instance_image_info_iwdi(
|
||||||
|
self, glance_mock, get_image_inst_mock, validate_image_mock,
|
||||||
|
validate_href_mock):
|
||||||
|
instance_info = {
|
||||||
|
'ilo_boot_iso': 'boot-iso',
|
||||||
|
'image_source': '6b2f0c0c-79e8-4db6-842e-43c9764204af'
|
||||||
|
}
|
||||||
|
driver_internal_info = self.node.driver_internal_info or {}
|
||||||
|
driver_internal_info['is_whole_disk_image'] = True
|
||||||
|
self.node.driver_internal_info = driver_internal_info
|
||||||
|
self.node.save()
|
||||||
|
get_image_inst_mock.return_value = instance_info
|
||||||
|
glance_mock.return_value = True
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
|
||||||
|
task.driver.boot._validate_instance_image_info(task)
|
||||||
|
get_image_inst_mock.assert_called_once_with(task.node)
|
||||||
|
glance_mock.assert_not_called()
|
||||||
|
validate_image_mock.assert_called_once_with(task.context,
|
||||||
|
instance_info, [])
|
||||||
|
validate_href_mock.assert_called_once_with(mock.ANY, instance_info)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_current_boot_mode',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(noop_storage.NoopStorage, 'should_write_image',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_driver_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot,
|
||||||
|
'_validate_instance_image_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def test_validate(self, mock_val_instance_image_info,
|
||||||
|
mock_val_driver_info, storage_mock, get_boot_mock):
|
||||||
|
get_boot_mock.return_value = 'UEFI'
|
||||||
|
instance_info = self.node.instance_info
|
||||||
|
|
||||||
|
instance_info['ilo_boot_iso'] = 'boot-iso'
|
||||||
|
instance_info['image_source'] = '6b2f0c0c-79e8-4db6-842e-43c9764204af'
|
||||||
|
self.node.instance_info = instance_info
|
||||||
|
self.node.save()
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
|
||||||
|
task.node.driver_info['ilo_deploy_kernel'] = 'deploy-kernel'
|
||||||
|
task.node.driver_info['ilo_deploy_ramdisk'] = 'deploy-ramdisk'
|
||||||
|
task.node.driver_info['ilo_bootloader'] = 'bootloader'
|
||||||
|
storage_mock.return_value = True
|
||||||
|
task.driver.boot.validate(task)
|
||||||
|
mock_val_instance_image_info.assert_called_once_with(
|
||||||
|
mock.ANY, task)
|
||||||
|
mock_val_driver_info.assert_called_once_with(mock.ANY, task)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_current_boot_mode',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(noop_storage.NoopStorage, 'should_write_image',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_driver_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot,
|
||||||
|
'_validate_instance_image_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def test_validate_bios(self, mock_val_instance_image_info,
|
||||||
|
mock_val_driver_info, storage_mock, get_boot_mock):
|
||||||
|
get_boot_mock.return_value = 'LEGACY'
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
self.assertRaisesRegex(exception.InvalidParameterValue,
|
||||||
|
"Validation for 'ilo-uefi-https' boot "
|
||||||
|
"interface failed.*",
|
||||||
|
task.driver.boot.validate, task)
|
||||||
|
mock_val_instance_image_info.assert_not_called()
|
||||||
|
mock_val_driver_info.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_current_boot_mode',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_driver_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(image_service.HttpImageService, 'validate_href',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_validate_ramdisk_boot_option_glance(self, is_glance_image_mock,
|
||||||
|
validate_href_mock,
|
||||||
|
val_driver_info_mock,
|
||||||
|
get_boot_mock):
|
||||||
|
get_boot_mock.return_value = 'UEFI'
|
||||||
|
instance_info = self.node.instance_info
|
||||||
|
boot_iso = '6b2f0c0c-79e8-4db6-842e-43c9764204af'
|
||||||
|
instance_info['ilo_boot_iso'] = boot_iso
|
||||||
|
instance_info['capabilities'] = '{"boot_option": "ramdisk"}'
|
||||||
|
self.node.instance_info = instance_info
|
||||||
|
self.node.save()
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
is_glance_image_mock.return_value = True
|
||||||
|
task.driver.boot.validate(task)
|
||||||
|
is_glance_image_mock.assert_called_once_with(boot_iso)
|
||||||
|
self.assertFalse(validate_href_mock.called)
|
||||||
|
self.assertFalse(val_driver_info_mock.called)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_current_boot_mode',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_driver_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(image_service.HttpImageService, 'validate_href',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_validate_ramdisk_boot_option_webserver(self, is_glance_image_mock,
|
||||||
|
validate_href_mock,
|
||||||
|
val_driver_info_mock,
|
||||||
|
get_boot_mock):
|
||||||
|
get_boot_mock.return_value = 'UEFI'
|
||||||
|
instance_info = self.node.instance_info
|
||||||
|
boot_iso = 'http://myserver/boot.iso'
|
||||||
|
instance_info['ilo_boot_iso'] = boot_iso
|
||||||
|
instance_info['capabilities'] = '{"boot_option": "ramdisk"}'
|
||||||
|
self.node.instance_info = instance_info
|
||||||
|
self.node.save()
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
is_glance_image_mock.return_value = False
|
||||||
|
task.driver.boot.validate(task)
|
||||||
|
is_glance_image_mock.assert_called_once_with(boot_iso)
|
||||||
|
validate_href_mock.assert_called_once_with(mock.ANY, boot_iso)
|
||||||
|
self.assertFalse(val_driver_info_mock.called)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_current_boot_mode',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.LOG, 'error', spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_driver_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(image_service.HttpImageService, 'validate_href',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_validate_ramdisk_boot_option_webserver_exc(
|
||||||
|
self, is_glance_image_mock, validate_href_mock,
|
||||||
|
val_driver_info_mock, log_mock, get_boot_mock):
|
||||||
|
|
||||||
|
get_boot_mock.return_value = 'UEFI'
|
||||||
|
instance_info = self.node.instance_info
|
||||||
|
validate_href_mock.side_effect = exception.ImageRefValidationFailed(
|
||||||
|
image_href='http://myserver/boot.iso', reason='fail')
|
||||||
|
boot_iso = 'http://myserver/boot.iso'
|
||||||
|
instance_info['ilo_boot_iso'] = boot_iso
|
||||||
|
instance_info['capabilities'] = '{"boot_option": "ramdisk"}'
|
||||||
|
self.node.instance_info = instance_info
|
||||||
|
self.node.save()
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
|
||||||
|
is_glance_image_mock.return_value = False
|
||||||
|
self.assertRaisesRegex(exception.ImageRefValidationFailed,
|
||||||
|
"Validation of image href "
|
||||||
|
"http://myserver/boot.iso failed",
|
||||||
|
task.driver.boot.validate, task)
|
||||||
|
is_glance_image_mock.assert_called_once_with(boot_iso)
|
||||||
|
validate_href_mock.assert_called_once_with(mock.ANY, boot_iso)
|
||||||
|
self.assertFalse(val_driver_info_mock.called)
|
||||||
|
self.assertIn("UEFI-HTTPS boot with 'ramdisk' boot_option "
|
||||||
|
"accepts only Glance images or HTTPS URLs as "
|
||||||
|
"instance_info['ilo_boot_iso'].",
|
||||||
|
log_mock.call_args[0][0])
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_validate_driver_info',
|
||||||
|
autospec=True)
|
||||||
|
def test_validate_inspection(self, mock_val_driver_info):
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
task.driver.boot.validate_inspection(task)
|
||||||
|
mock_val_driver_info.assert_called_once_with(mock.ANY, task)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_parse_driver_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def test_validate_inspection_missing(self, mock_parse_driver_info):
|
||||||
|
mock_parse_driver_info.side_effect = exception.MissingParameterValue(
|
||||||
|
"Error validating iLO UEFIHTTPS for deploy.")
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||||
|
task.driver.boot.validate_inspection, task)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'setup_uefi_https',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(image_utils, 'prepare_deploy_iso',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot, 'prepare_node_for_deploy',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_parse_driver_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'get_single_nic_with_vif_port_id',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
def _test_prepare_ramdisk(self, get_nic_mock,
|
||||||
|
parse_driver_mock,
|
||||||
|
prepare_node_for_deploy_mock,
|
||||||
|
prepare_deploy_iso_mock,
|
||||||
|
setup_uefi_https_mock,
|
||||||
|
ilo_boot_iso, image_source,
|
||||||
|
ramdisk_params={'a': 'b'},
|
||||||
|
mode='deploy', state=states.DEPLOYING):
|
||||||
|
self.node.provision_state = state
|
||||||
|
self.node.save()
|
||||||
|
instance_info = self.node.instance_info
|
||||||
|
instance_info['ilo_boot_iso'] = ilo_boot_iso
|
||||||
|
instance_info['image_source'] = image_source
|
||||||
|
self.node.instance_info = instance_info
|
||||||
|
self.node.save()
|
||||||
|
iso = 'provisioning-iso'
|
||||||
|
|
||||||
|
d_info = {
|
||||||
|
'ilo_' + mode + '_kernel': mode + '-kernel',
|
||||||
|
'ilo_' + mode + '_ramdisk': mode + '-ramdisk',
|
||||||
|
'ilo_' + 'bootloader': 'bootloader'
|
||||||
|
}
|
||||||
|
parse_driver_mock.return_value = d_info
|
||||||
|
prepare_deploy_iso_mock.return_value = 'recreated-iso'
|
||||||
|
|
||||||
|
get_nic_mock.return_value = '12:34:56:78:90:ab'
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
|
||||||
|
driver_info = task.node.driver_info
|
||||||
|
driver_info['ilo_%s_iso' % mode] = iso
|
||||||
|
task.node.driver_info = driver_info
|
||||||
|
|
||||||
|
task.driver.boot.prepare_ramdisk(task, ramdisk_params)
|
||||||
|
|
||||||
|
prepare_node_for_deploy_mock.assert_called_once_with(task)
|
||||||
|
|
||||||
|
get_nic_mock.assert_called_once_with(task)
|
||||||
|
parse_driver_mock.assert_called_once_with(
|
||||||
|
mock.ANY, task.node, mode)
|
||||||
|
prepare_deploy_iso_mock.assert_called_once_with(
|
||||||
|
task, ramdisk_params, mode, d_info)
|
||||||
|
setup_uefi_https_mock.assert_called_once_with(task,
|
||||||
|
'recreated-iso')
|
||||||
|
|
||||||
|
def test_prepare_ramdisk_rescue_glance_image(self):
|
||||||
|
self._test_prepare_ramdisk(
|
||||||
|
ilo_boot_iso='swift:abcdef',
|
||||||
|
image_source='6b2f0c0c-79e8-4db6-842e-43c9764204af',
|
||||||
|
mode='rescue', state=states.RESCUING)
|
||||||
|
self.node.refresh()
|
||||||
|
self.assertNotIn('ilo_boot_iso', self.node.instance_info)
|
||||||
|
|
||||||
|
def test_prepare_ramdisk_rescue_not_a_glance_image(self):
|
||||||
|
self._test_prepare_ramdisk(
|
||||||
|
ilo_boot_iso='http://mybootiso',
|
||||||
|
image_source='http://myimage',
|
||||||
|
mode='rescue', state=states.RESCUING)
|
||||||
|
self.node.refresh()
|
||||||
|
self.assertEqual('http://mybootiso',
|
||||||
|
self.node.instance_info['ilo_boot_iso'])
|
||||||
|
|
||||||
|
def test_prepare_ramdisk_glance_image(self):
|
||||||
|
self._test_prepare_ramdisk(
|
||||||
|
ilo_boot_iso='swift:abcdef',
|
||||||
|
image_source='6b2f0c0c-79e8-4db6-842e-43c9764204af')
|
||||||
|
self.node.refresh()
|
||||||
|
self.assertNotIn('ilo_boot_iso', self.node.instance_info)
|
||||||
|
|
||||||
|
def test_prepare_ramdisk_not_a_glance_image(self):
|
||||||
|
self._test_prepare_ramdisk(
|
||||||
|
ilo_boot_iso='http://mybootiso',
|
||||||
|
image_source='http://myimage')
|
||||||
|
self.node.refresh()
|
||||||
|
self.assertEqual('http://mybootiso',
|
||||||
|
self.node.instance_info['ilo_boot_iso'])
|
||||||
|
|
||||||
|
def test_prepare_ramdisk_glance_image_cleaning(self):
|
||||||
|
self._test_prepare_ramdisk(
|
||||||
|
ilo_boot_iso='swift:abcdef',
|
||||||
|
image_source='6b2f0c0c-79e8-4db6-842e-43c9764204af',
|
||||||
|
mode='deploy', state=states.CLEANING)
|
||||||
|
self.node.refresh()
|
||||||
|
self.assertNotIn('ilo_boot_iso', self.node.instance_info)
|
||||||
|
|
||||||
|
def test_prepare_ramdisk_not_a_glance_image_cleaning(self):
|
||||||
|
self._test_prepare_ramdisk(
|
||||||
|
ilo_boot_iso='http://mybootiso',
|
||||||
|
image_source='http://myimage',
|
||||||
|
mode='deploy', state=states.CLEANING)
|
||||||
|
self.node.refresh()
|
||||||
|
self.assertEqual('http://mybootiso',
|
||||||
|
self.node.instance_info['ilo_boot_iso'])
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, 'cleanup_iso_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_clean_up_ramdisk(self, cleanup_iso_mock):
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
task.driver.boot.clean_up_ramdisk(task)
|
||||||
|
cleanup_iso_mock.assert_called_once_with(task)
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, 'cleanup_iso_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'setup_uefi_https',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(image_utils, 'prepare_deploy_iso',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_parse_deploy_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def _test_prepare_instance_local_or_whole_disk_image(
|
||||||
|
self, set_boot_device_mock,
|
||||||
|
parse_deploy_mock, prepare_iso_mock, setup_uefi_https_mock,
|
||||||
|
cleanup_iso_mock):
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
task.driver.boot.prepare_instance(task)
|
||||||
|
|
||||||
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
|
boot_devices.DISK,
|
||||||
|
persistent=True)
|
||||||
|
cleanup_iso_mock.assert_called_once_with(task)
|
||||||
|
prepare_iso_mock.assert_not_called()
|
||||||
|
setup_uefi_https_mock.assert_not_called()
|
||||||
|
|
||||||
|
def test_prepare_instance_image_local(self):
|
||||||
|
self.node.instance_info = {'capabilities': '{"boot_option": "local"}'}
|
||||||
|
self.node.save()
|
||||||
|
self._test_prepare_instance_local_or_whole_disk_image()
|
||||||
|
|
||||||
|
def test_prepare_instance_whole_disk_image(self):
|
||||||
|
self.node.driver_internal_info = {'is_whole_disk_image': True}
|
||||||
|
self.node.save()
|
||||||
|
self._test_prepare_instance_local_or_whole_disk_image()
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, 'cleanup_iso_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'setup_uefi_https',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(image_utils, 'prepare_boot_iso',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_parse_deploy_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_prepare_instance_partition_image(
|
||||||
|
self, set_boot_device_mock,
|
||||||
|
parse_deploy_mock, prepare_iso_mock, setup_uefi_https_mock,
|
||||||
|
cleanup_iso_mock):
|
||||||
|
|
||||||
|
self.node.instance_info = {
|
||||||
|
'capabilities': '{"boot_option": "netboot"}'
|
||||||
|
}
|
||||||
|
self.node.driver_internal_info = {
|
||||||
|
'root_uuid_or_disk_id': (
|
||||||
|
"12312642-09d3-467f-8e09-12385826a123")
|
||||||
|
}
|
||||||
|
self.node.driver_internal_info.update({'is_whole_disk_image': False})
|
||||||
|
self.node.save()
|
||||||
|
d_info = {'a': 'x', 'b': 'y'}
|
||||||
|
parse_deploy_mock.return_value = d_info
|
||||||
|
prepare_iso_mock.return_value = "recreated-iso"
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
task.driver.boot.prepare_instance(task)
|
||||||
|
|
||||||
|
cleanup_iso_mock.assert_called_once_with(task)
|
||||||
|
set_boot_device_mock.assert_not_called()
|
||||||
|
parse_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||||
|
prepare_iso_mock.assert_called_once_with(
|
||||||
|
task, d_info, root_uuid='12312642-09d3-467f-8e09-12385826a123')
|
||||||
|
setup_uefi_https_mock.assert_called_once_with(
|
||||||
|
task, "recreated-iso", True)
|
||||||
|
self.assertEqual(task.node.instance_info['ilo_boot_iso'],
|
||||||
|
"recreated-iso")
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, 'cleanup_iso_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch.object(ilo_common, 'setup_uefi_https',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(image_utils, 'prepare_boot_iso',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(ilo_boot.IloUefiHttpsBoot, '_parse_deploy_info',
|
||||||
|
spec_set=True, autospec=True)
|
||||||
|
@mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_prepare_instance_boot_ramdisk(
|
||||||
|
self, set_boot_device_mock,
|
||||||
|
parse_deploy_mock, prepare_iso_mock, setup_uefi_https_mock,
|
||||||
|
cleanup_iso_mock):
|
||||||
|
|
||||||
|
self.node.driver_internal_info.update({'is_whole_disk_image': False})
|
||||||
|
self.node.save()
|
||||||
|
d_info = {'a': 'x', 'b': 'y'}
|
||||||
|
parse_deploy_mock.return_value = d_info
|
||||||
|
prepare_iso_mock.return_value = "recreated-iso"
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
instance_info = task.node.instance_info
|
||||||
|
instance_info['capabilities'] = '{"boot_option": "ramdisk"}'
|
||||||
|
task.node.instance_info = instance_info
|
||||||
|
task.node.save()
|
||||||
|
task.driver.boot.prepare_instance(task)
|
||||||
|
|
||||||
|
cleanup_iso_mock.assert_called_once_with(task)
|
||||||
|
set_boot_device_mock.assert_not_called()
|
||||||
|
parse_deploy_mock.assert_called_once_with(mock.ANY, task.node)
|
||||||
|
prepare_iso_mock.assert_called_once_with(
|
||||||
|
task, d_info)
|
||||||
|
setup_uefi_https_mock.assert_called_once_with(
|
||||||
|
task, "recreated-iso", True)
|
||||||
|
self.assertTrue('ilo_boot_iso' not in task.node.instance_info)
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, 'cleanup_iso_image', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_clean_up_instance(self, cleanup_iso_mock):
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
task.driver.boot.clean_up_instance(task)
|
||||||
|
cleanup_iso_mock.assert_called_once_with(task)
|
||||||
|
|
||||||
|
def test_validate_rescue(self):
|
||||||
|
driver_info = self.node.driver_info
|
||||||
|
driver_info['ilo_rescue_kernel'] = 'rescue-kernel'
|
||||||
|
driver_info['ilo_rescue_ramdisk'] = 'rescue-ramdisk'
|
||||||
|
driver_info['ilo_bootloader'] = 'bootloader'
|
||||||
|
self.node.driver_info = driver_info
|
||||||
|
self.node.save()
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
|
task.driver.boot.validate_rescue(task)
|
||||||
|
|
||||||
|
def test_validate_rescue_no_rescue_ramdisk(self):
|
||||||
|
driver_info = self.node.driver_info
|
||||||
|
driver_info['ilo_rescue_kernel'] = 'rescue-kernel'
|
||||||
|
driver_info['ilo_rescue_ramdisk'] = 'rescue-ramdisk'
|
||||||
|
driver_info.pop('ilo_bootloader', None)
|
||||||
|
self.node.driver_info = driver_info
|
||||||
|
self.node.save()
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
|
self.assertRaisesRegex(exception.MissingParameterValue,
|
||||||
|
"Error validating rescue for iLO UEFI "
|
||||||
|
"HTTPS boot.* ['ilo_bootloader']",
|
||||||
|
task.driver.boot.validate_rescue, task)
|
||||||
|
@ -675,7 +675,6 @@ class IloCommonMethodsTestCase(BaseIloTest):
|
|||||||
swift_obj_mock = swift_api_mock.return_value
|
swift_obj_mock = swift_api_mock.return_value
|
||||||
boot_iso = 'swift:object-name'
|
boot_iso = 'swift:object-name'
|
||||||
swift_obj_mock.get_temp_url.return_value = 'image_url'
|
swift_obj_mock.get_temp_url.return_value = 'image_url'
|
||||||
CONF.keystone_authtoken.auth_uri = 'http://authurl'
|
|
||||||
CONF.ilo.swift_ilo_container = 'ilo_cont'
|
CONF.ilo.swift_ilo_container = 'ilo_cont'
|
||||||
CONF.ilo.swift_object_expiry_timeout = 1
|
CONF.ilo.swift_object_expiry_timeout = 1
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
@ -1206,3 +1205,79 @@ class IloCommonMethodsTestCase(BaseIloTest):
|
|||||||
ilo_common.get_server_post_state,
|
ilo_common.get_server_post_state,
|
||||||
task.node)
|
task.node)
|
||||||
ilo_mock_object.get_host_post_state.assert_called_once_with()
|
ilo_mock_object.get_host_post_state.assert_called_once_with()
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_setup_uefi_https_scheme_http(self, get_ilo_object_mock):
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
iso = "http://1.1.1.1/image.iso"
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
self.assertRaises(exception.IloOperationNotSupported,
|
||||||
|
ilo_common.setup_uefi_https,
|
||||||
|
task, iso, True)
|
||||||
|
|
||||||
|
ilo_mock_object.set_http_boot_url.assert_not_called()
|
||||||
|
ilo_mock_object.set_one_time_boot.assert_not_called()
|
||||||
|
ilo_mock_object.update_persistent_boot.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def _test_setup_uefi_https(self, get_ilo_object_mock, persistent):
|
||||||
|
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
|
||||||
|
iso = "https://1.1.1.1/image.iso"
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
ilo_common.setup_uefi_https(task, iso, persistent=persistent)
|
||||||
|
|
||||||
|
ilo_mock_object.set_http_boot_url.assert_called_once_with(iso)
|
||||||
|
if persistent:
|
||||||
|
ilo_mock_object.set_one_time_boot.assert_not_called()
|
||||||
|
ilo_mock_object.update_persistent_boot.assert_called_once_with(
|
||||||
|
['UEFIHTTP'])
|
||||||
|
else:
|
||||||
|
ilo_mock_object.set_one_time_boot.assert_called_once_with(
|
||||||
|
'UEFIHTTP')
|
||||||
|
ilo_mock_object.update_persistent_boot.assert_not_called()
|
||||||
|
|
||||||
|
def test_setup_uefi_https_persistent_true(self):
|
||||||
|
self._test_setup_uefi_https(persistent=True)
|
||||||
|
|
||||||
|
def test_setup_uefi_https_persistent_false(self):
|
||||||
|
self._test_setup_uefi_https(persistent=False)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_setup_uefi_https_raises_not_supported(self, get_ilo_object_mock):
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
|
||||||
|
exc = ilo_error.IloCommandNotSupportedInBiosError('error')
|
||||||
|
ilo_mock_object.set_http_boot_url.side_effect = exc
|
||||||
|
|
||||||
|
iso = "https://1.1.1.1/image.iso"
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
self.assertRaises(exception.IloOperationNotSupported,
|
||||||
|
ilo_common.setup_uefi_https,
|
||||||
|
task, iso, True)
|
||||||
|
|
||||||
|
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
|
||||||
|
autospec=True)
|
||||||
|
def test_setup_uefi_https_raises_ilo_error(self, get_ilo_object_mock):
|
||||||
|
ilo_mock_object = get_ilo_object_mock.return_value
|
||||||
|
|
||||||
|
exc = ilo_error.IloError('error')
|
||||||
|
ilo_mock_object.set_http_boot_url.side_effect = exc
|
||||||
|
|
||||||
|
iso = "https://1.1.1.1/image.iso"
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
self.assertRaises(exception.IloOperationError,
|
||||||
|
ilo_common.setup_uefi_https,
|
||||||
|
task, iso, True)
|
||||||
|
@ -314,18 +314,51 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
|
|||||||
root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
||||||
base_iso='/path/to/baseiso')
|
base_iso='/path/to/baseiso')
|
||||||
|
|
||||||
|
def test__find_param(self):
|
||||||
|
param_dict = {
|
||||||
|
'ilo_deploy_kernel': 'kernel',
|
||||||
|
'ilo_deploy_ramdisk': 'ramdisk',
|
||||||
|
'ilo_bootloader': 'bootloader'
|
||||||
|
}
|
||||||
|
param_str = "deploy_kernel"
|
||||||
|
expected = "kernel"
|
||||||
|
|
||||||
|
actual = image_utils._find_param(param_str, param_dict)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
def test__find_param_not_found(self):
|
||||||
|
param_dict = {
|
||||||
|
'ilo_deploy_ramdisk': 'ramdisk',
|
||||||
|
'ilo_bootloader': 'bootloader'
|
||||||
|
}
|
||||||
|
param_str = "deploy_kernel"
|
||||||
|
expected = None
|
||||||
|
actual = image_utils._find_param(param_str, param_dict)
|
||||||
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, '_find_param', autospec=True)
|
||||||
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
|
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
|
||||||
def test_prepare_deploy_iso(self, mock__prepare_iso_image):
|
def test_prepare_deploy_iso(self, mock__prepare_iso_image,
|
||||||
|
find_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=True) as task:
|
shared=True) as task:
|
||||||
|
|
||||||
d_info = {
|
d_info = {
|
||||||
'deploy_kernel': 'kernel',
|
'ilo_deploy_kernel': 'kernel',
|
||||||
'deploy_ramdisk': 'ramdisk',
|
'ilo_deploy_ramdisk': 'ramdisk',
|
||||||
'bootloader': 'bootloader'
|
'ilo_bootloader': 'bootloader'
|
||||||
}
|
}
|
||||||
task.node.driver_info.update(d_info)
|
task.node.driver_info.update(d_info)
|
||||||
|
|
||||||
|
find_call_list = [
|
||||||
|
mock.call('deploy_kernel', d_info),
|
||||||
|
mock.call('deploy_ramdisk', d_info),
|
||||||
|
mock.call('bootloader', d_info)
|
||||||
|
]
|
||||||
|
find_mock.side_effect = [
|
||||||
|
'kernel', 'ramdisk', 'bootloader'
|
||||||
|
]
|
||||||
|
|
||||||
task.node.instance_info.update(deploy_boot_mode='uefi')
|
task.node.instance_info.update(deploy_boot_mode='uefi')
|
||||||
|
|
||||||
image_utils.prepare_deploy_iso(task, {}, 'deploy', d_info)
|
image_utils.prepare_deploy_iso(task, {}, 'deploy', d_info)
|
||||||
@ -333,21 +366,33 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
|
|||||||
mock__prepare_iso_image.assert_called_once_with(
|
mock__prepare_iso_image.assert_called_once_with(
|
||||||
task, 'kernel', 'ramdisk', 'bootloader', params={})
|
task, 'kernel', 'ramdisk', 'bootloader', params={})
|
||||||
|
|
||||||
|
find_mock.assert_has_calls(find_call_list)
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, '_find_param', autospec=True)
|
||||||
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
|
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
|
||||||
@mock.patch.object(images, 'create_vfat_image', autospec=True)
|
@mock.patch.object(images, 'create_vfat_image', autospec=True)
|
||||||
def test_prepare_deploy_iso_network_data(
|
def test_prepare_deploy_iso_network_data(
|
||||||
self, mock_create_vfat_image, mock__prepare_iso_image):
|
self, mock_create_vfat_image, mock__prepare_iso_image,
|
||||||
|
find_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=True) as task:
|
shared=True) as task:
|
||||||
|
|
||||||
d_info = {
|
d_info = {
|
||||||
'deploy_kernel': 'kernel',
|
'ilo_deploy_kernel': 'kernel',
|
||||||
'deploy_ramdisk': 'ramdisk'
|
'ilo_deploy_ramdisk': 'ramdisk'
|
||||||
}
|
}
|
||||||
task.node.driver_info.update(d_info)
|
task.node.driver_info.update(d_info)
|
||||||
|
|
||||||
task.node.instance_info.update()
|
task.node.instance_info.update()
|
||||||
|
|
||||||
|
find_call_list = [
|
||||||
|
mock.call('deploy_kernel', d_info),
|
||||||
|
mock.call('deploy_ramdisk', d_info),
|
||||||
|
mock.call('bootloader', d_info)
|
||||||
|
]
|
||||||
|
find_mock.side_effect = [
|
||||||
|
'kernel', 'ramdisk', None
|
||||||
|
]
|
||||||
network_data = {'a': ['b']}
|
network_data = {'a': ['b']}
|
||||||
|
|
||||||
mock_get_node_nw_data = mock.MagicMock(return_value=network_data)
|
mock_get_node_nw_data = mock.MagicMock(return_value=network_data)
|
||||||
@ -362,16 +407,19 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
|
|||||||
task, 'kernel', 'ramdisk', bootloader_href=None,
|
task, 'kernel', 'ramdisk', bootloader_href=None,
|
||||||
configdrive=mock.ANY, params={})
|
configdrive=mock.ANY, params={})
|
||||||
|
|
||||||
|
find_mock.assert_has_calls(find_call_list)
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, '_find_param', autospec=True)
|
||||||
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
|
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
|
||||||
@mock.patch.object(images, 'create_boot_iso', autospec=True)
|
@mock.patch.object(images, 'create_boot_iso', autospec=True)
|
||||||
def test_prepare_boot_iso(self, mock_create_boot_iso,
|
def test_prepare_boot_iso(self, mock_create_boot_iso,
|
||||||
mock__prepare_iso_image):
|
mock__prepare_iso_image, find_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=True) as task:
|
shared=True) as task:
|
||||||
d_info = {
|
d_info = {
|
||||||
'deploy_kernel': 'kernel',
|
'ilo_deploy_kernel': 'kernel',
|
||||||
'deploy_ramdisk': 'ramdisk',
|
'ilo_deploy_ramdisk': 'ramdisk',
|
||||||
'bootloader': 'bootloader'
|
'ilo_bootloader': 'bootloader'
|
||||||
}
|
}
|
||||||
task.node.driver_info.update(d_info)
|
task.node.driver_info.update(d_info)
|
||||||
|
|
||||||
@ -380,6 +428,14 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
|
|||||||
'kernel': 'http://kernel/img',
|
'kernel': 'http://kernel/img',
|
||||||
'ramdisk': 'http://ramdisk/img'})
|
'ramdisk': 'http://ramdisk/img'})
|
||||||
|
|
||||||
|
find_call_list = [
|
||||||
|
mock.call('bootloader', d_info)
|
||||||
|
]
|
||||||
|
|
||||||
|
find_mock.side_effect = [
|
||||||
|
'bootloader'
|
||||||
|
]
|
||||||
|
|
||||||
image_utils.prepare_boot_iso(
|
image_utils.prepare_boot_iso(
|
||||||
task, d_info, root_uuid=task.node.uuid)
|
task, d_info, root_uuid=task.node.uuid)
|
||||||
|
|
||||||
@ -388,10 +444,14 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
|
|||||||
'bootloader', root_uuid=task.node.uuid,
|
'bootloader', root_uuid=task.node.uuid,
|
||||||
base_iso=None)
|
base_iso=None)
|
||||||
|
|
||||||
|
find_mock.assert_has_calls(find_call_list)
|
||||||
|
|
||||||
|
@mock.patch.object(image_utils, '_find_param', autospec=True)
|
||||||
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
|
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
|
||||||
@mock.patch.object(images, 'create_boot_iso', autospec=True)
|
@mock.patch.object(images, 'create_boot_iso', autospec=True)
|
||||||
def test_prepare_boot_iso_user_supplied(self, mock_create_boot_iso,
|
def test_prepare_boot_iso_user_supplied(self, mock_create_boot_iso,
|
||||||
mock__prepare_iso_image):
|
mock__prepare_iso_image,
|
||||||
|
find_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=True) as task:
|
shared=True) as task:
|
||||||
d_info = {
|
d_info = {
|
||||||
@ -404,6 +464,13 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
|
|||||||
task.node.instance_info.update(
|
task.node.instance_info.update(
|
||||||
{'boot_iso': 'http://boot/iso'})
|
{'boot_iso': 'http://boot/iso'})
|
||||||
|
|
||||||
|
find_call_list = [
|
||||||
|
mock.call('bootloader', d_info)
|
||||||
|
]
|
||||||
|
|
||||||
|
find_mock.side_effect = [
|
||||||
|
'bootloader'
|
||||||
|
]
|
||||||
image_utils.prepare_boot_iso(
|
image_utils.prepare_boot_iso(
|
||||||
task, d_info, root_uuid=task.node.uuid)
|
task, d_info, root_uuid=task.node.uuid)
|
||||||
|
|
||||||
@ -411,3 +478,5 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
|
|||||||
mock.ANY, None, None,
|
mock.ANY, None, None,
|
||||||
'bootloader', root_uuid=task.node.uuid,
|
'bootloader', root_uuid=task.node.uuid,
|
||||||
base_iso='http://boot/iso')
|
base_iso='http://boot/iso')
|
||||||
|
|
||||||
|
find_mock.assert_has_calls(find_call_list)
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Adds ``ilo-uefi-https`` boot interface to ``ilo5`` hardware type.
|
||||||
|
This boot interface levereges the iLO UEFI firmware capability to
|
||||||
|
boot from given HTTPS URLs hosted securely over HTTPS webserver
|
||||||
|
with standard/custom certificates.
|
@ -69,6 +69,7 @@ ironic.hardware.interfaces.boot =
|
|||||||
ilo-pxe = ironic.drivers.modules.ilo.boot:IloPXEBoot
|
ilo-pxe = ironic.drivers.modules.ilo.boot:IloPXEBoot
|
||||||
ilo-ipxe = ironic.drivers.modules.ilo.boot:IloiPXEBoot
|
ilo-ipxe = ironic.drivers.modules.ilo.boot:IloiPXEBoot
|
||||||
ilo-virtual-media = ironic.drivers.modules.ilo.boot:IloVirtualMediaBoot
|
ilo-virtual-media = ironic.drivers.modules.ilo.boot:IloVirtualMediaBoot
|
||||||
|
ilo-uefi-https = ironic.drivers.modules.ilo.boot:IloUefiHttpsBoot
|
||||||
ipxe = ironic.drivers.modules.ipxe:iPXEBoot
|
ipxe = ironic.drivers.modules.ipxe:iPXEBoot
|
||||||
irmc-pxe = ironic.drivers.modules.irmc.boot:IRMCPXEBoot
|
irmc-pxe = ironic.drivers.modules.irmc.boot:IRMCPXEBoot
|
||||||
irmc-virtual-media = ironic.drivers.modules.irmc.boot:IRMCVirtualMediaBoot
|
irmc-virtual-media = ironic.drivers.modules.irmc.boot:IRMCVirtualMediaBoot
|
||||||
|
Loading…
Reference in New Issue
Block a user