Merge "ML2: update port's status to DOWN if its binding info has changed"

This commit is contained in:
Jenkins 2016-02-05 14:05:46 +00:00 committed by Gerrit Code Review
commit fea9e09816
4 changed files with 49 additions and 44 deletions

View File

@ -38,7 +38,6 @@ class L2populationMechanismDriver(api.MechanismDriver):
def initialize(self):
LOG.debug("Experimental L2 population driver")
self.rpc_ctx = n_context.get_admin_context_without_session()
self.migrated_ports = {}
def _get_port_fdb_entries(self, port):
return [l2pop_rpc.PortInfo(mac_address=port['mac_address'],
@ -48,8 +47,8 @@ class L2populationMechanismDriver(api.MechanismDriver):
def delete_port_postcommit(self, context):
port = context.current
agent_host = context.host
fdb_entries = self._get_agent_fdb(context, port, agent_host)
fdb_entries = self._get_agent_fdb(context.bottom_bound_segment,
port, agent_host)
self.L2populationAgentNotify.remove_fdb_entries(self.rpc_ctx,
fdb_entries)
@ -122,50 +121,41 @@ class L2populationMechanismDriver(api.MechanismDriver):
if context.status == const.PORT_STATUS_DOWN:
agent_host = context.host
fdb_entries = self._get_agent_fdb(
context, port, agent_host)
context.bottom_bound_segment, port, agent_host)
self.L2populationAgentNotify.remove_fdb_entries(
self.rpc_ctx, fdb_entries)
elif (context.host != context.original_host
and context.status == const.PORT_STATUS_ACTIVE
and not self.migrated_ports.get(orig['id'])):
# The port has been migrated. We have to store the original
# binding to send appropriate fdb once the port will be set
# on the destination host
self.migrated_ports[orig['id']] = (
(orig, context.original_host))
and context.original_status == const.PORT_STATUS_ACTIVE
and context.status == const.PORT_STATUS_DOWN):
# The port has been migrated. Send notification about port
# removal from old host.
fdb_entries = self._get_agent_fdb(
context.original_bottom_bound_segment,
orig, context.original_host)
self.L2populationAgentNotify.remove_fdb_entries(
self.rpc_ctx, fdb_entries)
elif context.status != context.original_status:
if context.status == const.PORT_STATUS_ACTIVE:
self._update_port_up(context)
elif context.status == const.PORT_STATUS_DOWN:
fdb_entries = self._get_agent_fdb(
context, port, context.host)
context.bottom_bound_segment, port, context.host)
self.L2populationAgentNotify.remove_fdb_entries(
self.rpc_ctx, fdb_entries)
elif context.status == const.PORT_STATUS_BUILD:
orig = self.migrated_ports.pop(port['id'], None)
if orig:
original_port = orig[0]
original_host = orig[1]
# this port has been migrated: remove its entries from fdb
fdb_entries = self._get_agent_fdb(
context, original_port, original_host)
self.L2populationAgentNotify.remove_fdb_entries(
self.rpc_ctx, fdb_entries)
def _get_and_validate_segment(self, context, port_id, agent):
segment = context.bottom_bound_segment
def _validate_segment(self, segment, port_id, agent):
if not segment:
LOG.debug("Port %(port)s updated by agent %(agent)s isn't bound "
"to any segment", {'port': port_id, 'agent': agent})
return
return False
network_types = l2pop_db.get_agent_l2pop_network_types(agent)
if network_types is None:
network_types = l2pop_db.get_agent_tunnel_types(agent)
if segment['network_type'] not in network_types:
return
return False
return segment
return True
def _create_agent_fdb(self, session, agent, segment, network_id):
agent_fdb_entries = {network_id:
@ -220,8 +210,8 @@ class L2populationMechanismDriver(api.MechanismDriver):
session, agent_host, network_id)
agent_ip = l2pop_db.get_agent_ip(agent)
segment = self._get_and_validate_segment(context, port['id'], agent)
if not segment:
segment = context.bottom_bound_segment
if not self._validate_segment(segment, port['id'], agent):
return
other_fdb_entries = self._get_fdb_entries_template(
segment, agent_ip, network_id)
@ -250,7 +240,7 @@ class L2populationMechanismDriver(api.MechanismDriver):
self.L2populationAgentNotify.add_fdb_entries(self.rpc_ctx,
other_fdb_entries)
def _get_agent_fdb(self, context, port, agent_host):
def _get_agent_fdb(self, segment, port, agent_host):
if not agent_host:
return
@ -261,8 +251,7 @@ class L2populationMechanismDriver(api.MechanismDriver):
session, agent_host, network_id)
agent = l2pop_db.get_agent_by_host(db_api.get_session(), agent_host)
segment = self._get_and_validate_segment(context, port['id'], agent)
if not segment:
if not self._validate_segment(segment, port['id'], agent):
return
agent_ip = l2pop_db.get_agent_ip(agent)

