Change-Id: Ia6d5336ee074ac5f44226c09c3b3c239f0e50162 Story: #1753128 Task: #22592
8.1 KiB
Pluggable Drivers
Ironic supports a pluggable driver model. This allows contributors to
easily add new drivers, and operators to use third-party drivers or
write their own. A driver is built at runtime from a hardware
type and hardware interfaces. See /install/enabling-drivers
for
a detailed explanation of these concepts.
Hardware types and interfaces are loaded by the
ironic-conductor
service during initialization from the
setuptools entrypoints ironic.hardware.types
and
ironic.hardware.interfaces.<INTERFACE>
where
<INTERFACE>
is an interface type (for example,
deploy
). Only hardware types listed in the configuration
option enabled_hardware_types
and interfaces listed in
configuration options enabled_<INTERFACE>_interfaces
are loaded. A complete list of hardware types available on the system
may be found by enumerating this entrypoint by running the following
python script:
#!/usr/bin/env python
import pkg_resources as pkg
print [p.name for p in pkg.iter_entry_points("ironic.hardware.types") if not p.name.startswith("fake")]
A list of drivers enabled in a running Ironic service may be found by issuing the following command against that API end point:
openstack baremetal driver list
Writing a hardware type
A hardware type is a Python class, inheriting :pyironic.drivers.hardware_type.AbstractHardwareType
and listed in the setuptools entry point
ironic.hardware.types
. Most of the real world hardware
types inherit :pyironic.drivers.generic.GenericHardware
instead. This
helper class provides useful implementations for interfaces that are
usually the same for all hardware types, such as
deploy
.
The minimum required interfaces are:
boot </admin/interfaces/boot>
that specifies how to boot ramdisks and instances on the hardware. A genericpxe
implementation is provided by theGenericHardware
base class.deploy </admin/interfaces/deploy>
that orchestrates the deployment. A few common implementations are provided by theGenericHardware
base class.As of the Rocky release, a deploy interface should decorate its deploy method to indicate that it is a deploy step. Conventionally, the deploy method uses a priority of 100.
@ironic.drivers.base.deploy_step(priority=100) def deploy(self, task):
Note
Most of the hardware types should not override this interface.
power implements power actions for the hardware. These common implementations may be used, if supported by the hardware:
- :py
ironic.drivers.modules.ipmitool.IPMIPower
- :py
ironic.drivers.modules.redfish.power.RedfishPower
Otherwise, you need to write your own implementation by subclassing :py
ironic.drivers.base.PowerInterface
and providing missing methods.Note
Power actions in Ironic are blocking - methods of a power interface should not return until the power action is finished or errors out.
- :py
management implements additional out-of-band management actions, such as setting a boot device. A few common implementations exist and may be used, if supported by the hardware:
- :py
ironic.drivers.modules.ipmitool.IPMIManagement
- :py
ironic.drivers.modules.redfish.management.RedfishManagement
Some hardware types, such as
snmp
do not support out-of-band management. They use the fake implementation in :pyironic.drivers.modules.fake.FakeManagement
instead.Otherwise, you need to write your own implementation by subclassing :py
ironic.drivers.base.ManagementInterface
and providing missing methods.- :py
Combine the interfaces in a hardware type by populating the lists of supported interfaces. These lists are prioritized, with the most preferred implementation first. For example:
class MyHardware(generic.GenericHardware):
@property
def supported_management_interfaces(self):
"""List of supported management interfaces."""
return [MyManagement, ipmitool.IPMIManagement]
@property
def supported_power_interfaces(self):
"""List of supported power interfaces."""
return [MyPower, ipmitool.IPMIPower]
Note
In this example, all interfaces, except for management
and power
are taken from the GenericHardware
base class.
Finally, give the new hardware type and new interfaces human-friendly
names and create entry points for them in the setup.cfg
file:
ironic.hardware.types =
my-hardware = ironic.drivers.my_hardware:MyHardware
ironic.hardware.interfaces.power =
my-power = ironic.drivers.modules.my_hardware:MyPower
ironic.hardware.interfaces.management =
my-management = ironic.drivers.modules.my_hardware:MyManagement
Supported Drivers
For a list of supported drivers (those that are continuously tested
on every upstream commit) please consult the drivers page </admin/drivers>
.
Node Vendor Passthru
Drivers may implement a passthrough API, which is accessible via the
/v1/nodes/<Node UUID or Name>/vendor_passthru?method={METHOD}
endpoint. Beyond basic checking, Ironic does not introspect the message
body and simply "passes it through" to the relevant driver.
A method:
- can support one or more HTTP methods (for example, GET, POST)
- is asynchronous or synchronous
- For asynchronous methods, a 202 (Accepted) HTTP status code is returned to indicate that the request was received, accepted and is being acted upon. No body is returned in the response.
- For synchronous methods, a 200 (OK) HTTP status code is returned to indicate that the request was fulfilled. The response may include a body.
- can require an exclusive lock on the node. This only occurs if the method doesn't specify require_exclusive_lock=False in the decorator. If an exclusive lock is held on the node, other requests for the node will be delayed and may fail with an HTTP 409 (Conflict) error code.
This endpoint exposes a node's driver directly, and as such, it is expressly not part of Ironic's standard REST API. There is only a single HTTP endpoint exposed, and the semantics of the message body are determined solely by the driver. Ironic makes no guarantees about backwards compatibility; this is solely up to the discretion of each driver's author.
To get information about all the methods available via the vendor_passthru endpoint for a particular node, you can issue an HTTP GET request:
GET /v1/nodes/<Node UUID or name>/vendor_passthru/methods
The response's JSON body will contain information for each method, such as the method's name, a description, the HTTP methods supported, and whether it's asynchronous or synchronous.
Driver Vendor Passthru
Drivers may implement an API for requests not related to any node, at
/v1/drivers/<driver name>/vendor_passthru?method={METHOD}
.
A method:
- can support one or more HTTP methods (for example, GET, POST)
- is asynchronous or synchronous
- For asynchronous methods, a 202 (Accepted) HTTP status code is returned to indicate that the request was received, accepted and is being acted upon. No body is returned in the response.
- For synchronous methods, a 200 (OK) HTTP status code is returned to indicate that the request was fulfilled. The response may include a body.
Note
Unlike methods in Node Vendor Passthru, a request does not lock any resource, so it will not delay other requests and will not fail with an HTTP 409 (Conflict) error code.
Ironic makes no guarantees about the semantics of the message BODY sent to this endpoint. That is left up to each driver's author.
To get information about all the methods available via the driver vendor_passthru endpoint, you can issue an HTTP GET request:
GET /v1/drivers/<driver name>/vendor_passthru/methods
The response's JSON body will contain information for each method, such as the method's name, a description, the HTTP methods supported, and whether it's asynchronous or synchronous.