From f8b2f5b73646c10a54c473b5d6e56e5e5d48f684 Mon Sep 17 00:00:00 2001 From: Haiyang DING Date: Wed, 24 Sep 2014 23:26:11 +0800 Subject: [PATCH] Represent memory unit in string Enable unit representation for property 'mem_size' in Compute node. 1. valid units are: MB or GB, MB being default choice 2. allow space separator(s) between number and unit 3. no space between number and unit is also allowed 4. use default value if property mem_size is not given 5. default value is 1024 MB implements blueprint: compute-type-memory-unit-wd2 Change-Id: I124524200b6bde7f32ceafd0d45d412026f4ee1f --- translator/toscalib/common/exception.py | 4 + .../toscalib/elements/TOSCA_definition.yaml | 1 + .../toscalib/elements/property_definition.py | 8 + translator/toscalib/entity_template.py | 21 ++- translator/toscalib/properties.py | 24 ++- translator/toscalib/tests/base.py | 3 +- translator/toscalib/tests/test_memunit.py | 167 ++++++++++++++++++ translator/toscalib/tests/test_toscadef.py | 7 + 8 files changed, 223 insertions(+), 12 deletions(-) create mode 100644 translator/toscalib/tests/test_memunit.py diff --git a/translator/toscalib/common/exception.py b/translator/toscalib/common/exception.py index 5bf4bda9..3ccd1acc 100644 --- a/translator/toscalib/common/exception.py +++ b/translator/toscalib/common/exception.py @@ -81,3 +81,7 @@ class ValidationError(TOSCAException): class UnknownInputError(TOSCAException): msg_fmt = _('Unknown input: %(input_name)s') + + +class InvalidPropertyValueError(TOSCAException): + msg_fmt = _('Value of property "%(what)s" is invalid.') diff --git a/translator/toscalib/elements/TOSCA_definition.yaml b/translator/toscalib/elements/TOSCA_definition.yaml index 0051b54c..3cd5660a 100644 --- a/translator/toscalib/elements/TOSCA_definition.yaml +++ b/translator/toscalib/elements/TOSCA_definition.yaml @@ -54,6 +54,7 @@ tosca.nodes.Compute: description: > Size of memory, in Megabytes (MB), available to applications running on the Compute node. + default: 1024 os_arch: required: no default: x86_64 diff --git a/translator/toscalib/elements/property_definition.py b/translator/toscalib/elements/property_definition.py index 99098fc9..ad5d04e1 100644 --- a/translator/toscalib/elements/property_definition.py +++ b/translator/toscalib/elements/property_definition.py @@ -26,3 +26,11 @@ class PropertyDef(object): if prop_key == 'required' and prop_vale: return True return False + + @property + def default(self): + if self.schema: + for prop_key, prop_value in self.schema.items(): + if prop_key == 'default': + return prop_value + return None diff --git a/translator/toscalib/entity_template.py b/translator/toscalib/entity_template.py index 16ea85e4..92e41f50 100644 --- a/translator/toscalib/entity_template.py +++ b/translator/toscalib/entity_template.py @@ -78,7 +78,7 @@ class EntityTemplate(object): self.entity_tpl) if caps: for name, value in caps.items(): - for prop, val in value.items(): + for val in value.values(): properties = val for c in self.type_definition.capabilities: if c.name == name: @@ -99,7 +99,7 @@ class EntityTemplate(object): if properties: self._common_validate_field(properties, allowed_props, 'Properties') - #make sure it's not missing any property required by a tosca type + # make sure it's not missing any property required by a tosca type missingprop = [] for r in required_props: if r not in properties.keys(): @@ -147,13 +147,16 @@ class EntityTemplate(object): def _create_properties(self): props = [] properties = self.type_definition.get_value(self.PROPERTIES, - self.entity_tpl) - if properties: - for name, value in properties.items(): - for p in self.type_definition.properties_def: - if p.name == name: - prop = Property(name, value, p.schema) - props.append(prop) + self.entity_tpl) or {} + for name, value in properties.items(): + for p in self.type_definition.properties_def: + if p.name == name: + prop = Property(name, value, p.schema) + props.append(prop) + for p in self.type_definition.properties_def: + if p.default is not None and p.name not in properties.keys(): + prop = Property(p.name, p.default, p.schema) + props.append(prop) return props def _create_interfaces(self): diff --git a/translator/toscalib/properties.py b/translator/toscalib/properties.py index 1d865bbd..e5769afa 100644 --- a/translator/toscalib/properties.py +++ b/translator/toscalib/properties.py @@ -10,6 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. +import re + +from translator.toscalib.common.exception import InvalidPropertyValueError from translator.toscalib.elements.constraints import Constraint from translator.toscalib.elements.constraints import Schema from translator.toscalib.functions import Function @@ -21,12 +24,12 @@ class Property(object): PROPERTY_KEYS = ( TYPE, REQUIRED, DESCRIPTION, DEFAULT, CONSTRAINTS, ) = ( - 'type', 'required', 'description', 'default', 'constraints' + 'type', 'required', 'description', 'default', 'constraints', ) def __init__(self, property_name, value, schema_dict): self.name = property_name - self.value = value + self.value = self._convert_value(value) self.schema = Schema(property_name, schema_dict) @property @@ -73,3 +76,20 @@ class Property(object): if self.constraints: for constraint in self.constraints: constraint.validate(self.value) + + def _convert_value(self, value): + if self.name == 'mem_size': + mem_reader = re.compile('(\d*)\s*(\w*)') + matcher = str(value) + result = mem_reader.match(matcher).groups() + r = [] + if (result[0] != '') and (result[1] == ''): + r = int(result[0]) + elif (result[0] != '') and (result[1] == 'MB'): + r = int(result[0]) + elif (result[0] != '') and (result[1] == 'GB'): + r = int(result[0]) * 1024 + else: + raise InvalidPropertyValueError(what=self.name) + return r + return value diff --git a/translator/toscalib/tests/base.py b/translator/toscalib/tests/base.py index 23c0dd36..3844db08 100644 --- a/translator/toscalib/tests/base.py +++ b/translator/toscalib/tests/base.py @@ -18,6 +18,7 @@ import os import fixtures +import testscenarios import testtools from translator.toscalib.tosca_template import ToscaTemplate @@ -25,7 +26,7 @@ from translator.toscalib.tosca_template import ToscaTemplate _TRUE_VALUES = ('True', 'true', '1', 'yes') -class TestCase(testtools.TestCase): +class TestCase(testscenarios.TestWithScenarios, testtools.TestCase): """Test case base class for all unit tests.""" diff --git a/translator/toscalib/tests/test_memunit.py b/translator/toscalib/tests/test_memunit.py new file mode 100644 index 00000000..58b4d310 --- /dev/null +++ b/translator/toscalib/tests/test_memunit.py @@ -0,0 +1,167 @@ +# 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. + +from translator.toscalib.common.exception import InvalidPropertyValueError +from translator.toscalib.nodetemplate import NodeTemplate +from translator.toscalib.tests.base import TestCase +import translator.toscalib.utils.yamlparser + + +class ToscaTemplateMemorySizeOutputTest(TestCase): + + scenarios = [ + ( + # tpl_snippet with mem_size given as number + 'mem_size_is_number', + dict(tpl_snippet=''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + # compute properties (flavor) + mem_size: 1024 + # host image properties + os_type: Linux + ''', + expected=1024) + ), + ( + # tpl_snippet with mem_size given as number+space+MB + 'mem_size_is_number_Space_MB', + dict(tpl_snippet=''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + # compute properties (flavor) + mem_size: 1024 MB + # host image properties + os_type: Linux + ''', + expected=1024) + ), + ( + # tpl_snippet with mem_size given as number+space+GB + 'mem_size_is_number_Space_GB', + dict(tpl_snippet=''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + # compute properties (flavor) + mem_size: 1 GB + # host image properties + os_type: Linux + ''', + expected=1024) + ), + ( + # tpl_snippet with mem_size given as number+GB + 'mem_size_is_number_NoSpace_GB', + dict(tpl_snippet=''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + # compute properties (flavor) + mem_size: 1GB + # host image properties + os_type: Linux + ''', + expected=1024) + ), + ( + # tpl_snippet with mem_size given as number+Spaces+GB + 'mem_size_is_number_Spaces_GB', + dict(tpl_snippet=''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + # compute properties (flavor) + mem_size: 1 GB + # host image properties + os_type: Linux + ''', + expected=1024) + ), + ( + # tpl_snippet with no mem_size given + 'mem_size_is_absent', + dict(tpl_snippet=''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + # compute properties (flavor) + # host image properties + os_type: Linux + ''', + expected=1024) + ), + ] + + def test_scenario_mem_size(self): + tpl = self.tpl_snippet + nodetemplates = (translator.toscalib.utils.yamlparser. + simple_parse(tpl))['node_templates'] + name = list(nodetemplates.keys())[0] + nodetemplate = NodeTemplate(name, nodetemplates) + for p in nodetemplate.properties: + if p.name == 'mem_size': + resolved = p.value + self.assertEqual(resolved, self.expected) + + +class ToscaTemplateMemorySizeErrorTest(TestCase): + + scenarios = [ + ( + # tpl_snippet with mem_size given as empty (error) + 'mem_size_is_empty', + dict(tpl_snippet=''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + # compute properties (flavor) + mem_size: + # host image properties + os_type: Linux + ''', + err=InvalidPropertyValueError) + ), + ( + # tpl_snippet with mem_size given as number+InvalidUnit (error) + 'mem_size_unit_is_invalid', + dict(tpl_snippet=''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + # compute properties (flavor) + mem_size: 1 QB + # host image properties + os_type: Linux + ''', + err=InvalidPropertyValueError) + ), + ] + + def test_scenario_mem_size_error(self): + tpl = self.tpl_snippet + nodetemplates = (translator.toscalib.utils.yamlparser. + simple_parse(tpl))['node_templates'] + name = list(nodetemplates.keys())[0] + nodetemplate = NodeTemplate(name, nodetemplates) + self.assertRaises(self.err, + nodetemplate._create_properties) diff --git a/translator/toscalib/tests/test_toscadef.py b/translator/toscalib/tests/test_toscadef.py index d1681021..b9749d3d 100644 --- a/translator/toscalib/tests/test_toscadef.py +++ b/translator/toscalib/tests/test_toscadef.py @@ -64,3 +64,10 @@ class ToscaDefTest(TestCase): self.assertEqual(compute_type.interfaces, None) root_node = NodeType('tosca.nodes.Root') self.assertIn('tosca.interfaces.node.Lifecycle', root_node.interfaces) + + def test_default_mem_size(self): + test_value = 0 + for p_def in compute_type.properties_def: + if p_def.name == 'mem_size': + test_value = p_def.default + self.assertEqual(test_value, 1024)