summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-12-08 18:23:00 +0000
committerGerrit Code Review <review@openstack.org>2014-12-08 18:23:00 +0000
commit8a07fdffb2a0ddcbe913e08364f0d41cd4b29699 (patch)
tree8a84def18fd7f2173c740aabc9c45be897d17d04
parent403926334c606cb82222727df0e16db683b673a7 (diff)
parent93f885dfd9fbb7cba3aa544739227a5490bfbbad (diff)
downloadpython-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.py61
-rw-r--r--ironicclient/tests/v1/test_driver_shell.py7
-rw-r--r--ironicclient/tests/v1/test_node.py51
-rw-r--r--ironicclient/tests/v1/test_node_shell.py7
-rw-r--r--ironicclient/v1/driver.py34
-rw-r--r--ironicclient/v1/driver_shell.py13
-rw-r--r--ironicclient/v1/node.py25
-rw-r--r--ironicclient/v1/node_shell.py31
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(