DistributedVirtualRouter mac address to OVO
This patch introduces and integrates Oslo-Versioned Objects for the DistributedVirtualRouter mac address model class. Partially-Implements: blueprint adopt-oslo-versioned-objects-for-db Co-Authored-By: Victor Morales<victor.morales@intel.com> Change-Id: I3b8a213a7daf95d2492b48ae59d3ad534911e1bb
This commit is contained in:
parent
d37f0652b9
commit
071cb905f3
|
@ -13,6 +13,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import netaddr
|
||||
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib.callbacks import registry
|
||||
|
@ -20,20 +22,18 @@ from neutron_lib.callbacks import resources
|
|||
from neutron_lib import constants
|
||||
from neutron_lib import exceptions as n_exc
|
||||
from neutron_lib.plugins import directory
|
||||
from neutron_lib.utils import net
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as db_exc
|
||||
from oslo_log import helpers as log_helpers
|
||||
from oslo_log import log as logging
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
from neutron._i18n import _, _LE
|
||||
from neutron.common import utils
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db.models import dvr as dvr_models
|
||||
from neutron.db import models_v2
|
||||
from neutron.extensions import dvr as ext_dvr
|
||||
from neutron.objects import exceptions
|
||||
from neutron.objects import router
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -81,42 +81,33 @@ class DVRDbMixin(ext_dvr.DVRMacAddressPluginBase):
|
|||
# there are still agents on this host, don't mess with the mac
|
||||
# entry until they are all deleted.
|
||||
return
|
||||
try:
|
||||
with db_api.context_manager.writer.using(context):
|
||||
entry = (context.session.
|
||||
query(dvr_models.DistributedVirtualRouterMacAddress).
|
||||
filter_by(host=host).
|
||||
one())
|
||||
context.session.delete(entry)
|
||||
except exc.NoResultFound:
|
||||
|
||||
if not router.DVRMacAddress.delete_objects(context, host=host):
|
||||
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)
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def _get_dvr_mac_address_by_host(self, context, host):
|
||||
try:
|
||||
query = context.session.query(
|
||||
dvr_models.DistributedVirtualRouterMacAddress)
|
||||
dvrma = query.filter(
|
||||
dvr_models.DistributedVirtualRouterMacAddress.host == host
|
||||
).one()
|
||||
except exc.NoResultFound:
|
||||
dvr_obj = router.DVRMacAddress.get_object(context, host=host)
|
||||
if not dvr_obj:
|
||||
raise ext_dvr.DVRMacAddressNotFound(host=host)
|
||||
return dvrma
|
||||
return self._make_dvr_mac_address_dict(dvr_obj)
|
||||
|
||||
@utils.transaction_guard
|
||||
@db_api.retry_if_session_inactive()
|
||||
def _create_dvr_mac_address_retry(self, context, host, base_mac):
|
||||
with db_api.context_manager.writer.using(context):
|
||||
mac_address = net.get_random_mac(base_mac)
|
||||
dvr_mac_binding = dvr_models.DistributedVirtualRouterMacAddress(
|
||||
host=host, mac_address=mac_address)
|
||||
context.session.add(dvr_mac_binding)
|
||||
mac_address = utils.get_random_mac(base_mac)
|
||||
dvr_mac_binding = router.DVRMacAddress(
|
||||
context, host=host, mac_address=netaddr.EUI(mac_address))
|
||||
dvr_mac_binding.create()
|
||||
LOG.debug("Generated DVR mac for host %(host)s "
|
||||
"is %(mac_address)s",
|
||||
{'host': host, 'mac_address': mac_address})
|
||||
|
||||
dvr_macs = self.get_dvr_mac_address_list(context)
|
||||
# TODO(vivek): improve scalability of this fanout by
|
||||
# sending a single mac address rather than the entire set
|
||||
|
@ -128,15 +119,17 @@ class DVRDbMixin(ext_dvr.DVRMacAddressPluginBase):
|
|||
base_mac = cfg.CONF.dvr_base_mac.split(':')
|
||||
try:
|
||||
return self._create_dvr_mac_address_retry(context, host, base_mac)
|
||||
except db_exc.DBDuplicateEntry:
|
||||
except exceptions.NeutronDbObjectDuplicateEntry:
|
||||
LOG.error(_LE("MAC generation error after %s attempts"),
|
||||
db_api.MAX_RETRIES)
|
||||
raise ext_dvr.MacAddressGenerationFailure(host=host)
|
||||
|
||||
@db_api.context_manager.reader
|
||||
def get_dvr_mac_address_list(self, context):
|
||||
return (context.session.
|
||||
query(dvr_models.DistributedVirtualRouterMacAddress).all())
|
||||
return [
|
||||
dvr_mac.to_dict()
|
||||
for dvr_mac in router.DVRMacAddress.get_objects(context)
|
||||
]
|
||||
|
||||
def get_dvr_mac_address_by_host(self, context, host):
|
||||
"""Determine the MAC for the DVR port associated to host."""
|
||||
|
@ -150,7 +143,7 @@ class DVRDbMixin(ext_dvr.DVRMacAddressPluginBase):
|
|||
|
||||
def _make_dvr_mac_address_dict(self, dvr_mac_entry, fields=None):
|
||||
return {'host': dvr_mac_entry['host'],
|
||||
'mac_address': dvr_mac_entry['mac_address']}
|
||||
'mac_address': str(dvr_mac_entry['mac_address'])}
|
||||
|
||||
@log_helpers.log_method_call
|
||||
@db_api.retry_if_session_inactive()
|
||||
|
|
|
@ -17,6 +17,7 @@ from oslo_versionedobjects import fields as obj_fields
|
|||
from sqlalchemy import func
|
||||
|
||||
from neutron.common import utils
|
||||
from neutron.db.models import dvr as dvr_models
|
||||
from neutron.db.models import l3
|
||||
from neutron.db.models import l3_attrs
|
||||
from neutron.db.models import l3agent as rb_model
|
||||
|
@ -134,3 +135,34 @@ class RouterPort(base.NeutronDbObject):
|
|||
'port_id': common_types.UUIDField(),
|
||||
'port_type': obj_fields.StringField(nullable=True),
|
||||
}
|
||||
|
||||
|
||||
@obj_base.VersionedObjectRegistry.register
|
||||
class DVRMacAddress(base.NeutronDbObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
db_model = dvr_models.DistributedVirtualRouterMacAddress
|
||||
|
||||
primary_keys = ['host']
|
||||
|
||||
fields = {
|
||||
'host': obj_fields.StringField(),
|
||||
'mac_address': common_types.MACAddressField()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def modify_fields_from_db(cls, db_obj):
|
||||
fields = super(DVRMacAddress, cls).modify_fields_from_db(db_obj)
|
||||
if 'mac_address' in fields:
|
||||
# NOTE(tonytan4ever): Here uses AuthenticEUI to retain the format
|
||||
# passed from API.
|
||||
fields['mac_address'] = utils.AuthenticEUI(fields['mac_address'])
|
||||
return fields
|
||||
|
||||
@classmethod
|
||||
def modify_fields_to_db(cls, fields):
|
||||
result = super(DVRMacAddress, cls).modify_fields_to_db(fields)
|
||||
if 'mac_address' in fields:
|
||||
result['mac_address'] = cls.filter_to_str(result['mac_address'])
|
||||
return result
|
||||
|
|
|
@ -22,10 +22,10 @@ from neutron_lib import constants
|
|||
from neutron_lib import context
|
||||
from neutron_lib.plugins import directory
|
||||
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db import dvr_mac_db
|
||||
from neutron.db.models import dvr as dvr_models
|
||||
from neutron.extensions import dvr
|
||||
from neutron.objects import router
|
||||
from neutron.tests import tools
|
||||
from neutron.tests.unit.plugins.ml2 import test_plugin
|
||||
|
||||
|
||||
|
@ -43,19 +43,16 @@ class DvrDbMixinTestCase(test_plugin.Ml2PluginV2TestCase):
|
|||
self.mixin = DVRDbMixinImpl(mock.Mock())
|
||||
|
||||
def _create_dvr_mac_entry(self, host, mac_address):
|
||||
with db_api.context_manager.writer.using(self.ctx):
|
||||
entry = dvr_models.DistributedVirtualRouterMacAddress(
|
||||
host=host, mac_address=mac_address)
|
||||
self.ctx.session.add(entry)
|
||||
router.DVRMacAddress(
|
||||
self.ctx, host=host, mac_address=mac_address).create()
|
||||
|
||||
def test__get_dvr_mac_address_by_host(self):
|
||||
with db_api.context_manager.writer.using(self.ctx):
|
||||
entry = dvr_models.DistributedVirtualRouterMacAddress(
|
||||
host='foo_host', mac_address='foo_mac_address')
|
||||
self.ctx.session.add(entry)
|
||||
result = self.mixin._get_dvr_mac_address_by_host(self.ctx,
|
||||
'foo_host')
|
||||
self.assertEqual(entry, result)
|
||||
entry = router.DVRMacAddress(
|
||||
self.ctx, host='foo_host',
|
||||
mac_address=tools.get_random_EUI())
|
||||
entry.create()
|
||||
result = self.mixin._get_dvr_mac_address_by_host(self.ctx, 'foo_host')
|
||||
self.assertEqual(entry.to_dict(), result)
|
||||
|
||||
def test__get_dvr_mac_address_by_host_not_found(self):
|
||||
self.assertRaises(dvr.DVRMacAddressNotFound,
|
||||
|
@ -63,8 +60,8 @@ class DvrDbMixinTestCase(test_plugin.Ml2PluginV2TestCase):
|
|||
self.ctx, 'foo_host')
|
||||
|
||||
def test__create_dvr_mac_address_success(self):
|
||||
entry = {'host': 'foo_host', 'mac_address': '00:11:22:33:44:55:66'}
|
||||
with mock.patch.object(dvr_mac_db.net, 'get_random_mac') as f:
|
||||
entry = {'host': 'foo_host', 'mac_address': tools.get_random_EUI()}
|
||||
with mock.patch.object(dvr_mac_db.utils, 'get_random_mac') as f:
|
||||
f.return_value = entry['mac_address']
|
||||
expected = self.mixin._create_dvr_mac_address(
|
||||
self.ctx, entry['host'])
|
||||
|
@ -74,17 +71,21 @@ class DvrDbMixinTestCase(test_plugin.Ml2PluginV2TestCase):
|
|||
# limit retries so test doesn't take 40 seconds
|
||||
mock.patch('neutron.db.api._retry_db_errors.max_retries',
|
||||
new=2).start()
|
||||
self._create_dvr_mac_entry('foo_host_1', 'non_unique_mac')
|
||||
with mock.patch.object(dvr_mac_db.net, 'get_random_mac') as f:
|
||||
f.return_value = 'non_unique_mac'
|
||||
|
||||
non_unique_mac = tools.get_random_EUI()
|
||||
self._create_dvr_mac_entry('foo_host_1', non_unique_mac)
|
||||
with mock.patch.object(dvr_mac_db.utils, 'get_random_mac') as f:
|
||||
f.return_value = non_unique_mac
|
||||
self.assertRaises(dvr.MacAddressGenerationFailure,
|
||||
self.mixin._create_dvr_mac_address,
|
||||
self.ctx, "foo_host_2")
|
||||
|
||||
def test_mac_not_cleared_on_agent_delete_event_with_remaining_agents(self):
|
||||
plugin = directory.get_plugin()
|
||||
self._create_dvr_mac_entry('host_1', 'mac_1')
|
||||
self._create_dvr_mac_entry('host_2', 'mac_2')
|
||||
mac_1 = tools.get_random_EUI()
|
||||
mac_2 = tools.get_random_EUI()
|
||||
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]):
|
||||
|
@ -92,33 +93,41 @@ class DvrDbMixinTestCase(test_plugin.Ml2PluginV2TestCase):
|
|||
registry.notify(resources.AGENT, events.BEFORE_DELETE, self,
|
||||
context=self.ctx, agent=agent1)
|
||||
mac_list = self.mixin.get_dvr_mac_address_list(self.ctx)
|
||||
for mac in mac_list:
|
||||
self.assertIsInstance(mac, dict)
|
||||
self.assertEqual(2, len(mac_list))
|
||||
self.assertFalse(notifier.dvr_mac_address_update.called)
|
||||
|
||||
def test_mac_cleared_on_agent_delete_event(self):
|
||||
plugin = directory.get_plugin()
|
||||
|
||||
with db_api.context_manager.writer.using(self.ctx):
|
||||
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)
|
||||
mac_1 = tools.get_random_EUI()
|
||||
mac_2 = tools.get_random_EUI()
|
||||
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))
|
||||
for mac in mac_list:
|
||||
self.assertIsInstance(mac, dict)
|
||||
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')
|
||||
mac_1 = tools.get_random_EUI()
|
||||
mac_2 = tools.get_random_EUI()
|
||||
self._create_dvr_mac_entry('host_1', mac_1)
|
||||
self._create_dvr_mac_entry('host_2', mac_2)
|
||||
mac_list = self.mixin.get_dvr_mac_address_list(self.ctx)
|
||||
self.assertEqual(2, len(mac_list))
|
||||
for mac in mac_list:
|
||||
self.assertIsInstance(mac, dict)
|
||||
|
||||
def test_get_dvr_mac_address_by_host_existing_host(self):
|
||||
self._create_dvr_mac_entry('foo_host', 'foo_mac')
|
||||
self._create_dvr_mac_entry('foo_host', tools.get_random_EUI())
|
||||
with mock.patch.object(self.mixin,
|
||||
'_get_dvr_mac_address_by_host') as f:
|
||||
self.mixin.get_dvr_mac_address_by_host(self.ctx, 'foo_host')
|
||||
|
|
|
@ -34,6 +34,7 @@ object_data = {
|
|||
'DistributedPortBinding': '1.0-39c0d17b281991dcb66716fee5a8bef2',
|
||||
'DNSNameServer': '1.0-bf87a85327e2d812d1666ede99d9918b',
|
||||
'ExternalNetwork': '1.0-53d885e033cb931f9bb3bdd6bbe3f0ce',
|
||||
'DVRMacAddress': '1.0-d3c61a8338d20da74db2364d4d6554f2',
|
||||
'ExtraDhcpOpt': '1.0-632f689cbeb36328995a7aed1d0a78d3',
|
||||
'FlatAllocation': '1.0-bf666f24f4642b047eeca62311fbcb41',
|
||||
'Flavor': '1.0-82194de5c9aafce08e8527bb7977f5c6',
|
||||
|
|
|
@ -65,3 +65,14 @@ class RouterPortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
|||
self.update_obj_fields(
|
||||
{'router_id': lambda: self._create_test_router_id(),
|
||||
'port_id': lambda: self._create_test_port_id()})
|
||||
|
||||
|
||||
class DVRMacAddressIfaceObjectTestCase(obj_test_base.BaseObjectIfaceTestCase):
|
||||
|
||||
_test_class = router.DVRMacAddress
|
||||
|
||||
|
||||
class DVRMacAddressDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
||||
_test_class = router.DVRMacAddress
|
||||
|
|
Loading…
Reference in New Issue