Merge "Add binding activation to the Linuxbridge agent"

This commit is contained in:
Zuul
2018-07-24 06:06:47 +00:00
committed by Gerrit Code Review
8 changed files with 89 additions and 3 deletions
@@ -39,6 +39,7 @@ from neutron.agent import securitygroups_rpc as agent_sg_rpc
from neutron.api.rpc.callbacks import resources
from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc
from neutron.common import config as common_config
from neutron.common import constants as c_const
from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb
from neutron.plugins.ml2.drivers.agent import capabilities
from neutron.plugins.ml2.drivers.agent import config as cagt_config # noqa
@@ -311,6 +312,8 @@ class CommonAgentLoop(service.Service):
events.AFTER_UPDATE, self,
context=self.context,
device_details=device_details)
elif c_const.NO_ACTIVE_BINDING in device_details:
LOG.info("Device %s has no active binding in host", device)
else:
LOG.info("Device %s not defined on plugin", device)
@@ -835,7 +835,8 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
[topics.NETWORK, topics.DELETE],
[topics.NETWORK, topics.UPDATE],
[topics.SECURITY_GROUP, topics.UPDATE],
[n_topics.PORT_BINDING, n_topics.DEACTIVATE]]
[n_topics.PORT_BINDING, n_topics.DEACTIVATE],
[n_topics.PORT_BINDING, n_topics.ACTIVATE]]
if cfg.CONF.VXLAN.l2_population:
consumers.append([topics.L2POPULATION, topics.UPDATE])
return consumers
@@ -871,7 +872,7 @@ 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
# 1.5 Added binding_deactivate
# 1.5 Added binding_activate and binding_deactivate
target = oslo_messaging.Target(version='1.5')
def network_delete(self, context, **kwargs):
@@ -914,6 +915,17 @@ class LinuxBridgeRpcCallbacks(
'bridge_name': bridge_name})
self.agent.mgr.remove_interface(bridge_name, interface_name)
def binding_activate(self, context, **kwargs):
if kwargs.get('host') != cfg.CONF.host:
return
# Since the common agent loop treats added and updated the same way,
# just add activated ports to the updated devices list. This way,
# adding binding activation is less disruptive to the existing code
port_id = kwargs.get('port_id')
device_name = self.agent.mgr.get_tap_device_name(port_id)
self.updated_devices.add(device_name)
LOG.debug("Binding activation received for port: %s", port_id)
def network_update(self, context, **kwargs):
network_id = kwargs['network']['id']
LOG.debug("network_update message processed for network "
+2
View File
@@ -2203,6 +2203,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
self.notifier.binding_deactivate(context, port_id,
active_binding.host,
network['id'])
self.notifier.binding_activate(context, port_id,
inactive_binding.host)
return self._make_port_binding_dict(cur_context._binding)
raise n_exc.PortBindingError(port_id=port_id, host=host)
+18 -1
View File
@@ -28,6 +28,7 @@ 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 constants as c_const
from neutron.common import rpc as n_rpc
from neutron.db import l3_hamode_db
from neutron.db import provisioning_blocks
@@ -124,6 +125,15 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
'vif_type': port_context.vif_type})
return {'device': device}
if (port['device_owner'].startswith(
n_const.DEVICE_OWNER_COMPUTE_PREFIX) and
port[portbindings.HOST_ID] != host):
LOG.debug("Device %(device)s has no active binding in host "
"%(host)s", {'device': device,
'host': host})
return {'device': device,
c_const.NO_ACTIVE_BINDING: True}
network_qos_policy_id = port_context.network._network.get(
qos_consts.QOS_POLICY_ID)
entry = {'device': device,
@@ -384,7 +394,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
1.5 - Added binding_activate and binding_deactivate
"""
def __init__(self, topic):
@@ -403,6 +413,8 @@ class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin,
topics.UPDATE)
self.topic_port_binding_deactivate = topics.get_topic_name(
topic, n_topics.PORT_BINDING, n_topics.DEACTIVATE)
self.topic_port_binding_activate = topics.get_topic_name(
topic, n_topics.PORT_BINDING, n_topics.ACTIVATE)
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
@@ -435,3 +447,8 @@ class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin,
fanout=True, version='1.5')
cctxt.cast(context, 'binding_deactivate', port_id=port_id, host=host,
network_id=network_id)
def binding_activate(self, context, port_id, host):
cctxt = self.client.prepare(topic=self.topic_port_binding_activate,
fanout=True, version='1.5')
cctxt.cast(context, 'binding_activate', port_id=port_id, host=host)
@@ -24,6 +24,7 @@ from oslo_config import cfg
import testtools
from neutron.agent.linux import bridge_lib
from neutron.common import constants as n_const
from neutron.plugins.ml2.drivers.agent import _agent_manager_base as amb
from neutron.plugins.ml2.drivers.agent import _common_agent as ca
from neutron.tests import base
@@ -520,6 +521,13 @@ class TestCommonAgentLoop(base.BaseTestCase):
# device exists so it should raise
self.agent._process_device_if_exists(mock_details)
def test__process_device_if_exists_no_active_binding_in_host(self):
mock_details = {'device': 'dev123',
n_const.NO_ACTIVE_BINDING: True}
self.agent.mgr = mock.Mock()
self.agent._process_device_if_exists(mock_details)
self.agent.mgr.setup_arp_spoofing_protection.assert_not_called()
def test_set_rpc_timeout(self):
self.agent.stop()
for rpc_client in (self.agent.plugin_rpc.client,
@@ -1051,6 +1051,18 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase):
get_tap_fn.assert_not_called()
rem_intf.assert_not_called()
def test_binding_activate(self):
with mock.patch.object(self.lb_rpc.agent.mgr,
"get_tap_device_name") as get_tap_fun:
get_tap_fun.return_value = "tap456"
self.lb_rpc.binding_activate(mock.ANY, host="host", port_id="456")
self.assertIn("tap456", self.lb_rpc.updated_devices)
def test_binding_activate_not_for_host(self):
self.lb_rpc.binding_activate(mock.ANY, host="other-host",
port_id="456")
self.assertFalse(self.lb_rpc.updated_devices)
def _test_fdb_add(self, proxy_enabled=False):
fdb_entries = {'net_id':
{'ports':
@@ -21,6 +21,7 @@ import collections
import mock
from neutron_lib.agent import topics
from neutron_lib.api.definitions import portbindings
from neutron_lib.callbacks import resources
from neutron_lib import constants
from neutron_lib.plugins import constants as plugin_constants
@@ -31,6 +32,7 @@ from oslo_context import context as oslo_context
from sqlalchemy.orm import exc
from neutron.agent import rpc as agent_rpc
from neutron.common import constants as n_const
from neutron.db import provisioning_blocks
from neutron.plugins.ml2 import db as ml2_db
from neutron.plugins.ml2.drivers import type_tunnel
@@ -154,6 +156,14 @@ class RpcCallbacksTestCase(base.BaseTestCase):
res = self.callbacks.get_device_details(mock.Mock(), host='fake')
self.assertEqual('test-policy-id', res['network_qos_policy_id'])
def test_get_device_details_port_no_active_in_host(self):
port = collections.defaultdict(lambda: 'fake_port')
self.plugin.get_bound_port_context().current = port
port['device_owner'] = constants.DEVICE_OWNER_COMPUTE_PREFIX
port[portbindings.HOST_ID] = 'other-host'
res = self.callbacks.get_device_details(mock.Mock(), host='host')
self.assertIn(n_const.NO_ACTIVE_BINDING, res)
def test_get_device_details_qos_policy_id_from_port(self):
port = collections.defaultdict(
lambda: 'fake_port',
@@ -0,0 +1,22 @@
---
prelude: >
Support multiple bindings for compute owned ports.
features:
- |
In order to better support instance migration, multiple port
bindings can be associated to compute owned ports.
* Create, update, list, show and activate operations are supported
for port bindings by the ReST API.
* A compute owned port can have one active binding and many
inactive bindings.
* There can be only one binding (active or inactive) per compute
host.
* When the ``activate`` operation is executed, a previously
inactive binding is made active. The previously active binding
becomes inactive.
* As a consequence of the multiple port bindings implementation,
the ``port_binding`` relationship in the SQLAlchemy ``Port``
object has been renamed ``port_bindings``. Similarly, the
``binding`` attribute of the ``Port`` OVO has been renamed
``bindings``.