summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Fournier <bfournie@redhat.com>2020-11-17 08:18:10 -0500
committerBob Fournier <bfournie@redhat.com>2020-11-24 09:25:44 -0500
commit98958cd0a49d987290b84a76ce5d6eb95e067148 (patch)
treee0a508a0a4cf7b1a106157a23cf1449af3f6f33a
parentb5932bc6bf6ad96ee6a03c6d83e94e8824fd7e6f (diff)
downloadironic-98958cd0a49d987290b84a76ce5d6eb95e067148.tar.gz
Add vendor_passthru method for virtual media
Add a vendor_passthru method to eject_vmedia for Redfish and idrac. Story: 2008363 Task: 41271 Change-Id: Ib5ae16bacfd79f479a9aa8fbf69edc5cfdf73ce3
-rw-r--r--ironic/drivers/drac.py3
-rw-r--r--ironic/drivers/modules/drac/vendor_passthru.py8
-rw-r--r--ironic/drivers/modules/redfish/boot.py16
-rw-r--r--ironic/drivers/modules/redfish/vendor.py92
-rw-r--r--ironic/drivers/redfish.py6
-rw-r--r--ironic/tests/unit/drivers/modules/redfish/test_boot.py36
-rw-r--r--ironic/tests/unit/drivers/modules/redfish/test_vendor.py116
-rw-r--r--releasenotes/notes/vendor-passthru-eject-vmedia-e4456320ee1c70c1.yaml7
-rw-r--r--setup.cfg2
9 files changed, 259 insertions, 27 deletions
diff --git a/ironic/drivers/drac.py b/ironic/drivers/drac.py
index 9c58ea079..87d7e7217 100644
--- a/ironic/drivers/drac.py
+++ b/ironic/drivers/drac.py
@@ -81,4 +81,5 @@ class IDRACHardware(generic.GenericHardware):
def supported_vendor_interfaces(self):
"""List of supported vendor interfaces."""
return [vendor_passthru.DracWSManVendorPassthru,
- vendor_passthru.DracVendorPassthru, noop.NoVendor]
+ vendor_passthru.DracVendorPassthru,
+ vendor_passthru.DracRedfishVendorPassthru, noop.NoVendor]
diff --git a/ironic/drivers/modules/drac/vendor_passthru.py b/ironic/drivers/modules/drac/vendor_passthru.py
index ff43cc95c..fb25397a9 100644
--- a/ironic/drivers/modules/drac/vendor_passthru.py
+++ b/ironic/drivers/modules/drac/vendor_passthru.py
@@ -24,6 +24,7 @@ from ironic.drivers import base
from ironic.drivers.modules.drac import bios as drac_bios
from ironic.drivers.modules.drac import common as drac_common
from ironic.drivers.modules.drac import job as drac_job
+from ironic.drivers.modules.redfish import vendor as redfish_vendor
LOG = logging.getLogger(__name__)
@@ -190,3 +191,10 @@ class DracVendorPassthru(DracWSManVendorPassthru):
LOG.warning("Vendor passthru interface 'idrac' is deprecated and may "
"be removed in a future release. Use 'idrac-wsman' "
"instead.")
+
+
+class DracRedfishVendorPassthru(redfish_vendor.RedfishVendorPassthru):
+ """iDRAC Redfish interface for vendor_passthru.
+
+ Use the Redfish implementation for vendor passthru.
+ """
diff --git a/ironic/drivers/modules/redfish/boot.py b/ironic/drivers/modules/redfish/boot.py
index 15c8352d1..67812acc3 100644
--- a/ironic/drivers/modules/redfish/boot.py
+++ b/ironic/drivers/modules/redfish/boot.py
@@ -183,7 +183,7 @@ def _insert_vmedia(task, boot_url, boot_device):
_('No suitable virtual media device found'))
-def _eject_vmedia(task, boot_device=None):
+def eject_vmedia(task, boot_device=None):
"""Eject virtual CDs and DVDs
:param task: A task from TaskManager.
@@ -430,7 +430,7 @@ class RedfishVirtualMediaBoot(base.BootInterface):
floppy_ref = image_utils.prepare_floppy_image(
task, params=ramdisk_params)
- _eject_vmedia(task, sushy.VIRTUAL_MEDIA_FLOPPY)
+ eject_vmedia(task, sushy.VIRTUAL_MEDIA_FLOPPY)
_insert_vmedia(
task, floppy_ref, sushy.VIRTUAL_MEDIA_FLOPPY)
@@ -447,7 +447,7 @@ class RedfishVirtualMediaBoot(base.BootInterface):
iso_ref = image_utils.prepare_deploy_iso(task, ramdisk_params,
mode, d_info)
- _eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
+ eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
_insert_vmedia(task, iso_ref, sushy.VIRTUAL_MEDIA_CD)
boot_mode_utils.sync_boot_mode(task)
@@ -474,12 +474,12 @@ class RedfishVirtualMediaBoot(base.BootInterface):
LOG.debug("Cleaning up deploy boot for "
"%(node)s", {'node': task.node.uuid})
- _eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
+ eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
image_utils.cleanup_iso_image(task)
if (config_via_floppy
and _has_vmedia_device(task, sushy.VIRTUAL_MEDIA_FLOPPY)):
- _eject_vmedia(task, sushy.VIRTUAL_MEDIA_FLOPPY)
+ eject_vmedia(task, sushy.VIRTUAL_MEDIA_FLOPPY)
image_utils.cleanup_floppy_image(task)
@@ -533,7 +533,7 @@ class RedfishVirtualMediaBoot(base.BootInterface):
deploy_info = _parse_deploy_info(node)
iso_ref = image_utils.prepare_boot_iso(task, deploy_info, **params)
- _eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
+ eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
_insert_vmedia(task, iso_ref, sushy.VIRTUAL_MEDIA_CD)
boot_mode_utils.sync_boot_mode(task)
@@ -556,11 +556,11 @@ class RedfishVirtualMediaBoot(base.BootInterface):
LOG.debug("Cleaning up instance boot for "
"%(node)s", {'node': task.node.uuid})
- _eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
+ eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
d_info = task.node.driver_info
config_via_floppy = d_info.get('config_via_floppy')
if config_via_floppy:
- _eject_vmedia(task, sushy.VIRTUAL_MEDIA_FLOPPY)
+ eject_vmedia(task, sushy.VIRTUAL_MEDIA_FLOPPY)
image_utils.cleanup_iso_image(task)
diff --git a/ironic/drivers/modules/redfish/vendor.py b/ironic/drivers/modules/redfish/vendor.py
new file mode 100644
index 000000000..76927c2c7
--- /dev/null
+++ b/ironic/drivers/modules/redfish/vendor.py
@@ -0,0 +1,92 @@
+# Copyright 2015 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+"""
+Vendor Interface for Redfish drivers and its supporting methods.
+"""
+
+from ironic_lib import metrics_utils
+
+from ironic.common import exception
+from ironic.common.i18n import _
+from ironic.drivers import base
+from ironic.drivers.modules.redfish import boot as redfish_boot
+from ironic.drivers.modules.redfish import utils as redfish_utils
+
+METRICS = metrics_utils.get_metrics_logger(__name__)
+
+
+class RedfishVendorPassthru(base.VendorInterface):
+ """Vendor-specific interfaces for Redfish drivers."""
+
+ def get_properties(self):
+ return {}
+
+ @METRICS.timer('RedfishVendorPassthru.validate')
+ def validate(self, task, method, **kwargs):
+ """Validate vendor-specific actions.
+
+ Checks if a valid vendor passthru method was passed and validates
+ the parameters for the vendor passthru method.
+
+ :param task: a TaskManager instance containing the node to act on.
+ :param method: method to be validated.
+ :param kwargs: kwargs containing the vendor passthru method's
+ parameters.
+ :raises: InvalidParameterValue, if any of the parameters have invalid
+ value.
+ """
+ if method == 'eject_vmedia':
+ self._validate_eject_vmedia(task, kwargs)
+ return
+ super(RedfishVendorPassthru, self).validate(task, method, **kwargs)
+
+ def _validate_eject_vmedia(self, task, kwargs):
+ """Verify that the boot_device input is valid."""
+
+ # If a boot device is provided check that it's valid.
+ # It is OK to eject if already ejected
+ boot_device = kwargs.get('boot_device')
+
+ if not boot_device:
+ return
+
+ system = redfish_utils.get_system(task.node)
+
+ for manager in system.managers:
+ for v_media in manager.virtual_media.get_members():
+ if boot_device not in v_media.media_types:
+ raise exception.InvalidParameterValue(_(
+ "Boot device %s is not a valid value ") % boot_device)
+
+ @METRICS.timer('RedfishVendorPassthru.eject_vmedia')
+ @base.passthru(['POST'],
+ description=_("Eject a virtual media device. If no device "
+ "is provided than all attached devices will "
+ "be ejected. "
+ "Optional arguments: "
+ "'boot_device' - the boot device to eject, "
+ "either 'cd', 'dvd', 'usb', or 'floppy'"))
+ # @task_manager.require_exclusive_lock
+ def eject_vmedia(self, task, **kwargs):
+ """Eject a virtual media device.
+
+ :param task: A TaskManager object.
+ :param kwargs: The arguments sent with vendor passthru. The optional
+ kwargs are::
+ 'boot_device': the boot device to eject
+ """
+
+ # If boot_device not provided all vmedia devices will be ejected
+ boot_device = kwargs.get('boot_device')
+ redfish_boot.eject_vmedia(task, boot_device)
diff --git a/ironic/drivers/redfish.py b/ironic/drivers/redfish.py
index feb579af0..9a00b0497 100644
--- a/ironic/drivers/redfish.py
+++ b/ironic/drivers/redfish.py
@@ -24,6 +24,7 @@ from ironic.drivers.modules.redfish import boot as redfish_boot
from ironic.drivers.modules.redfish import inspect as redfish_inspect
from ironic.drivers.modules.redfish import management as redfish_mgmt
from ironic.drivers.modules.redfish import power as redfish_power
+from ironic.drivers.modules.redfish import vendor as redfish_vendor
class RedfishHardware(generic.GenericHardware):
@@ -57,3 +58,8 @@ class RedfishHardware(generic.GenericHardware):
# vendors support.
return [ipxe.iPXEBoot, pxe.PXEBoot,
redfish_boot.RedfishVirtualMediaBoot]
+
+ @property
+ def supported_vendor_interfaces(self):
+ """List of supported vendor interfaces."""
+ return [redfish_vendor.RedfishVendorPassthru, noop.NoVendor]
diff --git a/ironic/tests/unit/drivers/modules/redfish/test_boot.py b/ironic/tests/unit/drivers/modules/redfish/test_boot.py
index 540b99ceb..bc350b1b1 100644
--- a/ironic/tests/unit/drivers/modules/redfish/test_boot.py
+++ b/ironic/tests/unit/drivers/modules/redfish/test_boot.py
@@ -326,7 +326,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
autospec=True)
@mock.patch.object(image_utils, 'prepare_deploy_iso', autospec=True)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_parse_driver_info', autospec=True)
@mock.patch.object(redfish_boot.manager_utils, 'node_power_action',
@@ -371,7 +371,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
autospec=True)
@mock.patch.object(image_utils, 'prepare_deploy_iso', autospec=True)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_parse_driver_info', autospec=True)
@mock.patch.object(redfish_boot.manager_utils, 'node_power_action',
@@ -417,7 +417,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(image_utils, 'prepare_floppy_image', autospec=True)
@mock.patch.object(image_utils, 'prepare_deploy_iso', autospec=True)
@mock.patch.object(redfish_boot, '_has_vmedia_device', autospec=True)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_parse_driver_info', autospec=True)
@mock.patch.object(redfish_boot.manager_utils, 'node_power_action',
@@ -482,7 +482,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_boot_mode_utils.sync_boot_mode.assert_called_once_with(task)
@mock.patch.object(redfish_boot, '_has_vmedia_device', autospec=True)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(image_utils, 'cleanup_floppy_image', autospec=True)
@mock.patch.object(redfish_boot, '_parse_driver_info', autospec=True)
@@ -517,7 +517,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'clean_up_instance', autospec=True)
@mock.patch.object(image_utils, 'prepare_boot_iso', autospec=True)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_parse_deploy_info', autospec=True)
@mock.patch.object(redfish_boot, 'manager_utils', autospec=True)
@@ -569,7 +569,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'clean_up_instance', autospec=True)
@mock.patch.object(image_utils, 'prepare_boot_iso', autospec=True)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_parse_deploy_info', autospec=True)
@mock.patch.object(redfish_boot, 'manager_utils', autospec=True)
@@ -617,7 +617,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'clean_up_instance', autospec=True)
@mock.patch.object(image_utils, 'prepare_boot_iso', autospec=True)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_parse_deploy_info', autospec=True)
@mock.patch.object(redfish_boot, 'manager_utils', autospec=True)
@@ -663,7 +663,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'clean_up_instance', autospec=True)
@mock.patch.object(image_utils, 'prepare_boot_iso', autospec=True)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_insert_vmedia', autospec=True)
@mock.patch.object(redfish_boot, '_parse_deploy_info', autospec=True)
@mock.patch.object(redfish_boot, 'manager_utils', autospec=True)
@@ -700,7 +700,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_boot_mode_utils.sync_boot_mode.assert_called_once_with(task)
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
@mock.patch.object(redfish_boot, 'manager_utils', autospec=True)
def _test_prepare_instance_local_boot(
@@ -733,7 +733,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
self.node.save()
self._test_prepare_instance_local_boot()
- @mock.patch.object(redfish_boot, '_eject_vmedia', autospec=True)
+ @mock.patch.object(redfish_boot, 'eject_vmedia', autospec=True)
@mock.patch.object(image_utils, 'cleanup_iso_image', autospec=True)
def _test_clean_up_instance(self, mock_cleanup_iso_image,
mock__eject_vmedia):
@@ -832,7 +832,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
task, 'img-url', sushy.VIRTUAL_MEDIA_CD)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
- def test__eject_vmedia_everything(self, mock_redfish_utils):
+ def test_eject_vmedia_everything(self, mock_redfish_utils):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@@ -851,13 +851,13 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_redfish_utils.get_system.return_value.managers = [
mock_manager]
- redfish_boot._eject_vmedia(task)
+ redfish_boot.eject_vmedia(task)
mock_vmedia_cd.eject_media.assert_called_once_with()
mock_vmedia_floppy.eject_media.assert_called_once_with()
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
- def test__eject_vmedia_specific(self, mock_redfish_utils):
+ def test_eject_vmedia_specific(self, mock_redfish_utils):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@@ -876,13 +876,13 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_redfish_utils.get_system.return_value.managers = [
mock_manager]
- redfish_boot._eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
+ redfish_boot.eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
mock_vmedia_cd.eject_media.assert_called_once_with()
self.assertFalse(mock_vmedia_floppy.eject_media.call_count)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
- def test__eject_vmedia_not_inserted(self, mock_redfish_utils):
+ def test_eject_vmedia_not_inserted(self, mock_redfish_utils):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@@ -901,13 +901,13 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_redfish_utils.get_system.return_value.managers = [
mock_manager]
- redfish_boot._eject_vmedia(task)
+ redfish_boot.eject_vmedia(task)
self.assertFalse(mock_vmedia_cd.eject_media.call_count)
self.assertFalse(mock_vmedia_floppy.eject_media.call_count)
@mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
- def test__eject_vmedia_unknown(self, mock_redfish_utils):
+ def test_eject_vmedia_unknown(self, mock_redfish_utils):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@@ -923,6 +923,6 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_redfish_utils.get_system.return_value.managers = [
mock_manager]
- redfish_boot._eject_vmedia(task)
+ redfish_boot.eject_vmedia(task)
self.assertFalse(mock_vmedia_cd.eject_media.call_count)
diff --git a/ironic/tests/unit/drivers/modules/redfish/test_vendor.py b/ironic/tests/unit/drivers/modules/redfish/test_vendor.py
new file mode 100644
index 000000000..156b3defb
--- /dev/null
+++ b/ironic/tests/unit/drivers/modules/redfish/test_vendor.py
@@ -0,0 +1,116 @@
+# Copyright 2018 DMTF. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from unittest import mock
+
+from oslo_utils import importutils
+
+from ironic.common import exception
+from ironic.conductor import task_manager
+from ironic.drivers.modules.redfish import boot as redfish_boot
+from ironic.drivers.modules.redfish import vendor as redfish_vendor
+from ironic.tests.unit.db import base as db_base
+from ironic.tests.unit.db import utils as db_utils
+from ironic.tests.unit.objects import utils as obj_utils
+
+sushy = importutils.try_import('sushy')
+
+INFO_DICT = db_utils.get_test_redfish_info()
+
+
+class RedfishVendorPassthruTestCase(db_base.DbTestCase):
+
+ def setUp(self):
+ super(RedfishVendorPassthruTestCase, self).setUp()
+ self.config(enabled_bios_interfaces=['redfish'],
+ enabled_hardware_types=['redfish'],
+ enabled_power_interfaces=['redfish'],
+ enabled_boot_interfaces=['redfish-virtual-media'],
+ enabled_management_interfaces=['redfish'],
+ enabled_vendor_interfaces=['redfish'])
+ self.node = obj_utils.create_test_node(
+ self.context, driver='redfish', driver_info=INFO_DICT)
+
+ @mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
+ def test_eject_vmedia_all(self, mock_redfish_utils):
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+
+ mock_vmedia_cd = mock.MagicMock(
+ inserted=True,
+ media_types=[sushy.VIRTUAL_MEDIA_CD])
+ mock_vmedia_floppy = mock.MagicMock(
+ inserted=True,
+ media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
+
+ mock_manager = mock.MagicMock()
+
+ mock_manager.virtual_media.get_members.return_value = [
+ mock_vmedia_cd, mock_vmedia_floppy]
+
+ mock_redfish_utils.get_system.return_value.managers = [
+ mock_manager]
+
+ task.driver.vendor.eject_vmedia(task)
+
+ mock_vmedia_cd.eject_media.assert_called_once_with()
+ mock_vmedia_floppy.eject_media.assert_called_once_with()
+
+ @mock.patch.object(redfish_boot, 'redfish_utils', autospec=True)
+ def test_eject_vmedia_cd(self, mock_redfish_utils):
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+
+ mock_vmedia_cd = mock.MagicMock(
+ inserted=True,
+ media_types=[sushy.VIRTUAL_MEDIA_CD])
+ mock_vmedia_floppy = mock.MagicMock(
+ inserted=True,
+ media_types=[sushy.VIRTUAL_MEDIA_FLOPPY])
+
+ mock_manager = mock.MagicMock()
+
+ mock_manager.virtual_media.get_members.return_value = [
+ mock_vmedia_cd, mock_vmedia_floppy]
+
+ mock_redfish_utils.get_system.return_value.managers = [
+ mock_manager]
+
+ task.driver.vendor.eject_vmedia(task,
+ boot_device=sushy.VIRTUAL_MEDIA_CD)
+
+ mock_vmedia_cd.eject_media.assert_called_once_with()
+ mock_vmedia_floppy.eject_media.assert_not_called()
+
+ @mock.patch.object(redfish_vendor, 'redfish_utils', autospec=True)
+ def test_eject_vmedia_invalid_dev(self, mock_redfish_utils):
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=True) as task:
+
+ mock_vmedia_cd = mock.MagicMock(
+ inserted=True,
+ media_types=[sushy.VIRTUAL_MEDIA_CD])
+
+ mock_manager = mock.MagicMock()
+
+ mock_manager.virtual_media.get_members.return_value = [
+ mock_vmedia_cd]
+
+ mock_redfish_utils.get_system.return_value.managers = [
+ mock_manager]
+
+ kwargs = {'boot_device': 'foo'}
+ self.assertRaises(
+ exception.InvalidParameterValue,
+ task.driver.vendor.validate, task, 'eject_vmedia', **kwargs)
diff --git a/releasenotes/notes/vendor-passthru-eject-vmedia-e4456320ee1c70c1.yaml b/releasenotes/notes/vendor-passthru-eject-vmedia-e4456320ee1c70c1.yaml
new file mode 100644
index 000000000..95e069618
--- /dev/null
+++ b/releasenotes/notes/vendor-passthru-eject-vmedia-e4456320ee1c70c1.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Provides a new vendor passthru method for Redfish to eject a virtual_media
+ device. A specific device can be given (either ``cd``, ``dvd``,
+ ``floppy``, or ``usb``), or if no device is provided then all attached
+ devices will be ejected.
diff --git a/setup.cfg b/setup.cfg
index 75da36990..cd2888928 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -162,9 +162,11 @@ ironic.hardware.interfaces.vendor =
ibmc = ironic.drivers.modules.ibmc.vendor:IBMCVendor
idrac = ironic.drivers.modules.drac.vendor_passthru:DracVendorPassthru
idrac-wsman = ironic.drivers.modules.drac.vendor_passthru:DracWSManVendorPassthru
+ idrac-redfish = ironic.drivers.modules.drac.vendor_passthru:DracRedfishVendorPassthru
ilo = ironic.drivers.modules.ilo.vendor:VendorPassthru
ipmitool = ironic.drivers.modules.ipmitool:VendorPassthru
no-vendor = ironic.drivers.modules.noop:NoVendor
+ redfish = ironic.drivers.modules.redfish.vendor:RedfishVendorPassthru
ironic.hardware.types =
fake-hardware = ironic.drivers.fake_hardware:FakeHardware