diff --git a/watcher/applier/actions/base.py b/watcher/applier/actions/base.py index ec1cb5d82..ef1a5c094 100644 --- a/watcher/applier/actions/base.py +++ b/watcher/applier/actions/base.py @@ -126,7 +126,7 @@ class BaseAction(loadable.Loadable): :returns: A schema declaring the input parameters this action should be provided along with their respective constraints - :rtype: :py:class:`voluptuous.Schema` instance + :rtype: :py:class:`jsonschema.Schema` instance """ raise NotImplementedError() diff --git a/watcher/decision_engine/goal/efficacy/base.py b/watcher/decision_engine/goal/efficacy/base.py index f4c6b1587..364c377a0 100644 --- a/watcher/decision_engine/goal/efficacy/base.py +++ b/watcher/decision_engine/goal/efficacy/base.py @@ -24,10 +24,10 @@ calculating its :ref:`global efficacy `. """ import abc +import jsonschema from oslo_serialization import jsonutils import six -import voluptuous @six.add_metaclass(abc.ABCMeta) @@ -65,17 +65,21 @@ class EfficacySpecification(object): @property def schema(self): """Combined schema from the schema of the indicators""" - schema = voluptuous.Schema({}, required=True) + schema = { + "type": "object", + "properties": {}, + "required": [] + } for indicator in self.indicators_specs: - key_constraint = (voluptuous.Required - if indicator.required else voluptuous.Optional) - schema = schema.extend( - {key_constraint(indicator.name): indicator.schema.schema}) - + schema["properties"][indicator.name] = indicator.schema + schema["required"].append(indicator.name) return schema def validate_efficacy_indicators(self, indicators_map): - return self.schema(indicators_map) + if indicators_map: + jsonschema.validate(indicators_map, self.schema) + else: + True def get_indicators_specs_dicts(self): return [indicator.to_dict() diff --git a/watcher/decision_engine/goal/efficacy/indicators.py b/watcher/decision_engine/goal/efficacy/indicators.py index 7363f21de..73c152260 100644 --- a/watcher/decision_engine/goal/efficacy/indicators.py +++ b/watcher/decision_engine/goal/efficacy/indicators.py @@ -15,10 +15,13 @@ # limitations under the License. import abc +import jsonschema +from jsonschema import SchemaError +from jsonschema import ValidationError import six from oslo_log import log -import voluptuous +from oslo_serialization import jsonutils from watcher._i18n import _ from watcher.common import exception @@ -37,10 +40,9 @@ class IndicatorSpecification(object): @abc.abstractproperty def schema(self): - """Schema used to validate the indicator value + """JsonSchema used to validate the indicator value - :return: A Voplutuous Schema - :rtype: :py:class:`.voluptuous.Schema` instance + :return: A Schema """ raise NotImplementedError() @@ -54,7 +56,10 @@ class IndicatorSpecification(object): value = None try: value = getattr(solution, indicator.name) - indicator.schema(value) + jsonschema.validate(value, cls.schema) + except (SchemaError, ValidationError) as exc: + LOG.exception(exc) + raise exc except Exception as exc: LOG.exception(exc) raise exception.InvalidIndicatorValue( @@ -65,7 +70,7 @@ class IndicatorSpecification(object): "name": self.name, "description": self.description, "unit": self.unit, - "schema": str(self.schema.schema) if self.schema else None, + "schema": jsonutils.dumps(self.schema) if self.schema else None, } def __str__(self): @@ -82,8 +87,10 @@ class ComputeNodesCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class ReleasedComputeNodesCount(IndicatorSpecification): @@ -96,8 +103,10 @@ class ReleasedComputeNodesCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class InstanceMigrationsCount(IndicatorSpecification): @@ -110,8 +119,10 @@ class InstanceMigrationsCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class LiveInstanceMigrateCount(IndicatorSpecification): @@ -124,8 +135,10 @@ class LiveInstanceMigrateCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class PlannedLiveInstanceMigrateCount(IndicatorSpecification): @@ -138,8 +151,10 @@ class PlannedLiveInstanceMigrateCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class ColdInstanceMigrateCount(IndicatorSpecification): @@ -152,8 +167,10 @@ class ColdInstanceMigrateCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class PlannedColdInstanceMigrateCount(IndicatorSpecification): @@ -166,8 +183,10 @@ class PlannedColdInstanceMigrateCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class VolumeMigrateCount(IndicatorSpecification): @@ -180,8 +199,10 @@ class VolumeMigrateCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class PlannedVolumeMigrateCount(IndicatorSpecification): @@ -195,8 +216,10 @@ class PlannedVolumeMigrateCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class VolumeUpdateCount(IndicatorSpecification): @@ -210,8 +233,10 @@ class VolumeUpdateCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } class PlannedVolumeUpdateCount(IndicatorSpecification): @@ -225,5 +250,7 @@ class PlannedVolumeUpdateCount(IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0), required=True) + return { + "type": "integer", + "minimum": 0 + } diff --git a/watcher/tests/decision_engine/fake_goals.py b/watcher/tests/decision_engine/fake_goals.py index 435253daf..19d51095d 100644 --- a/watcher/tests/decision_engine/fake_goals.py +++ b/watcher/tests/decision_engine/fake_goals.py @@ -14,8 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import voluptuous - from watcher.decision_engine.goal import base as base_goal from watcher.decision_engine.goal.efficacy import base as efficacy_base from watcher.decision_engine.goal.efficacy import indicators @@ -55,8 +53,10 @@ class DummyIndicator(indicators.IndicatorSpecification): @property def schema(self): - return voluptuous.Schema( - voluptuous.Range(min=0, max=100), required=True) + return { + "type": "integer", + "minimum": 0 + } class DummySpec1(efficacy_base.EfficacySpecification): diff --git a/watcher/tests/decision_engine/test_sync.py b/watcher/tests/decision_engine/test_sync.py index 7894f63d8..d2fde7563 100644 --- a/watcher/tests/decision_engine/test_sync.py +++ b/watcher/tests/decision_engine/test_sync.py @@ -16,6 +16,8 @@ import mock +from oslo_serialization import jsonutils + from watcher.common import context from watcher.common import utils from watcher.decision_engine.loading import default @@ -451,8 +453,7 @@ class TestSyncer(base.DbTestCase): dummy_1_spec = [ {'description': 'Dummy indicator', 'name': 'dummy', - 'schema': 'Range(min=0, max=100, min_included=True, ' - 'max_included=True, msg=None)', + 'schema': jsonutils.dumps({'minimum': 0, 'type': 'integer'}), 'unit': '%'}] dummy_2_spec = [] self.assertEqual(