From 143fe0ede8018f3392cbcdead791bf42c9567243 Mon Sep 17 00:00:00 2001 From: Lucas Alvares Gomes Date: Fri, 23 Aug 2013 12:53:08 +0100 Subject: [PATCH] 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 --- doc/source/dev/api-spec-v1.rst | 29 +++++++++++++++++++++++++++++ ironic/api/controllers/v1/node.py | 27 +++++++++++++++++++++++++++ ironic/tests/api/test_nodes.py | 25 ++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/doc/source/dev/api-spec-v1.rst b/doc/source/dev/api-spec-v1.rst index 4047500839..fe27c26b7c 100644 --- a/doc/source/dev/api-spec-v1.rst +++ b/doc/source/dev/api-spec-v1.rst @@ -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/ Invoke a specific +======= ================================== ========================== + +Example +^^^^^^^^ + +Invoking "custom_method":: + + POST /nodes/1/vendor_passthru/custom_method + { + ... + "foo": "bar", + ... + } + Areas To Be Defined #################### diff --git a/ironic/api/controllers/v1/node.py b/ironic/api/controllers/v1/node.py index 5b00ff49ee..4031e6d283 100644 --- a/ironic/api/controllers/v1/node.py +++ b/ironic/api/controllers/v1/node.py @@ -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'], } diff --git a/ironic/tests/api/test_nodes.py b/ironic/tests/api/test_nodes.py index 13851c98db..9b8bb223bf 100644 --- a/ironic/tests/api/test_nodes.py +++ b/ironic/tests/api/test_nodes.py @@ -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'])