From 582b8b29cb443410ee87f8518e648aefdc42014f Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Sun, 17 Jun 2012 16:05:57 +0200 Subject: [PATCH] Move parsing of parameters out of template parser The format of the params received by the manager is a property of the Heat API protocol, not of the template, so it makes sense not to have the parser extracting the template parameter data from them. This simplifies the implementation of #123 (Nested Stacks). Change-Id: I8eaf50caf79f69359cbc8ee1f0193c08d7944d1c Signed-off-by: Zane Bitter --- heat/engine/manager.py | 35 +++++++++++++++++++---- heat/engine/parser.py | 15 +--------- heat/tests/test_manager.py | 55 ++++++++++++++++++++++++++++++++++++ heat/tests/test_resources.py | 8 ++---- 4 files changed, 87 insertions(+), 26 deletions(-) create mode 100644 heat/tests/test_manager.py diff --git a/heat/engine/manager.py b/heat/engine/manager.py index 1faad3bc6c..51271b2330 100644 --- a/heat/engine/manager.py +++ b/heat/engine/manager.py @@ -20,6 +20,7 @@ import logging import webob import json import urlparse +import re import httplib import eventlet @@ -39,6 +40,25 @@ from novaclient.exceptions import AuthorizationFailure logger = logging.getLogger('heat.engine.manager') greenpool = eventlet.GreenPool() +_param_key = re.compile(r'Parameters\.member\.(.*?)\.ParameterKey$') + + +def _extract_user_params(params): + def get_param_pairs(): + for k in params: + keymatch = _param_key.match(k) + if keymatch: + key = params[k] + v = 'Parameters.member.%s.ParameterValue' % 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()) + class EngineManager(manager.Manager): """ @@ -126,7 +146,7 @@ class EngineManager(manager.Manager): for s in stacks: ps = parser.Stack(context, s.name, s.raw_template.parsed_template.template, - s.id, params) + s.id, _extract_user_params(params)) mem = {} mem['StackId'] = s.id mem['StackName'] = s.name @@ -152,7 +172,7 @@ class EngineManager(manager.Manager): if s: ps = parser.Stack(context, s.name, s.raw_template.parsed_template.template, - s.id, params) + s.id, _extract_user_params(params)) mem = {} mem['StackId'] = s.id mem['StackName'] = s.name @@ -193,12 +213,14 @@ class EngineManager(manager.Manager): if db_api.stack_get_by_name(None, stack_name): return {'Error': 'Stack already exists with that name.'} + user_params = _extract_user_params(params) metadata_server = config.FLAGS.heat_metadata_server_url # We don't want to reset the stack template, so we are making # an instance just for validation. template_copy = deepcopy(template) stack_validator = parser.Stack(context, stack_name, - template_copy, 0, params, + template_copy, 0, + user_params, metadata_server=metadata_server) response = stack_validator.validate() stack_validator = None @@ -207,7 +229,7 @@ class EngineManager(manager.Manager): response['ValidateTemplateResult']['Description']: return response - stack = parser.Stack(context, stack_name, template, 0, params, + stack = parser.Stack(context, stack_name, template, 0, user_params, metadata_server=metadata_server) rt = {} rt['template'] = template @@ -253,7 +275,8 @@ class EngineManager(manager.Manager): return webob.exc.HTTPBadRequest(explanation=msg) try: - s = parser.Stack(context, 'validate', template, 0, params) + s = parser.Stack(context, 'validate', template, 0, + _extract_user_params(params)) except KeyError as ex: res = ('A Fn::FindInMap operation referenced ' 'a non-existent map [%s]' % str(ex)) @@ -298,7 +321,7 @@ class EngineManager(manager.Manager): ps = parser.Stack(context, st.name, st.raw_template.parsed_template.template, - st.id, params) + st.id, _extract_user_params(params)) greenpool.spawn_n(ps.delete) return None diff --git a/heat/engine/parser.py b/heat/engine/parser.py index a215dc76f1..5b0f3b38ea 100644 --- a/heat/engine/parser.py +++ b/heat/engine/parser.py @@ -68,7 +68,7 @@ class Stack(object): # user Parameters if parms is not None: - self._apply_user_parameters(parms) + self.parms.update(parms) self.resources = dict((name, Resource(name, data, self)) @@ -295,19 +295,6 @@ class Stack(object): # TODO(asalkeld) if any of this fails we Should # restart the whole stack - def _apply_user_parameters(self, parms): - for p in parms: - if 'Parameters.member.' in p and 'ParameterKey' in p: - s = p.split('.') - try: - key_name = 'Parameters.member.%s.ParameterKey' % s[2] - value_name = 'Parameters.member.%s.ParameterValue' % s[2] - logger.debug('applying user parameter %s=%s' % - (key_name, value_name)) - self.parms[parms[key_name]] = parms[value_name] - except Exception: - logger.error('Could not apply parameter %s' % p) - def parameter_get(self, key): if not key in self.parms: raise exception.UserParameterMissing(key=key) diff --git a/heat/tests/test_manager.py b/heat/tests/test_manager.py new file mode 100644 index 0000000000..a9943c186a --- /dev/null +++ b/heat/tests/test_manager.py @@ -0,0 +1,55 @@ +import nose +import unittest +from nose.plugins.attrib import attr + +import heat.engine.manager as manager + + +@attr(tag=['unit', 'manager']) +@attr(speed='fast') +class managerTest(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 = manager._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 = manager._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 = manager._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 = manager._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 = manager._extract_user_params(p) + self.assertFalse(params) diff --git a/heat/tests/test_resources.py b/heat/tests/test_resources.py index 59a1075e1e..dededd0968 100644 --- a/heat/tests/test_resources.py +++ b/heat/tests/test_resources.py @@ -34,11 +34,9 @@ class instancesTest(unittest.TestCase): t = json.loads(f.read()) f.close() - params = {} parameters = {} - params['KeyStoneCreds'] = None t['Parameters']['KeyName']['Value'] = 'test' - stack = parser.Stack(None, 'test_stack', t, 0, params) + stack = parser.Stack(None, 'test_stack', t, 0) self.m.StubOutWithMock(db_api, 'resource_get_by_name_and_stack') db_api.resource_get_by_name_and_stack(None, 'test_resource_name', @@ -82,11 +80,9 @@ class instancesTest(unittest.TestCase): t = json.loads(f.read()) f.close() - params = {} parameters = {} - params['KeyStoneCreds'] = None t['Parameters']['KeyName']['Value'] = 'test' - stack = parser.Stack(None, 'test_stack', t, 0, params) + stack = parser.Stack(None, 'test_stack', t, 0) self.m.StubOutWithMock(db_api, 'resource_get_by_name_and_stack') db_api.resource_get_by_name_and_stack(None, 'test_resource_name',