Example #1
0
class SOA(Record):
    """
    SOA Resource Record Type
    Defined in: RFC1035
    """

    fields = {
        'mname': fields.DomainField(maxLength=255),
        'rname': fields.DomainField(maxLength=255),
        'serial': fields.IntegerFields(minimum=1, maximum=4294967295),
        'refresh': fields.IntegerFields(minimum=0, maximum=2147483647),
        'retry': fields.IntegerFields(minimum=0, maximum=2147483647),
        'expire': fields.IntegerFields(minimum=0, maximum=2147483647),
        'minimum': fields.IntegerFields(minimum=0, maximum=2147483647)
    }

    def _to_string(self):
        return ("%(mname)s %(rname)s %(serial)s %(refresh)s %(retry)s "
                "%(expire)s %(minimum)s" % self)

    def _from_string(self, v):
        mname, rname, serial, refresh, retry, expire, minimum = v.split(' ')
        self.mname = mname
        self.rname = rname
        self.serial = int(serial)
        self.refresh = int(refresh)
        self.retry = int(retry)
        self.expire = int(expire)
        self.minimum = int(minimum)

    # The record type is defined in the RFC. This will be used when the record
    # is sent by mini-dns.
    RECORD_TYPE = 6
Example #2
0
class SRV(Record):
    """
    SRV Resource Record Type
    Defined in: RFC2782
    """
    fields = {
        'priority': fields.IntegerFields(minimum=0, maximum=65535),
        'weight': fields.IntegerFields(minimum=0, maximum=65535),
        'port': fields.IntegerFields(minimum=0, maximum=65535),
        'target': fields.DomainField(maxLength=255),
    }

    @classmethod
    def get_recordset_schema_changes(cls):
        return {'name': fields.SRVField(maxLength=255, nullable=True)}

    def _to_string(self):
        return "%(priority)s %(weight)s %(target)s %(port)s" % self

    def _from_string(self, value):
        priority, weight, port, target = value.split(' ')
        self.priority = int(priority)
        self.weight = int(weight)
        self.port = int(port)
        self.target = target

    # The record type is defined in the RFC. This will be used when the record
    # is sent by mini-dns.
    RECORD_TYPE = 33
Example #3
0
class FloatingIP(base.DictObjectMixin, base.PersistentObjectMixin,
                 base.DesignateObject):
    fields = {
        "address":
        fields.IPV4AddressField(nullable=True),
        "description":
        fields.StringFields(nullable=True, maxLength=160),
        "ptrdname":
        fields.DomainField(nullable=True),
        "ttl":
        fields.IntegerFields(nullable=True, minimum=1, maximum=2147483647),
        "region":
        fields.StringFields(nullable=True),
        "action":
        fields.EnumField(['CREATE', 'DELETE', 'UPDATE', 'NONE'],
                         nullable=True),
        "status":
        fields.EnumField(['ACTIVE', 'PENDING', 'ERROR'], nullable=True)
    }

    STRING_KEYS = ['key', 'address', 'ptrdname']

    @property
    def key(self):
        return '%s:%s' % (self.region, self.id)
Example #4
0
class NAPTR(Record):
    """
    NAPTR Resource Record Type
    Defined in: RFC2915
    """
    fields = {
        'order': fields.IntegerFields(minimum=0, maximum=65535),
        'preference': fields.IntegerFields(minimum=0, maximum=65535),
        'flags': fields.NaptrFlagsField(),
        'service': fields.NaptrServiceField(),
        'regexp': fields.NaptrRegexpField(),
        'replacement': fields.DomainField(maxLength=255)
    }

    def _to_string(self):
        return ("%(order)s %(preference)s %(flags)s %(service)s %(regexp)s "
                "%(replacement)s" % self)

    def _from_string(self, v):
        order, preference, flags, service, regexp, replacement = v.split(' ')
        self.order = int(order)
        self.preference = int(preference)
        self.flags = flags
        self.service = service
        self.regexp = regexp
        self.replacement = replacement

    # The record type is defined in the RFC. This will be used when the record
    # is sent by mini-dns.
    RECORD_TYPE = 35
