]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
DB: add stack status & reason, resource metadata
authorAngus Salkeld <asalkeld@redhat.com>
Wed, 20 Jun 2012 13:49:26 +0000 (23:49 +1000)
committerAngus Salkeld <asalkeld@redhat.com>
Wed, 20 Jun 2012 13:49:26 +0000 (23:49 +1000)
Change-Id: I369aa688fa9890a5484de417a9995e7f04f34ad2
Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
heat/db/sqlalchemy/migrate_repo/versions/007_resource_work.py [new file with mode: 0644]
heat/db/sqlalchemy/models.py
heat/engine/manager.py
heat/engine/parser.py
heat/engine/resources.py
heat/engine/wait_condition.py
heat/tests/test_stacks.py

diff --git a/heat/db/sqlalchemy/migrate_repo/versions/007_resource_work.py b/heat/db/sqlalchemy/migrate_repo/versions/007_resource_work.py
new file mode 100644 (file)
index 0000000..02bf553
--- /dev/null
@@ -0,0 +1,32 @@
+from sqlalchemy import *
+from migrate import *
+
+
+def upgrade(migrate_engine):
+    meta = MetaData(bind=migrate_engine)
+
+    resource = Table('resource', meta, autoload=True)
+    Column('rsrc_metadata', Text()).create(resource)
+
+    stack = Table('stack', meta, autoload=True)
+    Column('status', String(length=255,
+                            convert_unicode=False,
+                            assert_unicode=None,
+                            unicode_error=None,
+                            _warn_on_bytestring=False)).create(stack)
+    Column('status_reason', String(length=255,
+                                   convert_unicode=False,
+                                   assert_unicode=None,
+                                   unicode_error=None,
+                                   _warn_on_bytestring=False)).create(stack)
+
+
+def downgrade(migrate_engine):
+    meta = MetaData(bind=migrate_engine)
+
+    resource = Table('resource', meta, autoload=True)
+    resource.c.rsrc_metadata.drop()
+
+    stack = Table('stack', meta, autoload=True)
+    stack.c.status.drop()
+    stack.c.status_reason.drop()
index e4721d253c8fbe27d580326bad76d125a341d8a5..9d477815a4a1f55eb688d9d9cb99a823d879be07 100644 (file)
@@ -142,6 +142,8 @@ class Stack(BASE, HeatBase):
     raw_template = relationship(RawTemplate,
         backref=backref('stack'))
     username = Column(String)
+    status = Column('status', String)
+    status_reason = Column('status_reason', String)
     user_creds_id = Column(Integer, ForeignKey('user_creds.id'),
                            nullable=False)
     owner_id = Column(Integer, nullable=True)
@@ -196,6 +198,8 @@ class Resource(BASE, HeatBase):
     name = Column('name', String, nullable=False)
     nova_instance = Column('nova_instance', String)
     state_description = Column('state_description', String)
