Esempio n. 1
0
    def validate(self):
        try:
            if self.type == 'SECONDARY' and self.masters is None:
                errors = ValidationErrorList()
                e = ValidationError()
                e.path = ['type']
                e.validator = 'required'
                e.validator_value = ['masters']
                e.message = "'masters' is a required property"
                errors.append(e)
                raise exceptions.InvalidObject(
                    "Provided object does not match "
                    "schema",
                    errors=errors,
                    object=self)

            super(Domain, self).validate()
        except exceptions.RelationNotLoaded as ex:
            errors = ValidationErrorList()
            e = ValidationError()
            e.path = ['type']
            e.validator = 'required'
            e.validator_value = [ex.relation]
            e.message = "'%s' is a required property" % ex.relation
            errors.append(e)
            raise exceptions.InvalidObject(
                "Provided object does not match "
                "schema",
                errors=errors,
                object=self)
Esempio n. 2
0
    def parse(cls, format_, values, output_object, *args, **kwargs):

        LOG.debug("Creating %s object with values %r",
                  output_object.obj_name(), values)
        LOG.debug(output_object)

        try:
            if isinstance(output_object, objects.ListObjectMixin):
                # type_ = 'list'
                return cls.get_object_adapter(format_,
                                              output_object)._parse_list(
                                                  values, output_object, *args,
                                                  **kwargs)
            else:
                # type_ = 'object'
                return cls.get_object_adapter(format_,
                                              output_object)._parse_object(
                                                  values, output_object, *args,
                                                  **kwargs)

        except TypeError as e:
            LOG.exception("TypeError creating %(name)s with values %(values)r",
                          {
                              "name": output_object.obj_name(),
                              "values": values
                          })
            error_message = (u'Provided object is not valid. '
                             u'Got a TypeError with message {}'.format(
                                 six.text_type(e)))
            raise exceptions.InvalidObject(error_message)

        except AttributeError as e:
            LOG.exception(
                "AttributeError creating %(name)s with values %(values)r", {
                    "name": output_object.obj_name(),
                    "values": values
                })
            error_message = (u'Provided object is not valid. '
                             u'Got an AttributeError with message {}'.format(
                                 six.text_type(e)))
            raise exceptions.InvalidObject(error_message)

        except exceptions.InvalidObject:
            LOG.info("InvalidObject creating %(name)s with values %(values)r",
                     {
                         "name": output_object.obj_name(),
                         "values": values
                     })
            raise

        except Exception as e:
            LOG.exception("Exception creating %(name)s with values %(values)r",
                          {
                              "name": output_object.obj_name(),
                              "values": values
                          })
            error_message = (u'Provided object is not valid. '
                             u'Got a {} error with message {}'.format(
                                 type(e).__name__, six.text_type(e)))
            raise exceptions.InvalidObject(error_message)
Esempio n. 3
0
 def validate(self):
     # NOTE(kiall, daidv): We make use of the Object registry here
     # in order to avoid an impossible circular import.
     ValidationErrorList = self.obj_cls_from_name('ValidationErrorList')
     ValidationError = self.obj_cls_from_name('ValidationError')
     self.fields = self.FIELDS
     for name in self.fields:
         field = self.fields[name]
         if self.obj_attr_is_set(name):
             value = getattr(self, name)  # Check relation
             if isinstance(value, ListObjectMixin):
                 for obj in value.objects:
                     obj.validate()
             elif isinstance(value, DesignateObject):
                 value.validate()
             else:
                 try:
                     field.coerce(self, name, value)  # Check value
                 except Exception:
                     raise exceptions.InvalidObject(
                         "{} is invalid".format(name))
         elif not field.nullable:
             # Check required is True ~ nullable is False
             errors = ValidationErrorList()
             e = ValidationError()
             e.path = ['records', 0]
             e.validator = 'required'
             e.validator_value = [name]
             e.message = "'%s' is a required property" % name
             errors.append(e)
             raise exceptions.InvalidObject(
                 "Provided object does not match "
                 "schema",
                 errors=errors,
                 object=self)
Esempio n. 4
0
 def _raise(self, errors):
     if len(errors) != 0:
         raise exceptions.InvalidObject(
             "Provided object does not match "
             "schema",
             errors=errors,
             object=self)
Esempio n. 5
0
    def validate(self):

        make_class_validator(self)

        # NOTE(kiall): We make use of the Object registry here in order to
        #              avoid an impossible circular import.
        ValidationErrorList = self.obj_cls_from_name('ValidationErrorList')
        ValidationError = self.obj_cls_from_name('ValidationError')

        values = self.to_dict()
        errors = ValidationErrorList()

        LOG.debug("Validating '%(name)s' object with values: %(values)r", {
            'name': self.obj_name(),
            'values': values,
        })

        for error in self._obj_validator.iter_errors(values):
            errors.append(ValidationError.from_js_error(error))

        if len(errors) > 0:
            raise exceptions.InvalidObject(
                "Provided object does not match "
                "schema",
                errors=errors,
                object=self)
Esempio n. 6
0
    def patch_one(self, zone_id):
        """Update Zone"""
        # TODO(kiall): This needs cleanup to say the least..
        request = pecan.request
        context = request.environ['context']
        body = request.body_dict
        response = pecan.response

        # TODO(kiall): Validate we have a sane UUID for zone_id

        # Fetch the existing zone
        zone = self.central_api.get_zone(context, zone_id)

        # Don't allow updates to zones that are being deleted
        if zone.action == "DELETE":
            raise exceptions.BadRequest('Can not update a deleting zone')

        if request.content_type == 'application/json-patch+json':
            # Possible pattern:
            #
            # 1) Load existing zone.
            # 2) Apply patch, maintain list of changes.
            # 3) Return changes, after passing through the code ^ for plain
            #    JSON.
            #
            # Difficulties:
            #
            # 1) "Nested" resources? records inside a recordset.
            # 2) What to do when a zone doesn't exist in the first place?
            # 3) ...?
            raise NotImplemented('json-patch not implemented')
        else:
            # Update the zone object with the new values
            zone = DesignateAdapter.parse('API_v2', body, zone)

            zone.validate()
            # If masters are specified then we set zone.transferred_at to None
            # which will cause a new transfer
            if 'attributes' in zone.obj_what_changed():
                zone.transferred_at = None

            # Update and persist the resource

            if zone.type == 'SECONDARY' and 'email' in zone.obj_what_changed():
                msg = "Changed email is not allowed."
                raise exceptions.InvalidObject(msg)

            increment_serial = zone.type == 'PRIMARY'
            zone = self.central_api.update_zone(
                context, zone, increment_serial=increment_serial)

        LOG.info(_LI("Updated %(zone)s"), {'zone': zone})

        if zone.status == 'PENDING':
            response.status_int = 202
        else:
            response.status_int = 200

        return DesignateAdapter.render('API_v2', zone, request=request)
Esempio n. 7
0
    def validate(self):

        make_class_validator(self)

        # NOTE(kiall): We make use of the Object registry here in order to
        #              avoid an impossible circular import.
        ValidationErrorList = self.obj_cls_from_name('ValidationErrorList')
        ValidationError = self.obj_cls_from_name('ValidationError')

        errors = ValidationErrorList()

        try:
            values = self.to_dict()
        except exceptions.RelationNotLoaded as e:
            e = ValidationError()
            e.path = ['type']
            e.validator = 'required'
            e.validator_value = [e.relation]
            e.message = "'%s' is a required property" % e.relation
            errors.append(e)
            raise exceptions.InvalidObject(
                "Provided object does not match "
                "schema",
                errors=errors,
                object=self)

        LOG.debug("Validating '%(name)s' object with values: %(values)r", {
            'name': self.obj_name(),
            'values': values,
        })

        for error in self._obj_validator.iter_errors(values):
            errors.append(ValidationError.from_js_error(error))

        if len(errors) > 0:
            LOG.debug(
                "Error Validating '%(name)s' object with values: "
                "%(values)r", {
                    'name': self.obj_name(),
                    'values': values,
                })
            raise exceptions.InvalidObject(
                "Provided object does not match "
                "schema",
                errors=errors,
                object=self)
