Fixes #62.
This commit implements the `DescribeStackResource`,
`DescribeStackResources` and `ListStackResources` AWS API calls.
Change-Id: Id9161b3c3eb527d5936c5b8978e32a67ba6c12bb
print result
+@utils.catch_error('resource')
+def stack_resource_show(options, arguments):
+ '''
+ Display details of the specified resource.
+ '''
+ c = get_client(options)
+ try:
+ stack_name, resource_name = arguments
+ except ValueError:
+ print 'Enter stack name and logical resource id'
+ return
+
+ parameters = {
+ 'StackName': stack_name,
+ 'LogicalResourceId': resource_name,
+ }
+ result = c.describe_stack_resource(**parameters)
+ print result
+
+
+@utils.catch_error('resource-list')
+def stack_resources_list(options, arguments):
+ '''
+ Display summary of all resources in the specified stack.
+ '''
+ c = get_client(options)
+ try:
+ stack_name = arguments.pop(0)
+ except IndexError:
+ print 'Enter stack name'
+ return
+
+ parameters = {
+ 'StackName': stack_name,
+ }
+ result = c.list_stack_resources(**parameters)
+ print result
+
+
+@utils.catch_error('resource-list-details')
+def stack_resources_list_details(options, arguments):
+ '''
+ Display details of all resources in the specified stack.
+ '''
+ c = get_client(options)
+ logical_resource_id = arguments.pop(0) if arguments else None
+ parameters = {
+ 'StackName': options.stack_name,
+ 'PhysicalResourceId': options.physical_resource_id,
+ 'LogicalResourceId': logical_resource_id,
+ }
+ result = c.describe_stack_resources(**parameters)
+ print result
+
+
@utils.catch_error('list')
def stack_list(options, arguments):
'''
parser.add_option('-P', '--parameters', metavar="parameters", default=None,
help="Parameter values used to create the stack.")
+ parser.add_option('-n', '--stack-name', default=None,
+ help="Name of the queried stack")
+ parser.add_option('-c', '--physical-resource-id', default=None,
+ help="Physical ID of the queried resource")
def credentials_from_env():
'list': stack_list,
'events_list': stack_events_list, # DEPRECATED
'event-list': stack_events_list,
+ 'resource': stack_resource_show,
+ 'resource-list': stack_resources_list,
+ 'resource-list-details': stack_resources_list_details,
'validate': template_validate,
'gettemplate': get_template,
'estimate-template-cost': estimate_template_cost,
event-list List events for a stack
+ resource Describe the resource
+
+ resource-list Show list of resources belonging to a stack
+
+ resource-list-details Detailed view of resources belonging to a stack
+
"""
oparser = optparse.OptionParser(version='%%prog %s'
'validate_template': 'ValidateTemplate',
'get_template': 'GetTemplate',
'estimate_template_cost': 'EstimateTemplateCost',
+ 'describe_stack_resource': 'DescribeStackResource',
+ 'describe_stack_resources': 'DescribeStackResources',
+ 'list_stack_resources': 'ListStackResources',
}
def __init__(self, conf, **local_conf):
return {'DescribeStackEventsResult': {'StackEvents': events}}
+ def describe_stack_resource(self, req):
+ """
+ Return the details of the given resource belonging to the given stack.
+ """
+ con = req.context
+ args = {
+ 'stack_name': req.params.get('StackName'),
+ 'resource_name': req.params.get('LogicalResourceId'),
+ }
+
+ try:
+ resource_details = rpc.call(con, 'engine',
+ {'method': 'describe_stack_resource',
+ 'args': args})
+
+ except rpc_common.RemoteError as ex:
+ return webob.exc.HTTPBadRequest(str(ex))
+
+ return {
+ 'DescribeStackResourceResponse': {
+ 'DescribeStackResourceResult': {
+ 'StackResourceDetail': resource_details,
+ },
+ },
+ }
+
+ def describe_stack_resources(self, req):
+ """
+ Return details of resources specified by the parameters.
+
+ `StackName`: returns all resources belonging to the stack
+ `PhysicalResourceId`: returns all resources belonging to the stack this
+ resource is associated with.
+
+ Only one of the parameters may be specified.
+
+ Optional parameter:
+
+ `LogicalResourceId`: filter the resources list by the logical resource
+ id.
+ """
+ con = req.context
+ stack_name = req.params.get('StackName')
+ physical_resource_id = req.params.get('PhysicalResourceId')
+ if stack_name and physical_resource_id:
+ msg = 'Use `StackName` or `PhysicalResourceId` but not both'
+ return webob.exc.HTTPBadRequest(msg)
+
+ args = {
+ 'stack_name': stack_name,
+ 'physical_resource_id': physical_resource_id,
+ 'logical_resource_id': req.params.get('LogicalResourceId'),
+ }
+
+ try:
+ resources = rpc.call(con, 'engine',
+ {'method': 'describe_stack_resources',
+ 'args': args})
+
+ except rpc_common.RemoteError as ex:
+ return webob.exc.HTTPBadRequest(str(ex))
+
+ response = {
+ 'DescribeStackResourcesResult': {
+ 'StackResources': resources,
+ }
+ }
+ return response
+
+ def list_stack_resources(self, req):
+ """
+ Return summary of the resources belonging to the specified stack.
+
+ """
+ con = req.context
+
+ try:
+ resources = rpc.call(con, 'engine', {
+ 'method': 'list_stack_resources',
+ 'args': {'stack_name': req.params.get('StackName')}
+ })
+ except rpc_common.RemoteError as ex:
+ return webob.exc.HTTPBadRequest(str(ex))
+
+ return {
+ 'ListStackResourcesResponse': {
+ 'ListStackResourcesResult': {
+ 'StackResourceSummaries': resources,
+ },
+ },
+ }
+
def create_resource(options):
"""
def list_stack_events(self, **kwargs):
return self.stack_request("DescribeStackEvents", "GET", **kwargs)
+ def describe_stack_resource(self, **kwargs):
+ return self.stack_request("DescribeStackResource", "GET", **kwargs)
+
+ def describe_stack_resources(self, **kwargs):
+ return self.stack_request("DescribeStackResources", "GET", **kwargs)
+
+ def list_stack_resources(self, **kwargs):
+ return self.stack_request("ListStackResources", "GET", **kwargs)
+
def validate_template(self, **kwargs):
return self.stack_request("ValidateTemplate", "GET", **kwargs)
SUPPORTED_PARAMS = ('StackName', 'TemplateBody', 'TemplateUrl',
'NotificationARNs', 'Parameters', 'Version',
'SignatureVersion', 'Timestamp', 'AWSAccessKeyId',
- 'Signature', 'KeyStoneCreds', 'Timeout')
+ 'Signature', 'KeyStoneCreds', 'Timeout',
+ 'LogicalResourceId', 'PhysicalResourceId', 'NextToken',
+)
resource_name, stack_id)
+def resource_get_by_physical_resource_id(context, physical_resource_id):
+ return IMPL.resource_get_by_physical_resource_id(context,
+ physical_resource_id)
+
+
def stack_get(context, stack_id):
return IMPL.stack_get(context, stack_id)
return result
+def resource_get_by_physical_resource_id(context, physical_resource_id):
+ result = (model_query(context, models.Resource)
+ .filter_by(nova_instance=physical_resource_id)
+ .first())
+ return result
+
+
def resource_get_all(context):
results = model_query(context, models.Resource).all()
msg = 'Error creating event'
return [msg, None]
+ def describe_stack_resource(self, context, stack_name, resource_name):
+ self._authenticate(context)
+
+ stack = db_api.stack_get(context, stack_name)
+ if not stack:
+ raise AttributeError('Unknown stack name')
+ resource = db_api.resource_get_by_name_and_stack(context,
+ resource_name,
+ stack.id)
+ if not resource:
+ raise AttributeError('Unknown resource name')
+ return format_resource_attributes(stack, resource)
+
+ def describe_stack_resources(self, context, stack_name,
+ physical_resource_id, logical_resource_id):
+ self._authenticate(context)
+
+ if stack_name:
+ stack = db_api.stack_get(context, stack_name)
+ else:
+ resource = db_api.resource_get_by_physical_resource_id(context,
+ physical_resource_id)
+ if not resource:
+ msg = "The specified PhysicalResourceId doesn't exist"
+ raise AttributeError(msg)
+ stack = resource.stack
+
+ if not stack:
+ raise AttributeError("The specified stack doesn't exist")
+
+ resources = []
+ for r in stack.resources:
+ if logical_resource_id and r.name != logical_resource_id:
+ continue
+ formatted = format_resource_attributes(stack, r)
+ # this API call uses Timestamp instead of LastUpdatedTimestamp
+ formatted['Timestamp'] = formatted['LastUpdatedTimestamp']
+ del formatted['LastUpdatedTimestamp']
+ resources.append(formatted)
+
+ return resources
+
+ def list_stack_resources(self, context, stack_name):
+ self._authenticate(context)
+
+ stack = db_api.stack_get(context, stack_name)
+ if not stack:
+ raise AttributeError('Unknown stack name')
+
+ resources = []
+ response_keys = ('ResourceStatus', 'LogicalResourceId',
+ 'LastUpdatedTimestamp', 'PhysicalResourceId',
+ 'ResourceType')
+ for r in stack.resources:
+ formatted = format_resource_attributes(stack, r)
+ for key in formatted.keys():
+ if not key in response_keys:
+ del formatted[key]
+ resources.append(formatted)
+ return resources
+
def metadata_register_address(self, context, url):
config.FLAGS.heat_metadata_server_url = url
self.run_rule(None, wr)
return [None, wd.data]
+
+
+def format_resource_attributes(stack, resource):
+ """
+ Return a representation of the given resource that mathes the API output
+ expectations.
+ """
+ template = resource.parsed_template.template
+ template_resources = template.get('Resources', {})
+ resource_type = template_resources.get(resource.name, {}).get('Type', '')
+ last_updated_time = resource.updated_at or resource.created_at
+ return {
+ 'StackId': stack.id,
+ 'StackName': stack.name,
+ 'LogicalResourceId': resource.name,
+ 'PhysicalResourceId': resource.nova_instance or '',
+ 'ResourceType': resource_type,
+ 'LastUpdatedTimestamp': last_updated_time.isoformat(),
+ 'ResourceStatus': resource.state,
+ }