Browse Source

All record value(s) are first-class objects, as compatible as possible with previous str/dict

pull/929/head
Ross McFarland 3 years ago
parent
commit
b402a43665
No known key found for this signature in database GPG Key ID: 943B179E15D3B22A
2 changed files with 618 additions and 273 deletions
  1. +450
    -153
      octodns/record/__init__.py
  2. +168
    -120
      tests/test_octodns_record.py

+ 450
- 153
octodns/record/__init__.py View File

@ -9,7 +9,7 @@ from __future__ import (
unicode_literals, unicode_literals,
) )
from ipaddress import IPv4Address, IPv6Address
from ipaddress import IPv4Address as _IPv4Address, IPv6Address as _IPv6Address
from logging import getLogger from logging import getLogger
import re import re
@ -326,7 +326,7 @@ class ValuesMixin(object):
def __init__(self, zone, name, data, source=None): def __init__(self, zone, name, data, source=None):
super(ValuesMixin, self).__init__(zone, name, data, source=source) super(ValuesMixin, self).__init__(zone, name, data, source=source)
try: try:
values = data['values']
values = [v for v in data['values']]
except KeyError: except KeyError:
values = [data['value']] values = [data['value']]
self.values = sorted(self._value_type.process(values)) self.values = sorted(self._value_type.process(values))
@ -770,7 +770,38 @@ class _DynamicMixin(object):
return super(_DynamicMixin, self).__repr__() return super(_DynamicMixin, self).__repr__()
class _IpList(object):
class _TargetValue(str):
@classmethod
def validate(cls, data, _type):
reasons = []
if data == '':
reasons.append('empty value')
elif not data:
reasons.append('missing value')
# NOTE: FQDN complains if the data it receives isn't a str, it doesn't
# allow unicode... This is likely specific to 2.7
elif not FQDN(str(data), allow_underscores=True).is_valid:
reasons.append(f'{_type} value "{data}" is not a valid FQDN')
elif not data.endswith('.'):
reasons.append(f'{_type} value "{data}" missing trailing .')
return reasons
@classmethod
def process(self, value):
if value:
return value.lower()
return value
class CnameValue(_TargetValue):
pass
class DnameValue(_TargetValue):
pass
class _IpAddress(str):
@classmethod @classmethod
def validate(cls, data, _type): def validate(cls, data, _type):
if not isinstance(data, (list, tuple)): if not isinstance(data, (list, tuple)):
@ -795,64 +826,38 @@ class _IpList(object):
def process(cls, values): def process(cls, values):
# Translating None into '' so that the list will be sortable in # Translating None into '' so that the list will be sortable in
# python3, get everything to str first # python3, get everything to str first
values = [str(v) if v is not None else '' for v in values]
values = [v if v is not None else '' for v in values]
# Now round trip all non-'' through the address type and back to a str # Now round trip all non-'' through the address type and back to a str
# to normalize the address representation. # to normalize the address representation.
return [str(cls._address_type(v)) if v != '' else '' for v in values]
class Ipv4List(_IpList):
_address_name = 'IPv4'
_address_type = IPv4Address
class Ipv6List(_IpList):
_address_name = 'IPv6'
_address_type = IPv6Address
class _TargetValue(object):
@classmethod
def validate(cls, data, _type):
reasons = []
if data == '':
reasons.append('empty value')
elif not data:
reasons.append('missing value')
# NOTE: FQDN complains if the data it receives isn't a str, it doesn't
# allow unicode... This is likely specific to 2.7
elif not FQDN(str(data), allow_underscores=True).is_valid:
reasons.append(f'{_type} value "{data}" is not a valid FQDN')
elif not data.endswith('.'):
reasons.append(f'{_type} value "{data}" missing trailing .')
return reasons
@classmethod
def process(self, value):
if value:
return value.lower()
return value
return [cls(v) if v != '' else '' for v in values]
class CnameValue(_TargetValue):
pass
def __new__(cls, v):
if v:
v = str(cls._address_type(v))
return super().__new__(cls, v)
class DnameValue(_TargetValue):
pass
class Ipv4Address(_IpAddress):
_address_type = _IPv4Address
_address_name = 'IPv4'
class ARecord(_DynamicMixin, _GeoMixin, Record): class ARecord(_DynamicMixin, _GeoMixin, Record):
_type = 'A' _type = 'A'
_value_type = Ipv4List
_value_type = Ipv4Address
Record.register_type(ARecord) Record.register_type(ARecord)
class Ipv6Address(_IpAddress):
_address_type = _IPv6Address
_address_name = 'IPv6'
class AaaaRecord(_DynamicMixin, _GeoMixin, Record): class AaaaRecord(_DynamicMixin, _GeoMixin, Record):
_type = 'AAAA' _type = 'AAAA'
_value_type = Ipv6List
_value_type = Ipv6Address
Record.register_type(AaaaRecord) Record.register_type(AaaaRecord)
@ -878,7 +883,7 @@ class AliasRecord(ValueMixin, Record):
Record.register_type(AliasRecord) Record.register_type(AliasRecord)
class CaaValue(EqualityTupleMixin):
class CaaValue(EqualityTupleMixin, dict):
# https://tools.ietf.org/html/rfc6844#page-5 # https://tools.ietf.org/html/rfc6844#page-5
@classmethod @classmethod
@ -905,13 +910,41 @@ class CaaValue(EqualityTupleMixin):
return [CaaValue(v) for v in values] return [CaaValue(v) for v in values]
def __init__(self, value): def __init__(self, value):
self.flags = int(value.get('flags', 0))
self.tag = value['tag']
self.value = value['value']
super().__init__(
{
'flags': int(value.get('flags', 0)),
'tag': value['tag'],
'value': value['value'],
}
)
@property
def flags(self):
return self['flags']
@flags.setter
def flags(self, value):
self['flags'] = value
@property
def tag(self):
return self['tag']
@tag.setter
def tag(self, value):
self['tag'] = value
@property
def value(self):
return self['value']
@value.setter
def value(self, value):
self['value'] = value
@property @property
def data(self): def data(self):
return {'flags': self.flags, 'tag': self.tag, 'value': self.value}
return self
def _equality_tuple(self): def _equality_tuple(self):
return (self.flags, self.tag, self.value) return (self.flags, self.tag, self.value)
@ -952,7 +985,7 @@ class DnameRecord(_DynamicMixin, ValueMixin, Record):
Record.register_type(DnameRecord) Record.register_type(DnameRecord)
class LocValue(EqualityTupleMixin):
class LocValue(EqualityTupleMixin, dict):
# TODO: work out how to do defaults per RFC # TODO: work out how to do defaults per RFC
@classmethod @classmethod
@ -1051,35 +1084,122 @@ class LocValue(EqualityTupleMixin):
return [LocValue(v) for v in values] return [LocValue(v) for v in values]
def __init__(self, value): def __init__(self, value):
self.lat_degrees = int(value['lat_degrees'])
self.lat_minutes = int(value['lat_minutes'])
self.lat_seconds = float(value['lat_seconds'])
self.lat_direction = value['lat_direction'].upper()
self.long_degrees = int(value['long_degrees'])
self.long_minutes = int(value['long_minutes'])
self.long_seconds = float(value['long_seconds'])
self.long_direction = value['long_direction'].upper()
self.altitude = float(value['altitude'])
self.size = float(value['size'])
self.precision_horz = float(value['precision_horz'])
self.precision_vert = float(value['precision_vert'])
super().__init__(
{
'lat_degrees': int(value['lat_degrees']),
'lat_minutes': int(value['lat_minutes']),
'lat_seconds': float(value['lat_seconds']),
'lat_direction': value['lat_direction'].upper(),
'long_degrees': int(value['long_degrees']),
'long_minutes': int(value['long_minutes']),
'long_seconds': float(value['long_seconds']),
'long_direction': value['long_direction'].upper(),
'altitude': float(value['altitude']),
'size': float(value['size']),
'precision_horz': float(value['precision_horz']),
'precision_vert': float(value['precision_vert']),
}
)
@property
def lat_degrees(self):
return self['lat_degrees']
@lat_degrees.setter
def lat_degrees(self, value):
self['lat_degrees'] = value
@property
def lat_minutes(self):
return self['lat_minutes']
@lat_minutes.setter
def lat_minutes(self, value):
self['lat_minutes'] = value
@property
def lat_seconds(self):
return self['lat_seconds']
@lat_seconds.setter
def lat_seconds(self, value):
self['lat_seconds'] = value
@property
def lat_direction(self):
return self['lat_direction']
@lat_direction.setter
def lat_direction(self, value):
self['lat_direction'] = value
@property
def long_degrees(self):
return self['long_degrees']
@long_degrees.setter
def long_degrees(self, value):
self['long_degrees'] = value
@property
def long_minutes(self):
return self['long_minutes']
@long_minutes.setter
def long_minutes(self, value):
self['long_minutes'] = value
@property
def long_seconds(self):
return self['long_seconds']
@long_seconds.setter
def long_seconds(self, value):
self['long_seconds'] = value
@property
def long_direction(self):
return self['long_direction']
@long_direction.setter
def long_direction(self, value):
self['long_direction'] = value
@property
def altitude(self):
return self['altitude']
@altitude.setter
def altitude(self, value):
self['altitude'] = value
@property
def size(self):
return self['size']
@size.setter
def size(self, value):
self['size'] = value
@property
def precision_horz(self):
return self['precision_horz']
@precision_horz.setter
def precision_horz(self, value):
self['precision_horz'] = value
@property
def precision_vert(self):
return self['precision_vert']
@precision_vert.setter
def precision_vert(self, value):
self['precision_vert'] = value
@property @property
def data(self): def data(self):
return {
'lat_degrees': self.lat_degrees,
'lat_minutes': self.lat_minutes,
'lat_seconds': self.lat_seconds,
'lat_direction': self.lat_direction,
'long_degrees': self.long_degrees,
'long_minutes': self.long_minutes,
'long_seconds': self.long_seconds,
'long_direction': self.long_direction,
'altitude': self.altitude,
'size': self.size,
'precision_horz': self.precision_horz,
'precision_vert': self.precision_vert,
}
return self
def __hash__(self): def __hash__(self):
return hash( return hash(
@ -1134,7 +1254,7 @@ class LocRecord(ValuesMixin, Record):
Record.register_type(LocRecord) Record.register_type(LocRecord)
class MxValue(EqualityTupleMixin):
class MxValue(EqualityTupleMixin, dict):
@classmethod @classmethod
def validate(cls, data, _type): def validate(cls, data, _type):
if not isinstance(data, (list, tuple)): if not isinstance(data, (list, tuple)):
@ -1177,17 +1297,34 @@ class MxValue(EqualityTupleMixin):
preference = value['preference'] preference = value['preference']
except KeyError: except KeyError:
preference = value['priority'] preference = value['priority']
self.preference = int(preference)
# UNTIL 1.0 remove value fallback # UNTIL 1.0 remove value fallback
try: try:
exchange = value['exchange'] exchange = value['exchange']
except KeyError: except KeyError:
exchange = value['value'] exchange = value['value']
self.exchange = exchange.lower()
super().__init__(
{'preference': int(preference), 'exchange': exchange.lower()}
)
@property
def preference(self):
return self['preference']
@preference.setter
def preference(self, value):
self['preference'] = value
@property
def exchange(self):
return self['exchange']
@exchange.setter
def exchange(self, value):
self['exchange'] = value
@property @property
def data(self): def data(self):
return {'preference': self.preference, 'exchange': self.exchange}
return self
def __hash__(self): def __hash__(self):
return hash((self.preference, self.exchange)) return hash((self.preference, self.exchange))
@ -1207,7 +1344,7 @@ class MxRecord(ValuesMixin, Record):
Record.register_type(MxRecord) Record.register_type(MxRecord)
class NaptrValue(EqualityTupleMixin):
class NaptrValue(EqualityTupleMixin, dict):
VALID_FLAGS = ('S', 'A', 'U', 'P') VALID_FLAGS = ('S', 'A', 'U', 'P')
@classmethod @classmethod
@ -1247,23 +1384,68 @@ class NaptrValue(EqualityTupleMixin):
return [NaptrValue(v) for v in values] return [NaptrValue(v) for v in values]
def __init__(self, value): def __init__(self, value):
self.order = int(value['order'])
self.preference = int(value['preference'])
self.flags = value['flags']
self.service = value['service']
self.regexp = value['regexp']
self.replacement = value['replacement']
super().__init__(
{
'order': int(value['order']),
'preference': int(value['preference']),
'flags': value['flags'],
'service': value['service'],
'regexp': value['regexp'],
'replacement': value['replacement'],
}
)
@property
def order(self):
return self['order']
@order.setter
def order(self, value):
self['order'] = value
@property
def preference(self):
return self['preference']
@preference.setter
def preference(self, value):
self['preference'] = value
@property
def flags(self):
return self['flags']
@flags.setter
def flags(self, value):
self['flags'] = value
@property
def service(self):
return self['service']
@service.setter
def service(self, value):
self['service'] = value
@property
def regexp(self):
return self['regexp']
@regexp.setter
def regexp(self, value):
self['regexp'] = value
@property
def replacement(self):
return self['replacement']
@replacement.setter
def replacement(self, value):
self['replacement'] = value
@property @property
def data(self): def data(self):
return {
'order': self.order,
'preference': self.preference,
'flags': self.flags,
'service': self.service,
'regexp': self.regexp,
'replacement': self.replacement,
}
return self
def __hash__(self): def __hash__(self):
return hash(self.__repr__()) return hash(self.__repr__())
@ -1296,7 +1478,7 @@ class NaptrRecord(ValuesMixin, Record):
Record.register_type(NaptrRecord) Record.register_type(NaptrRecord)
class _NsValue(object):
class _NsValue(str):
@classmethod @classmethod
def validate(cls, data, _type): def validate(cls, data, _type):
if not data: if not data:
@ -1361,7 +1543,7 @@ class PtrRecord(ValuesMixin, Record):
Record.register_type(PtrRecord) Record.register_type(PtrRecord)
class SshfpValue(EqualityTupleMixin):
class SshfpValue(EqualityTupleMixin, dict):
VALID_ALGORITHMS = (1, 2, 3, 4) VALID_ALGORITHMS = (1, 2, 3, 4)
VALID_FINGERPRINT_TYPES = (1, 2) VALID_FINGERPRINT_TYPES = (1, 2)
@ -1400,17 +1582,41 @@ class SshfpValue(EqualityTupleMixin):
return [SshfpValue(v) for v in values] return [SshfpValue(v) for v in values]
def __init__(self, value): def __init__(self, value):
self.algorithm = int(value['algorithm'])
self.fingerprint_type = int(value['fingerprint_type'])
self.fingerprint = value['fingerprint']
super().__init__(
{
'algorithm': int(value['algorithm']),
'fingerprint_type': int(value['fingerprint_type']),
'fingerprint': value['fingerprint'],
}
)
@property
def algorithm(self):
return self['algorithm']
@algorithm.setter
def algorithm(self, value):
self['algorithm'] = value
@property
def fingerprint_type(self):
return self['fingerprint_type']
@fingerprint_type.setter
def fingerprint_type(self, value):
self['fingerprint_type'] = value
@property
def fingerprint(self):
return self['fingerprint']
@fingerprint.setter
def fingerprint(self, value):
self['fingerprint'] = value
@property @property
def data(self): def data(self):
return {
'algorithm': self.algorithm,
'fingerprint_type': self.fingerprint_type,
'fingerprint': self.fingerprint,
}
return self
def __hash__(self): def __hash__(self):
return hash(self.__repr__()) return hash(self.__repr__())
@ -1451,7 +1657,7 @@ class _ChunkedValuesMixin(ValuesMixin):
return values return values
class _ChunkedValue(object):
class _ChunkedValue(str):
_unescaped_semicolon_re = re.compile(r'\w;') _unescaped_semicolon_re = re.compile(r'\w;')
@classmethod @classmethod
@ -1472,7 +1678,7 @@ class _ChunkedValue(object):
for v in values: for v in values:
if v and v[0] == '"': if v and v[0] == '"':
v = v[1:-1] v = v[1:-1]
ret.append(v.replace('" "', ''))
ret.append(cls(v.replace('" "', '')))
return ret return ret
@ -1484,7 +1690,7 @@ class SpfRecord(_ChunkedValuesMixin, Record):
Record.register_type(SpfRecord) Record.register_type(SpfRecord)
class SrvValue(EqualityTupleMixin):
class SrvValue(EqualityTupleMixin, dict):
@classmethod @classmethod
def validate(cls, data, _type): def validate(cls, data, _type):
if not isinstance(data, (list, tuple)): if not isinstance(data, (list, tuple)):
@ -1530,19 +1736,50 @@ class SrvValue(EqualityTupleMixin):
return [SrvValue(v) for v in values] return [SrvValue(v) for v in values]
def __init__(self, value): def __init__(self, value):
self.priority = int(value['priority'])
self.weight = int(value['weight'])
self.port = int(value['port'])
self.target = value['target'].lower()
super().__init__(
{
'priority': int(value['priority']),
'weight': int(value['weight']),
'port': int(value['port']),
'target': value['target'].lower(),
}
)
@property
def priority(self):
return self['priority']
@priority.setter
def priority(self, value):
self['priority'] = value
@property
def weight(self):
return self['weight']
@weight.setter
def weight(self, value):
self['weight'] = value
@property
def port(self):
return self['port']
@port.setter
def port(self, value):
self['port'] = value
@property
def target(self):
return self['target']
@target.setter
def target(self, value):
self['target'] = value
@property @property
def data(self): def data(self):
return {
'priority': self.priority,
'weight': self.weight,
'port': self.port,
'target': self.target,
}
return self
def __hash__(self): def __hash__(self):
return hash(self.__repr__()) return hash(self.__repr__())
@ -1571,7 +1808,7 @@ class SrvRecord(ValuesMixin, Record):
Record.register_type(SrvRecord) Record.register_type(SrvRecord)
class TlsaValue(EqualityTupleMixin):
class TlsaValue(EqualityTupleMixin, dict):
@classmethod @classmethod
def validate(cls, data, _type): def validate(cls, data, _type):
if not isinstance(data, (list, tuple)): if not isinstance(data, (list, tuple)):
@ -1621,21 +1858,48 @@ class TlsaValue(EqualityTupleMixin):
return [TlsaValue(v) for v in values] return [TlsaValue(v) for v in values]
def __init__(self, value): def __init__(self, value):
self.certificate_usage = int(value.get('certificate_usage', 0))
self.selector = int(value.get('selector', 0))
self.matching_type = int(value.get('matching_type', 0))
self.certificate_association_data = value[
'certificate_association_data'
]
super().__init__(
{
'certificate_usage': int(value.get('certificate_usage', 0)),
'selector': int(value.get('selector', 0)),
'matching_type': int(value.get('matching_type', 0)),
'certificate_association_data': value[
'certificate_association_data'
],
}
)
@property @property
def data(self):
return {
'certificate_usage': self.certificate_usage,
'selector': self.selector,
'matching_type': self.matching_type,
'certificate_association_data': self.certificate_association_data,
}
def certificate_usage(self):
return self['certificate_usage']
@certificate_usage.setter
def certificate_usage(self, value):
self['certificate_usage'] = value
@property
def selector(self):
return self['selector']
@selector.setter
def selector(self, value):
self['selector'] = value
@property
def matching_type(self):
return self['matching_type']
@matching_type.setter
def matching_type(self, value):
self['matching_type'] = value
@property
def certificate_association_data(self):
return self['certificate_association_data']
@certificate_association_data.setter
def certificate_association_data(self, value):
self['certificate_association_data'] = value
def _equality_tuple(self): def _equality_tuple(self):
return ( return (
@ -1672,7 +1936,7 @@ class TxtRecord(_ChunkedValuesMixin, Record):
Record.register_type(TxtRecord) Record.register_type(TxtRecord)
class UrlfwdValue(EqualityTupleMixin):
class UrlfwdValue(EqualityTupleMixin, dict):
VALID_CODES = (301, 302) VALID_CODES = (301, 302)
VALID_MASKS = (0, 1, 2) VALID_MASKS = (0, 1, 2)
VALID_QUERY = (0, 1) VALID_QUERY = (0, 1)
@ -1717,34 +1981,67 @@ class UrlfwdValue(EqualityTupleMixin):
return [UrlfwdValue(v) for v in values] return [UrlfwdValue(v) for v in values]
def __init__(self, value): def __init__(self, value):
self.path = value['path']
self.target = value['target']
self.code = int(value['code'])
self.masking = int(value['masking'])
self.query = int(value['query'])
super().__init__(
{
'path': value['path'],
'target': value['target'],
'code': int(value['code']),
'masking': int(value['masking']),
'query': int(value['query']),
}
)
@property @property
def data(self):
return {
'path': self.path,
'target': self.target,
'code': self.code,
'masking': self.masking,
'query': self.query,
}
def path(self):
return self['path']
def __hash__(self):
return hash(self.__repr__())
@path.setter
def path(self, value):
self['path'] = value
@property
def target(self):
return self['target']
@target.setter
def target(self, value):
self['target'] = value
@property
def code(self):
return self['code']
@code.setter
def code(self, value):
self['code'] = value
@property
def masking(self):
return self['masking']
@masking.setter
def masking(self, value):
self['masking'] = value
@property
def query(self):
return self['query']
@query.setter
def query(self, value):
self['query'] = value
def _equality_tuple(self): def _equality_tuple(self):
return (self.path, self.target, self.code, self.masking, self.query) return (self.path, self.target, self.code, self.masking, self.query)
def __repr__(self):
return (
f'"{self.path}" "{self.target}" {self.code} '
f'{self.masking} {self.query}'
def __hash__(self):
return hash(
(self.path, self.target, self.code, self.masking, self.query)
) )
def __repr__(self):
return f'"{self.path}" "{self.target}" {self.code} {self.masking} {self.query}'
class UrlfwdRecord(ValuesMixin, Record): class UrlfwdRecord(ValuesMixin, Record):
_type = 'URLFWD' _type = 'URLFWD'


+ 168
- 120
tests/test_octodns_record.py View File

@ -38,6 +38,7 @@ from octodns.record import (
SrvRecord, SrvRecord,
SrvValue, SrvValue,
TlsaRecord, TlsaRecord,
TlsaValue,
TxtRecord, TxtRecord,
Update, Update,
UrlfwdRecord, UrlfwdRecord,
@ -380,12 +381,14 @@ class TestRecord(TestCase):
def test_caa(self): def test_caa(self):
a_values = [ a_values = [
{'flags': 0, 'tag': 'issue', 'value': 'ca.example.net'},
{
'flags': 128,
'tag': 'iodef',
'value': 'mailto:security@example.com',
},
CaaValue({'flags': 0, 'tag': 'issue', 'value': 'ca.example.net'}),
CaaValue(
{
'flags': 128,
'tag': 'iodef',
'value': 'mailto:security@example.com',
}
),
] ]
a_data = {'ttl': 30, 'values': a_values} a_data = {'ttl': 30, 'values': a_values}
a = CaaRecord(self.zone, 'a', a_data) a = CaaRecord(self.zone, 'a', a_data)
@ -400,7 +403,9 @@ class TestRecord(TestCase):
self.assertEqual(a_values[1]['value'], a.values[1].value) self.assertEqual(a_values[1]['value'], a.values[1].value)
self.assertEqual(a_data, a.data) self.assertEqual(a_data, a.data)
b_value = {'tag': 'iodef', 'value': 'http://iodef.example.com/'}
b_value = CaaValue(
{'tag': 'iodef', 'value': 'http://iodef.example.com/'}
)
b_data = {'ttl': 30, 'value': b_value} b_data = {'ttl': 30, 'value': b_value}
b = CaaRecord(self.zone, 'b', b_data) b = CaaRecord(self.zone, 'b', b_data)
self.assertEqual(0, b.values[0].flags) self.assertEqual(0, b.values[0].flags)
@ -442,20 +447,22 @@ class TestRecord(TestCase):
def test_loc(self): def test_loc(self):
a_values = [ a_values = [
{
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
LocValue(
{
'lat_degrees': 31,
'lat_minutes': 58,
'lat_seconds': 52.1,
'lat_direction': 'S',
'long_degrees': 115,
'long_minutes': 49,
'long_seconds': 11.7,
'long_direction': 'E',
'altitude': 20,
'size': 10,
'precision_horz': 10,
'precision_vert': 2,
}
)
] ]
a_data = {'ttl': 30, 'values': a_values} a_data = {'ttl': 30, 'values': a_values}
a = LocRecord(self.zone, 'a', a_data) a = LocRecord(self.zone, 'a', a_data)
@ -483,20 +490,22 @@ class TestRecord(TestCase):
a_values[0]['precision_vert'], a.values[0].precision_vert a_values[0]['precision_vert'], a.values[0].precision_vert
) )
b_value = {
'lat_degrees': 32,
'lat_minutes': 7,
'lat_seconds': 19,
'lat_direction': 'S',
'long_degrees': 116,
'long_minutes': 2,
'long_seconds': 25,
'long_direction': 'E',
'altitude': 10,
'size': 1,
'precision_horz': 10000,
'precision_vert': 10,
}
b_value = LocValue(
{
'lat_degrees': 32,
'lat_minutes': 7,
'lat_seconds': 19,
'lat_direction': 'S',
'long_degrees': 116,
'long_minutes': 2,
'long_seconds': 25,
'long_direction': 'E',
'altitude': 10,
'size': 1,
'precision_horz': 10000,
'precision_vert': 10,
}
)
b_data = {'ttl': 30, 'value': b_value} b_data = {'ttl': 30, 'value': b_value}
b = LocRecord(self.zone, 'b', b_data) b = LocRecord(self.zone, 'b', b_data)
self.assertEqual(b_value['lat_degrees'], b.values[0].lat_degrees) self.assertEqual(b_value['lat_degrees'], b.values[0].lat_degrees)
@ -534,8 +543,8 @@ class TestRecord(TestCase):
def test_mx(self): def test_mx(self):
a_values = [ a_values = [
{'preference': 10, 'exchange': 'smtp1.'},
{'priority': 20, 'value': 'smtp2.'},
MxValue({'preference': 10, 'exchange': 'smtp1.'}),
MxValue({'priority': 20, 'value': 'smtp2.'}),
] ]
a_data = {'ttl': 30, 'values': a_values} a_data = {'ttl': 30, 'values': a_values}
a = MxRecord(self.zone, 'a', a_data) a = MxRecord(self.zone, 'a', a_data)
@ -544,12 +553,12 @@ class TestRecord(TestCase):
self.assertEqual(30, a.ttl) self.assertEqual(30, a.ttl)
self.assertEqual(a_values[0]['preference'], a.values[0].preference) self.assertEqual(a_values[0]['preference'], a.values[0].preference)
self.assertEqual(a_values[0]['exchange'], a.values[0].exchange) self.assertEqual(a_values[0]['exchange'], a.values[0].exchange)
self.assertEqual(a_values[1]['priority'], a.values[1].preference)
self.assertEqual(a_values[1]['value'], a.values[1].exchange)
a_data['values'][1] = {'preference': 20, 'exchange': 'smtp2.'}
self.assertEqual(a_values[1]['preference'], a.values[1].preference)
self.assertEqual(a_values[1]['exchange'], a.values[1].exchange)
a_data['values'][1] = MxValue({'preference': 20, 'exchange': 'smtp2.'})
self.assertEqual(a_data, a.data) self.assertEqual(a_data, a.data)
b_value = {'preference': 0, 'exchange': 'smtp3.'}
b_value = MxValue({'preference': 0, 'exchange': 'smtp3.'})
b_data = {'ttl': 30, 'value': b_value} b_data = {'ttl': 30, 'value': b_value}
b = MxRecord(self.zone, 'b', b_data) b = MxRecord(self.zone, 'b', b_data)
self.assertEqual(b_value['preference'], b.values[0].preference) self.assertEqual(b_value['preference'], b.values[0].preference)
@ -585,22 +594,26 @@ class TestRecord(TestCase):
def test_naptr(self): def test_naptr(self):
a_values = [ a_values = [
{
'order': 10,
'preference': 11,
'flags': 'X',
'service': 'Y',
'regexp': 'Z',
'replacement': '.',
},
{
'order': 20,
'preference': 21,
'flags': 'A',
'service': 'B',
'regexp': 'C',
'replacement': 'foo.com',
},
NaptrValue(
{
'order': 10,
'preference': 11,
'flags': 'X',
'service': 'Y',
'regexp': 'Z',
'replacement': '.',
}
),
NaptrValue(
{
'order': 20,
'preference': 21,
'flags': 'A',
'service': 'B',
'regexp': 'C',
'replacement': 'foo.com',
}
),
] ]
a_data = {'ttl': 30, 'values': a_values} a_data = {'ttl': 30, 'values': a_values}
a = NaptrRecord(self.zone, 'a', a_data) a = NaptrRecord(self.zone, 'a', a_data)
@ -612,14 +625,16 @@ class TestRecord(TestCase):
self.assertEqual(a_values[i][k], getattr(a.values[i], k)) self.assertEqual(a_values[i][k], getattr(a.values[i], k))
self.assertEqual(a_data, a.data) self.assertEqual(a_data, a.data)
b_value = {
'order': 30,
'preference': 31,
'flags': 'M',
'service': 'N',
'regexp': 'O',
'replacement': 'x',
}
b_value = NaptrValue(
{
'order': 30,
'preference': 31,
'flags': 'M',
'service': 'N',
'regexp': 'O',
'replacement': 'x',
}
)
b_data = {'ttl': 30, 'value': b_value} b_data = {'ttl': 30, 'value': b_value}
b = NaptrRecord(self.zone, 'b', b_data) b = NaptrRecord(self.zone, 'b', b_data)
for k in a_values[0].keys(): for k in a_values[0].keys():
@ -861,8 +876,20 @@ class TestRecord(TestCase):
def test_sshfp(self): def test_sshfp(self):
a_values = [ a_values = [
{'algorithm': 10, 'fingerprint_type': 11, 'fingerprint': 'abc123'},
{'algorithm': 20, 'fingerprint_type': 21, 'fingerprint': 'def456'},
SshfpValue(
{
'algorithm': 10,
'fingerprint_type': 11,
'fingerprint': 'abc123',
}
),
SshfpValue(
{
'algorithm': 20,
'fingerprint_type': 21,
'fingerprint': 'def456',
}
),
] ]
a_data = {'ttl': 30, 'values': a_values} a_data = {'ttl': 30, 'values': a_values}
a = SshfpRecord(self.zone, 'a', a_data) a = SshfpRecord(self.zone, 'a', a_data)
@ -876,11 +903,9 @@ class TestRecord(TestCase):
self.assertEqual(a_values[0]['fingerprint'], a.values[0].fingerprint) self.assertEqual(a_values[0]['fingerprint'], a.values[0].fingerprint)
self.assertEqual(a_data, a.data) self.assertEqual(a_data, a.data)
b_value = {
'algorithm': 30,
'fingerprint_type': 31,
'fingerprint': 'ghi789',
}
b_value = SshfpValue(
{'algorithm': 30, 'fingerprint_type': 31, 'fingerprint': 'ghi789'}
)
b_data = {'ttl': 30, 'value': b_value} b_data = {'ttl': 30, 'value': b_value}
b = SshfpRecord(self.zone, 'b', b_data) b = SshfpRecord(self.zone, 'b', b_data)
self.assertEqual(b_value['algorithm'], b.values[0].algorithm) self.assertEqual(b_value['algorithm'], b.values[0].algorithm)
@ -924,8 +949,12 @@ class TestRecord(TestCase):
def test_srv(self): def test_srv(self):
a_values = [ a_values = [
{'priority': 10, 'weight': 11, 'port': 12, 'target': 'server1'},
{'priority': 20, 'weight': 21, 'port': 22, 'target': 'server2'},
SrvValue(
{'priority': 10, 'weight': 11, 'port': 12, 'target': 'server1'}
),
SrvValue(
{'priority': 20, 'weight': 21, 'port': 22, 'target': 'server2'}
),
] ]
a_data = {'ttl': 30, 'values': a_values} a_data = {'ttl': 30, 'values': a_values}
a = SrvRecord(self.zone, '_a._tcp', a_data) a = SrvRecord(self.zone, '_a._tcp', a_data)
@ -936,14 +965,21 @@ class TestRecord(TestCase):
self.assertEqual(a_values[0]['weight'], a.values[0].weight) self.assertEqual(a_values[0]['weight'], a.values[0].weight)
self.assertEqual(a_values[0]['port'], a.values[0].port) self.assertEqual(a_values[0]['port'], a.values[0].port)
self.assertEqual(a_values[0]['target'], a.values[0].target) self.assertEqual(a_values[0]['target'], a.values[0].target)
from pprint import pprint
pprint(
{
'a_values': a_values,
'self': a_data,
'other': a.data,
'a.values': a.values,
}
)
self.assertEqual(a_data, a.data) self.assertEqual(a_data, a.data)
b_value = {
'priority': 30,
'weight': 31,
'port': 32,
'target': 'server3',
}
b_value = SrvValue(
{'priority': 30, 'weight': 31, 'port': 32, 'target': 'server3'}
)
b_data = {'ttl': 30, 'value': b_value} b_data = {'ttl': 30, 'value': b_value}
b = SrvRecord(self.zone, '_b._tcp', b_data) b = SrvRecord(self.zone, '_b._tcp', b_data)
self.assertEqual(b_value['priority'], b.values[0].priority) self.assertEqual(b_value['priority'], b.values[0].priority)
@ -987,18 +1023,22 @@ class TestRecord(TestCase):
def test_tlsa(self): def test_tlsa(self):
a_values = [ a_values = [
{
'certificate_usage': 1,
'selector': 1,
'matching_type': 1,
'certificate_association_data': 'ABABABABABABABABAB',
},
{
'certificate_usage': 2,
'selector': 0,
'matching_type': 2,
'certificate_association_data': 'ABABABABABABABABAC',
},
TlsaValue(
{
'certificate_usage': 1,
'selector': 1,
'matching_type': 1,
'certificate_association_data': 'ABABABABABABABABAB',
}
),
TlsaValue(
{
'certificate_usage': 2,
'selector': 0,
'matching_type': 2,
'certificate_association_data': 'ABABABABABABABABAC',
}
),
] ]
a_data = {'ttl': 30, 'values': a_values} a_data = {'ttl': 30, 'values': a_values}
a = TlsaRecord(self.zone, 'a', a_data) a = TlsaRecord(self.zone, 'a', a_data)
@ -1030,12 +1070,14 @@ class TestRecord(TestCase):
) )
self.assertEqual(a_data, a.data) self.assertEqual(a_data, a.data)
b_value = {
'certificate_usage': 0,
'selector': 0,
'matching_type': 0,
'certificate_association_data': 'AAAAAAAAAAAAAAA',
}
b_value = TlsaValue(
{
'certificate_usage': 0,
'selector': 0,
'matching_type': 0,
'certificate_association_data': 'AAAAAAAAAAAAAAA',
}
)
b_data = {'ttl': 30, 'value': b_value} b_data = {'ttl': 30, 'value': b_value}
b = TlsaRecord(self.zone, 'b', b_data) b = TlsaRecord(self.zone, 'b', b_data)
self.assertEqual( self.assertEqual(
@ -1087,20 +1129,24 @@ class TestRecord(TestCase):
def test_urlfwd(self): def test_urlfwd(self):
a_values = [ a_values = [
{
'path': '/',
'target': 'http://foo',
'code': 301,
'masking': 2,
'query': 0,
},
{
'path': '/target',
'target': 'http://target',
'code': 302,
'masking': 2,
'query': 0,
},
UrlfwdValue(
{
'path': '/',
'target': 'http://foo',
'code': 301,
'masking': 2,
'query': 0,
}
),
UrlfwdValue(
{
'path': '/target',
'target': 'http://target',
'code': 302,
'masking': 2,
'query': 0,
}
),
] ]
a_data = {'ttl': 30, 'values': a_values} a_data = {'ttl': 30, 'values': a_values}
a = UrlfwdRecord(self.zone, 'a', a_data) a = UrlfwdRecord(self.zone, 'a', a_data)
@ -1119,13 +1165,15 @@ class TestRecord(TestCase):
self.assertEqual(a_values[1]['query'], a.values[1].query) self.assertEqual(a_values[1]['query'], a.values[1].query)
self.assertEqual(a_data, a.data) self.assertEqual(a_data, a.data)
b_value = {
'path': '/',
'target': 'http://location',
'code': 301,
'masking': 2,
'query': 0,
}
b_value = UrlfwdValue(
{
'path': '/',
'target': 'http://location',
'code': 301,
'masking': 2,
'query': 0,
}
)
b_data = {'ttl': 30, 'value': b_value} b_data = {'ttl': 30, 'value': b_value}
b = UrlfwdRecord(self.zone, 'b', b_data) b = UrlfwdRecord(self.zone, 'b', b_data)
self.assertEqual(b_value['path'], b.values[0].path) self.assertEqual(b_value['path'], b.values[0].path)
@ -2987,7 +3035,7 @@ class TestRecordValidation(TestCase):
) )
self.assertEqual('.', record.values[0].exchange) self.assertEqual('.', record.values[0].exchange)
def test_NXPTR(self):
def test_NAPTR(self):
# doesn't blow up # doesn't blow up
Record.new( Record.new(
self.zone, self.zone,


Loading…
Cancel
Save