Add binding_deactivate method to Linux bridge agent

This commit adds a binding_deactivate method to the Linux bridge agent
to receive messages from the ML2 plugin when a binding is de-activated
for a port. After receiving that message, the agent un-plugs the
corresponding tap interface from the port's network bridge.

To support this, a binding_deactivate method is also added to the agents
notifier. Finally, the activate method in the ML2 plugin is updated to
use the binding_deactivate method in the agents notifier.

Change-Id: I3f4e34766791c472a2c81842190094f697baa05c
Partial-Bug: #1580880
This commit is contained in:
Miguel Lavalle 2018-04-23 21:53:43 -05:00
parent 390b6a531f
commit f374697760
5 changed files with 83 additions and 3 deletions

24
neutron/agent/_topics.py Normal file
View File

@ -0,0 +1,24 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
TODO(mlavalle): This module should be deleted once neutron-lib containing
https://review.openstack.org/#/c/564381/ change is released.
"""
from neutron_lib.api.definitions import portbindings_extended
PORT_BINDING = portbindings_extended.RESOURCE_NAME
ACTIVATE = 'activate'
DEACTIVATE = 'deactivate'

View File

@ -33,6 +33,7 @@ from oslo_service import service
from oslo_utils import excutils
from six import moves
from neutron.agent import _topics as n_topics
from neutron.agent.linux import bridge_lib
from neutron.agent.linux import ip_lib
from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc
@ -833,7 +834,8 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
consumers = [[topics.PORT, topics.UPDATE],
[topics.NETWORK, topics.DELETE],
[topics.NETWORK, topics.UPDATE],
[topics.SECURITY_GROUP, topics.UPDATE]]
[topics.SECURITY_GROUP, topics.UPDATE],
[n_topics.PORT_BINDING, n_topics.DEACTIVATE]]
if cfg.CONF.VXLAN.l2_population:
consumers.append([topics.L2POPULATION, topics.UPDATE])
return consumers
@ -869,7 +871,8 @@ class LinuxBridgeRpcCallbacks(
# 1.1 Support Security Group 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 network_delete(self, context, **kwargs):
LOG.debug("network_delete received")
@ -899,6 +902,18 @@ class LinuxBridgeRpcCallbacks(
self.updated_devices.add(device_name)
LOG.debug("port_update RPC received for port: %s", port_id)
def binding_deactivate(self, context, **kwargs):
if kwargs.get('host') != cfg.CONF.host:
return
interface_name = self.agent.mgr.get_tap_device_name(
kwargs.get('port_id'))
bridge_name = self.agent.mgr.get_bridge_name(kwargs.get('network_id'))
LOG.debug("Removing device %(interface_name)s from bridge "
"%(bridge_name)s due to binding being de-activated",
{'interface_name': interface_name,
'bridge_name': bridge_name})
self.agent.mgr.remove_interface(bridge_name, interface_name)
def network_update(self, context, **kwargs):
network_id = kwargs['network']['id']
LOG.debug("network_update message processed for network "

View File

@ -2199,7 +2199,9 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
original_context, bind_context, need_notify=True,
try_again=True)
if not try_again:
self.notifier.port_delete(context, port_id)
self.notifier.binding_deactivate(context, port_id,
active_binding.host,
network['id'])
return self._make_port_binding_dict(cur_context._binding)
raise n_exc.PortBindingError(port_id=port_id, host=host)

View File

@ -25,6 +25,7 @@ from oslo_log import log
import oslo_messaging
from sqlalchemy.orm import exc
from neutron.agent import _topics as n_topics
from neutron.api.rpc.handlers import dvr_rpc
from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc
from neutron.common import rpc as n_rpc
@ -383,6 +384,7 @@ class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin,
1.1 - Added get_active_networks_info, create_dhcp_port,
update_dhcp_port, and removed get_dhcp_port methods.
1.4 - Added network_update
1.5 - Added binding_deactivate
"""
def __init__(self, topic):
@ -399,6 +401,8 @@ class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin,
self.topic_network_update = topics.get_topic_name(topic,
topics.NETWORK,
topics.UPDATE)
self.topic_port_binding_deactivate = topics.get_topic_name(
topic, n_topics.PORT_BINDING, n_topics.DEACTIVATE)
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
@ -425,3 +429,9 @@ class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin,
cctxt = self.client.prepare(topic=self.topic_network_update,
fanout=True, version='1.4')
cctxt.cast(context, 'network_update', network=network)
def binding_deactivate(self, context, port_id, host, network_id):
cctxt = self.client.prepare(topic=self.topic_port_binding_deactivate,
fanout=True, version='1.5')
cctxt.cast(context, 'binding_deactivate', port_id=port_id, host=host,
network_id=network_id)

View File

@ -975,6 +975,7 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
segment.network_type = 'vxlan'
segment.segmentation_id = 1
self.lb_rpc.network_map['net_id'] = segment
cfg.CONF.set_default('host', 'host')
def test_network_delete_mapped_net(self):
mock_net = mock.Mock()
@ -1022,6 +1023,34 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
self.assertEqual(0, del_fn.call_count)
self.assertEqual(1, log.call_count)
def test_binding_deactivate(self):
with mock.patch.object(self.lb_rpc.agent.mgr,
"get_bridge_name") as get_br_fn,\
mock.patch.object(self.lb_rpc.agent.mgr,
"get_tap_device_name") as get_tap_fn,\
mock.patch.object(self.lb_rpc.agent.mgr,
"remove_interface") as rem_intf:
get_br_fn.return_value = "br0"
get_tap_fn.return_value = "tap456"
self.lb_rpc.binding_deactivate(mock.ANY, host="host",
network_id="123", port_id="456")
get_br_fn.assert_called_once_with("123")
get_tap_fn.assert_called_once_with("456")
rem_intf.assert_called_once_with("br0", "tap456")
def test_binding_deactivate_not_for_host(self):
with mock.patch.object(self.lb_rpc.agent.mgr,
"get_bridge_name") as get_br_fn,\
mock.patch.object(self.lb_rpc.agent.mgr,
"get_tap_device_name") as get_tap_fn,\
mock.patch.object(self.lb_rpc.agent.mgr,
"remove_interface") as rem_intf:
self.lb_rpc.binding_deactivate(mock.ANY, host="other_host",
network_id="123", port_id="456")
get_br_fn.assert_not_called()
get_tap_fn.assert_not_called()
rem_intf.assert_not_called()
def _test_fdb_add(self, proxy_enabled=False):
fdb_entries = {'net_id':
{'ports':