Merge "VendorPassthru commands to support different HTTP methods"
This commit is contained in:
commit
8a07fdffb2
|
@ -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
|
||||
}
|
||||
self.mgr.vendor_passthru(**kwargs)
|
||||
|
||||
final_path = '/v1/drivers/driver_name/vendor_passthru/method'
|
||||
update_mock.assert_once_called_with(final_path,
|
||||
vendor_passthru_args,
|
||||
method='POST')
|
||||
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)
|
||||
|
||||
@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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
self.mgr.vendor_passthru(**kwargs)
|
||||
|
||||
final_path = '/v1/nodes/node_uuid/vendor_passthru/method'
|
||||
update_mock.assert_once_called_with(final_path,
|
||||
vendor_passthru_args,
|
||||
method='POST')
|
||||
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)
|
||||
|
||||
@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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue