diff --git a/api-ref/source/containers.inc b/api-ref/source/containers.inc index 57d2c7fb4..b47e2548a 100644 --- a/api-ref/source/containers.inc +++ b/api-ref/source/containers.inc @@ -1276,6 +1276,7 @@ Response - port_id: port_id - version: version - ip_address: ip_address + - fixed_ips: fixed_ips Response Example ---------------- diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index 209098002..34e9e02bd 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -427,6 +427,13 @@ exec_url: The URL to start an exec instance. in: body type: dict +fixed_ips: + description: | + A list of fixed IP addresses with subnet IDs and other detailed + information. + in: body + required: true + type: string forced_down: description: | Whether or not this service was forced down manually by an @@ -608,7 +615,7 @@ interactive-request: type: boolean ip_address: description: | - The IP address of the port. + The IP address. in: body required: true type: string @@ -834,7 +841,7 @@ status_reason: type: string subnet_id: description: | - The UUID of subnet of a container. + The UUID of subnet. in: body required: true type: string diff --git a/api-ref/source/samples/container-network-list-resp.json b/api-ref/source/samples/container-network-list-resp.json index 47c1671dc..dabe60584 100644 --- a/api-ref/source/samples/container-network-list-resp.json +++ b/api-ref/source/samples/container-network-list-resp.json @@ -1,11 +1,13 @@ { "networks": [ { - "subnet_id": "ae8d7cce-859e-432f-8a33-d7d8834ccd14", "port_id": "5be06e49-70dc-4984-94a2-1b946bb136fb", - "version": 4, - "ip_address": "30.30.30.10", - "net_id": "7e6b5e1b-9b44-4f55-b4e3-16a1ead98161" + "net_id": "7e6b5e1b-9b44-4f55-b4e3-16a1ead98161", + "fixed_ips": { + "ip_address": "30.30.30.10", + "version": 4, + "subnet_id": "ae8d7cce-859e-432f-8a33-d7d8834ccd14" + } } ] } diff --git a/zun/api/controllers/v1/containers.py b/zun/api/controllers/v1/containers.py index 4f9582b60..91c38831a 100644 --- a/zun/api/controllers/v1/containers.py +++ b/zun/api/controllers/v1/containers.py @@ -1039,7 +1039,32 @@ class ContainersController(base.Controller): requested_networks = utils.build_requested_networks(context, [kwargs]) compute_api.network_attach(context, container, requested_networks[0]) - @base.Controller.api_version("1.13") + @base.Controller.api_version("1.13", "1.17") + @pecan.expose('json') + @exception.wrap_pecan_controller_exception + def network_list(self, container_ident): + """Retrieve a list of networks of the container. + + :param container_ident: UUID or Name of a container. + """ + container = utils.get_container(container_ident) + container_networks = self._get_container_networks_legacy(container) + return {'networks': container_networks} + + def _get_container_networks_legacy(self, container): + container_networks = [] + for net_id, net_infos in container.addresses.items(): + for net_info in net_infos: + container_networks.append({ + 'net_id': net_id, + 'subnet_id': net_info.get("subnet_id"), + 'port_id': net_info.get("port"), + 'version': net_info.get("version"), + 'ip_address': net_info.get("addr") + }) + return container_networks + + @base.Controller.api_version("1.18") # noqa @pecan.expose('json') @exception.wrap_pecan_controller_exception def network_list(self, container_ident): @@ -1054,12 +1079,19 @@ class ContainersController(base.Controller): def _get_container_networks(self, container): container_networks = [] for net_id, net_infos in container.addresses.items(): + addresses = {} for net_info in net_infos: - container_networks.append({ - 'net_id': net_id, + port_id = net_info["port"] + addresses.setdefault(port_id, []) + addresses[port_id].append({ 'subnet_id': net_info.get("subnet_id"), - 'port_id': net_info.get("port"), 'version': net_info.get("version"), 'ip_address': net_info.get("addr") }) + for port_id, fixed_ips in addresses.items(): + container_networks.append({ + 'net_id': net_id, + 'port_id': port_id, + 'fixed_ips': fixed_ips, + }) return container_networks diff --git a/zun/api/controllers/versions.py b/zun/api/controllers/versions.py index d4e8c7d02..1ae9c6aeb 100644 --- a/zun/api/controllers/versions.py +++ b/zun/api/controllers/versions.py @@ -50,10 +50,11 @@ REST_API_VERSION_HISTORY = """REST API Version History: * 1.15 - Remove add_security_group and remove_security_group * 1.16 - Modify restart_policy to capsule spec content * 1.17 - Add support for detaching ports + * 1.18 - Modify the response of network list """ BASE_VER = '1.1' -CURRENT_MAX_VER = '1.17' +CURRENT_MAX_VER = '1.18' class Version(object): diff --git a/zun/api/rest_api_version_history.rst b/zun/api/rest_api_version_history.rst index 669c1391f..0409c71d2 100644 --- a/zun/api/rest_api_version_history.rst +++ b/zun/api/rest_api_version_history.rst @@ -140,3 +140,24 @@ user documentation. Add parameter ``port`` to the network_detach API. This allow users to detach a container from a neutron port. + +1.18 +---- + + Modify the response of network_list + (GET /v1/containers/{container_ident}/network_list) API. The normal response + will be something like:: + + { + "networks": [ + { + "port_id": "5be06e49-70dc-4984-94a2-1b946bb136fb", + "net_id": "7e6b5e1b-9b44-4f55-b4e3-16a1ead98161", + "fixed_ips" [ + "ip_address": "30.30.30.10", + "version": 4, + "subnet_id": "ae8d7cce-859e-432f-8a33-d7d8834ccd14" + ] + } + ] + } diff --git a/zun/tests/unit/api/base.py b/zun/tests/unit/api/base.py index 86532ab88..6bd2cc0c3 100644 --- a/zun/tests/unit/api/base.py +++ b/zun/tests/unit/api/base.py @@ -26,7 +26,7 @@ from zun.tests.unit.db import base PATH_PREFIX = '/v1' -CURRENT_VERSION = "container 1.14" +CURRENT_VERSION = "container 1.18" class FunctionalTest(base.DbTestCase): diff --git a/zun/tests/unit/api/controllers/test_root.py b/zun/tests/unit/api/controllers/test_root.py index 9d15cc953..fbfbf9321 100644 --- a/zun/tests/unit/api/controllers/test_root.py +++ b/zun/tests/unit/api/controllers/test_root.py @@ -28,7 +28,7 @@ class TestRootController(api_base.FunctionalTest): 'default_version': {'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.17', + 'max_version': '1.18', 'min_version': '1.1', 'status': 'CURRENT'}, 'description': 'Zun is an OpenStack project which ' @@ -37,7 +37,7 @@ class TestRootController(api_base.FunctionalTest): 'versions': [{'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.17', + 'max_version': '1.18', 'min_version': '1.1', 'status': 'CURRENT'}]} diff --git a/zun/tests/unit/api/controllers/v1/test_containers.py b/zun/tests/unit/api/controllers/v1/test_containers.py index 4ccc905d7..5809d6ca7 100644 --- a/zun/tests/unit/api/controllers/v1/test_containers.py +++ b/zun/tests/unit/api/controllers/v1/test_containers.py @@ -1749,6 +1749,7 @@ class TestContainerController(api_base.FunctionalTest): def test_add_security_group_by_uuid(self, mock_get_resource, mock_find_resourceid, mock_add_security_group): + headers = {"OpenStack-API-Version": "container 1.14"} test_container = utils.get_test_container() test_container_obj = objects.Container(self.context, **test_container) mock_get_resource.return_value = test_container_obj @@ -1758,7 +1759,7 @@ class TestContainerController(api_base.FunctionalTest): url = '/v1/containers/%s/%s?name=%s' % (container_name, 'add_security_group', security_group_id_to_add) - response = self.post(url) + response = self.post(url, headers=headers) self.assertEqual(202, response.status_int) self.assertEqual('application/json', response.content_type) mock_find_resourceid.assert_called_once_with( @@ -1772,6 +1773,7 @@ class TestContainerController(api_base.FunctionalTest): def test_add_security_group_not_found(self, mock_get_resource, mock_find_resourceid, mock_add_security_group): + headers = {"OpenStack-API-Version": "container 1.14"} test_container = utils.get_test_container() test_container_obj = objects.Container(self.context, **test_container) mock_get_resource.return_value = test_container_obj @@ -1781,7 +1783,7 @@ class TestContainerController(api_base.FunctionalTest): url = '/v1/containers/%s/%s?name=%s' % (container_name, 'add_security_group', security_group_to_add) - response = self.post(url, expect_errors=True) + response = self.post(url, expect_errors=True, headers=headers) self.assertEqual(400, response.status_int) self.assertEqual('application/json', response.content_type) self.assertEqual( @@ -1794,6 +1796,7 @@ class TestContainerController(api_base.FunctionalTest): def test_add_security_group_not_unique_match(self, mock_get_resource, mock_find_resourceid, mock_add_security_group): + headers = {"OpenStack-API-Version": "container 1.14"} test_container = utils.get_test_container() test_container_obj = objects.Container(self.context, **test_container) mock_get_resource.return_value = test_container_obj @@ -1803,7 +1806,7 @@ class TestContainerController(api_base.FunctionalTest): url = '/v1/containers/%s/%s?name=%s' % (container_name, 'add_security_group', security_group_to_add) - response = self.post(url, expect_errors=True) + response = self.post(url, expect_errors=True, headers=headers) self.assertEqual(409, response.status_int) self.assertEqual('application/json', response.content_type) self.assertEqual( @@ -1935,8 +1938,23 @@ class TestContainerController(api_base.FunctionalTest): mock_container_get_by_uuid.assert_called_once_with( mock.ANY, test_container['uuid']) - self.assertEqual(test_container['addresses']['private'][0]['port'], - response.json['networks'][0]['port_id']) + self._assert_networks(test_container['addresses'], + response.json['networks']) + + def _assert_networks(self, addresses, networks): + self.assertEqual(len(addresses), len(networks)) + for network in networks: + address_list = addresses[network['net_id']] + self.assertEqual(len(address_list), len(network['fixed_ips'])) + for address in address_list: + matched = 0 + for fixed_ip in network['fixed_ips']: + if (address['addr'] == fixed_ip['ip_address'] and + address['version'] == fixed_ip['version'] and + address['subnet_id'] == fixed_ip['subnet_id'] and + address['port'] == network['port_id']): + matched += 1 + self.assertEqual(1, matched) @mock.patch('zun.compute.api.API.remove_security_group') @mock.patch('zun.network.neutron.NeutronAPI.find_resourceid_by_name_or_id') @@ -1944,6 +1962,7 @@ class TestContainerController(api_base.FunctionalTest): def test_remove_security_group_by_uuid(self, mock_get_resource, mock_find_resourceid, mock_remove_security_group): + headers = {"OpenStack-API-Version": "container 1.14"} test_container = utils.get_test_container( security_groups=['affb9021-964d-4b1b-80a8-9b9db60497e4']) test_container_obj = objects.Container(self.context, **test_container) @@ -1955,7 +1974,7 @@ class TestContainerController(api_base.FunctionalTest): url = '/v1/containers/%s/%s?name=%s' % (container_name, 'remove_security_group', security_group_id_to_remove) - response = self.post(url) + response = self.post(url, headers=headers) self.assertEqual(202, response.status_int) self.assertEqual('application/json', response.content_type) mock_find_resourceid.assert_called_once_with(