Allow overriding an external URL for virtual media

Virtual media deployments can be conducted outside of the provisioning
network as long as the node gets an IP address somehow and can reach
ironic and its HTTP server. This changes adds new configuration that
allows to use public IP addresses for virtual media while keeping PXE
boots working and constrained to the provisioning network.

Change-Id: I8b859b2812160ff3911eb7d648eab835ef61d934
Story: #2008566
Task: #41706
This commit is contained in:
Dmitry Tantsur 2021-03-24 15:23:24 +01:00 committed by Julia Kreger
parent 2e68c318b3
commit 133dac255f
8 changed files with 140 additions and 4 deletions

View File

@ -87,3 +87,26 @@ An example network data:
.. _Glean: https://docs.openstack.org/infra/glean/
.. _simple-init: https://docs.openstack.org/diskimage-builder/latest/elements/simple-init/README.html
.. _network_data: https://specs.openstack.org/openstack/nova-specs/specs/liberty/implemented/metadata-service-network-info.html
.. _l3-external-ip:
Deploying outside of the provisioning network
---------------------------------------------
If you need to combine traditional deployments using a provisioning network
with virtual media deployments over L3, you may need to provide an alternative
IP address for the remote nodes to connect to:
.. code-block:: ini
[deploy]
http_url = <HTTP server URL internal to the provisioning network>
external_http_url = <HTTP server URL with a routable IP address>
You may also need to override the callback URL, which is normally fetched from
the service catalog or configured in the ``[service_catalog]`` section:
.. code-block:: ini
[deploy]
external_callback_url = <Bare Metal API URL with a routable IP address>

View File

@ -71,6 +71,9 @@ ironic configuration file to match the HTTP server configurations.
http_url = http://example.com
http_root = /httpboot
.. note::
See also: :ref:`l3-external-ip`.
Each HTTP server should be configured to follow symlinks for images
accessible from HTTP service. Please refer to configuration option
``FollowSymLinks`` if you are using Apache HTTP server, or

View File

@ -332,6 +332,8 @@ on the Bare Metal service node(s) where ``ironic-conductor`` is running.
# http://192.1.2.3:8080 (string value)
http_url=http://192.168.0.2:8080
See also: :ref:`l3-external-ip`.
#. Install the iPXE package with the boot images:
Ubuntu::

View File

