Integration of IPAllocation

This patch integrates the Oslo-Versioned Object created for
IPAllocation model class.

Change-Id: Ibb731c4635d89c848081fac73f216d2ecf10b599
Partially-Implements: blueprint adopt-oslo-versioned-objects-for-db
This commit is contained in:
Victor Morales 2016-11-11 11:58:07 -06:00 committed by Lujin
parent 05c22d6199
commit 6670954de3
9 changed files with 95 additions and 70 deletions

View File

@ -32,6 +32,7 @@ from neutron.db import _utils as db_utils
from neutron.db import api as db_api
from neutron.db import common_db_mixin
from neutron.db import models_v2
from neutron.objects import ports as port_obj
from neutron.objects import subnet as subnet_obj
from neutron.objects import subnetpool as subnetpool_obj
@ -102,12 +103,9 @@ class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
{'ip_address': ip_address,
'network_id': network_id,
'subnet_id': subnet_id})
with db_api.context_manager.writer.using(context):
for ipal in (context.session.query(models_v2.IPAllocation).
filter_by(network_id=network_id,
ip_address=ip_address,
subnet_id=subnet_id)):
context.session.delete(ipal)
port_obj.IPAllocation.delete_objects(
context, network_id=network_id, ip_address=ip_address,
subnet_id=subnet_id)
@staticmethod
@db_api.context_manager.writer
@ -119,16 +117,20 @@ class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
'network_id': network_id,
'subnet_id': subnet_id,
'port_id': port_id})
allocated = models_v2.IPAllocation(
network_id=network_id,
port_id=port_id,
ip_address=ip_address,
subnet_id=subnet_id
)
port_db = context.session.query(models_v2.Port).get(port_id)
port_db.fixed_ips.append(allocated)
port_db.fixed_ips.sort(key=lambda fip: (fip['ip_address'],
fip['subnet_id']))
allocated = port_obj.IPAllocation(
context, network_id=network_id, port_id=port_id,
ip_address=ip_address, subnet_id=subnet_id)
# NOTE(lujinluo): Add IPAllocations obj to the port fixed_ips
# in Port OVO integration, i.e. the same way we did in
# Ib32509d974c8654131112234bcf19d6eae8f7cca
allocated.create()
# NOTE(kevinbenton): We add this to the session info so the sqlalchemy
# object isn't immediately garbage collected. Otherwise when the
# fixed_ips relationship is referenced a new persistent object will be
# added to the session that will interfere with retry operations.
# See bug 1556178 for details.
context.session.info.setdefault('allocated_ips', []).append(allocated)
def _make_subnet_dict(self, subnet, fields=None, context=None):
res = {'id': subnet['id'],

View File

@ -58,6 +58,7 @@ from neutron.ipam import exceptions as ipam_exc
from neutron.ipam import subnet_alloc
from neutron import neutron_plugin_base_v2
from neutron.objects import base as base_obj
from neutron.objects import ports as port_obj
from neutron.objects import subnetpool as subnetpool_obj
@ -552,6 +553,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
# executed concurrently
if cur_subnet and not ipv6_utils.is_ipv6_pd_enabled(s):
with db_api.context_manager.reader.using(context):
# TODO(electrocucaracha): Look a solution for Join in OVO
ipal = models_v2.IPAllocation
alloc_qry = context.session.query(ipal)
alloc_qry = alloc_qry.join("port", "routerport")
@ -915,24 +917,16 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
@db_api.context_manager.reader
def _subnet_get_user_allocation(self, context, subnet_id):
"""Check if there are any user ports on subnet and return first."""
# need to join with ports table as IPAllocation's port
# is not joined eagerly and thus producing query which yields
# incorrect results
return (context.session.query(models_v2.IPAllocation).
filter_by(subnet_id=subnet_id).join(models_v2.Port).
filter(~models_v2.Port.device_owner.
in_(AUTO_DELETE_PORT_OWNERS)).first())
return port_obj.IPAllocation.get_alloc_by_subnet_id(
context, subnet_id, AUTO_DELETE_PORT_OWNERS)
@db_api.context_manager.reader
def _subnet_check_ip_allocations_internal_router_ports(self, context,
subnet_id):
# Do not delete the subnet if IP allocations for internal
# router ports still exist
allocs = context.session.query(models_v2.IPAllocation).filter_by(
subnet_id=subnet_id).join(models_v2.Port).filter(
models_v2.Port.device_owner.in_(
constants.ROUTER_INTERFACE_OWNERS)
).first()
allocs = port_obj.IPAllocation.get_alloc_by_subnet_id(
context, subnet_id, constants.ROUTER_INTERFACE_OWNERS, False)
if allocs:
LOG.debug("Subnet %s still has internal router ports, "
"cannot delete", subnet_id)

View File

@ -32,6 +32,7 @@ from neutron.db import ipam_backend_mixin
from neutron.db import models_v2
from neutron.ipam import driver
from neutron.ipam import exceptions as ipam_exc
from neutron.objects import ports as port_obj
from neutron.objects import subnet as obj_subnet
@ -389,6 +390,7 @@ class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
port_copy['fixed_ips'] = auto_assign_subnets
self.allocate_ips_for_port_and_store(context,
{'port': port_copy}, port_copy['id'])
context.session.refresh(db_port)
except Exception:
with excutils.save_and_reraise_exception():
@ -478,16 +480,15 @@ class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin):
port, ip, subnet, ipam_subnet)
try:
ip_address = ipam_subnet.allocate(ip_request)
allocated = models_v2.IPAllocation(network_id=network_id,
port_id=port['id'],
ip_address=ip_address,
subnet_id=subnet['id'])
allocated = port_obj.IPAllocation(
context, network_id=network_id, port_id=port['id'],
ip_address=ip_address, subnet_id=subnet['id'])
# Do the insertion of each IP allocation entry within
# the context of a nested transaction, so that the entry
# is rolled back independently of other entries whenever
# the corresponding port has been deleted.
with db_api.context_manager.writer.using(context):
context.session.add(allocated)
allocated.create()
updated_ports.append(port['id'])
except db_exc.DBReferenceError:
LOG.debug("Port %s was deleted while updating it with an "

View File

@ -182,6 +182,27 @@ class IPAllocation(base.NeutronDbObject):
fields['ip_address'] = netaddr.IPAddress(fields['ip_address'])
return fields
@classmethod
def get_alloc_by_subnet_id(cls, context, subnet_id, device_owner,
exclude=True):
# need to join with ports table as IPAllocation's port
# is not joined eagerly and thus producing query which yields
# incorrect results
if exclude:
alloc_db = (context.session.query(models_v2.IPAllocation).
filter_by(subnet_id=subnet_id).join(models_v2.Port).
filter(~models_v2.Port.device_owner.
in_(device_owner)).first())
else:
alloc_db = (context.session.query(models_v2.IPAllocation).
filter_by(subnet_id=subnet_id).join(models_v2.Port).
filter(models_v2.Port.device_owner.
in_(device_owner)).first())
if exclude and alloc_db:
return super(IPAllocation, cls)._load_object(context, alloc_db)
if alloc_db:
return True
@obj_base.VersionedObjectRegistry.register
class PortDNS(base.NeutronDbObject):

View File

@ -22,7 +22,6 @@ from oslo_config import cfg
from oslo_log import log as logging
from neutron._i18n import _LE, _LI
from neutron.db import models_v2
from neutron.db import segments_db
from neutron.extensions import dns
from neutron.objects import network as net_obj
@ -431,8 +430,8 @@ def _delete_port_in_external_dns_service(resource, event, trigger, **kwargs):
if not dns_data_db:
return
if dns_data_db['current_dns_name']:
ip_allocations = context.session.query(
models_v2.IPAllocation).filter_by(port_id=port_id).all()
ip_allocations = port_obj.IPAllocation.get_objects(context,
port_id=port_id)
records = [alloc['ip_address'] for alloc in ip_allocations]
_remove_data_from_external_dns_service(
context, dns_driver, dns_data_db['current_dns_domain'],

View File

@ -22,7 +22,7 @@ from oslo_utils import uuidutils
import testtools
from neutron.db import db_base_plugin_v2 as base_plugin
from neutron.db import models_v2
from neutron.objects import ports as port_obj
from neutron.objects import subnet as subnet_obj
from neutron.tests.unit import testlib_api
@ -64,7 +64,7 @@ class IpamTestCase(testlib_api.SqlTestCase):
return dicts
def assert_ip_alloc_matches(self, expected):
result_set = self.cxt.session.query(models_v2.IPAllocation).all()
result_set = port_obj.IPAllocation.get_objects(self.cxt)
keys = ['port_id', 'ip_address', 'subnet_id', 'network_id']
actual = self.result_set_to_dicts(result_set, keys)
self.assertEqual(expected, actual)
@ -119,7 +119,8 @@ class IpamTestCase(testlib_api.SqlTestCase):
self._create_port(self.port_id, fixed_ip)
ip_alloc_expected = [{'port_id': self.port_id,
'ip_address': fixed_ip[0].get('ip_address'),
'ip_address': netaddr.IPAddress(
fixed_ip[0].get('ip_address')),
'subnet_id': self.subnet_id,
'network_id': self.network_id}]
ip_alloc_pool_expected = [{'start': netaddr.IPAddress('10.10.10.2'),

View File

@ -30,6 +30,7 @@ from neutron.db import ipam_backend_mixin
from neutron.db import ipam_pluggable_backend
from neutron.db import models_v2
from neutron.ipam import requests as ipam_req
from neutron.objects import ports as port_obj
from neutron.objects import subnet as obj_subnet
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_base
@ -93,7 +94,7 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
mocks['driver'].allocate_subnet.return_value = mocks['subnet']
mocks['driver'].get_allocator.return_value = mocks['subnets']
mocks['subnets'].allocate.return_value = (
mock.sentinel.address, mock.sentinel.subnet_id)
'127.0.0.1', uuidutils.generate_uuid())
mocks['driver'].get_subnet_request_factory.return_value = (
subnet_factory)
mocks['driver'].get_address_request_factory.return_value = (
@ -671,7 +672,7 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
@mock.patch('neutron.ipam.driver.Pool')
def test_update_ips_for_port_passes_port_id_to_factory(self, pool_mock):
port_id = mock.Mock()
port_id = uuidutils.generate_uuid()
network_id = uuidutils.generate_uuid()
address_factory = mock.Mock()
mocks = self._prepare_mocks_with_pool_mock(
@ -695,9 +696,10 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
mocks['ipam']._ipam_get_subnets = get_subnets_mock
mocks['ipam']._get_subnet = get_subnet_mock
mocks['ipam'].allocate_ips_for_port_and_store(context,
port_dict,
port_id)
with mock.patch.object(port_obj.IPAllocation, 'create'):
mocks['ipam'].allocate_ips_for_port_and_store(context,
port_dict,
port_id)
mocks['driver'].get_address_request_factory.assert_called_once_with()
@ -781,21 +783,22 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
mocks['ipam'] = ipam_pluggable_backend.IpamPluggableBackend()
new_port = {'fixed_ips': [{'ip_address': '192.168.1.20',
'subnet_id': 'some-id'},
'subnet_id': uuidutils.generate_uuid()},
{'ip_address': '192.168.1.50',
'subnet_id': 'some-id'}]}
db_port = models_v2.Port(id='id', network_id='some-net-id')
'subnet_id': uuidutils.generate_uuid()}]}
db_port = models_v2.Port(id=uuidutils.generate_uuid(),
network_id=uuidutils.generate_uuid())
old_port = {'fixed_ips': [{'ip_address': '192.168.1.10',
'subnet_id': 'some-id'},
'subnet_id': uuidutils.generate_uuid()},
{'ip_address': '192.168.1.50',
'subnet_id': 'some-id'}]}
'subnet_id': uuidutils.generate_uuid()}]}
changes = mocks['ipam'].Changes(
add=[{'ip_address': '192.168.1.20',
'subnet_id': 'some-id'}],
'subnet_id': uuidutils.generate_uuid()}],
original=[{'ip_address': '192.168.1.50',
'subnet_id': 'some-id'}],
'subnet_id': uuidutils.generate_uuid()}],
remove=[{'ip_address': '192.168.1.10',
'subnet_id': 'some-id'}])
'subnet_id': uuidutils.generate_uuid()}])
mocks['ipam']._delete_ip_allocation = mock.Mock()
mocks['ipam']._make_port_dict = mock.Mock(return_value=old_port)
mocks['ipam']._update_ips_for_port = mock.Mock(return_value=changes)
@ -807,16 +810,17 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
# Validate original exception (DBDeadlock) is not overridden by
# exception raised on rollback (ValueError)
self.assertRaises(db_exc.DBDeadlock,
mocks['ipam'].update_port_with_ips,
context,
None,
db_port,
new_port,
mock.Mock())
mocks['ipam']._ipam_deallocate_ips.assert_called_once_with(
context, mocks['driver'], db_port,
changes.add, revert_on_fail=False)
with mock.patch.object(port_obj.IPAllocation, 'create'):
self.assertRaises(db_exc.DBDeadlock,
mocks['ipam'].update_port_with_ips,
context,
None,
db_port,
new_port,
mock.Mock())
mocks['ipam']._ipam_deallocate_ips.assert_called_once_with(
context, mocks['driver'], db_port,
changes.add, revert_on_fail=False)
mocks['ipam']._ipam_allocate_ips.assert_called_once_with(
context, mocks['driver'], db_port,
changes.remove, revert_on_fail=False)