+    # odd name as "metadata" is reserved
+    rsrc_metadata = Column('rsrc_metadata', Json)
     parsed_template_id = Column(Integer, ForeignKey('parsed_template.id'),
                                  nullable=True)
     parsed_template = relationship(ParsedTemplate,
index 7b8eb94599199aef6fb7b31df3587efdb40e0fd5..06e397874b0e0201bb961e0f18e3d10512cb9db8 100644 (file)
@@ -155,7 +155,7 @@ class EngineManager(manager.Manager):
             mem['CreationTime'] = heat_utils.strtime(s.created_at)
             mem['TemplateDescription'] = ps.t.get('Description',
                                                    'No description')
-            mem['StackStatus'] = ps.t.get('stack_status', 'unknown')
+            mem['StackStatus'] = s.status
             res['stacks'].append(mem)
 
         return res
@@ -185,12 +185,11 @@ class EngineManager(manager.Manager):
             mem['TimeoutInMinutes'] = ps.t.get('Timeout', '60')
             mem['TemplateDescription'] = ps.t.get('Description',
                                                   'No description')
-            mem['StackStatus'] = ps.t.get('stack_status', 'unknown')
-            mem['StackStatusReason'] = ps.t.get('stack_status_reason',
-                                                'State changed')
+            mem['StackStatus'] = s.status
+            mem['StackStatusReason'] = s.status_reason
 
             # only show the outputs on a completely created stack
-            if ps.t['stack_status'] == ps.CREATE_COMPLETE:
+            if s.state == ps.CREATE_COMPLETE:
                 mem['Outputs'] = ps.get_outputs()
 
             res['stacks'].append(mem)
@@ -481,12 +480,11 @@ class EngineManager(manager.Manager):
         if not s:
             return ['stack', None]
 
-        template = s.raw_template.parsed_template.template
-        if not resource_id in template.get('Resources', {}):
+        r = db_api.resource_get_by_name_and_stack(None, resource_id, s.id)
+        if r is None:
             return ['resource', None]
 
-        metadata = template['Resources'][resource_id].get('Metadata', {})
-        return [None, metadata]
+        return [None, r.rsrc_metadata]
 
     def metadata_update(self, context, stack_name, resource_id, metadata):
         """
@@ -495,21 +493,14 @@ class EngineManager(manager.Manager):
         s = db_api.stack_get_by_name(None, stack_name)
         if not s:
             return ['stack', None]
-        pt_id = s.raw_template.parsed_template.id
 
-        pt = db_api.parsed_template_get(None, pt_id)
-        if not resource_id in pt.template.get('Resources', {}):
+        r = db_api.resource_get_by_name_and_stack(None, resource_id, s.id)
+        if r is None:
+            logger.warn("Resource not found %s:%s." % (stack_name,
+                                                       resource_id))
             return ['resource', None]
 
-        # TODO(shadower) deep copy of the template is required here. Without
-        # it, we directly modify parsed_template.template by assigning the new
-        # metadata. When we then call parsed_template.update_and_save, the
-        # session will detect no changes and thus not update the database.
-        # Just updating the values and calling save didn't seem to work either.
-        # There's probably an idiomatic way I'm missing right now.
-        t = deepcopy(pt.template)
-        t['Resources'][resource_id]['Metadata'] = metadata
-        pt.update_and_save({'template': t})
+        r.update_and_save({'rsrc_metadata': metadata})
         return [None, metadata]
 
     @manager.periodic_task
index 2ae68a5548441a2464f789b0418108fcd3a78196..331a942fd02740e6388d3fa5cbc0a724a824d96c 100644 (file)
@@ -178,9 +178,17 @@ class Stack(object):
                         self.parsed_template_id)
 
     def state_set(self, new_status, reason='change in resource state'):
-        self.t['stack_status'] = new_status
-        self.t['stack_status_reason'] = reason
-        self.update_parsed_template()
+        if self.id != 0:
+            stack = db_api.stack_get(self.context, self.id)
+        else:
+            stack = db_api.stack_get_by_name(self.context, self.name)
+
+        if stack is None:
+            return
+
+        self.id = stack.id
+        stack.update_and_save({'status': new_status,
+                               'status_reason': reason})
 
     def _timeout(self):
         '''Return the stack creation timeout in seconds'''
index 67cd13e5cf1da0dfafe41972ff47d00293b28b15..2e3e39290e9f93968eab39ddba6543f44503ff27 100644 (file)
@@ -64,6 +64,10 @@ class Resource(object):
             # make a dummy entry to prevent having to check all over the
             # place for it.
             self.t['Properties'] = {}
+        if 'Metadata' not in self.t:
+            # make a dummy entry to prevent having to check all over the
+            # place for it.
+            self.t['Metadata'] = {}
 
         resource = db_api.resource_get_by_name_and_stack(self.stack.context,
                                                          name, stack.id)
@@ -146,11 +150,10 @@ class Resource(object):
 
         logger.info('creating %s' % str(self))
 
-        self.state_set(self.CREATE_IN_PROGRESS)
-
         try:
             self.calculate_properties()
             self.properties.validate()
+            self.state_set(self.CREATE_IN_PROGRESS)
             if callable(getattr(self, 'handle_create', None)):
                 self.handle_create()
         except Exception as ex:
@@ -207,7 +210,7 @@ class Resource(object):
     def instance_id_set(self, inst):
         self.instance_id = inst
 
-    def _create_db(self):
+    def _create_db(self, metadata=None):
         '''Create the resource in the database'''
         try:
             rs = {'state': self.state,
@@ -215,6 +218,7 @@ class Resource(object):
                   'parsed_template_id': self.stack.parsed_template_id,
                   'nova_instance': self.instance_id,
                   'name': self.name,
+                  'rsrc_metadata': metadata,
                   'stack_name': self.stack.name}
 
             new_rs = db_api.resource_create(self.stack.context, rs)
@@ -259,7 +263,7 @@ class Resource(object):
                 logger.error('DB error %s' % str(ex))
 
         elif new_state in (self.CREATE_COMPLETE, self.CREATE_FAILED):
-            self._create_db()
+            self._create_db(metadata=self.parsed_template()['Metadata'])
 
         if new_state != old_state:
             self._add_event(new_state, reason)
index 8d2fa05d6c6d951b2acb39101541f65a64e5a5c4..16d64082bcf58a8876cd6b194c01775cfc578ccf 100644 (file)
@@ -75,11 +75,11 @@ class WaitCondition(Resource):
         tmo = eventlet.Timeout(self.timeout)
         status = 'WAITING'
         reason = ''
+        res = None
         try:
             while status == 'WAITING':
-                pt = None
                 try:
-                    pt = self.stack.parsed_template_get()
+                    res = db_api.resource_get(self.stack.context, self.id)
                 except Exception as ex:
                     if 'not found' in ex:
                         # it has been deleted
@@ -87,11 +87,11 @@ class WaitCondition(Resource):
                     else:
                         pass
 
-                if pt:
-                    res = pt.template['Resources'][self.resource_id]
-                    metadata = res.get('Metadata', {})
-                    status = metadata.get('Status', 'WAITING')
-                    reason = metadata.get('Reason', 'Reason not provided')
+                if res and res.rsrc_metadata:
+                    metadata = res.rsrc_metadata
+                    if metadata:
+                        status = metadata.get('Status', 'WAITING')
+                        reason = metadata.get('Reason', 'Reason not provided')
                     logger.debug('got %s' % json.dumps(metadata))
                 if status == 'WAITING':
                     logger.debug('Waiting some more for the Metadata[Status]')
@@ -111,10 +111,14 @@ class WaitCondition(Resource):
 
     def FnGetAtt(self, key):
         res = None
-        self._get_handle_resource_id()
         if key == 'Data':
-            resource = self.stack.t['Resources'][self.resource_id]
-            res = resource['Metadata']['Data']
+            try:
+                r = db_api.resource_get(self.stack.context, self.id)
+                if r.rsrc_metadata and 'Data' in r.rsrc_metadata:
+                    res = r.rsrc_metadata['Data']
+            except Exception as ex:
+                pass
+
         else:
             raise exception.InvalidTemplateAttribute(resource=self.name,
                                                      key=key)
index e9b437db7763b1bbf67cb9d2319db226ccb99679..1a785467f5f39a7792b3a740e5962610f3cf3bc1 100644 (file)
@@ -90,7 +90,7 @@ class stacksTest(unittest.TestCase):
         assert(stack.resources['WebServer'].instance_id > 0)
         stack.delete()
         assert(stack.resources['WebServer'].state == 'DELETE_COMPLETE')
-        assert(stack.t['stack_status'] == 'DELETE_COMPLETE')
+        assert(new_s.status == 'DELETE_COMPLETE')
 
     def test_stack_event_list(self):
         stack = self.start_wordpress_stack('test_event_list_stack')