From 01380b7c67ba2ef1f923d0cf4265dd2d20e31093 Mon Sep 17 00:00:00 2001 From: Robert Kukura Date: Thu, 3 Apr 2014 17:01:00 -0400 Subject: [PATCH] ML2: ODL driver sets port status The OpenDaylight mechanism driver does not depend on an L2 agent to plug the port. Now that nova waits for notification that the port status is ACTIVE, the ML2 driver API is extended so that the mechanism driver that binds a port can optionally set the port status, and the OpenDaylight mechanism driver uses this to set the port status to ACTIVE. Closes-Bug: 1301449 Change-Id: I171c405f36b4f2354d9585e8ae3dfa50ddaa9a7e (cherry picked from commit a9e3074aa9f442c2fff1ba058ac8ed585c6caa24) --- neutron/plugins/ml2/driver_api.py | 4 +++- neutron/plugins/ml2/driver_context.py | 5 ++++- neutron/plugins/ml2/drivers/mechanism_odl.py | 4 +++- neutron/plugins/ml2/plugin.py | 13 +++++++++++++ neutron/tests/unit/ml2/drivers/mechanism_test.py | 6 ++++++ neutron/tests/unit/ml2/test_port_binding.py | 16 +++++++++++++--- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/neutron/plugins/ml2/driver_api.py b/neutron/plugins/ml2/driver_api.py index d55240faa39..d6291fcee15 100644 --- a/neutron/plugins/ml2/driver_api.py +++ b/neutron/plugins/ml2/driver_api.py @@ -271,12 +271,14 @@ class PortContext(object): pass @abstractmethod - def set_binding(self, segment_id, vif_type, vif_details): + def set_binding(self, segment_id, vif_type, vif_details, + status=None): """Set the binding for the port. :param segment_id: Network segment bound for the port. :param vif_type: The VIF type for the bound port. :param vif_details: Dictionary with details for VIF driver. + :param status: Port status to set if not None. Called by MechanismDriver.bind_port to indicate success and specify binding details to use for port. The segment_id must diff --git a/neutron/plugins/ml2/driver_context.py b/neutron/plugins/ml2/driver_context.py index facee4e3004..0c1180619f8 100644 --- a/neutron/plugins/ml2/driver_context.py +++ b/neutron/plugins/ml2/driver_context.py @@ -84,6 +84,7 @@ class PortContext(MechanismDriverContext, api.PortContext): else: self._original_bound_segment_id = None self._original_bound_driver = None + self._new_port_status = None @property def current(self): @@ -125,8 +126,10 @@ class PortContext(MechanismDriverContext, api.PortContext): filters={'agent_type': [agent_type], 'host': [self._binding.host]}) - def set_binding(self, segment_id, vif_type, vif_details): + def set_binding(self, segment_id, vif_type, vif_details, + status=None): # TODO(rkukura) Verify binding allowed, segment in network self._binding.segment = segment_id self._binding.vif_type = vif_type self._binding.vif_details = jsonutils.dumps(vif_details) + self._new_port_status = status diff --git a/neutron/plugins/ml2/drivers/mechanism_odl.py b/neutron/plugins/ml2/drivers/mechanism_odl.py index b099a5f98de..87d89afcb3a 100644 --- a/neutron/plugins/ml2/drivers/mechanism_odl.py +++ b/neutron/plugins/ml2/drivers/mechanism_odl.py @@ -20,6 +20,7 @@ import time from oslo.config import cfg import requests +from neutron.common import constants as n_const from neutron.common import exceptions as n_exc from neutron.common import utils from neutron.extensions import portbindings @@ -332,7 +333,8 @@ class OpenDaylightMechanismDriver(api.MechanismDriver): if self.check_segment(segment): context.set_binding(segment[api.ID], self.vif_type, - self.vif_details) + self.vif_details, + status=n_const.PORT_STATUS_ACTIVE) LOG.debug(_("Bound using segment: %s"), segment) return else: diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 4080ea07e50..a72c5128aba 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -259,6 +259,19 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, self.mechanism_manager.bind_port(mech_context) self._update_port_dict_binding(port, binding) + # Update the port status if requested by the bound driver. + if binding.segment and mech_context._new_port_status: + # REVISIT(rkukura): This function is currently called + # inside a transaction with the port either newly + # created or locked for update. After the fix for bug + # 1276391 is merged, this will no longer be true, and + # the port status update will need to be handled in + # the transaction that commits the new binding. + port_db = db.get_port(mech_context._plugin_context.session, + port['id']) + port_db.status = mech_context._new_port_status + port['status'] = mech_context._new_port_status + return ret_value def _update_port_dict_binding(self, port, binding): diff --git a/neutron/tests/unit/ml2/drivers/mechanism_test.py b/neutron/tests/unit/ml2/drivers/mechanism_test.py index 64b793ae4ec..6a0ca1e8630 100644 --- a/neutron/tests/unit/ml2/drivers/mechanism_test.py +++ b/neutron/tests/unit/ml2/drivers/mechanism_test.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.common import constants as const from neutron.extensions import portbindings from neutron.plugins.ml2 import driver_api as api @@ -163,3 +164,8 @@ class TestMechanismDriver(api.MechanismDriver): context.set_binding(segment, portbindings.VIF_TYPE_BRIDGE, {portbindings.CAP_PORT_FILTER: True}) self.bound_ports.add(context.current['id']) + elif host == "host-ovs-filter-active": + context.set_binding(segment, portbindings.VIF_TYPE_OVS, + {portbindings.CAP_PORT_FILTER: True}, + status=const.PORT_STATUS_ACTIVE) + self.bound_ports.add(context.current['id']) diff --git a/neutron/tests/unit/ml2/test_port_binding.py b/neutron/tests/unit/ml2/test_port_binding.py index c9263c20037..86ff76cb03f 100644 --- a/neutron/tests/unit/ml2/test_port_binding.py +++ b/neutron/tests/unit/ml2/test_port_binding.py @@ -41,20 +41,25 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase): self.plugin = manager.NeutronManager.get_plugin() self.plugin.start_rpc_listener() - def _check_response(self, port, vif_type, has_port_filter, bound): + def _check_response(self, port, vif_type, has_port_filter, bound, status): self.assertEqual(port[portbindings.VIF_TYPE], vif_type) vif_details = port[portbindings.VIF_DETAILS] + port_status = port['status'] if bound: # TODO(rkukura): Replace with new VIF security details self.assertEqual(vif_details[portbindings.CAP_PORT_FILTER], has_port_filter) + self.assertEqual(port_status, status or 'DOWN') + else: + self.assertEqual(port_status, 'DOWN') - def _test_port_binding(self, host, vif_type, has_port_filter, bound): + def _test_port_binding(self, host, vif_type, has_port_filter, bound, + status=None): host_arg = {portbindings.HOST_ID: host} with self.port(name='name', arg_list=(portbindings.HOST_ID,), **host_arg) as port: self._check_response(port['port'], vif_type, has_port_filter, - bound) + bound, status) port_id = port['port']['id'] neutron_context = context.get_admin_context() details = self.plugin.callbacks.get_device_details( @@ -84,6 +89,11 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase): portbindings.VIF_TYPE_BRIDGE, True, True) + def test_binding_status_active(self): + self._test_port_binding("host-ovs-filter-active", + portbindings.VIF_TYPE_OVS, + True, True, 'ACTIVE') + def _test_update_port_binding(self, host, new_host=None): with mock.patch.object(self.plugin, '_notify_port_updated') as notify_mock: