Don't update existing port if no change

This allows external service pre-create a neutron port with
'binding:host_id' populated before passing the port to kuryr.
This is important because setting 'binding:host_id' is slow.
This blocks the docker daemon for serving other requests,
which blocks the whole system.
We want to have this field pre-populated so that the docker
daemon won't be blocked by this API call.

This commit also allows the 'device_owner' field to be
customized if users choose to pre-populate the 'binding:host_id'
field. In addition, this commit skips the API call to update the
neutron port if the 'binding:host_id' is pre-populated
and 'admin_state_up' and 'mac_address' fields are unchanged.

Closes-Bug: #1809306
Change-Id: I09c4a51410dffaec21ab0bb3db85df8c776c92e5
This commit is contained in:
Hongbin Lu 2018-12-24 23:35:29 +00:00
parent a9a70b3178
commit 5fb8275d12
4 changed files with 51 additions and 15 deletions

View File

@ -129,26 +129,29 @@ class Driver(object):
python-neutronclient
"""
try:
updated_port = {
'device_owner': lib_const.DEVICE_OWNER,
'binding:host_id': lib_utils.get_hostname(),
'admin_state_up': True,
}
updated_port = {}
hostname = lib_utils.get_hostname()
if port['binding:host_id'] != hostname:
updated_port['binding:host_id'] = hostname
updated_port['device_owner'] = lib_const.DEVICE_OWNER
if port['admin_state_up'] is not True:
updated_port['admin_state_up'] = True
if not tags:
# rename the port if tagging is not supported
updated_port['name'] = libnet_utils.get_neutron_port_name(
endpoint_id)
if not port.get('device_id'):
updated_port['device_id'] = endpoint_id
if interface_mac:
if interface_mac and port['mac_address'] != interface_mac:
updated_port['mac_address'] = interface_mac
response_port = app.neutron.update_port(port['id'],
{'port': updated_port})
if updated_port:
port = app.neutron.update_port(port['id'],
{'port': updated_port})['port']
except n_exceptions.NeutronClientException as ex:
LOG.error("Error happened during updating a "
"Neutron port: %s", ex)
raise
return response_port['port']
return port
def __str__(self):
return self.__class__.__name__

View File

@ -172,7 +172,9 @@ class TestKuryrBase(TestCase):
neutron_trunk_id=None,
tags=None,
name=None,
binding_profile=None):
binding_profile=None,
binding_host=None,
admin_state_up=True):
# The following fake response is retrieved from the Neutron doc:
# http://developer.openstack.org/api-ref-networking-v2.html#createPort # noqa
if not name:
@ -182,7 +184,7 @@ class TestKuryrBase(TestCase):
"status": neutron_port_status,
"name": name,
"allowed_address_pairs": [],
"admin_state_up": True,
"admin_state_up": admin_state_up,
"network_id": neutron_network_id,
"tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
"device_owner": device_owner,
@ -198,6 +200,9 @@ class TestKuryrBase(TestCase):
if binding_profile is not None:
fake_port['port']['binding:profile'] = binding_profile
if binding_host is not None:
fake_port['port']['binding:host_id'] = binding_host
if neutron_subnet_v4_id:
fake_port['port']['fixed_ips'].append({
"subnet_id": neutron_subnet_v4_id,

View File

@ -100,7 +100,8 @@ class TestVethDriver(base.TestKuryrBase):
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
'192.168.1.3', 'fe80::f816:3eff:fe1c:36a9',
fake_mac_address1)['port']
fake_mac_address1,
admin_state_up=False, binding_host='')['port']
fake_port_name = '-'.join([fake_endpoint_id, lib_utils.PORT_POSTFIX])
mock_get_port_name.return_value = fake_port_name
@ -133,7 +134,8 @@ class TestVethDriver(base.TestKuryrBase):
fake_endpoint_id, fake_neutron_net_id,
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
'192.168.1.3', 'fe80::f816:3eff:fe1c:36a9')['port']
'192.168.1.3', 'fe80::f816:3eff:fe1c:36a9',
admin_state_up=False, binding_host='')['port']
fake_port_name = '-'.join([fake_endpoint_id, lib_utils.PORT_POSTFIX])
mock_get_port_name.return_value = fake_port_name
@ -167,7 +169,8 @@ class TestVethDriver(base.TestKuryrBase):
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
'192.168.1.3', 'fe80::f816:3eff:fe1c:36a9',
fake_mac_address1)['port']
fake_mac_address1,
admin_state_up=False, binding_host='')['port']
fake_neutron_port.pop('device_id')
fake_port_name = '-'.join([fake_endpoint_id, lib_utils.PORT_POSTFIX])
mock_get_port_name.return_value = fake_port_name
@ -188,3 +191,27 @@ class TestVethDriver(base.TestKuryrBase):
}
mock_update_port.assert_called_with(fake_neutron_port_id,
expected_update_port)
@mock.patch('kuryr_libnetwork.app.neutron.update_port')
@mock.patch.object(libnet_utils, 'get_neutron_port_name')
def test_update_port_with_no_changes(self, mock_get_port_name,
mock_update_port):
fake_endpoint_id = lib_utils.get_hash()
fake_neutron_port_id = uuidutils.generate_uuid()
fake_neutron_net_id = uuidutils.generate_uuid()
fake_neutron_v4_subnet_id = uuidutils.generate_uuid()
fake_neutron_v6_subnet_id = uuidutils.generate_uuid()
fake_neutron_port = self._get_fake_port(
fake_endpoint_id, fake_neutron_net_id,
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
'192.168.1.3', 'fe80::f816:3eff:fe1c:36a9',
binding_host=lib_utils.get_hostname())['port']
fake_port_name = '-'.join([fake_endpoint_id, lib_utils.PORT_POSTFIX])
mock_get_port_name.return_value = fake_port_name
veth_driver = veth.VethDriver()
veth_driver.update_port(fake_neutron_port, fake_endpoint_id, '')
mock_get_port_name.assert_called_with(fake_endpoint_id)
mock_update_port.assert_not_called()

View File

@ -270,7 +270,8 @@ class TestVlanDriver(base.TestKuryrBase):
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
'192.168.1.3', 'fe80::f816:3eff:fe1c:36a9',
fake_neutron_mac_address1)['port']
fake_neutron_mac_address1,
binding_host='', admin_state_up=False)['port']
fake_vm_port = self._get_fake_port(
fake_endpoint_id, fake_neutron_net_id,
fake_vm_port_id, lib_const.PORT_STATUS_ACTIVE,