Clear DVR MAC on last agent deletion from host

Once all agents are deleted from a host, the DVR MAC generated
for that host should be deleted as well to prevent a buildup of
pointless flows generated in the OVS agent for hosts that don't
exist.

Closes-Bug: #1568206
Change-Id: I51e736aa0431980a595ecf810f148ca62d990d20
This commit is contained in:
Kevin Benton 2016-04-08 17:52:14 -07:00
parent 3e26a7851e
commit 92527c2de2
4 changed files with 69 additions and 1 deletions

View File

@ -11,6 +11,7 @@
# under the License.
# String literals representing core resources.
AGENT = 'agent'
EXTERNAL_NETWORK = 'external_network'
FLOATING_IP = 'floating_ip'
PORT = 'port'

View File

@ -31,6 +31,9 @@ from sqlalchemy import sql
from neutron._i18n import _, _LE, _LI, _LW
from neutron.api.rpc.callbacks import version_manager
from neutron.api.v2 import attributes
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.common import constants
from neutron import context
from neutron.db import api as db_api
@ -251,8 +254,10 @@ class AgentDbMixin(ext_agent.AgentPluginBase, AgentAvailabilityZoneMixin):
return self._fields(res, fields)
def delete_agent(self, context, id):
agent = self._get_agent(context, id)
registry.notify(resources.AGENT, events.BEFORE_DELETE, self,
context=context, agent=agent)
with context.session.begin(subtransactions=True):
agent = self._get_agent(context, id)
context.session.delete(agent)
def update_agent(self, context, id, agent):

View File

@ -22,6 +22,9 @@ from sqlalchemy import or_
from sqlalchemy.orm import exc
from neutron._i18n import _, _LE
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.common import constants
from neutron.common import exceptions as n_exc
from neutron.common import utils
@ -60,9 +63,36 @@ class DistributedVirtualRouterMacAddress(model_base.BASEV2):
mac_address = sa.Column(sa.String(32), nullable=False, unique=True)
def _delete_mac_associated_with_agent(resource, event, trigger, context, agent,
**kwargs):
host = agent['host']
plugin = manager.NeutronManager.get_plugin()
if [a for a in plugin.get_agents(context, filters={'host': [host]})
if a['id'] != agent['id']]:
# there are still agents on this host, don't mess with the mac entry
# until they are all deleted.
return
try:
with context.session.begin(subtransactions=True):
entry = (context.session.query(DistributedVirtualRouterMacAddress).
filter(DistributedVirtualRouterMacAddress.host == host).
one())
context.session.delete(entry)
except exc.NoResultFound:
return
# notify remaining agents so they cleanup flows
dvr_macs = plugin.get_dvr_mac_address_list(context)
plugin.notifier.dvr_mac_address_update(context, dvr_macs)
class DVRDbMixin(ext_dvr.DVRMacAddressPluginBase):
"""Mixin class to add dvr mac address to db_plugin_base_v2."""
def __new__(cls, *args, **kwargs):
registry.subscribe(_delete_mac_associated_with_agent,
resources.AGENT, events.BEFORE_DELETE)
return super(DVRDbMixin, cls).__new__(cls)
@property
def plugin(self):
try:

View File

@ -16,11 +16,15 @@
import mock
from oslo_config import cfg
from neutron.callbacks import events
from neutron.callbacks import registry
from neutron.callbacks import resources
from neutron.common import constants
from neutron import context
from neutron.db import dvr_mac_db
from neutron.extensions import dvr
from neutron.extensions import portbindings
from neutron import manager
from neutron.tests.unit.plugins.ml2 import test_plugin
@ -75,6 +79,34 @@ class DvrDbMixinTestCase(test_plugin.Ml2PluginV2TestCase):
self.ctx, "foo_host_2")
self.assertEqual(new_retries, f.call_count)
def test_mac_not_cleared_on_agent_delete_event_with_remaining_agents(self):
plugin = manager.NeutronManager.get_plugin()
self._create_dvr_mac_entry('host_1', 'mac_1')
self._create_dvr_mac_entry('host_2', 'mac_2')
agent1 = {'host': 'host_1', 'id': 'a1'}
agent2 = {'host': 'host_1', 'id': 'a2'}
with mock.patch.object(plugin, 'get_agents', return_value=[agent2]):
with mock.patch.object(plugin, 'notifier') as notifier:
registry.notify(resources.AGENT, events.BEFORE_DELETE, self,
context=self.ctx, agent=agent1)
mac_list = self.mixin.get_dvr_mac_address_list(self.ctx)
self.assertEqual(2, len(mac_list))
self.assertFalse(notifier.dvr_mac_address_update.called)
def test_mac_cleared_on_agent_delete_event(self):
plugin = manager.NeutronManager.get_plugin()
self._create_dvr_mac_entry('host_1', 'mac_1')
self._create_dvr_mac_entry('host_2', 'mac_2')
agent = {'host': 'host_1', 'id': 'a1'}
with mock.patch.object(plugin, 'notifier') as notifier:
registry.notify(resources.AGENT, events.BEFORE_DELETE, self,
context=self.ctx, agent=agent)
mac_list = self.mixin.get_dvr_mac_address_list(self.ctx)
self.assertEqual(1, len(mac_list))
self.assertEqual('host_2', mac_list[0]['host'])
notifier.dvr_mac_address_update.assert_called_once_with(
self.ctx, mac_list)
def test_get_dvr_mac_address_list(self):
self._create_dvr_mac_entry('host_1', 'mac_1')
self._create_dvr_mac_entry('host_2', 'mac_2')