View File

@ -31,7 +31,6 @@ from neutron.db import api as db_api
from neutron.db import l3_db
from neutron.db import l3_gwmode_db
from neutron.db.models import l3 as l3_models
from neutron.db import models_v2
from neutron.extensions import l3
from neutron.extensions import l3_ext_gw_mode
from neutron.objects import network as net_obj
@ -200,7 +199,7 @@ class TestL3GwModeMixin(testlib_api.SqlTestCase):
status=constants.PORT_STATUS_ACTIVE,
mac_address=netaddr.EUI(FAKE_ROUTER_PORT_MAC),
network_id=self.int_net_id)
self.router_port_ip_info = models_v2.IPAllocation(
self.router_port_ip_info = port_obj.IPAllocation(self.context,
port_id=self.router_port.id,
network_id=self.int_net.id,
subnet_id=self.int_sub_id,
@ -208,7 +207,7 @@ class TestL3GwModeMixin(testlib_api.SqlTestCase):
self.int_net.create()
self.int_sub.create()
self.router_port.create()
self.context.session.add(self.router_port_ip_info)
self.router_port_ip_info.create()
self.context.session.flush()
self.fip_int_port = port_obj.Port(
self.context,
@ -220,7 +219,7 @@ class TestL3GwModeMixin(testlib_api.SqlTestCase):
status=constants.PORT_STATUS_ACTIVE,
mac_address=netaddr.EUI(FAKE_FIP_INT_PORT_MAC),
network_id=self.int_net_id)
self.fip_int_ip_info = models_v2.IPAllocation(
self.fip_int_ip_info = port_obj.IPAllocation(self.context,
port_id=self.fip_int_port.id,
network_id=self.int_net.id,
subnet_id=self.int_sub_id,
@ -234,7 +233,7 @@ class TestL3GwModeMixin(testlib_api.SqlTestCase):
fixed_ip_address=None,
router_id=None)
self.fip_int_port.create()
self.context.session.add(self.fip_int_ip_info)
self.fip_int_ip_info.create()
self.context.session.add(self.fip)
self.context.session.flush()
self.context.session.expire_all()

View File

@ -20,6 +20,7 @@ from neutron_lib.plugins import directory
from oslo_utils import uuidutils
from neutron.db import models_v2
from neutron.objects import ports as port_obj
from neutron.plugins.ml2 import config
from neutron.tests.unit.plugins.ml2 import test_plugin
@ -62,9 +63,12 @@ class TestRevisionPlugin(test_plugin.Ml2PluginV2TestCase):
rp = directory.get_plugin('revision_plugin')
with self.port():
with self.ctx.session.begin():
ipal_obj = self.ctx.session.query(models_v2.IPAllocation).one()
ipal_objs = port_obj.IPAllocation.get_objects(self.ctx)
if not ipal_objs:
raise Exception("No IP allocations available.")
ipal_obj = ipal_objs[0]
# load port into our session
port_obj = self.ctx.session.query(models_v2.Port).one()
port = self.ctx.session.query(models_v2.Port).one()
# simulate concurrent delete in another session
other_ctx = nctx.get_admin_context()
other_ctx.session.delete(
@ -72,7 +76,7 @@ class TestRevisionPlugin(test_plugin.Ml2PluginV2TestCase):
)
# expire the port so the revision bumping code will trigger a
# lookup on its attributes and encounter an ObjectDeletedError
self.ctx.session.expire(port_obj)
self.ctx.session.expire(port)
rp._bump_related_revisions(self.ctx.session, ipal_obj)
def test_port_name_update_revises(self):