Browse Source

MX RFC1035 - priority -> preference & value -> exchange

pull/77/head
Ross McFarland 9 years ago
parent
commit
1340aee8a9
14 changed files with 118 additions and 88 deletions
  1. +4
    -4
      octodns/provider/cloudflare.py
  2. +4
    -4
      octodns/provider/dnsimple.py
  3. +3
    -3
      octodns/provider/dyn.py
  4. +4
    -4
      octodns/provider/ns1.py
  5. +4
    -4
      octodns/provider/powerdns.py
  6. +5
    -4
      octodns/provider/route53.py
  7. +27
    -14
      octodns/record.py
  8. +2
    -2
      octodns/source/tinydns.py
  9. +6
    -6
      tests/config/unit.tests.yaml
  10. +4
    -4
      tests/test_octodns_provider_dyn.py
  11. +4
    -4
      tests/test_octodns_provider_ns1.py
  12. +6
    -6
      tests/test_octodns_provider_route53.py
  13. +37
    -21
      tests/test_octodns_record.py
  14. +8
    -8
      tests/test_octodns_source_tinydns.py

+ 4
- 4
octodns/provider/cloudflare.py View File

@ -116,8 +116,8 @@ class CloudflareProvider(BaseProvider):
values = [] values = []
for r in records: for r in records:
values.append({ values.append({
'priority': r['priority'],
'value': '{}.'.format(r['content']),
'preference': r['priority'],
'exchange': '{}.'.format(r['content']),
}) })
return { return {
'ttl': records[0]['ttl'], 'ttl': records[0]['ttl'],
@ -207,8 +207,8 @@ class CloudflareProvider(BaseProvider):
def _contents_for_MX(self, record): def _contents_for_MX(self, record):
for value in record.values: for value in record.values:
yield { yield {
'priority': value.priority,
'content': value.value
'priority': value.preference,
'content': value.exchange
} }
def _apply_Create(self, change): def _apply_Create(self, change):


+ 4
- 4
octodns/provider/dnsimple.py View File

@ -128,8 +128,8 @@ class DnsimpleProvider(BaseProvider):
values = [] values = []
for record in records: for record in records:
values.append({ values.append({
'priority': record['priority'],
'value': '{}.'.format(record['content'])
'preference': record['priority'],
'exchange': '{}.'.format(record['content'])
}) })
return { return {
'ttl': records[0]['ttl'], 'ttl': records[0]['ttl'],
@ -290,9 +290,9 @@ class DnsimpleProvider(BaseProvider):
def _params_for_MX(self, record): def _params_for_MX(self, record):
for value in record.values: for value in record.values:
yield { yield {
'content': value.value,
'content': value.exchange,
'name': record.name, 'name': record.name,
'priority': value.priority,
'priority': value.preference,
'ttl': record.ttl, 'ttl': record.ttl,
'type': record._type 'type': record._type
} }


+ 3
- 3
octodns/provider/dyn.py View File

@ -206,7 +206,7 @@ class DynProvider(BaseProvider):
return { return {
'type': _type, 'type': _type,
'ttl': records[0].ttl, 'ttl': records[0].ttl,
'values': [{'priority': r.preference, 'value': r.exchange}
'values': [{'preference': r.preference, 'exchange': r.exchange}
for r in records], for r in records],
} }
@ -400,8 +400,8 @@ class DynProvider(BaseProvider):
def _kwargs_for_MX(self, record): def _kwargs_for_MX(self, record):
return [{ return [{
'preference': v.priority,
'exchange': v.value,
'preference': v.preference,
'exchange': v.exchange,
'ttl': record.ttl, 'ttl': record.ttl,
} for v in record.values] } for v in record.values]


+ 4
- 4
octodns/provider/ns1.py View File

@ -57,10 +57,10 @@ class Ns1Provider(BaseProvider):
def _data_for_MX(self, _type, record): def _data_for_MX(self, _type, record):
values = [] values = []
for answer in record['short_answers']: for answer in record['short_answers']:
priority, value = answer.split(' ', 1)
preference, exchange = answer.split(' ', 1)
values.append({ values.append({
'priority': priority,
'value': value,
'preference': preference,
'exchange': exchange,
}) })
return { return {
'ttl': record['ttl'], 'ttl': record['ttl'],
@ -150,7 +150,7 @@ class Ns1Provider(BaseProvider):
_params_for_PTR = _params_for_CNAME _params_for_PTR = _params_for_CNAME
def _params_for_MX(self, record): def _params_for_MX(self, record):
values = [(v.priority, v.value) for v in record.values]
values = [(v.preference, v.exchange) for v in record.values]
return {'answers': values, 'ttl': record.ttl} return {'answers': values, 'ttl': record.ttl}
def _params_for_NAPTR(self, record): def _params_for_NAPTR(self, record):


+ 4
- 4
octodns/provider/powerdns.py View File

@ -83,10 +83,10 @@ class PowerDnsBaseProvider(BaseProvider):
def _data_for_MX(self, rrset): def _data_for_MX(self, rrset):
values = [] values = []
for record in rrset['records']: for record in rrset['records']:
priority, value = record['content'].split(' ', 1)
preference, exchange = record['content'].split(' ', 1)
values.append({ values.append({
'priority': priority,
'value': value,
'preference': preference,
'exchange': exchange,
}) })
return { return {
'type': rrset['type'], 'type': rrset['type'],
@ -208,7 +208,7 @@ class PowerDnsBaseProvider(BaseProvider):
def _records_for_MX(self, record): def _records_for_MX(self, record):
return [{ return [{
'content': '{} {}'.format(v.priority, v.value),
'content': '{} {}'.format(v.preference, v.exchange),
'disabled': False 'disabled': False
} for v in record.values] } for v in record.values]


+ 5
- 4
octodns/provider/route53.py View File

@ -96,7 +96,8 @@ class _Route53Record(object):
_values_for_PTR = _values_for_value _values_for_PTR = _values_for_value
def _values_for_MX(self, record): def _values_for_MX(self, record):
return ['{} {}'.format(v.priority, v.value) for v in record.values]
return ['{} {}'.format(v.preference, v.exchange)
for v in record.values]
def _values_for_NAPTR(self, record): def _values_for_NAPTR(self, record):
return ['{} {} "{}" "{}" "{}" {}' return ['{} {} "{}" "{}" "{}" {}'
@ -335,10 +336,10 @@ class Route53Provider(BaseProvider):
def _data_for_MX(self, rrset): def _data_for_MX(self, rrset):
values = [] values = []
for rr in rrset['ResourceRecords']: for rr in rrset['ResourceRecords']:
priority, value = rr['Value'].split(' ')
preference, exchange = rr['Value'].split(' ')
values.append({ values.append({
'priority': priority,
'value': value,
'preference': preference,
'exchange': exchange,
}) })
return { return {
'type': rrset['Type'], 'type': rrset['Type'],


+ 27
- 14
octodns/record.py View File

@ -424,32 +424,45 @@ class MxValue(object):
@classmethod @classmethod
def _validate_value(cls, value): def _validate_value(cls, value):
reasons = [] reasons = []
if 'priority' not in value:
reasons.append('missing priority')
if 'value' not in value:
reasons.append('missing value')
if 'preference' not in value and 'priority' not in value:
reasons.append('missing preference')
exchange = None
try:
exchange = value.get('exchange', None) or value['value']
if not exchange.endswith('.'):
reasons.append('missing trailing .')
except KeyError:
reasons.append('missing exchange')
return reasons return reasons
def __init__(self, value): def __init__(self, value):
# TODO: rename preference
self.priority = int(value['priority'])
# TODO: rename to exchange?
self.value = value['value'].lower()
# RFC1035 says preference, half the providers use priority
try:
preference = value['preference']
except KeyError:
preference = value['priority']
self.preference = int(preference)
# UNTIL 1.0 remove value fallback
try:
exchange = value['exchange']
except KeyError:
exchange = value['value']
self.exchange = exchange
@property @property
def data(self): def data(self):
return { return {
'priority': self.priority,
'value': self.value,
'preference': self.preference,
'exchange': self.exchange,
} }
def __cmp__(self, other): def __cmp__(self, other):
if self.priority == other.priority:
return cmp(self.value, other.value)
return cmp(self.priority, other.priority)
if self.preference == other.preference:
return cmp(self.exchange, other.exchange)
return cmp(self.preference, other.preference)
def __repr__(self): def __repr__(self):
return "'{} {}'".format(self.priority, self.value)
return "'{} {}'".format(self.preference, self.exchange)
class MxRecord(_ValuesMixin, Record): class MxRecord(_ValuesMixin, Record):


+ 2
- 2
octodns/source/tinydns.py View File

@ -65,8 +65,8 @@ class TinyDnsBaseSource(BaseSource):
'ttl': ttl, 'ttl': ttl,
'type': _type, 'type': _type,
'values': [{ 'values': [{
'priority': r[1],
'value': '{}.'.format(r[0])
'preference': r[1],
'exchange': '{}.'.format(r[0])
} for r in records] } for r in records]
} }


+ 6
- 6
tests/config/unit.tests.yaml View File

@ -60,12 +60,12 @@ mx:
ttl: 300 ttl: 300
type: MX type: MX
values: values:
- priority: 40
value: smtp-1.unit.tests.
- priority: 20
value: smtp-2.unit.tests.
- priority: 30
value: smtp-3.unit.tests.
- exchange: smtp-1.unit.tests.
preference: 40
- exchange: smtp-2.unit.tests.
preference: 20
- exchange: smtp-3.unit.tests.
preference: 30
- priority: 10 - priority: 10
value: smtp-4.unit.tests. value: smtp-4.unit.tests.
naptr: naptr:


+ 4
- 4
tests/test_octodns_provider_dyn.py View File

@ -46,11 +46,11 @@ class TestDynProvider(TestCase):
'type': 'MX', 'type': 'MX',
'ttl': 302, 'ttl': 302,
'values': [{ 'values': [{
'priority': 10,
'value': 'smtp-1.unit.tests.'
'preference': 10,
'exchange': 'smtp-1.unit.tests.'
}, { }, {
'priority': 20,
'value': 'smtp-2.unit.tests.'
'preference': 20,
'exchange': 'smtp-2.unit.tests.'
}] }]
}), }),
('naptr', { ('naptr', {


+ 4
- 4
tests/test_octodns_provider_ns1.py View File

@ -44,11 +44,11 @@ class TestNs1Provider(TestCase):
'ttl': 35, 'ttl': 35,
'type': 'MX', 'type': 'MX',
'values': [{ 'values': [{
'priority': 10,
'value': 'mx1.unit.tests.',
'preference': 10,
'exchange': 'mx1.unit.tests.',
}, { }, {
'priority': 20,
'value': 'mx2.unit.tests.',
'preference': 20,
'exchange': 'mx2.unit.tests.',
}] }]
})) }))
expected.add(Record.new(zone, 'naptr', { expected.add(Record.new(zone, 'naptr', {


+ 6
- 6
tests/test_octodns_provider_route53.py View File

@ -52,11 +52,11 @@ class TestRoute53Provider(TestCase):
'Goodbye World?']}), 'Goodbye World?']}),
('', {'ttl': 64, 'type': 'MX', ('', {'ttl': 64, 'type': 'MX',
'values': [{ 'values': [{
'priority': 10,
'value': 'smtp-1.unit.tests.',
'preference': 10,
'exchange': 'smtp-1.unit.tests.',
}, { }, {
'priority': 20,
'value': 'smtp-2.unit.tests.',
'preference': 20,
'exchange': 'smtp-2.unit.tests.',
}]}), }]}),
('naptr', {'ttl': 65, 'type': 'NAPTR', ('naptr', {'ttl': 65, 'type': 'NAPTR',
'value': { 'value': {
@ -1262,8 +1262,8 @@ class TestRoute53Records(TestCase):
d = _Route53Record(None, Record.new(existing, '', d = _Route53Record(None, Record.new(existing, '',
{'ttl': 42, 'type': 'MX', {'ttl': 42, 'type': 'MX',
'value': { 'value': {
'priority': 10,
'value': 'foo.bar.'}}),
'preference': 10,
'exchange': 'foo.bar.'}}),
False) False)
self.assertEquals(d, d) self.assertEquals(d, d)


+ 37
- 21
tests/test_octodns_record.py View File

@ -212,45 +212,49 @@ class TestRecord(TestCase):
def test_mx(self): def test_mx(self):
a_values = [{ a_values = [{
'priority': 10,
'value': 'smtp1'
'preference': 10,
'exchange': 'smtp1.'
}, { }, {
'priority': 20, 'priority': 20,
'value': 'smtp2'
'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)
self.assertEquals('a', a.name) self.assertEquals('a', a.name)
self.assertEquals('a.unit.tests.', a.fqdn) self.assertEquals('a.unit.tests.', a.fqdn)
self.assertEquals(30, a.ttl) self.assertEquals(30, a.ttl)
self.assertEquals(a_values[0]['priority'], a.values[0].priority)
self.assertEquals(a_values[0]['value'], a.values[0].value)
self.assertEquals(a_values[1]['priority'], a.values[1].priority)
self.assertEquals(a_values[1]['value'], a.values[1].value)
self.assertEquals(a_values[0]['preference'], a.values[0].preference)
self.assertEquals(a_values[0]['exchange'], a.values[0].exchange)
self.assertEquals(a_values[1]['priority'], a.values[1].preference)
self.assertEquals(a_values[1]['value'], a.values[1].exchange)
a_data['values'][1] = {
'preference': 20,
'exchange': 'smtp2.',
}
self.assertEquals(a_data, a.data) self.assertEquals(a_data, a.data)
b_value = { b_value = {
'priority': 12,
'value': 'smtp3',
'preference': 12,
'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.assertEquals(b_value['priority'], b.values[0].priority)
self.assertEquals(b_value['value'], b.values[0].value)
self.assertEquals(b_value['preference'], b.values[0].preference)
self.assertEquals(b_value['exchange'], b.values[0].exchange)
self.assertEquals(b_data, b.data) self.assertEquals(b_data, b.data)
target = SimpleProvider() target = SimpleProvider()
# No changes with self # No changes with self
self.assertFalse(a.changes(a, target)) self.assertFalse(a.changes(a, target))
# Diff in priority causes change
# Diff in preference causes change
other = MxRecord(self.zone, 'a', {'ttl': 30, 'values': a_values}) other = MxRecord(self.zone, 'a', {'ttl': 30, 'values': a_values})
other.values[0].priority = 22
other.values[0].preference = 22
change = a.changes(other, target) change = a.changes(other, target)
self.assertEqual(change.existing, a) self.assertEqual(change.existing, a)
self.assertEqual(change.new, other) self.assertEqual(change.new, other)
# Diff in value causes change # Diff in value causes change
other.values[0].priority = a.values[0].priority
other.values[0].value = 'smtpX'
other.values[0].preference = a.values[0].preference
other.values[0].exchange = 'smtpX'
change = a.changes(other, target) change = a.changes(other, target)
self.assertEqual(change.existing, a) self.assertEqual(change.existing, a)
self.assertEqual(change.new, other) self.assertEqual(change.new, other)
@ -889,8 +893,8 @@ class TestRecordValidation(TestCase):
'type': 'MX', 'type': 'MX',
'ttl': 600, 'ttl': 600,
'value': { 'value': {
'priority': 10,
'value': 'foo.bar.com.'
'preference': 10,
'exchange': 'foo.bar.com.'
} }
}) })
@ -900,10 +904,10 @@ class TestRecordValidation(TestCase):
'type': 'MX', 'type': 'MX',
'ttl': 600, 'ttl': 600,
'value': { 'value': {
'value': 'foo.bar.com.'
'exchange': 'foo.bar.com.'
} }
}) })
self.assertEquals(['missing priority'], ctx.exception.reasons)
self.assertEquals(['missing preference'], ctx.exception.reasons)
# missing value # missing value
with self.assertRaises(ValidationError) as ctx: with self.assertRaises(ValidationError) as ctx:
@ -911,10 +915,22 @@ class TestRecordValidation(TestCase):
'type': 'MX', 'type': 'MX',
'ttl': 600, 'ttl': 600,
'value': { 'value': {
'priority': 10,
'preference': 10,
} }
}) })
self.assertEquals(['missing value'], ctx.exception.reasons)
self.assertEquals(['missing exchange'], ctx.exception.reasons)
# missing trailing .
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, '', {
'type': 'MX',
'ttl': 600,
'value': {
'preference': 10,
'exchange': 'foo.bar.com'
}
})
self.assertEquals(['missing trailing .'], ctx.exception.reasons)
def test_NXPTR(self): def test_NXPTR(self):
# doesn't blow up # doesn't blow up


+ 8
- 8
tests/test_octodns_source_tinydns.py View File

@ -68,22 +68,22 @@ class TestTinyDnsFileSource(TestCase):
'type': 'MX', 'type': 'MX',
'ttl': 3600, 'ttl': 3600,
'values': [{ 'values': [{
'priority': 10,
'value': 'smtp-1-host.example.com.',
'preference': 10,
'exchange': 'smtp-1-host.example.com.',
}, { }, {
'priority': 20,
'value': 'smtp-2-host.example.com.',
'preference': 20,
'exchange': 'smtp-2-host.example.com.',
}] }]
}), }),
('smtp', { ('smtp', {
'type': 'MX', 'type': 'MX',
'ttl': 1800, 'ttl': 1800,
'values': [{ 'values': [{
'priority': 30,
'value': 'smtp-1-host.example.com.',
'preference': 30,
'exchange': 'smtp-1-host.example.com.',
}, { }, {
'priority': 40,
'value': 'smtp-2-host.example.com.',
'preference': 40,
'exchange': 'smtp-2-host.example.com.',
}] }]
}), }),
): ):


Loading…
Cancel
Save