From f288e2a2f9ce9bfd175477086c225f04a1cf6cc2 Mon Sep 17 00:00:00 2001 From: Akihiro MOTOKI <motoki@da.jp.nec.com> Date: Thu, 10 Jan 2013 03:22:14 +0900 Subject: [PATCH] Display subnet cidr information in net-list Fixes bug 1074415 This commit introduces extend_list() in ListCommand class. This method can be used to update a retrieved list (e.g., add additional information or convert some field to more human-readable value). Change-Id: Icf5ab616ab4d9add16c921e1944ba37b376b2ab2 --- quantumclient/quantum/v2_0/__init__.py | 30 ++++++-- quantumclient/quantum/v2_0/network.py | 24 ++++-- quantumclient/tests/unit/test_cli20_network.py | 101 +++++++++++++++++++++---- 3 files changed, 127 insertions(+), 28 deletions(-) diff --git a/quantumclient/quantum/v2_0/__init__.py b/quantumclient/quantum/v2_0/__init__.py index 5eae661..530b07a 100644 --- a/quantumclient/quantum/v2_0/__init__.py +++ b/quantumclient/quantum/v2_0/__init__.py @@ -375,7 +375,7 @@ class DeleteCommand(QuantumCommand): class ListCommand(QuantumCommand, lister.Lister): - """List resourcs that belong to a given tenant + """List resources that belong to a given tenant """ @@ -391,11 +391,10 @@ class ListCommand(QuantumCommand, lister.Lister): add_extra_argument(parser, 'filter_specs', 'filters options') return parser - def get_data(self, parsed_args): - self.log.debug('get_data(%s)' % parsed_args) + def retrieve_list(self, parsed_args): + """Retrieve a list of resources from Quantum server""" quantum_client = self.get_client() search_opts = parse_args_to_dict(parsed_args.filter_specs) - self.log.debug('search options: %s', search_opts) quantum_client.format = parsed_args.request_format fields = parsed_args.fields @@ -411,12 +410,21 @@ class ListCommand(QuantumCommand, lister.Lister): search_opts.update({'verbose': 'True'}) obj_lister = getattr(quantum_client, "list_%ss" % self.resource) - data = obj_lister(**search_opts) - info = [] + collection = self.resource + "s" - if collection in data: - info = data[collection] + return data.get(collection, []) + + def extend_list(self, data, parsed_args): + """Update a retrieved list. + + This method provides a way to modify a original list returned from + the quantum server. For example, you can add subnet cidr information + to a list network. + """ + pass + + def setup_columns(self, info, parsed_args): _columns = len(info) > 0 and sorted(info[0].keys()) or [] if not _columns: # clean the parsed_args.columns so that cliff will not break @@ -432,6 +440,12 @@ class ListCommand(QuantumCommand, lister.Lister): s, _columns, formatters=self._formatters, ) for s in info), ) + def get_data(self, parsed_args): + self.log.debug('get_data(%s)' % parsed_args) + data = self.retrieve_list(parsed_args) + self.extend_list(data, parsed_args) + return self.setup_columns(data, parsed_args) + class ShowCommand(QuantumCommand, show.ShowOne): """Show information of a given resource diff --git a/quantumclient/quantum/v2_0/network.py b/quantumclient/quantum/v2_0/network.py index 4d88ab3..b714ae5 100644 --- a/quantumclient/quantum/v2_0/network.py +++ b/quantumclient/quantum/v2_0/network.py @@ -18,6 +18,7 @@ import argparse import logging +from quantumclient.common import utils from quantumclient.quantum.v2_0 import CreateCommand from quantumclient.quantum.v2_0 import DeleteCommand from quantumclient.quantum.v2_0 import ListCommand @@ -27,7 +28,8 @@ from quantumclient.quantum.v2_0 import UpdateCommand def _format_subnets(network): try: - return '\n'.join(network['subnets']) + return '\n'.join([' '.join([s['id'], s.get('cidr', '')]) + for s in network['subnets']]) except Exception: return '' @@ -40,20 +42,28 @@ class ListNetwork(ListCommand): _formatters = {'subnets': _format_subnets, } list_columns = ['id', 'name', 'subnets'] + def extend_list(self, data, parsed_args): + """Add subnet information to a network list""" + quantum_client = self.get_client() + search_opts = {'fields': ['id', 'cidr']} + subnets = quantum_client.list_subnets(**search_opts).get('subnets', []) + subnet_dict = dict([(s['id'], s) for s in subnets]) + for n in data: + if 'subnets' in n: + n['subnets'] = [(subnet_dict.get(s) or {"id": s}) + for s in n['subnets']] -class ListExternalNetwork(ListCommand): + +class ListExternalNetwork(ListNetwork): """List external networks that belong to a given tenant""" - resource = 'network' log = logging.getLogger(__name__ + '.ListExternalNetwork') - _formatters = {'subnets': _format_subnets, } - list_colums = ['id', 'name', 'subnets'] - def get_data(self, parsed_args): + def retrieve_list(self, parsed_args): if '--' not in parsed_args.filter_specs: parsed_args.filter_specs.append('--') parsed_args.filter_specs.append('--router:external=True') - return super(ListExternalNetwork, self).get_data(parsed_args) + return super(ListExternalNetwork, self).retrieve_list(parsed_args) class ShowNetwork(ShowCommand): diff --git a/quantumclient/tests/unit/test_cli20_network.py b/quantumclient/tests/unit/test_cli20_network.py index 1ef0215..8eebcfe 100644 --- a/quantumclient/tests/unit/test_cli20_network.py +++ b/quantumclient/tests/unit/test_cli20_network.py @@ -17,7 +17,8 @@ import sys -from mox import ContainsKeyValue +import mox +from mox import (ContainsKeyValue, IgnoreArg, IsA) from quantumclient.common import exceptions from quantumclient.common import utils @@ -101,6 +102,8 @@ class CLITestV20Network(CLITestV20Base): cmd = ListNetwork(MyApp(sys.stdout), None) self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") + self.mox.StubOutWithMock(ListNetwork, "extend_list") + ListNetwork.extend_list(IsA(list), IgnoreArg()) cmd.get_client().MultipleTimes().AndReturn(self.client) reses = {resources: []} resstr = self.client.serialize(reses) @@ -125,40 +128,109 @@ class CLITestV20Network(CLITestV20Base): _str = self.fake_stdout.make_string() self.assertEquals('\n', _str) + def _test_list_networks(self, cmd, detail=False, tags=[], + fields_1=[], fields_2=[]): + 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) + def test_list_nets_detail(self): """list nets: -D.""" - resources = "networks" cmd = ListNetwork(MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, True) + self._test_list_networks(cmd, True) def test_list_nets_tags(self): """List nets: -- --tags a b.""" - resources = "networks" cmd = ListNetwork(MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, tags=['a', 'b']) + self._test_list_networks(cmd, tags=['a', 'b']) def test_list_nets_detail_tags(self): """List nets: -D -- --tags a b.""" resources = "networks" cmd = ListNetwork(MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, detail=True, tags=['a', 'b']) + self._test_list_networks(cmd, detail=True, tags=['a', 'b']) + + def _test_list_nets_extend_subnets(self, data, expected): + def setup_list_stub(resources, data, query): + reses = {resources: data} + resstr = self.client.serialize(reses) + resp = (test_cli20.MyResp(200), resstr) + path = getattr(self.client, resources + '_path') + self.client.httpclient.request( + test_cli20.end_url(path, query), 'GET', + body=None, + headers=ContainsKeyValue( + 'X-Auth-Token', test_cli20.TOKEN)).AndReturn(resp) + + resources = "networks" + cmd = ListNetwork(test_cli20.MyApp(sys.stdout), None) + self.mox.StubOutWithMock(cmd, 'get_client') + self.mox.StubOutWithMock(self.client.httpclient, 'request') + cmd.get_client().AndReturn(self.client) + setup_list_stub('networks', data, '') + cmd.get_client().AndReturn(self.client) + 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') + self.mox.ReplayAll() + + args = [] + cmd_parser = cmd.get_parser('list_networks') + parsed_args = cmd_parser.parse_args(args) + result = cmd.get_data(parsed_args) + self.mox.VerifyAll() + self.mox.UnsetStubs() + _result = [x for x in result[1]] + self.assertEqual(len(_result), len(expected)) + for res, exp in zip(_result, expected): + self.assertEqual(len(res), len(exp)) + for a, b in zip(res, exp): + self.assertEqual(a, b) + + def test_list_nets_extend_subnets(self): + data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']}, + {'id': 'netid2', 'name': 'net2', 'subnets': ['mysubid2', + 'mysubid3']}] + # id, name, subnets + expected = [('netid1', 'net1', 'mysubid1 192.168.1.0/24'), + ('netid2', 'net2', + 'mysubid2 172.16.0.0/24\nmysubid3 10.1.1.0/24')] + self._test_list_nets_extend_subnets(data, expected) + + def test_list_nets_extend_subnets_no_subnet(self): + data = [{'id': 'netid1', 'name': 'net1', 'subnets': ['mysubid1']}, + {'id': 'netid2', 'name': 'net2', 'subnets': ['mysubid4']}] + # id, name, subnets + expected = [('netid1', 'net1', 'mysubid1 192.168.1.0/24'), + ('netid2', 'net2', 'mysubid4 ')] + self._test_list_nets_extend_subnets(data, expected) def test_list_nets_fields(self): """List nets: --fields a --fields b -- --fields c d.""" resources = "networks" cmd = ListNetwork(MyApp(sys.stdout), None) - self._test_list_resources(resources, cmd, - fields_1=['a', 'b'], fields_2=['c', 'd']) + self._test_list_networks(cmd, + fields_1=['a', 'b'], fields_2=['c', 'd']) - def test_list_nets_defined_column(self): + def _test_list_nets_columns(self, cmd, returned_body, + args=['-f', 'json']): resources = 'networks' + self.mox.StubOutWithMock(ListNetwork, "extend_list") + ListNetwork.extend_list(IsA(list), IgnoreArg()) + self._test_list_columns(cmd, resources, returned_body, args=args) + + def test_list_nets_defined_column(self): cmd = ListNetwork(MyApp(sys.stdout), None) returned_body = {"networks": [{"name": "buildname3", "id": "id3", "tenant_id": "tenant_3", "subnets": []}]} - self._test_list_columns(cmd, resources, returned_body, - args=['-f', 'json', '-c', 'id']) + self._test_list_nets_columns(cmd, returned_body, + args=['-f', 'json', '-c', 'id']) _str = self.fake_stdout.make_string() returned_networks = utils.loads(_str) self.assertEquals(1, len(returned_networks)) @@ -167,13 +239,12 @@ class CLITestV20Network(CLITestV20Base): self.assertEquals("id", network.keys()[0]) def test_list_nets_with_default_column(self): - resources = 'networks' cmd = ListNetwork(MyApp(sys.stdout), None) returned_body = {"networks": [{"name": "buildname3", "id": "id3", "tenant_id": "tenant_3", "subnets": []}]} - self._test_list_columns(cmd, resources, returned_body) + self._test_list_nets_columns(cmd, returned_body) _str = self.fake_stdout.make_string() returned_networks = utils.loads(_str) self.assertEquals(1, len(returned_networks)) @@ -187,6 +258,8 @@ class CLITestV20Network(CLITestV20Base): cmd = ListExternalNetwork(MyApp(sys.stdout), None) self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") + self.mox.StubOutWithMock(ListNetwork, "extend_list") + ListNetwork.extend_list(IsA(list), IgnoreArg()) cmd.get_client().MultipleTimes().AndReturn(self.client) reses = {resources: []} resstr = self.client.serialize(reses) @@ -217,6 +290,8 @@ class CLITestV20Network(CLITestV20Base): fields_1=[], fields_2=[]): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") + self.mox.StubOutWithMock(ListNetwork, "extend_list") + ListNetwork.extend_list(IsA(list), IgnoreArg()) cmd.get_client().MultipleTimes().AndReturn(self.client) reses = {resources: [{'id': 'myid1', }, {'id': 'myid2', }, ], }