Initial PropertiesGroup implementation

Added PropertiesGroup class and validation
for it's schema.

implements bp heat-property-group

Change-Id: I0147cbb852de384a8f92fa869cb1480fd8346fcd
This commit is contained in:
Peter Razumovsky 2016-10-28 16:49:30 +03:00
parent 0725399a5e
commit 498ee45ed9
2 changed files with 176 additions and 0 deletions

View File

@ -0,0 +1,84 @@
#
# 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 six
from heat.common import exception
from heat.common.i18n import _
OPERATORS = (
AND, OR, XOR
) = (
'AND', 'OR', 'XOR'
)
class PropertiesGroup(object):
"""A class for specifying properties relationships.
Properties group allows to specify relations between properties or other
properties groups with operators AND, OR and XOR by one-key dict with list
value. For example, if there are two properties: "subprop1", which is
child of property "prop1", and property "prop2", and they should not be
specified together, then properties group for them should be next::
{XOR: [["prop1", "subprop1"], ["prop2"]]}
where each property name should be set as list of strings. Also, if these
properties are exclusive with properties "prop3" and "prop4", which should
be specified both, then properties group will be defined such way::
{XOR: [ ["prop1", "subprop1"], ["prop2"],
{AND: [ ["prop3"], ["prop4"] ]} ]}
where one-key dict with key "AND" is nested properties group.
"""
def __init__(self, schema, properties=None):
self._properties = properties
self.validate_schema(schema)
self.schema = schema
def validate_schema(self, current_schema):
msg = _('Properties group schema incorrectly specified.')
if not isinstance(current_schema, dict):
msg = _('%(msg)s Schema should be a mapping, found '
'%(t)s instead.') % dict(msg=msg, t=type(current_schema))
raise exception.InvalidSchemaError(message=msg)
if len(current_schema.keys()) > 1:
msg = _("%(msg)s Schema should be one-key dict.") % dict(msg=msg)
raise exception.InvalidSchemaError(message=msg)
current_key = next(iter(current_schema))
if current_key not in OPERATORS:
msg = _('%(msg)s Properties group schema key should be one of the '
'operators: %(op)s.') % dict(msg=msg,
op=', '.join(OPERATORS))
raise exception.InvalidSchemaError(message=msg)
if not isinstance(current_schema[current_key], list):
msg = _("%(msg)s Schemas' values should be lists of properties "
"names or nested schemas.") % dict(msg=msg)
raise exception.InvalidSchemaError(message=msg)
next_msg = _('%(msg)s List items should be properties list-type names '
'with format "[prop, prop_child, prop_sub_child, ...]" '
'or nested properties group schemas.') % dict(msg=msg)
for item in current_schema[current_key]:
if isinstance(item, dict):
self.validate_schema(item)
elif isinstance(item, list):
for name in item:
if not isinstance(name, six.string_types):
raise exception.InvalidSchemaError(message=next_msg)
else:
raise exception.InvalidSchemaError(message=next_msg)

View File

@ -0,0 +1,92 @@
#
# 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 six
from heat.common import exception
from heat.engine import properties_group as pg
from heat.tests import common
class TestSchemaSimpleValidation(common.HeatTestCase):
scenarios = [
('correct schema', dict(
schema={pg.AND: [['a'], ['b']]},
message=None,
)),
('invalid type schema', dict(
schema=[{pg.OR: [['a'], ['b']]}],
message="Properties group schema incorrectly specified. "
"Schema should be a mapping, "
"found %s instead." % list,
)),
('invalid type subschema', dict(
schema={pg.OR: [['a'], ['b'], [{pg.XOR: [['c'], ['d']]}]]},
message='Properties group schema incorrectly specified. List '
'items should be properties list-type names with format '
'"[prop, prop_child, prop_sub_child, ...]" or nested '
'properties group schemas.',
)),
('several keys schema', dict(
schema={pg.OR: [['a'], ['b']],
pg.XOR: [['v', 'g']]},
message='Properties group schema incorrectly specified. Schema '
'should be one-key dict.',
)),
('several keys subschema', dict(
schema={pg.OR: [['a'], ['b'], {pg.XOR: [['c']], pg.OR: ['d']}]},
message='Properties group schema incorrectly specified. '
'Schema should be one-key dict.',
)),
('invalid key schema', dict(
schema={'NOT KEY': [['a'], ['b']]},
message='Properties group schema incorrectly specified. '
'Properties group schema key should be one of the '
'operators: AND, OR, XOR.',
)),
('invalid key subschema', dict(
schema={pg.AND: [['a'], {'NOT KEY': [['b']]}]},
message='Properties group schema incorrectly specified. '
'Properties group schema key should be one of the '
'operators: AND, OR, XOR.',
)),
('invalid value type schema', dict(
schema={pg.OR: 'a'},
message="Properties group schema incorrectly specified. "
"Schemas' values should be lists of properties names "
"or nested schemas.",
)),
('invalid value type subschema', dict(
schema={pg.OR: [{pg.XOR: 'a'}]},
message="Properties group schema incorrectly specified. "
"Schemas' values should be lists of properties names "
"or nested schemas.",
)),
('invalid prop name schema', dict(
schema={pg.OR: ['a', 'b']},
message='Properties group schema incorrectly specified. List '
'items should be properties list-type names with format '
'"[prop, prop_child, prop_sub_child, ...]" or nested '
'properties group schemas.',
)),
]
def test_properties_group_schema_validate(self):
if self.message is not None:
ex = self.assertRaises(exception.InvalidSchemaError,
pg.PropertiesGroup, self.schema)
self.assertEqual(self.message, six.text_type(ex))
else:
self.assertIsInstance(pg.PropertiesGroup(self.schema),
pg.PropertiesGroup)