Count all nested stack resources with DB operations

The count is performed in 2 parts
- A function which builds a list of all nested stacks
  by recursively calling stack_get_all_by_owner_id
- A query which counts resources which belong to the list
  of stacks

Considering this will be the basis for fixing bug #1455589 then this
approach is appropriate for backporting to stable/kilo, but master would
ideally replace this soon with a new Stack attribute that stores the
calculated resource count.

Partial-Bug: #1455589

Change-Id: Ifa2e5609fd9a6853e20037e94a5e16696bb378ee
This commit is contained in:
Steve Baker 2015-05-22 10:51:52 -07:00
parent b02805bea5
commit be41d08a44
4 changed files with 105 additions and 0 deletions

View File

@ -202,6 +202,10 @@ def stack_get_root_id(context, stack_id):
return IMPL.stack_get_root_id(context, stack_id)
def stack_count_total_resources(context, stack_id):
return IMPL.stack_count_total_resources(context, stack_id)
def user_creds_create(context):
return IMPL.user_creds_create(context)

View File

@ -583,6 +583,29 @@ def stack_get_root_id(context, stack_id):
return s.id
def stack_count_total_resources(context, stack_id):
# start with a stack_get to confirm the context can access the stack
if stack_id is None or stack_get(context, stack_id) is None:
return 0
def nested_stack_ids(sid):
yield sid
for child in stack_get_all_by_owner_id(context, sid):
for stack in nested_stack_ids(child.id):
yield stack
stack_ids = list(nested_stack_ids(stack_id))
# count all resources which belong to the stacks
results = model_query(
context, models.Resource
).filter(
models.Resource.stack_id.in_(stack_ids)
).count()
return results
def user_creds_create(context):
values = context.to_dict()
user_creds_ref = models.UserCreds()

View File

@ -138,6 +138,10 @@ class Stack(
def count_all(cls, context, **kwargs):
return db_api.stack_count_all(context, **kwargs)
@classmethod
def count_total_resources(cls, context, stack_id):
return db_api.stack_count_total_resources(context, stack_id)
@classmethod
def create(cls, context, values):
return db_api.stack_create(context, values)

View File

@ -1820,6 +1820,80 @@ class DBAPIStackTest(common.HeatTestCase):
self.assertEqual(root.id, db_api.stack_get_root_id(
self.ctx, child_1.id))
def test_stack_count_total_resources(self):
def add_resources(stack, count):
for i in range(count):
create_resource(
self.ctx, stack, name='%s-%s' % (stack.name, i))
root = create_stack(self.ctx, self.template, self.user_creds,
name='root stack')
# stack with 3 children
s_1 = create_stack(self.ctx, self.template, self.user_creds,
name='s_1', owner_id=root.id)
s_1_1 = create_stack(self.ctx, self.template, self.user_creds,
name='s_1_1', owner_id=s_1.id)
s_1_2 = create_stack(self.ctx, self.template, self.user_creds,
name='s_1_2', owner_id=s_1.id)
s_1_3 = create_stack(self.ctx, self.template, self.user_creds,
name='s_1_3', owner_id=s_1.id)
# stacks 4 ancestors deep
s_2 = create_stack(self.ctx, self.template, self.user_creds,
name='s_2', owner_id=root.id)
s_2_1 = create_stack(self.ctx, self.template, self.user_creds,
name='s_2_1', owner_id=s_2.id)
s_2_1_1 = create_stack(self.ctx, self.template, self.user_creds,
name='s_2_1_1', owner_id=s_2_1.id)
s_2_1_1_1 = create_stack(self.ctx, self.template, self.user_creds,
name='s_2_1_1_1', owner_id=s_2_1_1.id)
s_3 = create_stack(self.ctx, self.template, self.user_creds,
name='s_3', owner_id=root.id)
s_4 = create_stack(self.ctx, self.template, self.user_creds,
name='s_4', owner_id=root.id)
add_resources(root, 3)
add_resources(s_1, 2)
add_resources(s_1_1, 4)
add_resources(s_1_2, 5)
add_resources(s_1_3, 6)
add_resources(s_2, 1)
add_resources(s_2_1_1_1, 1)
add_resources(s_3, 4)
self.assertEqual(26, db_api.stack_count_total_resources(
self.ctx, root.id))
self.assertEqual(17, db_api.stack_count_total_resources(
self.ctx, s_1.id))
self.assertEqual(4, db_api.stack_count_total_resources(
self.ctx, s_1_1.id))
self.assertEqual(5, db_api.stack_count_total_resources(
self.ctx, s_1_2.id))
self.assertEqual(6, db_api.stack_count_total_resources(
self.ctx, s_1_3.id))
self.assertEqual(2, db_api.stack_count_total_resources(
self.ctx, s_2.id))
self.assertEqual(1, db_api.stack_count_total_resources(
self.ctx, s_2_1.id))
self.assertEqual(1, db_api.stack_count_total_resources(
self.ctx, s_2_1_1.id))
self.assertEqual(1, db_api.stack_count_total_resources(
self.ctx, s_2_1_1_1.id))
self.assertEqual(4, db_api.stack_count_total_resources(
self.ctx, s_3.id))
self.assertEqual(0, db_api.stack_count_total_resources(
self.ctx, s_4.id))
self.assertEqual(0, db_api.stack_count_total_resources(
self.ctx, 'asdf'))
self.assertEqual(0, db_api.stack_count_total_resources(
self.ctx, None))
class DBAPIResourceTest(common.HeatTestCase):
def setUp(self):