Support pre-built deploy/rescue ISO in Redfish

This change adds support to pre-built ISO images via the new driver_info
parameters redfish_deploy_iso and redfish_rescue_iso, similarly to the
iLO hardware type.

Also removes overly eager mocking in image unit tests.

Change-Id: I1366791a6c6eb34f3a43337c4199592783765912
This commit is contained in:
Dmitry Tantsur 2021-03-22 15:32:15 +01:00
parent 8762fb1eb3
commit d55929fc7d
6 changed files with 100 additions and 67 deletions

View File

@ -198,6 +198,27 @@ property can be used to pass user-specified kernel command line parameters.
For ramdisk kernel, ``[instance_info]/kernel_append_params`` property serves
the same purpose.
Pre-built ISO images
~~~~~~~~~~~~~~~~~~~~
By default an ISO images is built per node using the deploy kernel and
initramfs provided in the configuration or the node's ``driver_info``. Starting
with the Wallaby release it's possible to provide a pre-built ISO image:
.. code-block:: bash
baremetal node set node-0 \
--driver_info redfish_deploy_iso=http://url/of/deploy.iso \
--driver_info redfish_rescue_iso=http://url/of/rescue.iso
.. note::
OpenStack Image service (glance) image IDs and ``file://`` links are also
accepted.
No customization is currently done to the image, so e.g.
:doc:`/admin/dhcp-less` won't work. `Configuring an ESP image`_ is also
unnecessary.
Configuring an ESP image
~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -436,6 +436,14 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href,
boot_mode = boot_mode_utils.get_boot_mode(task.node)
if base_iso:
# TODO(dtantsur): fix this limitation eventually (see
# images.create_boot_iso).
LOG.debug('Using pre-built %(boot_mode)s ISO %(iso)s for node '
'%(node)s, custom configuration will not be available',
{'boot_mode': boot_mode, 'node': task.node.uuid,
'iso': base_iso})
else:
LOG.debug("Trying to create %(boot_mode)s ISO image for node %(node)s "
"with kernel %(kernel_href)s, ramdisk %(ramdisk_href)s, "
"bootloader %(bootloader_href)s and kernel params %(params)s"
@ -479,6 +487,7 @@ def _find_param(param_str, param_dict):
for param_key in param_dict:
if param_str in param_key:
val = param_dict.get(param_key)
if val is not None:
return val
@ -516,10 +525,12 @@ def prepare_deploy_iso(task, params, mode, d_info):
kernel_str = '%s_kernel' % mode
ramdisk_str = '%s_ramdisk' % mode
iso_str = '%s_iso' % mode
bootloader_str = 'bootloader'
kernel_href = _find_param(kernel_str, d_info)
ramdisk_href = _find_param(ramdisk_str, d_info)
iso_href = _find_param(iso_str, d_info)
bootloader_href = _find_param(bootloader_str, d_info)
# TODO(TheJulia): At some point we should support something like
@ -527,7 +538,7 @@ def prepare_deploy_iso(task, params, mode, d_info):
# injection.
prepare_iso_image = functools.partial(
_prepare_iso_image, task, kernel_href, ramdisk_href,
bootloader_href=bootloader_href, params=params)
bootloader_href=bootloader_href, params=params, base_iso=iso_href)
inject_files = {}
if CONF.agent.api_ca_file:

View File

@ -100,6 +100,11 @@ def _parse_driver_info(node):
d_info = node.driver_info
mode = deploy_utils.rescue_or_deploy_mode(node)
iso_param = 'redfish_%s_iso' % mode
iso_ref = d_info.get(iso_param)
if iso_ref is not None:
deploy_info = {iso_param: iso_ref}
else:
params_to_check = KERNEL_RAMDISK_LABELS[mode]
deploy_info = {option: d_info.get(option)

View File

@ -72,6 +72,17 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.assertIn('ramdisk', actual_driver_info['deploy_ramdisk'])
self.assertIn('bootloader', actual_driver_info['bootloader'])
def test_parse_driver_info_iso(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_info.update(
{'redfish_deploy_iso': 'http://boot.iso'})
actual_driver_info = redfish_boot._parse_driver_info(task.node)
self.assertEqual('http://boot.iso',
actual_driver_info['redfish_deploy_iso'])
def test_parse_driver_info_rescue(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:

View File

@ -529,10 +529,8 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
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)
def test_prepare_deploy_iso(self, mock__prepare_iso_image,
find_mock):
def test_prepare_deploy_iso(self, mock__prepare_iso_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@ -543,29 +541,34 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
}
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')
image_utils.prepare_deploy_iso(task, {}, 'deploy', d_info)
mock__prepare_iso_image.assert_called_once_with(
task, 'kernel', 'ramdisk', 'bootloader', params={},
inject_files={})
inject_files={}, 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)
def test_prepare_deploy_iso_network_data(
self, mock__prepare_iso_image, find_mock):
def test_prepare_deploy_iso_existing_iso(self, mock__prepare_iso_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
d_info = {
'redfish_deploy_iso': 'iso',
}
task.node.driver_info.update(d_info)
task.node.instance_info.update(deploy_boot_mode='uefi')
image_utils.prepare_deploy_iso(task, {}, 'deploy', d_info)
mock__prepare_iso_image.assert_called_once_with(
task, None, None, None, params={},
inject_files={}, base_iso='iso')
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
def test_prepare_deploy_iso_network_data(self, mock__prepare_iso_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@ -577,14 +580,6 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
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']}
mock_get_node_nw_data = mock.MagicMock(return_value=network_data)
@ -602,14 +597,10 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
mock__prepare_iso_image.assert_called_once_with(
task, 'kernel', 'ramdisk', bootloader_href=None,
params={}, inject_files=expected_files)
params={}, inject_files=expected_files, 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)
def test_prepare_deploy_iso_tls(self, mock__prepare_iso_image,
find_mock):
def test_prepare_deploy_iso_tls(self, mock__prepare_iso_image):
with tempfile.NamedTemporaryFile(delete=False) as tf:
temp_name = tf.name
self.addCleanup(lambda: os.unlink(temp_name))
@ -626,15 +617,6 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
}
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')
image_utils.prepare_deploy_iso(task, {}, 'deploy', d_info)
@ -648,9 +630,7 @@ cafile = /etc/ironic-python-agent/ironic.crt
mock__prepare_iso_image.assert_called_once_with(
task, 'kernel', 'ramdisk', 'bootloader', params={},
inject_files=expected_files)
find_mock.assert_has_calls(find_call_list)
inject_files=expected_files, base_iso=None)
@mock.patch.object(image_utils, '_find_param', autospec=True)
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)

View File

@ -0,0 +1,5 @@
---
features:
- |
Adds support for pre-built ISO images to the ``redfish-virtual-media``
boot interface and its derivatives.