View File

@ -284,6 +284,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
binding.vif_details = ''
db.clear_binding_levels(session, port_id, original_host)
mech_context._clear_binding_levels()
port['status'] = const.PORT_STATUS_DOWN
super(Ml2Plugin, self).update_port(
mech_context._plugin_context, port_id,
{attributes.PORT: {'status': const.PORT_STATUS_DOWN}})
if port['device_owner'] == const.DEVICE_OWNER_DVR_INTERFACE:
binding.vif_type = portbindings.VIF_TYPE_UNBOUND

View File

@ -778,6 +778,7 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase):
device_owner=DEVICE_OWNER_COMPUTE,
arg_list=(portbindings.HOST_ID,),
**host_arg) as port1:
tunnel_ip = '20.0.0.1'
p1 = port1['port']
device1 = 'tap' + p1['id']
self.callbacks.update_device_up(
@ -785,22 +786,21 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase):
agent_id=HOST,
device=device1)
if twice:
tunnel_ip = '20.0.0.4'
self._update_and_check_portbinding(p1['id'], HOST_4)
self._update_and_check_portbinding(p1['id'], HOST_2)
self.callbacks.update_device_up(self.adminContext,
agent_id=HOST_4,
device=device1)
self.mock_fanout.reset_mock()
# NOTE(yamamoto): see bug #1441488
self.adminContext.session.expire_all()
self.callbacks.get_device_details(
self.adminContext,
device=device1,
agent_id=HOST_2)
self._update_and_check_portbinding(p1['id'], HOST_2)
p1_ips = [p['ip_address'] for p in p1['fixed_ips']]
expected = {p1['network_id']:
{'ports':
{'20.0.0.1': [constants.FLOODING_ENTRY,
l2pop_rpc.PortInfo(
p1['mac_address'],
p1_ips[0])]},
{tunnel_ip: [constants.FLOODING_ENTRY,
l2pop_rpc.PortInfo(
p1['mac_address'],
p1_ips[0])]},
'network_type': 'vxlan',
'segment_id': 1}}

View File

@ -518,6 +518,16 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
plugin.update_port(ctx, port['port']['id'], port)
self.assertTrue(sg_member_update.called)
def test_update_port_host_id_changed(self):
ctx = context.get_admin_context()
plugin = manager.NeutronManager.get_plugin()
host_id = {portbindings.HOST_ID: 'host1'}
with self.port(**host_id) as port:
plugin.update_port_status(ctx, port['port']['id'], 'UP')
port['port']['binding:host_id'] = 'host2'
result = plugin.update_port(ctx, port['port']['id'], port)
self.assertEqual(constants.PORT_STATUS_DOWN, result['status'])
def test_update_port_status_with_network(self):
ctx = context.get_admin_context()
plugin = manager.NeutronManager.get_plugin()
@ -1793,7 +1803,9 @@ class TestMl2PluginCreateUpdateDeletePort(base.BaseTestCase):
def test_create_port_rpc_outside_transaction(self):
with mock.patch.object(ml2_plugin.Ml2Plugin, '__init__') as init,\
mock.patch.object(base_plugin.NeutronDbPluginV2,
'create_port') as db_create_port:
'create_port') as db_create_port, \
mock.patch.object(base_plugin.NeutronDbPluginV2,
'update_port'):
init.return_value = None
new_port = mock.MagicMock()