]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Add methods to help calculate a stack's resources
authorClint Byrum <clint@fewbar.com>
Thu, 5 Sep 2013 22:02:32 +0000 (15:02 -0700)
committerGerrit Code Review <review@openstack.org>
Fri, 13 Sep 2013 07:26:49 +0000 (07:26 +0000)
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

heat/engine/parser.py
heat/tests/test_parser.py

index 492d2c3bfd4ce40451a01bd45f8c8472c7a89101..b2e15fd4ebcf8eb4869c1d851f8afbf820971dde 100644 (file)
@@ -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
index a9c0908f8f442549bca59d94a788aad9da4fe0cb..552143ead5d97eec3e32814866778458a3a6f4b0 100644 (file)
@@ -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',