Esempio n. 8
0
    def _parse_object(cls, new_recordset, recordset, *args, **kwargs):
        # TODO(Graham): Remove this when
        # https://bugs.launchpad.net/designate/+bug/1432842 is fixed
        try:
            recordset.records
        except exceptions.RelationNotLoaded:
            recordset.records = objects.RecordList()

        original_records = set()
        for record in recordset.records:
            original_records.add(record.data)
        # Get new list of Records
        new_records = set()
        if 'records' in new_recordset:
            if isinstance(new_recordset['records'], list):
                for record in new_recordset['records']:
                    new_records.add(record)
            else:
                errors = objects.ValidationErrorList()
                e = objects.ValidationError()
                e.path = ['records']
                e.validator = 'type'
                e.validator_value = ["list"]
                e.message = ("'%(data)s' is not a valid list of records" % {
                    'data': new_recordset['records']
                })
                # Add it to the list for later
                errors.append(e)
                raise exceptions.InvalidObject(
                    "Provided object does not match "
                    "schema",
                    errors=errors,
                    object=cls.ADAPTER_OBJECT())

        # Get differences of Records
        records_to_add = new_records.difference(original_records)
        records_to_rm = original_records.difference(new_records)

        # Update all items except records
        record_update = False
        if 'records' in new_recordset:
            record_update = True
            del new_recordset['records']

        # Remove deleted records if we have provided a records array
        if record_update:
            recordset.records[:] = [
                record for record in recordset.records
                if record.data not in records_to_rm
            ]

        # Add new records
        for record in records_to_add:
            recordset.records.append(objects.Record(data=record))

        return super(RecordSetAPIv2Adapter,
                     cls)._parse_object(new_recordset, recordset, *args,
                                        **kwargs)
Esempio n. 9
0
 def _validate_fail(self, errors, msg):
     e = ValidationError()
     e.path = ['recordset', 'type']
     e.validator = 'value'
     e.validator_value = [self.type]
     e.message = msg
     # Add it to the list for later
     errors.append(e)
     raise exceptions.InvalidObject(
         "Provided object does not match "
         "schema", errors=errors, object=self)
Esempio n. 10
0
    def validate(self, obj):
        LOG.debug('Validating values: %r' % obj)
        errors = []

        for error in self.validator.iter_errors(obj):
            errors.append({
                'path': ".".join([str(x) for x in error.path]),
                'message': error.message,
                'validator': error.validator
            })

        if len(errors) > 0:
            LOG.debug('Errors in validation: %r' % errors)
            raise exceptions.InvalidObject("Provided object does not match "
                                           "schema", errors=errors)
Esempio n. 11
0
def load_values(request, valid_keys):
    """Load valid attributes from request"""
    result = {}
    error_keys = []
    values = request.json
    for k in values:
        if k in valid_keys:
            result[k] = values[k]
        else:
            error_keys.append(k)

    if error_keys:
        error_msg = 'Provided object does not match schema. Keys {0} are not \
                     valid in the request body', error_keys
        raise exceptions.InvalidObject(error_msg)

    return result
Esempio n. 12
0
    def _load(self, context, request, body, valid_keys):
        """Extract a "central" compatible dict from an API call"""
        result = {}
        item = body[self._resource_name]
        error_keys = []

        # Copy keys which need no alterations
        for k in item:
            if k in valid_keys:
                result[k] = item[k]
            else:
                error_keys.append(k)

        if error_keys:
            error_message = str.format(
                'Provided object does not match schema.  Keys {0} are not '
                'valid in the request body', error_keys)

            raise exceptions.InvalidObject(error_message)

        return result
Esempio n. 13
0
    def validate(self):

        LOG.debug("Validating '%(name)s' object with values: %(values)r", {
            'name': self.obj_name(),
            'values': self.to_dict(),
        })
        LOG.debug(list(self.records))

        errors = ValidationErrorList()

        # Get the right classes (e.g. A for Recordsets with type: 'A')
        try:
            record_list_cls = self.obj_cls_from_name('%sList' % self.type)
            record_cls = self.obj_cls_from_name(self.type)
        except (KeyError, ovo_exc.UnsupportedObjectError) as e:
            err_msg = ("'%(type)s' is not a valid record type" % {
                'type': self.type
            })
            self._validate_fail(errors, err_msg)

        if self.type not in cfg.CONF.supported_record_type:
            err_msg = ("'%(type)s' is not a supported record type" % {
                'type': self.type
            })
            self._validate_fail(errors, err_msg)

        # Get any rules that the record type imposes on the record
        changes = record_cls.get_recordset_schema_changes()
        old_fields = {}
        if changes:
            LOG.debug("Record %s is overriding the RecordSet schema with: %s",
                      record_cls.obj_name(), changes)
            old_fields = deepcopy(self.FIELDS)
            self.FIELDS = utils.deep_dict_merge(self.FIELDS, changes)

        error_indexes = []
        # Copy these for safekeeping
        old_records = deepcopy(self.records)

        # Blank the records for this object with the right list type
        self.records = record_list_cls()

        i = 0

        for record in old_records:
            record_obj = record_cls()
            try:
                record_obj._from_string(record.data)
            # The _from_string() method will throw a ValueError if there is not
            # enough data blobs
            except ValueError as e:
                # Something broke in the _from_string() method
                # Fake a correct looking ValidationError() object
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except TypeError as e:
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except AttributeError as e:
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except Exception as e:
                error_message = ('Provided object is not valid. Got a %s error'
                                 ' with message %s' %
                                 (type(e).__name__, six.text_type(e)))
                raise exceptions.InvalidObject(error_message)

            else:
                # Seems to have loaded right - add it to be validated by
                # JSONSchema
                self.records.append(record_obj)
            i += 1

        try:
            # Run the actual validate code
            super(RecordSet, self).validate()

        except exceptions.InvalidObject as e:
            raise e
        else:
            # If JSONSchema passes, but we found parsing errors,
            # raise an exception
            if len(errors) > 0:
                LOG.debug(
                    "Error Validating '%(name)s' object with values: "
                    "%(values)r", {
                        'name': self.obj_name(),
                        'values': self.to_dict(),
                    })
                raise exceptions.InvalidObject(
                    "Provided object does not match "
                    "schema",
                    errors=errors,
                    object=self)
        finally:
            if old_fields:
                self.FIELDS = old_fields

        # Send in the traditional Record objects to central / storage
        self.records = old_records
