The doc8 linter found several syntax problems in our docs; primarily a large number of places we used single-backticks to surround something when we should've used double-backticks. This is frontrunning a change that will add these checks to CI. Change-Id: Ib23b5728c072f2008cb3b19e9fb7192ee5d82413
6.3 KiB
Vendor Methods
This document is a quick tutorial on writing vendor specific methods to a driver.
The first thing to note is that the Ironic API supports two vendor endpoints: A driver vendor passthru and a node vendor passthru.
The
VendorInterface
allows hardware types to expose a custom top-level functionality which is not specific to a Node. For example, let's say the driveripmi
exposed a method calledauthentication_types
that would return what are the authentication types supported. It could be accessed via the Ironic API like:GET http://<address>:<port>/v1/drivers/ipmi/vendor_passthru/authentication_types
Warning
The Bare Metal API currently only allows to use driver passthru for the default
vendor
interface implementation for a given hardware type. This limitation will be lifted in the future.The node vendor passthru allows drivers to expose custom functionality on per-node basis. For example the same driver
ipmi
exposing a method calledsend_raw
that would send raw bytes to the BMC, the method also receives a parameter calledraw_bytes
which the value would be the bytes to be sent. It could be accessed via the Ironic API like:POST {'raw_bytes': '0x01 0x02'} http://<address>:<port>/v1/nodes/<node UUID>/vendor_passthru/send_raw
Writing Vendor Methods
Writing a custom vendor method in Ironic should be simple. The first thing to do is write a class inheriting from the VendorInterface class:
class ExampleVendor(VendorInterface)
def get_properties(self):
return {}
def validate(self, task, **kwargs):
pass
The get_properties
is a method that all driver
interfaces have, it should return a dictionary of
<property>:<description> telling in the description whether
that property is required or optional so the node can be manageable by
that driver. For example, a required property for a ipmi
driver would be ipmi_address
which is the IP address or
hostname of the node. We are returning an empty dictionary in our
example to make it simpler.
The validate
method is responsible for validating the
parameters passed to the vendor methods. Ironic will not introspect into
what is passed to the drivers, it's up to the developers writing the
vendor method to validate that data.
Let's extend the ExampleVendor
class to support two
methods, the authentication_types
which will be exposed on
the driver vendor passthru endpoint; And the send_raw
method that will be exposed on the node vendor passthru endpoint:
class ExampleVendor(VendorInterface)
def get_properties(self):
return {}
def validate(self, task, method, **kwargs):
if method == 'send_raw':
if 'raw_bytes' not in kwargs:
raise MissingParameterValue()
@base.driver_passthru(['GET'], async_call=False)
def authentication_types(self, context, **kwargs):
return {"types": ["NONE", "MD5", "MD2"]}
@base.passthru(['POST'])
def send_raw(self, task, **kwargs):
= kwargs.get('raw_bytes')
raw_bytes ...
That's it!
Writing a node or driver vendor passthru method is pretty much the
same, the only difference is how you decorate the methods and the first
parameter of the method (ignoring self). A method decorated with the
@passthru
decorator should expect a Task object as first
parameter and a method decorated with the @driver_passthru
decorator should expect a Context object as first parameter.
Both decorators accept these parameters:
http_methods: A list of what the HTTP methods supported by that vendor function. To know what HTTP method that function was invoked with, a
http_method
parameter will be present in thekwargs
. Supported HTTP methods are POST, PUT, GET and PATCH.method: By default the method name is the name of the python function, if you want to use a different name this parameter is where this name can be set. For example:
@passthru(['PUT'], method="alternative_name") def name(self, task, **kwargs): ...
description: A string containing a nice description about what that method is supposed to do. Defaults to "" (empty string).
async_call: A boolean value to determine whether this method should run asynchronously or synchronously. Defaults to True (Asynchronously).
Note
This parameter was previously called "async".
The node vendor passthru decorator (@passthru
) also
accepts the following parameter:
- require_exclusive_lock: A boolean value determining whether this method should require an exclusive lock on a node between validate() and the beginning of method execution. For synchronous methods, the lock on the node would also be kept for the duration of method execution. Defaults to True.
Warning
Please avoid having a synchronous method for slow/long-running operations or if the method does talk to a BMC; BMCs are flaky and very easy to break.
Warning
Each asynchronous request consumes a worker thread in the
ironic-conductor
process. This can lead to starvation of
the thread pool, resulting in a denial of service.
Give the new vendor interface implementation a human-friendly name
and create an entry point for it in the setup.cfg
:
ironic.hardware.interfaces.vendor =
example = ironic.drivers.modules.example:ExampleVendor
Finally, add it to the list of supported vendor interfaces for relevant hardware types, for example:
class ExampleHardware(generic.GenericHardware):
...
@property
def supported_vendor_interfaces(self):
return [example.ExampleVendor]
Backwards Compatibility
There is no requirement that changes to a vendor method be backwards compatible. However, for your users' sakes, we highly recommend that you do so.
If you are changing the exceptions being raised, you might want to ensure that the same HTTP code is being returned to the user.
For non-backwards compatibility, please make sure you add a release note that indicates this.