Add describe resource API calls
Fixes #62. This commit implements the `DescribeStackResource`, `DescribeStackResources` and `ListStackResources` AWS API calls. Change-Id: Id9161b3c3eb527d5936c5b8978e32a67ba6c12bb
This commit is contained in:
parent
614f7868c4
commit
247cc2bb9a
68
bin/heat
68
bin/heat
@ -297,6 +297,61 @@ def stack_events_list(options, arguments):
|
||||
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):
|
||||
'''
|
||||
@ -397,6 +452,10 @@ def create_options(parser):
|
||||
|
||||
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():
|
||||
@ -484,6 +543,9 @@ def lookup_command(parser, command_name):
|
||||
'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,
|
||||
@ -530,6 +592,12 @@ Commands:
|
||||
|
||||
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'
|
||||
|
@ -127,6 +127,9 @@ class API(wsgi.Router):
|
||||
'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):
|
||||
|
@ -243,6 +243,98 @@ class StackController(object):
|
||||
|
||||
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):
|
||||
"""
|
||||
|
@ -65,6 +65,15 @@ class V1Client(base_client.BaseClient):
|
||||
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)
|
||||
|
||||
|
@ -16,4 +16,6 @@
|
||||
SUPPORTED_PARAMS = ('StackName', 'TemplateBody', 'TemplateUrl',
|
||||
'NotificationARNs', 'Parameters', 'Version',
|
||||
'SignatureVersion', 'Timestamp', 'AWSAccessKeyId',
|
||||
'Signature', 'KeyStoneCreds', 'Timeout')
|
||||
'Signature', 'KeyStoneCreds', 'Timeout',
|
||||
'LogicalResourceId', 'PhysicalResourceId', 'NextToken',
|
||||
)
|
||||
|
@ -95,6 +95,11 @@ def resource_get_by_name_and_stack(context, resource_name, stack_id):
|
||||
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)
|
||||
|
||||
|
@ -98,6 +98,13 @@ def resource_get_by_name_and_stack(context, resource_name, 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()
|
||||
|
||||
|
@ -391,6 +391,67 @@ class EngineManager(manager.Manager):
|
||||
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
|
||||
|
||||
@ -520,3 +581,23 @@ class EngineManager(manager.Manager):
|
||||
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,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user