diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 4f9085373..caf3236ab 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -36,7 +36,7 @@ def _format_admin_state(state): return 'UP' if state else 'DOWN' -def _format_external_gateway_info(info): +def _format_router_info(info): try: return json.dumps(info) except (TypeError, KeyError): @@ -54,7 +54,7 @@ def _format_routes(routes): _formatters = { 'admin_state_up': _format_admin_state, 'is_admin_state_up': _format_admin_state, - 'external_gateway_info': _format_external_gateway_info, + 'external_gateway_info': _format_router_info, 'availability_zones': utils.format_list, 'availability_zone_hints': utils.format_list, 'routes': _format_routes, @@ -69,6 +69,8 @@ def _get_columns(item): 'is_distributed': 'distributed', 'is_admin_state_up': 'admin_state_up', } + if hasattr(item, 'interfaces_info'): + column_map['interfaces_info'] = 'interfaces_info' return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) @@ -657,7 +659,22 @@ class ShowRouter(command.ShowOne): def take_action(self, parsed_args): client = self.app.client_manager.network obj = client.find_router(parsed_args.router, ignore_missing=False) + interfaces_info = [] + filters = {} + filters['device_id'] = obj.id + for port in client.ports(**filters): + if port.device_owner != "network:router_gateway": + for ip_spec in port.fixed_ips: + int_info = { + 'port_id': port.id, + 'ip_address': ip_spec.get('ip_address'), + 'subnet_id': ip_spec.get('subnet_id') + } + interfaces_info.append(int_info) + + setattr(obj, 'interfaces_info', interfaces_info) display_columns, columns = _get_columns(obj) + _formatters['interfaces_info'] = _format_router_info data = utils.get_item_properties(obj, columns, formatters=_formatters) return (display_columns, data) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 2248db9a9..f383c1ddb 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -137,7 +137,7 @@ class TestCreateRouter(TestRouter): osc_utils.format_list(new_router.availability_zones), new_router.description, new_router.distributed, - router._format_external_gateway_info(new_router.external_gateway_info), + router._format_router_info(new_router.external_gateway_info), new_router.ha, new_router.id, new_router.name, @@ -448,7 +448,7 @@ class TestListRouter(TestRouter): data_long.append( data[i] + ( router._format_routes(r.routes), - router._format_external_gateway_info(r.external_gateway_info), + router._format_router_info(r.external_gateway_info), osc_utils.format_list(r.availability_zones), osc_utils.format_list(r.tags), ) @@ -459,7 +459,7 @@ class TestListRouter(TestRouter): data_long_no_az.append( data[i] + ( router._format_routes(r.routes), - router._format_external_gateway_info(r.external_gateway_info), + router._format_router_info(r.external_gateway_info), osc_utils.format_list(r.tags), ) ) @@ -1118,6 +1118,15 @@ class TestShowRouter(TestRouter): # The router to set. _router = network_fakes.FakeRouter.create_one_router() + _port = network_fakes.FakePort.create_one_port({ + 'device_owner': 'network:router_interface', + 'device_id': _router.id + }) + setattr(_router, + 'interfaces_info', + [{'port_id': _port.id, + 'ip_address': _port.fixed_ips[0]['ip_address'], + 'subnet_id': _port.fixed_ips[0]['subnet_id']}]) columns = ( 'admin_state_up', @@ -1128,6 +1137,7 @@ class TestShowRouter(TestRouter): 'external_gateway_info', 'ha', 'id', + 'interfaces_info', 'name', 'project_id', 'routes', @@ -1140,9 +1150,10 @@ class TestShowRouter(TestRouter): osc_utils.format_list(_router.availability_zones), _router.description, _router.distributed, - router._format_external_gateway_info(_router.external_gateway_info), + router._format_router_info(_router.external_gateway_info), _router.ha, _router.id, + router._format_router_info(_router.interfaces_info), _router.name, _router.tenant_id, router._format_routes(_router.routes), @@ -1154,6 +1165,7 @@ class TestShowRouter(TestRouter): super(TestShowRouter, self).setUp() self.network.find_router = mock.Mock(return_value=self._router) + self.network.ports = mock.Mock(return_value=[self._port]) # Get the command object to test self.cmd = router.ShowRouter(self.app, self.namespace) @@ -1178,6 +1190,9 @@ class TestShowRouter(TestRouter): self.network.find_router.assert_called_once_with( self._router.name, ignore_missing=False) + self.network.ports.assert_called_with(**{ + 'device_id': self._router.id + }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/releasenotes/notes/bug-1675489-a1d226f2ee911420.yaml b/releasenotes/notes/bug-1675489-a1d226f2ee911420.yaml new file mode 100644 index 000000000..5a296d483 --- /dev/null +++ b/releasenotes/notes/bug-1675489-a1d226f2ee911420.yaml @@ -0,0 +1,6 @@ +--- +features: + - Add router interfaces info (as field ``interfaces_info``) to ``router show`` command. + The information of router interface include port's ID, IP address, + the subnet ID it belongs. + [Bug `1675489 `_]