Browse Source

Base of Record type registration

pull/889/head
Ross McFarland 4 years ago
parent
commit
7f98d9dfca
No known key found for this signature in database GPG Key ID: 943B179E15D3B22A
2 changed files with 75 additions and 22 deletions
  1. +66
    -19
      octodns/record/__init__.py
  2. +9
    -3
      tests/test_octodns_record.py

+ 66
- 19
octodns/record/__init__.py View File

@ -64,7 +64,11 @@ class Delete(Change):
return f'Delete {self.existing}' return f'Delete {self.existing}'
class ValidationError(Exception):
class RecordException(Exception):
pass
class ValidationError(RecordException):
@classmethod @classmethod
def build_message(cls, fqdn, reasons): def build_message(cls, fqdn, reasons):
@ -80,6 +84,18 @@ class ValidationError(Exception):
class Record(EqualityTupleMixin): class Record(EqualityTupleMixin):
log = getLogger('Record') log = getLogger('Record')
_CLASSES = {}
@classmethod
def register_type(cls, _type, _class):
existing = cls._CLASSES.get(_type, None)
if existing:
module = existing.__module__
name = existing.__name__
msg = f'Type "{_type}" already registered by {module}.{name}'
raise RecordException(msg)
cls._CLASSES[_type] = _class
@classmethod @classmethod
def new(cls, zone, name, data, source=None, lenient=False): def new(cls, zone, name, data, source=None, lenient=False):
name = str(name) name = str(name)
@ -89,24 +105,7 @@ class Record(EqualityTupleMixin):
except KeyError: except KeyError:
raise Exception(f'Invalid record {fqdn}, missing type') raise Exception(f'Invalid record {fqdn}, missing type')
try: try:
_class = {
'A': ARecord,
'AAAA': AaaaRecord,
'ALIAS': AliasRecord,
'CAA': CaaRecord,
'CNAME': CnameRecord,
'DNAME': DnameRecord,
'LOC': LocRecord,
'MX': MxRecord,
'NAPTR': NaptrRecord,
'NS': NsRecord,
'PTR': PtrRecord,
'SPF': SpfRecord,
'SRV': SrvRecord,
'SSHFP': SshfpRecord,
'TXT': TxtRecord,
'URLFWD': UrlfwdRecord,
}[_type]
_class = cls._CLASSES[_type]
except KeyError: except KeyError:
raise Exception(f'Unknown record type: "{_type}"') raise Exception(f'Unknown record type: "{_type}"')
reasons = _class.validate(name, fqdn, data) reasons = _class.validate(name, fqdn, data)
@ -804,11 +803,17 @@ class ARecord(_DynamicMixin, _GeoMixin, Record):
_value_type = Ipv4List _value_type = Ipv4List
Record.register_type('A', ARecord)
class AaaaRecord(_DynamicMixin, _GeoMixin, Record): class AaaaRecord(_DynamicMixin, _GeoMixin, Record):
_type = 'AAAA' _type = 'AAAA'
_value_type = Ipv6List _value_type = Ipv6List
Record.register_type('AAAA', AaaaRecord)
class AliasValue(_TargetValue): class AliasValue(_TargetValue):
pass pass
@ -826,6 +831,9 @@ class AliasRecord(_ValueMixin, Record):
return reasons return reasons
Record.register_type('ALIAS', AliasRecord)
class CaaValue(EqualityTupleMixin): class CaaValue(EqualityTupleMixin):
# https://tools.ietf.org/html/rfc6844#page-5 # https://tools.ietf.org/html/rfc6844#page-5
@ -877,6 +885,9 @@ class CaaRecord(_ValuesMixin, Record):
_value_type = CaaValue _value_type = CaaValue
Record.register_type('CAA', CaaRecord)
class CnameRecord(_DynamicMixin, _ValueMixin, Record): class CnameRecord(_DynamicMixin, _ValueMixin, Record):
_type = 'CNAME' _type = 'CNAME'
_value_type = CnameValue _value_type = CnameValue
@ -890,11 +901,17 @@ class CnameRecord(_DynamicMixin, _ValueMixin, Record):
return reasons return reasons
Record.register_type('CNAME', CnameRecord)
class DnameRecord(_DynamicMixin, _ValueMixin, Record): class DnameRecord(_DynamicMixin, _ValueMixin, Record):
_type = 'DNAME' _type = 'DNAME'
_value_type = DnameValue _value_type = DnameValue
Record.register_type('DNAME', DnameRecord)
class LocValue(EqualityTupleMixin): class LocValue(EqualityTupleMixin):
# TODO: work out how to do defaults per RFC # TODO: work out how to do defaults per RFC
@ -1071,6 +1088,9 @@ class LocRecord(_ValuesMixin, Record):
_value_type = LocValue _value_type = LocValue
Record.register_type('LOC', LocRecord)
class MxValue(EqualityTupleMixin): class MxValue(EqualityTupleMixin):
@classmethod @classmethod
@ -1141,6 +1161,9 @@ class MxRecord(_ValuesMixin, Record):
_value_type = MxValue _value_type = MxValue
Record.register_type('MX', MxRecord)
class NaptrValue(EqualityTupleMixin): class NaptrValue(EqualityTupleMixin):
VALID_FLAGS = ('S', 'A', 'U', 'P') VALID_FLAGS = ('S', 'A', 'U', 'P')
@ -1219,6 +1242,9 @@ class NaptrRecord(_ValuesMixin, Record):
_value_type = NaptrValue _value_type = NaptrValue
Record.register_type('NAPTR', NaptrRecord)
class _NsValue(object): class _NsValue(object):
@classmethod @classmethod
@ -1246,6 +1272,9 @@ class NsRecord(_ValuesMixin, Record):
_value_type = _NsValue _value_type = _NsValue
Record.register_type('NS', NsRecord)
class PtrValue(_TargetValue): class PtrValue(_TargetValue):
@classmethod @classmethod
@ -1279,6 +1308,9 @@ class PtrRecord(_ValuesMixin, Record):
return self.values[0] return self.values[0]
Record.register_type('PTR', PtrRecord)
class SshfpValue(EqualityTupleMixin): class SshfpValue(EqualityTupleMixin):
VALID_ALGORITHMS = (1, 2, 3, 4) VALID_ALGORITHMS = (1, 2, 3, 4)
VALID_FINGERPRINT_TYPES = (1, 2) VALID_FINGERPRINT_TYPES = (1, 2)
@ -1343,6 +1375,9 @@ class SshfpRecord(_ValuesMixin, Record):
_value_type = SshfpValue _value_type = SshfpValue
Record.register_type('SSHFP', SshfpRecord)
class _ChunkedValuesMixin(_ValuesMixin): class _ChunkedValuesMixin(_ValuesMixin):
CHUNK_SIZE = 255 CHUNK_SIZE = 255
_unescaped_semicolon_re = re.compile(r'\w;') _unescaped_semicolon_re = re.compile(r'\w;')
@ -1392,6 +1427,9 @@ class SpfRecord(_ChunkedValuesMixin, Record):
_value_type = _ChunkedValue _value_type = _ChunkedValue
Record.register_type('SPF', SpfRecord)
class SrvValue(EqualityTupleMixin): class SrvValue(EqualityTupleMixin):
@classmethod @classmethod
@ -1474,6 +1512,9 @@ class SrvRecord(_ValuesMixin, Record):
return reasons return reasons
Record.register_type('SRV', SrvRecord)
class _TxtValue(_ChunkedValue): class _TxtValue(_ChunkedValue):
pass pass
@ -1483,6 +1524,9 @@ class TxtRecord(_ChunkedValuesMixin, Record):
_value_type = _TxtValue _value_type = _TxtValue
Record.register_type('TXT', TxtRecord)
class UrlfwdValue(EqualityTupleMixin): class UrlfwdValue(EqualityTupleMixin):
VALID_CODES = (301, 302) VALID_CODES = (301, 302)
VALID_MASKS = (0, 1, 2) VALID_MASKS = (0, 1, 2)
@ -1558,3 +1602,6 @@ class UrlfwdValue(EqualityTupleMixin):
class UrlfwdRecord(_ValuesMixin, Record): class UrlfwdRecord(_ValuesMixin, Record):
_type = 'URLFWD' _type = 'URLFWD'
_value_type = UrlfwdValue _value_type = UrlfwdValue
Record.register_type('URLFWD', UrlfwdRecord)

