summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Tantsur <dtantsur@protonmail.com>2021-01-22 15:56:13 +0100
committerDmitry Tantsur <dtantsur@protonmail.com>2021-01-22 16:26:44 +0100
commitbb318008b91da907658678ee93d4ab7ae7576c00 (patch)
tree081bec4529ddf41c095b539b2fd3d1c0ab23668b
parent5640860c81f2c172d8ad1e3756546f64825c7ed4 (diff)
downloadironic-bb318008b91da907658678ee93d4ab7ae7576c00.tar.gz
redfish-virtual-media: allow a link to raw configdrive image
For historical reasons we always base64+gzip configdrives, even when accessing them via a URL. This change allows binary images to work for the redfish-virtual-media case. Change-Id: If19144de800b67275e3f8fb297f0a5c4a54b2981
-rw-r--r--doc/source/admin/drivers/redfish.rst14
-rw-r--r--ironic/common/utils.py6
-rw-r--r--ironic/drivers/modules/image_utils.py14
-rw-r--r--ironic/tests/unit/drivers/modules/test_image_utils.py32
4 files changed, 61 insertions, 5 deletions
diff --git a/doc/source/admin/drivers/redfish.rst b/doc/source/admin/drivers/redfish.rst
index 80b981984..cc107fd2a 100644
--- a/doc/source/admin/drivers/redfish.rst
+++ b/doc/source/admin/drivers/redfish.rst
@@ -272,7 +272,19 @@ parameter injection, as such the ``[instance_info]/kernel_append_params``
setting is ignored.
Configuration drives are supported starting with the Wallaby release
-for nodes that have a free virtual USB slot.
+for nodes that have a free virtual USB slot:
+
+.. code-block:: bash
+
+ baremetal node deploy <node name or UUID> \
+ --config-drive '{"meta_data": {...}, "user_data": "..."}'
+
+or via a link to a raw image:
+
+.. code-block:: bash
+
+ baremetal node deploy <node name or UUID> \
+ --config-drive http://example.com/config.img
Layer 3 or DHCP-less ramdisk booting
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/ironic/common/utils.py b/ironic/common/utils.py
index 7cc0199bf..529e02477 100644
--- a/ironic/common/utils.py
+++ b/ironic/common/utils.py
@@ -580,3 +580,9 @@ def wrap_ipv6(ip):
if ipaddress.ip_address(ip).version == 6:
return "[%s]" % ip
return ip
+
+
+def file_mime_type(path):
+ """Gets a mime type of the given file."""
+ return execute('file', '--brief', '--mime-type', path,
+ use_standard_locale=True)[0].strip()
diff --git a/ironic/drivers/modules/image_utils.py b/ironic/drivers/modules/image_utils.py
index d12d23b0a..fb61a8d1d 100644
--- a/ironic/drivers/modules/image_utils.py
+++ b/ironic/drivers/modules/image_utils.py
@@ -29,6 +29,7 @@ from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import images
from ironic.common import swift
+from ironic.common import utils
from ironic.conf import CONF
from ironic.drivers.modules import boot_mode_utils
from ironic.drivers.modules import deploy_utils
@@ -311,10 +312,17 @@ def prepare_configdrive_image(task, content):
"""
with tempfile.TemporaryFile(dir=CONF.tempdir) as comp_tmpfile_obj:
if '://' in content:
- with tempfile.TemporaryFile(dir=CONF.tempdir) as tmpfile2:
+ with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as tmpfile2:
images.fetch_into(task.context, content, tmpfile2)
- tmpfile2.seek(0)
- base64.decode(tmpfile2, comp_tmpfile_obj)
+ tmpfile2.flush()
+
+ if utils.file_mime_type(tmpfile2.name) == "text/plain":
+ tmpfile2.seek(0)
+ base64.decode(tmpfile2, comp_tmpfile_obj)
+ else:
+ # A binary image, use it as it is.
+ return prepare_disk_image(task, tmpfile2.name,
+ prefix='configdrive')
else:
comp_tmpfile_obj.write(base64.b64decode(content))
comp_tmpfile_obj.seek(0)
diff --git a/ironic/tests/unit/drivers/modules/test_image_utils.py b/ironic/tests/unit/drivers/modules/test_image_utils.py
index c4d817af2..2ebb84c5e 100644
--- a/ironic/tests/unit/drivers/modules/test_image_utils.py
+++ b/ironic/tests/unit/drivers/modules/test_image_utils.py
@@ -20,6 +20,7 @@ from unittest import mock
from oslo_utils import importutils
from ironic.common import images
+from ironic.common import utils
from ironic.conductor import task_manager
from ironic.drivers.modules import image_utils
from ironic.tests.unit.db import base as db_base
@@ -304,9 +305,11 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
result = image_utils.prepare_configdrive_image(task, encoded)
self.assertEqual(expected_url, result)
+ @mock.patch.object(utils, 'execute', autospec=True)
@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):
+ def test_prepare_configdrive_image_url(self, mock_prepare, mock_fetch,
+ mock_execute):
content = 'https://swift/path'
expected_url = 'https://a.b/c.f?e=f'
encoded = b'H4sIAPJ8418C/0vOzytJzSsBAKkwxf4HAAAA'
@@ -322,6 +325,33 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
mock_fetch.side_effect = _fetch
mock_prepare.side_effect = _prepare
+ mock_execute.return_value = 'text/plain', ''
+
+ 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(utils, 'execute', autospec=True)
+ @mock.patch.object(images, 'fetch_into', autospec=True)
+ @mock.patch.object(image_utils, 'prepare_disk_image', autospec=True)
+ def test_prepare_configdrive_image_binary_url(self, mock_prepare,
+ mock_fetch, mock_execute):
+ content = 'https://swift/path'
+ expected_url = 'https://a.b/c.f?e=f'
+
+ def _fetch(context, image_href, image_file):
+ self.assertEqual(content, image_href)
+ image_file.write(b'content')
+
+ 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
+ mock_execute.return_value = 'application/octet-stream'
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: