def _validate_type(self):
     schema = self._schema
     json_type = schema.type
     if json_type == "any":
         return
     obj = self._object
     if json_type == "boolean":
         # Bool is special cased because in python there is no
         # way to test for isinstance(something, bool) that would
         # not catch isinstance(1, bool) :/
         if obj is not True and obj is not False:
             self._report_error(
                 "{obj!r} does not match type {type!r}".format(
                     obj=obj, type=json_type),
                 "Object has incorrect type (expected boolean)",
                 schema_suffix=".type")
     elif isinstance(json_type, dict):
         # Nested type check. This is pretty odd case. Here we
         # don't change our object stack (it's the same object).
         self._push_schema(Schema(json_type), ".type")
         self._validate()
         self._pop_schema()
     elif isinstance(json_type, list):
         # Alternative type check, here we may match _any_ of the types
         # in the list to be considered valid.
         json_type_list = json_type
         if json_type == []:
             return
         for index, json_type in enumerate(json_type_list):
             # Aww, ugly. The level of packaging around Schema is annoying
             self._push_schema(Schema({'type': json_type}),
                               ".type.%d" % index)
             try:
                 self._validate()
             except ValidationError:
                 # Ignore errors, we just want one thing to match
                 pass
             else:
                 # We've got a match - break the loop
                 break
             finally:
                 # Pop the schema regardless of match/mismatch
                 self._pop_schema()
         else:
             # We were not interupted (no break) so we did not match
             self._report_error(
                 "{obj!r} does not match any of the types in {type!r}".
                 format(obj=obj, type=json_type_list),
                 "Object has incorrect type (multiple types possible)",
                 schema_suffix=".type")
     else:
         # Simple type check
         if not isinstance(obj, self.JSON_TYPE_MAP[json_type]):
             self._report_error(
                 "{obj!r} does not match type {type!r}".format(
                     obj=obj, type=json_type),
                 "Object has incorrect type (expected {type})".format(
                     type=json_type),
                 schema_suffix=".type")
Example #2
0
def validate(schema_text, data_text, deserializer=_default_deserializer):
    """
    Validate specified JSON text with specified schema.

    Both arguments are converted to JSON objects with :func:`simplejson.loads`,
    if present, or :func:`json.loads`.

    :param schema_text:
        Text of the JSON schema to check against
    :type schema_text:
        :class:`str`
    :param data_text:
        Text of the JSON object to check
    :type data_text:
        :class:`str`
    :param deserializer:
        Function to convert the schema and data to JSON objects
    :type deserializer:
        :class:`callable`
    :returns:
        Same as :meth:`json_schema_validator.validator.Validator.validate`
    :raises:
        Whatever may be raised by simplejson (in particular
        :class:`simplejson.decoder.JSONDecoderError`, a subclass of
        :class:`ValueError`) or json
    :raises:
        Whatever may be raised by
        :meth:`json_schema_validator.validator.Validator.validate`. In particular
        :class:`json_schema_validator.errors.ValidationError` and
        :class:`json_schema_validator.errors.SchemaError`
    """
    schema = Schema(deserializer(schema_text))
    data = deserializer(data_text)
    return Validator.validate(schema, data)
Example #3
0
def validate(schema_text, data_text):
    """
    Validate specified JSON text (data_text) with specified schema (schema
    text). Both are converted to JSON objects with :func:`simplesjon.loads`.

    :param schema_text:
        Text of the JSON schema to check against
    :type schema_text:
        :class:`str`
    :param data_text:
        Text of the JSON object to check
    :type data_text:
        :class:`str`
    :returns:
        Same as :meth:`json_schema_validator.validator.Validator.validate`
    :raises:
        Whatever may be raised by simplejson (in particular
        :class:`simplejson.decoder.JSONDecoderError`, a subclass of :class:`ValueError`)
    :raises:
        Whatever may be raised by
        :meth:`json_schema_validator.validator.Validator.validate`. In particular
        :class:`json_schema_validator.errors.ValidationError` and
        :class:`json_schema_validator.errors.SchemaError`


    """
    schema = Schema(simplejson.loads(schema_text))
    data = simplejson.loads(data_text)
    return Validator.validate(schema, data)
 def _push_property_schema(self, prop):
     """
     Construct a sub-schema from the value of the specified attribute
     of the current schema.
     """
     schema = Schema(self._schema.properties[prop])
     self._push_schema(schema, ".properties." + prop)
