import re
from heat.common import exception
-
+from heat.engine import parameters
SCHEMA_KEYS = (
REQUIRED, IMPLEMENTED, DEFAULT, TYPE, SCHEMA,
def __iter__(self):
return iter(self.props)
+
+ @staticmethod
+ def _generate_input(schema, params=None, path=None):
+ '''Generate an input based on a path in the schema or property
+ defaults.
+
+ :param schema: The schema to generate a parameter or value for.
+ :param params: A dict to map a schema to a parameter path.
+ :param path: Required if params != None. The params key
+ to save the schema at.
+ :returns: A Ref to the parameter if path != None and params != None
+ :returns: The property default if params == None or path == None
+ '''
+ if schema.get('Implemented') is False:
+ return
+
+ if schema[TYPE] == LIST:
+ params[path] = {parameters.TYPE: parameters.COMMA_DELIMITED_LIST}
+ return {'Fn::Split': {'Ref': path}}
+
+ elif schema[TYPE] == MAP:
+ params[path] = {parameters.TYPE: parameters.JSON}
+ return {'Ref': path}
+
+ elif params is not None and path is not None:
+ for prop in schema.keys():
+ if prop not in parameters.PARAMETER_KEYS and prop in schema:
+ del schema[prop]
+ params[path] = schema
+ return {'Ref': path}
+ else:
+ prop = Property(schema)
+ return prop.has_default() and prop.default() or None
+
+ @staticmethod
+ def _schema_to_params_and_props(schema, params=None):
+ '''Generates a default template based on the provided schema.
+
+ ex: input: schema = {'foo': {'Type': 'String'}}, params = {}
+ output: {'foo': {'Ref': 'foo'}},
+ params = {'foo': {'Type': 'String'}}
+
+ ex: input: schema = {'foo' :{'Type': 'List'}, 'bar': {'Type': 'Map'}}
+ ,params={}
+ output: {'foo': {'Fn::Split': {'Ref': 'foo'}},
+ 'bar': {'Ref': 'bar'}},
+ params = {'foo' : {parameters.TYPE:
+ parameters.COMMA_DELIMITED_LIST},
+ 'bar': {parameters.TYPE: parameters.JSON}}
+
+ :param schema: The schema to generate a parameter or value for.
+ :param params: A dict to map a schema to a parameter path.
+ :returns: A dict of properties resolved for a template's schema
+ '''
+ properties = {}
+ for prop, nested_schema in schema.iteritems():
+ properties[prop] = Properties._generate_input(nested_schema,
+ params,
+ prop)
+ #remove not implemented properties
+ if properties[prop] is None:
+ del properties[prop]
+ return properties
+
+ @staticmethod
+ def schema_to_parameters_and_properties(schema):
+ '''Generates properties with params resolved for a resource's
+ properties_schema.
+ :param schema: A resource's properties_schema
+ :param explode_nested: True if a resource's nested properties schema
+ should be resolved.
+ :returns: A tuple of params and properties dicts
+ '''
+ params = {}
+ properties = (Properties.
+ _schema_to_params_and_props(schema, params=params))
+ return (params, properties)
if new_metadata:
logger.warning("Resource %s does not implement metadata update" %
self.name)
+
+ @classmethod
+ def resource_to_template(cls, resource_type):
+ '''
+ :param resource_type: The resource type to be displayed in the template
+ :param explode_nested: True if a resource's nested properties schema
+ should be resolved.
+ :returns: A template where the resource's properties_schema is mapped
+ as parameters, and the resource's attributes_schema is mapped as
+ outputs
+ '''
+ (parameters, properties) = (Properties.
+ schema_to_parameters_and_properties(
+ cls.properties_schema))
+
+ resource_name = cls.__name__
+ return {
+ 'Parameters': parameters,
+ 'Resources': {
+ resource_name: {
+ 'Type': resource_type,
+ 'Properties': properties
+ }
+ },
+ 'Outputs': Attributes.as_outputs(resource_name, cls)
+ }
schema = {'foo': {'Type': 'List', 'Default': ['one', 'two']}}
props = properties.Properties(schema, {'foo': None})
self.assertEqual(props.validate(), None)
+
+ def test_schema_to_template_nested_map_map_schema(self):
+ nested_schema = {'Key': {'Type': 'String',
+ 'Required': True},
+ 'Value': {'Type': 'String',
+ 'Required': True,
+ 'Default': 'fewaf'}}
+ schema = {'foo': {'Type': 'Map', 'Schema': {'Type': 'Map',
+ 'Schema': nested_schema}}}
+
+ prop_expected = {'foo': {'Ref': 'foo'}}
+ param_expected = {'foo': {'Type': 'Json'}}
+ (parameters, props) = \
+ properties.Properties.schema_to_parameters_and_properties(schema)
+ self.assertEquals(param_expected, parameters)
+ self.assertEquals(prop_expected, props)
+
+ def test_schema_to_template_nested_map_list_map_schema(self):
+ key_schema = {'bar': {'Type': 'Number'}}
+ nested_schema = {'Key': {'Type': 'Map', 'Schema': {'Type': 'Map',
+ 'Schema': key_schema}},
+ 'Value': {'Type': 'String',
+ 'Required': True}}
+ schema = {'foo': {'Type': 'List', 'Schema': {'Type': 'Map',
+ 'Schema': nested_schema}}}
+
+ prop_expected = {'foo': {'Fn::Split': {'Ref': 'foo'}}}
+ param_expected = {'foo': {'Type': 'CommaDelimitedList'}}
+ (parameters, props) = \
+ properties.Properties.schema_to_parameters_and_properties(schema)
+ self.assertEquals(param_expected, parameters)
+ self.assertEquals(prop_expected, props)
+
+ def test_schema_invalid_parameters_stripped(self):
+ schema = {'foo': {'Type': 'String',
+ 'Required': True,
+ 'Implemented': True}}
+
+ prop_expected = {'foo': {'Ref': 'foo'}}
+ param_expected = {'foo': {'Type': 'String'}}
+
+ (parameters, props) = \
+ properties.Properties.schema_to_parameters_and_properties(schema)
+ self.assertEquals(param_expected, parameters)
+ self.assertEquals(prop_expected, props)
self.assertRaises(exception.ResourceFailure, resume)
self.assertEqual((res.RESUME, res.FAILED), res.state)
+ def test_resource_class_to_template(self):
+
+ class TestResource(resource.Resource):
+ list_schema = {'wont_show_up': {'Type': 'Number'}}
+ map_schema = {'will_show_up': {'Type': 'Integer'}}
+
+ properties_schema = {
+ 'name': {'Type': 'String'},
+ 'bool': {'Type': 'Boolean'},
+ 'implemented': {'Type': 'String',
+ 'Implemented': True,
+ 'AllowedPattern': '.*',
+ 'MaxLength': 7,
+ 'MinLength': 2,
+ 'Required': True},
+ 'not_implemented': {'Type': 'String',
+ 'Implemented': False},
+ 'number': {'Type': 'Number',
+ 'MaxValue': 77,
+ 'MinValue': 41,
+ 'Default': 42},
+ 'list': {'Type': 'List', 'Schema': {'Type': 'Map',
+ 'Schema': list_schema}},
+ 'map': {'Type': 'Map', 'Schema': {'Type': 'Map',
+ 'Schema': map_schema}},
+ }
+
+ attributes_schema = {
+ 'output1': 'output1_desc',
+ 'output2': 'output2_desc'
+ }
+
+ expected_template = {
+ 'Parameters': {
+ 'name': {'Type': 'String'},
+ 'bool': {'Type': 'Boolean'},
+ 'implemented': {
+ 'Type': 'String',
+ 'AllowedPattern': '.*',
+ 'MaxLength': 7,
+ 'MinLength': 2
+ },
+ 'number': {'Type': 'Number',
+ 'MaxValue': 77,
+ 'MinValue': 41,
+ 'Default': 42},
+ 'list': {'Type': 'CommaDelimitedList'},
+ 'map': {'Type': 'Json'}
+ },
+ 'Resources': {
+ 'TestResource': {
+ 'Type': 'Test::Resource::resource',
+ 'Properties': {
+ 'name': {'Ref': 'name'},
+ 'bool': {'Ref': 'bool'},
+ 'implemented': {'Ref': 'implemented'},
+ 'number': {'Ref': 'number'},
+ 'list': {'Fn::Split': {'Ref': 'list'}},
+ 'map': {'Ref': 'map'}
+ }
+ }
+ },
+ 'Outputs': {
+ 'output1': {
+ 'Description': 'output1_desc',
+ 'Value': '{"Fn::GetAtt": ["TestResource", "output1"]}'
+ },
+ 'output2': {
+ 'Description': 'output2_desc',
+ 'Value': '{"Fn::GetAtt": ["TestResource", "output2"]}'
+ }
+ }
+ }
+ self.assertEqual(expected_template,
+ TestResource.resource_to_template(
+ 'Test::Resource::resource')
+ )
+
class MetadataTest(HeatTestCase):
def setUp(self):