Introduce 'fixed_ips' to network_list

Modify the response of network_list API. Add a 'fixed_ips' field
that contains the list of IP addresses and their subnet IDs.
This change is primarily for aligning the format with nova and
neutron so that API consumers can use the same parsing code.
The first use case is from Heat. Heat would be able to use the
same code for parsing nova's interface_list and zun's network_list.

Change-Id: Id1e652d944be852697f18958899e395918ab0885
This commit is contained in:
Hongbin Lu 2018-06-16 22:10:47 +00:00
parent 4db4adf6e5
commit 53c7f6bb9c
9 changed files with 103 additions and 20 deletions

View File

@ -1276,6 +1276,7 @@ Response
- port_id: port_id
- version: version
- ip_address: ip_address
- fixed_ips: fixed_ips
Response Example
----------------

View File

@ -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

View File

@ -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"
}
}
]
}

View File

@ -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

View File

@ -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):

View File

@ -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"
]
}
]
}

View File

@ -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):

View File

@ -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'}]}

View File

@ -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(