Merge "Eliminate a race condition on instance deletes."
This commit is contained in:
@@ -87,6 +87,32 @@ class NoMoreTargets(exception.NovaException):
|
|||||||
###################
|
###################
|
||||||
|
|
||||||
|
|
||||||
|
def constraint(**conditions):
|
||||||
|
"""Return a constraint object suitable for use with some updates."""
|
||||||
|
return IMPL.constraint(**conditions)
|
||||||
|
|
||||||
|
|
||||||
|
def equal_any(*values):
|
||||||
|
"""Return an equality condition object suitable for use in a constraint.
|
||||||
|
|
||||||
|
Equal_any conditions require that a model object's attribute equal any
|
||||||
|
one of the given values.
|
||||||
|
"""
|
||||||
|
return IMPL.equal_any(*values)
|
||||||
|
|
||||||
|
|
||||||
|
def not_equal(*values):
|
||||||
|
"""Return an inequality condition object suitable for use in a constraint.
|
||||||
|
|
||||||
|
Not_equal conditions require that a model object's attribute differs from
|
||||||
|
all of the given values.
|
||||||
|
"""
|
||||||
|
return IMPL.not_equal(*values)
|
||||||
|
|
||||||
|
|
||||||
|
###################
|
||||||
|
|
||||||
|
|
||||||
def service_destroy(context, instance_id):
|
def service_destroy(context, instance_id):
|
||||||
"""Destroy the service or raise if it does not exist."""
|
"""Destroy the service or raise if it does not exist."""
|
||||||
return IMPL.service_destroy(context, instance_id)
|
return IMPL.service_destroy(context, instance_id)
|
||||||
@@ -527,9 +553,9 @@ def instance_data_get_for_project(context, project_id, session=None):
|
|||||||
session=session)
|
session=session)
|
||||||
|
|
||||||
|
|
||||||
def instance_destroy(context, instance_id):
|
def instance_destroy(context, instance_id, constraint=None):
|
||||||
"""Destroy the instance or raise if it does not exist."""
|
"""Destroy the instance or raise if it does not exist."""
|
||||||
return IMPL.instance_destroy(context, instance_id)
|
return IMPL.instance_destroy(context, instance_id, constraint)
|
||||||
|
|
||||||
|
|
||||||
def instance_get_by_uuid(context, uuid):
|
def instance_get_by_uuid(context, uuid):
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ from sqlalchemy.orm import joinedload_all
|
|||||||
from sqlalchemy.sql.expression import asc
|
from sqlalchemy.sql.expression import asc
|
||||||
from sqlalchemy.sql.expression import desc
|
from sqlalchemy.sql.expression import desc
|
||||||
from sqlalchemy.sql.expression import literal_column
|
from sqlalchemy.sql.expression import literal_column
|
||||||
|
from sqlalchemy.sql.expression import or_
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
@@ -263,6 +264,52 @@ def exact_filter(query, model, filters, legal_keys):
|
|||||||
###################
|
###################
|
||||||
|
|
||||||
|
|
||||||
|
def constraint(**conditions):
|
||||||
|
return Constraint(conditions)
|
||||||
|
|
||||||
|
|
||||||
|
def equal_any(*values):
|
||||||
|
return EqualityCondition(values)
|
||||||
|
|
||||||
|
|
||||||
|
def not_equal(*values):
|
||||||
|
return InequalityCondition(values)
|
||||||
|
|
||||||
|
|
||||||
|
class Constraint(object):
|
||||||
|
|
||||||
|
def __init__(self, conditions):
|
||||||
|
self.conditions = conditions
|
||||||
|
|
||||||
|
def apply(self, model, query):
|
||||||
|
clauses = []
|
||||||
|
for key, condition in self.conditions.iteritems():
|
||||||
|
for clause in condition.clauses(getattr(model, key)):
|
||||||
|
query = query.filter(clause)
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
class EqualityCondition(object):
|
||||||
|
|
||||||
|
def __init__(self, values):
|
||||||
|
self.values = values
|
||||||
|
|
||||||
|
def clauses(self, field):
|
||||||
|
return or_([field == value for value in self.values])
|
||||||
|
|
||||||
|
|
||||||
|
class InequalityCondition(object):
|
||||||
|
|
||||||
|
def __init__(self, values):
|
||||||
|
self.values = values
|
||||||
|
|
||||||
|
def clauses(self, field):
|
||||||
|
return [field != value for value in self.values]
|
||||||
|
|
||||||
|
|
||||||
|
###################
|
||||||
|
|
||||||
|
|
||||||
@require_admin_context
|
@require_admin_context
|
||||||
def service_destroy(context, service_id):
|
def service_destroy(context, service_id):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
@@ -1311,7 +1358,7 @@ def instance_data_get_for_project(context, project_id, session=None):
|
|||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def instance_destroy(context, instance_id):
|
def instance_destroy(context, instance_id, constraint=None):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
with session.begin():
|
with session.begin():
|
||||||
if utils.is_uuid_like(instance_id):
|
if utils.is_uuid_like(instance_id):
|
||||||
@@ -1321,11 +1368,14 @@ def instance_destroy(context, instance_id):
|
|||||||
else:
|
else:
|
||||||
instance_ref = instance_get(context, instance_id,
|
instance_ref = instance_get(context, instance_id,
|
||||||
session=session)
|
session=session)
|
||||||
session.query(models.Instance).\
|
query = session.query(models.Instance).filter_by(id=instance_id)
|
||||||
filter_by(id=instance_id).\
|
if constraint is not None:
|
||||||
update({'deleted': True,
|
query = constraint.apply(models.Instance, query)
|
||||||
'deleted_at': utils.utcnow(),
|
count = query.update({'deleted': True,
|
||||||
'updated_at': literal_column('updated_at')})
|
'deleted_at': utils.utcnow(),
|
||||||
|
'updated_at': literal_column('updated_at')})
|
||||||
|
if count == 0:
|
||||||
|
raise exception.ConstraintNotMet()
|
||||||
session.query(models.SecurityGroupInstanceAssociation).\
|
session.query(models.SecurityGroupInstanceAssociation).\
|
||||||
filter_by(instance_id=instance_id).\
|
filter_by(instance_id=instance_id).\
|
||||||
update({'deleted': True,
|
update({'deleted': True,
|
||||||
|
|||||||
Reference in New Issue
Block a user