Improve stack error reporting

Change-Id: Ib88854d50319f476cf4f7dc1d1b6ec599fc296c5
This commit is contained in:
Federico Ressi 2018-12-19 10:18:03 +01:00
parent 08bacea477
commit e790cc131b
1 changed files with 62 additions and 25 deletions

View File

@ -22,6 +22,7 @@ from oslo_log import log
import yaml
from tobiko.common import constants
from tobiko.common import exceptions
LOG = log.getLogger(__name__)
@ -33,6 +34,7 @@ CREATE_COMPLETE = 'CREATE_COMPLETE'
CREATE_FAILED = 'CREATE_FAILED'
DELETE_IN_PROGRESS = 'DELETE_IN_PROGRESS'
DELETE_COMPLETE = 'DELETE_COMPLETE'
DELETE_FAILED = 'DELETE_FAILED'
class StackManager(object):
@ -51,9 +53,9 @@ class StackManager(object):
def create_stack(self, stack_name, template_name, parameters, wait=True):
"""Creates stack based on passed parameters."""
stack = self.wait_for_stack_status(
stack_name=stack_name, status={DELETE_COMPLETE,
CREATE_COMPLETE,
CREATE_FAILED})
stack_name=stack_name, expected_status={DELETE_COMPLETE,
CREATE_COMPLETE,
CREATE_FAILED})
if stack and stack.stack_status == CREATE_COMPLETE:
LOG.debug('Stack %r already exists.', stack_name)
return stack
@ -82,7 +84,8 @@ class StackManager(object):
"""Deletes stack."""
self.client.stacks.delete(stack_name)
if wait:
self.wait_for_stack_status(stack_name, status={DELETE_COMPLETE})
self.wait_for_stack_status(stack_name,
expected_status={DELETE_COMPLETE})
def get_stack(self, stack_name):
"""Returns stack ID."""
@ -99,39 +102,37 @@ class StackManager(object):
time.sleep(self.wait_interval)
res = self.client.resources.get(stack_id, resource_name)
def wait_for_stack_status(self, stack_name, status=None, stack=None,
check=True):
def wait_for_stack_status(self, stack_name=None, stack=None,
expected_status=None, check=True):
"""Waits for the stack to reach the given status."""
status = status or {CREATE_COMPLETE}
expected_status = expected_status or {CREATE_COMPLETE}
stack_name = stack_name or stack.stack_name
stack = stack or self.get_stack(stack_name=stack_name)
while (stack and stack.stack_status.endswith('_IN_PROGRESS') and
stack.stack_status not in status):
LOG.debug('Waiting for %r stack status (expected=%r, acual=%r)...',
stack_name, status, stack.stack_status)
stack.stack_status not in expected_status):
LOG.debug("Waiting for %r stack status (observed=%r, expected=%r)",
stack_name, stack.stack_status, expected_status)
time.sleep(self.wait_interval)
stack = self.get_stack(stack_name=stack_name)
if check:
if stack is None:
if DELETE_COMPLETE not in status:
msg = "Stack {!r} not found".format(stack_name)
raise RuntimeError(msg)
elif stack.stack_status not in status:
msg = ("Invalid stack {!r} status (expected={!r}, "
"actual={!r})").format(stack_name, status,
stack.stack_status)
raise RuntimeError(msg)
if DELETE_COMPLETE not in expected_status:
raise StackNotFound(name=stack_name)
else:
check_stack_status(stack, expected_status)
return stack
def get_output(self, stack, key):
"""Returns a specific value from stack outputs by using a given key."""
if stack.stack_status != CREATE_COMPLETE:
raise ValueError("Invalid stack status: {!r}".format(
stack.stack_status))
for output in stack.outputs:
if output['output_key'] == key:
return output['output_value']
raise KeyError("No such key: {!r}".format(key))
check_stack_status(stack, {CREATE_COMPLETE})
outputs = {output['output_key']: output['output_value']
for output in stack.outputs}
try:
return outputs[key]
except KeyError:
raise InvalidOutputKey(name=stack.stack_name,
key=key)
def get_templates_names(self, strip_suffix=False):
"""Returns a list of all the files in templates dir."""
@ -156,3 +157,39 @@ class StackManager(object):
matched_stacks.append(stack.stack_name)
return matched_stacks
def check_stack_status(stack, expected):
observed = stack.stack_status
if observed not in expected:
if observed == CREATE_FAILED:
error_class = StackCreationFailed
elif observed == DELETE_FAILED:
error_class = StackDeletionFailed
else:
error_class = InvalidStackStatus
raise error_class(name=stack.stack_name,
observed=observed,
expected=expected,
reason=stack.stack_status_reason)
class InvalidOutputKey(exceptions.TobikoException):
msg = ("Output key %(key)r not found in stack %(name).")
class StackNotFound(exceptions.TobikoException):
msg = ("Stack %(name)r not found")
class InvalidStackStatus(exceptions.TobikoException):
msg = ("Stack %(name)r status %(observed)r not in %(expected)r "
"(reason=%(status_reason)r)")
class StackCreationFailed(InvalidStackStatus):
pass
class StackDeletionFailed(InvalidStackStatus):
pass