def validate_job_data(job_data):
    schema = Schema(job_schema)
    Validator.validate(schema, job_data)
    lava_commands = get_all_cmds()
    for action in job_data['actions']:
        command_name = action['command']
        command = lava_commands.get(command_name)
        if command is None:
            raise ValueError("action %r not known" % command_name)
        command.validate_parameters(action.get('parameters'))
Example #6
0
    def schema(self):
        """
        Schema associated with this fragment

        Schema may be None

        This is a read-only property. Schema is automatically provided when a
        sub-fragment is accessed on a parent fragment (all the way up to the
        document). To provide schema for your fragments make sure to include
        them in the ``properties`` or ``items``. Alternatively you can provide
        ``additionalProperties`` that will act as a catch-all clause allowing
        you to define a schema for anything that was not explicitly matched by
        ``properties``.
        """
        if self._schema is not None:
            return Schema(self._schema)
 def _validate_requires(self):
     obj = self._object
     schema = self._schema
     requires_json = schema.requires
     if requires_json == {}:
         # default value, don't do anything
         return
     # Find our enclosing object in the object stack
     if len(self._object_stack) < 2:
         self._report_error(
             "{obj!r} requires that enclosing object matches"
             " schema {schema!r} but there is no enclosing"
             " object".format(obj=obj, schema=requires_json),
             "Object has no enclosing object that matches schema",
             schema_suffix=".requires")
     # Note: Parent object can be None, (e.g. a null property)
     parent_obj = self._object_stack[-2][0]
     if isinstance(requires_json, basestring):
         # This is a simple property test
         if (not isinstance(parent_obj, dict)
                 or requires_json not in parent_obj):
             self._report_error(
                 "{obj!r} requires presence of property {requires!r}"
                 " in the same object".format(obj=obj,
                                              requires=requires_json),
                 "Enclosing object does not have property"
                 " {prop!r}".format(prop=requires_json),
                 schema_suffix=".requires")
     elif isinstance(requires_json, dict):
         # Requires designates a whole schema, the enclosing object
         # must match against that schema.
         # Here we resort to a small hack. Proper implementation
         # would require us to validate the parent object from its
         # own context (own list of parent objects). Since doing that
         # and restoring the state would be very complicated we just
         # instantiate a new validator with a subset of our current
         # history here.
         sub_validator = Validator()
         sub_validator._object_stack = self._object_stack[:-1]
         sub_validator._schema_stack = self._schema_stack[:]
         sub_validator._push_schema(Schema(requires_json), ".requires")
         sub_validator._validate()
 def test_schema_attribute(self):
     schema = Schema(simplejson.loads(self.schema))
     if hasattr(self, 'expected'):
         for attr, expected_value in self.expected.iteritems():
             self.assertEqual(
                 expected_value, getattr(schema, attr))
     elif hasattr(self, 'access') and hasattr(self, 'raises'):
         self.assertRaises(
             type(self.raises),
             getattr, schema, self.access)
         try:
             getattr(schema, self.access)
         except type(self.raises) as ex:
             self.assertEqual(str(ex), str(self.raises))
         except Exception as ex:
             self.fail("Raised exception {0!r} instead of {1!r}".format(
                 ex, self.raises))
     else:
         self.fail("Broken test definition, must define 'expected' "
                   "or 'access' and 'raises' scenario attributes")
    def test_schema_attribute(self):
        if deserializer != json.loads:
            # Always check the serialised JSON using the native JSON loader
            # so that any error messages are consistent and appropriate.
            json.loads(self.schema)

        schema = Schema(deserializer(self.schema))
        if hasattr(self, 'expected'):
            for attr, expected_value in self.expected.items():
                self.assertEqual(expected_value, getattr(schema, attr))
        elif hasattr(self, 'access') and hasattr(self, 'raises'):
            self.assertRaises(type(self.raises), getattr, schema, self.access)
            try:
                getattr(schema, self.access)
            except type(self.raises) as ex:
                self.assertEqual(str(ex), str(self.raises))
            except Exception as ex:
                self.fail("Raised exception {0!r} instead of {1!r}".format(
                    ex, self.raises))
        else:
            self.fail("Broken test definition, must define 'expected' "
                      "or 'access' and 'raises' scenario attributes")
