Allow not resolving outputs on get stacks

Depending on the stack complexity, resolving outputs of a Heat stack can
be fairly expensive. The API has a parameter to disable output
resolution: this adds this parameter to the get_stack call, and use it
in places where outputs are superfluous.

Change-Id: I779e451c9f058a954a0526404489a4fb1b39ee9c
This commit is contained in:
Thomas Herve 2018-02-06 12:26:29 +01:00
parent 99bfb9c03c
commit b1bc65c599
3 changed files with 54 additions and 38 deletions

View File

@ -86,7 +86,7 @@ def poll_for_events(
if no_event_polls >= 2:
# after 2 polls with no events, fall back to a stack get
stack = cloud.get_stack(stack_name)
stack = cloud.get_stack(stack_name, resolve_outputs=False)
stack_status = stack['stack_status']
msg = msg_template % dict(
name=stack_name, status=stack_status)

View File

@ -1394,7 +1394,7 @@ class OpenStackCloud(
:raises: ``OpenStackCloudException`` if something goes wrong during
the OpenStack API call
"""
stack = self.get_stack(name_or_id)
stack = self.get_stack(name_or_id, resolve_outputs=False)
if stack is None:
self.log.debug("Stack %s not found for deleting", name_or_id)
return False
@ -1416,7 +1416,7 @@ class OpenStackCloud(
marker=marker)
except OpenStackCloudHTTPError:
pass
stack = self.get_stack(name_or_id)
stack = self.get_stack(name_or_id, resolve_outputs=False)
if stack and stack['stack_status'] == 'DELETE_FAILED':
raise OpenStackCloudException(
"Failed to delete stack {id}: {reason}".format(
@ -3336,12 +3336,14 @@ class OpenStackCloud(
return self._normalize_floating_ip(
self._get_and_munchify('floating_ip', data))
def get_stack(self, name_or_id, filters=None):
def get_stack(self, name_or_id, filters=None, resolve_outputs=True):
"""Get exactly one stack.
:param name_or_id: Name or ID of the desired stack.
:param filters: a dict containing additional filters to use. e.g.
{'stack_status': 'CREATE_COMPLETE'}
:param resolve_outputs: If True, then outputs for this
stack will be resolved
:returns: a ``munch.Munch`` containing the stack description
@ -3353,8 +3355,11 @@ class OpenStackCloud(
# stack names are mandatory and enforced unique in the project
# so a StackGet can always be used for name or ID.
try:
url = '/stacks/{name_or_id}'.format(name_or_id=name_or_id)
if not resolve_outputs:
url = '{url}?resolve_outputs=False'.format(url=url)
data = self._orchestration_client.get(
'/stacks/{name_or_id}'.format(name_or_id=name_or_id),
url,
error_message="Error fetching stack")
stack = self._get_and_munchify('stack', data)
# Treat DELETE_COMPLETE stacks as a NotFound

View File

@ -112,20 +112,22 @@ class TestStack(base.RequestsMockTestCase):
self.cloud.search_stacks()
def test_delete_stack(self):
resolve = 'resolve_outputs=False'
self.register_uris([
dict(method='GET',
uri='{endpoint}/stacks/{name}'.format(
uri='{endpoint}/stacks/{name}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
name=self.stack_name),
name=self.stack_name, resolve=resolve),
status_code=302,
headers=dict(
location='{endpoint}/stacks/{name}/{id}'.format(
location='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name))),
id=self.stack_id, name=self.stack_name,
resolve=resolve))),
dict(method='GET',
uri='{endpoint}/stacks/{name}/{id}'.format(
uri='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name),
id=self.stack_id, name=self.stack_name, resolve=resolve),
json={"stack": self.stack}),
dict(method='DELETE',
uri='{endpoint}/stacks/{id}'.format(
@ -136,30 +138,33 @@ class TestStack(base.RequestsMockTestCase):
self.assert_calls()
def test_delete_stack_not_found(self):
resolve = 'resolve_outputs=False'
self.register_uris([
dict(method='GET',
uri='{endpoint}/stacks/stack_name'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT),
uri='{endpoint}/stacks/stack_name?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT, resolve=resolve),
status_code=404),
])
self.assertFalse(self.cloud.delete_stack('stack_name'))
self.assert_calls()
def test_delete_stack_exception(self):
resolve = 'resolve_outputs=False'
self.register_uris([
dict(method='GET',
uri='{endpoint}/stacks/{id}'.format(
uri='{endpoint}/stacks/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id),
id=self.stack_id, resolve=resolve),
status_code=302,
headers=dict(
location='{endpoint}/stacks/{name}/{id}'.format(
location='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name))),
id=self.stack_id, name=self.stack_name,
resolve=resolve))),
dict(method='GET',
uri='{endpoint}/stacks/{name}/{id}'.format(
uri='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name),
id=self.stack_id, name=self.stack_name, resolve=resolve),
json={"stack": self.stack}),
dict(method='DELETE',
uri='{endpoint}/stacks/{id}'.format(
@ -177,20 +182,23 @@ class TestStack(base.RequestsMockTestCase):
self.stack_id, self.stack_name, status='CREATE_COMPLETE')
marker_qs = 'marker={e_id}&sort_dir=asc'.format(
e_id=marker_event['id'])
resolve = 'resolve_outputs=False'
self.register_uris([
dict(method='GET',
uri='{endpoint}/stacks/{id}'.format(
uri='{endpoint}/stacks/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id),
id=self.stack_id,
resolve=resolve),
status_code=302,
headers=dict(
location='{endpoint}/stacks/{name}/{id}'.format(
location='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name))),
id=self.stack_id, name=self.stack_name,
resolve=resolve))),
dict(method='GET',
uri='{endpoint}/stacks/{name}/{id}'.format(
uri='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name),
id=self.stack_id, name=self.stack_name, resolve=resolve),
json={"stack": self.stack}),
dict(method='GET',
uri='{endpoint}/stacks/{id}/events?{qs}'.format(
@ -215,9 +223,9 @@ class TestStack(base.RequestsMockTestCase):
status='DELETE_COMPLETE'),
]}),
dict(method='GET',
uri='{endpoint}/stacks/{id}'.format(
uri='{endpoint}/stacks/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name),
id=self.stack_id, name=self.stack_name, resolve=resolve),
status_code=404),
])
@ -231,20 +239,22 @@ class TestStack(base.RequestsMockTestCase):
self.stack_id, self.stack_name, status='CREATE_COMPLETE')
marker_qs = 'marker={e_id}&sort_dir=asc'.format(
e_id=marker_event['id'])
resolve = 'resolve_outputs=False'
self.register_uris([
dict(method='GET',
uri='{endpoint}/stacks/{id}'.format(
uri='{endpoint}/stacks/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id),
id=self.stack_id, resolve=resolve),
status_code=302,
headers=dict(
location='{endpoint}/stacks/{name}/{id}'.format(
location='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name))),
id=self.stack_id, name=self.stack_name,
resolve=resolve))),
dict(method='GET',
uri='{endpoint}/stacks/{name}/{id}'.format(
uri='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name),
id=self.stack_id, name=self.stack_name, resolve=resolve),
json={"stack": self.stack}),
dict(method='GET',
uri='{endpoint}/stacks/{id}/events?{qs}'.format(
@ -269,18 +279,19 @@ class TestStack(base.RequestsMockTestCase):
status='DELETE_COMPLETE'),
]}),
dict(method='GET',
uri='{endpoint}/stacks/{id}'.format(
uri='{endpoint}/stacks/{id}?resolve_outputs=False'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name),
status_code=302,
headers=dict(
location='{endpoint}/stacks/{name}/{id}'.format(
location='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name))),
id=self.stack_id, name=self.stack_name,
resolve=resolve))),
dict(method='GET',
uri='{endpoint}/stacks/{name}/{id}'.format(
uri='{endpoint}/stacks/{name}/{id}?{resolve}'.format(
endpoint=fakes.ORCHESTRATION_ENDPOINT,
id=self.stack_id, name=self.stack_name),
id=self.stack_id, name=self.stack_name, resolve=resolve),
json={"stack": failed_stack}),
])