Merge "Integration of IPAllocation"
This commit is contained in:
commit
3d4aee57db
@ -35,6 +35,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
|
||||
|
||||
@ -105,12 +106,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
|
||||
@ -122,16 +120,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'],
|
||||
|
@ -60,6 +60,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
|
||||
|
||||
|
||||
@ -554,6 +555,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")
|
||||
@ -917,24 +919,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)
|
||||
|
@ -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 "
|
||||
|
@ -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):
|
||||
|
@ -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'],
|
||||
|
@ -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'),
|
||||
|
@ -31,6 +31,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
|
||||
|
||||
@ -94,7 +95,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 = (
|
||||
@ -672,7 +673,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(
|
||||
@ -696,9 +697,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()
|
||||
|
||||
@ -782,21 +784,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)
|
||||
@ -808,16 +811,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)
|
||||
|
@ -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()
|
||||
|
@ -24,6 +24,7 @@ from webob import exc
|
||||
|
||||
from neutron.db import api as db_api
|
||||
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
|
||||
|
||||
@ -66,9 +67,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(
|
||||
@ -76,7 +80,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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user