Merge "API to list nodes using the same driver"
This commit is contained in:
commit
b9fa1172f5
@ -32,6 +32,9 @@ always requests the newest supported API version.
|
||||
API Versions History
|
||||
--------------------
|
||||
|
||||
**1.16**
|
||||
Add ability to filter nodes by driver.
|
||||
|
||||
**1.15**
|
||||
|
||||
Add ability to do manual cleaning when a node is in the manageable
|
||||
|
@ -974,8 +974,8 @@ class NodesController(rest.RestController):
|
||||
|
||||
def _get_nodes_collection(self, chassis_uuid, instance_uuid, associated,
|
||||
maintenance, provision_state, marker, limit,
|
||||
sort_key, sort_dir, resource_url=None,
|
||||
fields=None):
|
||||
sort_key, sort_dir, driver=None,
|
||||
resource_url=None, fields=None):
|
||||
if self.from_chassis and not chassis_uuid:
|
||||
raise exception.MissingParameterValue(
|
||||
_("Chassis id not specified."))
|
||||
@ -1005,6 +1005,8 @@ class NodesController(rest.RestController):
|
||||
filters['maintenance'] = maintenance
|
||||
if provision_state:
|
||||
filters['provision_state'] = provision_state
|
||||
if driver:
|
||||
filters['driver'] = driver
|
||||
|
||||
nodes = objects.Node.list(pecan.request.context, limit, marker_obj,
|
||||
sort_key=sort_key, sort_dir=sort_dir,
|
||||
@ -1082,14 +1084,15 @@ class NodesController(rest.RestController):
|
||||
|
||||
@expose.expose(NodeCollection, types.uuid, types.uuid, types.boolean,
|
||||
types.boolean, wtypes.text, types.uuid, int, wtypes.text,
|
||||
wtypes.text, types.listtype)
|
||||
wtypes.text, wtypes.text, types.listtype)
|
||||
def get_all(self, chassis_uuid=None, instance_uuid=None, associated=None,
|
||||
maintenance=None, provision_state=None, marker=None,
|
||||
limit=None, sort_key='id', sort_dir='asc', fields=None):
|
||||
limit=None, sort_key='id', sort_dir='asc', driver=None,
|
||||
fields=None):
|
||||
"""Retrieve a list of nodes.
|
||||
|
||||
:param chassis_uuid: Optional UUID of a chassis, to get only nodes for
|
||||
that chassis.
|
||||
that chassis.
|
||||
:param instance_uuid: Optional UUID of an instance, to find the node
|
||||
associated with that instance.
|
||||
:param associated: Optional boolean whether to return a list of
|
||||
@ -1104,25 +1107,28 @@ class NodesController(rest.RestController):
|
||||
:param limit: maximum number of resources to return in a single result.
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
:param driver: Optional string value to get only nodes using that
|
||||
driver.
|
||||
:param fields: Optional, a list with a specified set of fields
|
||||
of the resource to be returned.
|
||||
of the resource to be returned.
|
||||
"""
|
||||
api_utils.check_allow_specify_fields(fields)
|
||||
api_utils.check_for_invalid_state_and_allow_filter(provision_state)
|
||||
api_utils.check_allow_specify_driver(driver)
|
||||
if fields is None:
|
||||
fields = _DEFAULT_RETURN_FIELDS
|
||||
return self._get_nodes_collection(chassis_uuid, instance_uuid,
|
||||
associated, maintenance,
|
||||
provision_state, marker,
|
||||
limit, sort_key, sort_dir,
|
||||
fields=fields)
|
||||
driver, fields=fields)
|
||||
|
||||
@expose.expose(NodeCollection, types.uuid, types.uuid, types.boolean,
|
||||
types.boolean, wtypes.text, types.uuid, int, wtypes.text,
|
||||
wtypes.text)
|
||||
wtypes.text, wtypes.text)
|
||||
def detail(self, chassis_uuid=None, instance_uuid=None, associated=None,
|
||||
maintenance=None, provision_state=None, marker=None,
|
||||
limit=None, sort_key='id', sort_dir='asc'):
|
||||
limit=None, sort_key='id', sort_dir='asc', driver=None):
|
||||
"""Retrieve a list of nodes with detail.
|
||||
|
||||
:param chassis_uuid: Optional UUID of a chassis, to get only nodes for
|
||||
@ -1141,8 +1147,11 @@ class NodesController(rest.RestController):
|
||||
:param limit: maximum number of resources to return in a single result.
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
:param driver: Optional string value to get only nodes using that
|
||||
driver.
|
||||
"""
|
||||
api_utils.check_for_invalid_state_and_allow_filter(provision_state)
|
||||
api_utils.check_allow_specify_driver(driver)
|
||||
# /detail should only work against collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "nodes":
|
||||
@ -1153,7 +1162,7 @@ class NodesController(rest.RestController):
|
||||
associated, maintenance,
|
||||
provision_state, marker,
|
||||
limit, sort_key, sort_dir,
|
||||
resource_url)
|
||||
driver, resource_url)
|
||||
|
||||
@expose.expose(wtypes.text, types.uuid_or_name, types.uuid)
|
||||
def validate(self, node=None, node_uuid=None):
|
||||
|
@ -214,6 +214,20 @@ def check_for_invalid_state_and_allow_filter(provision_state):
|
||||
_('Provision state "%s" is not valid') % provision_state)
|
||||
|
||||
|
||||
def check_allow_specify_driver(driver):
|
||||
"""Check if filtering nodes by driver is allowed.
|
||||
|
||||
Version 1.16 of the API allows filter nodes by driver.
|
||||
"""
|
||||
if (driver is not None and pecan.request.version.minor <
|
||||
versions.MINOR_16_DRIVER_FILTER):
|
||||
raise exception.NotAcceptable(_(
|
||||
"Request not acceptable. The minimal required API version "
|
||||
"should be %(base)s.%(opr)s") %
|
||||
{'base': versions.BASE_VERSION,
|
||||
'opr': versions.MINOR_16_DRIVER_FILTER})
|
||||
|
||||
|
||||
def initial_node_provision_state():
|
||||
"""Return node state to use by default when creating new nodes.
|
||||
|
||||
|
@ -45,6 +45,7 @@ BASE_VERSION = 1
|
||||
# 1. '/v1/nodes/<uuid>/states'
|
||||
# 2. '/v1/drivers/<driver-name>/properties'
|
||||
# v1.15: Add ability to do manual cleaning of nodes
|
||||
# v1.16: Add ability to filter nodes by driver.
|
||||
|
||||
MINOR_0_JUNO = 0
|
||||
MINOR_1_INITIAL_VERSION = 1
|
||||
@ -62,11 +63,12 @@ MINOR_12_RAID_CONFIG = 12
|
||||
MINOR_13_ABORT_VERB = 13
|
||||
MINOR_14_LINKS_NODESTATES_DRIVERPROPERTIES = 14
|
||||
MINOR_15_MANUAL_CLEAN = 15
|
||||
MINOR_16_DRIVER_FILTER = 16
|
||||
|
||||
# When adding another version, update MINOR_MAX_VERSION and also update
|
||||
# doc/source/webapi/v1.rst with a detailed explanation of what the version has
|
||||
# changed.
|
||||
MINOR_MAX_VERSION = MINOR_15_MANUAL_CLEAN
|
||||
MINOR_MAX_VERSION = MINOR_16_DRIVER_FILTER
|
||||
|
||||
# String representations of the minor and maximum versions
|
||||
MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
||||
|
@ -695,6 +695,38 @@ class TestListNodes(test_api_base.BaseApiTest):
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_get_nodes_by_driver(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
driver='pxe_ssh')
|
||||
node1 = obj_utils.create_test_node(self.context,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
driver='fake')
|
||||
|
||||
data = self.get_json('/nodes?driver=pxe_ssh',
|
||||
headers={api_base.Version.string: "1.16"})
|
||||
uuids = [n['uuid'] for n in data['nodes']]
|
||||
self.assertIn(node.uuid, uuids)
|
||||
self.assertNotIn(node1.uuid, uuids)
|
||||
data = self.get_json('/nodes?driver=fake',
|
||||
headers={api_base.Version.string: "1.16"})
|
||||
uuids = [n['uuid'] for n in data['nodes']]
|
||||
self.assertIn(node1.uuid, uuids)
|
||||
self.assertNotIn(node.uuid, uuids)
|
||||
|
||||
def test_get_nodes_by_invalid_driver(self):
|
||||
data = self.get_json('/nodes?driver=test',
|
||||
headers={api_base.Version.string: "1.16"})
|
||||
self.assertEqual(0, len(data['nodes']))
|
||||
|
||||
def test_get_nodes_by_driver_invalid_api_version(self):
|
||||
response = self.get_json(
|
||||
'/nodes?driver=fake',
|
||||
headers={api_base.Version.string: str(api_v1.MIN_VER)},
|
||||
expect_errors=True)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def test_get_console_information(self):
|
||||
node = obj_utils.create_test_node(self.context)
|
||||
expected_console_info = {'test': 'test-data'}
|
||||
|
@ -97,6 +97,17 @@ class TestApiUtils(base.TestCase):
|
||||
self.assertRaises(exception.NotAcceptable,
|
||||
utils.check_allow_specify_fields, ['foo'])
|
||||
|
||||
@mock.patch.object(pecan, 'request', spec_set=['version'])
|
||||
def test_check_allow_specify_driver(self, mock_request):
|
||||
mock_request.version.minor = 16
|
||||
self.assertIsNone(utils.check_allow_specify_driver(['fake']))
|
||||
|
||||
@mock.patch.object(pecan, 'request', spec_set=['version'])
|
||||
def test_check_allow_specify_driver_fail(self, mock_request):
|
||||
mock_request.version.minor = 15
|
||||
self.assertRaises(exception.NotAcceptable,
|
||||
utils.check_allow_specify_driver, ['fake'])
|
||||
|
||||
@mock.patch.object(pecan, 'request', spec_set=['version'])
|
||||
def test_allow_links_node_states_and_driver_properties(self, mock_request):
|
||||
mock_request.version.minor = 14
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Add support for filtering nodes using the same driver via the API.
|
Loading…
Reference in New Issue
Block a user