Add tag to port more earlier

During some ml2 ovs agent port processing performance test, we noticed
that some ports are missing tag before it really done processing. While
ovs treats those ports without tag as trunk port, so some packets will
be flooded to it. In large scale cloud, if too many port added to the
bridge, the ovs-vswitchd will consume a huge amount of CPU cores if
ports are not bound in a short time.

So, in the port_bound function of ovs-agent, we set the port tag to
it after a local_vlan id is allocated. Because after that, setup
security groups (setup_port_filters) and bind devices in DB
(update_device_list) are really time-consuming.

And also fix a potential bug, port is processed as created first,
but no tag in ovsdb, so openflow security group will not be processed
successfully [1]. It must be done in a update event during next loop,
after port bound and ovsdb set the required value.

This patch can also fix some upstream test failures of waiting too
long time to ping some cases.

[1] https://github.com/openstack/neutron/blob/master/neutron/agent/linux/openvswitch_firewall/firewall.py#L112

Closes-Bug: #1952567
Change-Id: I3533f0d416d32f8d0888ad58f975960d89a985d9
This commit is contained in:
LIU Yulong 2021-11-29 12:27:23 +08:00
parent b8dcb0b7af
commit c63ebef2d5
3 changed files with 30 additions and 11 deletions

View File

@ -1117,6 +1117,16 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
self.available_local_vlans.add(lvm.vlan)
def _set_port_vlan(self, port, vlan):
ovsdb = self.int_br.ovsdb
with self.int_br.ovsdb.transaction() as txn:
# When adding the port's tag,
# also clear port's vlan_mode and trunks,
# which were set to make sure all packets are dropped.
txn.add(ovsdb.db_set('Port', port.port_name, ('tag', vlan)))
txn.add(ovsdb.db_clear('Port', port.port_name, 'vlan_mode'))
txn.add(ovsdb.db_clear('Port', port.port_name, 'trunks'))
def port_bound(self, port, net_uuid,
network_type, physical_network,
segmentation_id, fixed_ips, device_owner,
@ -1157,10 +1167,12 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
vlan_mapping = {'net_uuid': net_uuid,
'network_type': network_type,
'physical_network': str(physical_network)}
'physical_network': str(physical_network),
'tag': str(lvm.vlan)}
if segmentation_id is not None:
vlan_mapping['segmentation_id'] = str(segmentation_id)
port_other_config.update(vlan_mapping)
self._set_port_vlan(port, lvm.vlan)
self.int_br.set_db_attribute("Port", port.port_name, "other_config",
port_other_config)
return True

View File

@ -179,18 +179,21 @@ class TestOvsNeutronAgent(object):
self.agent.vlan_manager.add(
net_uuid, old_local_vlan, None, None, None)
with mock.patch.object(self.agent, 'int_br', autospec=True) as int_br:
int_br.db_get_val.return_value = db_get_val
int_br.set_db_attribute.return_value = True
needs_binding = self.agent.port_bound(
port, net_uuid, 'local', None, None,
fixed_ips, DEVICE_OWNER_COMPUTE, False)
with mock.patch.object(self.agent, '_set_port_vlan') as set_vlan:
int_br.db_get_val.return_value = db_get_val
int_br.set_db_attribute.return_value = True
needs_binding = self.agent.port_bound(
port, net_uuid, 'local', None, None,
fixed_ips, DEVICE_OWNER_COMPUTE, False)
if db_get_val is None:
int_br.assert_not_called()
self.assertFalse(needs_binding)
else:
vlan_mapping = {'net_uuid': net_uuid,
'network_type': 'local',
'physical_network': 'None'}
'physical_network': 'None',
'tag': str(new_local_vlan)}
set_vlan.assert_called_once_with(port, new_local_vlan)
int_br.set_db_attribute.assert_called_once_with(
"Port", mock.ANY, "other_config", vlan_mapping)
self.assertTrue(needs_binding)
@ -3102,6 +3105,7 @@ class TestOvsDvrNeutronAgent(object):
self.agent = self.mod_agent.OVSNeutronAgent(self._bridge_classes(),
ext_manager, cfg.CONF)
self.agent.tun_br = self.br_tun_cls(br_name='br-tun')
self.agent._set_port_vlan = mock.Mock()
self.agent.sg_agent = mock.Mock()
def _setup_for_dvr_test(self):

View File

@ -503,7 +503,8 @@ class TunnelTest(object):
vlan_mapping = {'segmentation_id': str(LS_ID),
'physical_network': 'None',
'net_uuid': NET_UUID,
'network_type': 'gre'}
'network_type': 'gre',
'tag': str(LV_ID)}
self.mock_int_bridge_expected += [
mock.call.db_get_val('Port', 'port', 'other_config'),
mock.call.set_db_attribute('Port', VIF_PORT.port_name,
@ -514,9 +515,11 @@ class TunnelTest(object):
a.vlan_manager.add(NET_UUID, *self.LVM_DATA)
a.local_dvr_map = {}
self.ovs_bridges[self.INT_BRIDGE].db_get_val.return_value = {}
a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID,
FIXED_IPS, VM_DEVICE_OWNER, False)
self._verify_mock_calls()
with mock.patch.object(a, "_set_port_vlan") as set_vlan:
a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID,
FIXED_IPS, VM_DEVICE_OWNER, False)
self._verify_mock_calls()
set_vlan.assert_called_once_with(VIF_PORT, LV_ID)
def test_port_unbound(self):
with mock.patch.object(self.mod_agent.OVSNeutronAgent,