Add binding de-activation to OVS agent

To support multiple port bindings, the L2 agents have to have the
capability to handle binding deactivation notifications from the
Neutron server. This patch adds the necessary code to the OVS agent.

After receiving the notification, the agent un-plugs the corresponding
VIF from the integration bridge.

Change-Id: I78178de2039ccabc649558de4f6549a38de90418
Partial-Bug: #1580880
This commit is contained in:
Miguel Lavalle 2018-05-09 10:39:05 -05:00
parent be7ad302e1
commit 0694bebd6d
2 changed files with 55 additions and 1 deletions

View File

@ -124,7 +124,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
# 1.2 Support DVR (Distributed Virtual Router) RPC
# 1.3 Added param devices_to_update to security_groups_provider_updated
# 1.4 Added support for network_update
target = oslo_messaging.Target(version='1.4')
# 1.5 Added binding_deactivate
target = oslo_messaging.Target(version='1.5')
def __init__(self, bridge_classes, ext_manager, conf=None):
'''Constructor.
@ -174,6 +175,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
self.updated_ports = set()
# Stores port delete notifications
self.deleted_ports = set()
# Stores the port IDs whose binding has been deactivated
self.deactivated_bindings = set()
self.network_ports = collections.defaultdict(set)
# keeps association between ports and ofports to detect ofport change
@ -414,6 +417,12 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
{'network_id': network_id,
'ports': self.network_ports[network_id]})
def binding_deactivate(self, context, **kwargs):
if kwargs.get('host') != self.conf.host:
return
port_id = kwargs.get('port_id')
self.deactivated_bindings.add(port_id)
def _clean_network_ports(self, port_id):
for port_set in self.network_ports.values():
if port_id in port_set:
@ -444,6 +453,20 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
# more secure
self.sg_agent.remove_devices_filter(deleted_ports)
def process_deactivated_bindings(self, port_info):
# don't try to deactivate bindings for removed ports since they are
# already gone
if 'removed' in port_info:
self.deactivated_bindings -= port_info['removed']
while self.deactivated_bindings:
port_id = self.deactivated_bindings.pop()
port = self.int_br.get_vif_port_by_id(port_id)
if not port:
continue
self.int_br.delete_port(port.port_name)
LOG.debug(("Port id %s unplugged from integration bridge because "
"its binding was de-activated"), port_id)
def tunnel_update(self, context, **kwargs):
LOG.debug("tunnel_update received")
if not self.enable_tunneling:
@ -1786,6 +1809,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
return (polling_manager.is_polling_required or
self.updated_ports or
self.deleted_ports or
self.deactivated_bindings or
self.sg_agent.firewall_refresh_needed())
def _port_info_has_changes(self, port_info):
@ -2075,6 +2099,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
failed_devices, failed_ancillary_devices))
sync = False
self.process_deleted_ports(port_info)
self.process_deactivated_bindings(port_info)
ofport_changed_ports = self.update_stale_ofport_rules()
if ofport_changed_ports:
port_info.setdefault('updated', set()).update(

View File

@ -114,6 +114,7 @@ class TestOvsNeutronAgent(object):
group='SECURITYGROUP')
cfg.CONF.set_default('quitting_rpc_timeout', 10, 'AGENT')
cfg.CONF.set_default('local_ip', '127.0.0.1', 'OVS')
cfg.CONF.set_default('host', 'host')
mock.patch(
'neutron.agent.ovsdb.native.helpers.enable_connection_uri').start()
mock.patch(
@ -1218,6 +1219,34 @@ class TestOvsNeutronAgent(object):
self.assertFalse(int_br.set_db_attribute.called)
self.assertFalse(int_br.drop_port.called)
def test_binding_deactivate_not_for_host(self):
self.agent.binding_deactivate('unused_context', port_id='id',
host='other_host')
self.assertEqual(set(), self.agent.deactivated_bindings)
def test_binding_deactivate(self):
vif = FakeVif()
with mock.patch.object(self.agent, 'int_br') as int_br:
int_br.get_vif_port_by_id.return_value = vif
self.agent.binding_deactivate('unused_context', port_id='id',
host='host')
self.assertEqual(set(['id']), self.agent.deactivated_bindings)
self.agent.process_deactivated_bindings(port_info={})
int_br.get_vif_port_by_id.assert_called_once_with('id')
int_br.delete_port.assert_called_once_with(vif.port_name)
self.assertEqual(set(), self.agent.deactivated_bindings)
def test_binding_deactivate_removed_port(self):
with mock.patch.object(self.agent, 'int_br') as int_br:
self.agent.binding_deactivate('unused_context', port_id='id',
host='host')
self.assertEqual(set(['id']), self.agent.deactivated_bindings)
self.agent.process_deactivated_bindings(
port_info={'removed': {'id', }})
int_br.get_vif_port_by_id.assert_not_called()
int_br.delete_port.assert_not_called()
self.assertEqual(set(), self.agent.deactivated_bindings)
def _test_setup_physical_bridges(self, port_exists=False):
with mock.patch.object(ip_lib.IPDevice, "exists") as devex_fn,\
mock.patch.object(sys, "exit"),\