Merge "Support searching existing port with macaddress"

This commit is contained in:
Zuul 2017-10-26 11:43:10 +00:00 committed by Gerrit Code Review
commit 2b185b92ce
3 changed files with 182 additions and 35 deletions

View File

@ -33,6 +33,7 @@ NEUTRON_ID_LH_OPTION = 'kuryr.net.uuid.lh'
NEUTRON_ID_UH_OPTION = 'kuryr.net.uuid.uh'
DOCKER_EXPOSED_PORTS_OPTION = 'com.docker.network.endpoint.exposedports'
DOCKER_MAC_ADDRESS_OPTION = 'com.docker.network.endpoint.macaddress'
KURYR_EXISTING_NEUTRON_NET = 'kuryr.net.existing'
KURYR_EXISTING_NEUTRON_SUBNETPOOL = 'kuryr.subnetpool.existing'
KURYR_EXISTING_NEUTRON_PORT = 'kuryr.port.existing'

View File

@ -487,7 +487,7 @@ def _get_cidr_from_subnetpool(**kwargs):
.format(kwargs))
def _update_existing_port(existing_port, fixed_ip):
def _update_existing_port(existing_port, fixed_ip, mac_address):
host = existing_port.get('binding:host_id')
vif_type = existing_port.get('binding:vif_type')
if not host and vif_type == 'unbound':
@ -496,6 +496,8 @@ def _update_existing_port(existing_port, fixed_ip):
'admin_state_up': True,
'binding:host_id': lib_utils.get_hostname(),
}
if mac_address:
updated_port['mac_address'] = mac_address
updated_port_resp = app.neutron.update_port(
existing_port['id'],
{'port': updated_port})
@ -771,14 +773,14 @@ def network_driver_create_network():
if options:
generic_options = options.get(const.NETWORK_GENERIC_OPTIONS)
if generic_options:
v4_subnet_id = \
generic_options.get(const.NEUTRON_SUBNET_UUID_OPTION)
v4_subnet_name = \
generic_options.get(const.NEUTRON_SUBNET_NAME_OPTION)
v6_subnet_id = \
generic_options.get(const.NEUTRON_V6_SUBNET_UUID_OPTION)
v6_subnet_name = \
generic_options.get(const.NEUTRON_V6_SUBNET_NAME_OPTION)
v4_subnet_id = generic_options.get(
const.NEUTRON_SUBNET_UUID_OPTION)
v4_subnet_name = generic_options.get(
const.NEUTRON_SUBNET_NAME_OPTION)
v6_subnet_id = generic_options.get(
const.NEUTRON_V6_SUBNET_UUID_OPTION)
v6_subnet_name = generic_options.get(
const.NEUTRON_V6_SUBNET_NAME_OPTION)
neutron_uuid = generic_options.get(const.NEUTRON_UUID_OPTION)
neutron_name = generic_options.get(const.NEUTRON_NAME_OPTION)
v4_pool_name = generic_options.get(const.NEUTRON_POOL_NAME_OPTION)
@ -1408,7 +1410,7 @@ def ipam_get_capabilities():
https://github.com/docker/libnetwork/blob/master/docs/ipam.md#getcapabilities # noqa
"""
LOG.debug("Received /IpamDriver.GetCapabilities")
capabilities = {'RequiresMACAddress': False}
capabilities = {'RequiresMACAddress': True}
return flask.jsonify(capabilities)
@ -1565,12 +1567,14 @@ def ipam_request_address():
jsonschema.validate(json_data, schemata.REQUEST_ADDRESS_SCHEMA)
pool_id = json_data['PoolID']
req_address = json_data['Address']
req_mac_address = ''
is_gateway = False
allocated_address = ''
subnet = {}
# Check if the port is gateway
options = json_data.get('Options')
if options:
req_mac_address = options.get(const.DOCKER_MAC_ADDRESS_OPTION)
request_address_type = options.get(const.REQUEST_ADDRESS_TYPE)
if request_address_type == const.NETWORK_GATEWAY_OPTIONS:
is_gateway = True
@ -1632,23 +1636,29 @@ def ipam_request_address():
'admin_state_up': True,
'network_id': neutron_network_id,
}
if req_mac_address:
port['mac_address'] = req_mac_address
fixed_ips = port['fixed_ips'] = []
fixed_ip = {'subnet_id': subnet['id']}
num_ports = 0
filtered_ports = []
if req_address:
fixed_ip['ip_address'] = req_address
fixed_ip_existing = [('subnet_id=%s' % subnet['id'])]
fixed_ip_existing.append('ip_address='
'%s' % str(req_address))
filtered_ports = app.neutron.list_ports(
fixed_ips=fixed_ip_existing)
num_ports = len(filtered_ports.get('ports', []))
fixed_ips=fixed_ip_existing).get('ports', [])
if not filtered_ports:
filtered_ports = app.neutron.list_ports(
mac_address=req_mac_address).get('ports', [])
num_ports = len(filtered_ports)
fixed_ips.append(fixed_ip)
if num_ports:
existing_port = filtered_ports['ports'][0]
created_port = _update_existing_port(existing_port,
fixed_ip)
existing_port = filtered_ports[0]
created_port = _update_existing_port(
existing_port, fixed_ip, req_mac_address)
# REVISIT(yedongcan) For tag-ext extension not
# supported, the Neutron existing port still can not
# be deleted in ipam_release_address.
@ -1664,9 +1674,11 @@ def ipam_request_address():
lib_const.DEVICE_OWNER)
LOG.debug("created port %s", created_port)
allocated_address = \
(req_address or
created_port['fixed_ips'][0]['ip_address'])
fixed_ips = created_port['fixed_ips']
fixed_ips = [ip for ip in fixed_ips
if ip['subnet_id'] == subnet['id']]
allocated_address = (req_address or
fixed_ips[0]['ip_address'])
allocated_address = '{}/{}'.format(allocated_address,
subnet_cidr.prefixlen)
except n_exceptions.NeutronClientException as ex:

View File

@ -53,7 +53,7 @@ class TestKuryrIpam(base.TestKuryrBase):
"GlobalDefaultAddressSpace":
config.CONF.global_default_address_space}),
('/IpamDriver.GetCapabilities',
{"RequiresMACAddress": False}))
{"RequiresMACAddress": True}))
@ddt.unpack
def test_remote_ipam_driver_endpoint(self, endpoint, expected):
response = self.app.post(endpoint)
@ -595,15 +595,18 @@ class TestKuryrIpam(base.TestKuryrBase):
# faking create_port
fake_neutron_port_id = uuidutils.generate_uuid()
fake_mac_address = 'fa:16:3e:ca:59:88'
fake_port = base.TestKuryrBase._get_fake_port(
docker_endpoint_id, neutron_network_id,
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
subnet_v4_id,
neutron_subnet_v4_address="10.0.0.5")
neutron_subnet_v4_address="10.0.0.5",
neutron_mac_address=fake_mac_address)
port_request = {
'name': const.KURYR_UNBOUND_PORT,
'admin_state_up': True,
'network_id': neutron_network_id,
'mac_address': fake_mac_address,
}
fixed_ips = port_request['fixed_ips'] = []
fixed_ip = {'subnet_id': subnet_v4_id}
@ -614,7 +617,7 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': '', # Querying for container address
'Options': {}
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: fake_mac_address}
}
mock_port_add_tag.return_value = None
response = self.app.post('/IpamDriver.RequestAddress',
@ -637,6 +640,7 @@ class TestKuryrIpam(base.TestKuryrBase):
def test_ipam_driver_request_address_when_subnet_not_exist(self,
mock_list_subnetpools, mock_list_subnets):
requested_address = '10.0.0.5'
fake_mac_address = 'fa:16:3e:ca:59:88'
fake_kuryr_subnetpool_id = uuidutils.generate_uuid()
fake_name = lib_utils.get_neutron_subnetpool_name(FAKE_IP4_CIDR)
kuryr_subnetpools = self._get_fake_v4_subnetpools(
@ -652,7 +656,7 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': requested_address,
'Options': {}
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: fake_mac_address}
}
response = self.app.post('/IpamDriver.RequestAddress',
content_type='application/json',
@ -692,12 +696,14 @@ class TestKuryrIpam(base.TestKuryrBase):
# faking update_port or create_port
requested_address = '10.0.0.5'
fake_mac_address = 'fa:16:3e:ca:59:88'
fake_neutron_port_id = uuidutils.generate_uuid()
fake_port = base.TestKuryrBase._get_fake_port(
docker_endpoint_id, neutron_network_id,
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
subnet_v4_id,
neutron_subnet_v4_address=requested_address)
neutron_subnet_v4_address=requested_address,
neutron_mac_address=fake_mac_address)
fixed_ip_existing = [('subnet_id=%s' % subnet_v4_id)]
fixed_ip_existing.append('ip_address=%s' % requested_address)
@ -708,6 +714,7 @@ class TestKuryrIpam(base.TestKuryrBase):
'name': const.KURYR_UNBOUND_PORT,
'admin_state_up': True,
'network_id': neutron_network_id,
'mac_address': fake_mac_address,
}
fixed_ips = port_request['fixed_ips'] = []
fixed_ip = {'subnet_id': subnet_v4_id,
@ -719,7 +726,7 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': requested_address,
'Options': {}
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: fake_mac_address}
}
mock_port_add_tag.return_value = None
response = self.app.post('/IpamDriver.RequestAddress',
@ -729,8 +736,9 @@ class TestKuryrIpam(base.TestKuryrBase):
self.assertEqual(200, response.status_code)
mock_list_subnets.assert_called_with(
subnetpool_id=fake_kuryr_subnetpool_id)
mock_list_ports.assert_called_with(
fixed_ips=fixed_ip_existing)
mock_list_ports.assert_has_calls([
mock.call(fixed_ips=fixed_ip_existing),
mock.call(mac_address=fake_mac_address)])
mock_create_port.assert_called_with({'port': port_request})
if mock_app.tag_ext:
mock_port_add_tag.assert_called()
@ -781,13 +789,15 @@ class TestKuryrIpam(base.TestKuryrBase):
# faking update_port or create_port
requested_address = '10.0.0.5'
requested_address_v6 = 'fe80::6'
requested_mac_address = 'fa:16:3e:86:a0:fe'
fake_neutron_port_id = uuidutils.generate_uuid()
fake_port = base.TestKuryrBase._get_fake_port(
docker_endpoint_id, neutron_network_id,
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
subnet_v4_id, subnet_v6_id,
neutron_subnet_v4_address=requested_address,
neutron_subnet_v6_address=requested_address_v6)
neutron_subnet_v6_address=requested_address_v6,
neutron_mac_address=requested_mac_address)
fixed_ip_existing = [('subnet_id=%s' % subnet_v4_id)]
fixed_ipv6_existing = [('subnet_id=%s' % subnet_v6_id)]
@ -808,6 +818,7 @@ class TestKuryrIpam(base.TestKuryrBase):
'name': const.NEUTRON_UNBOUND_PORT,
'admin_state_up': True,
'binding:host_id': lib_utils.get_hostname(),
'mac_address': requested_mac_address,
}
mock_update_port.return_value = fake_port
@ -815,7 +826,7 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': requested_address,
'Options': {}
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: requested_mac_address}
}
mock_port_add_tag.return_value = None
response = self.app.post('/IpamDriver.RequestAddress',
@ -829,7 +840,7 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_request_2 = {
'PoolID': fake_kuryr_subnetpool_v6_id,
'Address': requested_address_v6,
'Options': {}
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: requested_mac_address}
}
response = self.app.post('/IpamDriver.RequestAddress',
content_type='application/json',
@ -852,6 +863,122 @@ class TestKuryrIpam(base.TestKuryrBase):
else:
self.assertEqual(0, mock_port_add_tag.call_count)
@mock.patch('kuryr_libnetwork.controllers._neutron_port_add_tag')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.create_port')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.update_port')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_ports')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets')
@mock.patch('kuryr_libnetwork.controllers.app')
@ddt.data((False), (True))
def test_ipam_driver_request_specific_mac_address_existing_port(self,
use_tag_ext, mock_app, mock_list_subnets, mock_list_ports,
mock_update_port, mock_create_port, mock_port_add_tag):
mock_app.tag_ext = use_tag_ext
# faking list_subnets
neutron_network_id = uuidutils.generate_uuid()
docker_endpoint_id = lib_utils.get_hash()
subnet_v4_id = uuidutils.generate_uuid()
subnet_v6_id = uuidutils.generate_uuid()
fake_kuryr_subnetpool_id = uuidutils.generate_uuid()
fake_kuryr_subnetpool_v6_id = uuidutils.generate_uuid()
fake_v4_subnet = self._get_fake_v4_subnet(
neutron_network_id, docker_endpoint_id, subnet_v4_id,
subnetpool_id=fake_kuryr_subnetpool_id,
cidr=FAKE_IP4_CIDR)
fake_v6_subnet = self._get_fake_v6_subnet(
neutron_network_id, docker_endpoint_id, subnet_v6_id,
subnetpool_id=fake_kuryr_subnetpool_v6_id,
cidr=FAKE_IP6_CIDR)
fake_subnet_response = {
'subnets': [
fake_v4_subnet['subnet']
]
}
fake_subnet_response_v6 = {
'subnets': [
fake_v6_subnet['subnet']
]
}
mock_list_subnets.side_effect = [
fake_subnet_response, fake_subnet_response_v6]
# faking update_port or create_port
fake_address = '10.0.0.5'
fake_address_v6 = 'fe80::6'
requested_mac_address = 'fa:16:3e:86:a0:fe'
fake_neutron_port_id = uuidutils.generate_uuid()
fake_port = base.TestKuryrBase._get_fake_port(
docker_endpoint_id, neutron_network_id,
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
subnet_v4_id, subnet_v6_id,
neutron_subnet_v4_address=fake_address,
neutron_subnet_v6_address=fake_address_v6,
neutron_mac_address=requested_mac_address)
fixed_ip_existing = [('subnet_id=%s' % subnet_v4_id)]
fixed_ipv6_existing = [('subnet_id=%s' % subnet_v6_id)]
fixed_ip_existing.append('ip_address=%s' % fake_address)
fixed_ipv6_existing.append('ip_address=%s' % fake_address_v6)
fake_existing_port = dict(fake_port['port'])
fake_existing_port['binding:host_id'] = ''
fake_existing_port['binding:vif_type'] = 'unbound'
fake_ports_response = {'ports': [fake_existing_port]}
fake_existing_port_2 = dict(fake_port['port'])
fake_existing_port_2['name'] = const.NEUTRON_UNBOUND_PORT
fake_existing_port_2['binding:host_id'] = lib_utils.get_hostname()
fake_ports_response_2 = {'ports': [fake_existing_port_2]}
mock_list_ports.side_effect = [
fake_ports_response, fake_ports_response_2]
update_port = {
'name': const.NEUTRON_UNBOUND_PORT,
'admin_state_up': True,
'binding:host_id': lib_utils.get_hostname(),
'mac_address': requested_mac_address,
}
mock_update_port.return_value = fake_port
# Testing container ip allocation
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': '',
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: requested_mac_address}
}
mock_port_add_tag.return_value = None
response = self.app.post('/IpamDriver.RequestAddress',
content_type='application/json',
data=jsonutils.dumps(fake_request))
self.assertEqual(200, response.status_code)
decoded_json = jsonutils.loads(response.data)
self.assertEqual(fake_address + '/16', decoded_json['Address'])
fake_request_2 = {
'PoolID': fake_kuryr_subnetpool_v6_id,
'Address': '',
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: requested_mac_address}
}
response = self.app.post('/IpamDriver.RequestAddress',
content_type='application/json',
data=jsonutils.dumps(fake_request_2))
self.assertEqual(200, response.status_code)
decoded_json = jsonutils.loads(response.data)
self.assertEqual(fake_address_v6 + '/64', decoded_json['Address'])
mock_list_subnets.assert_has_calls([
mock.call(subnetpool_id=fake_kuryr_subnetpool_id),
mock.call(subnetpool_id=fake_kuryr_subnetpool_v6_id)])
mock_list_ports.assert_has_calls([
mock.call(mac_address=requested_mac_address),
mock.call(mac_address=requested_mac_address)])
mock_update_port.assert_called_with(fake_neutron_port_id,
{'port': update_port})
if mock_app.tag_ext:
self.assertEqual(2, mock_port_add_tag.call_count)
else:
self.assertEqual(0, mock_port_add_tag.call_count)
@mock.patch('kuryr_libnetwork.controllers._neutron_port_add_tag')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.create_port')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets')
@ -903,16 +1030,19 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_subnet_response, fake_subnet_response2]
# faking create_port
fake_neutron_port_id = uuidutils.generate_uuid()
fake_mac_address = 'fa:16:3e:86:a0:fe'
fake_port = self._get_fake_port(
docker_endpoint_id, neutron_network_id,
fake_neutron_port_id,
neutron_subnet_v4_id=neutron_subnet_v4_id,
neutron_subnet_v4_address="10.0.0.5")
neutron_subnet_v4_address="10.0.0.5",
neutron_mac_address=fake_mac_address)
mock_create_port.return_value = fake_port
port_request = {
'name': const.KURYR_UNBOUND_PORT,
'admin_state_up': True,
'network_id': neutron_network_id,
'mac_address': fake_mac_address,
}
port_request['fixed_ips'] = []
fixed_ip = {'subnet_id': neutron_subnet_v4_id}
@ -922,7 +1052,7 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': '', # Querying for container address
'Options': {}
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: fake_mac_address}
}
mock_port_add_tag.return_value = None
response = self.app.post('/IpamDriver.RequestAddress',
@ -993,10 +1123,11 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_subnet_response, fake_subnet_response2]
# Testing container ip allocation
fake_mac_address = 'fa:16:3e:86:a0:fe'
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': '', # Querying for container address
'Options': {}
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: fake_mac_address}
}
response = self.app.post('/IpamDriver.RequestAddress',
content_type='application/json',
@ -1057,16 +1188,19 @@ class TestKuryrIpam(base.TestKuryrBase):
mock_list_subnets.return_value = fake_subnet_response
# faking create_port
fake_neutron_port_id = uuidutils.generate_uuid()
fake_mac_address = 'fa:16:3e:86:a0:fe'
fake_port = self._get_fake_port(
docker_endpoint_id, neutron_network_id,
fake_neutron_port_id,
neutron_subnet_v4_id=neutron_subnet_v4_id,
neutron_subnet_v4_address="10.0.0.5")
neutron_subnet_v4_address="10.0.0.5",
neutron_mac_address=fake_mac_address)
mock_create_port.return_value = fake_port
port_request = {
'name': const.KURYR_UNBOUND_PORT,
'admin_state_up': True,
'network_id': neutron_network_id,
'mac_address': fake_mac_address,
}
port_request['fixed_ips'] = []
fixed_ip = {'subnet_id': neutron_subnet_v4_id}
@ -1076,7 +1210,7 @@ class TestKuryrIpam(base.TestKuryrBase):
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': '', # Querying for container address
'Options': {}
'Options': {const.DOCKER_MAC_ADDRESS_OPTION: fake_mac_address}
}
mock_port_add_tag.return_value = None
response = self.app.post('/IpamDriver.RequestAddress',