From 77ecd23a6b3336ba602fb0a61c3a3568bbc4e238 Mon Sep 17 00:00:00 2001 From: ivan-zhu <bozhu@linux.vnet.ibm.com> Date: Wed, 10 Oct 2012 17:24:35 +0800 Subject: [PATCH] Convenience cmds for l3 Bug #1049551 Add two CLI and unit tests: quantum net-external-list (runs net-list with router:external=True filter) quantum router-port-list <router-id/name> (runs port-list, filtering with device_id equal to specified router) Change-Id: I9a9668836ac24d4cbc6a3867ec031611b64ded14 --- quantumclient/quantum/v2_0/network.py | 15 +++ quantumclient/quantum/v2_0/port.py | 29 ++++- quantumclient/shell.py | 4 + quantumclient/tests/unit/test_cli20_network.py | 160 ++++++++++++++++++++++--- quantumclient/tests/unit/test_cli20_port.py | 127 +++++++++++++++++--- 5 files changed, 302 insertions(+), 33 deletions(-) diff --git a/quantumclient/quantum/v2_0/network.py b/quantumclient/quantum/v2_0/network.py index 21efd2d..070de54 100644 --- a/quantumclient/quantum/v2_0/network.py +++ b/quantumclient/quantum/v2_0/network.py @@ -41,6 +41,21 @@ class ListNetwork(ListCommand): list_columns = ['id', 'name', 'subnets'] +class ListExternalNetwork(ListCommand): + """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): + 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) + + class ShowNetwork(ShowCommand): """Show information of a given network.""" diff --git a/quantumclient/quantum/v2_0/port.py b/quantumclient/quantum/v2_0/port.py index 6984f5d..7f39365 100644 --- a/quantumclient/quantum/v2_0/port.py +++ b/quantumclient/quantum/v2_0/port.py @@ -35,7 +35,7 @@ def _format_fixed_ips(port): class ListPort(ListCommand): - """List networks that belong to a given tenant.""" + """List ports that belong to a given tenant.""" resource = 'port' log = logging.getLogger(__name__ + '.ListPort') @@ -43,6 +43,33 @@ class ListPort(ListCommand): list_columns = ['id', 'name', 'mac_address', 'fixed_ips'] +class ListRouterPort(ListCommand): + """List ports that belong to a given tenant, with specified router""" + + resource = 'port' + log = logging.getLogger(__name__ + '.ListRouterPort') + _formatters = {'fixed_ips': _format_fixed_ips, } + list_columns = ['id', 'name', 'mac_address', 'fixed_ips'] + + def get_parser(self, prog_name): + parser = super(ListCommand, self).get_parser(prog_name) + quantumv20.add_show_list_common_argument(parser) + parser.add_argument( + 'id', metavar='router', + help='ID or name of router to look up') + quantumv20.add_extra_argument(parser, 'filter_specs', + 'filters options') + return parser + + def get_data(self, parsed_args): + quantum_client = self.get_client() + quantum_client.format = parsed_args.request_format + _id = quantumv20.find_resourceid_by_name_or_id( + quantum_client, 'router', parsed_args.id) + parsed_args.filter_specs.append('--device_id=%s' % _id) + return super(ListRouterPort, self).get_data(parsed_args) + + class ShowPort(ShowCommand): """Show information of a given port.""" diff --git a/quantumclient/shell.py b/quantumclient/shell.py index c4195ba..34f1a99 100644 --- a/quantumclient/shell.py +++ b/quantumclient/shell.py @@ -54,6 +54,8 @@ def env(*_vars, **kwargs): COMMAND_V2 = { 'net-list': utils.import_class( 'quantumclient.quantum.v2_0.network.ListNetwork'), + 'net-external-list': utils.import_class( + 'quantumclient.quantum.v2_0.network.ListExternalNetwork'), 'net-show': utils.import_class( 'quantumclient.quantum.v2_0.network.ShowNetwork'), 'net-create': utils.import_class( @@ -96,6 +98,8 @@ COMMAND_V2 = { 'quantumclient.quantum.v2_0.extension.ShowExt'), 'router-list': utils.import_class( 'quantumclient.quantum.v2_0.router.ListRouter'), + 'router-port-list': utils.import_class( + 'quantumclient.quantum.v2_0.port.ListRouterPort'), 'router-show': utils.import_class( 'quantumclient.quantum.v2_0.router.ShowRouter'), 'router-create': utils.import_class( diff --git a/quantumclient/tests/unit/test_cli20_network.py b/quantumclient/tests/unit/test_cli20_network.py index 729e982..5360c08 100644 --- a/quantumclient/tests/unit/test_cli20_network.py +++ b/quantumclient/tests/unit/test_cli20_network.py @@ -16,6 +16,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 import sys +from mox import ContainsKeyValue from quantumclient.common import exceptions from quantumclient.common import utils @@ -27,6 +28,7 @@ from quantumclient.quantum.v2_0.network import ListNetwork from quantumclient.quantum.v2_0.network import UpdateNetwork from quantumclient.quantum.v2_0.network import ShowNetwork from quantumclient.quantum.v2_0.network import DeleteNetwork +from quantumclient.quantum.v2_0.network import ListExternalNetwork class CLITestV20Network(CLITestV20Base): @@ -39,8 +41,8 @@ class CLITestV20Network(CLITestV20Base): args = [name, ] position_names = ['name', ] position_values = [name, ] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) def test_create_network_tenant(self): """Create net: --tenant_id tenantid myname.""" @@ -51,15 +53,15 @@ class CLITestV20Network(CLITestV20Base): args = ['--tenant_id', 'tenantid', name] position_names = ['name', ] position_values = [name, ] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') # Test dashed options args = ['--tenant-id', 'tenantid', name] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') def test_create_network_tags(self): """Create net: myname --tags a b.""" @@ -70,9 +72,9 @@ class CLITestV20Network(CLITestV20Base): args = [name, '--tags', 'a', 'b'] position_names = ['name', ] position_values = [name, ] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tags=['a', 'b']) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tags=['a', 'b']) def test_create_network_state(self): """Create net: --admin_state_down myname.""" @@ -83,15 +85,15 @@ class CLITestV20Network(CLITestV20Base): args = ['--admin_state_down', name, ] position_names = ['name', ] position_values = [name, ] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - admin_state_up=False) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + admin_state_up=False) # Test dashed options args = ['--admin-state-down', name, ] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - admin_state_up=False) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + admin_state_up=False) def test_list_nets_empty_with_column(self): resources = "networks" @@ -179,6 +181,130 @@ class CLITestV20Network(CLITestV20Base): self.assertEquals(0, len(set(network) ^ set(cmd.list_columns))) + def test_list_external_nets_empty_with_column(self): + resources = "networks" + cmd = ListExternalNetwork(MyApp(sys.stdout), None) + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + reses = {resources: []} + resstr = self.client.serialize(reses) + # url method body + query = "router%3Aexternal=True&id=myfakeid" + args = ['-c', 'id', '--', '--id', 'myfakeid'] + path = getattr(self.client, resources + "_path") + self.client.httpclient.request( + test_cli20.end_url(path, query), 'GET', + body=None, + headers=test_cli20.ContainsKeyValue( + 'X-Auth-Token', + test_cli20.TOKEN)).AndReturn( + (test_cli20.MyResp(200), resstr)) + self.mox.ReplayAll() + cmd_parser = cmd.get_parser("list_" + resources) + + parsed_args = cmd_parser.parse_args(args) + + cmd.run(parsed_args) + self.mox.VerifyAll() + self.mox.UnsetStubs() + _str = self.fake_stdout.make_string() + self.assertEquals('\n', _str) + + def _test_list_external_nets(self, resources, cmd, + detail=False, tags=[], + fields_1=[], fields_2=[]): + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + reses = {resources: [{'id': 'myid1', }, + {'id': 'myid2', }, ], } + + resstr = self.client.serialize(reses) + + # url method body + query = "" + args = detail and ['-D', ] or [] + if fields_1: + for field in fields_1: + args.append('--fields') + args.append(field) + if tags: + args.append('--') + args.append("--tag") + for tag in tags: + args.append(tag) + if (not tags) and fields_2: + args.append('--') + if fields_2: + args.append("--fields") + for field in fields_2: + args.append(field) + fields_1.extend(fields_2) + for field in fields_1: + if query: + query += "&fields=" + field + else: + query = "fields=" + field + if query: + query += '&router%3Aexternal=True' + else: + query += 'router%3Aexternal=True' + for tag in tags: + if query: + query += "&tag=" + tag + else: + query = "tag=" + tag + if detail: + query = query and query + '&verbose=True' or 'verbose=True' + 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( + (test_cli20.MyResp(200), resstr)) + self.mox.ReplayAll() + cmd_parser = cmd.get_parser("list_" + resources) + + parsed_args = cmd_parser.parse_args(args) + cmd.run(parsed_args) + + self.mox.VerifyAll() + self.mox.UnsetStubs() + _str = self.fake_stdout.make_string() + + self.assertTrue('myid1' in _str) + + def test_list_external_nets_detail(self): + """list external nets: -D.""" + resources = "networks" + cmd = ListExternalNetwork(MyApp(sys.stdout), None) + self._test_list_external_nets(resources, cmd, True) + + def test_list_external_nets_tags(self): + """List external nets: -- --tags a b.""" + resources = "networks" + cmd = ListExternalNetwork(MyApp(sys.stdout), None) + self._test_list_external_nets(resources, + cmd, tags=['a', 'b']) + + def test_list_external_nets_detail_tags(self): + """List external nets: -D -- --tags a b.""" + resources = "networks" + cmd = ListExternalNetwork(MyApp(sys.stdout), None) + self._test_list_external_nets(resources, cmd, + detail=True, tags=['a', 'b']) + + def test_list_externel_nets_fields(self): + """List external nets: --fields a --fields b -- --fields c d.""" + resources = "networks" + cmd = ListExternalNetwork(MyApp(sys.stdout), None) + self._test_list_external_nets(resources, cmd, + fields_1=['a', 'b'], + fields_2=['c', 'd']) + def test_update_network_exception(self): """Update net: myid.""" resource = 'network' diff --git a/quantumclient/tests/unit/test_cli20_port.py b/quantumclient/tests/unit/test_cli20_port.py index c57aa89..6ddbf31 100644 --- a/quantumclient/tests/unit/test_cli20_port.py +++ b/quantumclient/tests/unit/test_cli20_port.py @@ -16,14 +16,17 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 import sys +from mox import ContainsKeyValue from quantumclient.tests.unit.test_cli20 import CLITestV20Base from quantumclient.tests.unit.test_cli20 import MyApp +from quantumclient.tests.unit import test_cli20 from quantumclient.quantum.v2_0.port import CreatePort from quantumclient.quantum.v2_0.port import ListPort from quantumclient.quantum.v2_0.port import UpdatePort from quantumclient.quantum.v2_0.port import ShowPort from quantumclient.quantum.v2_0.port import DeletePort +from quantumclient.quantum.v2_0.port import ListRouterPort class CLITestV20Port(CLITestV20Base): @@ -39,8 +42,8 @@ class CLITestV20Port(CLITestV20Base): position_names = ['network_id'] position_values = [] position_values.extend([netid]) - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) def test_create_port_full(self): """Create port: --mac_address mac --device_id deviceid netid.""" @@ -52,14 +55,14 @@ class CLITestV20Port(CLITestV20Base): args = ['--mac_address', 'mac', '--device_id', 'deviceid', netid] position_names = ['network_id', 'mac_address', 'device_id'] position_values = [netid, 'mac', 'deviceid'] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) # Test dashed options args = ['--mac-address', 'mac', '--device-id', 'deviceid', netid] position_names = ['network_id', 'mac_address', 'device_id'] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values) def test_create_port_tenant(self): """Create port: --tenant_id tenantid netid.""" @@ -72,15 +75,15 @@ class CLITestV20Port(CLITestV20Base): position_names = ['network_id'] position_values = [] position_values.extend([netid]) - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') # Test dashed options args = ['--tenant-id', 'tenantid', netid, ] - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tenant_id='tenantid') + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tenant_id='tenantid') def test_create_port_tags(self): """Create port: netid mac_address device_id --tags a b.""" @@ -93,9 +96,9 @@ class CLITestV20Port(CLITestV20Base): position_names = ['network_id'] position_values = [] position_values.extend([netid]) - _str = self._test_create_resource(resource, cmd, name, myid, args, - position_names, position_values, - tags=['a', 'b']) + self._test_create_resource(resource, cmd, name, myid, args, + position_names, position_values, + tags=['a', 'b']) def test_list_ports(self): """List ports: -D.""" @@ -122,6 +125,100 @@ class CLITestV20Port(CLITestV20Base): self._test_list_resources(resources, cmd, fields_1=['a', 'b'], fields_2=['c', 'd']) + def _test_list_router_port(self, resources, cmd, + myid, detail=False, tags=[], + fields_1=[], fields_2=[]): + self.mox.StubOutWithMock(cmd, "get_client") + self.mox.StubOutWithMock(self.client.httpclient, "request") + cmd.get_client().MultipleTimes().AndReturn(self.client) + reses = {resources: [{'id': 'myid1', }, + {'id': 'myid2', }, ], } + + resstr = self.client.serialize(reses) + + # url method body + query = "" + args = detail and ['-D', ] or [] + + if fields_1: + for field in fields_1: + args.append('--fields') + args.append(field) + args.append(myid) + if tags: + args.append('--') + args.append("--tag") + for tag in tags: + args.append(tag) + if (not tags) and fields_2: + args.append('--') + if fields_2: + args.append("--fields") + for field in fields_2: + args.append(field) + fields_1.extend(fields_2) + for field in fields_1: + if query: + query += "&fields=" + field + else: + query = "fields=" + field + + for tag in tags: + if query: + query += "&tag=" + tag + else: + query = "tag=" + tag + if detail: + query = query and query + '&verbose=True' or 'verbose=True' + query = query and query + '&device_id=%s' or 'device_id=%s' + path = getattr(self.client, resources + "_path") + self.client.httpclient.request( + test_cli20.end_url(path, query % myid), 'GET', + body=None, + headers=ContainsKeyValue('X-Auth-Token', + test_cli20.TOKEN)).AndReturn( + (test_cli20.MyResp(200), resstr)) + self.mox.ReplayAll() + cmd_parser = cmd.get_parser("list_" + resources) + + parsed_args = cmd_parser.parse_args(args) + cmd.run(parsed_args) + + self.mox.VerifyAll() + self.mox.UnsetStubs() + _str = self.fake_stdout.make_string() + + self.assertTrue('myid1' in _str) + + def test_list_router_ports(self): + """List router ports: -D.""" + resources = "ports" + cmd = ListRouterPort(MyApp(sys.stdout), None) + self._test_list_router_port(resources, cmd, + self.test_id, True) + + def test_list_router_ports_tags(self): + """List router ports: -- --tags a b.""" + resources = "ports" + cmd = ListRouterPort(MyApp(sys.stdout), None) + self._test_list_router_port(resources, cmd, + self.test_id, tags=['a', 'b']) + + def test_list_router_ports_detail_tags(self): + """List router ports: -D -- --tags a b.""" + resources = "ports" + cmd = ListRouterPort(MyApp(sys.stdout), None) + self._test_list_router_port(resources, cmd, self.test_id, + detail=True, tags=['a', 'b']) + + def test_list_router_ports_fields(self): + """List ports: --fields a --fields b -- --fields c d.""" + resources = "ports" + cmd = ListRouterPort(MyApp(sys.stdout), None) + self._test_list_router_port(resources, cmd, self.test_id, + fields_1=['a', 'b'], + fields_2=['c', 'd']) + def test_update_port(self): """Update port: myid --name myname --tags a b.""" resource = 'port'