heat engine/API : Internal API rework
Refactor engine-api code so that AWS specific details like key names are handled in the API Fixes #172 Change-Id: I4c5b153557216c03e5a98193e54cf75e3c7b97dd Signed-off-by: Steven Hardy <shardy@redhat.com>
This commit is contained in:
parent
90e22dabde
commit
d06c4ce416
@ -31,6 +31,7 @@ from heat.common import context
|
|||||||
from heat import utils
|
from heat import utils
|
||||||
from heat import rpc
|
from heat import rpc
|
||||||
import heat.rpc.common as rpc_common
|
import heat.rpc.common as rpc_common
|
||||||
|
import heat.engine.api as engine_api
|
||||||
|
|
||||||
from heat.openstack.common import log as logging
|
from heat.openstack.common import log as logging
|
||||||
|
|
||||||
@ -79,22 +80,103 @@ class StackController(object):
|
|||||||
# FIXME : further investigation into engine errors required
|
# FIXME : further investigation into engine errors required
|
||||||
return exception.HeatInternalFailureError(detail=ex.value)
|
return exception.HeatInternalFailureError(detail=ex.value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_user_params(params):
|
||||||
|
"""
|
||||||
|
Extract a dictionary of user input parameters for the stack
|
||||||
|
|
||||||
|
In the AWS API parameters, each user parameter appears as two key-value
|
||||||
|
pairs with keys of the form below:
|
||||||
|
|
||||||
|
Parameters.member.1.ParameterKey
|
||||||
|
Parameters.member.1.ParameterValue
|
||||||
|
|
||||||
|
We reformat this into a normal dict here to match the heat
|
||||||
|
engine API expected format
|
||||||
|
|
||||||
|
Note this implemented outside of "create" as it will also be
|
||||||
|
used by update (and EstimateTemplateCost if appropriate..)
|
||||||
|
"""
|
||||||
|
# Define the AWS key format to extract
|
||||||
|
PARAM_KEYS = (
|
||||||
|
PARAM_USER_KEY_re,
|
||||||
|
PARAM_USER_VALUE_fmt,
|
||||||
|
) = (
|
||||||
|
re.compile(r'Parameters\.member\.(.*?)\.ParameterKey$'),
|
||||||
|
'Parameters.member.%s.ParameterValue',
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_param_pairs():
|
||||||
|
for k in params:
|
||||||
|
keymatch = PARAM_USER_KEY_re.match(k)
|
||||||
|
if keymatch:
|
||||||
|
key = params[k]
|
||||||
|
v = PARAM_USER_VALUE_fmt % keymatch.group(1)
|
||||||
|
try:
|
||||||
|
value = params[v]
|
||||||
|
except KeyError:
|
||||||
|
logger.error('Could not apply parameter %s' % key)
|
||||||
|
|
||||||
|
yield (key, value)
|
||||||
|
|
||||||
|
return dict(get_param_pairs())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _reformat_dict_keys(keymap={}, inputdict={}):
|
||||||
|
'''
|
||||||
|
Utility function for mapping one dict format to another
|
||||||
|
'''
|
||||||
|
result = {}
|
||||||
|
for key in keymap:
|
||||||
|
result[keymap[key]] = inputdict[key]
|
||||||
|
return result
|
||||||
|
|
||||||
def list(self, req):
|
def list(self, req):
|
||||||
"""
|
"""
|
||||||
Implements ListStacks API action
|
Implements ListStacks API action
|
||||||
Lists summary information for all stacks
|
Lists summary information for all stacks
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def format_stack_summary(s):
|
||||||
|
"""
|
||||||
|
Reformat engine output into the AWS "StackSummary" format
|
||||||
|
"""
|
||||||
|
# Map the engine-api format to the AWS StackSummary datatype
|
||||||
|
keymap = {
|
||||||
|
engine_api.STACK_CREATION_TIME: 'CreationTime',
|
||||||
|
engine_api.STACK_UPDATED_TIME: 'LastUpdatedTime',
|
||||||
|
engine_api.STACK_ID: 'StackId',
|
||||||
|
engine_api.STACK_NAME: 'StackName',
|
||||||
|
engine_api.STACK_STATUS: 'StackStatus',
|
||||||
|
engine_api.STACK_STATUS_DATA: 'StackStatusReason',
|
||||||
|
engine_api.STACK_TMPL_DESCRIPTION: 'TemplateDescription',
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self._reformat_dict_keys(keymap, s)
|
||||||
|
|
||||||
|
# AWS docs indicate DeletionTime is ommitted for current stacks
|
||||||
|
# This is still TODO in the engine, we don't keep data for
|
||||||
|
# stacks after they are deleted
|
||||||
|
if engine_api.STACK_DELETION_TIME in s:
|
||||||
|
result['DeletionTime'] = s[engine_api.STACK_DELETION_TIME]
|
||||||
|
|
||||||
|
return self._stackid_addprefix(result)
|
||||||
|
|
||||||
con = req.context
|
con = req.context
|
||||||
parms = dict(req.params)
|
parms = dict(req.params)
|
||||||
|
|
||||||
stack_list = rpc.call(con, 'engine',
|
try:
|
||||||
{'method': 'list_stacks',
|
# Note show_stack returns details for all stacks when called with
|
||||||
'args': {'params': parms}})
|
# no stack_name, we only use a subset of the result here though
|
||||||
|
stack_list = rpc.call(con, 'engine',
|
||||||
|
{'method': 'show_stack',
|
||||||
|
'args': {'stack_name': None,
|
||||||
|
'params': parms}})
|
||||||
|
except rpc_common.RemoteError as ex:
|
||||||
|
return self._remote_error(ex)
|
||||||
|
|
||||||
res = {'StackSummaries': []}
|
res = {'StackSummaries': [format_stack_summary(s)
|
||||||
if stack_list is not None:
|
for s in stack_list['stacks']]}
|
||||||
for s in stack_list['stacks']:
|
|
||||||
res['StackSummaries'].append(self._stackid_addprefix(s))
|
|
||||||
|
|
||||||
return self._format_response('ListStacks', res)
|
return self._format_response('ListStacks', res)
|
||||||
|
|
||||||
@ -103,6 +185,53 @@ class StackController(object):
|
|||||||
Implements DescribeStacks API action
|
Implements DescribeStacks API action
|
||||||
Gets detailed information for a stack (or all stacks)
|
Gets detailed information for a stack (or all stacks)
|
||||||
"""
|
"""
|
||||||
|
def format_stack_outputs(o):
|
||||||
|
keymap = {
|
||||||
|
engine_api.OUTPUT_DESCRIPTION: 'Description',
|
||||||
|
engine_api.OUTPUT_KEY: 'OutputKey',
|
||||||
|
engine_api.OUTPUT_VALUE: 'OutputValue',
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._reformat_dict_keys(keymap, o)
|
||||||
|
|
||||||
|
def format_stack(s):
|
||||||
|
"""
|
||||||
|
Reformat engine output into the AWS "StackSummary" format
|
||||||
|
"""
|
||||||
|
keymap = {
|
||||||
|
engine_api.STACK_CAPABILITIES: 'Capabilities',
|
||||||
|
engine_api.STACK_CREATION_TIME: 'CreationTime',
|
||||||
|
engine_api.STACK_DESCRIPTION: 'Description',
|
||||||
|
engine_api.STACK_DISABLE_ROLLBACK: 'DisableRollback',
|
||||||
|
engine_api.STACK_UPDATED_TIME: 'LastUpdatedTime',
|
||||||
|
engine_api.STACK_NOTIFICATION_TOPICS: 'NotificationARNs',
|
||||||
|
engine_api.STACK_PARAMETERS: 'Parameters',
|
||||||
|
engine_api.STACK_ID: 'StackId',
|
||||||
|
engine_api.STACK_NAME: 'StackName',
|
||||||
|
engine_api.STACK_STATUS: 'StackStatus',
|
||||||
|
engine_api.STACK_STATUS_DATA: 'StackStatusReason',
|
||||||
|
engine_api.STACK_TIMEOUT: 'TimeoutInMinutes',
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self._reformat_dict_keys(keymap, s)
|
||||||
|
|
||||||
|
# Reformat outputs, these are handled separately as they are
|
||||||
|
# only present in the engine output for a completely created
|
||||||
|
# stack
|
||||||
|
result['Outputs'] = []
|
||||||
|
if engine_api.STACK_OUTPUTS in s:
|
||||||
|
for o in s[engine_api.STACK_OUTPUTS]:
|
||||||
|
result['Outputs'].append(format_stack_outputs(o))
|
||||||
|
|
||||||
|
# Reformat Parameters dict-of-dict into AWS API format
|
||||||
|
# This is a list-of-dict with nasty "ParameterKey" : key
|
||||||
|
# "ParameterValue" : value format.
|
||||||
|
result['Parameters'] = [{'ParameterKey':k,
|
||||||
|
'ParameterValue':v.get('Default')}
|
||||||
|
for (k, v) in result['Parameters'].items()]
|
||||||
|
|
||||||
|
return self._stackid_addprefix(result)
|
||||||
|
|
||||||
con = req.context
|
con = req.context
|
||||||
parms = dict(req.params)
|
parms = dict(req.params)
|
||||||
|
|
||||||
@ -122,15 +251,7 @@ class StackController(object):
|
|||||||
except rpc_common.RemoteError as ex:
|
except rpc_common.RemoteError as ex:
|
||||||
return self._remote_error(ex)
|
return self._remote_error(ex)
|
||||||
|
|
||||||
res = {'Stacks': []}
|
res = {'Stacks': [format_stack(s) for s in stack_list['stacks']]}
|
||||||
for s in stack_list['stacks']:
|
|
||||||
# Reformat Parameters dict-of-dict into AWS API format
|
|
||||||
# This is a list-of-dict with nasty "ParameterKey" : key
|
|
||||||
# "ParameterValue" : value format.
|
|
||||||
s['Parameters'] = [{'ParameterKey':k,
|
|
||||||
'ParameterValue':v.get('Default')}
|
|
||||||
for (k, v) in s['Parameters'].items()]
|
|
||||||
res['Stacks'].append(self._stackid_addprefix(s))
|
|
||||||
|
|
||||||
return self._format_response('DescribeStacks', res)
|
return self._format_response('DescribeStacks', res)
|
||||||
|
|
||||||
@ -165,8 +286,29 @@ class StackController(object):
|
|||||||
Implements CreateStack API action
|
Implements CreateStack API action
|
||||||
Create stack as defined in template file
|
Create stack as defined in template file
|
||||||
"""
|
"""
|
||||||
|
def extract_args(params):
|
||||||
|
"""
|
||||||
|
Extract request parameters/arguments and reformat them to match
|
||||||
|
the engine API. FIXME: we currently only support a subset of
|
||||||
|
the AWS defined parameters (both here and in the engine)
|
||||||
|
"""
|
||||||
|
# TODO : Capabilities, DisableRollback, NotificationARNs
|
||||||
|
keymap = {'TimeoutInMinutes': engine_api.PARAM_TIMEOUT, }
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for k in keymap:
|
||||||
|
if k in req.params:
|
||||||
|
result[keymap[k]] = params[k]
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
con = req.context
|
con = req.context
|
||||||
parms = dict(req.params)
|
|
||||||
|
# Extract the stack input parameters
|
||||||
|
stack_parms = self._extract_user_params(req.params)
|
||||||
|
|
||||||
|
# Extract any additional arguments ("Request Parameters")
|
||||||
|
create_args = extract_args(req.params)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
templ = self._get_template(req)
|
templ = self._get_template(req)
|
||||||
@ -189,7 +331,8 @@ class StackController(object):
|
|||||||
{'method': 'create_stack',
|
{'method': 'create_stack',
|
||||||
'args': {'stack_name': req.params['StackName'],
|
'args': {'stack_name': req.params['StackName'],
|
||||||
'template': stack,
|
'template': stack,
|
||||||
'params': parms}})
|
'params': stack_parms,
|
||||||
|
'args': create_args}})
|
||||||
except rpc_common.RemoteError as ex:
|
except rpc_common.RemoteError as ex:
|
||||||
return self._remote_error(ex)
|
return self._remote_error(ex)
|
||||||
|
|
||||||
@ -288,6 +431,27 @@ class StackController(object):
|
|||||||
Implements the DescribeStackEvents API action
|
Implements the DescribeStackEvents API action
|
||||||
Returns events related to a specified stack (or all stacks)
|
Returns events related to a specified stack (or all stacks)
|
||||||
"""
|
"""
|
||||||
|
def format_stack_event(e):
|
||||||
|
"""
|
||||||
|
Reformat engine output into the AWS "StackEvent" format
|
||||||
|
"""
|
||||||
|
keymap = {
|
||||||
|
engine_api.EVENT_ID: 'EventId',
|
||||||
|
engine_api.EVENT_RES_NAME: 'LogicalResourceId',
|
||||||
|
engine_api.EVENT_RES_PHYSICAL_ID: 'PhysicalResourceId',
|
||||||
|
engine_api.EVENT_RES_PROPERTIES: 'ResourceProperties',
|
||||||
|
engine_api.EVENT_RES_STATUS: 'ResourceStatus',
|
||||||
|
engine_api.EVENT_RES_STATUS_DATA: 'ResourceStatusData',
|
||||||
|
engine_api.EVENT_RES_TYPE: 'ResourceType',
|
||||||
|
engine_api.EVENT_STACK_ID: 'StackId',
|
||||||
|
engine_api.EVENT_STACK_NAME: 'StackName',
|
||||||
|
engine_api.EVENT_TIMESTAMP: 'Timestamp',
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self._reformat_dict_keys(keymap, e)
|
||||||
|
|
||||||
|
return self._stackid_addprefix(result)
|
||||||
|
|
||||||
con = req.context
|
con = req.context
|
||||||
parms = dict(req.params)
|
parms = dict(req.params)
|
||||||
|
|
||||||
@ -302,9 +466,7 @@ class StackController(object):
|
|||||||
|
|
||||||
events = 'Error' not in event_res and event_res['events'] or []
|
events = 'Error' not in event_res and event_res['events'] or []
|
||||||
|
|
||||||
result = []
|
result = [format_stack_event(e) for e in events]
|
||||||
for e in events:
|
|
||||||
result.append(self._stackid_addprefix(e))
|
|
||||||
|
|
||||||
return self._format_response('DescribeStackEvents',
|
return self._format_response('DescribeStackEvents',
|
||||||
{'StackEvents': result})
|
{'StackEvents': result})
|
||||||
@ -314,6 +476,28 @@ class StackController(object):
|
|||||||
Implements the DescribeStackResource API action
|
Implements the DescribeStackResource API action
|
||||||
Return the details of the given resource belonging to the given stack.
|
Return the details of the given resource belonging to the given stack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def format_resource_detail(r):
|
||||||
|
"""
|
||||||
|
Reformat engine output into the AWS "StackResourceDetail" format
|
||||||
|
"""
|
||||||
|
keymap = {
|
||||||
|
engine_api.RES_DESCRIPTION: 'Description',
|
||||||
|
engine_api.RES_UPDATED_TIME: 'LastUpdatedTimestamp',
|
||||||
|
engine_api.RES_NAME: 'LogicalResourceId',
|
||||||
|
engine_api.RES_METADATA: 'Metadata',
|
||||||
|
engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
|
||||||
|
engine_api.RES_STATUS: 'ResourceStatus',
|
||||||
|
engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
|
||||||
|
engine_api.RES_TYPE: 'ResourceType',
|
||||||
|
engine_api.RES_STACK_ID: 'StackId',
|
||||||
|
engine_api.RES_STACK_NAME: 'StackName',
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self._reformat_dict_keys(keymap, r)
|
||||||
|
|
||||||
|
return self._stackid_addprefix(result)
|
||||||
|
|
||||||
con = req.context
|
con = req.context
|
||||||
args = {
|
args = {
|
||||||
'stack_name': req.params.get('StackName'),
|
'stack_name': req.params.get('StackName'),
|
||||||
@ -328,8 +512,10 @@ class StackController(object):
|
|||||||
except rpc_common.RemoteError as ex:
|
except rpc_common.RemoteError as ex:
|
||||||
return self._remote_error(ex)
|
return self._remote_error(ex)
|
||||||
|
|
||||||
|
result = format_resource_detail(resource_details)
|
||||||
|
|
||||||
return self._format_response('DescribeStackResource',
|
return self._format_response('DescribeStackResource',
|
||||||
{'StackResourceDetail': resource_details})
|
{'StackResourceDetail': result})
|
||||||
|
|
||||||
def describe_stack_resources(self, req):
|
def describe_stack_resources(self, req):
|
||||||
"""
|
"""
|
||||||
@ -347,6 +533,27 @@ class StackController(object):
|
|||||||
`LogicalResourceId`: filter the resources list by the logical resource
|
`LogicalResourceId`: filter the resources list by the logical resource
|
||||||
id.
|
id.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def format_stack_resource(r):
|
||||||
|
"""
|
||||||
|
Reformat engine output into the AWS "StackResource" format
|
||||||
|
"""
|
||||||
|
keymap = {
|
||||||
|
engine_api.RES_DESCRIPTION: 'Description',
|
||||||
|
engine_api.RES_NAME: 'LogicalResourceId',
|
||||||
|
engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
|
||||||
|
engine_api.RES_STATUS: 'ResourceStatus',
|
||||||
|
engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
|
||||||
|
engine_api.RES_TYPE: 'ResourceType',
|
||||||
|
engine_api.RES_STACK_ID: 'StackId',
|
||||||
|
engine_api.RES_STACK_NAME: 'StackName',
|
||||||
|
engine_api.RES_UPDATED_TIME: 'Timestamp',
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self._reformat_dict_keys(keymap, r)
|
||||||
|
|
||||||
|
return self._stackid_addprefix(result)
|
||||||
|
|
||||||
con = req.context
|
con = req.context
|
||||||
stack_name = req.params.get('StackName')
|
stack_name = req.params.get('StackName')
|
||||||
physical_resource_id = req.params.get('PhysicalResourceId')
|
physical_resource_id = req.params.get('PhysicalResourceId')
|
||||||
@ -368,9 +575,7 @@ class StackController(object):
|
|||||||
except rpc_common.RemoteError as ex:
|
except rpc_common.RemoteError as ex:
|
||||||
return self._remote_error(ex)
|
return self._remote_error(ex)
|
||||||
|
|
||||||
result = []
|
result = [format_stack_resource(r) for r in resources]
|
||||||
for r in resources:
|
|
||||||
result.append(self._stackid_addprefix(r))
|
|
||||||
|
|
||||||
return self._format_response('DescribeStackResources',
|
return self._format_response('DescribeStackResources',
|
||||||
{'StackResources': result})
|
{'StackResources': result})
|
||||||
@ -380,6 +585,21 @@ class StackController(object):
|
|||||||
Implements the ListStackResources API action
|
Implements the ListStackResources API action
|
||||||
Return summary of the resources belonging to the specified stack.
|
Return summary of the resources belonging to the specified stack.
|
||||||
"""
|
"""
|
||||||
|
def format_resource_summary(r):
|
||||||
|
"""
|
||||||
|
Reformat engine output into the AWS "StackResourceSummary" format
|
||||||
|
"""
|
||||||
|
keymap = {
|
||||||
|
engine_api.RES_UPDATED_TIME: 'LastUpdatedTimestamp',
|
||||||
|
engine_api.RES_NAME: 'LogicalResourceId',
|
||||||
|
engine_api.RES_PHYSICAL_ID: 'PhysicalResourceId',
|
||||||
|
engine_api.RES_STATUS: 'ResourceStatus',
|
||||||
|
engine_api.RES_STATUS_DATA: 'ResourceStatusReason',
|
||||||
|
engine_api.RES_TYPE: 'ResourceType',
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._reformat_dict_keys(keymap, r)
|
||||||
|
|
||||||
con = req.context
|
con = req.context
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -390,8 +610,10 @@ class StackController(object):
|
|||||||
except rpc_common.RemoteError as ex:
|
except rpc_common.RemoteError as ex:
|
||||||
return self._remote_error(ex)
|
return self._remote_error(ex)
|
||||||
|
|
||||||
|
summaries = [format_resource_summary(r) for r in resources]
|
||||||
|
|
||||||
return self._format_response('ListStackResources',
|
return self._format_response('ListStackResources',
|
||||||
{'StackResourceSummaries': resources})
|
{'StackResourceSummaries': summaries})
|
||||||
|
|
||||||
|
|
||||||
def create_resource(options):
|
def create_resource(options):
|
||||||
|
@ -20,48 +20,14 @@ from heat.openstack.common import log as logging
|
|||||||
|
|
||||||
logger = logging.getLogger('heat.engine.manager')
|
logger = logging.getLogger('heat.engine.manager')
|
||||||
|
|
||||||
PARAM_KEYS = (
|
PARAM_KEYS = (PARAM_TIMEOUT, ) = ('timeout_mins', )
|
||||||
PARAM_TIMEOUT,
|
|
||||||
PARAM_USER_KEY_re,
|
|
||||||
PARAM_USER_VALUE_fmt,
|
|
||||||
) = (
|
|
||||||
'TimeoutInMinutes',
|
|
||||||
re.compile(r'Parameters\.member\.(.*?)\.ParameterKey$'),
|
|
||||||
'Parameters.member.%s.ParameterValue',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def extract_user_params(params):
|
|
||||||
'''
|
|
||||||
Extract a dictionary of user parameters (to e.g. a stack create command)
|
|
||||||
from the parameter dictionary passed through the API.
|
|
||||||
|
|
||||||
In the API parameters, each user parameter appears as two key-value pairs
|
|
||||||
with keys of the form:
|
|
||||||
|
|
||||||
Parameters.member.1.ParameterKey
|
|
||||||
Parameters.member.1.ParameterValue
|
|
||||||
'''
|
|
||||||
def get_param_pairs():
|
|
||||||
for k in params:
|
|
||||||
keymatch = PARAM_USER_KEY_re.match(k)
|
|
||||||
if keymatch:
|
|
||||||
key = params[k]
|
|
||||||
v = PARAM_USER_VALUE_fmt % keymatch.group(1)
|
|
||||||
try:
|
|
||||||
value = params[v]
|
|
||||||
except KeyError:
|
|
||||||
logger.error('Could not apply parameter %s' % key)
|
|
||||||
|
|
||||||
yield (key, value)
|
|
||||||
|
|
||||||
return dict(get_param_pairs())
|
|
||||||
|
|
||||||
|
|
||||||
def extract_args(params):
|
def extract_args(params):
|
||||||
'''
|
'''
|
||||||
Extract any arguments passed as parameters through the API and return them
|
Extract any arguments passed as parameters through the API and return them
|
||||||
as a dictionary.
|
as a dictionary. This allows us to filter the passed args and do type
|
||||||
|
conversion where appropriate
|
||||||
'''
|
'''
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
try:
|
try:
|
||||||
@ -70,63 +36,34 @@ def extract_args(params):
|
|||||||
logger.exception('create timeout conversion')
|
logger.exception('create timeout conversion')
|
||||||
else:
|
else:
|
||||||
if timeout_mins > 0:
|
if timeout_mins > 0:
|
||||||
kwargs['timeout_mins'] = timeout_mins
|
kwargs[PARAM_TIMEOUT] = timeout_mins
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
def _filter_keys(data, keys):
|
|
||||||
'''
|
|
||||||
Filter the provided data so that only the dictionary keys specified are
|
|
||||||
present. If keys is None, return all of the data.
|
|
||||||
'''
|
|
||||||
if keys is not None:
|
|
||||||
data = dict((k, v) for (k, v) in data.iteritems() if k in keys)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
STACK_KEYS = (
|
STACK_KEYS = (
|
||||||
STACK_NAME, STACK_ID,
|
STACK_NAME, STACK_ID,
|
||||||
STACK_CREATION_TIME, STACK_UPDATED_TIME, STACK_DELETION_TIME,
|
STACK_CREATION_TIME, STACK_UPDATED_TIME, STACK_DELETION_TIME,
|
||||||
STACK_NOTIFICATION_TOPICS,
|
STACK_NOTIFICATION_TOPICS,
|
||||||
STACK_DESCRIPTION, STACK_TMPL_DESCRIPTION,
|
STACK_DESCRIPTION, STACK_TMPL_DESCRIPTION,
|
||||||
STACK_PARAMETERS, STACK_OUTPUTS,
|
STACK_PARAMETERS, STACK_OUTPUTS,
|
||||||
STACK_STATUS, STACK_STATUS_DATA,
|
STACK_STATUS, STACK_STATUS_DATA, STACK_CAPABILITIES,
|
||||||
STACK_TIMEOUT,
|
STACK_DISABLE_ROLLBACK, STACK_TIMEOUT,
|
||||||
) = (
|
) = (
|
||||||
'StackName', 'StackId',
|
'stack_name', 'stack_id',
|
||||||
'CreationTime', 'LastUpdatedTime', 'DeletionTime',
|
'creation_time', 'updated_time', 'deletion_time',
|
||||||
'NotificationARNs',
|
'notification_topics',
|
||||||
'Description', 'TemplateDescription',
|
'description', 'template_description',
|
||||||
'Parameters', 'Outputs',
|
'parameters', 'outputs',
|
||||||
'StackStatus', 'StackStatusReason',
|
'stack_status', 'stack_status_reason', 'capabilities',
|
||||||
PARAM_TIMEOUT,
|
'disable_rollback', 'timeout_mins'
|
||||||
)
|
)
|
||||||
|
|
||||||
KEYS_STACK = (
|
|
||||||
STACK_NAME, STACK_ID,
|
|
||||||
STACK_CREATION_TIME, STACK_UPDATED_TIME,
|
|
||||||
STACK_NOTIFICATION_TOPICS,
|
|
||||||
STACK_DESCRIPTION,
|
|
||||||
STACK_PARAMETERS, STACK_DESCRIPTION, STACK_OUTPUTS,
|
|
||||||
STACK_STATUS, STACK_STATUS_DATA,
|
|
||||||
STACK_TIMEOUT,
|
|
||||||
)
|
|
||||||
KEYS_STACK_SUMMARY = (
|
|
||||||
STACK_CREATION_TIME, STACK_DELETION_TIME,
|
|
||||||
STACK_UPDATED_TIME,
|
|
||||||
STACK_ID, STACK_NAME,
|
|
||||||
STACK_TMPL_DESCRIPTION,
|
|
||||||
STACK_STATUS, STACK_STATUS_DATA,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
STACK_OUTPUT_KEYS = (
|
STACK_OUTPUT_KEYS = (
|
||||||
OUTPUT_DESCRIPTION,
|
OUTPUT_DESCRIPTION,
|
||||||
OUTPUT_KEY, OUTPUT_VALUE,
|
OUTPUT_KEY, OUTPUT_VALUE,
|
||||||
) = (
|
) = (
|
||||||
'Description',
|
'description',
|
||||||
'OutputKey', 'OutputValue',
|
'output_key', 'output_value',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -144,7 +81,7 @@ def format_stack_outputs(stack, outputs):
|
|||||||
return [format_stack_output(key) for key in outputs]
|
return [format_stack_output(key) for key in outputs]
|
||||||
|
|
||||||
|
|
||||||
def format_stack(stack, keys=None):
|
def format_stack(stack):
|
||||||
'''
|
'''
|
||||||
Return a representation of the given stack that matches the API output
|
Return a representation of the given stack that matches the API output
|
||||||
expectations.
|
expectations.
|
||||||
@ -160,6 +97,8 @@ def format_stack(stack, keys=None):
|
|||||||
STACK_TMPL_DESCRIPTION: stack.t[parser.DESCRIPTION],
|
STACK_TMPL_DESCRIPTION: stack.t[parser.DESCRIPTION],
|
||||||
STACK_STATUS: stack.state,
|
STACK_STATUS: stack.state,
|
||||||
STACK_STATUS_DATA: stack.state_description,
|
STACK_STATUS_DATA: stack.state_description,
|
||||||
|
STACK_CAPABILITIES: [], # TODO Not implemented yet
|
||||||
|
STACK_DISABLE_ROLLBACK: True, # TODO Not implemented yet
|
||||||
STACK_TIMEOUT: stack.timeout_mins,
|
STACK_TIMEOUT: stack.timeout_mins,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +106,7 @@ def format_stack(stack, keys=None):
|
|||||||
if stack.state == stack.CREATE_COMPLETE:
|
if stack.state == stack.CREATE_COMPLETE:
|
||||||
info[STACK_OUTPUTS] = format_stack_outputs(stack, stack.outputs)
|
info[STACK_OUTPUTS] = format_stack_outputs(stack, stack.outputs)
|
||||||
|
|
||||||
return _filter_keys(info, keys)
|
return info
|
||||||
|
|
||||||
|
|
||||||
RES_KEYS = (
|
RES_KEYS = (
|
||||||
@ -175,42 +114,21 @@ RES_KEYS = (
|
|||||||
RES_NAME, RES_PHYSICAL_ID, RES_METADATA,
|
RES_NAME, RES_PHYSICAL_ID, RES_METADATA,
|
||||||
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
|
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
|
||||||
RES_STACK_ID, RES_STACK_NAME,
|
RES_STACK_ID, RES_STACK_NAME,
|
||||||
RES_TIMESTAMP,
|
|
||||||
) = (
|
) = (
|
||||||
'Description', 'LastUpdatedTimestamp',
|
'description', 'updated_time',
|
||||||
'LogicalResourceId', 'PhysicalResourceId', 'Metadata',
|
'logical_resource_id', 'physical_resource_id', 'metadata',
|
||||||
'ResourceStatus', 'ResourceStatusReason', 'ResourceType',
|
'resource_status', 'resource_status_reason', 'resource_type',
|
||||||
STACK_ID, STACK_NAME,
|
STACK_ID, STACK_NAME,
|
||||||
'Timestamp',
|
|
||||||
)
|
|
||||||
|
|
||||||
KEYS_RESOURCE_DETAIL = (
|
|
||||||
RES_DESCRIPTION, RES_UPDATED_TIME,
|
|
||||||
RES_NAME, RES_PHYSICAL_ID, RES_METADATA,
|
|
||||||
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
|
|
||||||
RES_STACK_ID, RES_STACK_NAME,
|
|
||||||
)
|
|
||||||
KEYS_RESOURCE = (
|
|
||||||
RES_DESCRIPTION,
|
|
||||||
RES_NAME, RES_PHYSICAL_ID,
|
|
||||||
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
|
|
||||||
RES_STACK_ID, RES_STACK_NAME,
|
|
||||||
RES_TIMESTAMP,
|
|
||||||
)
|
|
||||||
KEYS_RESOURCE_SUMMARY = (
|
|
||||||
RES_UPDATED_TIME,
|
|
||||||
RES_NAME, RES_PHYSICAL_ID,
|
|
||||||
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def format_stack_resource(resource, keys=None):
|
def format_stack_resource(resource):
|
||||||
'''
|
'''
|
||||||
Return a representation of the given resource that matches the API output
|
Return a representation of the given resource that matches the API output
|
||||||
expectations.
|
expectations.
|
||||||
'''
|
'''
|
||||||
last_updated_time = resource.updated_time or resource.created_time
|
last_updated_time = resource.updated_time or resource.created_time
|
||||||
attrs = {
|
res = {
|
||||||
RES_DESCRIPTION: resource.parsed_template().get('Description', ''),
|
RES_DESCRIPTION: resource.parsed_template().get('Description', ''),
|
||||||
RES_UPDATED_TIME: heat_utils.strtime(last_updated_time),
|
RES_UPDATED_TIME: heat_utils.strtime(last_updated_time),
|
||||||
RES_NAME: resource.name,
|
RES_NAME: resource.name,
|
||||||
@ -221,10 +139,9 @@ def format_stack_resource(resource, keys=None):
|
|||||||
RES_TYPE: resource.t['Type'],
|
RES_TYPE: resource.t['Type'],
|
||||||
RES_STACK_ID: resource.stack.id,
|
RES_STACK_ID: resource.stack.id,
|
||||||
RES_STACK_NAME: resource.stack.name,
|
RES_STACK_NAME: resource.stack.name,
|
||||||
RES_TIMESTAMP: heat_utils.strtime(last_updated_time),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _filter_keys(attrs, keys)
|
return res
|
||||||
|
|
||||||
|
|
||||||
EVENT_KEYS = (
|
EVENT_KEYS = (
|
||||||
@ -235,18 +152,18 @@ EVENT_KEYS = (
|
|||||||
EVENT_RES_STATUS, EVENT_RES_STATUS_DATA, EVENT_RES_TYPE,
|
EVENT_RES_STATUS, EVENT_RES_STATUS_DATA, EVENT_RES_TYPE,
|
||||||
EVENT_RES_PROPERTIES,
|
EVENT_RES_PROPERTIES,
|
||||||
) = (
|
) = (
|
||||||
'EventId',
|
'event_id',
|
||||||
STACK_ID, STACK_NAME,
|
STACK_ID, STACK_NAME,
|
||||||
RES_TIMESTAMP,
|
"event_time",
|
||||||
RES_NAME, RES_PHYSICAL_ID,
|
RES_NAME, RES_PHYSICAL_ID,
|
||||||
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
|
RES_STATUS, RES_STATUS_DATA, RES_TYPE,
|
||||||
'ResourceProperties',
|
'resource_properties',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def format_event(event, keys=None):
|
def format_event(event):
|
||||||
s = event.stack
|
s = event.stack
|
||||||
attrs = {
|
event = {
|
||||||
EVENT_ID: event.id,
|
EVENT_ID: event.id,
|
||||||
EVENT_STACK_ID: s.id,
|
EVENT_STACK_ID: s.id,
|
||||||
EVENT_STACK_NAME: s.name,
|
EVENT_STACK_NAME: s.name,
|
||||||
@ -259,4 +176,4 @@ def format_event(event, keys=None):
|
|||||||
EVENT_RES_PROPERTIES: event.resource_properties,
|
EVENT_RES_PROPERTIES: event.resource_properties,
|
||||||
}
|
}
|
||||||
|
|
||||||
return _filter_keys(attrs, keys)
|
return event
|
||||||
|
@ -60,26 +60,6 @@ class EngineManager(manager.Manager):
|
|||||||
"""Load configuration options and connect to the hypervisor."""
|
"""Load configuration options and connect to the hypervisor."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def list_stacks(self, context, params):
|
|
||||||
"""
|
|
||||||
The list_stacks method is the end point that actually implements
|
|
||||||
the 'list' command of the heat API.
|
|
||||||
arg1 -> RPC context.
|
|
||||||
arg2 -> Dict of http request parameters passed in from API side.
|
|
||||||
"""
|
|
||||||
|
|
||||||
auth.authenticate(context)
|
|
||||||
|
|
||||||
stacks = db_api.stack_get_by_user(context)
|
|
||||||
if stacks is None:
|
|
||||||
stacks = []
|
|
||||||
|
|
||||||
def format_stack_summary(s):
|
|
||||||
stack = parser.Stack.load(context, s.id)
|
|
||||||
return api.format_stack(stack, api.KEYS_STACK_SUMMARY)
|
|
||||||
|
|
||||||
return {'stacks': [format_stack_summary(s) for s in stacks]}
|
|
||||||
|
|
||||||
def show_stack(self, context, stack_name, params):
|
def show_stack(self, context, stack_name, params):
|
||||||
"""
|
"""
|
||||||
The show_stack method returns the attributes of one stack.
|
The show_stack method returns the attributes of one stack.
|
||||||
@ -100,11 +80,11 @@ class EngineManager(manager.Manager):
|
|||||||
|
|
||||||
def format_stack_detail(s):
|
def format_stack_detail(s):
|
||||||
stack = parser.Stack.load(context, s.id)
|
stack = parser.Stack.load(context, s.id)
|
||||||
return api.format_stack(stack, api.KEYS_STACK)
|
return api.format_stack(stack)
|
||||||
|
|
||||||
return {'stacks': [format_stack_detail(s) for s in stacks]}
|
return {'stacks': [format_stack_detail(s) for s in stacks]}
|
||||||
|
|
||||||
def create_stack(self, context, stack_name, template, params):
|
def create_stack(self, context, stack_name, template, params, args):
|
||||||
"""
|
"""
|
||||||
The create_stack method creates a new stack using the template
|
The create_stack method creates a new stack using the template
|
||||||
provided.
|
provided.
|
||||||
@ -113,7 +93,8 @@ class EngineManager(manager.Manager):
|
|||||||
arg1 -> RPC context.
|
arg1 -> RPC context.
|
||||||
arg2 -> Name of the stack you want to create.
|
arg2 -> Name of the stack you want to create.
|
||||||
arg3 -> Template of stack you want to create.
|
arg3 -> Template of stack you want to create.
|
||||||
arg4 -> Params passed from API.
|
arg4 -> Stack Input Params
|
||||||
|
arg4 -> Request parameters/args passed from API
|
||||||
"""
|
"""
|
||||||
logger.info('template is %s' % template)
|
logger.info('template is %s' % template)
|
||||||
|
|
||||||
@ -123,10 +104,11 @@ class EngineManager(manager.Manager):
|
|||||||
return {'Error': 'Stack already exists with that name.'}
|
return {'Error': 'Stack already exists with that name.'}
|
||||||
|
|
||||||
tmpl = parser.Template(template)
|
tmpl = parser.Template(template)
|
||||||
|
|
||||||
# Extract the template parameters, and any common query parameters
|
# Extract the template parameters, and any common query parameters
|
||||||
template_params = parser.Parameters(stack_name, tmpl,
|
template_params = parser.Parameters(stack_name, tmpl, params)
|
||||||
api.extract_user_params(params))
|
common_params = api.extract_args(args)
|
||||||
common_params = api.extract_args(params)
|
|
||||||
stack = parser.Stack(context, stack_name, tmpl, template_params,
|
stack = parser.Stack(context, stack_name, tmpl, template_params,
|
||||||
**common_params)
|
**common_params)
|
||||||
|
|
||||||
@ -159,8 +141,7 @@ class EngineManager(manager.Manager):
|
|||||||
stack_name = 'validate'
|
stack_name = 'validate'
|
||||||
try:
|
try:
|
||||||
tmpl = parser.Template(template)
|
tmpl = parser.Template(template)
|
||||||
user_params = parser.Parameters(stack_name, tmpl,
|
user_params = parser.Parameters(stack_name, tmpl, params)
|
||||||
api.extract_user_params(params))
|
|
||||||
s = parser.Stack(context, stack_name, tmpl, user_params)
|
s = parser.Stack(context, stack_name, tmpl, user_params)
|
||||||
except KeyError as ex:
|
except KeyError as ex:
|
||||||
res = ('A Fn::FindInMap operation referenced '
|
res = ('A Fn::FindInMap operation referenced '
|
||||||
@ -272,8 +253,7 @@ class EngineManager(manager.Manager):
|
|||||||
if resource.id is None:
|
if resource.id is None:
|
||||||
raise AttributeError('Resource not created')
|
raise AttributeError('Resource not created')
|
||||||
|
|
||||||
return api.format_stack_resource(stack[resource_name],
|
return api.format_stack_resource(stack[resource_name])
|
||||||
api.KEYS_RESOURCE_DETAIL)
|
|
||||||
|
|
||||||
def describe_stack_resources(self, context, stack_name,
|
def describe_stack_resources(self, context, stack_name,
|
||||||
physical_resource_id, logical_resource_id):
|
physical_resource_id, logical_resource_id):
|
||||||
@ -299,7 +279,7 @@ class EngineManager(manager.Manager):
|
|||||||
else:
|
else:
|
||||||
name_match = lambda r: True
|
name_match = lambda r: True
|
||||||
|
|
||||||
return [api.format_stack_resource(resource, api.KEYS_RESOURCE)
|
return [api.format_stack_resource(resource)
|
||||||
for resource in stack if resource.id is not None and
|
for resource in stack if resource.id is not None and
|
||||||
name_match(resource)]
|
name_match(resource)]
|
||||||
|
|
||||||
@ -312,7 +292,7 @@ class EngineManager(manager.Manager):
|
|||||||
|
|
||||||
stack = parser.Stack.load(context, s.id)
|
stack = parser.Stack.load(context, s.id)
|
||||||
|
|
||||||
return [api.format_stack_resource(resource, api.KEYS_RESOURCE_SUMMARY)
|
return [api.format_stack_resource(resource)
|
||||||
for resource in stack if resource.id is not None]
|
for resource in stack if resource.id is not None]
|
||||||
|
|
||||||
def metadata_register_address(self, context, url):
|
def metadata_register_address(self, context, url):
|
||||||
|
91
heat/tests/test_api_v1.py
Normal file
91
heat/tests/test_api_v1.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
import nose
|
||||||
|
import unittest
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
|
||||||
|
from heat.common.config import HeatConfigOpts
|
||||||
|
import heat.api.v1.stacks as stacks
|
||||||
|
|
||||||
|
|
||||||
|
@attr(tag=['unit', 'api-v1-stacks', 'StackController'])
|
||||||
|
@attr(speed='fast')
|
||||||
|
class StackControllerTest(unittest.TestCase):
|
||||||
|
'''
|
||||||
|
Tests the API class which acts as the WSGI controller,
|
||||||
|
the endpoint processing API requests after they are routed
|
||||||
|
'''
|
||||||
|
def test_params_extract(self):
|
||||||
|
p = {'Parameters.member.Foo.ParameterKey': 'foo',
|
||||||
|
'Parameters.member.Foo.ParameterValue': 'bar',
|
||||||
|
'Parameters.member.Blarg.ParameterKey': 'blarg',
|
||||||
|
'Parameters.member.Blarg.ParameterValue': 'wibble'}
|
||||||
|
params = self.controller._extract_user_params(p)
|
||||||
|
self.assertEqual(len(params), 2)
|
||||||
|
self.assertTrue('foo' in params)
|
||||||
|
self.assertEqual(params['foo'], 'bar')
|
||||||
|
self.assertTrue('blarg' in params)
|
||||||
|
self.assertEqual(params['blarg'], 'wibble')
|
||||||
|
|
||||||
|
def test_params_extract_dots(self):
|
||||||
|
p = {'Parameters.member.Foo.Bar.ParameterKey': 'foo',
|
||||||
|
'Parameters.member.Foo.Bar.ParameterValue': 'bar',
|
||||||
|
'Parameters.member.Foo.Baz.ParameterKey': 'blarg',
|
||||||
|
'Parameters.member.Foo.Baz.ParameterValue': 'wibble'}
|
||||||
|
params = self.controller._extract_user_params(p)
|
||||||
|
self.assertEqual(len(params), 2)
|
||||||
|
self.assertTrue('foo' in params)
|
||||||
|
self.assertEqual(params['foo'], 'bar')
|
||||||
|
self.assertTrue('blarg' in params)
|
||||||
|
self.assertEqual(params['blarg'], 'wibble')
|
||||||
|
|
||||||
|
def test_params_extract_garbage(self):
|
||||||
|
p = {'Parameters.member.Foo.Bar.ParameterKey': 'foo',
|
||||||
|
'Parameters.member.Foo.Bar.ParameterValue': 'bar',
|
||||||
|
'Foo.Baz.ParameterKey': 'blarg',
|
||||||
|
'Foo.Baz.ParameterValue': 'wibble'}
|
||||||
|
params = self.controller._extract_user_params(p)
|
||||||
|
self.assertEqual(len(params), 1)
|
||||||
|
self.assertTrue('foo' in params)
|
||||||
|
self.assertEqual(params['foo'], 'bar')
|
||||||
|
|
||||||
|
def test_params_extract_garbage_prefix(self):
|
||||||
|
p = {'prefixParameters.member.Foo.Bar.ParameterKey': 'foo',
|
||||||
|
'Parameters.member.Foo.Bar.ParameterValue': 'bar'}
|
||||||
|
params = self.controller._extract_user_params(p)
|
||||||
|
self.assertFalse(params)
|
||||||
|
|
||||||
|
def test_params_extract_garbage_suffix(self):
|
||||||
|
p = {'Parameters.member.Foo.Bar.ParameterKeysuffix': 'foo',
|
||||||
|
'Parameters.member.Foo.Bar.ParameterValue': 'bar'}
|
||||||
|
params = self.controller._extract_user_params(p)
|
||||||
|
self.assertFalse(params)
|
||||||
|
|
||||||
|
# TODO : lots more StackController tests..
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# Create WSGI controller instance
|
||||||
|
options = HeatConfigOpts()
|
||||||
|
self.controller = stacks.StackController(options)
|
||||||
|
print "setup complete"
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
print "teardown complete"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.argv.append(__file__)
|
||||||
|
nose.main()
|
@ -23,69 +23,23 @@ import heat.engine.api as api
|
|||||||
@attr(tag=['unit', 'engine-api'])
|
@attr(tag=['unit', 'engine-api'])
|
||||||
@attr(speed='fast')
|
@attr(speed='fast')
|
||||||
class EngineApiTest(unittest.TestCase):
|
class EngineApiTest(unittest.TestCase):
|
||||||
def test_params_extract(self):
|
|
||||||
p = {'Parameters.member.Foo.ParameterKey': 'foo',
|
|
||||||
'Parameters.member.Foo.ParameterValue': 'bar',
|
|
||||||
'Parameters.member.Blarg.ParameterKey': 'blarg',
|
|
||||||
'Parameters.member.Blarg.ParameterValue': 'wibble'}
|
|
||||||
params = api.extract_user_params(p)
|
|
||||||
self.assertEqual(len(params), 2)
|
|
||||||
self.assertTrue('foo' in params)
|
|
||||||
self.assertEqual(params['foo'], 'bar')
|
|
||||||
self.assertTrue('blarg' in params)
|
|
||||||
self.assertEqual(params['blarg'], 'wibble')
|
|
||||||
|
|
||||||
def test_params_extract_dots(self):
|
|
||||||
p = {'Parameters.member.Foo.Bar.ParameterKey': 'foo',
|
|
||||||
'Parameters.member.Foo.Bar.ParameterValue': 'bar',
|
|
||||||
'Parameters.member.Foo.Baz.ParameterKey': 'blarg',
|
|
||||||
'Parameters.member.Foo.Baz.ParameterValue': 'wibble'}
|
|
||||||
params = api.extract_user_params(p)
|
|
||||||
self.assertEqual(len(params), 2)
|
|
||||||
self.assertTrue('foo' in params)
|
|
||||||
self.assertEqual(params['foo'], 'bar')
|
|
||||||
self.assertTrue('blarg' in params)
|
|
||||||
self.assertEqual(params['blarg'], 'wibble')
|
|
||||||
|
|
||||||
def test_params_extract_garbage(self):
|
|
||||||
p = {'Parameters.member.Foo.Bar.ParameterKey': 'foo',
|
|
||||||
'Parameters.member.Foo.Bar.ParameterValue': 'bar',
|
|
||||||
'Foo.Baz.ParameterKey': 'blarg',
|
|
||||||
'Foo.Baz.ParameterValue': 'wibble'}
|
|
||||||
params = api.extract_user_params(p)
|
|
||||||
self.assertEqual(len(params), 1)
|
|
||||||
self.assertTrue('foo' in params)
|
|
||||||
self.assertEqual(params['foo'], 'bar')
|
|
||||||
|
|
||||||
def test_params_extract_garbage_prefix(self):
|
|
||||||
p = {'prefixParameters.member.Foo.Bar.ParameterKey': 'foo',
|
|
||||||
'Parameters.member.Foo.Bar.ParameterValue': 'bar'}
|
|
||||||
params = api.extract_user_params(p)
|
|
||||||
self.assertFalse(params)
|
|
||||||
|
|
||||||
def test_params_extract_garbage_suffix(self):
|
|
||||||
p = {'Parameters.member.Foo.Bar.ParameterKeysuffix': 'foo',
|
|
||||||
'Parameters.member.Foo.Bar.ParameterValue': 'bar'}
|
|
||||||
params = api.extract_user_params(p)
|
|
||||||
self.assertFalse(params)
|
|
||||||
|
|
||||||
def test_timeout_extract(self):
|
def test_timeout_extract(self):
|
||||||
p = {'TimeoutInMinutes': '5'}
|
p = {'timeout_mins': '5'}
|
||||||
args = api.extract_args(p)
|
args = api.extract_args(p)
|
||||||
self.assertEqual(args['timeout_mins'], 5)
|
self.assertEqual(args['timeout_mins'], 5)
|
||||||
|
|
||||||
def test_timeout_extract_zero(self):
|
def test_timeout_extract_zero(self):
|
||||||
p = {'TimeoutInMinutes': '0'}
|
p = {'timeout_mins': '0'}
|
||||||
args = api.extract_args(p)
|
args = api.extract_args(p)
|
||||||
self.assertTrue('timeout_mins' not in args)
|
self.assertTrue('timeout_mins' not in args)
|
||||||
|
|
||||||
def test_timeout_extract_garbage(self):
|
def test_timeout_extract_garbage(self):
|
||||||
p = {'TimeoutInMinutes': 'wibble'}
|
p = {'timeout_mins': 'wibble'}
|
||||||
args = api.extract_args(p)
|
args = api.extract_args(p)
|
||||||
self.assertTrue('timeout_mins' not in args)
|
self.assertTrue('timeout_mins' not in args)
|
||||||
|
|
||||||
def test_timeout_extract_none(self):
|
def test_timeout_extract_none(self):
|
||||||
p = {'TimeoutInMinutes': None}
|
p = {'timeout_mins': None}
|
||||||
args = api.extract_args(p)
|
args = api.extract_args(p)
|
||||||
self.assertTrue('timeout_mins' not in args)
|
self.assertTrue('timeout_mins' not in args)
|
||||||
|
|
||||||
|
@ -172,63 +172,48 @@ class stackManagerTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(events), 2)
|
self.assertEqual(len(events), 2)
|
||||||
for ev in events:
|
for ev in events:
|
||||||
self.assertTrue('EventId' in ev)
|
self.assertTrue('event_id' in ev)
|
||||||
self.assertTrue(ev['EventId'] > 0)
|
self.assertTrue(ev['event_id'] > 0)
|
||||||
|
|
||||||
self.assertTrue('LogicalResourceId' in ev)
|
self.assertTrue('logical_resource_id' in ev)
|
||||||
self.assertEqual(ev['LogicalResourceId'], 'WebServer')
|
self.assertEqual(ev['logical_resource_id'], 'WebServer')
|
||||||
|
|
||||||
self.assertTrue('PhysicalResourceId' in ev)
|
self.assertTrue('physical_resource_id' in ev)
|
||||||
|
|
||||||
self.assertTrue('ResourceProperties' in ev)
|
self.assertTrue('resource_properties' in ev)
|
||||||
# Big long user data field.. it mentions 'wordpress'
|
# Big long user data field.. it mentions 'wordpress'
|
||||||
# a few times so this should work.
|
# a few times so this should work.
|
||||||
user_data = ev['ResourceProperties']['UserData']
|
user_data = ev['resource_properties']['UserData']
|
||||||
self.assertNotEqual(user_data.find('wordpress'), -1)
|
self.assertNotEqual(user_data.find('wordpress'), -1)
|
||||||
self.assertEqual(ev['ResourceProperties']['ImageId'],
|
self.assertEqual(ev['resource_properties']['ImageId'],
|
||||||
'F16-x86_64-gold')
|
'F16-x86_64-gold')
|
||||||
self.assertEqual(ev['ResourceProperties']['InstanceType'],
|
self.assertEqual(ev['resource_properties']['InstanceType'],
|
||||||
'm1.large')
|
'm1.large')
|
||||||
|
|
||||||
self.assertTrue('ResourceStatus' in ev)
|
self.assertTrue('resource_status' in ev)
|
||||||
self.assertTrue(ev['ResourceStatus'] in ('IN_PROGRESS',
|
self.assertTrue(ev['resource_status'] in ('IN_PROGRESS',
|
||||||
'CREATE_COMPLETE'))
|
'CREATE_COMPLETE'))
|
||||||
|
|
||||||
self.assertTrue('ResourceStatusReason' in ev)
|
self.assertTrue('resource_status_reason' in ev)
|
||||||
self.assertEqual(ev['ResourceStatusReason'], 'state changed')
|
self.assertEqual(ev['resource_status_reason'], 'state changed')
|
||||||
|
|
||||||
self.assertTrue('ResourceType' in ev)
|
self.assertTrue('resource_type' in ev)
|
||||||
self.assertEqual(ev['ResourceType'], 'AWS::EC2::Instance')
|
self.assertEqual(ev['resource_type'], 'AWS::EC2::Instance')
|
||||||
|
|
||||||
self.assertTrue('StackId' in ev)
|
self.assertTrue('stack_id' in ev)
|
||||||
|
|
||||||
self.assertTrue('StackName' in ev)
|
self.assertTrue('stack_name' in ev)
|
||||||
self.assertEqual(ev['StackName'], self.stack_name)
|
self.assertEqual(ev['stack_name'], self.stack_name)
|
||||||
|
|
||||||
self.assertTrue('Timestamp' in ev)
|
self.assertTrue('event_time' in ev)
|
||||||
|
|
||||||
def test_stack_list(self):
|
|
||||||
sl = self.man.list_stacks(self.ctx, {})
|
|
||||||
|
|
||||||
self.assertTrue(len(sl['stacks']) > 0)
|
|
||||||
for s in sl['stacks']:
|
|
||||||
self.assertTrue('CreationTime' in s)
|
|
||||||
self.assertTrue('LastUpdatedTime' in s)
|
|
||||||
self.assertTrue('StackId' in s)
|
|
||||||
self.assertNotEqual(s['StackId'], None)
|
|
||||||
self.assertTrue('StackName' in s)
|
|
||||||
self.assertTrue('StackStatus' in s)
|
|
||||||
self.assertTrue('StackStatusReason' in s)
|
|
||||||
self.assertTrue('TemplateDescription' in s)
|
|
||||||
self.assertNotEqual(s['TemplateDescription'].find('WordPress'), -1)
|
|
||||||
|
|
||||||
def test_stack_describe_all(self):
|
def test_stack_describe_all(self):
|
||||||
sl = self.man.show_stack(self.ctx, None, {})
|
sl = self.man.show_stack(self.ctx, None, {})
|
||||||
|
|
||||||
self.assertEqual(len(sl['stacks']), 1)
|
self.assertEqual(len(sl['stacks']), 1)
|
||||||
for s in sl['stacks']:
|
for s in sl['stacks']:
|
||||||
self.assertNotEqual(s['StackId'], None)
|
self.assertNotEqual(s['stack_id'], None)
|
||||||
self.assertNotEqual(s['Description'].find('WordPress'), -1)
|
self.assertNotEqual(s['description'].find('WordPress'), -1)
|
||||||
|
|
||||||
def test_stack_describe_all_empty(self):
|
def test_stack_describe_all_empty(self):
|
||||||
self.tearDown()
|
self.tearDown()
|
||||||
@ -250,35 +235,35 @@ class stackManagerTest(unittest.TestCase):
|
|||||||
self.assertEqual(len(sl['stacks']), 1)
|
self.assertEqual(len(sl['stacks']), 1)
|
||||||
|
|
||||||
s = sl['stacks'][0]
|
s = sl['stacks'][0]
|
||||||
self.assertTrue('CreationTime' in s)
|
self.assertTrue('creation_time' in s)
|
||||||
self.assertTrue('LastUpdatedTime' in s)
|
self.assertTrue('updated_time' in s)
|
||||||
self.assertTrue('StackId' in s)
|
self.assertTrue('stack_id' in s)
|
||||||
self.assertNotEqual(s['StackId'], None)
|
self.assertNotEqual(s['stack_id'], None)
|
||||||
self.assertTrue('StackName' in s)
|
self.assertTrue('stack_name' in s)
|
||||||
self.assertEqual(s['StackName'], self.stack_name)
|
self.assertEqual(s['stack_name'], self.stack_name)
|
||||||
self.assertTrue('StackStatus' in s)
|
self.assertTrue('stack_status' in s)
|
||||||
self.assertTrue('StackStatusReason' in s)
|
self.assertTrue('stack_status_reason' in s)
|
||||||
self.assertTrue('Description' in s)
|
self.assertTrue('description' in s)
|
||||||
self.assertNotEqual(s['Description'].find('WordPress'), -1)
|
self.assertNotEqual(s['description'].find('WordPress'), -1)
|
||||||
self.assertTrue('Parameters' in s)
|
self.assertTrue('parameters' in s)
|
||||||
|
|
||||||
def test_stack_resource_describe(self):
|
def test_stack_resource_describe(self):
|
||||||
r = self.man.describe_stack_resource(self.ctx, self.stack_name,
|
r = self.man.describe_stack_resource(self.ctx, self.stack_name,
|
||||||
'WebServer')
|
'WebServer')
|
||||||
|
|
||||||
self.assertTrue('Description' in r)
|
self.assertTrue('description' in r)
|
||||||
self.assertTrue('LastUpdatedTimestamp' in r)
|
self.assertTrue('updated_time' in r)
|
||||||
self.assertTrue('StackId' in r)
|
self.assertTrue('stack_id' in r)
|
||||||
self.assertNotEqual(r['StackId'], None)
|
self.assertNotEqual(r['stack_id'], None)
|
||||||
self.assertTrue('StackName' in r)
|
self.assertTrue('stack_name' in r)
|
||||||
self.assertEqual(r['StackName'], self.stack_name)
|
self.assertEqual(r['stack_name'], self.stack_name)
|
||||||
self.assertTrue('Metadata' in r)
|
self.assertTrue('metadata' in r)
|
||||||
self.assertTrue('ResourceStatus' in r)
|
self.assertTrue('resource_status' in r)
|
||||||
self.assertTrue('ResourceStatusReason' in r)
|
self.assertTrue('resource_status_reason' in r)
|
||||||
self.assertTrue('ResourceType' in r)
|
self.assertTrue('resource_type' in r)
|
||||||
self.assertTrue('PhysicalResourceId' in r)
|
self.assertTrue('physical_resource_id' in r)
|
||||||
self.assertTrue('LogicalResourceId' in r)
|
self.assertTrue('logical_resource_id' in r)
|
||||||
self.assertEqual(r['LogicalResourceId'], 'WebServer')
|
self.assertEqual(r['logical_resource_id'], 'WebServer')
|
||||||
|
|
||||||
def test_stack_resource_describe_nonexist_stack(self):
|
def test_stack_resource_describe_nonexist_stack(self):
|
||||||
self.assertRaises(AttributeError,
|
self.assertRaises(AttributeError,
|
||||||
@ -297,18 +282,18 @@ class stackManagerTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(resources), 1)
|
self.assertEqual(len(resources), 1)
|
||||||
r = resources[0]
|
r = resources[0]
|
||||||
self.assertTrue('Description' in r)
|
self.assertTrue('description' in r)
|
||||||
self.assertTrue('Timestamp' in r)
|
self.assertTrue('updated_time' in r)
|
||||||
self.assertTrue('StackId' in r)
|
self.assertTrue('stack_id' in r)
|
||||||
self.assertNotEqual(r['StackId'], None)
|
self.assertNotEqual(r['stack_id'], None)
|
||||||
self.assertTrue('StackName' in r)
|
self.assertTrue('stack_name' in r)
|
||||||
self.assertEqual(r['StackName'], self.stack_name)
|
self.assertEqual(r['stack_name'], self.stack_name)
|
||||||
self.assertTrue('ResourceStatus' in r)
|
self.assertTrue('resource_status' in r)
|
||||||
self.assertTrue('ResourceStatusReason' in r)
|
self.assertTrue('resource_status_reason' in r)
|
||||||
self.assertTrue('ResourceType' in r)
|
self.assertTrue('resource_type' in r)
|
||||||
self.assertTrue('PhysicalResourceId' in r)
|
self.assertTrue('physical_resource_id' in r)
|
||||||
self.assertTrue('LogicalResourceId' in r)
|
self.assertTrue('logical_resource_id' in r)
|
||||||
self.assertEqual(r['LogicalResourceId'], 'WebServer')
|
self.assertEqual(r['logical_resource_id'], 'WebServer')
|
||||||
|
|
||||||
def test_stack_resources_describe_no_filter(self):
|
def test_stack_resources_describe_no_filter(self):
|
||||||
resources = self.man.describe_stack_resources(self.ctx,
|
resources = self.man.describe_stack_resources(self.ctx,
|
||||||
@ -317,8 +302,8 @@ class stackManagerTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(resources), 1)
|
self.assertEqual(len(resources), 1)
|
||||||
r = resources[0]
|
r = resources[0]
|
||||||
self.assertTrue('LogicalResourceId' in r)
|
self.assertTrue('logical_resource_id' in r)
|
||||||
self.assertEqual(r['LogicalResourceId'], 'WebServer')
|
self.assertEqual(r['logical_resource_id'], 'WebServer')
|
||||||
|
|
||||||
def test_stack_resources_describe_bad_lookup(self):
|
def test_stack_resources_describe_bad_lookup(self):
|
||||||
self.assertRaises(AttributeError,
|
self.assertRaises(AttributeError,
|
||||||
@ -340,13 +325,13 @@ class stackManagerTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(resources), 1)
|
self.assertEqual(len(resources), 1)
|
||||||
r = resources[0]
|
r = resources[0]
|
||||||
self.assertTrue('LastUpdatedTimestamp' in r)
|
self.assertTrue('updated_time' in r)
|
||||||
self.assertTrue('PhysicalResourceId' in r)
|
self.assertTrue('physical_resource_id' in r)
|
||||||
self.assertTrue('LogicalResourceId' in r)
|
self.assertTrue('logical_resource_id' in r)
|
||||||
self.assertEqual(r['LogicalResourceId'], 'WebServer')
|
self.assertEqual(r['logical_resource_id'], 'WebServer')
|
||||||
self.assertTrue('ResourceStatus' in r)
|
self.assertTrue('resource_status' in r)
|
||||||
self.assertTrue('ResourceStatusReason' in r)
|
self.assertTrue('resource_status_reason' in r)
|
||||||
self.assertTrue('ResourceType' in r)
|
self.assertTrue('resource_type' in r)
|
||||||
|
|
||||||
def test_stack_resources_list_nonexist_stack(self):
|
def test_stack_resources_list_nonexist_stack(self):
|
||||||
self.assertRaises(AttributeError,
|
self.assertRaises(AttributeError,
|
||||||
|
Loading…
Reference in New Issue
Block a user