Follow-up for ramdisk deploy configdrive support
1) Do not issue a warning if the boot interface supports configdrive 2) Implement missing support for Swift URLs in configdrives Change-Id: I4b06478a14ab514d785f8e3972e5afbd79f8d3b5
This commit is contained in:
@@ -272,8 +272,7 @@ parameter injection, as such the ``[instance_info]/kernel_append_params``
|
|||||||
setting is ignored.
|
setting is ignored.
|
||||||
|
|
||||||
Configuration drives are supported starting with the Wallaby release
|
Configuration drives are supported starting with the Wallaby release
|
||||||
for nodes that have a free virtual USB slot. The configuration option
|
for nodes that have a free virtual USB slot.
|
||||||
``[deploy]configdrive_use_object_store`` must be set to ``False`` for now.
|
|
||||||
|
|
||||||
Layer 3 or DHCP-less ramdisk booting
|
Layer 3 or DHCP-less ramdisk booting
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@@ -346,7 +346,7 @@ def create_esp_image_for_uefi(
|
|||||||
raise exception.ImageCreationFailed(image_type='iso', error=e)
|
raise exception.ImageCreationFailed(image_type='iso', error=e)
|
||||||
|
|
||||||
|
|
||||||
def fetch(context, image_href, path, force_raw=False):
|
def fetch_into(context, image_href, image_file):
|
||||||
# TODO(vish): Improve context handling and add owner and auth data
|
# TODO(vish): Improve context handling and add owner and auth data
|
||||||
# when it is added to glance. Right now there is no
|
# when it is added to glance. Right now there is no
|
||||||
# auth checking in glance, so we assume that access was
|
# auth checking in glance, so we assume that access was
|
||||||
@@ -357,9 +357,13 @@ def fetch(context, image_href, path, force_raw=False):
|
|||||||
{'image_service': image_service.__class__,
|
{'image_service': image_service.__class__,
|
||||||
'image_href': image_href})
|
'image_href': image_href})
|
||||||
|
|
||||||
|
image_service.download(image_href, image_file)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch(context, image_href, path, force_raw=False):
|
||||||
with fileutils.remove_path_on_error(path):
|
with fileutils.remove_path_on_error(path):
|
||||||
with open(path, "wb") as image_file:
|
with open(path, "wb") as image_file:
|
||||||
image_service.download(image_href, image_file)
|
fetch_into(context, image_href, image_file)
|
||||||
|
|
||||||
if force_raw:
|
if force_raw:
|
||||||
image_to_raw(image_href, path, "%s.part" % path)
|
image_to_raw(image_href, path, "%s.part" % path)
|
||||||
|
@@ -299,20 +299,26 @@ def cleanup_floppy_image(task):
|
|||||||
def prepare_configdrive_image(task, content):
|
def prepare_configdrive_image(task, content):
|
||||||
"""Prepare an image with configdrive.
|
"""Prepare an image with configdrive.
|
||||||
|
|
||||||
|
Decodes base64 contents and writes it into a disk image that can be
|
||||||
|
attached e.g. to a virtual USB device. Images stored in Swift are
|
||||||
|
downloaded first.
|
||||||
|
|
||||||
:param task: a TaskManager instance containing the node to act on.
|
:param task: a TaskManager instance containing the node to act on.
|
||||||
:param content: Config drive as a base64-encoded string.
|
:param content: Config drive as a base64-encoded string.
|
||||||
:raises: ImageCreationFailed, if it failed while creating the image.
|
:raises: ImageCreationFailed, if it failed while creating the image.
|
||||||
:raises: SwiftOperationError, if any operation with Swift fails.
|
:raises: SwiftOperationError, if any operation with Swift fails.
|
||||||
:returns: image URL for the image.
|
:returns: image URL for the image.
|
||||||
"""
|
"""
|
||||||
# FIXME(dtantsur): download and convert?
|
|
||||||
if '://' in content:
|
|
||||||
raise exception.ImageCreationFailed(
|
|
||||||
_('URLs are not supported for configdrive images yet'))
|
|
||||||
|
|
||||||
with tempfile.TemporaryFile(dir=CONF.tempdir) as comp_tmpfile_obj:
|
with tempfile.TemporaryFile(dir=CONF.tempdir) as comp_tmpfile_obj:
|
||||||
comp_tmpfile_obj.write(base64.b64decode(content))
|
if '://' in content:
|
||||||
|
with tempfile.TemporaryFile(dir=CONF.tempdir) as tmpfile2:
|
||||||
|
images.fetch_into(task.context, content, tmpfile2)
|
||||||
|
tmpfile2.seek(0)
|
||||||
|
base64.decode(tmpfile2, comp_tmpfile_obj)
|
||||||
|
else:
|
||||||
|
comp_tmpfile_obj.write(base64.b64decode(content))
|
||||||
comp_tmpfile_obj.seek(0)
|
comp_tmpfile_obj.seek(0)
|
||||||
|
|
||||||
gz = gzip.GzipFile(fileobj=comp_tmpfile_obj, mode='rb')
|
gz = gzip.GzipFile(fileobj=comp_tmpfile_obj, mode='rb')
|
||||||
with tempfile.NamedTemporaryFile(
|
with tempfile.NamedTemporaryFile(
|
||||||
dir=CONF.tempdir, suffix='.img') as image_tmpfile_obj:
|
dir=CONF.tempdir, suffix='.img') as image_tmpfile_obj:
|
||||||
|
@@ -58,12 +58,15 @@ class PXERamdiskDeploy(agent_base.AgentBaseMixin, agent_base.HeartbeatMixin,
|
|||||||
@base.deploy_step(priority=100)
|
@base.deploy_step(priority=100)
|
||||||
@task_manager.require_exclusive_lock
|
@task_manager.require_exclusive_lock
|
||||||
def deploy(self, task):
|
def deploy(self, task):
|
||||||
if 'configdrive' in task.node.instance_info:
|
if ('configdrive' in task.node.instance_info
|
||||||
LOG.warning('A configuration drive is present with '
|
and 'ramdisk_boot_configdrive' not in
|
||||||
'in the deployment request of node %(node)s. '
|
task.driver.boot.capabilities):
|
||||||
'The configuration drive will be ignored for '
|
# TODO(dtantsur): make it an actual error?
|
||||||
'this deployment.',
|
LOG.warning('A configuration drive is present in the ramdisk '
|
||||||
{'node': task.node})
|
'deployment request of node %(node)s with boot '
|
||||||
|
'interface %(drv)s. The configuration drive will be '
|
||||||
|
'ignored for this deployment.',
|
||||||
|
{'node': task.node, 'drv': task.node.boot_interface})
|
||||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||||
# Tenant neworks must enable connectivity to the boot
|
# Tenant neworks must enable connectivity to the boot
|
||||||
# location, as reboot() can otherwise be very problematic.
|
# location, as reboot() can otherwise be very problematic.
|
||||||
|
@@ -279,7 +279,8 @@ class RedfishVirtualMediaBoot(base.BootInterface):
|
|||||||
`[instance_info]image_source` node property.
|
`[instance_info]image_source` node property.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
capabilities = ['iscsi_volume_boot', 'ramdisk_boot']
|
capabilities = ['iscsi_volume_boot', 'ramdisk_boot',
|
||||||
|
'ramdisk_boot_configdrive']
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize the Redfish virtual media boot interface.
|
"""Initialize the Redfish virtual media boot interface.
|
||||||
|
@@ -304,6 +304,30 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
|
|||||||
result = image_utils.prepare_configdrive_image(task, encoded)
|
result = image_utils.prepare_configdrive_image(task, encoded)
|
||||||
self.assertEqual(expected_url, result)
|
self.assertEqual(expected_url, result)
|
||||||
|
|
||||||
|
@mock.patch.object(images, 'fetch_into', autospec=True)
|
||||||
|
@mock.patch.object(image_utils, 'prepare_disk_image', autospec=True)
|
||||||
|
def test_prepare_configdrive_image_url(self, mock_prepare, mock_fetch):
|
||||||
|
content = 'https://swift/path'
|
||||||
|
expected_url = 'https://a.b/c.f?e=f'
|
||||||
|
encoded = b'H4sIAPJ8418C/0vOzytJzSsBAKkwxf4HAAAA'
|
||||||
|
|
||||||
|
def _fetch(context, image_href, image_file):
|
||||||
|
self.assertEqual(content, image_href)
|
||||||
|
image_file.write(encoded)
|
||||||
|
|
||||||
|
def _prepare(task, content, prefix):
|
||||||
|
with open(content, 'rb') as fp:
|
||||||
|
self.assertEqual(b'content', fp.read())
|
||||||
|
return expected_url
|
||||||
|
|
||||||
|
mock_fetch.side_effect = _fetch
|
||||||
|
mock_prepare.side_effect = _prepare
|
||||||
|
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
result = image_utils.prepare_configdrive_image(task, content)
|
||||||
|
self.assertEqual(expected_url, result)
|
||||||
|
|
||||||
@mock.patch.object(image_utils.ImageHandler, 'unpublish_image',
|
@mock.patch.object(image_utils.ImageHandler, 'unpublish_image',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
def test_cleanup_iso_image(self, mock_unpublish):
|
def test_cleanup_iso_image(self, mock_unpublish):
|
||||||
|
@@ -3,5 +3,4 @@ features:
|
|||||||
- |
|
- |
|
||||||
Supports attaching configdrives when doing ``ramdisk`` deploy with the
|
Supports attaching configdrives when doing ``ramdisk`` deploy with the
|
||||||
``redfish-virtual-media`` boot. A configdrive is attached to a free USB
|
``redfish-virtual-media`` boot. A configdrive is attached to a free USB
|
||||||
slot. Swift must not be used for configdrive storage (this limitation will
|
slot.
|
||||||
be fixed later).
|
|
||||||
|
Reference in New Issue
Block a user