From: Clint Byrum Date: Thu, 5 Sep 2013 22:02:32 +0000 (-0700) Subject: Add methods to help calculate a stack's resources X-Git-Tag: 2014.1~42^2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=8f528704e154c9927c7f2bcd6bc25ea3fb100ae4;p=openstack-build%2Fheat-build.git Add methods to help calculate a stack's resources In order to limit a stack's size in future changes, we need to know how many resources are already in the stack. We also need to be able to find the root stack object of a nested stack. Change-Id: Ib848bcd2e10d02dffc30dce45a2675a9f718fa7d Related-Bug: #1215100 --- diff --git a/heat/engine/parser.py b/heat/engine/parser.py index 492d2c3b..b2e15fd4 100644 --- a/heat/engine/parser.py +++ b/heat/engine/parser.py @@ -109,6 +109,26 @@ class Stack(object): self.dependencies = self._get_dependencies(self.resources.itervalues()) + @property + def root_stack(self): + ''' + Return the root stack if this is nested (otherwise return self). + ''' + if (self.parent_resource and self.parent_resource.stack): + return self.parent_resource.stack.root_stack + return self + + def total_resources(self): + ''' + Total number of resources in a stack, including nested stacks below. + ''' + total = 0 + for res in iter(self.resources.values()): + if hasattr(res, 'nested') and res.nested(): + total += res.nested().total_resources() + total += 1 + return total + def _set_param_stackid(self): ''' Update self.parameters with the current ARN which is then provided diff --git a/heat/tests/test_parser.py b/heat/tests/test_parser.py index a9c0908f..552143ea 100644 --- a/heat/tests/test_parser.py +++ b/heat/tests/test_parser.py @@ -18,6 +18,7 @@ import time from heat.engine import environment from heat.common import exception from heat.common import template_format +from heat.common import urlfetch from heat.engine import clients from heat.engine import resource from heat.engine import parser @@ -519,6 +520,8 @@ Mappings: parent_resource.metadata = '{"foo": "bar"}' parent_resource.t = {'DeletionPolicy': 'Retain', 'UpdatePolicy': '{"foo": "bar"}'} + parent_resource.stack = parser.Stack(self.ctx, 'toplevel_stack', + parser.Template({})) stack = parser.Stack(self.ctx, 'test_stack', parser.Template({}), parent_resource=parent_resource) @@ -548,6 +551,8 @@ Mappings: parent_resource = DummyClass() parent_resource.metadata = '{"foo": "bar"}' parent_resource.t = {} + parent_resource.stack = parser.Stack(self.ctx, 'toplevel_stack', + parser.Template({})) stack = parser.Stack(self.ctx, 'test_stack', parser.Template({}), parent_resource=parent_resource) @@ -626,6 +631,54 @@ class StackTest(HeatTestCase): self.assertRaises(exception.NotFound, parser.Stack.load, None, -1) + def test_total_resources_empty(self): + stack = parser.Stack(self.ctx, 'test_stack', parser.Template({}), + status_reason='flimflam') + self.assertEqual(0, stack.total_resources()) + + def test_total_resources_generic(self): + tpl = {'Resources': + {'A': {'Type': 'GenericResourceType'}}} + stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl), + status_reason='blarg') + self.assertEqual(1, stack.total_resources()) + + def _setup_nested(self, name): + nested_tpl = ('{"Resources":{' + '"A": {"Type": "GenericResourceType"},' + '"B": {"Type": "GenericResourceType"}}}') + tpl = {'Resources': + {'A': {'Type': 'AWS::CloudFormation::Stack', + 'Properties': + {'TemplateURL': 'http://server.test/nested.json'}}, + 'B': {'Type': 'GenericResourceType'}}} + self.m.StubOutWithMock(urlfetch, 'get') + urlfetch.get('http://server.test/nested.json').AndReturn(nested_tpl) + self.m.ReplayAll() + self.stack = parser.Stack(self.ctx, 'test_stack', parser.Template(tpl), + status_reason=name) + self.stack.store() + self.stack.create() + + @utils.stack_delete_after + def test_total_resources_nested(self): + self._setup_nested('zyzzyx') + self.assertEqual(4, self.stack.total_resources()) + self.assertNotEqual(None, self.stack.resources['A'].nested()) + self.assertEqual( + 2, self.stack.resources['A'].nested().total_resources()) + self.assertEqual( + 4, + self.stack.resources['A'].nested().root_stack.total_resources()) + + @utils.stack_delete_after + def test_root_stack(self): + self._setup_nested('toor') + self.assertEqual(self.stack, self.stack.root_stack) + self.assertNotEqual(None, self.stack.resources['A'].nested()) + self.assertEqual( + self.stack, self.stack.resources['A'].nested().root_stack) + @utils.stack_delete_after def test_load_parent_resource(self): self.stack = parser.Stack(self.ctx, 'load_parent_resource',