Files
python-heatclient/heatclient/v1/stacks.py
Steven Hardy e259163d56 Revert "Don't call credentials_headers() twice"
When we create or update a stack, when deferred_auth_method=password
(which is the default in heat.conf), the client must pass the username
and password, even if they don't need to for authentication (ie
they're passing a token). Otherwise there's no username and password
in the context to store, and the engine validation in
heat/engine/service.py::_validate_deferred_auth_context fails.

No such issue exists when deferred_auth_method=trusts, which is why
this problem was missed during testing of the patch being reverted.

Note some fixup in addition to the revert was required to make the
tests pass, as ShellTestToken and ShellTestStandaloneToken now require
username/password set in the fake env.

This reverts commit a7ba3c323b
Change-Id: I0e7bc3f748022c2bf5ef7ef6dfe4d97953e3fcab
2013-12-03 14:41:45 +00:00

162 lines
5.3 KiB
Python

# Copyright 2012 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from heatclient.openstack.common.py3kcompat import urlutils
from heatclient.common import base
class Stack(base.Resource):
def __repr__(self):
return "<Stack %s>" % self._info
def create(self, **fields):
return self.manager.create(self.identifier, **fields)
def update(self, **fields):
self.manager.update(self.identifier, **fields)
def delete(self):
return self.manager.delete(self.identifier)
def get(self):
# set_loaded() first ... so if we have to bail, we know we tried.
self.set_loaded(True)
if not hasattr(self.manager, 'get'):
return
new = self.manager.get(self.identifier)
if new:
self._add_details(new._info)
@property
def action(self):
s = self.stack_status
# Return everything before the first underscore
return s[:s.index('_')]
@property
def status(self):
s = self.stack_status
# Return everything after the first underscore
return s[s.index('_') + 1:]
@property
def identifier(self):
return '%s/%s' % (self.stack_name, self.id)
class StackManager(base.Manager):
resource_class = Stack
def list(self, **kwargs):
"""Get a list of stacks.
:param page_size: number of items to request in each paginated request
:param limit: maximum number of stacks to return
:param marker: begin returning stacks that appear later in the stack
list than that represented by this stack id
:param filters: dict of direct comparison filters that mimics the
structure of a stack object
:rtype: list of :class:`Stack`
"""
absolute_limit = kwargs.get('limit')
def paginate(qp, seen=0):
sort_qp = sorted(qp.items(), key=lambda x: x[0])
url = '/stacks?%s' % urlutils.urlencode(sort_qp)
stacks = self._list(url, "stacks")
for stack in stacks:
seen += 1
if absolute_limit is not None and seen > absolute_limit:
return
yield stack
page_size = qp.get('limit')
if (page_size and len(stacks) == page_size and
(absolute_limit is None or 0 < seen < absolute_limit)):
qp['marker'] = stack.id
for stack in paginate(qp, seen):
yield stack
params = {}
if 'page_size' in kwargs:
params['limit'] = kwargs['page_size']
if 'marker' in kwargs:
params['marker'] = kwargs['marker']
filters = kwargs.get('filters', {})
properties = filters.pop('properties', {})
for key, value in properties.items():
params['property-%s' % key] = value
params.update(filters)
return paginate(params)
def create(self, **kwargs):
"""Create a stack."""
headers = self.api.credentials_headers()
resp, body = self.api.json_request('POST', '/stacks',
body=kwargs, headers=headers)
return body
def update(self, stack_id, **kwargs):
"""Update a stack."""
headers = self.api.credentials_headers()
resp, body = self.api.json_request('PUT', '/stacks/%s' % stack_id,
body=kwargs, headers=headers)
def delete(self, stack_id):
"""Delete a stack."""
self._delete("/stacks/%s" % stack_id)
def get(self, stack_id):
"""Get the metadata for a specific stack.
:param stack_id: Stack ID to lookup
"""
resp, body = self.api.json_request('GET', '/stacks/%s' % stack_id)
return Stack(self, body['stack'])
def template(self, stack_id):
"""Get the template content for a specific stack as a parsed JSON
object.
:param stack_id: Stack ID to get the template for
"""
resp, body = self.api.json_request(
'GET', '/stacks/%s/template' % stack_id)
return body
def validate(self, **kwargs):
"""Validate a stack template."""
resp, body = self.api.json_request('POST', '/validate', body=kwargs)
return body
class StackChildManager(base.Manager):
def _resolve_stack_id(self, stack_id):
# if the id already has a slash in it,
# then it is already {stack_name}/{stack_id}
if stack_id.find('/') > 0:
return stack_id
resp, body = self.api.json_request('GET',
'/stacks/%s' % stack_id)
stack = body['stack']
return '%s/%s' % (stack['stack_name'], stack['id'])