Skip to content

Commit

Permalink
Lock tables for update on allocation/deletion
Browse files Browse the repository at this point in the history
Allocating, creating and deleting port might happen
in parallel and we need to make sure we don't
assign same IP to multiple different requests.

Added treatment for vlan tags and tunnel ID's

Fixes: bug #1110807

Change-Id: Idbb04d3ce6eacd308b05536f1942a35a0792199e
  • Loading branch information
ivoks authored and Gary Kotton committed Mar 8, 2013
1 parent f94b149 commit 5a2ef81
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 9 deletions.
22 changes: 14 additions & 8 deletions quantum/db/db_base_plugin_v2.py
Expand Up @@ -255,7 +255,8 @@ def _check_unique_mac(context, network_id, mac_address):

@staticmethod
def _hold_ip(context, network_id, subnet_id, port_id, ip_address):
alloc_qry = context.session.query(models_v2.IPAllocation)
alloc_qry = context.session.query(
models_v2.IPAllocation).with_lockmode('update')
allocated = alloc_qry.filter_by(network_id=network_id,
port_id=port_id,
ip_address=ip_address,
Expand All @@ -278,7 +279,8 @@ def _recycle_expired_ip_allocations(context, network_id):
if network_id in getattr(context, '_recycled_networks', set()):
return

expired_qry = context.session.query(models_v2.IPAllocation)
expired_qry = context.session.query(
models_v2.IPAllocation).with_lockmode('update')
expired_qry = expired_qry.filter_by(network_id=network_id,
port_id=None)
expired_qry = expired_qry.filter(
Expand All @@ -301,7 +303,8 @@ def _recycle_ip(context, network_id, subnet_id, ip_address):
subnet.
"""
# Grab all allocation pools for the subnet
pool_qry = context.session.query(models_v2.IPAllocationPool)
pool_qry = context.session.query(
models_v2.IPAllocationPool).with_lockmode('update')
allocation_pools = pool_qry.filter_by(subnet_id=subnet_id).all()
# Find the allocation pool for the IP to recycle
pool_id = None
Expand All @@ -322,7 +325,8 @@ def _recycle_ip(context, network_id, subnet_id, ip_address):
# If 1 of the above holds true then the specific entry will be
# modified. If both hold true then the two ranges will be merged.
# If there are no entries then a single entry will be added.
range_qry = context.session.query(models_v2.IPAvailabilityRange)
range_qry = context.session.query(
models_v2.IPAvailabilityRange).with_lockmode('update')
ip_first = str(netaddr.IPAddress(ip_address) + 1)
ip_last = str(netaddr.IPAddress(ip_address) - 1)
LOG.debug("Recycle %s", ip_address)
Expand Down Expand Up @@ -401,7 +405,8 @@ def _delete_ip_allocation(context, network_id, subnet_id, ip_address):
# Delete the IP address from the IPAllocate table
LOG.debug("Delete allocated IP %s (%s/%s)", ip_address,
network_id, subnet_id)
alloc_qry = context.session.query(models_v2.IPAllocation)
alloc_qry = context.session.query(
models_v2.IPAllocation).with_lockmode('update')
allocated = alloc_qry.filter_by(network_id=network_id,
ip_address=ip_address,
subnet_id=subnet_id).delete()
Expand All @@ -415,7 +420,7 @@ def _generate_ip(context, network_id, subnets):
"""
range_qry = context.session.query(
models_v2.IPAvailabilityRange).join(
models_v2.IPAllocationPool)
models_v2.IPAllocationPool).with_lockmode('update')
for subnet in subnets:
range = range_qry.filter_by(subnet_id=subnet['id']).first()
if not range:
Expand Down Expand Up @@ -443,7 +448,7 @@ def _allocate_specific_ip(context, subnet_id, ip_address):
range_qry = context.session.query(
models_v2.IPAvailabilityRange,
models_v2.IPAllocationPool).join(
models_v2.IPAllocationPool)
models_v2.IPAllocationPool).with_lockmode('update')
results = range_qry.filter_by(subnet_id=subnet_id).all()
for (range, pool) in results:
first = int(netaddr.IPAddress(range['first_ip']))
Expand Down Expand Up @@ -1260,7 +1265,8 @@ def delete_port(self, context, id):
def _delete_port(self, context, id):
port = self._get_port(context, id)

