diff options
author | Jenkins <jenkins@review.openstack.org> | 2014-12-08 18:23:00 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2014-12-08 18:23:00 +0000 |
commit | 8a07fdffb2a0ddcbe913e08364f0d41cd4b29699 (patch) | |
tree | 8a84def18fd7f2173c740aabc9c45be897d17d04 | |
parent | 403926334c606cb82222727df0e16db683b673a7 (diff) | |
parent | 93f885dfd9fbb7cba3aa544739227a5490bfbbad (diff) | |
download | python-ironicclient-0.3.2.tar.gz |
Merge "VendorPassthru commands to support different HTTP methods"0.3.2
-rw-r--r-- | ironicclient/tests/v1/test_driver.py | 61 | ||||
-rw-r--r-- | ironicclient/tests/v1/test_driver_shell.py | 7 | ||||
-rw-r--r-- | ironicclient/tests/v1/test_node.py | 51 | ||||
-rw-r--r-- | ironicclient/tests/v1/test_node_shell.py | 7 | ||||
-rw-r--r-- | ironicclient/v1/driver.py | 34 | ||||
-rw-r--r-- | ironicclient/v1/driver_shell.py | 13 | ||||
-rw-r--r-- | ironicclient/v1/node.py | 25 | ||||
-rw-r--r-- | ironicclient/v1/node_shell.py | 31 |
8 files changed, 183 insertions, 46 deletions
diff --git a/ironicclient/tests/v1/test_driver.py b/ironicclient/tests/v1/test_driver.py index 8beec3a..dcba0e5 100644 --- a/ironicclient/tests/v1/test_driver.py +++ b/ironicclient/tests/v1/test_driver.py @@ -19,9 +19,9 @@ import mock import testtools from testtools import matchers -from ironicclient.common import base +from ironicclient import exc from ironicclient.tests import utils -import ironicclient.v1.driver +from ironicclient.v1 import driver DRIVER1 = {'name': 'fake', 'hosts': ['fake-host1', 'fake-host2']} @@ -63,7 +63,7 @@ class DriverManagerTest(testtools.TestCase): def setUp(self): super(DriverManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) - self.mgr = ironicclient.v1.driver.DriverManager(self.api) + self.mgr = driver.DriverManager(self.api) def test_driver_list(self): drivers = self.mgr.list() @@ -74,13 +74,13 @@ class DriverManagerTest(testtools.TestCase): self.assertThat(drivers, matchers.HasLength(1)) def test_driver_show(self): - driver = self.mgr.get(DRIVER1['name']) + driver_ = self.mgr.get(DRIVER1['name']) expect = [ ('GET', '/v1/drivers/%s' % DRIVER1['name'], {}, None) ] self.assertEqual(expect, self.api.calls) - self.assertEqual(DRIVER1['name'], driver.name) - self.assertEqual(DRIVER1['hosts'], driver.hosts) + self.assertEqual(DRIVER1['name'], driver_.name) + self.assertEqual(DRIVER1['hosts'], driver_.hosts) def test_driver_properties(self): properties = self.mgr.properties(DRIVER2['name']) @@ -90,8 +90,8 @@ class DriverManagerTest(testtools.TestCase): self.assertEqual(expect, self.api.calls) self.assertEqual(DRIVER2_PROPERTIES, properties) - @mock.patch.object(base.Manager, '_update') - def test_vendor_passthru(self, update_mock): + @mock.patch.object(driver.DriverManager, 'update') + def test_vendor_passthru_update(self, update_mock): # For now just mock the tests because vendor-passthru doesn't return # anything to verify. vendor_passthru_args = {'arg1': 'val1'} @@ -100,9 +100,46 @@ class DriverManagerTest(testtools.TestCase): 'method': 'method', 'args': vendor_passthru_args } + + final_path = 'driver_name/vendor_passthru/method' + for http_method in ('POST', 'PUT', 'PATCH'): + kwargs['http_method'] = http_method + self.mgr.vendor_passthru(**kwargs) + update_mock.assert_called_once_with(final_path, + vendor_passthru_args, + http_method=http_method) + update_mock.reset_mock() + + @mock.patch.object(driver.DriverManager, 'get') + def test_vendor_passthru_get(self, get_mock): + kwargs = { + 'driver_name': 'driver_name', + 'method': 'method', + 'http_method': 'GET', + } + + final_path = 'driver_name/vendor_passthru/method' + self.mgr.vendor_passthru(**kwargs) + get_mock.assert_called_once_with(final_path) + + @mock.patch.object(driver.DriverManager, 'delete') + def test_vendor_passthru_delete(self, delete_mock): + kwargs = { + 'driver_name': 'driver_name', + 'method': 'method', + 'http_method': 'DELETE', + } + + final_path = 'driver_name/vendor_passthru/method' self.mgr.vendor_passthru(**kwargs) + delete_mock.assert_called_once_with(final_path) - final_path = '/v1/drivers/driver_name/vendor_passthru/method' - update_mock.assert_once_called_with(final_path, - vendor_passthru_args, - method='POST') + @mock.patch.object(driver.DriverManager, 'delete') + def test_vendor_passthru_unknown_http_method(self, delete_mock): + kwargs = { + 'driver_name': 'driver_name', + 'method': 'method', + 'http_method': 'UNKNOWN', + } + self.assertRaises(exc.InvalidAttribute, self.mgr.vendor_passthru, + **kwargs) diff --git a/ironicclient/tests/v1/test_driver_shell.py b/ironicclient/tests/v1/test_driver_shell.py index c29d792..f7c38d8 100644 --- a/ironicclient/tests/v1/test_driver_shell.py +++ b/ironicclient/tests/v1/test_driver_shell.py @@ -36,21 +36,24 @@ class DriverShellTest(utils.BaseTestCase): client_mock = mock.MagicMock() args = mock.MagicMock() args.driver_name = 'driver_name' + args.http_method = 'POST' args.method = 'method' args.arguments = [['arg1=val1', 'arg2=val2']] d_shell.do_driver_vendor_passthru(client_mock, args) client_mock.driver.vendor_passthru.assert_called_once_with( - args.driver_name, args.method, + args.driver_name, args.method, http_method=args.http_method, args={'arg1': 'val1', 'arg2': 'val2'}) def test_do_driver_vendor_passthru_without_args(self): client_mock = mock.MagicMock() args = mock.MagicMock() args.driver_name = 'driver_name' + args.http_method = 'POST' args.method = 'method' args.arguments = [[]] d_shell.do_driver_vendor_passthru(client_mock, args) client_mock.driver.vendor_passthru.assert_called_once_with( - args.driver_name, args.method, args={}) + args.driver_name, args.method, args={}, + http_method=args.http_method) diff --git a/ironicclient/tests/v1/test_node.py b/ironicclient/tests/v1/test_node.py index 7746280..a8c2efd 100644 --- a/ironicclient/tests/v1/test_node.py +++ b/ironicclient/tests/v1/test_node.py @@ -20,7 +20,7 @@ import mock import testtools from testtools.matchers import HasLength -from ironicclient.common import base +from ironicclient import exc from ironicclient.tests import utils from ironicclient.v1 import node @@ -648,8 +648,8 @@ class NodeManagerTest(testtools.TestCase): self.assertEqual(expect, self.api.calls) self.assertEqual(CONSOLE_DATA_DISABLED, info) - @mock.patch.object(base.Manager, '_update') - def test_vendor_passthru(self, update_mock): + @mock.patch.object(node.NodeManager, 'update') + def test_vendor_passthru_update(self, update_mock): # For now just mock the tests because vendor-passthru doesn't return # anything to verify. vendor_passthru_args = {'arg1': 'val1'} @@ -658,12 +658,49 @@ class NodeManagerTest(testtools.TestCase): 'method': 'method', 'args': vendor_passthru_args } + + final_path = 'node_uuid/vendor_passthru/method' + for http_method in ('POST', 'PUT', 'PATCH'): + kwargs['http_method'] = http_method + self.mgr.vendor_passthru(**kwargs) + update_mock.assert_called_once_with(final_path, + vendor_passthru_args, + http_method=http_method) + update_mock.reset_mock() + + @mock.patch.object(node.NodeManager, 'get') + def test_vendor_passthru_get(self, get_mock): + kwargs = { + 'node_id': 'node_uuid', + 'method': 'method', + 'http_method': 'GET', + } + + final_path = 'node_uuid/vendor_passthru/method' self.mgr.vendor_passthru(**kwargs) + get_mock.assert_called_once_with(final_path) - final_path = '/v1/nodes/node_uuid/vendor_passthru/method' - update_mock.assert_once_called_with(final_path, - vendor_passthru_args, - method='POST') + @mock.patch.object(node.NodeManager, 'delete') + def test_vendor_passthru_delete(self, delete_mock): + kwargs = { + 'node_id': 'node_uuid', + 'method': 'method', + 'http_method': 'DELETE', + } + + final_path = 'node_uuid/vendor_passthru/method' + self.mgr.vendor_passthru(**kwargs) + delete_mock.assert_called_once_with(final_path) + + @mock.patch.object(node.NodeManager, 'delete') + def test_vendor_passthru_unknown_http_method(self, delete_mock): + kwargs = { + 'node_id': 'node_uuid', + 'method': 'method', + 'http_method': 'UNKNOWN', + } + self.assertRaises(exc.InvalidAttribute, self.mgr.vendor_passthru, + **kwargs) def _test_node_set_boot_device(self, boot_device, persistent=False): self.mgr.set_boot_device(NODE1['uuid'], boot_device, persistent) diff --git a/ironicclient/tests/v1/test_node_shell.py b/ironicclient/tests/v1/test_node_shell.py index d87a6ed..ff4a2ca 100644 --- a/ironicclient/tests/v1/test_node_shell.py +++ b/ironicclient/tests/v1/test_node_shell.py @@ -230,23 +230,26 @@ class NodeShellTest(utils.BaseTestCase): client_mock = mock.MagicMock() args = mock.MagicMock() args.node = 'node_uuid' + args.http_method = 'POST' args.method = 'method' args.arguments = [['arg1=val1', 'arg2=val2']] n_shell.do_node_vendor_passthru(client_mock, args) client_mock.node.vendor_passthru.assert_called_once_with( - args.node, args.method, args={'arg1': 'val1', 'arg2': 'val2'}) + args.node, args.method, args={'arg1': 'val1', 'arg2': 'val2'}, + http_method=args.http_method) def test_do_node_vendor_passthru_without_args(self): client_mock = mock.MagicMock() args = mock.MagicMock() args.node = 'node_uuid' + args.http_method = 'POST' args.method = 'method' args.arguments = [[]] n_shell.do_node_vendor_passthru(client_mock, args) client_mock.node.vendor_passthru.assert_called_once_with( - args.node, args.method, args={}) + args.node, args.method, args={}, http_method=args.http_method) def test_do_node_set_provision_state_active(self): client_mock = mock.MagicMock() diff --git a/ironicclient/v1/driver.py b/ironicclient/v1/driver.py index c616a10..35b0a59 100644 --- a/ironicclient/v1/driver.py +++ b/ironicclient/v1/driver.py @@ -16,6 +16,7 @@ # under the License. from ironicclient.common import base +from ironicclient import exc class Driver(base.Resource): @@ -35,6 +36,13 @@ class DriverManager(base.Manager): except IndexError: return None + def update(self, driver_name, patch, http_method='PATCH'): + path = '/v1/drivers/%s' % driver_name + return self._update(path, patch, method=http_method) + + def delete(self, driver_name): + return self._delete('/v1/drivers/%s' % driver_name) + def properties(self, driver_name): try: info = self._list('/v1/drivers/%s/properties' % driver_name)[0] @@ -44,19 +52,31 @@ class DriverManager(base.Manager): except IndexError: return {} - def vendor_passthru(self, driver_name, method, args=None): + def vendor_passthru(self, driver_name, method, args=None, + http_method=None): """Issue requests for vendor-specific actions on a given driver. :param driver_name: Name of the driver. :param method: Name of the vendor method. :param args: Optional. The arguments to be passed to the method. - + :param http_method: The HTTP method to use on the request. + Defaults to POST. """ if args is None: args = {} - path = "/v1/drivers/%(driver_name)s/vendor_passthru/%(method)s" % { - 'driver_name': driver_name, - 'method': method - } - return self._update(path, args, method='POST') + if http_method is None: + http_method = 'POST' + + http_method = http_method.upper() + + path = "%s/vendor_passthru/%s" % (driver_name, method) + if http_method in ('POST', 'PUT', 'PATCH'): + return self.update(path, args, http_method=http_method) + elif http_method == 'DELETE': + return self.delete(path) + elif http_method == 'GET': + return self.get(path) + else: + raise exc.InvalidAttribute( + _('Unknown HTTP method: %s') % http_method) diff --git a/ironicclient/v1/driver_shell.py b/ironicclient/v1/driver_shell.py index 90e7f7d..a63142d 100644 --- a/ironicclient/v1/driver_shell.py +++ b/ironicclient/v1/driver_shell.py @@ -70,6 +70,12 @@ def do_driver_properties(cc, args): action='append', default=[], help="arguments to be passed to vendor-passthru method") +@cliutils.arg('--http_method', + metavar='<http_method>', + choices=['POST', 'PUT', 'GET', 'DELETE', 'PATCH'], + help="The HTTP method to use in the request. Valid HTTP " + "methods are: 'POST', 'PUT', 'GET', 'DELETE', 'PATCH'. " + "Defaults to 'POST'.") def do_driver_vendor_passthru(cc, args): """Call a vendor-passthru extension for a driver.""" arguments = utils.args_array_to_dict({'args': args.arguments[0]}, @@ -80,4 +86,9 @@ def do_driver_vendor_passthru(cc, args): if not arguments: arguments = {} - cc.driver.vendor_passthru(args.driver_name, args.method, args=arguments) + resp = cc.driver.vendor_passthru(args.driver_name, args.method, + http_method=args.http_method, + args=arguments) + if resp: + # Print the raw response we don't know how it should be formated + print(str(resp.to_dict())) diff --git a/ironicclient/v1/node.py b/ironicclient/v1/node.py index bd70ee2..0452ab7 100644 --- a/ironicclient/v1/node.py +++ b/ironicclient/v1/node.py @@ -162,22 +162,37 @@ class NodeManager(base.Manager): def delete(self, node_id): return self._delete(self._path(node_id)) - def update(self, node_id, patch): - return self._update(self._path(node_id), patch) + def update(self, node_id, patch, http_method='PATCH'): + return self._update(self._path(node_id), patch, method=http_method) - def vendor_passthru(self, node_id, method, args=None): + def vendor_passthru(self, node_id, method, args=None, http_method=None): """Issue requests for vendor-specific actions on a given node. :param node_id: The UUID of the node. :param method: Name of the vendor method. :param args: Optional. The arguments to be passed to the method. + :param http_method: The HTTP method to use on the request. + Defaults to POST. """ if args is None: args = {} - path = self._path(node_id) + "/vendor_passthru/%s" % method - return self._update(path, args, method='POST') + if http_method is None: + http_method = 'POST' + + http_method = http_method.upper() + + path = "%s/vendor_passthru/%s" % (node_id, method) + if http_method in ('POST', 'PUT', 'PATCH'): + return self.update(path, args, http_method=http_method) + elif http_method == 'DELETE': + return self.delete(path) + elif http_method == 'GET': + return self.get(path) + else: + raise exc.InvalidAttribute( + _('Unknown HTTP method: %s') % http_method) def set_maintenance(self, node_id, state, maint_reason=None): path = "%s/maintenance" % node_id diff --git a/ironicclient/v1/node_shell.py b/ironicclient/v1/node_shell.py index 7a981f3..5b5e267 100644 --- a/ironicclient/v1/node_shell.py +++ b/ironicclient/v1/node_shell.py @@ -183,17 +183,23 @@ def do_node_update(cc, args): @cliutils.arg('node', - metavar='<node id>', - help="UUID of node") + metavar='<node id>', + help="UUID of node") @cliutils.arg('method', - metavar='<method>', - help="vendor-passthru method to be called") + metavar='<method>', + help="vendor-passthru method to be called") @cliutils.arg('arguments', - metavar='<arg=value>', - nargs='*', - action='append', - default=[], - help="arguments to be passed to vendor-passthru method") + metavar='<arg=value>', + nargs='*', + action='append', + default=[], + help="arguments to be passed to vendor-passthru method") +@cliutils.arg('--http_method', + metavar='<http_method>', + choices=['POST', 'PUT', 'GET', 'DELETE', 'PATCH'], + help="The HTTP method to use in the request. Valid HTTP " + "methods are: 'POST', 'PUT', 'GET', 'DELETE', 'PATCH'. " + "Defaults to 'POST'.") def do_node_vendor_passthru(cc, args): """Call a vendor-passthru extension for a node.""" arguments = utils.args_array_to_dict({'args': args.arguments[0]}, @@ -204,7 +210,12 @@ def do_node_vendor_passthru(cc, args): if not arguments: arguments = {} - cc.node.vendor_passthru(args.node, args.method, args=arguments) + resp = cc.node.vendor_passthru(args.node, args.method, + http_method=args.http_method, + args=arguments) + if resp: + # Print the raw response we don't know how it should be formated + print(str(resp.to_dict())) @cliutils.arg( |