diff --git a/octodns/provider/dyn.py b/octodns/provider/dyn.py index 6167364..f7b8aad 100644 --- a/octodns/provider/dyn.py +++ b/octodns/provider/dyn.py @@ -466,7 +466,7 @@ class DynProvider(BaseProvider): values = [] data = { 'dynamic': { - 'pool': pools, + 'pools': pools, 'rules': rules, }, 'type': _type, @@ -523,11 +523,13 @@ class DynProvider(BaseProvider): # Geo geo = ruleset.criteria['geoip'] geos = [] - # TODO: we need to reconstitude geos here :-/ + # Dyn uses the same 2-letter codes as octoDNS (except for + # continents) but it doesn't have the hierary, e.g. US is + # just US, not NA-US. We'll have to map these things back for code in geo['country']: - geos.append(code) + geos.append(GeoCodes.country_to_code(code)) for code in geo['province']: - geos.append(code) + geos.append(GeoCodes.province_to_code(code.upper())) for code in geo['region']: geos.append(self.REGION_CODES_LOOKUP[int(code)]) rule['geos'] = geos @@ -543,8 +545,6 @@ class DynProvider(BaseProvider): pprint(data) - raise Exception('boom') - name = zone.hostname_from_fqdn(fqdn) record = Record.new(zone, name, data, source=self, lenient=lenient) zone.add_record(record, lenient=lenient) @@ -1012,6 +1012,10 @@ class DynProvider(BaseProvider): def _mod_dynamic_rulesets(self, td, change): new = change.new + # TODO: make sure we can update TTLs + if td.ttl != new.ttl: + td.ttl = new.ttl + # Get existing pools. This should be simple, but it's not b/c the dyn # api is a POS. We need all response pools so we can GC and check to # make sure that what we're after doesn't already exist. diff --git a/octodns/record/__init__.py b/octodns/record/__init__.py index 6480784..4818912 100644 --- a/octodns/record/__init__.py +++ b/octodns/record/__init__.py @@ -12,6 +12,9 @@ import re from .geo import GeoCodes +from pprint import pprint + + class Change(object): def __init__(self, existing, new): @@ -384,7 +387,24 @@ class _DynamicPool(object): def __init__(self, _id, data): self._id = _id - self.data = data + + pprint(['before', data]) + + values = [ + { + 'value': d['value'], + 'weight': d.get('weight', 1), + } for d in data['values'] + ] + values.sort(key=lambda d: d['value']) + + fallback = data.get('fallback', None) + self.data = { + 'fallback': fallback if fallback != 'default' else None, + 'values': values, + } + + pprint(['after', self.data]) def _data(self): return self.data @@ -403,12 +423,30 @@ class _DynamicRule(object): def __init__(self, i, data): self.i = i - self.data = data + + pprint(['before', data]) + + self.data = {} + try: + self.data['pool'] = data['pool'] + except KeyError: + pass + try: + self.data['geos'] = sorted(data['geos']) + except KeyError: + pass + + pprint(['after', self.data]) def _data(self): return self.data def __eq__(self, other): + pprint([ + self.data, + other.data, + self.data == other.data, + ]) return self.data == other.data def __ne__(self, other): diff --git a/octodns/record/geo.py b/octodns/record/geo.py index 1979fd5..ed54194 100644 --- a/octodns/record/geo.py +++ b/octodns/record/geo.py @@ -2,11 +2,13 @@ # # +from logging import getLogger + from .geo_data import geo_data class GeoCodes(object): - __COUNTRIES = None + log = getLogger('GeoCodes') @classmethod def validate(cls, code, prefix): @@ -50,3 +52,20 @@ class GeoCodes(object): 'country_code': country_code, 'province_code': province_code, } + + @classmethod + def country_to_code(cls, country): + for continent, countries in geo_data.items(): + if country in countries: + return '{}-{}'.format(continent, country) + cls.log.warn('country_to_code: unrecognized country "%s"', country) + return + + @classmethod + def province_to_code(cls, province): + # We get to cheat on this one since we only support provinces in NA-US + if province not in geo_data['NA']['US']['provinces']: + cls.log.warn('country_to_code: unrecognized province "%s"', + province) + return + return 'NA-US-{}'.format(province) diff --git a/tests/test_octodns_record.py b/tests/test_octodns_record.py index 6be334d..55e3be3 100644 --- a/tests/test_octodns_record.py +++ b/tests/test_octodns_record.py @@ -1906,10 +1906,11 @@ class TestDynamicRecords(TestCase): }], }, 'two': { + # Testing out of order value sorting here 'values': [{ - 'value': '4.4.4.4', - }, { 'value': '5.5.5.5', + }, { + 'value': '4.4.4.4', }], }, 'three': { @@ -1948,10 +1949,24 @@ class TestDynamicRecords(TestCase): pools = dynamic.pools self.assertTrue(pools) - self.assertEquals(a_data['dynamic']['pools']['one'], pools['one'].data) - self.assertEquals(a_data['dynamic']['pools']['two'], pools['two'].data) - self.assertEquals(a_data['dynamic']['pools']['three'], - pools['three'].data) + self.assertEquals({ + 'value': '3.3.3.3', + 'weight': 1, + }, pools['one'].data['values'][0]) + self.assertEquals([{ + 'value': '4.4.4.4', + 'weight': 1, + }, { + 'value': '5.5.5.5', + 'weight': 1, + }], pools['two'].data['values']) + self.assertEquals([{ + 'weight': 10, + 'value': '4.4.4.4', + }, { + 'weight': 12, + 'value': '5.5.5.5', + }], pools['three'].data['values']) rules = dynamic.rules self.assertTrue(rules) @@ -1994,10 +2009,11 @@ class TestDynamicRecords(TestCase): }], }, 'two': { + # Testing out of order value sorting here 'values': [{ - 'value': '2601:642:500:e210:62f8:1dff:feb8:9474', - }, { 'value': '2601:642:500:e210:62f8:1dff:feb8:9475', + }, { + 'value': '2601:642:500:e210:62f8:1dff:feb8:9474', }], }, 'three': { @@ -2036,12 +2052,24 @@ class TestDynamicRecords(TestCase): pools = dynamic.pools self.assertTrue(pools) - self.assertEquals(aaaa_data['dynamic']['pools']['one'], - pools['one'].data) - self.assertEquals(aaaa_data['dynamic']['pools']['two'], - pools['two'].data) - self.assertEquals(aaaa_data['dynamic']['pools']['three'], - pools['three'].data) + self.assertEquals({ + 'value': '2601:642:500:e210:62f8:1dff:feb8:9473', + 'weight': 1, + }, pools['one'].data['values'][0]) + self.assertEquals([{ + 'value': '2601:642:500:e210:62f8:1dff:feb8:9474', + 'weight': 1, + }, { + 'value': '2601:642:500:e210:62f8:1dff:feb8:9475', + 'weight': 1, + }], pools['two'].data['values']) + self.assertEquals([{ + 'weight': 10, + 'value': '2601:642:500:e210:62f8:1dff:feb8:9476', + }, { + 'weight': 12, + 'value': '2601:642:500:e210:62f8:1dff:feb8:9477', + }], pools['three'].data['values']) rules = dynamic.rules self.assertTrue(rules) @@ -2094,12 +2122,21 @@ class TestDynamicRecords(TestCase): pools = dynamic.pools self.assertTrue(pools) - self.assertEquals(cname_data['dynamic']['pools']['one'], - pools['one'].data) - self.assertEquals(cname_data['dynamic']['pools']['two'], - pools['two'].data) - self.assertEquals(cname_data['dynamic']['pools']['three'], - pools['three'].data) + self.assertEquals({ + 'value': 'one.cname.target.', + 'weight': 1, + }, pools['one'].data['values'][0]) + self.assertEquals({ + 'value': 'two.cname.target.', + 'weight': 1, + }, pools['two'].data['values'][0]) + self.assertEquals([{ + 'value': 'three-1.cname.target.', + 'weight': 12, + }, { + 'value': 'three-2.cname.target.', + 'weight': 32, + }], pools['three'].data['values']) rules = dynamic.rules self.assertTrue(rules) @@ -2906,9 +2943,10 @@ class TestDynamicRecords(TestCase): a_data = { 'dynamic': { 'rules': [{ - 'pools': { - 1: 'one', - } + 'geos': ['EU'], + 'pool': 'two', + }, { + 'pool': 'one', }], }, 'ttl': 60, @@ -2928,7 +2966,19 @@ class TestDynamicRecords(TestCase): a_data = { 'dynamic': { 'pools': { - 'one': '1.1.1.1', + 'one': { + 'values': [{ + 'value': '3.3.3.3', + }] + }, + 'two': { + 'values': [{ + 'value': '4.4.4.4', + }, { + 'value': '5.5.5.5', + 'weight': 2, + }] + }, }, }, 'ttl': 60, @@ -2941,11 +2991,82 @@ class TestDynamicRecords(TestCase): a = Record.new(self.zone, 'bad', a_data, lenient=True) self.assertEquals({ 'pools': { - 'one': '1.1.1.1', + 'one': { + 'fallback': None, + 'values': [{ + 'value': '3.3.3.3', + 'weight': 1, + }] + }, + 'two': { + 'fallback': None, + 'values': [{ + 'value': '4.4.4.4', + 'weight': 1, + }, { + 'value': '5.5.5.5', + 'weight': 2, + }] + }, }, 'rules': [], }, a._data()['dynamic']) + # rule without pool + a_data = { + 'dynamic': { + 'pools': { + 'one': { + 'values': [{ + 'value': '3.3.3.3', + }] + }, + 'two': { + 'values': [{ + 'value': '4.4.4.4', + }, { + 'value': '5.5.5.5', + 'weight': 2, + }] + }, + }, + 'rules': [{ + 'geos': ['EU'], + 'pool': 'two', + }, { + }], + }, + 'ttl': 60, + 'type': 'A', + 'values': [ + '1.1.1.1', + '2.2.2.2', + ], + } + a = Record.new(self.zone, 'bad', a_data, lenient=True) + self.assertEquals({ + 'pools': { + 'one': { + 'fallback': None, + 'values': [{ + 'value': '3.3.3.3', + 'weight': 1, + }] + }, + 'two': { + 'fallback': None, + 'values': [{ + 'value': '4.4.4.4', + 'weight': 1, + }, { + 'value': '5.5.5.5', + 'weight': 2, + }] + }, + }, + 'rules': a_data['dynamic']['rules'], + }, a._data()['dynamic']) + def test_dynamic_changes(self): simple = SimpleProvider() dynamic = DynamicProvider() @@ -2953,17 +3074,24 @@ class TestDynamicRecords(TestCase): a_data = { 'dynamic': { 'pools': { - 'one': '3.3.3.3', - 'two': [ - '4.4.4.4', - '5.5.5.5', - ], + 'one': { + 'values': [{ + 'value': '3.3.3.3', + }] + }, + 'two': { + 'values': [{ + 'value': '4.4.4.4', + }, { + 'value': '5.5.5.5', + }] + }, }, 'rules': [{ - 'pools': { - 100: 'one', - 200: 'two', - } + 'geos': ['EU'], + 'pool': 'two', + }, { + 'pool': 'one', }], }, 'ttl': 60, @@ -2978,17 +3106,25 @@ class TestDynamicRecords(TestCase): b_data = { 'dynamic': { 'pools': { - 'one': '3.3.3.5', - 'two': [ - '4.4.4.4', - '5.5.5.5', - ], + 'one': { + 'values': [{ + 'value': '3.3.3.3', + }] + }, + 'two': { + 'values': [{ + 'value': '4.4.4.4', + 'weight': 2, + }, { + 'value': '5.5.5.5', + }] + }, }, 'rules': [{ - 'pools': { - 100: 'one', - 200: 'two', - } + 'geos': ['EU'], + 'pool': 'two', + }, { + 'pool': 'one', }], }, 'ttl': 60, @@ -3002,17 +3138,24 @@ class TestDynamicRecords(TestCase): c_data = { 'dynamic': { 'pools': { - 'one': '3.3.3.3', - 'two': [ - '4.4.4.4', - '5.5.5.5', - ], + 'one': { + 'values': [{ + 'value': '3.3.3.3', + }] + }, + 'two': { + 'values': [{ + 'value': '4.4.4.4', + }, { + 'value': '5.5.5.5', + }] + }, }, 'rules': [{ - 'pools': { - 100: 'one', - 300: 'two', - } + 'geos': ['NA'], + 'pool': 'two', + }, { + 'pool': 'one', }], }, 'ttl': 60, diff --git a/tests/test_octodns_record_geo.py b/tests/test_octodns_record_geo.py index d58009d..5b7454c 100644 --- a/tests/test_octodns_record_geo.py +++ b/tests/test_octodns_record_geo.py @@ -68,3 +68,13 @@ class TestRecordGeoCodes(TestCase): 'country_code': 'US', 'province_code': 'CA', }, GeoCodes.parse('NA-US-CA')) + + def test_country_to_code(self): + self.assertEquals('NA-US', GeoCodes.country_to_code('US')) + self.assertEquals('EU-GB', GeoCodes.country_to_code('GB')) + self.assertFalse(GeoCodes.country_to_code('XX')) + + def test_province_to_code(self): + self.assertEquals('NA-US-OR', GeoCodes.province_to_code('OR')) + self.assertEquals('NA-US-KY', GeoCodes.province_to_code('KY')) + self.assertFalse(GeoCodes.province_to_code('XX'))