allocated_qry = context.session.query(models_v2.IPAllocation)
allocated_qry = context.session.query(
models_v2.IPAllocation).with_lockmode('update')
# recycle all of the IP's
allocated = allocated_qry.filter_by(port_id=id).all()
if allocated:
Expand Down
3 changes: 3 additions & 0 deletions quantum/plugins/linuxbridge/db/l2network_db_v2.py
Expand Up @@ -110,6 +110,7 @@ def reserve_network(session):
with session.begin(subtransactions=True):
state = (session.query(l2network_models_v2.NetworkState).
filter_by(allocated=False).
with_lockmode('update').
first())
if not state:
raise q_exc.NoNetworkAvailable()
Expand All @@ -125,6 +126,7 @@ def reserve_specific_network(session, physical_network, vlan_id):
state = (session.query(l2network_models_v2.NetworkState).
filter_by(physical_network=physical_network,
vlan_id=vlan_id).
with_lockmode('update').
one())
if state.allocated:
if vlan_id == constants.FLAT_VLAN_ID:
Expand All @@ -150,6 +152,7 @@ def release_network(session, physical_network, vlan_id, network_vlan_ranges):
state = (session.query(l2network_models_v2.NetworkState).
filter_by(physical_network=physical_network,
vlan_id=vlan_id).
with_lockmode('update').
one())
state.allocated = False
inside = False
Expand Down
9 changes: 8 additions & 1 deletion quantum/plugins/openvswitch/ovs_db_v2.py
Expand Up @@ -129,6 +129,7 @@ def reserve_vlan(session):
with session.begin(subtransactions=True):
alloc = (session.query(ovs_models_v2.VlanAllocation).
filter_by(allocated=False).
with_lockmode('update').
first())
if alloc:
LOG.debug("reserving vlan %s on physical network %s from pool" %
Expand All @@ -144,6 +145,7 @@ def reserve_specific_vlan(session, physical_network, vlan_id):
alloc = (session.query(ovs_models_v2.VlanAllocation).
filter_by(physical_network=physical_network,
vlan_id=vlan_id).
with_lockmode('update').
one())
if alloc.allocated:
if vlan_id == constants.FLAT_VLAN_ID:
Expand All @@ -169,6 +171,7 @@ def release_vlan(session, physical_network, vlan_id, network_vlan_ranges):
alloc = (session.query(ovs_models_v2.VlanAllocation).
filter_by(physical_network=physical_network,
vlan_id=vlan_id).
with_lockmode('update').
one())
alloc.allocated = False
inside = False
Expand Down Expand Up @@ -227,6 +230,7 @@ def get_tunnel_allocation(tunnel_id):
try:
alloc = (session.query(ovs_models_v2.TunnelAllocation).
filter_by(tunnel_id=tunnel_id).
with_lockmode('update').
one())
return alloc
except exc.NoResultFound:
Expand All @@ -237,6 +241,7 @@ def reserve_tunnel(session):
with session.begin(subtransactions=True):
alloc = (session.query(ovs_models_v2.TunnelAllocation).
filter_by(allocated=False).
with_lockmode('update').
first())
if alloc:
LOG.debug("reserving tunnel %s from pool" % alloc.tunnel_id)
Expand All @@ -250,6 +255,7 @@ def reserve_specific_tunnel(session, tunnel_id):
try:
alloc = (session.query(ovs_models_v2.TunnelAllocation).
filter_by(tunnel_id=tunnel_id).
with_lockmode('update').
one())
if alloc.allocated:
raise q_exc.TunnelIdInUse(tunnel_id=tunnel_id)
Expand All @@ -267,6 +273,7 @@ def release_tunnel(session, tunnel_id, tunnel_id_ranges):
try:
alloc = (session.query(ovs_models_v2.TunnelAllocation).
filter_by(tunnel_id=tunnel_id).
with_lockmode('update').
one())
alloc.allocated = False
inside = False
Expand Down Expand Up @@ -330,7 +337,7 @@ def add_tunnel_endpoint(ip):
session = db.get_session()
try:
tunnel = (session.query(ovs_models_v2.TunnelEndpoint).
filter_by(ip_address=ip).one())
filter_by(ip_address=ip).with_lockmode('update').one())
except exc.NoResultFound:
id = _generate_tunnel_id(session)
tunnel = ovs_models_v2.TunnelEndpoint(ip, id)
Expand Down

0 comments on commit 5a2ef81

Please sign in to comment.