Merge "ML2: Use same port binding logic for DVR ports as non-DVR ports"
This commit is contained in:
commit
94965f7394
@ -103,6 +103,11 @@ class PortContext(MechanismDriverContext, api.PortContext):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
|
# REVISIT(rkukura): Eliminate special DVR case as part of
|
||||||
|
# resolving bug 1367391?
|
||||||
|
if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
|
||||||
|
return self._binding.status
|
||||||
|
|
||||||
return self._port['status']
|
return self._port['status']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -165,6 +170,11 @@ class PortContext(MechanismDriverContext, api.PortContext):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def host(self):
|
def host(self):
|
||||||
|
# REVISIT(rkukura): Eliminate special DVR case as part of
|
||||||
|
# resolving bug 1367391?
|
||||||
|
if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
|
||||||
|
return self._binding.host
|
||||||
|
|
||||||
return self._port.get(portbindings.HOST_ID)
|
return self._port.get(portbindings.HOST_ID)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -203,26 +213,3 @@ class PortContext(MechanismDriverContext, api.PortContext):
|
|||||||
def release_dynamic_segment(self, segment_id):
|
def release_dynamic_segment(self, segment_id):
|
||||||
return self._plugin.type_manager.release_dynamic_segment(
|
return self._plugin.type_manager.release_dynamic_segment(
|
||||||
self._plugin_context.session, segment_id)
|
self._plugin_context.session, segment_id)
|
||||||
|
|
||||||
|
|
||||||
class DvrPortContext(PortContext):
|
|
||||||
|
|
||||||
def __init__(self, plugin, plugin_context, port, network, binding,
|
|
||||||
original_port=None):
|
|
||||||
super(DvrPortContext, self).__init__(
|
|
||||||
plugin, plugin_context, port, network, binding,
|
|
||||||
original_port=original_port)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host(self):
|
|
||||||
if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
|
|
||||||
return self._binding.host
|
|
||||||
|
|
||||||
return super(DvrPortContext, self).host
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self):
|
|
||||||
if self._port['device_owner'] == constants.DEVICE_OWNER_DVR_INTERFACE:
|
|
||||||
return self._binding.status
|
|
||||||
|
|
||||||
return super(DvrPortContext, self).status
|
|
||||||
|
@ -353,6 +353,29 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
oport = self._make_port_dict(port_db)
|
oport = self._make_port_dict(port_db)
|
||||||
port = self._make_port_dict(port_db)
|
port = self._make_port_dict(port_db)
|
||||||
network = self.get_network(plugin_context, port['network_id'])
|
network = self.get_network(plugin_context, port['network_id'])
|
||||||
|
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||||
|
# REVISIT(rkukura): The PortBinding instance from the
|
||||||
|
# ml2_port_bindings table, returned as cur_binding
|
||||||
|
# from db.get_locked_port_and_binding() above, is
|
||||||
|
# currently not used for DVR distributed ports, and is
|
||||||
|
# replaced here with the DVRPortBinding instance from
|
||||||
|
# the ml2_dvr_port_bindings table specific to the host
|
||||||
|
# on which the distributed port is being bound. It
|
||||||
|
# would be possible to optimize this code to avoid
|
||||||
|
# fetching the PortBinding instance in the DVR case,
|
||||||
|
# and even to avoid creating the unused entry in the
|
||||||
|
# ml2_port_bindings table. But the upcoming resolution
|
||||||
|
# for bug 1367391 will eliminate the
|
||||||
|
# ml2_dvr_port_bindings table, use the
|
||||||
|
# ml2_port_bindings table to store non-host-specific
|
||||||
|
# fields for both distributed and non-distributed
|
||||||
|
# ports, and introduce a new ml2_port_binding_hosts
|
||||||
|
# table for the fields that need to be host-specific
|
||||||
|
# in the distributed case. Since the PortBinding
|
||||||
|
# instance will then be needed, it does not make sense
|
||||||
|
# to optimize this code to avoid fetching it.
|
||||||
|
cur_binding = db.get_dvr_port_binding_by_host(
|
||||||
|
session, port_id, orig_binding.host)
|
||||||
cur_context = driver_context.PortContext(
|
cur_context = driver_context.PortContext(
|
||||||
self, plugin_context, port, network, cur_binding,
|
self, plugin_context, port, network, cur_binding,
|
||||||
original_port=oport)
|
original_port=oport)
|
||||||
@ -1068,52 +1091,11 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
binding = db.ensure_dvr_port_binding(
|
binding = db.ensure_dvr_port_binding(
|
||||||
session, id, host, router_id=device_id)
|
session, id, host, router_id=device_id)
|
||||||
network = self.get_network(context, orig_port['network_id'])
|
network = self.get_network(context, orig_port['network_id'])
|
||||||
mech_context = driver_context.DvrPortContext(self,
|
mech_context = driver_context.PortContext(self,
|
||||||
context, orig_port, network,
|
context, orig_port, network,
|
||||||
binding, original_port=orig_port)
|
binding, original_port=orig_port)
|
||||||
self._process_dvr_port_binding(mech_context, context, attrs)
|
self._process_dvr_port_binding(mech_context, context, attrs)
|
||||||
self.mechanism_manager.bind_port(mech_context)
|
self._bind_port_if_needed(mech_context)
|
||||||
# Now try to commit result of attempting to bind the port.
|
|
||||||
self._commit_dvr_port_binding(mech_context._plugin_context,
|
|
||||||
orig_port['id'],
|
|
||||||
host,
|
|
||||||
mech_context)
|
|
||||||
|
|
||||||
def _commit_dvr_port_binding(self, plugin_context,
|
|
||||||
port_id, host,
|
|
||||||
mech_context):
|
|
||||||
session = plugin_context.session
|
|
||||||
new_binding = mech_context._binding
|
|
||||||
with contextlib.nested(lockutils.lock('db-access'),
|
|
||||||
session.begin(subtransactions=True)):
|
|
||||||
# Get the current port state and build a new PortContext
|
|
||||||
# reflecting this state as original state for subsequent
|
|
||||||
# mechanism driver update_port_*commit() calls.
|
|
||||||
cur_binding = db.get_dvr_port_binding_by_host(session,
|
|
||||||
port_id,
|
|
||||||
host)
|
|
||||||
if not cur_binding:
|
|
||||||
LOG.info(_LI("Binding info for port %s was not found, "
|
|
||||||
"it might have been deleted already."),
|
|
||||||
port_id)
|
|
||||||
return
|
|
||||||
# Commit our binding results only if port has not been
|
|
||||||
# successfully bound concurrently by another thread or
|
|
||||||
# process and no binding inputs have been changed.
|
|
||||||
commit = ((cur_binding.vif_type in
|
|
||||||
[portbindings.VIF_TYPE_UNBOUND,
|
|
||||||
portbindings.VIF_TYPE_BINDING_FAILED]) and
|
|
||||||
new_binding.host == cur_binding.host and
|
|
||||||
new_binding.vnic_type == cur_binding.vnic_type and
|
|
||||||
new_binding.profile == cur_binding.profile)
|
|
||||||
|
|
||||||
if commit:
|
|
||||||
# Update the port's binding state with our binding
|
|
||||||
# results.
|
|
||||||
cur_binding.vif_type = new_binding.vif_type
|
|
||||||
cur_binding.vif_details = new_binding.vif_details
|
|
||||||
cur_binding.driver = new_binding.driver
|
|
||||||
cur_binding.segment = new_binding.segment
|
|
||||||
|
|
||||||
def delete_port(self, context, id, l3_port_check=True):
|
def delete_port(self, context, id, l3_port_check=True):
|
||||||
LOG.debug("Deleting port %s", id)
|
LOG.debug("Deleting port %s", id)
|
||||||
@ -1144,7 +1126,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
if device_owner == const.DEVICE_OWNER_DVR_INTERFACE:
|
if device_owner == const.DEVICE_OWNER_DVR_INTERFACE:
|
||||||
bindings = db.get_dvr_port_bindings(context.session, id)
|
bindings = db.get_dvr_port_bindings(context.session, id)
|
||||||
for bind in bindings:
|
for bind in bindings:
|
||||||
mech_context = driver_context.DvrPortContext(
|
mech_context = driver_context.PortContext(
|
||||||
self, context, port, network, bind)
|
self, context, port, network, bind)
|
||||||
self.mechanism_manager.delete_port_precommit(mech_context)
|
self.mechanism_manager.delete_port_precommit(mech_context)
|
||||||
bound_mech_contexts.append(mech_context)
|
bound_mech_contexts.append(mech_context)
|
||||||
@ -1217,7 +1199,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
LOG.error(_LE("Binding info for DVR port %s not found"),
|
LOG.error(_LE("Binding info for DVR port %s not found"),
|
||||||
port_id)
|
port_id)
|
||||||
return None
|
return None
|
||||||
port_context = driver_context.DvrPortContext(
|
port_context = driver_context.PortContext(
|
||||||
self, plugin_context, port, network, binding)
|
self, plugin_context, port, network, binding)
|
||||||
else:
|
else:
|
||||||
# since eager loads are disabled in port_db query
|
# since eager loads are disabled in port_db query
|
||||||
@ -1288,7 +1270,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
original_port['network_id'])
|
original_port['network_id'])
|
||||||
port.status = db.generate_dvr_port_status(session, port['id'])
|
port.status = db.generate_dvr_port_status(session, port['id'])
|
||||||
updated_port = self._make_port_dict(port)
|
updated_port = self._make_port_dict(port)
|
||||||
mech_context = (driver_context.DvrPortContext(
|
mech_context = (driver_context.PortContext(
|
||||||
self, context, updated_port, network,
|
self, context, updated_port, network,
|
||||||
binding, original_port=original_port))
|
binding, original_port=original_port))
|
||||||
self.mechanism_manager.update_port_precommit(mech_context)
|
self.mechanism_manager.update_port_precommit(mech_context)
|
||||||
|
@ -21,7 +21,12 @@ from neutron.plugins.ml2 import driver_context
|
|||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
|
|
||||||
|
|
||||||
class TestDvrPortContext(base.BaseTestCase):
|
class TestPortContext(base.BaseTestCase):
|
||||||
|
|
||||||
|
# REVISIT(rkukura): These was originally for DvrPortContext tests,
|
||||||
|
# but DvrPortContext functionality has been folded into the
|
||||||
|
# regular PortContext class. Tests for non-DVR-specific
|
||||||
|
# functionality are needed here as well.
|
||||||
|
|
||||||
def test_host(self):
|
def test_host(self):
|
||||||
plugin = mock.Mock()
|
plugin = mock.Mock()
|
||||||
@ -33,11 +38,11 @@ class TestDvrPortContext(base.BaseTestCase):
|
|||||||
binding.host = 'foohost'
|
binding.host = 'foohost'
|
||||||
|
|
||||||
with mock.patch.object(driver_context.db, 'get_network_segments'):
|
with mock.patch.object(driver_context.db, 'get_network_segments'):
|
||||||
ctx = driver_context.DvrPortContext(plugin,
|
ctx = driver_context.PortContext(plugin,
|
||||||
plugin_context,
|
plugin_context,
|
||||||
port,
|
port,
|
||||||
network,
|
network,
|
||||||
binding)
|
binding)
|
||||||
self.assertEqual('foohost', ctx.host)
|
self.assertEqual('foohost', ctx.host)
|
||||||
|
|
||||||
def test_host_super(self):
|
def test_host_super(self):
|
||||||
@ -51,11 +56,11 @@ class TestDvrPortContext(base.BaseTestCase):
|
|||||||
binding.host = 'foohost'
|
binding.host = 'foohost'
|
||||||
|
|
||||||
with mock.patch.object(driver_context.db, 'get_network_segments'):
|
with mock.patch.object(driver_context.db, 'get_network_segments'):
|
||||||
ctx = driver_context.DvrPortContext(plugin,
|
ctx = driver_context.PortContext(plugin,
|
||||||
plugin_context,
|
plugin_context,
|
||||||
port,
|
port,
|
||||||
network,
|
network,
|
||||||
binding)
|
binding)
|
||||||
self.assertEqual('host', ctx.host)
|
self.assertEqual('host', ctx.host)
|
||||||
|
|
||||||
def test_status(self):
|
def test_status(self):
|
||||||
@ -68,11 +73,11 @@ class TestDvrPortContext(base.BaseTestCase):
|
|||||||
binding.status = 'foostatus'
|
binding.status = 'foostatus'
|
||||||
|
|
||||||
with mock.patch.object(driver_context.db, 'get_network_segments'):
|
with mock.patch.object(driver_context.db, 'get_network_segments'):
|
||||||
ctx = driver_context.DvrPortContext(plugin,
|
ctx = driver_context.PortContext(plugin,
|
||||||
plugin_context,
|
plugin_context,
|
||||||
port,
|
port,
|
||||||
network,
|
network,
|
||||||
binding)
|
binding)
|
||||||
self.assertEqual('foostatus', ctx.status)
|
self.assertEqual('foostatus', ctx.status)
|
||||||
|
|
||||||
def test_status_super(self):
|
def test_status_super(self):
|
||||||
@ -86,9 +91,9 @@ class TestDvrPortContext(base.BaseTestCase):
|
|||||||
binding.status = 'foostatus'
|
binding.status = 'foostatus'
|
||||||
|
|
||||||
with mock.patch.object(driver_context.db, 'get_network_segments'):
|
with mock.patch.object(driver_context.db, 'get_network_segments'):
|
||||||
ctx = driver_context.DvrPortContext(plugin,
|
ctx = driver_context.PortContext(plugin,
|
||||||
plugin_context,
|
plugin_context,
|
||||||
port,
|
port,
|
||||||
network,
|
network,
|
||||||
binding)
|
binding)
|
||||||
self.assertEqual('status', ctx.status)
|
self.assertEqual('status', ctx.status)
|
||||||
|
@ -489,7 +489,7 @@ class TestMl2PortBinding(Ml2PluginV2TestCase,
|
|||||||
with mock.patch.object(plugin, '_update_port_dict_binding'):
|
with mock.patch.object(plugin, '_update_port_dict_binding'):
|
||||||
with mock.patch.object(ml2_db, 'get_network_segments',
|
with mock.patch.object(ml2_db, 'get_network_segments',
|
||||||
return_value=[]):
|
return_value=[]):
|
||||||
mech_context = driver_context.DvrPortContext(
|
mech_context = driver_context.PortContext(
|
||||||
self, context, 'port', mock_network, binding)
|
self, context, 'port', mock_network, binding)
|
||||||
plugin._process_dvr_port_binding(mech_context, context, attrs)
|
plugin._process_dvr_port_binding(mech_context, context, attrs)
|
||||||
self.assertEqual(new_router_id,
|
self.assertEqual(new_router_id,
|
||||||
|
@ -108,20 +108,6 @@ class PortBindingTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
|||||||
self.assertIsNone(
|
self.assertIsNone(
|
||||||
self.plugin.get_bound_port_context(ctx, port['port']['id']))
|
self.plugin.get_bound_port_context(ctx, port['port']['id']))
|
||||||
|
|
||||||
def test_commit_dvr_port_binding(self):
|
|
||||||
ctx = context.get_admin_context()
|
|
||||||
|
|
||||||
class MechContext(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
mctx = MechContext()
|
|
||||||
mctx._binding = None
|
|
||||||
# making a shortcut: calling private method directly to
|
|
||||||
# avoid bothering with "concurrent" port binding deletion
|
|
||||||
res = self.plugin._commit_dvr_port_binding(ctx, 'anyUUID',
|
|
||||||
'HostA', mctx)
|
|
||||||
self.assertIsNone(res)
|
|
||||||
|
|
||||||
def _test_update_port_binding(self, host, new_host=None):
|
def _test_update_port_binding(self, host, new_host=None):
|
||||||
with mock.patch.object(self.plugin,
|
with mock.patch.object(self.plugin,
|
||||||
'_notify_port_updated') as notify_mock:
|
'_notify_port_updated') as notify_mock:
|
||||||
|
Loading…
Reference in New Issue
Block a user