DB: add stack status & reason, resource metadata

Change-Id: I369aa688fa9890a5484de417a9995e7f04f34ad2
Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
Angus Salkeld 2012-06-20 23:49:26 +10:00
parent b85bdc2d6c
commit 66fb2054fb
7 changed files with 82 additions and 39 deletions

View File

@ -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()

View 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,

View 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

View 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'''

View 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)

View 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,9 +87,9 @@ class WaitCondition(Resource):
else:
pass
if pt:
res = pt.template['Resources'][self.resource_id]
metadata = res.get('Metadata', {})
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))
@ -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)

View 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')