Merge "Add filename
parameter to Redfish virtual media boot URL"
This commit is contained in:
commit
20ca7d7cbf
@ -16,6 +16,7 @@ import tempfile
|
|||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
from six.moves.urllib import parse as urlparse
|
||||||
|
|
||||||
from ironic.common import boot_devices
|
from ironic.common import boot_devices
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
@ -218,6 +219,36 @@ class RedfishVirtualMediaBoot(base.BootInterface):
|
|||||||
{'node': task.node.uuid, 'image': object_name,
|
{'node': task.node.uuid, 'image': object_name,
|
||||||
'error': e})
|
'error': e})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _append_filename_param(url, filename):
|
||||||
|
"""Append 'filename=<file>' parameter to given URL.
|
||||||
|
|
||||||
|
Some BMCs seem to validate boot image URL requiring the URL to end
|
||||||
|
with something resembling ISO image file name.
|
||||||
|
|
||||||
|
This function tries to add, hopefully, meaningless 'filename'
|
||||||
|
parameter to URL's query string in hope to make the entire boot image
|
||||||
|
URL looking more convincing to the BMC.
|
||||||
|
|
||||||
|
However, `url` with fragments might not get cured by this hack.
|
||||||
|
|
||||||
|
:param url: a URL to work on
|
||||||
|
:param filename: name of the file to append to the URL
|
||||||
|
:returns: original URL with 'filename' parameter appended
|
||||||
|
"""
|
||||||
|
parsed_url = urlparse.urlparse(url)
|
||||||
|
parsed_qs = urlparse.parse_qsl(parsed_url.query)
|
||||||
|
|
||||||
|
has_filename = [x for x in parsed_qs if x[0].lower() == 'filename']
|
||||||
|
if has_filename:
|
||||||
|
return url
|
||||||
|
|
||||||
|
parsed_qs.append(('filename', filename))
|
||||||
|
parsed_url = list(parsed_url)
|
||||||
|
parsed_url[4] = urlparse.urlencode(parsed_qs)
|
||||||
|
|
||||||
|
return urlparse.urlunparse(parsed_url)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_floppy_image_name(node):
|
def _get_floppy_image_name(node):
|
||||||
"""Returns the floppy image name for a given node.
|
"""Returns the floppy image name for a given node.
|
||||||
@ -279,6 +310,8 @@ class RedfishVirtualMediaBoot(base.BootInterface):
|
|||||||
|
|
||||||
image_url = swift_api.get_temp_url(container, object_name, timeout)
|
image_url = swift_api.get_temp_url(container, object_name, timeout)
|
||||||
|
|
||||||
|
image_url = cls._append_filename_param(image_url, 'bootme.img')
|
||||||
|
|
||||||
LOG.debug("Created floppy image %(name)s in Swift for node %(node)s, "
|
LOG.debug("Created floppy image %(name)s in Swift for node %(node)s, "
|
||||||
"exposed as temporary URL "
|
"exposed as temporary URL "
|
||||||
"%(url)s", {'node': task.node.uuid,
|
"%(url)s", {'node': task.node.uuid,
|
||||||
@ -388,6 +421,9 @@ class RedfishVirtualMediaBoot(base.BootInterface):
|
|||||||
boot_iso_url = swift_api.get_temp_url(
|
boot_iso_url = swift_api.get_temp_url(
|
||||||
container, iso_object_name, timeout)
|
container, iso_object_name, timeout)
|
||||||
|
|
||||||
|
boot_iso_url = cls._append_filename_param(
|
||||||
|
boot_iso_url, 'bootme.iso')
|
||||||
|
|
||||||
LOG.debug("Created ISO %(name)s in Swift for node %(node)s, exposed "
|
LOG.debug("Created ISO %(name)s in Swift for node %(node)s, exposed "
|
||||||
"as temporary URL %(url)s", {'node': task.node.uuid,
|
"as temporary URL %(url)s", {'node': task.node.uuid,
|
||||||
'name': iso_object_name,
|
'name': iso_object_name,
|
||||||
|
@ -175,6 +175,30 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
|||||||
task.driver.boot._parse_deploy_info,
|
task.driver.boot._parse_deploy_info,
|
||||||
task.node)
|
task.node)
|
||||||
|
|
||||||
|
def test__append_filename_param_without_qs(self):
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
res = task.driver.boot._append_filename_param(
|
||||||
|
'http://a.b/c', 'b.img')
|
||||||
|
expected = 'http://a.b/c?filename=b.img'
|
||||||
|
self.assertEqual(expected, res)
|
||||||
|
|
||||||
|
def test__append_filename_param_with_qs(self):
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
res = task.driver.boot._append_filename_param(
|
||||||
|
'http://a.b/c?d=e&f=g', 'b.img')
|
||||||
|
expected = 'http://a.b/c?d=e&f=g&filename=b.img'
|
||||||
|
self.assertEqual(expected, res)
|
||||||
|
|
||||||
|
def test__append_filename_param_with_filename(self):
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
res = task.driver.boot._append_filename_param(
|
||||||
|
'http://a.b/c?filename=bootme.img', 'b.img')
|
||||||
|
expected = 'http://a.b/c?filename=bootme.img'
|
||||||
|
self.assertEqual(expected, res)
|
||||||
|
|
||||||
@mock.patch.object(redfish_boot, 'swift', autospec=True)
|
@mock.patch.object(redfish_boot, 'swift', autospec=True)
|
||||||
def test__cleanup_floppy_image(self, mock_swift):
|
def test__cleanup_floppy_image(self, mock_swift):
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
@ -193,14 +217,18 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
|||||||
def test__prepare_floppy_image(self, mock_create_vfat_image, mock_swift):
|
def test__prepare_floppy_image(self, mock_create_vfat_image, mock_swift):
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=True) as task:
|
shared=True) as task:
|
||||||
task.driver.boot._prepare_floppy_image(task)
|
mock_swift_api = mock_swift.SwiftAPI.return_value
|
||||||
|
mock_swift_api.get_temp_url.return_value = 'https://a.b/c.f?e=f'
|
||||||
|
|
||||||
|
url = task.driver.boot._prepare_floppy_image(task)
|
||||||
|
|
||||||
|
self.assertIn('filename=bootme.img', url)
|
||||||
|
|
||||||
|
mock_swift.SwiftAPI.assert_called_once_with()
|
||||||
|
|
||||||
mock_create_vfat_image.assert_called_once_with(
|
mock_create_vfat_image.assert_called_once_with(
|
||||||
mock.ANY, parameters=mock.ANY)
|
mock.ANY, parameters=mock.ANY)
|
||||||
|
|
||||||
mock_swift.SwiftAPI.assert_called_once_with()
|
|
||||||
mock_swift_api = mock_swift.SwiftAPI.return_value
|
|
||||||
|
|
||||||
mock_swift_api.create_object.assert_called_once_with(
|
mock_swift_api.create_object.assert_called_once_with(
|
||||||
mock.ANY, mock.ANY, mock.ANY, mock.ANY)
|
mock.ANY, mock.ANY, mock.ANY, mock.ANY)
|
||||||
|
|
||||||
@ -234,7 +262,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
|||||||
task, 'http://kernel/img', 'http://ramdisk/img',
|
task, 'http://kernel/img', 'http://ramdisk/img',
|
||||||
'http://bootloader/img', root_uuid=task.node.uuid)
|
'http://bootloader/img', root_uuid=task.node.uuid)
|
||||||
|
|
||||||
self.assertTrue(url)
|
self.assertIn('filename=bootme.iso', url)
|
||||||
|
|
||||||
mock_create_boot_iso.assert_called_once_with(
|
mock_create_boot_iso.assert_called_once_with(
|
||||||
mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img',
|
mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img',
|
||||||
@ -263,7 +291,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
|
|||||||
task, 'http://kernel/img', 'http://ramdisk/img',
|
task, 'http://kernel/img', 'http://ramdisk/img',
|
||||||
bootloader_href=None, root_uuid=task.node.uuid)
|
bootloader_href=None, root_uuid=task.node.uuid)
|
||||||
|
|
||||||
self.assertTrue(url)
|
self.assertIn('filename=bootme.iso', url)
|
||||||
|
|
||||||
mock_create_boot_iso.assert_called_once_with(
|
mock_create_boot_iso.assert_called_once_with(
|
||||||
mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img',
|
mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user