class PoolNsRecord(base.DictObjectMixin, base.PersistentObjectMixin,
                   base.DesignateObject):
    fields = {
        'pool_id': fields.UUIDFields(nullable=True),
        'priority': fields.IntegerFields(minimum=1, maximum=10000),
        'hostname': fields.DomainField(maxLength=255),
    }

    STRING_KEYS = ['id', 'hostname', 'priority', 'pool_id']
Example #6
0
class PTR(Record):
    """
    PTR Resource Record Type
    Defined in: RFC1035
    """
    fields = {'ptrdname': fields.DomainField(maxLength=255)}

    def _to_string(self):
        return self.ptrdname

    def _from_string(self, value):
        self.ptrdname = value

    # The record type is defined in the RFC. This will be used when the record
    # is sent by mini-dns.
    RECORD_TYPE = 12
Example #7
0
class NS(Record):
    """
    NS Resource Record Type
    Defined in: RFC1035
    """
    fields = {'nsdname': fields.DomainField(maxLength=255)}

    @classmethod
    def get_recordset_schema_changes(cls):
        return {
            'name': fields.DomainField(),
        }

    def _to_string(self):
        return self.nsdname

    def _from_string(self, value):
        self.nsdname = value

    # The record type is defined in the RFC. This will be used when the record
    # is sent by mini-dns.
    RECORD_TYPE = 2
Example #8
0
class RecordSet(base.DesignateObject, base.DictObjectMixin,
                base.PersistentObjectMixin):
    def __init__(self, *args, **kwargs):
        super(RecordSet, self).__init__(*args, **kwargs)

    @property
    def action(self):
        # Return action as UPDATE if present. CREATE and DELETE are returned
        # if they are the only ones.
        action = 'NONE'
        actions = {'CREATE': 0, 'DELETE': 0, 'UPDATE': 0, 'NONE': 0}
        for record in self.records:
            actions[record.action] += 1

        if actions['CREATE'] != 0 and actions['UPDATE'] == 0 and \
                        actions['DELETE'] == 0 and actions['NONE'] == 0:  # noqa
            action = 'CREATE'
        elif actions['DELETE'] != 0 and actions['UPDATE'] == 0 and \
                        actions['CREATE'] == 0 and actions['NONE'] == 0:  # noqa
            action = 'DELETE'
        elif actions['UPDATE'] != 0 or actions['CREATE'] != 0 or \
                        actions['DELETE'] != 0:  # noqa
            action = 'UPDATE'
        return action

    @property
    def managed(self):
        managed = False
        for record in self.records:
            if record.managed:
                return True
        return managed

    @property
    def status(self):
        # Return the worst status in order of ERROR, PENDING, ACTIVE, DELETED.
        status = None
        statuses = {
            'ERROR': 0,
            'PENDING': 1,
            'ACTIVE': 2,
            'DELETED': 3,
        }
        for record in self.records:
            if not status or statuses[record.status] < statuses[status]:
                status = record.status
        return status or 'ACTIVE'

    fields = {
        'shard': fields.IntegerFields(nullable=True, minimum=0, maximum=4095),
        'tenant_id': fields.StringFields(nullable=True, read_only=True),
        'zone_id': fields.UUIDFields(nullable=True, read_only=True),
        'zone_name': fields.DomainField(nullable=True, maxLength=255),
        'name': fields.HostField(maxLength=255, nullable=True),
        'type': fields.StringFields(nullable=True, read_only=True),
        'ttl': fields.IntegerFields(nullable=True,
                                    minimum=1,
                                    maximum=2147483647),
        'description': fields.StringFields(nullable=True, maxLength=160),
        'records': fields.PolymorphicObjectField('RecordList', nullable=True),
    }

    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)

    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

    STRING_KEYS = ['id', 'type', 'name', 'zone_id']
Example #9
0
 def get_recordset_schema_changes(cls):
     return {
         'name': fields.DomainField(),
     }
