heat API : Move aws api common code into aws/utils.py

Move heat-api AWS common utility functions into a new
utils.py, so these functions can be reused by cloudwatch

Change-Id: I030d796b1048ffc4e7c40f7c8760121ab2854733
Signed-off-by: Steven Hardy <shardy@redhat.com>
This commit is contained in:
Steven Hardy 2012-08-21 11:52:24 +01:00
parent 1b6c2dad19
commit ecc5a408a3
4 changed files with 200 additions and 135 deletions

78
heat/api/aws/utils.py Normal file
View File

@ -0,0 +1,78 @@
# 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.
'''
Helper utilities related to the AWS API implementations
'''
import re
def format_response(action, response):
"""
Format response from engine into API format
"""
return {'%sResponse' % action: {'%sResult' % action: response}}
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())
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

View File

@ -21,10 +21,10 @@ import json
import os
import socket
import sys
import re
import urlparse
import webob
from heat.api.aws import exception
from heat.api.aws import utils as api_utils
from heat.common import wsgi
from heat.common import config
from heat.common import context
@ -62,63 +62,6 @@ class StackController(object):
str(resp['StackId'])])
return resp
def _format_response(self, action, response):
"""
Format response from engine into API format
"""
return {'%sResponse' % action: {'%sResult' % action: response}}
@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):
"""
Implements ListStacks API action
@ -140,7 +83,7 @@ class StackController(object):
engine_api.STACK_TMPL_DESCRIPTION: 'TemplateDescription',
}
result = self._reformat_dict_keys(keymap, s)
result = api_utils.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
@ -165,7 +108,7 @@ class StackController(object):
res = {'StackSummaries': [format_stack_summary(s)
for s in stack_list['stacks']]}
return self._format_response('ListStacks', res)
return api_utils.format_response('ListStacks', res)
def describe(self, req):
"""
@ -179,7 +122,7 @@ class StackController(object):
engine_api.OUTPUT_VALUE: 'OutputValue',
}
return self._reformat_dict_keys(keymap, o)
return api_utils.reformat_dict_keys(keymap, o)
def format_stack(s):
"""
@ -200,7 +143,7 @@ class StackController(object):
engine_api.STACK_TIMEOUT: 'TimeoutInMinutes',
}
result = self._reformat_dict_keys(keymap, s)
result = api_utils.reformat_dict_keys(keymap, s)
# Reformat outputs, these are handled separately as they are
# only present in the engine output for a completely created
@ -239,7 +182,7 @@ class StackController(object):
res = {'Stacks': [format_stack(s) for s in stack_list['stacks']]}
return self._format_response('DescribeStacks', res)
return api_utils.format_response('DescribeStacks', res)
def _get_template(self, req):
"""
@ -310,7 +253,7 @@ class StackController(object):
con = req.context
# Extract the stack input parameters
stack_parms = self._extract_user_params(req.params)
stack_parms = api_utils.extract_user_params(req.params)
# Extract any additional arguments ("Request Parameters")
create_args = extract_args(req.params)
@ -340,7 +283,7 @@ class StackController(object):
except rpc_common.RemoteError as ex:
return exception.map_remote_error(ex)
return self._format_response(action, self._stackid_addprefix(res))
return api_utils.format_response(action, self._stackid_addprefix(res))
def get_template(self, req):
"""
@ -363,14 +306,15 @@ class StackController(object):
msg = _('stack not not found')
return exception.HeatInvalidParameterValueError(detail=msg)
return self._format_response('GetTemplate', {'TemplateBody': templ})
return api_utils.format_response('GetTemplate',
{'TemplateBody': templ})
def estimate_template_cost(self, req):
"""
Implements the EstimateTemplateCost API action
Get the estimated monthly cost of a template
"""
return self._format_response('EstimateTemplateCost',
return api_utils.format_response('EstimateTemplateCost',
{'Url': 'http://en.wikipedia.org/wiki/Gratis'})
def validate_template(self, req):
@ -423,9 +367,9 @@ class StackController(object):
return exception.map_remote_error(ex)
if res is None:
return self._format_response('DeleteStack', '')
return api_utils.format_response('DeleteStack', '')
else:
return self._format_response('DeleteStack', res['Error'])
return api_utils.format_response('DeleteStack', res['Error'])
def events_list(self, req):
"""
@ -449,7 +393,7 @@ class StackController(object):
engine_api.EVENT_TIMESTAMP: 'Timestamp',
}
result = self._reformat_dict_keys(keymap, e)
result = api_utils.reformat_dict_keys(keymap, e)
return self._stackid_addprefix(result)
@ -468,7 +412,7 @@ class StackController(object):
result = [format_stack_event(e) for e in events]
return self._format_response('DescribeStackEvents',
return api_utils.format_response('DescribeStackEvents',
{'StackEvents': result})
def describe_stack_resource(self, req):
@ -494,7 +438,7 @@ class StackController(object):
engine_api.RES_STACK_NAME: 'StackName',
}
result = self._reformat_dict_keys(keymap, r)
result = api_utils.reformat_dict_keys(keymap, r)
return self._stackid_addprefix(result)
@ -510,7 +454,7 @@ class StackController(object):
result = format_resource_detail(resource_details)
return self._format_response('DescribeStackResource',
return api_utils.format_response('DescribeStackResource',
{'StackResourceDetail': result})
def describe_stack_resources(self, req):
@ -546,7 +490,7 @@ class StackController(object):
engine_api.RES_UPDATED_TIME: 'Timestamp',
}
result = self._reformat_dict_keys(keymap, r)
result = api_utils.reformat_dict_keys(keymap, r)
return self._stackid_addprefix(result)
@ -568,7 +512,7 @@ class StackController(object):
result = [format_stack_resource(r) for r in resources]
return self._format_response('DescribeStackResources',
return api_utils.format_response('DescribeStackResources',
{'StackResources': result})
def list_stack_resources(self, req):
@ -589,7 +533,7 @@ class StackController(object):
engine_api.RES_TYPE: 'ResourceType',
}
return self._reformat_dict_keys(keymap, r)
return api_utils.reformat_dict_keys(keymap, r)
con = req.context
@ -601,7 +545,7 @@ class StackController(object):
summaries = [format_resource_summary(r) for r in resources]
return self._format_response('ListStackResources',
return api_utils.format_response('ListStackResources',
{'StackResourceSummaries': summaries})

101
heat/tests/test_api_aws.py Normal file
View File

@ -0,0 +1,101 @@
# 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 sys
import socket
import nose
import json
import unittest
from nose.plugins.attrib import attr
import re
from heat.api.aws import utils as api_utils
@attr(tag=['unit', 'api-aws', 'AWSCommon'])
@attr(speed='fast')
class AWSCommon(unittest.TestCase):
'''
Tests the api/aws common componenents
'''
# The tests
def test_format_response(self):
response = api_utils.format_response("Foo", "Bar")
expected = {'FooResponse': {'FooResult': 'Bar'}}
self.assert_(response == expected)
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_utils.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_utils.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_utils.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_utils.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_utils.extract_user_params(p)
self.assertFalse(params)
def test_reformat_dict_keys(self):
keymap = {"foo": "bar"}
data = {"foo": 123}
expected = {"bar": 123}
result = api_utils.reformat_dict_keys(keymap, data)
self.assertEqual(result, expected)
def setUp(self):
print "setup complete"
def tearDown(self):
print "teardown complete"
if __name__ == '__main__':
sys.argv.append(__file__)
nose.main()

View File

@ -60,11 +60,6 @@ class StackControllerTest(unittest.TestCase):
return req
# The tests
def test_format_response(self):
response = self.controller._format_response("Foo", "Bar")
expected = {'FooResponse': {'FooResult': 'Bar'}}
self.assert_(response == expected)
def test_stackid_addprefix(self):
# Stub socket.gethostname so it returns "ahostname"
@ -79,59 +74,6 @@ class StackControllerTest(unittest.TestCase):
'StackId': 'ahostname:8000:stack/Foo/123'}
self.assert_(response == expected)
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)
def test_reformat_dict_keys(self):
keymap = {"foo": "bar"}
data = {"foo": 123}
expected = {"bar": 123}
result = self.controller._reformat_dict_keys(keymap, data)
self.assertEqual(result, expected)
def test_list(self):
# Format a dummy GET request to pass into the WSGI handler
params = {'Action': 'ListStacks'}