Merge "Add binding activation to the Linuxbridge agent"
This commit is contained in:
@@ -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 "
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
+12
@@ -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``.
|
||||
Reference in New Issue
Block a user