Browse Source

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
tags/16.1.0
Bob Fournier 2 months ago
parent
commit
98958cd0a4
9 changed files with 259 additions and 27 deletions
  1. +2
    -1
      ironic/drivers/drac.py
  2. +8
    -0
      ironic/drivers/modules/drac/vendor_passthru.py
  3. +8
    -8
      ironic/drivers/modules/redfish/boot.py
  4. +92
    -0
      ironic/drivers/modules/redfish/vendor.py
  5. +6
    -0
      ironic/drivers/redfish.py
  6. +18
    -18
      ironic/tests/unit/drivers/modules/redfish/test_boot.py
  7. +116
    -0
      ironic/tests/unit/drivers/modules/redfish/test_vendor.py
  8. +7
    -0
      releasenotes/notes/vendor-passthru-eject-vmedia-e4456320ee1c70c1.yaml
  9. +2
    -0
      setup.cfg

+ 2
- 1
ironic/drivers/drac.py View File

@@ -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]

+ 8
- 0
ironic/drivers/modules/drac/vendor_passthru.py View File

@@ -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.
"""

+ 8
- 8
ironic/drivers/modules/redfish/boot.py View File

@@ -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)



+ 92
- 0
ironic/drivers/modules/redfish/vendor.py View File

@@ -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)

+ 6
- 0
ironic/drivers/redfish.py View File

@@ -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]

+ 18
- 18
ironic/tests/unit/drivers/modules/redfish/test_boot.py View File

@@ -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)

+ 116
- 0
ironic/tests/unit/drivers/modules/redfish/test_vendor.py View File

@@ -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)

+ 7
- 0
releasenotes/notes/vendor-passthru-eject-vmedia-e4456320ee1c70c1.yaml View File

@@ -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.

+ 2
- 0
setup.cfg View File

@@ -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


Loading…
Cancel
Save