+ 9
- 3
tests/test_octodns_record.py View File

@ -10,9 +10,9 @@ from unittest import TestCase
from octodns.record import ARecord, AaaaRecord, AliasRecord, CaaRecord, \ from octodns.record import ARecord, AaaaRecord, AliasRecord, CaaRecord, \
CaaValue, CnameRecord, DnameRecord, Create, Delete, GeoValue, LocRecord, \ CaaValue, CnameRecord, DnameRecord, Create, Delete, GeoValue, LocRecord, \
LocValue, MxRecord, MxValue, NaptrRecord, NaptrValue, NsRecord, \ LocValue, MxRecord, MxValue, NaptrRecord, NaptrValue, NsRecord, \
PtrRecord, Record, SshfpRecord, SshfpValue, SpfRecord, SrvRecord, \
SrvValue, TxtRecord, Update, UrlfwdRecord, UrlfwdValue, ValidationError, \
_Dynamic, _DynamicPool, _DynamicRule
PtrRecord, Record, RecordException, SshfpRecord, SshfpValue, SpfRecord, \
SrvRecord, SrvValue, TxtRecord, Update, UrlfwdRecord, UrlfwdValue, \
ValidationError, _Dynamic, _DynamicPool, _DynamicRule
from octodns.zone import Zone from octodns.zone import Zone
from helpers import DynamicProvider, GeoProvider, SimpleProvider from helpers import DynamicProvider, GeoProvider, SimpleProvider
@ -21,6 +21,12 @@ from helpers import DynamicProvider, GeoProvider, SimpleProvider
class TestRecord(TestCase): class TestRecord(TestCase):
zone = Zone('unit.tests.', []) zone = Zone('unit.tests.', [])
def test_registration(self):
with self.assertRaises(RecordException) as ctx:
Record.register_type('A', None)
self.assertEqual('Type "A" already registered by '
'octodns.record.ARecord', str(ctx.exception))
def test_lowering(self): def test_lowering(self):
record = ARecord(self.zone, 'MiXeDcAsE', { record = ARecord(self.zone, 'MiXeDcAsE', {
'ttl': 30, 'ttl': 30,


Loading…
Cancel
Save