From 0ac3ba9336fc564f0e86b63df174074fc0d397bc 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 --- 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 264a1b63e1..2384b0cf9d 100644 --- a/neutron/plugins/ml2/driver_api.py +++ b/neutron/plugins/ml2/driver_api.py @@ -270,12 +270,14 @@ class PortContext(object): pass @abc.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 facee4e300..0c1180619f 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 b099a5f98d..87d89afcb3 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 4080ea07e5..a72c5128ab 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 64b793ae4e..6a0ca1e863 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 c9263c2003..86ff76cb03 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: