diff --git a/doc/source/dev/vendor-passthru.rst b/doc/source/dev/vendor-passthru.rst new file mode 100644 index 0000000000..7ab11e98ac --- /dev/null +++ b/doc/source/dev/vendor-passthru.rst @@ -0,0 +1,126 @@ +.. _vendor-passthru: + +============== +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 driver vendor passthru allows drivers to expose a custom top-level + functionality which is not specific to a Node. For example, let's say + the driver `pxe_ipmitool` exposed a method called `authentication_types` + that would return what are the authentication types supported. It could + be accessed via the Ironic API like: + +:: + + GET http://
:/v1/drives/pxe_ipmitool/vendor_passthru/authentication_types + +* The node vendor passthru allows drivers to expose custom functionality + on per-node basis. For example the same driver `pxe_ipmitool` exposing a + method called `send_raw` that would send raw bytes to the BMC, the method + also receives a parameter called `raw_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://
:/v1/nodes//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: + +.. code-block:: python + + 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 : 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: + +.. code-block:: python + + 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=False) + def authentication_types(self, context **kwargs): + return {"types": ["NONE", "MD5", "MD2"]} + + @base.passthru(['POST']) + def send_raw(self, task, **kwargs): + raw_bytes = kwargs.get('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 accepts the same 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 the `kwargs`. 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: + +.. code-block:: python + + @passthru(['PUT'], method="alternative_name") + def name(self, task, **kwargs): + ... + +* description: A string containing a nice description about what that + method is suppose to do. Defaults to "" (empty string). + +.. _VendorInterface: ../api/ironic.drivers.base.html#ironic.drivers.base.VendorInterface + +* async: A boolean value to determine whether this method should run + asynchronously or synchronously. Defaults to True (Asynchronously). + +.. 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. diff --git a/doc/source/index.rst b/doc/source/index.rst index 4f5b700dcb..d504dda75b 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -34,6 +34,7 @@ Introduction .. toctree:: dev/dev-quickstart + dev/vendor-passthru API References --------------