Esempio n. 14
0
    def validate(self):

        LOG.debug("Validating '%(name)s' object with values: %(values)r", {
            'name': self.obj_name(),
            'values': self.to_dict(),
        })

        errors = ValidationErrorList()

        # Get the right classes (e.g. A for Recordsets with type: 'A')
        try:
            record_list_cls = self.obj_cls_from_name('%sList' % self.type)
            record_cls = self.obj_cls_from_name(self.type)
        except KeyError as e:
            e = ValidationError()
            e.path = ['recordset', 'type']
            e.validator = 'value'
            e.validator_value = [self.type]
            e.message = ("'%(type)s' is not a supported Record type" % {
                'type': self.type
            })
            # Add it to the list for later
            errors.append(e)
            raise exceptions.InvalidObject(
                "Provided object does not match "
                "schema",
                errors=errors,
                object=self)

        # Get any rules that the record type imposes on the record
        changes = record_cls.get_recordset_schema_changes()
        old_fields = {}
        if changes:
            LOG.debug("Record %s is overriding the RecordSet schema with: %s" %
                      (record_cls.obj_name(), changes))
            old_fields = deepcopy(self.FIELDS)
            self.FIELDS = utils.deep_dict_merge(self.FIELDS, changes)

        error_indexes = []
        # Copy these for safekeeping
        old_records = deepcopy(self.records)

        # Blank the records for this object with the right list type
        self.records = record_list_cls()

        i = 0

        for record in old_records:
            record_obj = record_cls()
            try:
                record_obj._from_string(record.data)
            # The _from_string() method will throw a ValueError if there is not
            # enough data blobs
            except ValueError as e:
                # Something broke in the _from_string() method
                # Fake a correct looking ValidationError() object
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except TypeError as e:
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except AttributeError as e:
                e = ValidationError()
                e.path = ['records', i]
                e.validator = 'format'
                e.validator_value = [self.type]
                e.message = ("'%(data)s' is not a '%(type)s' Record" % {
                    'data': record.data,
                    'type': self.type
                })
                # Add it to the list for later
                errors.append(e)
                error_indexes.append(i)

            except Exception as e:
                error_message = str.format(
                    'Provided object is not valid. '
                    'Got a %s error with message %s' %
                    (type(e).__name__, six.text_type(e)))
                raise exceptions.InvalidObject(error_message)

            else:
                # Seems to have loaded right - add it to be validated by
                # JSONSchema
                self.records.append(record_obj)
            i += 1

        try:
            # Run the actual validate code
            super(RecordSet, self).validate()

        except exceptions.InvalidObject as e:
            # Something is wrong according to JSONSchema - append our errors
            increment = 0
            # This code below is to make sure we have the index for the record
            # list correct. JSONSchema may be missing some of the objects due
            # to validation above, so this re - inserts them, and makes sure
            # the index is right
            for error in e.errors:
                if len(error.path) > 1 and isinstance(error.path[1], int):
                    error.path[1] += increment
                    while error.path[1] in error_indexes:
                        increment += 1
                        error.path[1] += 1
            # Add the list from above
            e.errors.extend(errors)
            # Raise the exception
            raise e
        else:
            # If JSONSchema passes, but we found parsing errors,
            # raise an exception
            if len(errors) > 0:
                LOG.debug(
                    "Error Validating '%(name)s' object with values: "
                    "%(values)r", {
                        'name': self.obj_name(),
                        'values': self.to_dict(),
                    })
                raise exceptions.InvalidObject(
                    "Provided object does not match "
                    "schema",
                    errors=errors,
                    object=self)
        finally:
            if old_fields:
                self.FIELDS = old_fields
        # Send in the traditional Record objects to central / storage
        self.records = old_records
