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:
parent
2e68c318b3
commit
133dac255f
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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::
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
13
releasenotes/notes/external-ip-5ec9b7b55a90cec4.yaml
Normal file
13
releasenotes/notes/external-ip-5ec9b7b55a90cec4.yaml
Normal 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>
|
Loading…
Reference in New Issue
Block a user