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