Esempio n. 15
0
    def _parse_object(cls, values, output_object, *args, **kwargs):
        error_keys = []

        for key, value in values.items():
            if key in cls.MODIFICATIONS['fields']:
                # No rename needed
                obj_key = key
                # This item may need to be translated
                if cls.MODIFICATIONS['fields'][key].get('rename', False):
                    obj_key = cls.MODIFICATIONS['fields'][key].get('rename')

                ##############################################################
                # TODO(graham): Remove this section of code  when validation #
                # is moved into DesignateObjects properly                    #
                ##############################################################

                # Check if the field should be allowed change after it is
                # initially set (eg zone name)
                if cls.MODIFICATIONS['fields'][key].get('immutable', False):
                    if getattr(output_object, obj_key, False) and \
                            getattr(output_object, obj_key) != value:
                        error_keys.append(key)
                        break
                # Is this field a read only field
                elif cls.MODIFICATIONS['fields'][key].get('read_only', True) \
                        and getattr(output_object, obj_key) != value:
                    error_keys.append(key)
                    break

                # Check if the key is a nested object
                if output_object.FIELDS.get(obj_key,
                                            {}).get('relation', False):
                    # Get the right class name
                    obj_class_name = output_object.FIELDS.get(
                        obj_key, {}).get('relation_cls')
                    # Get the an instance of it
                    obj_class = \
                        objects.DesignateObject.obj_cls_from_name(
                            obj_class_name)
                    # Get the adapted object
                    obj = \
                        cls.get_object_adapter(
                            cls.ADAPTER_FORMAT, obj_class_name).parse(
                                value, obj_class())
                    # Set the object on the main object
                    setattr(output_object, obj_key, obj)
                else:
                    # No nested objects here, just set the value
                    setattr(output_object, obj_key, value)
            else:
                # We got an extra key
                error_keys.append(key)

        if error_keys:
            error_message = str.format(
                'Provided object does not match schema.  Keys {0} are not '
                'valid for {1}', error_keys,
                cls.MODIFICATIONS['options']['resource_name'])

            raise exceptions.InvalidObject(error_message)

        return output_object
Esempio n. 16
0
    def patch_one(self, zone_id):
        """Update Zone"""
        # TODO(kiall): This needs cleanup to say the least..
        request = pecan.request
        context = request.environ['context']
        body = request.body_dict
        response = pecan.response

        # TODO(kiall): Validate we have a sane UUID for zone_id

        # Fetch the existing zone
        zone = self.central_api.get_domain(context, zone_id)

        # Convert to APIv2 Format
        zone_data = self._view.show(context, request, zone)

        if request.content_type == 'application/json-patch+json':
            # Possible pattern:
            #
            # 1) Load existing zone.
            # 2) Apply patch, maintain list of changes.
            # 3) Return changes, after passing through the code ^ for plain
            #    JSON.
            #
            # Difficulties:
            #
            # 1) "Nested" resources? records inside a recordset.
            # 2) What to do when a zone doesn't exist in the first place?
            # 3) ...?
            raise NotImplemented('json-patch not implemented')
        else:
            zone_data = utils.deep_dict_merge(zone_data, body)

            # Validate the new set of data
            self._resource_schema.validate(zone_data)

            # Unpack the values
            values = self._view.load(context, request, body)

            zone.set_masters(values.pop('masters', []))

            # If masters are specified then we set zone.transferred_at to None
            # which will cause a new transfer
            if 'attributes' in zone.obj_what_changed():
                zone.transferred_at = None

            # Update and persist the resource
            zone.update(values)

            if zone.type == 'SECONDARY' and 'email' in zone.obj_what_changed():
                msg = "Changed email is not allowed."
                raise exceptions.InvalidObject(msg)

            increment_serial = zone.type == 'PRIMARY'
            zone = self.central_api.update_domain(
                context, zone, increment_serial=increment_serial)

        if zone.status == 'PENDING':
            response.status_int = 202
        else:
            response.status_int = 200

        return self._view.show(context, request, zone)