diff --git a/heatclient/common/http.py b/heatclient/common/http.py index 9e6668c7..bf05e5c8 100644 --- a/heatclient/common/http.py +++ b/heatclient/common/http.py @@ -163,7 +163,8 @@ class HTTPClient(object): self.log_http_response(resp) if 400 <= resp.status < 600: - LOG.warn("Request returned failure status.") + if resp.status != 404: + LOG.warn("Request returned failure status %s" % resp.status) raise exc.from_response(resp) elif resp.status in (301, 302, 305): # Redirected. Reissue the request to the new location. diff --git a/heatclient/v1/client.py b/heatclient/v1/client.py index 4471c53b..22ad3825 100644 --- a/heatclient/v1/client.py +++ b/heatclient/v1/client.py @@ -15,6 +15,7 @@ from heatclient.common import http from heatclient.v1 import stacks +from heatclient.v1 import resources class Client(http.HTTPClient): @@ -31,3 +32,4 @@ class Client(http.HTTPClient): """ Initialize a new client for the Heat v1 API. """ super(Client, self).__init__(*args, **kwargs) self.stacks = stacks.StackManager(self) + self.resources = resources.ResourceManager(self) diff --git a/heatclient/v1/resources.py b/heatclient/v1/resources.py new file mode 100644 index 00000000..3afd00c8 --- /dev/null +++ b/heatclient/v1/resources.py @@ -0,0 +1,80 @@ +# Copyright 2012 OpenStack LLC. +# 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.common import base +import heatclient.exc as exc + +DEFAULT_PAGE_SIZE = 20 + + +class Resource(base.Resource): + def __repr__(self): + return "" % self._info + + def update(self, **fields): + self.manager.update(self, **fields) + + def delete(self): + return self.manager.delete(self) + + def data(self, **kwargs): + return self.manager.data(self, **kwargs) + + +class ResourceManager(base.Manager): + resource_class = Resource + + def list(self, stack_id): + """Get a list of resources. + :rtype: list of :class:`Resource` + """ + url = '/stacks/%s/resources' % stack_id + return self._list(url, "resources") + + def get(self, stack_id, resource_id): + """Get the details for a specific resource. + + :param stack_id: ID of stack containing the resource + :param resource_id: ID of resource to get the details for + """ + resp, body = self._get_resolve_fallback(stack_id, resource_id, + '/stacks/%s/resources/%s') + return Resource(self, body['resource']) + + def metadata(self, stack_id, resource_id): + """Get the metadata for a specific resource. + + :param stack_id: ID of stack containing the resource + :param resource_id: ID of resource to get metadata for + """ + resp, body = self._get_resolve_fallback(stack_id, resource_id, + '/stacks/%s/resources/%s/metadata') + return Resource(self, body['metadata']) + + def _get_resolve_fallback(self, stack_id, resource_id, path_pattern): + try: + resp, body = self.api.json_request('GET', + path_pattern % (stack_id, resource_id)) + except exc.HTTPNotFound: + stack_id = self._resolve_stack_id(stack_id) + resp, body = self.api.json_request('GET', + path_pattern % (stack_id, resource_id)) + return (resp, body) + + def _resolve_stack_id(self, stack_id): + resp, body = self.api.json_request('GET', + '/stacks/%s' % stack_id) + stack = body['stack'] + return '%s/%s' % (stack['stack_name'], stack['id']) diff --git a/heatclient/v1/shell.py b/heatclient/v1/shell.py index 5cd49239..8502c9ac 100644 --- a/heatclient/v1/shell.py +++ b/heatclient/v1/shell.py @@ -156,41 +156,66 @@ def do_validate(hc, args): validation = hc.stacks.validate(**fields) print json.dumps(validation, indent=2) + +@utils.arg('id', metavar='', + help='Name or ID of stack to show the resources for.') +def do_resource_list(hc, args): + '''Show list of resources belonging to a stack''' + fields = {'stack_id': args.id} + try: + resources = hc.resources.list(**fields) + except exc.HTTPNotFound: + raise exc.CommandError('Stack not found: %s' % args.id) + else: + field_labels = ['Name', 'Type', + 'Status', 'Updated'] + fields = ['logical_resource_id', 'resource_type', + 'resource_status', 'updated_time'] + utils.print_list(resources, fields, field_labels, sortby=3) + + +@utils.arg('id', metavar='', + help='Name or ID of stack to show the resource for.') +@utils.arg('resource', metavar='', + help='Name or ID of the resource to show the details for.') +def do_resource(hc, args): + '''Describe the resource''' + fields = {'stack_id': args.id, + 'resource_id': args.resource} + try: + resource = hc.resources.get(**fields) + except exc.HTTPNotFound: + raise exc.CommandError('Stack or resource not found: %s %s' % + (args.id, args.resource)) + else: + link_format = lambda links: '\n'.join([l['href'] for l in links]) + json_format = lambda js: json.dumps(js, indent=2) + formatters = { + 'links': link_format + } + utils.print_dict(resource.to_dict(), formatters=formatters) + + +@utils.arg('id', metavar='', + help='Name or ID of stack to show the resource metadata for.') +@utils.arg('resource', metavar='', + help='Name or ID of the resource to show the metadata for.') +def do_resource_metadata(hc, args): + '''List resource metadata''' + fields = {'stack_id': args.id, + 'resource_id': args.resource} + try: + resource = hc.resources.metadata(**fields) + except exc.HTTPNotFound: + raise exc.CommandError('Stack or resource not found: %s %s' % + (args.id, args.resource)) + else: + formatters = {} + utils.print_dict(resource.to_dict(), formatters=formatters) + # TODO only need to implement this once the server supports it -#@utils.arg('-u', '--template-url', metavar='', -# help='URL of template.') -#@utils.arg('-f', '--template-file', metavar='', -# help='Path to the template.') -#def do_estimate_template_cost(hc, args): -# '''Returns the estimated monthly cost of a template''' -# pass -# -# #@utils.arg('id', metavar='', # help='Name or ID of stack to show the events for.') #def do_event_list(hc, args): # '''List events for a stack''' # pass -# -# -#@utils.arg('-r', '--resource', metavar='', -# help='ID of the resource to show the details for.') -#@utils.arg('id', metavar='', -# help='Name or ID of stack to show the resource for.') -#def do_resource(hc, args): -# '''Describe the resource''' -# pass -# -# -#@utils.arg('id', metavar='', -# help='Name or ID of stack to show the resources for.') -#def do_resource_list(hc, args): -# '''Show list of resources belonging to a stack''' -# pass -# -# -#@utils.arg('id', metavar='', -# help='Name or ID of stack to show the resource details for.') -#def do_resource_list_details(hc, args): -# '''Detailed view of resources belonging to a stack''' -# pass diff --git a/heatclient/versioninfo b/heatclient/versioninfo index 2c2036ac..3d387714 100644 --- a/heatclient/versioninfo +++ b/heatclient/versioninfo @@ -1 +1 @@ -0.0.28.c1dbaa9 +0.0.33.cfb2205