@ -27,6 +27,17 @@ opts = [
cfg.StrOpt('http_root',
default='/httpboot',
help=_("ironic-conductor node's HTTP root path.")),
cfg.StrOpt('external_http_url',
help=_("URL of the ironic-conductor node's HTTP server for "
"boot methods such as virtual media, "
"where images could be served outside of the "
"provisioning network. Does not apply when Swift is "
"used. Defaults to http_url.")),
cfg.StrOpt('external_callback_url',
help=_("Agent callback URL of the bare metal API for boot "
"methods such as virtual media, where images could be "
"served outside of the provisioning network. Defaults "
"to the configuration from [service_catalog].")),
cfg.BoolOpt('enable_ata_secure_erase',
default=True,
mutable=True,

View File

@ -617,6 +617,9 @@ def is_software_raid(node):
return software_raid
IPA_URL_PARAM_NAME = 'ipa-api-url'
def build_agent_options(node):
"""Build the options to be passed to the agent ramdisk.
@ -625,7 +628,7 @@ def build_agent_options(node):
agent ramdisk.
"""
agent_config_opts = {
'ipa-api-url': get_ironic_api_url(),
IPA_URL_PARAM_NAME: get_ironic_api_url(),
}
return agent_config_opts

View File

@ -214,8 +214,8 @@ class ImageHandler(object):
shutil.copyfile(image_file, published_file)
os.chmod(published_file, self._file_permission)
image_url = os.path.join(
CONF.deploy.http_url, self._image_subdir, object_name)
http_url = CONF.deploy.external_http_url or CONF.deploy.http_url
image_url = os.path.join(http_url, self._image_subdir, object_name)
image_url = self._append_filename_param(
image_url, os.path.basename(image_file))
@ -244,6 +244,16 @@ def cleanup_iso_image(task):
suffix='.iso')
def override_api_url(params):
if not CONF.deploy.external_callback_url:
return params
params = params or {}
params[deploy_utils.IPA_URL_PARAM_NAME] = \
CONF.deploy.external_callback_url.rstrip('/')
return params
def prepare_floppy_image(task, params=None):
"""Prepares the floppy image for passing the parameters.
@ -264,6 +274,7 @@ def prepare_floppy_image(task, params=None):
:returns: image URL for the floppy image.
"""
object_name = _get_name(task.node, prefix='image')
params = override_api_url(params)
LOG.debug("Trying to create floppy image for node "
"%(node)s", {'node': task.node.uuid})
@ -533,6 +544,8 @@ def prepare_deploy_iso(task, params, mode, d_info):
iso_href = _find_param(iso_str, d_info)
bootloader_href = _find_param(bootloader_str, d_info)
params = override_api_url(params)
# TODO(TheJulia): At some point we should support something like
# boot_iso for the deploy interface, perhaps when we support config
# injection.

View File

@ -121,6 +121,28 @@ class RedfishImageHandlerTestCase(db_base.DbTestCase):
'file.iso', '/httpboot/redfish/boot.iso')
mock_chmod.assert_called_once_with('file.iso', 0o644)
@mock.patch.object(os, 'chmod', autospec=True)
@mock.patch.object(image_utils, 'shutil', autospec=True)
@mock.patch.object(os, 'link', autospec=True)
@mock.patch.object(os, 'mkdir', autospec=True)
def test_publish_image_external_ip(
self, mock_mkdir, mock_link, mock_shutil, mock_chmod):
self.config(use_swift=False, group='redfish')
self.config(http_url='http://localhost',
external_http_url='http://non-local.host',
group='deploy')
img_handler_obj = image_utils.ImageHandler(self.node.driver)
url = img_handler_obj.publish_image('file.iso', 'boot.iso')
self.assertEqual(
'http://non-local.host/redfish/boot.iso?filename=file.iso', url)
mock_mkdir.assert_called_once_with('/httpboot/redfish', 0o755)
mock_link.assert_called_once_with(
'file.iso', '/httpboot/redfish/boot.iso')
mock_chmod.assert_called_once_with('file.iso', 0o644)
@mock.patch.object(os, 'chmod', autospec=True)
@mock.patch.object(image_utils, 'shutil', autospec=True)
@mock.patch.object(os, 'link', autospec=True)
@ -248,7 +270,31 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
mock.ANY, object_name)
mock_create_vfat_image.assert_called_once_with(
mock.ANY, parameters=mock.ANY)
mock.ANY, parameters=None)
self.assertEqual(expected_url, url)
@mock.patch.object(image_utils.ImageHandler, 'publish_image',
autospec=True)
@mock.patch.object(images, 'create_vfat_image', autospec=True)
def test_prepare_floppy_image_with_external_ip(
self, mock_create_vfat_image, mock_publish_image):
self.config(external_callback_url='http://callback/', group='deploy')
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
expected_url = 'https://a.b/c.f?e=f'
mock_publish_image.return_value = expected_url
url = image_utils.prepare_floppy_image(task)
object_name = 'image-%s' % task.node.uuid
mock_publish_image.assert_called_once_with(mock.ANY,
mock.ANY, object_name)
mock_create_vfat_image.assert_called_once_with(
mock.ANY, parameters={"ipa-api-url": "http://callback"})
self.assertEqual(expected_url, url)
@ -632,6 +678,28 @@ cafile = /etc/ironic-python-agent/ironic.crt
task, 'kernel', 'ramdisk', 'bootloader', params={},
inject_files=expected_files, base_iso=None)
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
def test_prepare_deploy_iso_external_ip(self, mock__prepare_iso_image):
self.config(external_callback_url='http://callback/', group='deploy')
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
d_info = {
'ilo_deploy_kernel': 'kernel',
'ilo_deploy_ramdisk': 'ramdisk',
'ilo_bootloader': 'bootloader'
}
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, 'kernel', 'ramdisk', 'bootloader',
params={'ipa-api-url': 'http://callback'},
inject_files={}, base_iso=None)
@mock.patch.object(image_utils, '_find_param', autospec=True)
@mock.patch.object(image_utils, '_prepare_iso_image', autospec=True)
@mock.patch.object(images, 'create_boot_iso', autospec=True)

View File

@ -0,0 +1,13 @@
---
features:
- |
Provides operator ability to override URL settings required for
provisioning/cleaning in the event of virtual media based deployment.
These scenarios tend to require more delineation than more traditional
deployments as they often have a different environmental security
requirements. Set these two new configuration options using an IP
address that is available to these nodes (both the ramdisk and the BMCs)::
[deploy]
external_http_url = <routable URL of the HTTP server>
external_callback_url = <routable URL of bare metal API>