From 98958cd0a49d987290b84a76ce5d6eb95e067148 Mon Sep 17 00:00:00 2001 From: Bob Fournier Date: Tue, 17 Nov 2020 08:18:10 -0500 Subject: [PATCH] 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 --- ironic/drivers/drac.py | 3 +- .../drivers/modules/drac/vendor_passthru.py | 8 ++ ironic/drivers/modules/redfish/boot.py | 16 +-- ironic/drivers/modules/redfish/vendor.py | 92 ++++++++++++++ ironic/drivers/redfish.py | 6 + .../unit/drivers/modules/redfish/test_boot.py | 36 +++--- .../drivers/modules/redfish/test_vendor.py | 116 ++++++++++++++++++ ...assthru-eject-vmedia-e4456320ee1c70c1.yaml | 7 ++ setup.cfg | 2 + 9 files changed, 259 insertions(+), 27 deletions(-) create mode 100644 ironic/drivers/modules/redfish/vendor.py create mode 100644 ironic/tests/unit/drivers/modules/redfish/test_vendor.py create mode 100644 releasenotes/notes/vendor-passthru-eject-vmedia-e4456320ee1c70c1.yaml diff --git a/ironic/drivers/drac.py b/ironic/drivers/drac.py index 9c58ea079e..87d7e7217e 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 ff43cc95ce..fb25397a93 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 15c8352d1b..67812acc3e 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 0000000000..76927c2c73 --- /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 feb579af0d..9a00b04970 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 540b99cebd..bc350b1b16 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 0000000000..156b3defbc --- /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 0000000000..95e0696187 --- /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 75da36990f..cd2888928e 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