Convergence: DB api to get all active resources of stack
This patch reverts change I6a212da19a774239f014163774e75fe11dfe272c and adds new DB api resource_get_all_active_by_stack to be used by convergence. The new DB api will be used while generating graph for convergence stack and will fetch all resources of stack from DB excluding the DELETE COMPLETE resources, if any. Change-Id: I303ef2c9b5b6a0a49253425c00565c8981cc6825 Partial-Bug: #1528560
This commit is contained in:
parent
f5e7a319cb
commit
9cf021ac7e
|
@ -121,8 +121,12 @@ def resource_exchange_stacks(context, resource_id1, resource_id2):
|
||||||
return IMPL.resource_exchange_stacks(context, resource_id1, resource_id2)
|
return IMPL.resource_exchange_stacks(context, resource_id1, resource_id2)
|
||||||
|
|
||||||
|
|
||||||
def resource_get_all_by_stack(context, stack_id, key_id=False, filters=None):
|
def resource_get_all_by_stack(context, stack_id, filters=None):
|
||||||
return IMPL.resource_get_all_by_stack(context, stack_id, key_id, filters)
|
return IMPL.resource_get_all_by_stack(context, stack_id, filters)
|
||||||
|
|
||||||
|
|
||||||
|
def resource_get_all_active_by_stack(context, stack_id):
|
||||||
|
return IMPL.resource_get_all_active_by_stack(context, stack_id)
|
||||||
|
|
||||||
|
|
||||||
def resource_get_by_name_and_stack(context, resource_name, stack_id):
|
def resource_get_by_name_and_stack(context, resource_name, stack_id):
|
||||||
|
|
|
@ -341,7 +341,7 @@ def resource_create(context, values):
|
||||||
return resource_ref
|
return resource_ref
|
||||||
|
|
||||||
|
|
||||||
def resource_get_all_by_stack(context, stack_id, key_id=False, filters=None):
|
def resource_get_all_by_stack(context, stack_id, filters=None):
|
||||||
query = model_query(
|
query = model_query(
|
||||||
context, models.Resource
|
context, models.Resource
|
||||||
).filter_by(
|
).filter_by(
|
||||||
|
@ -354,12 +354,25 @@ def resource_get_all_by_stack(context, stack_id, key_id=False, filters=None):
|
||||||
if not results:
|
if not results:
|
||||||
raise exception.NotFound(_("no resources for stack_id %s were found")
|
raise exception.NotFound(_("no resources for stack_id %s were found")
|
||||||
% stack_id)
|
% stack_id)
|
||||||
if key_id:
|
|
||||||
return dict((res.id, res) for res in results)
|
|
||||||
else:
|
|
||||||
return dict((res.name, res) for res in results)
|
return dict((res.name, res) for res in results)
|
||||||
|
|
||||||
|
|
||||||
|
def resource_get_all_active_by_stack(context, stack_id):
|
||||||
|
filters = {'stack_id': stack_id, 'action': 'DELETE', 'status': 'COMPLETE'}
|
||||||
|
subquery = model_query(context, models.Resource.id).filter_by(**filters)
|
||||||
|
|
||||||
|
results = model_query(context, models.Resource).filter_by(
|
||||||
|
stack_id=stack_id).filter(
|
||||||
|
models.Resource.id.notin_(subquery.as_scalar())
|
||||||
|
).options(orm.joinedload("data")).all()
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
raise exception.NotFound(_("no active resources for stack_id %s were"
|
||||||
|
" found") % stack_id)
|
||||||
|
return dict((res.id, res) for res in results)
|
||||||
|
|
||||||
|
|
||||||
def stack_get_by_name_and_owner_id(context, stack_name, owner_id):
|
def stack_get_by_name_and_owner_id(context, stack_name, owner_id):
|
||||||
query = soft_delete_aware_query(
|
query = soft_delete_aware_query(
|
||||||
context, models.Stack
|
context, models.Stack
|
||||||
|
|
|
@ -314,7 +314,7 @@ class Stack(collections.Mapping):
|
||||||
def _find_filtered_resources(self, filters):
|
def _find_filtered_resources(self, filters):
|
||||||
for rsc in six.itervalues(
|
for rsc in six.itervalues(
|
||||||
resource_objects.Resource.get_all_by_stack(
|
resource_objects.Resource.get_all_by_stack(
|
||||||
self.context, self.id, True, filters)):
|
self.context, self.id, filters)):
|
||||||
yield self.resources[rsc.name]
|
yield self.resources[rsc.name]
|
||||||
|
|
||||||
def iter_resources(self, nested_depth=0, filters=None):
|
def iter_resources(self, nested_depth=0, filters=None):
|
||||||
|
@ -338,10 +338,10 @@ class Stack(collections.Mapping):
|
||||||
for nested_res in nested_stack.iter_resources(nested_depth - 1):
|
for nested_res in nested_stack.iter_resources(nested_depth - 1):
|
||||||
yield nested_res
|
yield nested_res
|
||||||
|
|
||||||
def _db_resources_get(self, key_id=False):
|
def db_active_resources_get(self):
|
||||||
try:
|
try:
|
||||||
return resource_objects.Resource.get_all_by_stack(
|
return resource_objects.Resource.get_all_active_by_stack(
|
||||||
self.context, self.id, key_id)
|
self.context, self.id)
|
||||||
except exception.NotFound:
|
except exception.NotFound:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -349,9 +349,13 @@ class Stack(collections.Mapping):
|
||||||
if not self.id:
|
if not self.id:
|
||||||
return None
|
return None
|
||||||
if self._db_resources is None:
|
if self._db_resources is None:
|
||||||
self._db_resources = self._db_resources_get()
|
try:
|
||||||
|
_db_resources = resource_objects.Resource.get_all_by_stack(
|
||||||
return self._db_resources.get(name) if self._db_resources else None
|
self.context, self.id)
|
||||||
|
self._db_resources = _db_resources
|
||||||
|
except exception.NotFound:
|
||||||
|
return None
|
||||||
|
return self._db_resources.get(name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dependencies(self):
|
def dependencies(self):
|
||||||
|
@ -1276,7 +1280,7 @@ class Stack(collections.Mapping):
|
||||||
return candidate
|
return candidate
|
||||||
|
|
||||||
def _update_or_store_resources(self):
|
def _update_or_store_resources(self):
|
||||||
self.ext_rsrcs_db = self._db_resources_get(key_id=True)
|
self.ext_rsrcs_db = self.db_active_resources_get()
|
||||||
|
|
||||||
curr_name_translated_dep = self.dependencies.translate(lambda res:
|
curr_name_translated_dep = self.dependencies.translate(lambda res:
|
||||||
res.name)
|
res.name)
|
||||||
|
|
|
@ -135,16 +135,28 @@ class Resource(
|
||||||
resource_id2)
|
resource_id2)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_all_by_stack(cls, context, stack_id, key_id=False, filters=None):
|
def get_all_by_stack(cls, context, stack_id, filters=None):
|
||||||
resources_db = db_api.resource_get_all_by_stack(context,
|
resources_db = db_api.resource_get_all_by_stack(context, stack_id,
|
||||||
stack_id, key_id,
|
|
||||||
filters)
|
filters)
|
||||||
resources = [
|
resources = [
|
||||||
(
|
(
|
||||||
resource_key,
|
resource_name,
|
||||||
cls._from_db_object(cls(context), context, resource_db)
|
cls._from_db_object(cls(context), context, resource_db)
|
||||||
)
|
)
|
||||||
for resource_key, resource_db in six.iteritems(resources_db)
|
for resource_name, resource_db in six.iteritems(resources_db)
|
||||||
|
]
|
||||||
|
return dict(resources)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all_active_by_stack(cls, context, stack_id):
|
||||||
|
resources_db = db_api.resource_get_all_active_by_stack(context,
|
||||||
|
stack_id)
|
||||||
|
resources = [
|
||||||
|
(
|
||||||
|
resource_id,
|
||||||
|
cls._from_db_object(cls(context), context, resource_db)
|
||||||
|
)
|
||||||
|
for resource_id, resource_db in six.iteritems(resources_db)
|
||||||
]
|
]
|
||||||
return dict(resources)
|
return dict(resources)
|
||||||
|
|
||||||
|
|
|
@ -2256,6 +2256,28 @@ class DBAPIResourceTest(common.HeatTestCase):
|
||||||
self.assertRaises(exception.NotFound, db_api.resource_get_all_by_stack,
|
self.assertRaises(exception.NotFound, db_api.resource_get_all_by_stack,
|
||||||
self.ctx, self.stack2.id)
|
self.ctx, self.stack2.id)
|
||||||
|
|
||||||
|
def test_resource_get_all_active_by_stack(self):
|
||||||
|
values = [
|
||||||
|
{'name': 'res1', 'action': rsrc.Resource.DELETE,
|
||||||
|
'status': rsrc.Resource.COMPLETE},
|
||||||
|
{'name': 'res2', 'action': rsrc.Resource.DELETE,
|
||||||
|
'status': rsrc.Resource.IN_PROGRESS},
|
||||||
|
{'name': 'res3', 'action': rsrc.Resource.UPDATE,
|
||||||
|
'status': rsrc.Resource.IN_PROGRESS},
|
||||||
|
{'name': 'res4', 'action': rsrc.Resource.UPDATE,
|
||||||
|
'status': rsrc.Resource.COMPLETE},
|
||||||
|
{'name': 'res5', 'action': rsrc.Resource.INIT,
|
||||||
|
'status': rsrc.Resource.COMPLETE},
|
||||||
|
{'name': 'res6'},
|
||||||
|
]
|
||||||
|
[create_resource(self.ctx, self.stack, **val) for val in values]
|
||||||
|
|
||||||
|
resources = db_api.resource_get_all_active_by_stack(self.ctx,
|
||||||
|
self.stack.id)
|
||||||
|
self.assertEqual(5, len(resources))
|
||||||
|
for rsrc_id, res in resources.items():
|
||||||
|
self.assertIn(res.name, ['res2', 'res3', 'res4', 'res5', 'res6'])
|
||||||
|
|
||||||
|
|
||||||
class DBAPIStackLockTest(common.HeatTestCase):
|
class DBAPIStackLockTest(common.HeatTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -131,7 +131,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
is_update, None))
|
is_update, None))
|
||||||
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
||||||
|
|
||||||
def _mock_convg_db_update_requires(self, key_id=False):
|
def _mock_convg_db_update_requires(self):
|
||||||
"""Updates requires column of resources.
|
"""Updates requires column of resources.
|
||||||
|
|
||||||
Required for testing the generation of convergence dependency graph
|
Required for testing the generation of convergence dependency graph
|
||||||
|
@ -144,8 +144,8 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
rsrc_id, is_update))
|
rsrc_id, is_update))
|
||||||
requires[rsrc_id] = list({id for id, is_update in reqs})
|
requires[rsrc_id] = list({id for id, is_update in reqs})
|
||||||
|
|
||||||
rsrcs_db = resource_objects.Resource.get_all_by_stack(
|
rsrcs_db = resource_objects.Resource.get_all_active_by_stack(
|
||||||
self.stack.context, self.stack.id, key_id=key_id)
|
self.stack.context, self.stack.id)
|
||||||
|
|
||||||
for rsrc_id, rsrc in rsrcs_db.items():
|
for rsrc_id, rsrc in rsrcs_db.items():
|
||||||
if rsrc.id in requires:
|
if rsrc.id in requires:
|
||||||
|
@ -172,7 +172,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
# rsrc.requires. Mock the same behavior here.
|
# rsrc.requires. Mock the same behavior here.
|
||||||
self.stack = stack
|
self.stack = stack
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
parser.Stack, '_db_resources_get',
|
parser.Stack, 'db_active_resources_get',
|
||||||
side_effect=self._mock_convg_db_update_requires):
|
side_effect=self._mock_convg_db_update_requires):
|
||||||
curr_stack.converge_stack(template=template2, action=stack.UPDATE)
|
curr_stack.converge_stack(template=template2, action=stack.UPDATE)
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
# rsrc.requires. Mock the same behavior here.
|
# rsrc.requires. Mock the same behavior here.
|
||||||
self.stack = stack
|
self.stack = stack
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
parser.Stack, '_db_resources_get',
|
parser.Stack, 'db_active_resources_get',
|
||||||
side_effect=self._mock_convg_db_update_requires):
|
side_effect=self._mock_convg_db_update_requires):
|
||||||
curr_stack.converge_stack(template=template2, action=stack.DELETE)
|
curr_stack.converge_stack(template=template2, action=stack.DELETE)
|
||||||
|
|
||||||
|
|
|
@ -272,7 +272,6 @@ class StackTest(common.HeatTestCase):
|
||||||
# Verify, the db query is called with expected filter
|
# Verify, the db query is called with expected filter
|
||||||
mock_db_call.assert_called_once_with(self.ctx,
|
mock_db_call.assert_called_once_with(self.ctx,
|
||||||
self.stack.id,
|
self.stack.id,
|
||||||
True,
|
|
||||||
dict(name=['A']))
|
dict(name=['A']))
|
||||||
# Make sure it returns only one resource.
|
# Make sure it returns only one resource.
|
||||||
self.assertEqual(1, len(all_resources))
|
self.assertEqual(1, len(all_resources))
|
||||||
|
|
Loading…
Reference in New Issue