Example #10
0
class Zone(base.DesignateObject, base.DictObjectMixin,
           base.PersistentObjectMixin, base.SoftDeleteObjectMixin):
    def __init__(self, *args, **kwargs):
        super(Zone, self).__init__(*args, **kwargs)

    fields = {
        'shard': fields.IntegerFields(nullable=True, minimum=0, maximum=4095),
        'tenant_id': fields.StringFields(nullable=True, read_only=False),
        'name': fields.DomainField(maxLength=255),
        'email': fields.EmailField(maxLength=255, nullable=True),
        'ttl': fields.IntegerFields(nullable=True, minimum=1,
                                    maximum=2147483647),
        'refresh': fields.IntegerFields(nullable=True, minimum=0,
                                        maximum=2147483647, read_only=False),
        'retry': fields.IntegerFields(nullable=True, minimum=0,
                                      maximum=2147483647, read_only=False),
        'expire': fields.IntegerFields(nullable=True, minimum=0,
                                       maximum=2147483647, read_only=False),
        'minimum': fields.IntegerFields(nullable=True, minimum=0,
                                        maximum=2147483647, read_only=False),
        'parent_zone_id': fields.UUIDFields(nullable=True, read_only=False),
        'serial': fields.IntegerFields(nullable=True, minimum=0,
                                       maximum=4294967295, read_only=False),
        'description': fields.StringFields(nullable=True, maxLength=160),
        'status': fields.EnumField(nullable=True, read_only=False,
                                   valid_values=[
                                       'ACTIVE', 'PENDING', 'ERROR',
                                       'DELETED', 'SUCCESS', 'NO_ZONE']

                                   ),
        'action': fields.EnumField(nullable=True,
                                   valid_values=[
                                       'CREATE', 'DELETE', 'UPDATE', 'NONE']
                                   ),
        'pool_id': fields.UUIDFields(nullable=True, read_only=False),
        'recordsets': fields.ObjectField('RecordSetList', nullable=True),
        'attributes': fields.ObjectField('ZoneAttributeList', nullable=True),
        'masters': fields.ObjectField('ZoneMasterList', nullable=True),
        'type': fields.EnumField(nullable=True,
                                 valid_values=['SECONDARY', 'PRIMARY'],
                                 read_only=False
                                 ),
        'transferred_at': fields.DateTimeField(nullable=True, read_only=False),
        'delayed_notify': fields.BooleanField(nullable=True),
    }

    STRING_KEYS = [
        'id', 'type', 'name', 'pool_id', 'serial', 'action', 'status'
    ]

    def get_master_by_ip(self, host):
        """
        Utility to get the master by it's ip for this zone.
        """
        for srv in self.masters:
            srv_host, _ = utils.split_host_port(srv.to_data())
            if host == srv_host:
                return srv
        return False

    def _raise(self, errors):
        if len(errors) != 0:
            raise exceptions.InvalidObject(
                "Provided object does not match "
                "schema", errors=errors, object=self)

    def __hash__(self):
        return hash(self.id)

    def validate(self):
        errors = ValidationErrorList()

        if self.type == 'PRIMARY':
            if self.obj_attr_is_set('masters') and len(self.masters) != 0:
                e = ValidationError()
                e.path = ['type']
                e.validator = 'maxItems'
                e.validator_value = ['masters']
                e.message = "'masters' has more items than allowed"
                errors.append(e)
            if self.email is None:
                e = ValidationError()
                e.path = ['type']
                e.validator = 'required'
                e.validator_value = 'email'
                e.message = "'email' is a required property"
                errors.append(e)
            self._raise(errors)

        try:
            if self.type == 'SECONDARY':
                if self.masters is None or len(self.masters) == 0:
                    e = ValidationError()
                    e.path = ['type']
                    e.validator = 'required'
                    e.validator_value = ['masters']
                    e.message = "'masters' is a required property"
                    errors.append(e)

                for i in ['email', 'ttl']:
                    if i in self.obj_what_changed():
                        e = ValidationError()
                        e.path = ['type']
                        e.validator = 'not_allowed'
                        e.validator_value = i
                        e.message = "'%s' can't be specified when type is " \
                                    "SECONDARY" % i
                        errors.append(e)
                self._raise(errors)

            super(Zone, 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)
            self._raise(errors)