DB: add stack status & reason, resource metadata
Change-Id: I369aa688fa9890a5484de417a9995e7f04f34ad2 Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
parent
b85bdc2d6c
commit
66fb2054fb
|
@ -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()
|
|
@ -142,6 +142,8 @@ class Stack(BASE, HeatBase):
|
||||||
raw_template = relationship(RawTemplate,
|
raw_template = relationship(RawTemplate,
|
||||||
backref=backref('stack'))
|
backref=backref('stack'))
|
||||||
username = Column(String)
|
username = Column(String)
|
||||||
|
status = Column('status', String)
|
||||||
|
status_reason = Column('status_reason', String)
|
||||||
user_creds_id = Column(Integer, ForeignKey('user_creds.id'),
|
user_creds_id = Column(Integer, ForeignKey('user_creds.id'),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
owner_id = Column(Integer, nullable=True)
|
owner_id = Column(Integer, nullable=True)
|
||||||
|
@ -196,6 +198,8 @@ class Resource(BASE, HeatBase):
|
||||||
name = Column('name', String, nullable=False)
|
name = Column('name', String, nullable=False)
|
||||||
nova_instance = Column('nova_instance', String)
|
nova_instance = Column('nova_instance', String)
|
||||||
state_description = Column('state_description', 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'),
|
parsed_template_id = Column(Integer, ForeignKey('parsed_template.id'),
|
||||||
nullable=True)
|
nullable=True)
|
||||||
parsed_template = relationship(ParsedTemplate,
|
parsed_template = relationship(ParsedTemplate,
|
||||||
|
|
|
@ -155,7 +155,7 @@ class EngineManager(manager.Manager):
|
||||||
mem['CreationTime'] = heat_utils.strtime(s.created_at)
|
mem['CreationTime'] = heat_utils.strtime(s.created_at)
|
||||||
mem['TemplateDescription'] = ps.t.get('Description',
|
mem['TemplateDescription'] = ps.t.get('Description',
|
||||||
'No description')
|
'No description')
|
||||||
mem['StackStatus'] = ps.t.get('stack_status', 'unknown')
|
mem['StackStatus'] = s.status
|
||||||
res['stacks'].append(mem)
|
res['stacks'].append(mem)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
@ -185,12 +185,11 @@ class EngineManager(manager.Manager):
|
||||||
mem['TimeoutInMinutes'] = ps.t.get('Timeout', '60')
|
mem['TimeoutInMinutes'] = ps.t.get('Timeout', '60')
|
||||||
mem['TemplateDescription'] = ps.t.get('Description',
|
mem['TemplateDescription'] = ps.t.get('Description',
|
||||||
'No description')
|
'No description')
|
||||||
mem['StackStatus'] = ps.t.get('stack_status', 'unknown')
|
mem['StackStatus'] = s.status
|
||||||
mem['StackStatusReason'] = ps.t.get('stack_status_reason',
|
mem['StackStatusReason'] = s.status_reason
|
||||||
'State changed')
|
|
||||||
|
|
||||||
# only show the outputs on a completely created stack
|
# 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()
|
mem['Outputs'] = ps.get_outputs()
|
||||||
|
|
||||||
res['stacks'].append(mem)
|
res['stacks'].append(mem)
|
||||||
|
@ -481,12 +480,11 @@ class EngineManager(manager.Manager):
|
||||||
if not s:
|
if not s:
|
||||||
return ['stack', None]
|
return ['stack', None]
|
||||||
|
|
||||||
template = s.raw_template.parsed_template.template
|
r = db_api.resource_get_by_name_and_stack(None, resource_id, s.id)
|
||||||
if not resource_id in template.get('Resources', {}):
|
if r is None:
|
||||||
return ['resource', None]
|
return ['resource', None]
|
||||||
|
|
||||||
metadata = template['Resources'][resource_id].get('Metadata', {})
|
return [None, r.rsrc_metadata]
|
||||||
return [None, metadata]
|
|
||||||
|
|
||||||
def metadata_update(self, context, stack_name, resource_id, 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)
|
s = db_api.stack_get_by_name(None, stack_name)
|
||||||
if not s:
|
if not s:
|
||||||
return ['stack', None]
|
return ['stack', None]
|
||||||
pt_id = s.raw_template.parsed_template.id
|
|
||||||
|
|
||||||
pt = db_api.parsed_template_get(None, pt_id)
|
r = db_api.resource_get_by_name_and_stack(None, resource_id, s.id)
|
||||||
if not resource_id in pt.template.get('Resources', {}):
|
if r is None:
|
||||||
|
logger.warn("Resource not found %s:%s." % (stack_name,
|
||||||
|
resource_id))
|
||||||
return ['resource', None]
|
return ['resource', None]
|
||||||
|
|
||||||
# TODO(shadower) deep copy of the template is required here. Without
|
r.update_and_save({'rsrc_metadata': metadata})
|
||||||
# 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})
|
|
||||||
return [None, metadata]
|
return [None, metadata]
|
||||||
|
|
||||||
@manager.periodic_task
|
@manager.periodic_task
|
||||||
|
|
|
@ -178,9 +178,17 @@ class Stack(object):
|
||||||
self.parsed_template_id)
|
self.parsed_template_id)
|
||||||
|
|
||||||
def state_set(self, new_status, reason='change in resource state'):
|
def state_set(self, new_status, reason='change in resource state'):
|
||||||
self.t['stack_status'] = new_status
|
if self.id != 0:
|
||||||
self.t['stack_status_reason'] = reason
|
stack = db_api.stack_get(self.context, self.id)
|
||||||
self.update_parsed_template()
|
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):
|
def _timeout(self):
|
||||||
'''Return the stack creation timeout in seconds'''
|
'''Return the stack creation timeout in seconds'''
|
||||||
|
|
|
@ -64,6 +64,10 @@ class Resource(object):
|
||||||
# make a dummy entry to prevent having to check all over the
|
# make a dummy entry to prevent having to check all over the
|
||||||
# place for it.
|
# place for it.
|
||||||
self.t['Properties'] = {}
|
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,
|
resource = db_api.resource_get_by_name_and_stack(self.stack.context,
|
||||||
name, stack.id)
|
name, stack.id)
|
||||||
|
@ -146,11 +150,10 @@ class Resource(object):
|
||||||
|
|
||||||
logger.info('creating %s' % str(self))
|
logger.info('creating %s' % str(self))
|
||||||
|
|
||||||
self.state_set(self.CREATE_IN_PROGRESS)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.calculate_properties()
|
self.calculate_properties()
|
||||||
self.properties.validate()
|
self.properties.validate()
|
||||||
|
self.state_set(self.CREATE_IN_PROGRESS)
|
||||||
if callable(getattr(self, 'handle_create', None)):
|
if callable(getattr(self, 'handle_create', None)):
|
||||||
self.handle_create()
|
self.handle_create()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -207,7 +210,7 @@ class Resource(object):
|
||||||
def instance_id_set(self, inst):
|
def instance_id_set(self, inst):
|
||||||
self.instance_id = inst
|
self.instance_id = inst
|
||||||
|
|
||||||
def _create_db(self):
|
def _create_db(self, metadata=None):
|
||||||
'''Create the resource in the database'''
|
'''Create the resource in the database'''
|
||||||
try:
|
try:
|
||||||
rs = {'state': self.state,
|
rs = {'state': self.state,
|
||||||
|
@ -215,6 +218,7 @@ class Resource(object):
|
||||||
'parsed_template_id': self.stack.parsed_template_id,
|
'parsed_template_id': self.stack.parsed_template_id,
|
||||||
'nova_instance': self.instance_id,
|
'nova_instance': self.instance_id,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
|
'rsrc_metadata': metadata,
|
||||||
'stack_name': self.stack.name}
|
'stack_name': self.stack.name}
|
||||||
|
|
||||||
new_rs = db_api.resource_create(self.stack.context, rs)
|
new_rs = db_api.resource_create(self.stack.context, rs)
|
||||||
|
@ -259,7 +263,7 @@ class Resource(object):
|
||||||
logger.error('DB error %s' % str(ex))
|
logger.error('DB error %s' % str(ex))
|
||||||
|
|
||||||
elif new_state in (self.CREATE_COMPLETE, self.CREATE_FAILED):
|
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:
|
if new_state != old_state:
|
||||||
self._add_event(new_state, reason)
|
self._add_event(new_state, reason)
|
||||||
|
|
|
@ -75,11 +75,11 @@ class WaitCondition(Resource):
|
||||||
tmo = eventlet.Timeout(self.timeout)
|
tmo = eventlet.Timeout(self.timeout)
|
||||||
status = 'WAITING'
|
status = 'WAITING'
|
||||||
reason = ''
|
reason = ''
|
||||||
|
res = None
|
||||||
try:
|
try:
|
||||||
while status == 'WAITING':
|
while status == 'WAITING':
|
||||||
pt = None
|
|
||||||
try:
|
try:
|
||||||
pt = self.stack.parsed_template_get()
|
res = db_api.resource_get(self.stack.context, self.id)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
if 'not found' in ex:
|
if 'not found' in ex:
|
||||||
# it has been deleted
|
# it has been deleted
|
||||||
|
@ -87,11 +87,11 @@ class WaitCondition(Resource):
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if pt:
|
if res and res.rsrc_metadata:
|
||||||
res = pt.template['Resources'][self.resource_id]
|
metadata = res.rsrc_metadata
|
||||||
metadata = res.get('Metadata', {})
|
if metadata:
|
||||||
status = metadata.get('Status', 'WAITING')
|
status = metadata.get('Status', 'WAITING')
|
||||||
reason = metadata.get('Reason', 'Reason not provided')
|
reason = metadata.get('Reason', 'Reason not provided')
|
||||||
logger.debug('got %s' % json.dumps(metadata))
|
logger.debug('got %s' % json.dumps(metadata))
|
||||||
if status == 'WAITING':
|
if status == 'WAITING':
|
||||||
logger.debug('Waiting some more for the Metadata[Status]')
|
logger.debug('Waiting some more for the Metadata[Status]')
|
||||||
|
@ -111,10 +111,14 @@ class WaitCondition(Resource):
|
||||||
|
|
||||||
def FnGetAtt(self, key):
|
def FnGetAtt(self, key):
|
||||||
res = None
|
res = None
|
||||||
self._get_handle_resource_id()
|
|
||||||
if key == 'Data':
|
if key == 'Data':
|
||||||
resource = self.stack.t['Resources'][self.resource_id]
|
try:
|
||||||
res = resource['Metadata']['Data']
|
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:
|
else:
|
||||||
raise exception.InvalidTemplateAttribute(resource=self.name,
|
raise exception.InvalidTemplateAttribute(resource=self.name,
|
||||||
key=key)
|
key=key)
|
||||||
|
|
|
@ -90,7 +90,7 @@ class stacksTest(unittest.TestCase):
|
||||||
assert(stack.resources['WebServer'].instance_id > 0)
|
assert(stack.resources['WebServer'].instance_id > 0)
|
||||||
stack.delete()
|
stack.delete()
|
||||||
assert(stack.resources['WebServer'].state == 'DELETE_COMPLETE')
|
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):
|
def test_stack_event_list(self):
|
||||||
stack = self.start_wordpress_stack('test_event_list_stack')
|
stack = self.start_wordpress_stack('test_event_list_stack')
|
||||||
|
|
Loading…
Reference in New Issue