Don't skip DVR port while neutron-openvswitch-agent is restared.

neutron-openvswitch-agent will refresh flows when it's restarted.
But the port's binding status is not changed, update_port_postcommit
will be skipped at function '_update_individual_port_db_status' in
'neutron/plugins/ml2/plugin.py', l2pop don't handle DVR ports, the
fdb entries about DVR port will not be added.

So, we can't skip DVR port at notify_l2pop_port_wiring when agent
is restared.

Closes-Bug: #1773286
Change-Id: I54e3db4822830a0c83daf7b5150575f8d6e2497b
(cherry picked from commit d0fa2c9ac5)
This commit is contained in:
Yang JianFeng 2018-06-02 05:10:59 +00:00 committed by Antonio Ojea
parent 8be878808e
commit 5a91c6de42
3 changed files with 77 additions and 11 deletions

View File

@ -248,6 +248,14 @@ class L2populationMechanismDriver(api.MechanismDriver):
return agents
def agent_restarted(self, context):
agent_host = context.host
session = db_api.get_reader_session()
agent = l2pop_db.get_agent_by_host(session, agent_host)
if l2pop_db.get_agent_uptime(agent) < cfg.CONF.l2pop.agent_boot_time:
return True
return False
def update_port_down(self, context):
port = context.current
agent_host = context.host
@ -296,9 +304,8 @@ class L2populationMechanismDriver(api.MechanismDriver):
# with high concurrency more than 1 port may be activated on an agent
# at the same time (like VM port + a DVR port) so checking for 1 or 2
is_first_port = agent_active_ports in (1, 2)
if is_first_port or (l2pop_db.get_agent_uptime(agent) <
cfg.CONF.l2pop.agent_boot_time):
# First port(s) activated on current agent in this network,
if is_first_port or self.agent_restarted(context):
# First port activated on current agent in this network,
# we have to provide it with the whole list of fdb entries
agent_fdb_entries = self._create_agent_fdb(session,
agent,

View File

@ -304,24 +304,28 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
port = ml2_db.get_port(rpc_context, port_id)
if not port:
return
# NOTE: DVR ports are already handled and updated through l2pop
# and so we don't need to update it again here
if port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE:
return
port_context = plugin.get_bound_port_context(
rpc_context, port_id)
rpc_context, port_id, host)
if not port_context:
# port deleted
return
# NOTE: DVR ports are already handled and updated through l2pop
# and so we don't need to update it again here. But, l2pop did not
# handle DVR ports while restart neutron-*-agent, we need to handle
# it here.
if (port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE and
not l2pop_driver.obj.agent_restarted(port_context)):
return
port = port_context.current
if (status == n_const.PORT_STATUS_ACTIVE and
if (port['device_owner'] != n_const.DEVICE_OWNER_DVR_INTERFACE and
status == n_const.PORT_STATUS_ACTIVE and
port[portbindings.HOST_ID] != host and
not l3_hamode_db.is_ha_router_port(rpc_context,
port['device_owner'],
port['device_id'])):
# don't setup ACTIVE forwarding entries unless bound to this
# host or if it's an HA port (which is special-cased in the
# mech driver)
# host or if it's an HA or DVR port (which is special-cased in
# the mech driver)
return
port_context.current['status'] = status
port_context.current[portbindings.HOST_ID] = host

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import mock
from neutron_lib.api.definitions import port as port_def
@ -353,6 +355,37 @@ class TestL2PopulationRpcTestCase(test_plugin.Ml2PluginV2TestCase):
self.mock_fanout.assert_called_with(
mock.ANY, 'remove_fdb_entries', expected)
def test_ovs_agent_restarted_with_dvr_port(self):
plugin = directory.get_plugin()
router = self._create_dvr_router()
with mock.patch.object(l2pop_mech_driver.L2populationMechanismDriver,
'agent_restarted', return_value=True):
with self.subnet(network=self._network,
enable_dhcp=False) as snet:
with self.port(
subnet=snet,
device_owner=constants.DEVICE_OWNER_DVR_INTERFACE)\
as port:
port_id = port['port']['id']
plugin.update_distributed_port_binding(self.adminContext,
port_id, {'port': {portbindings.HOST_ID: HOST_4,
'device_id': router['id']}})
port = self._show('ports', port_id)
self.assertEqual(portbindings.VIF_TYPE_DISTRIBUTED,
port['port'][portbindings.VIF_TYPE])
self.callbacks.update_device_up(self.adminContext,
agent_id=HOST_4,
device=port_id,
host=HOST_4)
fanout_expected = {port['port']['network_id']: {
'network_type': u'vxlan',
'ports': {
u'20.0.0.4': [('00:00:00:00:00:00', '0.0.0.0')]},
'segment_id': 1}}
self.mock_fanout.assert_called_with(mock.ANY,
'add_fdb_entries',
fanout_expected)
def test_ha_agents_with_dvr_rtr_does_not_get_other_fdb(self):
router = self._create_dvr_router()
directory.add_plugin(plugin_constants.L3, self.plugin)
@ -1454,3 +1487,25 @@ class TestL2PopulationMechDriver(base.BaseTestCase):
mech_driver = l2pop_mech_driver.L2populationMechanismDriver()
with testtools.ExpectedException(exceptions.InvalidInput):
mech_driver.update_port_precommit(ctx)
def test_agent_restarted(self):
mech_driver = l2pop_mech_driver.L2populationMechanismDriver()
ctx = mock.Mock()
ctx.host = "__host1__"
ctx._plugin_context = {}
agent = mock.Mock()
agent.started_at = datetime.datetime(2018, 5, 25, 15, 51, 20)
agent.heartbeat_timestamp = datetime.datetime(2018, 5, 25, 15,
51, 50)
with mock.patch.object(l2pop_db, 'get_agent_by_host',
return_value=agent):
res = mech_driver.agent_restarted(ctx)
self.assertTrue(res)
agent.heartbeat_timestamp = datetime.datetime(2018, 5, 25, 15,
58, 30)
with mock.patch.object(l2pop_db, 'get_agent_by_host',
return_value=agent):
res = mech_driver.agent_restarted(ctx)
self.assertFalse(res)