diff --git a/doc/source/command-objects/floating-ip.rst b/doc/source/command-objects/floating-ip.rst index 6a5e38b063..57dc0ccbab 100644 --- a/doc/source/command-objects/floating-ip.rst +++ b/doc/source/command-objects/floating-ip.rst @@ -89,6 +89,10 @@ List floating IP(s) [--network ] [--port ] [--fixed-ip-address ] + [--long] + [--status ] + [--project [--project-domain ]] + [--router ] .. option:: --network @@ -108,6 +112,37 @@ List floating IP(s) *Network version 2 only* +.. option:: --long + + List additional fields in output + + *Network version 2 only* + +.. option:: --status + + List floating IP(s) according to given status ('ACTIVE', 'DOWN') + + *Network version 2 only* + +.. option:: --project + + List floating IP(s) according to given project (name or ID) + + *Network version 2 only* + +.. option:: --project-domain + + Domain the project belongs to (name or ID). This can + be used in case collisions between project names exist. + + *Network version 2 only* + +.. option:: --router + + List floating IP(s) according to given router (name or ID) + + *Network version 2 only* + floating ip show ---------------- diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 7b8374e2ae..8202b3fae2 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -219,6 +219,8 @@ class DeleteIPFloating(DeleteFloatingIP): class ListFloatingIP(common.NetworkAndComputeLister): + # TODO(songminglong): Use SDK resource mapped attribute names once + # the OSC minimum requirements include SDK 1.0 _description = _("List floating IP(s)") def update_parser_network(self, parser): @@ -240,11 +242,38 @@ class ListFloatingIP(common.NetworkAndComputeLister): help=_("List floating IP(s) according to " "given fixed IP address") ) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_("List additional fields in output") + ) + parser.add_argument( + '--status', + metavar='', + choices=['ACTIVE', 'DOWN'], + help=_("List floating IP(s) according to " + "given status ('ACTIVE', 'DOWN')") + ) + parser.add_argument( + '--project', + metavar='', + help=_("List floating IP(s) according to " + "given project (name or ID)") + ) + identity_common.add_project_domain_option_to_parser(parser) + parser.add_argument( + '--router', + metavar='', + help=_("List floating IP(s) according to " + "given router (name or ID)") + ) return parser def take_action_network(self, client, parsed_args): network_client = self.app.client_manager.network + identity_client = self.app.client_manager.identity columns = ( 'id', @@ -262,6 +291,17 @@ class ListFloatingIP(common.NetworkAndComputeLister): 'Floating Network', 'Project', ) + if parsed_args.long: + columns = columns + ( + 'router_id', + 'status', + 'description', + ) + headers = headers + ( + 'Router', + 'Status', + 'Description', + ) query = {} @@ -275,6 +315,20 @@ class ListFloatingIP(common.NetworkAndComputeLister): query['port_id'] = port.id if parsed_args.fixed_ip_address is not None: query['fixed_ip_address'] = parsed_args.fixed_ip_address + if parsed_args.status: + query['status'] = parsed_args.status + if parsed_args.project is not None: + project = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ) + query['tenant_id'] = project.id + query['project_id'] = project.id + if parsed_args.router is not None: + router = network_client.find_router(parsed_args.router, + ignore_missing=False) + query['router_id'] = router.id data = client.ips(**query) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip.py b/openstackclient/tests/unit/network/v2/test_floating_ip.py index b3d211ba34..63d22bf84c 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip.py @@ -32,7 +32,10 @@ class TestFloatingIPNetwork(network_fakes.TestNetworkV2): # Get a shortcut to the network client self.network = self.app.client_manager.network + # Get a shortcut to the ProjectManager Mock self.projects_mock = self.app.client_manager.identity.projects + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.app.client_manager.identity.domains class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): @@ -287,6 +290,9 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): fake_port = network_fakes.FakePort.create_one_port({ 'id': 'fake_port_id', }) + fake_router = network_fakes.FakeRouter.create_one_router({ + 'id': 'fake_router_id', + }) columns = ( 'ID', @@ -296,8 +302,14 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): 'Floating Network', 'Project', ) + columns_long = columns + ( + 'Router', + 'Status', + 'Description', + ) data = [] + data_long = [] for ip in floating_ips: data.append(( ip.id, @@ -307,6 +319,17 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): ip.floating_network_id, ip.tenant_id, )) + data_long.append(( + ip.id, + ip.floating_ip_address, + ip.fixed_ip_address, + ip.port_id, + ip.floating_network_id, + ip.tenant_id, + ip.router_id, + ip.status, + ip.description, + )) def setUp(self): super(TestListFloatingIPNetwork, self).setUp() @@ -314,6 +337,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): self.network.ips = mock.Mock(return_value=self.floating_ips) self.network.find_network = mock.Mock(return_value=self.fake_network) self.network.find_port = mock.Mock(return_value=self.fake_port) + self.network.find_router = mock.Mock(return_value=self.fake_router) # Get the command object to test self.cmd = floating_ip.ListFloatingIP(self.app, self.namespace) @@ -380,6 +404,94 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) + def test_floating_ip_list_long(self): + arglist = ['--long', ] + verifylist = [('long', True), ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ips.assert_called_once_with() + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_floating_ip_list_status(self): + arglist = [ + '--status', 'ACTIVE', + '--long', + ] + verifylist = [ + ('status', 'ACTIVE'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ips.assert_called_once_with(**{ + 'status': 'ACTIVE', + }) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_floating_ip_list_project(self): + project = identity_fakes_v3.FakeProject.create_one_project() + self.projects_mock.get.return_value = project + arglist = [ + '--project', project.id, + ] + verifylist = [ + ('project', project.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'tenant_id': project.id, + 'project_id': project.id, } + + self.network.ips.assert_called_once_with(**filters) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_floating_ip_list_project_domain(self): + project = identity_fakes_v3.FakeProject.create_one_project() + self.projects_mock.get.return_value = project + arglist = [ + '--project', project.id, + '--project-domain', project.domain_id, + ] + verifylist = [ + ('project', project.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'tenant_id': project.id, + 'project_id': project.id, } + + self.network.ips.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_floating_ip_list_router(self): + arglist = [ + '--router', 'fake_router_id', + '--long', + ] + verifylist = [ + ('router', 'fake_router_id'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ips.assert_called_once_with(**{ + 'router_id': 'fake_router_id', + }) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + class TestShowFloatingIPNetwork(TestFloatingIPNetwork): diff --git a/releasenotes/notes/bug-1614379-d8e2815804d53cef.yaml b/releasenotes/notes/bug-1614379-d8e2815804d53cef.yaml new file mode 100644 index 0000000000..e9ca443d7c --- /dev/null +++ b/releasenotes/notes/bug-1614379-d8e2815804d53cef.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add ``--long``, ``--status``, ``--project``, ``--project-domain``, + and ``--router`` options to ``floating ip list`` command. + [Bug `1614379 `_] \ No newline at end of file