diff --git a/quantumclient/quantum/v2_0/router.py b/quantumclient/quantum/v2_0/router.py index 693fd56..e0387fb 100644 --- a/quantumclient/quantum/v2_0/router.py +++ b/quantumclient/quantum/v2_0/router.py @@ -18,6 +18,7 @@ import argparse import logging +from quantumclient.common import exceptions from quantumclient.common import utils from quantumclient.quantum import v2_0 as quantumv20 @@ -94,55 +95,77 @@ class RouterInterfaceCommand(quantumv20.QuantumCommand): """Based class to Add/Remove router interface.""" api = 'network' - log = logging.getLogger(__name__ + '.AddInterfaceRouter') resource = 'router' + def call_api(self, quantum_client, router_id, body): + raise NotImplementedError() + + def success_message(self, router_id, portinfo): + raise NotImplementedError() + def get_parser(self, prog_name): parser = super(RouterInterfaceCommand, self).get_parser(prog_name) parser.add_argument( 'router_id', metavar='router-id', help='ID of the router') parser.add_argument( - 'subnet_id', metavar='subnet-id', - help='ID of the internal subnet for the interface') + 'interface', metavar='INTERFACE', + help='The format is "SUBNET|subnet=SUBNET|port=PORT". ' + 'Either a subnet or port must be specified. ' + 'Both ID and name are accepted as SUBNET or PORT. ' + 'Note that "subnet=" can be omitted when specifying subnet.') return parser + def run(self, parsed_args): + self.log.debug('run(%s)' % parsed_args) + quantum_client = self.get_client() + quantum_client.format = parsed_args.request_format + + if '=' in parsed_args.interface: + resource, value = parsed_args.interface.split('=', 1) + if resource not in ['subnet', 'port']: + exceptions.CommandError('You must specify either subnet or ' + 'port for INTERFACE parameter.') + else: + resource = 'subnet' + value = parsed_args.interface + + _router_id = quantumv20.find_resourceid_by_name_or_id( + quantum_client, self.resource, parsed_args.router_id) + + _interface_id = quantumv20.find_resourceid_by_name_or_id( + quantum_client, resource, value) + body = {'%s_id' % resource: _interface_id} + + portinfo = self.call_api(quantum_client, _router_id, body) + print >>self.app.stdout, self.success_message(parsed_args.router_id, + portinfo) + class AddInterfaceRouter(RouterInterfaceCommand): """Add an internal network interface to a router.""" - def run(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) - quantum_client = self.get_client() - quantum_client.format = parsed_args.request_format - #TODO(danwent): handle passing in port-id - _router_id = quantumv20.find_resourceid_by_name_or_id( - quantum_client, self.resource, parsed_args.router_id) - _subnet_id = quantumv20.find_resourceid_by_name_or_id( - quantum_client, 'subnet', parsed_args.subnet_id) - quantum_client.add_interface_router(_router_id, - {'subnet_id': _subnet_id}) - #TODO(danwent): print port ID that is added - print >>self.app.stdout, ( - _('Added interface to router %s') % parsed_args.router_id) + log = logging.getLogger(__name__ + '.AddInterfaceRouter') + + def call_api(self, quantum_client, router_id, body): + return quantum_client.add_interface_router(router_id, body) + + def success_message(self, router_id, portinfo): + return (_('Added interface %(port)s to router %(router)s.') % + {'router': router_id, 'port': portinfo['port_id']}) class RemoveInterfaceRouter(RouterInterfaceCommand): """Remove an internal network interface from a router.""" - def run(self, parsed_args): - self.log.debug('run(%s)' % parsed_args) - quantum_client = self.get_client() - quantum_client.format = parsed_args.request_format - #TODO(danwent): handle passing in port-id - _router_id = quantumv20.find_resourceid_by_name_or_id( - quantum_client, self.resource, parsed_args.router_id) - _subnet_id = quantumv20.find_resourceid_by_name_or_id( - quantum_client, 'subnet', parsed_args.subnet_id) - quantum_client.remove_interface_router(_router_id, - {'subnet_id': _subnet_id}) - print >>self.app.stdout, ( - _('Removed interface from router %s') % parsed_args.router_id) + log = logging.getLogger(__name__ + '.RemoveInterfaceRouter') + + def call_api(self, quantum_client, router_id, body): + return quantum_client.remove_interface_router(router_id, body) + + def success_message(self, router_id, portinfo): + # portinfo is not used since it is None for router-interface-delete. + return _('Removed interface from router %s.') % router_id class SetGatewayRouter(quantumv20.QuantumCommand): diff --git a/tests/unit/test_cli20.py b/tests/unit/test_cli20.py index 0eec0c8..c89cdfa 100644 --- a/tests/unit/test_cli20.py +++ b/tests/unit/test_cli20.py @@ -443,7 +443,7 @@ class CLITestV20Base(testtools.TestCase): self.assertTrue(myid in _str) def _test_update_resource_action(self, resource, cmd, myid, action, args, - body): + body, retval=None): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -453,7 +453,7 @@ class CLITestV20Base(testtools.TestCase): end_url(path % path_action, format=self.format), 'PUT', body=MyComparator(body, self.client), headers=mox.ContainsKeyValue( - 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None)) + 'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), retval)) args.extend(['--request-format', self.format]) self.mox.ReplayAll() cmd_parser = cmd.get_parser("delete_" + resource) diff --git a/tests/unit/test_cli20_router.py b/tests/unit/test_cli20_router.py index 96517f5..35ac577 100644 --- a/tests/unit/test_cli20_router.py +++ b/tests/unit/test_cli20_router.py @@ -120,27 +120,56 @@ class CLITestV20RouterJSON(test_cli20.CLITestV20Base): self._test_show_resource(resource, cmd, self.test_id, args, ['id', 'name']) - def test_add_interface(self): - """Add interface to router: myid subnetid.""" + def _test_add_remove_interface(self, action, mode, cmd, args): resource = 'router' + subcmd = '%s_router_interface' % action + if mode == 'port': + body = {'port_id': 'portid'} + else: + body = {'subnet_id': 'subnetid'} + if action == 'add': + retval = {'subnet_id': 'subnetid', 'port_id': 'portid'} + else: + retval = None + self._test_update_resource_action(resource, cmd, 'myid', + subcmd, args, + body, retval) + + def test_add_interface_compat(self): + """Add interface to router: myid subnetid.""" cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'subnetid'] - self._test_update_resource_action(resource, cmd, 'myid', - 'add_router_interface', - args, - {'subnet_id': 'subnetid'} - ) + self._test_add_remove_interface('add', 'subnet', cmd, args) - def test_del_interface(self): + def test_add_interface_by_subnet(self): + """Add interface to router: myid subnet=subnetid.""" + cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'subnet=subnetid'] + self._test_add_remove_interface('add', 'subnet', cmd, args) + + def test_add_interface_by_port(self): + """Add interface to router: myid port=portid.""" + cmd = router.AddInterfaceRouter(test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'port=portid'] + self._test_add_remove_interface('add', 'port', cmd, args) + + def test_del_interface_compat(self): """Delete interface from router: myid subnetid.""" - resource = 'router' cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) args = ['myid', 'subnetid'] - self._test_update_resource_action(resource, cmd, 'myid', - 'remove_router_interface', - args, - {'subnet_id': 'subnetid'} - ) + self._test_add_remove_interface('remove', 'subnet', cmd, args) + + def test_del_interface_by_subnet(self): + """Delete interface from router: myid subnet=subnetid.""" + cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'subnet=subnetid'] + self._test_add_remove_interface('remove', 'subnet', cmd, args) + + def test_del_interface_by_port(self): + """Delete interface from router: myid port=portid.""" + cmd = router.RemoveInterfaceRouter(test_cli20.MyApp(sys.stdout), None) + args = ['myid', 'port=portid'] + self._test_add_remove_interface('remove', 'port', cmd, args) def test_set_gateway(self): """Set external gateway for router: myid externalid."""