summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHe Jie Xu <xuhj@linux.vnet.ibm.com>2013-01-17 19:38:36 +0800
committerHe Jie Xu <xuhj@linux.vnet.ibm.com>2013-03-01 12:43:25 +0800
commit49982a3aeab3445b2f99bedd21935710e0ddef5a (patch)
tree131e8a00dbf7f1adf790391e2b0c31743f7d39b9
parent4722df472ea76e39aee6536247644cf37e7b10bb (diff)
downloadpython-neutronclient-49982a3aeab3445b2f99bedd21935710e0ddef5a.tar.gz
Add pagination support for client
Fixes bug 1130625 Add pagination params: -P, --page-size SIZE Add sorting params: --sort-key FIELD --sort-dir {asc,desc} Add xml support Change-Id: I76abb6335f53176feade216413a8cabaaefe42be
-rw-r--r--quantumclient/common/constants.py2
-rw-r--r--quantumclient/common/serializer.py21
-rw-r--r--quantumclient/quantum/v2_0/__init__.py51
-rw-r--r--quantumclient/quantum/v2_0/floatingip.py2
-rw-r--r--quantumclient/quantum/v2_0/lb/healthmonitor.py2
-rw-r--r--quantumclient/quantum/v2_0/lb/member.py2
-rw-r--r--quantumclient/quantum/v2_0/lb/pool.py2
-rw-r--r--quantumclient/quantum/v2_0/lb/vip.py2
-rw-r--r--quantumclient/quantum/v2_0/network.py13
-rw-r--r--quantumclient/quantum/v2_0/port.py4
-rw-r--r--quantumclient/quantum/v2_0/router.py2
-rw-r--r--quantumclient/quantum/v2_0/securitygroup.py13
-rw-r--r--quantumclient/quantum/v2_0/subnet.py2
-rw-r--r--quantumclient/tests/unit/lb/test_cli20_healthmonitor.py25
-rw-r--r--quantumclient/tests/unit/lb/test_cli20_member.py22
-rw-r--r--quantumclient/tests/unit/lb/test_cli20_pool.py22
-rw-r--r--quantumclient/tests/unit/lb/test_cli20_vip.py22
-rw-r--r--quantumclient/tests/unit/test_cli20.py61
-rw-r--r--quantumclient/tests/unit/test_cli20_floatingips.py21
-rw-r--r--quantumclient/tests/unit/test_cli20_network.py45
-rw-r--r--quantumclient/tests/unit/test_cli20_port.py21
-rw-r--r--quantumclient/tests/unit/test_cli20_router.py21
-rw-r--r--quantumclient/tests/unit/test_cli20_securitygroup.py61
-rw-r--r--quantumclient/tests/unit/test_cli20_subnet.py21
-rw-r--r--quantumclient/v2_0/client.py86
25 files changed, 516 insertions, 30 deletions
diff --git a/quantumclient/common/constants.py b/quantumclient/common/constants.py
index 572baa9..7e56331 100644
--- a/quantumclient/common/constants.py
+++ b/quantumclient/common/constants.py
@@ -22,6 +22,8 @@ XSI_NIL_ATTR = "xmlns:xsi"
TYPE_XMLNS = "xmlns:quantum"
TYPE_ATTR = "quantum:type"
VIRTUAL_ROOT_KEY = "_v_root"
+ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
+ATOM_LINK_NOTATION = "{%s}link" % ATOM_NAMESPACE
TYPE_BOOL = "bool"
TYPE_INT = "int"
diff --git a/quantumclient/common/serializer.py b/quantumclient/common/serializer.py
index 7eba930..535171b 100644
--- a/quantumclient/common/serializer.py
+++ b/quantumclient/common/serializer.py
@@ -245,18 +245,33 @@ class XMLDeserializer(TextDeserializer):
else:
return tag
+ def _get_links(self, root_tag, node):
+ link_nodes = node.findall(constants.ATOM_LINK_NOTATION)
+ root_tag = self._get_key(node.tag)
+ link_key = "%s_links" % root_tag
+ link_list = []
+ for link in link_nodes:
+ link_list.append({'rel': link.get('rel'),
+ 'href': link.get('href')})
+ # Remove link node in order to avoid link node being
+ # processed as an item in _from_xml_node
+ node.remove(link)
+ return link_list and {link_key: link_list} or {}
+
def _from_xml(self, datastring):
if datastring is None:
return None
plurals = set(self.metadata.get('plurals', {}))
try:
node = etree.fromstring(datastring)
- result = self._from_xml_node(node, plurals)
root_tag = self._get_key(node.tag)
+ links = self._get_links(root_tag, node)
+ result = self._from_xml_node(node, plurals)
+ # There is no case where root_tag = constants.VIRTUAL_ROOT_KEY
+ # and links is not None because of the way data are serialized
if root_tag == constants.VIRTUAL_ROOT_KEY:
return result
- else:
- return {root_tag: result}
+ return dict({root_tag: result}, **links)
except Exception as e:
parseError = False
# Python2.7
diff --git a/quantumclient/quantum/v2_0/__init__.py b/quantumclient/quantum/v2_0/__init__.py
index cbf87b0..6101db9 100644
--- a/quantumclient/quantum/v2_0/__init__.py
+++ b/quantumclient/quantum/v2_0/__init__.py
@@ -90,6 +90,35 @@ def add_show_list_common_argument(parser):
default=[])
+def add_pagination_argument(parser):
+ parser.add_argument(
+ '-P', '--page-size',
+ dest='page_size', metavar='SIZE', type=int,
+ help=("specify retrieve unit of each request, then split one request "
+ "to several requests"),
+ default=None)
+
+
+def add_sorting_argument(parser):
+ parser.add_argument(
+ '--sort-key',
+ dest='sort_key', metavar='FIELD',
+ action='append',
+ help=("sort list by specified fields (This option can be repeated), "
+ "The number of sort_dir and sort_key should match each other, "
+ "more sort_dir specified will be omitted, less will be filled "
+ "with asc as default direction "),
+ default=[])
+ parser.add_argument(
+ '--sort-dir',
+ dest='sort_dir', metavar='{asc,desc}',
+ help=("sort list in specified directions "
+ "(This option can be repeated)"),
+ action='append',
+ default=[],
+ choices=['asc', 'desc'])
+
+
def add_extra_argument(parser, name, _help):
parser.add_argument(
name,
@@ -411,12 +440,18 @@ class ListCommand(QuantumCommand, lister.Lister):
_formatters = {}
list_columns = []
unknown_parts_flag = True
+ pagination_support = False
+ sorting_support = False
def get_parser(self, prog_name):
parser = super(ListCommand, self).get_parser(prog_name)
add_show_list_common_argument(parser)
if self.unknown_parts_flag:
add_extra_argument(parser, 'filter_specs', 'filters options')
+ if self.pagination_support:
+ add_pagination_argument(parser)
+ if self.sorting_support:
+ add_sorting_argument(parser)
return parser
def args2search_opts(self, parsed_args):
@@ -445,6 +480,22 @@ class ListCommand(QuantumCommand, lister.Lister):
self.values_specs)
search_opts = self.args2search_opts(parsed_args)
search_opts.update(_extra_values)
+ if self.pagination_support:
+ page_size = parsed_args.page_size
+ if page_size:
+ search_opts.update({'limit': page_size})
+ if self.sorting_support:
+ keys = parsed_args.sort_key
+ if keys:
+ search_opts.update({'sort_key': keys})
+ dirs = parsed_args.sort_dir
+ len_diff = len(keys) - len(dirs)
+ if len_diff > 0:
+ dirs += ['asc'] * len_diff
+ elif len_diff < 0:
+ dirs = dirs[:len(keys)]
+ if dirs:
+ search_opts.update({'sort_dir': dirs})
data = self.call_server(quantum_client, search_opts, parsed_args)
collection = self.resource + "s"
return data.get(collection, [])
diff --git a/quantumclient/quantum/v2_0/floatingip.py b/quantumclient/quantum/v2_0/floatingip.py
index 3198dfe..f4141f3 100644
--- a/quantumclient/quantum/v2_0/floatingip.py
+++ b/quantumclient/quantum/v2_0/floatingip.py
@@ -34,6 +34,8 @@ class ListFloatingIP(ListCommand):
_formatters = {}
list_columns = ['id', 'fixed_ip_address', 'floating_ip_address',
'port_id']
+ pagination_support = True
+ sorting_support = True
class ShowFloatingIP(ShowCommand):
diff --git a/quantumclient/quantum/v2_0/lb/healthmonitor.py b/quantumclient/quantum/v2_0/lb/healthmonitor.py
index 9aa2874..3071527 100644
--- a/quantumclient/quantum/v2_0/lb/healthmonitor.py
+++ b/quantumclient/quantum/v2_0/lb/healthmonitor.py
@@ -29,6 +29,8 @@ class ListHealthMonitor(quantumv20.ListCommand):
log = logging.getLogger(__name__ + '.ListHealthMonitor')
list_columns = ['id', 'type', 'admin_state_up', 'status']
_formatters = {}
+ pagination_support = True
+ sorting_support = True
class ShowHealthMonitor(quantumv20.ShowCommand):
diff --git a/quantumclient/quantum/v2_0/lb/member.py b/quantumclient/quantum/v2_0/lb/member.py
index c43b94b..e3da789 100644
--- a/quantumclient/quantum/v2_0/lb/member.py
+++ b/quantumclient/quantum/v2_0/lb/member.py
@@ -31,6 +31,8 @@ class ListMember(quantumv20.ListCommand):
'id', 'address', 'protocol_port', 'admin_state_up', 'status'
]
_formatters = {}
+ pagination_support = True
+ sorting_support = True
class ShowMember(quantumv20.ShowCommand):
diff --git a/quantumclient/quantum/v2_0/lb/pool.py b/quantumclient/quantum/v2_0/lb/pool.py
index b39c684..049ac67 100644
--- a/quantumclient/quantum/v2_0/lb/pool.py
+++ b/quantumclient/quantum/v2_0/lb/pool.py
@@ -30,6 +30,8 @@ class ListPool(quantumv20.ListCommand):
list_columns = ['id', 'name', 'lb_method', 'protocol',
'admin_state_up', 'status']
_formatters = {}
+ pagination_support = True
+ sorting_support = True
class ShowPool(quantumv20.ShowCommand):
diff --git a/quantumclient/quantum/v2_0/lb/vip.py b/quantumclient/quantum/v2_0/lb/vip.py
index 5c2c304..c41fafa 100644
--- a/quantumclient/quantum/v2_0/lb/vip.py
+++ b/quantumclient/quantum/v2_0/lb/vip.py
@@ -30,6 +30,8 @@ class ListVip(quantumv20.ListCommand):
list_columns = ['id', 'name', 'algorithm', 'address', 'protocol',
'admin_state_up', 'status']
_formatters = {}
+ pagination_support = True
+ sorting_support = True
class ShowVip(quantumv20.ShowCommand):
diff --git a/quantumclient/quantum/v2_0/network.py b/quantumclient/quantum/v2_0/network.py
index dfc0171..c7f0d8e 100644
--- a/quantumclient/quantum/v2_0/network.py
+++ b/quantumclient/quantum/v2_0/network.py
@@ -41,11 +41,22 @@ class ListNetwork(ListCommand):
log = logging.getLogger(__name__ + '.ListNetwork')
_formatters = {'subnets': _format_subnets, }
list_columns = ['id', 'name', 'subnets']
+ pagination_support = True
+ sorting_support = True
def extend_list(self, data, parsed_args):
"""Add subnet information to a network list"""
quantum_client = self.get_client()
search_opts = {'fields': ['id', 'cidr']}
+ if self.pagination_support:
+ page_size = parsed_args.page_size
+ if page_size:
+ search_opts.update({'limit': page_size})
+ subnet_ids = []
+ for n in data:
+ if 'subnets' in n:
+ subnet_ids.extend(n['subnets'])
+ search_opts.update({'id': subnet_ids})
subnets = quantum_client.list_subnets(**search_opts).get('subnets', [])
subnet_dict = dict([(s['id'], s) for s in subnets])
for n in data:
@@ -58,6 +69,8 @@ class ListExternalNetwork(ListNetwork):
"""List external networks that belong to a given tenant"""
log = logging.getLogger(__name__ + '.ListExternalNetwork')
+ pagination_support = True
+ sorting_support = True
def retrieve_list(self, parsed_args):
external = '--router:external=True'
diff --git a/quantumclient/quantum/v2_0/port.py b/quantumclient/quantum/v2_0/port.py
index 753df0e..e946511 100644
--- a/quantumclient/quantum/v2_0/port.py
+++ b/quantumclient/quantum/v2_0/port.py
@@ -41,6 +41,8 @@ class ListPort(ListCommand):
log = logging.getLogger(__name__ + '.ListPort')
_formatters = {'fixed_ips': _format_fixed_ips, }
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
+ pagination_support = True
+ sorting_support = True
class ListRouterPort(ListCommand):
@@ -50,6 +52,8 @@ class ListRouterPort(ListCommand):
log = logging.getLogger(__name__ + '.ListRouterPort')
_formatters = {'fixed_ips': _format_fixed_ips, }
list_columns = ['id', 'name', 'mac_address', 'fixed_ips']
+ pagination_support = True
+ sorting_support = True
def get_parser(self, prog_name):
parser = super(ListRouterPort, self).get_parser(prog_name)
diff --git a/quantumclient/quantum/v2_0/router.py b/quantumclient/quantum/v2_0/router.py
index a0de0c8..86eaa72 100644
--- a/quantumclient/quantum/v2_0/router.py
+++ b/quantumclient/quantum/v2_0/router.py
@@ -42,6 +42,8 @@ class ListRouter(ListCommand):
log = logging.getLogger(__name__ + '.ListRouter')
_formatters = {'external_gateway_info': _format_external_gateway_info, }
list_columns = ['id', 'name', 'external_gateway_info']
+ pagination_support = True
+ sorting_support = True
class ShowRouter(ShowCommand):
diff --git a/quantumclient/quantum/v2_0/securitygroup.py b/quantumclient/quantum/v2_0/securitygroup.py
index 9442ccc..971a2c8 100644
--- a/quantumclient/quantum/v2_0/securitygroup.py
+++ b/quantumclient/quantum/v2_0/securitygroup.py
@@ -28,6 +28,8 @@ class ListSecurityGroup(quantumv20.ListCommand):
log = logging.getLogger(__name__ + '.ListSecurityGroup')
_formatters = {}
list_columns = ['id', 'name', 'description']
+ pagination_support = True
+ sorting_support = True
class ShowSecurityGroup(quantumv20.ShowCommand):
@@ -81,6 +83,8 @@ class ListSecurityGroupRule(quantumv20.ListCommand):
'source_ip_prefix', 'source_group_id']
replace_rules = {'security_group_id': 'security_group',
'source_group_id': 'source_group'}
+ pagination_support = True
+ sorting_support = True
def get_parser(self, prog_name):
parser = super(ListSecurityGroupRule, self).get_parser(prog_name)
@@ -106,6 +110,15 @@ class ListSecurityGroupRule(quantumv20.ListCommand):
return
quantum_client = self.get_client()
search_opts = {'fields': ['id', 'name']}
+ if self.pagination_support:
+ page_size = parsed_args.page_size
+ if page_size:
+ search_opts.update({'limit': page_size})
+ sec_group_ids = set()
+ for rule in data:
+ for key in self.replace_rules:
+ sec_group_ids.add(rule[key])
+ search_opts.update({"id": sec_group_ids})
secgroups = quantum_client.list_security_groups(**search_opts)
secgroups = secgroups.get('security_groups', [])
sg_dict = dict([(sg['id'], sg['name'])
diff --git a/quantumclient/quantum/v2_0/subnet.py b/quantumclient/quantum/v2_0/subnet.py
index 9339ff5..4442b72 100644
--- a/quantumclient/quantum/v2_0/subnet.py
+++ b/quantumclient/quantum/v2_0/subnet.py
@@ -61,6 +61,8 @@ class ListSubnet(ListCommand):
'dns_nameservers': _format_dns_nameservers,
'host_routes': _format_host_routes, }
list_columns = ['id', 'name', 'cidr', 'allocation_pools']
+ pagination_support = True
+ sorting_support = True
class ShowSubnet(ShowCommand):
diff --git a/quantumclient/tests/unit/lb/test_cli20_healthmonitor.py b/quantumclient/tests/unit/lb/test_cli20_healthmonitor.py
index 657b24e..ab5327c 100644
--- a/quantumclient/tests/unit/lb/test_cli20_healthmonitor.py
+++ b/quantumclient/tests/unit/lb/test_cli20_healthmonitor.py
@@ -92,6 +92,31 @@ class CLITestV20LbHealthmonitor(test_cli20.CLITestV20Base):
None)
self._test_list_resources(resources, cmd, True)
+ def test_list_healthmonitors_pagination(self):
+ """lb-healthmonitor-list"""
+ resources = "health_monitors"
+ cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout),
+ None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_healthmonitors_sort(self):
+ """lb-healthmonitor-list --sort-key name --sort-key id --sort-key asc
+ --sort-key desc
+ """
+ resources = "health_monitors"
+ cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout),
+ None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_healthmonitors_limit(self):
+ """lb-healthmonitor-list -P"""
+ resources = "health_monitors"
+ cmd = healthmonitor.ListHealthMonitor(test_cli20.MyApp(sys.stdout),
+ None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_show_healthmonitor_id(self):
"""lb-healthmonitor-show test_id"""
resource = 'health_monitor'
diff --git a/quantumclient/tests/unit/lb/test_cli20_member.py b/quantumclient/tests/unit/lb/test_cli20_member.py
index 609bba1..2913d4c 100644
--- a/quantumclient/tests/unit/lb/test_cli20_member.py
+++ b/quantumclient/tests/unit/lb/test_cli20_member.py
@@ -72,6 +72,28 @@ class CLITestV20LbMember(test_cli20.CLITestV20Base):
cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
+ def test_list_members_pagination(self):
+ """lb-member-list"""
+ resources = "members"
+ cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_members_sort(self):
+ """lb-member-list --sort-key name --sort-key id --sort-key asc
+ --sort-key desc
+ """
+ resources = "members"
+ cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_members_limit(self):
+ """lb-member-list -P"""
+ resources = "members"
+ cmd = member.ListMember(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_show_member_id(self):
"""lb-member-show test_id"""
resource = 'member'
diff --git a/quantumclient/tests/unit/lb/test_cli20_pool.py b/quantumclient/tests/unit/lb/test_cli20_pool.py
index ee2f4e3..4bf6d3c 100644
--- a/quantumclient/tests/unit/lb/test_cli20_pool.py
+++ b/quantumclient/tests/unit/lb/test_cli20_pool.py
@@ -80,6 +80,28 @@ class CLITestV20LbPool(test_cli20.CLITestV20Base):
cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
+ def test_list_pools_pagination(self):
+ """lb-pool-list"""
+ resources = "pools"
+ cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_pools_sort(self):
+ """lb-pool-list --sort-key name --sort-key id --sort-key asc
+ --sort-key desc
+ """
+ resources = "pools"
+ cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_pools_limit(self):
+ """lb-pool-list -P"""
+ resources = "pools"
+ cmd = pool.ListPool(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_show_pool_id(self):
"""lb-pool-show test_id"""
resource = 'pool'
diff --git a/quantumclient/tests/unit/lb/test_cli20_vip.py b/quantumclient/tests/unit/lb/test_cli20_vip.py
index 880c20a..878e99f 100644
--- a/quantumclient/tests/unit/lb/test_cli20_vip.py
+++ b/quantumclient/tests/unit/lb/test_cli20_vip.py
@@ -125,6 +125,28 @@ class CLITestV20LbVip(test_cli20.CLITestV20Base):
cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
+ def test_list_vips_pagination(self):
+ """lb-vip-list"""
+ resources = "vips"
+ cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_vips_sort(self):
+ """lb-vip-list --sort-key name --sort-key id --sort-key asc
+ --sort-key desc
+ """
+ resources = "vips"
+ cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_vips_limit(self):
+ """lb-vip-list -P"""
+ resources = "vips"
+ cmd = vip.ListVip(test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_show_vip_id(self):
"""lb-vip-show test_id"""
resource = 'vip'
diff --git a/quantumclient/tests/unit/test_cli20.py b/quantumclient/tests/unit/test_cli20.py
index 4e62eda..21b1b51 100644
--- a/quantumclient/tests/unit/test_cli20.py
+++ b/quantumclient/tests/unit/test_cli20.py
@@ -225,7 +225,8 @@ class CLITestV20Base(testtools.TestCase):
self.mox.UnsetStubs()
def _test_list_resources(self, resources, cmd, detail=False, tags=[],
- fields_1=[], fields_2=[]):
+ fields_1=[], fields_2=[], page_size=None,
+ sort_key=[], sort_dir=[]):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
@@ -263,6 +264,32 @@ class CLITestV20Base(testtools.TestCase):
query += "&fields=" + field
else:
query = "fields=" + field
+ if page_size:
+ args.append("--page-size")
+ args.append(str(page_size))
+ if query:
+ query += "&limit=%s" % page_size
+ else:
+ query = "limit=%s" % page_size
+ if sort_key:
+ for key in sort_key:
+ args.append('--sort-key')
+ args.append(key)
+ if query:
+ query += '&'
+ query += 'sort_key=%s' % key
+ if sort_dir:
+ len_diff = len(sort_key) - len(sort_dir)
+ if len_diff > 0:
+ sort_dir += ['asc'] * len_diff
+ elif len_diff < 0:
+ sort_dir = sort_dir[:len(sort_key)]
+ for dir in sort_dir:
+ args.append('--sort-dir')
+ args.append(dir)
+ if query:
+ query += '&'
+ query += 'sort_dir=%s' % dir
path = getattr(self.client, resources + "_path")
self.client.httpclient.request(
MyUrlComparator(end_url(path, query), self.client), 'GET',
@@ -277,6 +304,38 @@ class CLITestV20Base(testtools.TestCase):
_str = self.fake_stdout.make_string()
self.assertTrue('myid1' in _str)
+ def _test_list_resources_with_pagination(self, resources, cmd):
+ self.mox.StubOutWithMock(cmd, "get_client")
+ self.mox.StubOutWithMock(self.client.httpclient, "request")
+ cmd.get_client().MultipleTimes().AndReturn(self.client)
+ path = getattr(self.client, resources + "_path")
+ fake_query = "marker=myid2&limit=2"
+ reses1 = {resources: [{'id': 'myid1', },
+ {'id': 'myid2', }],
+ '%s_links' % resources: [{'href': end_url(path, fake_query),
+ 'rel': 'next'}]}
+ reses2 = {resources: [{'id': 'myid3', },
+ {'id': 'myid4', }]}
+ resstr1 = self.client.serialize(reses1)
+ resstr2 = self.client.serialize(reses2)
+ self.client.httpclient.request(
+ end_url(path, ""), 'GET',
+ body=None,
+ headers=ContainsKeyValue('X-Auth-Token',
+ TOKEN)).AndReturn((MyResp(200), resstr1))
+ self.client.httpclient.request(
+ end_url(path, fake_query), 'GET',
+ body=None,
+ headers=ContainsKeyValue('X-Auth-Token',
+ TOKEN)).AndReturn((MyResp(200), resstr2))
+ self.mox.ReplayAll()
+ cmd_parser = cmd.get_parser("list_" + resources)
+
+ parsed_args = cmd_parser.parse_args("")
+ cmd.run(parsed_args)
+ self.mox.VerifyAll()
+ self.mox.UnsetStubs()
+
def _test_update_resource(self, resource, cmd, myid, args, extrafields):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
diff --git a/quantumclient/tests/unit/test_cli20_floatingips.py b/quantumclient/tests/unit/test_cli20_floatingips.py
index 7b5912c..620470d 100644
--- a/quantumclient/tests/unit/test_cli20_floatingips.py
+++ b/quantumclient/tests/unit/test_cli20_floatingips.py
@@ -86,6 +86,27 @@ class CLITestV20FloatingIps(CLITestV20Base):
cmd = ListFloatingIP(MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
+ def test_list_floatingips_pagination(self):
+ resources = 'floatingips'
+ cmd = ListFloatingIP(MyApp(sys.stdout), None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_floatingips_sort(self):
+ """list floatingips: --sort-key name --sort-key id --sort-key asc
+ --sort-key desc
+ """
+ resources = 'floatingips'
+ cmd = ListFloatingIP(MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_floatingips_limit(self):
+ """list floatingips: -P."""
+ resources = 'floatingips'
+ cmd = ListFloatingIP(MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_delete_floatingip(self):
"""Delete floatingip: fip1"""
resource = 'floatingip'
diff --git a/quantumclient/tests/unit/test_cli20_network.py b/quantumclient/tests/unit/test_cli20_network.py
index 6bc9ebd..a0a7c90 100644
--- a/quantumclient/tests/unit/test_cli20_network.py
+++ b/quantumclient/tests/unit/test_cli20_network.py
@@ -128,12 +128,47 @@ class CLITestV20Network(CLITestV20Base):
self.assertEquals('\n', _str)
def _test_list_networks(self, cmd, detail=False, tags=[],
- fields_1=[], fields_2=[]):
+ fields_1=[], fields_2=[], page_size=None,
+ sort_key=[], sort_dir=[]):
resources = "networks"
self.mox.StubOutWithMock(ListNetwork, "extend_list")
ListNetwork.extend_list(IsA(list), IgnoreArg())
self._test_list_resources(resources, cmd, detail, tags,
- fields_1, fields_2)
+ fields_1, fields_2, page_size=page_size,
+ sort_key=sort_key, sort_dir=sort_dir)
+
+ def test_list_nets_pagination(self):
+ cmd = ListNetwork(MyApp(sys.stdout), None)
+ self.mox.StubOutWithMock(ListNetwork, "extend_list")
+ ListNetwork.extend_list(IsA(list), IgnoreArg())
+ self._test_list_resources_with_pagination("networks", cmd)
+
+ def test_list_nets_sort(self):
+ """list nets: --sort-key name --sort-key id --sort-dir asc
+ --sort-dir desc
+ """
+ cmd = ListNetwork(MyApp(sys.stdout), None)
+ self._test_list_networks(cmd, sort_key=['name', 'id'],
+ sort_dir=['asc', 'desc'])
+
+ def test_list_nets_sort_with_keys_more_than_dirs(self):
+ """list nets: --sort-key name --sort-key id --sort-dir desc
+ """
+ cmd = ListNetwork(MyApp(sys.stdout), None)
+ self._test_list_networks(cmd, sort_key=['name', 'id'],
+ sort_dir=['desc'])
+
+ def test_list_nets_sort_with_dirs_more_than_keys(self):
+ """list nets: --sort-key name --sort-dir desc --sort-dir asc
+ """
+ cmd = ListNetwork(MyApp(sys.stdout), None)
+ self._test_list_networks(cmd, sort_key=['name'],
+ sort_dir=['desc', 'asc'])
+
+ def test_list_nets_limit(self):
+ """list nets: -P"""
+ cmd = ListNetwork(MyApp(sys.stdout), None)
+ self._test_list_networks(cmd, page_size=1000)
def test_list_nets_detail(self):
"""list nets: -D."""
@@ -170,11 +205,15 @@ class CLITestV20Network(CLITestV20Base):
cmd.get_client().AndReturn(self.client)
setup_list_stub('networks', data, '')
cmd.get_client().AndReturn(self.client)
+ filters = ''
+ for n in data:
+ for s in n['subnets']:
+ filters = filters + "&id=%s" % s
setup_list_stub('subnets',
[{'id': 'mysubid1', 'cidr': '192.168.1.0/24'},
{'id': 'mysubid2', 'cidr': '172.16.0.0/24'},
{'id': 'mysubid3', 'cidr': '10.1.1.0/24'}],
- query='fields=id&fields=cidr')
+ query='fields=id&fields=cidr' + filters)
self.mox.ReplayAll()
args = []
diff --git a/quantumclient/tests/unit/test_cli20_port.py b/quantumclient/tests/unit/test_cli20_port.py
index faec572..65cfc99 100644
--- a/quantumclient/tests/unit/test_cli20_port.py
+++ b/quantumclient/tests/unit/test_cli20_port.py
@@ -140,6 +140,27 @@ class CLITestV20Port(CLITestV20Base):
cmd = ListPort(MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
+ def test_list_ports_pagination(self):
+ resources = "ports"
+ cmd = ListPort(MyApp(sys.stdout), None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_ports_sort(self):
+ """list ports: --sort-key name --sort-key id --sort-key asc
+ --sort-key desc
+ """
+ resources = "ports"
+ cmd = ListPort(MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_ports_limit(self):
+ """list ports: -P"""
+ resources = "ports"
+ cmd = ListPort(MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_list_ports_tags(self):
"""List ports: -- --tags a b."""
resources = "ports"
diff --git a/quantumclient/tests/unit/test_cli20_router.py b/quantumclient/tests/unit/test_cli20_router.py
index 502365d..1875b8a 100644
--- a/quantumclient/tests/unit/test_cli20_router.py
+++ b/quantumclient/tests/unit/test_cli20_router.py
@@ -76,6 +76,27 @@ class CLITestV20Router(CLITestV20Base):
cmd = ListRouter(MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
+ def test_list_routers_pagination(self):
+ resources = "routers"
+ cmd = ListRouter(MyApp(sys.stdout), None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_routers_sort(self):
+ """list routers: --sort-key name --sort-key id --sort-key asc
+ --sort-key desc
+ """
+ resources = "routers"
+ cmd = ListRouter(MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_routers_limit(self):
+ """list routers: -P"""
+ resources = "routers"
+ cmd = ListRouter(MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_update_router_exception(self):
"""Update router: myid."""
resource = 'router'
diff --git a/quantumclient/tests/unit/test_cli20_securitygroup.py b/quantumclient/tests/unit/test_cli20_securitygroup.py
index 7a2e941..b5891f4 100644
--- a/quantumclient/tests/unit/test_cli20_securitygroup.py
+++ b/quantumclient/tests/unit/test_cli20_securitygroup.py
@@ -73,6 +73,26 @@ class CLITestV20SecurityGroups(test_cli20.CLITestV20Base):
test_cli20.MyApp(sys.stdout), None)
self._test_list_resources(resources, cmd, True)
+ def test_list_security_groups_pagination(self):
+ resources = "security_groups"
+ cmd = securitygroup.ListSecurityGroup(
+ test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_security_groups_sort(self):
+ resources = "security_groups"
+ cmd = securitygroup.ListSecurityGroup(
+ test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_security_groups_limit(self):
+ resources = "security_groups"
+ cmd = securitygroup.ListSecurityGroup(
+ test_cli20.MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_show_security_group_id(self):
resource = 'security_group'
cmd = securitygroup.ShowSecurityGroup(
@@ -145,6 +165,38 @@ class CLITestV20SecurityGroups(test_cli20.CLITestV20Base):
mox.IgnoreArg())
self._test_list_resources(resources, cmd, True)
+ def test_list_security_group_rules_pagination(self):
+ resources = "security_group_rules"
+ cmd = securitygroup.ListSecurityGroupRule(
+ test_cli20.MyApp(sys.stdout), None)
+ self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule,
+ "extend_list")
+ securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list),
+ mox.IgnoreArg())
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_security_group_rules_sort(self):
+ resources = "security_group_rules"
+ cmd = securitygroup.ListSecurityGroupRule(
+ test_cli20.MyApp(sys.stdout), None)
+ self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule,
+ "extend_list")
+ securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list),
+ mox.IgnoreArg())
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_security_group_rules_limit(self):
+ resources = "security_group_rules"
+ cmd = securitygroup.ListSecurityGroupRule(
+ test_cli20.MyApp(sys.stdout), None)
+ self.mox.StubOutWithMock(securitygroup.ListSecurityGroupRule,
+ "extend_list")
+ securitygroup.ListSecurityGroupRule.extend_list(mox.IsA(list),
+ mox.IgnoreArg())
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_show_security_group_rule(self):
resource = 'security_group_rule'
cmd = securitygroup.ShowSecurityGroupRule(
@@ -196,11 +248,18 @@ class CLITestV20SecurityGroups(test_cli20.CLITestV20Base):
setup_list_stub('security_group_rules', list_data, query)
if conv:
cmd.get_client().AndReturn(self.client)
+ sec_ids = set()
+ for n in data['data']:
+ sec_ids.add(n[1])
+ sec_ids.add(n[2])
+ filters = ''
+ for id in sec_ids:
+ filters = filters + "&id=%s" % id
setup_list_stub('security_groups',
[{'id': 'myid1', 'name': 'group1'},
{'id': 'myid2', 'name': 'group2'},
{'id': 'myid3', 'name': 'group3'}],
- query='fields=id&fields=name')
+ query='fields=id&fields=name' + filters)
self.mox.ReplayAll()
cmd_parser = cmd.get_parser('list_security_group_rules')
diff --git a/quantumclient/tests/unit/test_cli20_subnet.py b/quantumclient/tests/unit/test_cli20_subnet.py
index 9b9650e..ca27945 100644
--- a/quantumclient/tests/unit/test_cli20_subnet.py
+++ b/quantumclient/tests/unit/test_cli20_subnet.py
@@ -331,6 +331,27 @@ class CLITestV20Subnet(CLITestV20Base):
self._test_list_resources(resources, cmd,
fields_1=['a', 'b'], fields_2=['c', 'd'])
+ def test_list_subnets_pagination(self):
+ resources = "subnets"
+ cmd = ListSubnet(MyApp(sys.stdout), None)
+ self._test_list_resources_with_pagination(resources, cmd)
+
+ def test_list_subnets_sort(self):
+ """List subnets: --sort-key name --sort-key id --sort-key asc
+ --sort-key desc
+ """
+ resources = "subnets"
+ cmd = ListSubnet(MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd,
+ sort_key=["name", "id"],
+ sort_dir=["asc", "desc"])
+
+ def test_list_subnets_limit(self):
+ """List subnets: -P."""
+ resources = "subnets"
+ cmd = ListSubnet(MyApp(sys.stdout), None)
+ self._test_list_resources(resources, cmd, page_size=1000)
+
def test_update_subnet(self):
"""Update subnet: myid --name myname --tags a b."""
resource = 'subnet'
diff --git a/quantumclient/v2_0/client.py b/quantumclient/v2_0/client.py
index aa40a76..3010354 100644
--- a/quantumclient/v2_0/client.py
+++ b/quantumclient/v2_0/client.py
@@ -19,6 +19,7 @@ import httplib
import logging
import time
import urllib
+import urlparse
from quantumclient.client import HTTPClient
from quantumclient.common import _
@@ -245,12 +246,13 @@ class Client(object):
return self.get(self.ext_path % ext_alias, params=_params)
@APIParamsCall
- def list_ports(self, **_params):
+ def list_ports(self, retrieve_all=True, **_params):
"""
Fetches a list of all networks for a tenant
"""
# Pass filters in "params" argument to do_request
- return self.get(self.ports_path, params=_params)
+ return self.list('ports', self.ports_path, retrieve_all,
+ **_params)
@APIParamsCall
def show_port(self, port, **_params):
@@ -281,12 +283,13 @@ class Client(object):
return self.delete(self.port_path % (port))
@APIParamsCall
- def list_networks(self, **_params):
+ def list_networks(self, retrieve_all=True, **_params):
"""
Fetches a list of all networks for a tenant
"""
# Pass filters in "params" argument to do_request
- return self.get(self.networks_path, params=_params)
+ return self.list('networks', self.networks_path, retrieve_all,
+ **_params)
@APIParamsCall
def show_network(self, network, **_params):
@@ -317,11 +320,12 @@ class Client(object):
return self.delete(self.network_path % (network))
@APIParamsCall
- def list_subnets(self, **_params):
+ def list_subnets(self, retrieve_all=True, **_params):
"""
Fetches a list of all networks for a tenant
"""
- return self.get(self.subnets_path, params=_params)
+ return self.list('subnets', self.subnets_path, retrieve_all,
+ **_params)
@APIParamsCall
def show_subnet(self, subnet, **_params):
@@ -352,12 +356,13 @@ class Client(object):
return self.delete(self.subnet_path % (subnet))
@APIParamsCall
- def list_routers(self, **_params):
+ def list_routers(self, retrieve_all=True, **_params):
"""
Fetches a list of all routers for a tenant
"""
# Pass filters in "params" argument to do_request
- return self.get(self.routers_path, params=_params)
+ return self.list('routers', self.routers_path, retrieve_all,
+ **_params)
@APIParamsCall
def show_router(self, router, **_params):
@@ -420,12 +425,13 @@ class Client(object):
body={'router': {'external_gateway_info': {}}})
@APIParamsCall
- def list_floatingips(self, **_params):
+ def list_floatingips(self, retrieve_all=True, **_params):
"""
Fetches a list of all floatingips for a tenant
"""
# Pass filters in "params" argument to do_request
- return self.get(self.floatingips_path, params=_params)
+ return self.list('floatingips', self.floatingips_path, retrieve_all,
+ **_params)
@APIParamsCall
def show_floatingip(self, floatingip, **_params):
@@ -463,11 +469,12 @@ class Client(object):
return self.post(self.security_groups_path, body=body)
@APIParamsCall
- def list_security_groups(self, **_params):
+ def list_security_groups(self, retrieve_all=True, **_params):
"""
Fetches a list of all security groups for a tenant
"""
- return self.get(self.security_groups_path, params=_params)
+ return self.list('security_groups', self.security_groups_path,
+ retrieve_all, **_params)
@APIParamsCall
def show_security_group(self, security_group, **_params):
@@ -500,11 +507,13 @@ class Client(object):
(security_group_rule))
@APIParamsCall
- def list_security_group_rules(self, **_params):
+ def list_security_group_rules(self, retrieve_all=True, **_params):
"""
Fetches a list of all security group rules for a tenant
"""
- return self.get(self.security_group_rules_path, params=_params)
+ return self.list('security_group_rules',
+ self.security_group_rules_path,
+ retrieve_all, **_params)
@APIParamsCall
def show_security_group_rule(self, security_group_rule, **_params):
@@ -515,12 +524,13 @@ class Client(object):
params=_params)
@APIParamsCall
- def list_vips(self, **_params):
+ def list_vips(self, retrieve_all=True, **_params):
"""
Fetches a list of all load balancer vips for a tenant
"""
# Pass filters in "params" argument to do_request
- return self.get(self.vips_path, params=_params)
+ return self.list('vips', self.vips_path, retrieve_all,
+ **_params)
@APIParamsCall
def show_vip(self, vip, **_params):
@@ -551,12 +561,13 @@ class Client(object):
return self.delete(self.vip_path % (vip))
@APIParamsCall
- def list_pools(self, **_params):
+ def list_pools(self, retrieve_all=True, **_params):
"""
Fetches a list of all load balancer pools for a tenant
"""
# Pass filters in "params" argument to do_request
- return self.get(self.pools_path, params=_params)
+ return self.list('pools', self.pools_path, retrieve_all,
+ **_params)
@APIParamsCall
def show_pool(self, pool, **_params):
@@ -594,12 +605,13 @@ class Client(object):
return self.get(self.pool_path_stats % (pool), params=_params)
@APIParamsCall
- def list_members(self, **_params):
+ def list_members(self, retrieve_all=True, **_params):
"""
Fetches a list of all load balancer members for a tenant
"""
# Pass filters in "params" argument to do_request
- return self.get(self.members_path, params=_params)
+ return self.list('members', self.members_path, retrieve_all,
+ **_params)
@APIParamsCall
def show_member(self, member, **_params):
@@ -630,12 +642,13 @@ class Client(object):
return self.delete(self.member_path % (member))
@APIParamsCall
- def list_health_monitors(self, **_params):
+ def list_health_monitors(self, retrieve_all=True, **_params):
"""
Fetches a list of all load balancer health monitors for a tenant
"""
# Pass filters in "params" argument to do_request
- return self.get(self.health_monitors_path, params=_params)
+ return self.list('health_monitors', self.health_monitors_path,
+ retrieve_all, **_params)
@APIParamsCall
def show_health_monitor(self, health_monitor, **_params):
@@ -976,3 +989,32 @@ class Client(object):
def put(self, action, body=None, headers=None, params=None):
return self.retry_request("PUT", action, body=body,
headers=headers, params=params)
+
+ def list(self, collection, path, retrieve_all=True, **params):
+ if retrieve_all:
+ res = []
+ for r in self._pagination(collection, path, **params):
+ res.extend(r[collection])
+ return {collection: res}
+ else:
+ return self._pagination(collection, path, **params)
+
+ def _pagination(self, collection, path, **params):
+ if params.get('page_reverse', False):
+ linkrel = 'previous'
+ else:
+ linkrel = 'next'
+ next = True
+ while next:
+ res = self.get(path, params=params)
+ yield res
+ next = False
+ try:
+ for link in res['%s_links' % collection]:
+ if link['rel'] == linkrel:
+ query_str = urlparse.urlparse(link['href']).query
+ params = urlparse.parse_qs(query_str)
+ next = True
+ break
+ except KeyError:
+ break