class DocumentIO(object):
    """
    Document IO encapsulates various (current and past) file
    formats and provides a single entry point for analyzing a document,
    determining its format and validating the contents.
    """

    SCHEMAS = {
        'Dashboard Bundle Format 1.0': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.0.json'))),
        'Dashboard Bundle Format 1.0.1': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.0.1.json'))),
        'Dashboard Bundle Format 1.1': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.1.json'))),
        'Dashboard Bundle Format 1.2': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.2.json'))),
        'Dashboard Bundle Format 1.3': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.3.json'))),
        'Dashboard Bundle Format 1.4': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.4.json'))),
        'Dashboard Bundle Format 1.5': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.5.json'))),
        'Dashboard Bundle Format 1.6': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.6.json'))),
        'Dashboard Bundle Format 1.7': Schema(
            json.loads(
                resource_string(
                    __name__,
                    'schemas/dashboard_bundle_format_1.7.json'))),
    }

    @classmethod
    def _get_dict_impl(cls, retain_order):
        if retain_order:
            object_pairs_hook = json.OrderedDict
        else:
            object_pairs_hook = None
        return object_pairs_hook

    @classmethod
    def _get_indent_and_separators(cls, human_readable):
        if human_readable:
            indent = ' ' * 2
            separators = (', ', ': ')
        else:
            indent = None
            separators = (',', ':')
        return indent, separators

    @classmethod
    def load(cls, stream, retain_order=True):
        """
        Load and check a JSON document from the specified stream

        :Discussion:
            The document is read from the stream and parsed as JSON text. It is
            then validated against a set of known formats and their schemas.

        :Return value:
            Tuple (format, document) where format is the string identifying
            document format and document is a JSON document loaded from the
            passed text. If retain_order is True then the resulting objects are
            composed of ordered dictionaries. This mode is slightly slower and
            consumes more memory.

        :Exceptions:
            ValueError
                When the text does not represent a correct JSON document.
            Other exceptions
                This method can also raise exceptions raised by
                DocumentIO.check()
        """
        object_pairs_hook = cls._get_dict_impl(retain_order) 
        doc = json.load(stream, parse_float=decimal.Decimal, object_pairs_hook=object_pairs_hook)
        fmt = cls.check(doc)
        return fmt, doc

    @classmethod
    def loads(cls, text, retain_order=True):
        """
        Same as load() but reads data from a string
        """
        object_pairs_hook = cls._get_dict_impl(retain_order) 
        doc = json.loads(text, parse_float=decimal.Decimal, object_pairs_hook=object_pairs_hook)
        fmt = cls.check(doc)
        return fmt, doc

    @classmethod
    def dump(cls, stream, doc, human_readable=True, sort_keys=False):
        """
        Check and save a JSON document to the specified stream

        :Discussion:
            The document is validated against a set of known formats and
            schemas and saved to the specified stream.
            
            If human_readable is True the serialized stream is meant to be read
            by humans, it will have newlines, proper indentation and spaces
            after commas and colons. This option is enabled by default.

            If sort_keys is True then resulting JSON object will have sorted
            keys in all objects. This is useful for predictable format but is
            not recommended if you want to load-modify-save an existing
            document without altering it's general structure. This option is
            not enabled by default.

        :Return value:
            None

        :Exceptions:
            Other exceptions
                This method can also raise exceptions raised by
                DocumentIO.check()
        """
        cls.check(doc)
        indent, separators = cls._get_indent_and_separators(human_readable)
        json.dump(doc, stream,
                  use_decimal=True,
                  indent=indent,
                  separators=separators,
                  sort_keys=sort_keys)

    @classmethod
    def dumps(cls, doc, human_readable=True, sort_keys=False):
        """
        Check and save a JSON document as string

        :Discussion:
            The document is validated against a set of known formats and
            schemas and saved to a string.

            If human_readable is True the serialized value is meant to be read
            by humans, it will have newlines, proper indentation and spaces
            after commas and colons. This option is enabled by default.
            
            If sort_keys is True then resulting JSON object will have sorted
            keys in all objects. This is useful for predictable format but is
            not recommended if you want to load-modify-save an existing
            document without altering it's general structure. This option is
            not enabled by default.

        :Return value:
            JSON document as string

        :Exceptions:
            Other exceptions
                This method can also raise exceptions raised by
                DocumentIO.check()
        """
        cls.check(doc)
        indent, separators = cls._get_indent_and_separators(human_readable)
        return json.dumps(doc,
                          use_decimal=True, 
                          indent=indent,
                          separators=separators,
                          sort_keys=sort_keys)

    @classmethod
    def check(cls, doc):
        """
        Check document format and validate the contents against a schema.

        :Discussion:
            The document is validated against a set of known versions
            and their schemas.

        :Return value:
            String identifying document format

        :Exceptions:
            json_schema_validator.errors.ValidationError
                When the document does not match the appropriate schema.
            linaro_dashboard_bundle.errors.DocumentFormatError
                When the document format is not in the known set of formats.
        """
        fmt = doc.get('format')
        schema = cls.SCHEMAS.get(fmt)
        if schema is None:
            raise DocumentFormatError(fmt)
        Validator.validate(schema, doc)
        return fmt
 def _validate_items(self):
     obj = self._object
     schema = self._schema
     assert isinstance(obj, list)
     items_schema_json = schema.items
     if items_schema_json == {}:
         # default value, don't do anything
         return
     if isinstance(obj, list) and schema.uniqueItems is True and len(
             set(obj)) != len(obj):
         # If we want a list of unique items and the length of unique
         # elements is different from the length of the full list
         # then validation fails.
         # This implementation isn't strictly compatible with the specs, because
         # we are not checking unique dicts.
         self._report_error(
             "Repeated items found in {obj!r}".format(obj=obj),
             "Repeated items found in array",
             schema_suffix=".items")
     if schema.minItems:
         if len(obj) < schema.minItems:
             self._report_error(
                 "{obj!r} has fewer than the minimum number of items"
                 " {minItems!r}".format(obj=obj, minimum=schema.minItems),
                 "Object has fewer than the minimum number of items",
                 schema_suffix=".minItems")
     if schema.maxItems is not None:
         if len(obj) > schema.maxItems:
             self._report_error(
                 "{obj!r} has more than the maximum number of items"
                 " {maxItems!r}".format(obj=obj, minimum=schema.maxItems),
                 "Object has more than the maximum number of items",
                 schema_suffix=".maxItems")
     if isinstance(items_schema_json, dict):
         self._push_array_schema()
         for index, item in enumerate(obj):
             self._push_array_item_object(index)
             self._validate()
             self._pop_object()
         self._pop_schema()
     elif isinstance(items_schema_json, list):
         if len(obj) < len(items_schema_json):
             # If our data array is shorter than the schema then
             # validation fails. Longer arrays are okay (during this
             # step) as they are validated based on
             # additionalProperties schema
             self._report_error(
                 "{obj!r} is shorter than array schema {schema!r}".format(
                     obj=obj, schema=items_schema_json),
                 "Object array is shorter than schema array",
                 schema_suffix=".items")
         if len(obj) != len(items_schema_json
                            ) and schema.additionalProperties is False:
             # If our array is not exactly the same size as the
             # schema and additional properties are disallowed then
             # validation fails
             self._report_error(
                 "{obj!r} is not of the same length as array schema"
                 " {schema!r} and additionalProperties is"
                 " false".format(obj=obj, schema=items_schema_json),
                 "Object array is not of the same length as schema array",
                 schema_suffix=".items")
         # Validate each array element using schema for the
         # corresponding array index, fill missing values (since
         # there may be more items in our array than in the schema)
         # with additionalProperties which by now is not False
         for index, (item, item_schema_json) in enumerate(
                 itertools.izip_longest(
                     obj,
                     items_schema_json,
                     fillvalue=schema.additionalProperties)):
             item_schema = Schema(item_schema_json)
             if index < len(items_schema_json):
                 self._push_schema(item_schema, "items[%d]" % index)
             else:
                 self._push_schema(item_schema, ".additionalProperties")
             self._push_array_item_object(index)
             self._validate()
             self._pop_schema()
             self._pop_object()
 def _push_array_schema(self):
     schema = Schema(self._schema.items)
     self._push_schema(schema, ".items")
 def _push_additional_property_schema(self):
     schema = Schema(self._schema.additionalProperties)
     self._push_schema(schema, ".additionalProperties")
