Add pagination support to {node, port, chassis}-list

the presence of a 'next' field is the indicator to continue pagination
until there're no more values to be returned.

On the CLI two new options were added to commands used to list resources:

* --marker: Optional, the UUID of an item, eg the last item from a
  previous result set. Return the next result set.

* --limit: The maximum number of results to return per request, if:

  1) limit > 0, the maximum number of items to return.
  2) limit == 0, return the entire list of items.
  3) limit param is NOT specified (None), the number of items
  returned respect the maximum imposed by the Ironic API
  (see Ironic's api.max_limit option).

Closes-Bug: #1314992
Change-Id: I77cc7a7df65c3d2a84144b12a001487ff6832045
This commit is contained in:
Lucas Alvares Gomes 2014-05-01 15:22:20 +01:00
parent d7218d83a2
commit c3665d0916
10 changed files with 610 additions and 30 deletions

@ -21,6 +21,8 @@ Base utilities to build API operation managers and objects on top of.
import copy
import six.moves.urllib.parse as urlparse
from ironicclient.openstack.common.apiclient import base
@ -48,12 +50,7 @@ class Manager(object):
if body:
return self.resource_class(self, body)
def _list(self, url, response_key=None, obj_class=None, body=None):
resp, body = self.api.json_request('GET', url)
if obj_class is None:
obj_class = self.resource_class
def _format_body_data(self, body, response_key):
if response_key:
try:
data = body[response_key]
@ -61,9 +58,72 @@ class Manager(object):
return []
else:
data = body
if not isinstance(data, list):
data = [data]
return data
def _list_pagination(self, url, response_key=None, obj_class=None,
limit=None):
"""Retrieve a list of items.
The Ironic API is configured to return a maximum number of
items per request, (see Ironic's api.max_limit option). This
iterates over the 'next' link (pagination) in the responses,
to get the number of items specified by 'limit'. If 'limit'
is None this function will continue pagination until there are
no more values to be returned.
:param url: a partial URL, e.g. '/nodes'
:param response_key: the key to be looked up in response
dictionary, e.g. 'nodes'
:param obj_class: class for constructing the returned objects.
:param limit: maximum number of items to return. If None returns
everything.
"""
if obj_class is None:
obj_class = self.resource_class
if limit is not None:
limit = int(limit)
object_list = []
object_count = 0
limit_reached = False
while url:
resp, body = self.api.json_request('GET', url)
data = self._format_body_data(body, response_key)
for obj in data:
object_list.append(obj_class(self, obj, loaded=True))
object_count += 1
if limit and object_count >= limit:
# break the for loop
limit_reached = True
break
# break the while loop and return
if limit_reached:
break
url = body.get('next')
if url:
# NOTE(lucasagomes): We need to edit the URL to remove
# the scheme and netloc
url_parts = list(urlparse.urlparse(url))
url_parts[0] = url_parts[1] = ''
url = urlparse.urlunparse(url_parts)
return object_list
def _list(self, url, response_key=None, obj_class=None, body=None):
resp, body = self.api.json_request('GET', url)
if obj_class is None:
obj_class = self.resource_class
data = self._format_body_data(body, response_key)
return [obj_class(self, res, loaded=True) for res in data if res]
def _update(self, url, body, method='PATCH', response_key=None):

@ -18,6 +18,7 @@
import copy
import testtools
from testtools.matchers import HasLength
from ironicclient.tests import utils
import ironicclient.v1.chassis
@ -27,6 +28,12 @@ CHASSIS = {'id': 42,
'extra': {},
'description': 'data-center-1-chassis'}
CHASSIS2 = {'id': 43,
'uuid': 'eeeeeeee-dddd-cccc-bbbb-aaaaaaaaaaaa',
'extra': {},
'description': 'data-center-1-chassis'}
NODE = {'id': 123,
'uuid': '66666666-7777-8888-9999-000000000000',
'chassis_id': 42,
@ -79,6 +86,45 @@ fake_responses = {
},
}
fake_responses_pagination = {
'/v1/chassis':
{
'GET': (
{},
{"chassis": [CHASSIS],
"next": "http://127.0.0.1:6385/v1/chassis/?limit=1"}
),
},
'/v1/chassis/?limit=1':
{
'GET': (
{},
{"chassis": [CHASSIS2]}
),
},
'/v1/chassis/?marker=%s' % CHASSIS['uuid']:
{
'GET': (
{},
{"chassis": [CHASSIS2]}
),
},
'/v1/chassis/%s/nodes?limit=1' % CHASSIS['uuid']:
{
'GET': (
{},
{"nodes": [NODE]},
),
},
'/v1/chassis/%s/nodes?marker=%s' % (CHASSIS['uuid'], NODE['uuid']):
{
'GET': (
{},
{"nodes": [NODE]},
),
},
}
class ChassisManagerTest(testtools.TestCase):
@ -95,6 +141,37 @@ class ChassisManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(chassis))
def test_chassis_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.chassis.ChassisManager(self.api)
chassis = self.mgr.list(limit=1)
expect = [
('GET', '/v1/chassis/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(chassis, HasLength(1))
def test_chassis_list_marker(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.chassis.ChassisManager(self.api)
chassis = self.mgr.list(marker=CHASSIS['uuid'])
expect = [
('GET', '/v1/chassis/?marker=%s' % CHASSIS['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(chassis, HasLength(1))
def test_chassis_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.chassis.ChassisManager(self.api)
chassis = self.mgr.list(limit=0)
expect = [
('GET', '/v1/chassis', {}, None),
('GET', '/v1/chassis/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(chassis, HasLength(2))
def test_chassis_show(self):
chassis = self.mgr.get(CHASSIS['uuid'])
expect = [
@ -139,3 +216,28 @@ class ChassisManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(nodes))
self.assertEqual(NODE['uuid'], nodes[0].uuid)
def test_chassis_node_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.chassis.ChassisManager(self.api)
nodes = self.mgr.list_nodes(CHASSIS['uuid'], limit=1)
expect = [
('GET',
'/v1/chassis/%s/nodes?limit=1' % CHASSIS['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(nodes, HasLength(1))
self.assertEqual(NODE['uuid'], nodes[0].uuid)
def test_chassis_node_list_marker(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.chassis.ChassisManager(self.api)
nodes = self.mgr.list_nodes(CHASSIS['uuid'], marker=NODE['uuid'])
expect = [
('GET',
'/v1/chassis/%s/nodes?marker=%s' % (CHASSIS['uuid'],
NODE['uuid']), {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(nodes, HasLength(1))
self.assertEqual(NODE['uuid'], nodes[0].uuid)

@ -17,10 +17,10 @@
import copy
import testtools
from testtools.matchers import HasLength
from ironicclient.tests import utils
import ironicclient.v1.node
from testtools.matchers import HasLength
NODE1 = {'id': 123,
'uuid': '66666666-7777-8888-9999-000000000000',
@ -77,7 +77,7 @@ fake_responses = {
{
'GET': (
{},
{"nodes": [NODE1, NODE2]},
{"nodes": [NODE1, NODE2]}
),
'POST': (
{},
@ -203,6 +203,45 @@ fake_responses = {
},
}
fake_responses_pagination = {
'/v1/nodes':
{
'GET': (
{},
{"nodes": [NODE1],
"next": "http://127.0.0.1:6385/v1/nodes/?limit=1"}
),
},
'/v1/nodes/?limit=1':
{
'GET': (
{},
{"nodes": [NODE2]}
),
},
'/v1/nodes/?marker=%s' % NODE1['uuid']:
{
'GET': (
{},
{"nodes": [NODE2]}
),
},
'/v1/nodes/%s/ports?limit=1' % NODE1['uuid']:
{
'GET': (
{},
{"ports": [PORT]},
),
},
'/v1/nodes/%s/ports?marker=%s' % (NODE1['uuid'], PORT['uuid']):
{
'GET': (
{},
{"ports": [PORT]},
),
},
}
class NodeManagerTest(testtools.TestCase):
@ -219,6 +258,37 @@ class NodeManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(nodes))
def test_node_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.node.NodeManager(self.api)
nodes = self.mgr.list(limit=1)
expect = [
('GET', '/v1/nodes/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(nodes, HasLength(1))
def test_node_list_marker(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.node.NodeManager(self.api)
nodes = self.mgr.list(marker=NODE1['uuid'])
expect = [
('GET', '/v1/nodes/?marker=%s' % NODE1['uuid'], {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(nodes, HasLength(1))
def test_node_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.node.NodeManager(self.api)
nodes = self.mgr.list(limit=0)
expect = [
('GET', '/v1/nodes', {}, None),
('GET', '/v1/nodes/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(2, len(nodes))
def test_node_list_associated(self):
nodes = self.mgr.list(associated=True)
expect = [
@ -319,6 +389,29 @@ class NodeManagerTest(testtools.TestCase):
self.assertEqual(PORT['uuid'], ports[0].uuid)
self.assertEqual(PORT['address'], ports[0].address)
def test_node_port_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.node.NodeManager(self.api)
ports = self.mgr.list_ports(NODE1['uuid'], limit=1)
expect = [
('GET', '/v1/nodes/%s/ports?limit=1' % NODE1['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(ports, HasLength(1))
self.assertEqual(PORT['uuid'], ports[0].uuid)
self.assertEqual(PORT['address'], ports[0].address)
def test_node_port_list_marker(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.node.NodeManager(self.api)
ports = self.mgr.list_ports(NODE1['uuid'], marker=PORT['uuid'])
expect = [
('GET', '/v1/nodes/%s/ports?marker=%s' % (NODE1['uuid'],
PORT['uuid']), {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(ports, HasLength(1))
def test_node_set_power_state(self):
power_state = self.mgr.set_power_state(NODE1['uuid'], "on")
body = {'target': 'power on'}

@ -18,6 +18,7 @@
import copy
import testtools
from testtools.matchers import HasLength
from ironicclient.tests import utils
import ironicclient.v1.port
@ -28,6 +29,12 @@ PORT = {'id': 987,
'address': 'AA:BB:CC:DD:EE:FF',
'extra': {}}
PORT2 = {'id': 988,
'uuid': '55555555-4444-3333-2222-111111111111',
'node_uuid': '55555555-4444-3333-2222-111111111111',
'address': 'AA:AA:AA:BB:BB:BB',
'extra': {}}
CREATE_PORT = copy.deepcopy(PORT)
del CREATE_PORT['id']
del CREATE_PORT['uuid']
@ -65,6 +72,31 @@ fake_responses = {
},
}
fake_responses_pagination = {
'/v1/ports':
{
'GET': (
{},
{"ports": [PORT],
"next": "http://127.0.0.1:6385/v1/ports/?limit=1"}
),
},
'/v1/ports/?limit=1':
{
'GET': (
{},
{"ports": [PORT2]}
),
},
'/v1/ports/?marker=%s' % PORT['uuid']:
{
'GET': (
{},
{"ports": [PORT2]}
),
},
}
class PortManagerTest(testtools.TestCase):
@ -81,6 +113,37 @@ class PortManagerTest(testtools.TestCase):
self.assertEqual(expect, self.api.calls)
self.assertEqual(1, len(ports))
def test_ports_list_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.port.PortManager(self.api)
ports = self.mgr.list(limit=1)
expect = [
('GET', '/v1/ports/?limit=1', {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(ports, HasLength(1))
def test_ports_list_marker(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.port.PortManager(self.api)
ports = self.mgr.list(marker=PORT['uuid'])
expect = [
('GET', '/v1/ports/?marker=%s' % PORT['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertThat(ports, HasLength(1))
def test_ports_list_pagination_no_limit(self):
self.api = utils.FakeAPI(fake_responses_pagination)
self.mgr = ironicclient.v1.port.PortManager(self.api)
ports = self.mgr.list(limit=0)
expect = [
('GET', '/v1/ports', {}, None),
('GET', '/v1/ports/?limit=1', {}, None)
]
self.assertEqual(expect, self.api.calls)
self.assertThat(ports, HasLength(2))
def test_ports_show(self):
port = self.mgr.get(PORT['uuid'])
expect = [

@ -33,12 +33,80 @@ class ChassisManager(base.Manager):
def _path(id=None):
return '/v1/chassis/%s' % id if id else '/v1/chassis'
def list(self):
return self._list(self._path(), "chassis")
def list(self, marker=None, limit=None):
"""Retrieve a list of chassis.
:param marker: Optional, the UUID of a chassis, eg the last
chassis from a previous result set. Return
the next result set.
:param limit: The maximum number of results to return per
request, if:
1) limit > 0, the maximum number of chassis to return.
2) limit == 0, return the entire list of chassis.
3) limit param is NOT specified (None), the number of items
returned respect the maximum imposed by the Ironic API
(see Ironic's api.max_limit option).
:returns: A list of chassis.
"""
if limit is not None:
limit = int(limit)
filters = []
if isinstance(limit, int) and limit > 0:
filters.append('limit=%s' % limit)
if marker is not None:
filters.append('marker=%s' % marker)
path = None
if filters:
path = '?' + '&'.join(filters)
if limit is None:
return self._list(self._path(path), "chassis")
else:
return self._list_pagination(self._path(path), "chassis",
limit=limit)
def list_nodes(self, chassis_id, marker=None, limit=None):
"""List all the nodes for a given chassis.
:param chassis_id: The UUID of the chassis.
:param marker: Optional, the UUID of a node, eg the last
node from a previous result set. Return
the next result set.
:param limit: The maximum number of results to return per
request, if:
1) limit > 0, the maximum number of nodes to return.
2) limit == 0, return the entire list of nodes.
3) limit param is NOT specified (None), the number of items
returned respect the maximum imposed by the Ironic API
(see Ironic's api.max_limit option).
:returns: A list of nodes.
"""
if limit is not None:
limit = int(limit)
filters = []
if isinstance(limit, int) and limit > 0:
filters.append('limit=%s' % limit)
if marker is not None:
filters.append('marker=%s' % marker)
def list_nodes(self, chassis_id):
path = "%s/nodes" % chassis_id
return self._list(self._path(path), "nodes")
if filters:
path += '?' + '&'.join(filters)
if limit is None:
return self._list(self._path(path), "nodes")
else:
return self._list_pagination(self._path(path), "nodes",
limit=limit)
def get(self, chassis_id):
try:

@ -32,12 +32,31 @@ def do_chassis_show(cc, args):
_print_chassis_show(chassis)
@cliutils.arg(
'--limit',
metavar='<limit>',
type=int,
help='Maximum number of chassis to return per request, '
'0 for no limit. Default is the maximum number used '
'by the Ironic API Service.')
@cliutils.arg(
'--marker',
metavar='<marker>',
help='Chassis UUID (e.g of the last chassis in the list '
'from a previous request). Returns the list of chassis '
'after this UUID.')
def do_chassis_list(cc, args):
"""List chassis."""
chassis = cc.chassis.list()
params = {}
if args.marker is not None:
params['marker'] = args.marker
if args.limit is not None:
params['limit'] = args.limit
chassis = cc.chassis.list(**params)
field_labels = ['UUID', 'Description']
fields = ['uuid', 'description']
cliutils.print_list(chassis, fields, field_labels, sortby_index=1)
cliutils.print_list(chassis, fields, field_labels, sortby_index=None)
@cliutils.arg(
@ -96,11 +115,30 @@ def do_chassis_update(cc, args):
_print_chassis_show(chassis)
@cliutils.arg(
'--limit',
metavar='<limit>',
type=int,
help='Maximum number of nodes to return per request, '
'0 for no limit. Default is the maximum number used '
'by the Ironic API Service.')
@cliutils.arg(
'--marker',
metavar='<marker>',
help='Node UUID (e.g of the last node in the list from '
'a previous request). Returns the list of nodes '
'after this UUID.')
@cliutils.arg('chassis', metavar='<chassis id>', help="UUID of chassis")
def do_chassis_node_list(cc, args):
"""List the nodes contained in the chassis."""
nodes = cc.chassis.list_nodes(args.chassis)
params = {}
if args.marker is not None:
params['marker'] = args.marker
if args.limit is not None:
params['limit'] = args.limit
nodes = cc.chassis.list_nodes(args.chassis, **params)
field_labels = ['UUID', 'Instance UUID',
'Power State', 'Provisioning State']
fields = ['uuid', 'instance_uuid', 'power_state', 'provision_state']
cliutils.print_list(nodes, fields, field_labels, sortby_index=1)
cliutils.print_list(nodes, fields, field_labels, sortby_index=None)

@ -33,22 +33,89 @@ class NodeManager(base.Manager):
def _path(id=None):
return '/v1/nodes/%s' % id if id else '/v1/nodes'
def list(self, associated=None, maintenance=None):
def list(self, associated=None, maintenance=None, marker=None, limit=None):
"""Retrieve a list of nodes.
:param associated: Optional, boolean whether to return a list of
associated or unassociated nodes.
:param maintenance: Optional, boolean value that indicates whether
to get nodes in maintenance mode ("True"), or not
in maintenance mode ("False").
:param marker: Optional, the UUID of a node, eg the last
node from a previous result set. Return
the next result set.
:param limit: The maximum number of results to return per
request, if:
1) limit > 0, the maximum number of nodes to return.
2) limit == 0, return the entire list of nodes.
3) limit param is NOT specified (None), the number of items
returned respect the maximum imposed by the Ironic API
(see Ironic's api.max_limit option).
:returns: A list of nodes.
"""
if limit is not None:
limit = int(limit)
filters = []
if isinstance(limit, int) and limit > 0:
filters.append('limit=%s' % limit)
if marker is not None:
filters.append('marker=%s' % marker)
if associated is not None:
filters.append('associated=%s' % associated)
if maintenance is not None:
filters.append('maintenance=%s' % maintenance)
if not filters:
return self._list(self._path(), "nodes")
else:
path = None
if filters:
path = '?' + '&'.join(filters)
return self._list(self._path(path), "nodes")
def list_ports(self, node_id):
if limit is None:
return self._list(self._path(path), "nodes")
else:
return self._list_pagination(self._path(path), "nodes",
limit=limit)
def list_ports(self, node_id, marker=None, limit=None):
"""List all the ports for a given node.
:param node_id: The UUID of the node.
:param marker: Optional, the UUID of a port, eg the last
port from a previous result set. Return
the next result set.
:param limit: The maximum number of results to return per
request, if:
1) limit > 0, the maximum number of ports to return.
2) limit == 0, return the entire list of ports.
3) limit param is NOT specified (None), the number of items
returned respect the maximum imposed by the Ironic API
(see Ironic's api.max_limit option).
:returns: A list of ports.
"""
if limit is not None:
limit = int(limit)
filters = []
if isinstance(limit, int) and limit > 0:
filters.append('limit=%s' % limit)
if marker is not None:
filters.append('marker=%s' % marker)
path = "%s/ports" % node_id
return self._list(self._path(path), "ports")
if filters:
path += '?' + '&'.join(filters)
if limit is None:
return self._list(self._path(path), "ports")
else:
return self._list_pagination(self._path(path), "ports",
limit=limit)
def get(self, node_id):
try:

@ -47,6 +47,19 @@ def do_node_show(cc, args):
_print_node_show(node)
@cliutils.arg(
'--limit',
metavar='<limit>',
type=int,
help='Maximum number of nodes to return per request, '
'0 for no limit. Default is the maximum number used '
'by the Ironic API Service.')
@cliutils.arg(
'--marker',
metavar='<marker>',
help='Node UUID (e.g of the last node in the list from '
'a previous request). Returns the list of nodes '
'after this UUID.')
@cliutils.arg(
'--maintenance',
metavar='<maintenance>',
@ -64,13 +77,17 @@ def do_node_list(cc, args):
params['associated'] = args.associated
if args.maintenance is not None:
params['maintenance'] = args.maintenance
if args.marker is not None:
params['marker'] = args.marker
if args.limit is not None:
params['limit'] = args.limit
nodes = cc.node.list(**params)
field_labels = ['UUID', 'Instance UUID', 'Power State',
'Provisioning State', 'Maintenance']
fields = ['uuid', 'instance_uuid', 'power_state',
'provision_state', 'maintenance']
cliutils.print_list(nodes, fields, field_labels, sortby_index=1)
cliutils.print_list(nodes, fields, field_labels, sortby_index=None)
@cliutils.arg(
@ -145,13 +162,32 @@ def do_node_update(cc, args):
_print_node_show(node)
@cliutils.arg(
'--limit',
metavar='<limit>',
type=int,
help='Maximum number of ports to return per request, '
'0 for no limit. Default is the maximum number used '
'by the Ironic API Service.')
@cliutils.arg(
'--marker',
metavar='<marker>',
help='Port UUID (e.g of the last port in the list from '
'a previous request). Returns the list of ports '
'after this UUID.')
@cliutils.arg('node', metavar='<node id>', help="UUID of node")
def do_node_port_list(cc, args):
"""List the ports associated with the node."""
ports = cc.node.list_ports(args.node)
params = {}
if args.marker is not None:
params['marker'] = args.marker
if args.limit is not None:
params['limit'] = args.limit
ports = cc.node.list_ports(args.node, **params)
field_labels = ['UUID', 'Address']
fields = ['uuid', 'address']
cliutils.print_list(ports, fields, field_labels, sortby_index=1)
cliutils.print_list(ports, fields, field_labels, sortby_index=None)
@cliutils.arg('node', metavar='<node id>', help="UUID of node")

@ -32,8 +32,42 @@ class PortManager(base.Manager):
def _path(id=None):
return '/v1/ports/%s' % id if id else '/v1/ports'
def list(self):
return self._list(self._path(), "ports")
def list(self, limit=None, marker=None):
"""Retrieve a list of port.
:param marker: Optional, the UUID of a port, eg the last
port from a previous result set. Return
the next result set.
:param limit: The maximum number of results to return per
request, if:
1) limit > 0, the maximum number of ports to return.
2) limit == 0, return the entire list of ports.
3) limit param is NOT specified (None), the number of items
returned respect the maximum imposed by the Ironic API
(see Ironic's api.max_limit option).
:returns: A list of ports.
"""
if limit is not None:
limit = int(limit)
filters = []
if isinstance(limit, int) and limit > 0:
filters.append('limit=%s' % limit)
if marker is not None:
filters.append('marker=%s' % marker)
path = None
if filters:
path = '?' + '&'.join(filters)
if limit is None:
return self._list(self._path(path), "ports")
else:
return self._list_pagination(self._path(path), "ports",
limit=limit)
def get(self, port_id):
try:

@ -33,12 +33,31 @@ def do_port_show(cc, args):
_print_port_show(port)
@cliutils.arg(
'--limit',
metavar='<limit>',
type=int,
help='Maximum number of ports to return per request, '
'0 for no limit. Default is the maximum number used '
'by the Ironic API Service.')
@cliutils.arg(
'--marker',
metavar='<marker>',
help='Port UUID (e.g of the last port in the list from '
'a previous request). Returns the list of ports '
'after this UUID.')
def do_port_list(cc, args):
"""List ports."""
port = cc.port.list()
params = {}
if args.marker is not None:
params['marker'] = args.marker
if args.limit is not None:
params['limit'] = args.limit
port = cc.port.list(**params)
field_labels = ['UUID', 'Address']
fields = ['uuid', 'address']
cliutils.print_list(port, fields, field_labels, sortby_index=1)
cliutils.print_list(port, fields, field_labels, sortby_index=None)
@cliutils.arg(