Expose the vendor_passthru resource

The vendor_passthru resource allow vendors to expose a custom
functionality in the Ironic API. Ironic will merely relay the message
from here to the appropriate driver, no introspection will be made in
the message body.

Change-Id: I81f6460da80d44ab879fa4cadfbc0021b38ec96f
This commit is contained in:
Lucas Alvares Gomes 2013-08-23 12:53:08 +01:00
parent 33399cca31
commit 143fe0ede8
3 changed files with 80 additions and 1 deletions

View File

@ -1013,6 +1013,35 @@ JSON structure of a meta_data::
"bar": "foo"
}
VendorPassthru
---------
VendorPassthru allow vendors to expose a custom functionality in
the Ironic API. Ironic will merely relay the message from here to the
appropriate driver (see: Driver_), no introspection will be made in the
message body.
Usage:
^^^^^^
======= ================================== ==========================
Verb Path Response
======= ================================== ==========================
POST /nodes/1/vendor_passthru/<method> Invoke a specific <method>
======= ================================== ==========================
Example
^^^^^^^^
Invoking "custom_method"::
POST /nodes/1/vendor_passthru/custom_method
{
...
"foo": "bar",
...
}
Areas To Be Defined
####################

View File

@ -257,12 +257,39 @@ class NodeCollection(collection.Collection):
return collection
class NodeVendorPassthruController(rest.RestController):
"""REST controller for VendorPassthru.
This controller allow vendors to expose a custom functionality in
the Ironic API. Ironic will merely relay the message from here to the
appropriate driver, no introspection will be made in the message body.
"""
@wsme_pecan.wsexpose(None, unicode, unicode, body=unicode, status=202)
def _default(self, node_id, method, data):
# Only allow POST requests
if pecan.request.method.upper() != "POST":
raise exception.NotFound
# Raise an exception if node is not found
objects.Node.get_by_uuid(pecan.request.context, node_id)
# Raise an exception if method is not specified
if not method:
raise wsme.exc.ClientSideError(_("Method not specified"))
raise NotImplementedError()
class NodesController(rest.RestController):
"""REST controller for Nodes."""
state = NodeStatesController()
"Expose the state controller action as a sub-element of nodes"
vendor_passthru = NodeVendorPassthruController()
"A resource used for vendors to expose a custom functionality in the API"
_custom_actions = {
'ports': ['GET'],
}

View File

@ -243,6 +243,29 @@ class TestPost(base.FunctionalTest):
result = self.get_json('/nodes/%s' % ndict['uuid'])
self.assertEqual(ndict['uuid'], result['uuid'])
def test_vendor_passthru(self):
ndict = dbutils.get_test_node()
self.post_json('/nodes', ndict)
uuid = ndict['uuid']
# TODO(lucasagomes): When vendor_passthru gets implemented
# remove the expect_errors parameter
response = self.post_json('/nodes/%s/vendor_passthru/method' % uuid,
{'foo': 'bar'},
expect_errors=True)
# TODO(lucasagomes): it's expected to return 202, but because we are
# passing expect_errors=True to the post_json
# function the return code will be 500. So change
# the return code when vendor_passthru gets
# implemented
self.assertEqual(response.status_code, 500)
def test_vendor_passthru_without_method(self):
ndict = dbutils.get_test_node()
self.post_json('/nodes', ndict)
self.assertRaises(webtest.app.AppError, self.post_json,
'/nodes/%s/vendor_passthru' % ndict['uuid'],
{'foo': 'bar'})
class TestDelete(base.FunctionalTest):
@ -251,7 +274,7 @@ class TestDelete(base.FunctionalTest):
self.post_json('/nodes', ndict)
self.delete('/nodes/%s' % ndict['uuid'])
response = self.get_json('/nodes/%s' % ndict['uuid'],
expect_errors=True)
expect_errors=True)
self.assertEqual(response.status_int, 500)
self.assertEqual(response.content_type, 'application/json')
self.assertTrue(response.json['error_message'])