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

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

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

@ -22,6 +22,9 @@ from sqlalchemy import or_
from sqlalchemy.orm import exc from sqlalchemy.orm import exc
from neutron._i18n import _, _LE 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 constants
from neutron.common import exceptions as n_exc from neutron.common import exceptions as n_exc
from neutron.common import utils 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) 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): class DVRDbMixin(ext_dvr.DVRMacAddressPluginBase):
"""Mixin class to add dvr mac address to db_plugin_base_v2.""" """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 @property
def plugin(self): def plugin(self):
try: try:

@ -16,11 +16,15 @@
import mock import mock
from oslo_config import cfg 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.common import constants
from neutron import context from neutron import context
from neutron.db import dvr_mac_db from neutron.db import dvr_mac_db
from neutron.extensions import dvr from neutron.extensions import dvr
from neutron.extensions import portbindings from neutron.extensions import portbindings
from neutron import manager
from neutron.tests.unit.plugins.ml2 import test_plugin from neutron.tests.unit.plugins.ml2 import test_plugin
@ -75,6 +79,34 @@ class DvrDbMixinTestCase(test_plugin.Ml2PluginV2TestCase):
self.ctx, "foo_host_2") self.ctx, "foo_host_2")
self.assertEqual(new_retries, f.call_count) 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): def test_get_dvr_mac_address_list(self):
self._create_dvr_mac_entry('host_1', 'mac_1') self._create_dvr_mac_entry('host_1', 'mac_1')
self._create_dvr_mac_entry('host_2', 'mac_2') self._create_dvr_mac_entry('host_2', 'mac_2')