Example #14
0
 def _push_property_schema(self, prop):
     """Construct a sub-schema from a property of the current schema."""
     schema = Schema(self._schema.properties[prop])
     self._push_schema(schema, ".properties." + prop)
def _validate_lmp_module(lmp_module_data):
    schema = Schema(lmp_module_schema)
    Validator.validate(schema, lmp_module_data)
 def validate_parameters(cls, params):
     if cls.parameters_schema:
         if params is None:
             params = {}
         schema = Schema(cls.parameters_schema)
         Validator.validate(schema, params)
Example #17
0
class PanoptesConsumerRecordValidator(object):
    """
    This class implements validators for various consumer types
    """

    _metrics_schema = Schema({
        u"$schema":
        u"http://json-schema.org/draft-04/schema#",
        u"type":
        u"object",
        u"properties": {
            u"metrics": {
                u"type":
                u"array",
                u"items": [{
                    u"type":
                    u"object",
                    u"properties": {
                        u"metric_name": {
                            u"type": u"string"
                        },
                        u"metric_value": {
                            u"type": u"number"
                        },
                        u"metric_type": {
                            u"type": u"string",
                            u"enum": [u"gauge", u"counter"]
                        },
                        u"metric_creation_timestamp": {
                            u"type": u"number"
                        }
                    },
                    u"required": [
                        u"metric_name", u"metric_value", u"metric_type",
                        u"metric_creation_timestamp"
                    ]
                }],
                u"minItems":
                1
            },
            u"dimensions": {
                u"type":
                u"array",
                u"items": [{
                    u"type": u"object",
                    u"properties": {
                        u"dimension_name": {
                            u"type": u"string"
                        },
                        u"dimension_value": {
                            u"type": u"string"
                        }
                    },
                    u"required": [u"dimension_name", u"dimension_value"]
                }]
            },
            u"resource": {
                u"type":
                u"object",
                u"properties": {
                    u"resource_site": {
                        u"type": u"string"
                    },
                    u"resource_class": {
                        u"type": u"string"
                    },
                    u"resource_subclass": {
                        u"type": u"string"
                    },
                    u"resource_type": {
                        u"type": u"string"
                    },
                    u"resource_id": {
                        u"type": u"string"
                    }
                },
                u"required": [
                    u"resource_site", u"resource_class", u"resource_subclass",
                    u"resource_type", u"resource_id"
                ]
            },
            u"metrics_group_type": {
                u"type": u"string"
            },
            u"metrics_group_interval": {
                u"type": u"number"
            },
            u"metrics_group_creation_timestamp": {
                u"type": u"number"
            },
            u"metrics_group_schema_version": {
                u"type": u"string",
                u"enum": [u"0.2"]
            }
        },
        u"required": [
            u"metrics", u"dimensions", u"resource", u"metrics_group_type",
            u"metrics_group_interval", u"metrics_group_creation_timestamp",
            u"metrics_group_schema_version"
        ]
    })

    _resource_schema = Schema({
        u"$schema":
        u"http://json-schema.org/draft-04/schema#",
        u"type":
        u"object",
        u"properties": {
            u"resources": {
                u"type":
                u"array",
                u"items": [{
                    u"type":
                    u"object",
                    u"properties": {
                        u"resource_site": {
                            u"type": u"string"
                        },
                        u"resource_class": {
                            u"type": u"string"
                        },
                        u"resource_subclass": {
                            u"type": u"string"
                        },
                        u"resource_type": {
                            u"type": u"string"
                        },
                        u"resource_id": {
                            u"type": u"string"
                        },
                        u"resource_endpoint": {
                            u"type": u"string"
                        },
                        u"resource_creation_timestamp": {
                            u"type": u"number"
                        },
                        u"resource_metadata": {
                            u"type": u"object",
                            u"patternProperties": {
                                u"^[a-zA-Z0-9][a-zA-Z0-9]*$": {
                                    u"type": u"string"
                                }
                            }
                        }
                    },
                    u"required": [
                        u"resource_site", u"resource_class",
                        u"resource_subclass", u"resource_type", u"resource_id",
                        u"resource_endpoint", u"resource_creation_timestamp"
                    ]
                }],
                u"minItems":
                1
            },
            u"resource_set_creation_timestamp": {
                u"type": u"number"
            },
            u"resource_set_schema_version": {
                u"type": u"string",
                u"enum": [u"0.1"]
            }
        },
        u"required": [
            u"resources", u"resource_set_creation_timestamp",
            u"resource_set_schema_version"
        ]
    })

    @staticmethod
    def validate_metrics(consumer_record):
        """
        This method validates that the passed consumer record  is a valid metrics group record

        Args:
            consumer_record (str): A valid JSON string representation of the consumer record

        Returns:
            bool: True if validation passes, False otherwise
        """
        try:
            return Validator.validate(
                PanoptesConsumerRecordValidator._metrics_schema,
                consumer_record)
        except ValidationError:
            return False

    @staticmethod
    def validate_resources(consumer_record):
        """
        This method validates that the passed consumer record  is a valid resources record

        Args:
            consumer_record (str): A valid JSON string representation of the consumer record

        Returns:
            bool: True if validation passes, False otherwise
        """
        try:
            return Validator.validate(
                PanoptesConsumerRecordValidator._resource_schema,
                consumer_record)
        except ValidationError:
            return False

    @staticmethod
    def validate(consumer_type, consumer_record):
        """
        This method is the 'entry point' for this class and routes the validation to type specific methods

        Args:
            consumer_type (int): A valid consumer type
            consumer_record (str): A valid JSON string representation of the consumer record
        """
        if consumer_type == PanoptesConsumerTypes.METRICS:
            return PanoptesConsumerRecordValidator.validate_metrics(
                consumer_record)
        elif consumer_type == PanoptesConsumerTypes.RESOURCES:
            return PanoptesConsumerRecordValidator.validate_resources(
                consumer_record)
        elif consumer_type == PanoptesConsumerTypes.PROCESSED:
            return PanoptesConsumerRecordValidator.validate_metrics(
                consumer_record)
        else:
            return False