1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00

Merge branch 'main' into ensure-trailing-dots-fix

This commit is contained in:
Ross McFarland
2024-04-29 14:35:10 -07:00
committed by GitHub
3 changed files with 111 additions and 42 deletions

View File

@@ -15,12 +15,15 @@ Alternatively the value can be a dictionary with configuration options for the A
manager: manager:
auto_arpa: auto_arpa:
# Whether duplicate records should replace rather than error # Whether duplicate records should replace rather than error
# (optiona, default False) # (optional, default False)
populate_should_replace: false populate_should_replace: false
# Explicitly set the TTL of auto-created records, default is 3600s, 1hr # Explicitly set the TTL of auto-created records, default is 3600s, 1hr
ttl: 1800 ttl: 1800
# Set how many PTR records will be created for the same IP, default: 999 # Set how many PTR records will be created for the same IP, default: 999
max_auto_arpa: 1 max_auto_arpa: 1
# Inherit the TTL value of the corresponding A/AAAA record (optional,
# default False)
inherit_ttl: True
``` ```
Once enabled, a singleton `AutoArpa` instance, `auto-arpa`, will be added to the pool of providers and globally configured to run as the very last global processor so that it will see all records as they will be seen by targets. Further all zones ending with `arpa.` will be held back and processed after all other zones have been completed so that all `A` and `AAAA` records will have been seen prior to planning the `arpa.` zones. Once enabled, a singleton `AutoArpa` instance, `auto-arpa`, will be added to the pool of providers and globally configured to run as the very last global processor so that it will see all records as they will be seen by targets. Further all zones ending with `arpa.` will be held back and processed after all other zones have been completed so that all `A` and `AAAA` records will have been seen prior to planning the `arpa.` zones.

View File

@@ -12,19 +12,26 @@ from .base import BaseProcessor
class AutoArpa(BaseProcessor): class AutoArpa(BaseProcessor):
def __init__( def __init__(
self, name, ttl=3600, populate_should_replace=False, max_auto_arpa=999 self,
name,
ttl=3600,
populate_should_replace=False,
max_auto_arpa=999,
inherit_ttl=False,
): ):
super().__init__(name) super().__init__(name)
self.log = getLogger(f'AutoArpa[{name}]') self.log = getLogger(f'AutoArpa[{name}]')
self.log.info( self.log.info(
'__init__: ttl=%d, populate_should_replace=%s, max_auto_arpa=%d', '__init__: ttl=%d, populate_should_replace=%s, max_auto_arpa=%d, inherit_ttl=%s',
ttl, ttl,
populate_should_replace, populate_should_replace,
max_auto_arpa, max_auto_arpa,
inherit_ttl,
) )
self.ttl = ttl self.ttl = ttl
self.populate_should_replace = populate_should_replace self.populate_should_replace = populate_should_replace
self.max_auto_arpa = max_auto_arpa self.max_auto_arpa = max_auto_arpa
self.inherit_ttl = inherit_ttl
self._records = defaultdict(list) self._records = defaultdict(list)
def process_source_zone(self, desired, sources): def process_source_zone(self, desired, sources):
@@ -44,8 +51,12 @@ class AutoArpa(BaseProcessor):
auto_arpa_priority = record.octodns.get( auto_arpa_priority = record.octodns.get(
'auto_arpa_priority', 999 'auto_arpa_priority', 999
) )
if self.inherit_ttl:
record_ttl = record.ttl
else:
record_ttl = self.ttl
self._records[f'{ptr}.'].append( self._records[f'{ptr}.'].append(
(auto_arpa_priority, record.fqdn) (auto_arpa_priority, record_ttl, record.fqdn)
) )
return desired return desired
@@ -55,10 +66,10 @@ class AutoArpa(BaseProcessor):
# order the fqdns making a copy so we can reset the list below # order the fqdns making a copy so we can reset the list below
ordered = sorted(fqdns) ordered = sorted(fqdns)
fqdns = [] fqdns = []
for _, fqdn in ordered: for _, record_ttl, fqdn in ordered:
if fqdn in seen: if fqdn in seen:
continue continue
fqdns.append(fqdn) fqdns.append((record_ttl, fqdn))
seen.add(fqdn) seen.add(fqdn)
if len(seen) >= max_auto_arpa: if len(seen) >= max_auto_arpa:
break break
@@ -79,12 +90,16 @@ class AutoArpa(BaseProcessor):
for arpa, fqdns in self._records.items(): for arpa, fqdns in self._records.items():
if arpa.endswith(f'.{zone_name}'): if arpa.endswith(f'.{zone_name}'):
name = arpa[:-n] name = arpa[:-n]
# Note: this takes a list of (priority, fqdn) tuples and returns the ordered and uniqified list of fqdns. # Note: this takes a list of (priority, ttl, fqdn) tuples and returns the ordered and uniqified list of fqdns.
fqdns = self._order_and_unique_fqdns(fqdns, self.max_auto_arpa) fqdns = self._order_and_unique_fqdns(fqdns, self.max_auto_arpa)
record = Record.new( record = Record.new(
zone, zone,
name, name,
{'ttl': self.ttl, 'type': 'PTR', 'values': fqdns}, {
'ttl': fqdns[0][0],
'type': 'PTR',
'values': [fqdn[1] for fqdn in fqdns],
},
lenient=lenient, lenient=lenient,
) )
zone.add_record( zone.add_record(
@@ -92,7 +107,6 @@ class AutoArpa(BaseProcessor):
replace=self.populate_should_replace, replace=self.populate_should_replace,
lenient=lenient, lenient=lenient,
) )
self.log.info( self.log.info(
'populate: found %s records', len(zone.records) - before 'populate: found %s records', len(zone.records) - before
) )

View File

@@ -27,7 +27,8 @@ class TestAutoArpa(TestCase):
aa = AutoArpa('auto-arpa') aa = AutoArpa('auto-arpa')
aa.process_source_zone(zone, []) aa.process_source_zone(zone, [])
self.assertEqual( self.assertEqual(
{'4.3.2.1.in-addr.arpa.': [(999, 'a.unit.tests.')]}, aa._records {'4.3.2.1.in-addr.arpa.': [(999, 3600, 'a.unit.tests.')]},
aa._records,
) )
# matching zone # matching zone
@@ -56,8 +57,8 @@ class TestAutoArpa(TestCase):
aa.process_source_zone(zone, []) aa.process_source_zone(zone, [])
self.assertEqual( self.assertEqual(
{ {
'4.3.2.1.in-addr.arpa.': [(999, 'a.unit.tests.')], '4.3.2.1.in-addr.arpa.': [(999, 1600, 'a.unit.tests.')],
'5.3.2.1.in-addr.arpa.': [(999, 'a.unit.tests.')], '5.3.2.1.in-addr.arpa.': [(999, 1600, 'a.unit.tests.')],
}, },
aa._records, aa._records,
) )
@@ -81,7 +82,9 @@ class TestAutoArpa(TestCase):
aa = AutoArpa('auto-arpa') aa = AutoArpa('auto-arpa')
aa.process_source_zone(zone, []) aa.process_source_zone(zone, [])
ip6_arpa = '2.0.0.0.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.c.0.0.0.f.f.0.0.ip6.arpa.' ip6_arpa = '2.0.0.0.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.c.0.0.0.f.f.0.0.ip6.arpa.'
self.assertEqual({ip6_arpa: [(999, 'aaaa.unit.tests.')]}, aa._records) self.assertEqual(
{ip6_arpa: [(999, 3600, 'aaaa.unit.tests.')]}, aa._records
)
# matching zone # matching zone
arpa = Zone('c.0.0.0.f.f.0.0.ip6.arpa.', []) arpa = Zone('c.0.0.0.f.f.0.0.ip6.arpa.', [])
@@ -117,13 +120,13 @@ class TestAutoArpa(TestCase):
aa.process_source_zone(zone, []) aa.process_source_zone(zone, [])
self.assertEqual( self.assertEqual(
{ {
'4.3.2.1.in-addr.arpa.': [(999, 'geo.unit.tests.')], '4.3.2.1.in-addr.arpa.': [(999, 3600, 'geo.unit.tests.')],
'5.3.2.1.in-addr.arpa.': [(999, 'geo.unit.tests.')], '5.3.2.1.in-addr.arpa.': [(999, 3600, 'geo.unit.tests.')],
'1.1.1.1.in-addr.arpa.': [(999, 'geo.unit.tests.')], '1.1.1.1.in-addr.arpa.': [(999, 3600, 'geo.unit.tests.')],
'2.2.2.2.in-addr.arpa.': [(999, 'geo.unit.tests.')], '2.2.2.2.in-addr.arpa.': [(999, 3600, 'geo.unit.tests.')],
'3.3.3.3.in-addr.arpa.': [(999, 'geo.unit.tests.')], '3.3.3.3.in-addr.arpa.': [(999, 3600, 'geo.unit.tests.')],
'4.4.4.4.in-addr.arpa.': [(999, 'geo.unit.tests.')], '4.4.4.4.in-addr.arpa.': [(999, 3600, 'geo.unit.tests.')],
'5.5.5.5.in-addr.arpa.': [(999, 'geo.unit.tests.')], '5.5.5.5.in-addr.arpa.': [(999, 3600, 'geo.unit.tests.')],
}, },
aa._records, aa._records,
) )
@@ -176,7 +179,7 @@ class TestAutoArpa(TestCase):
unique_values = aa._order_and_unique_fqdns( unique_values = aa._order_and_unique_fqdns(
aa._records[f'{zone}'], 999 aa._records[f'{zone}'], 999
) )
self.assertEqual([('dynamic.unit.tests.')], unique_values) self.assertEqual([(3600, 'dynamic.unit.tests.')], unique_values)
def test_multiple_names(self): def test_multiple_names(self):
zone = Zone('unit.tests.', []) zone = Zone('unit.tests.', [])
@@ -194,8 +197,8 @@ class TestAutoArpa(TestCase):
self.assertEqual( self.assertEqual(
{ {
'4.3.2.1.in-addr.arpa.': [ '4.3.2.1.in-addr.arpa.': [
(999, 'a1.unit.tests.'), (999, 3600, 'a1.unit.tests.'),
(999, 'a2.unit.tests.'), (999, 3600, 'a2.unit.tests.'),
] ]
}, },
{'4.3.2.1.in-addr.arpa.': sorted_records}, {'4.3.2.1.in-addr.arpa.': sorted_records},
@@ -219,7 +222,8 @@ class TestAutoArpa(TestCase):
aa = AutoArpa('auto-arpa') aa = AutoArpa('auto-arpa')
aa.process_source_zone(zone, []) aa.process_source_zone(zone, [])
self.assertEqual( self.assertEqual(
{'4.3.20.10.in-addr.arpa.': [(999, 'a.unit.tests.')]}, aa._records {'4.3.20.10.in-addr.arpa.': [(999, 3600, 'a.unit.tests.')]},
aa._records,
) )
# matching zone # matching zone
@@ -259,7 +263,11 @@ class TestAutoArpa(TestCase):
aa = AutoArpa('auto-arpa') aa = AutoArpa('auto-arpa')
aa.process_source_zone(zone, []) aa.process_source_zone(zone, [])
self.assertEqual( self.assertEqual(
{'4.3.2.1.in-addr.arpa.': [(999, 'a with spaces.unit.tests.')]}, {
'4.3.2.1.in-addr.arpa.': [
(999, 3600, 'a with spaces.unit.tests.')
]
},
aa._records, aa._records,
) )
@@ -275,43 +283,87 @@ class TestAutoArpa(TestCase):
def test_arpa_priority(self): def test_arpa_priority(self):
aa = AutoArpa('auto-arpa') aa = AutoArpa('auto-arpa')
duplicate_values = [(999, 'a.unit.tests.'), (1, 'a.unit.tests.')] duplicate_values = [
(999, 3600, 'a.unit.tests.'),
(1, 3600, 'a.unit.tests.'),
]
self.assertEqual( self.assertEqual(
['a.unit.tests.'], [(3600, 'a.unit.tests.')],
aa._order_and_unique_fqdns(duplicate_values, max_auto_arpa=999), aa._order_and_unique_fqdns(duplicate_values, max_auto_arpa=999),
) )
duplicate_values = [ duplicate_values = [
(50, 'd.unit.tests.'), (50, 3600, 'd.unit.tests.'),
(999, 'dup.unit.tests.'), (999, 3600, 'dup.unit.tests.'),
(3, 'a.unit.tests.'), (3, 3600, 'a.unit.tests.'),
(1, 'dup.unit.tests.'), (1, 3600, 'dup.unit.tests.'),
(2, 'c.unit.tests.'), (2, 3600, 'c.unit.tests.'),
] ]
self.assertEqual( self.assertEqual(
[ [
'dup.unit.tests.', (3600, 'dup.unit.tests.'),
'c.unit.tests.', (3600, 'c.unit.tests.'),
'a.unit.tests.', (3600, 'a.unit.tests.'),
'd.unit.tests.', (3600, 'd.unit.tests.'),
], ],
aa._order_and_unique_fqdns(duplicate_values, max_auto_arpa=999), aa._order_and_unique_fqdns(duplicate_values, max_auto_arpa=999),
) )
duplicate_values_2 = [(999, 'a.unit.tests.'), (999, 'a.unit.tests.')] duplicate_values_2 = [
(999, 3600, 'a.unit.tests.'),
(999, 3600, 'a.unit.tests.'),
]
self.assertEqual( self.assertEqual(
['a.unit.tests.'], [(3600, 'a.unit.tests.')],
aa._order_and_unique_fqdns(duplicate_values_2, max_auto_arpa=999), aa._order_and_unique_fqdns(duplicate_values_2, max_auto_arpa=999),
) )
ordered_values = [(999, 'a.unit.tests.'), (1, 'b.unit.tests.')] ordered_values = [
(999, 3600, 'a.unit.tests.'),
(1, 3600, 'b.unit.tests.'),
]
self.assertEqual( self.assertEqual(
['b.unit.tests.', 'a.unit.tests.'], [(3600, 'b.unit.tests.'), (3600, 'a.unit.tests.')],
aa._order_and_unique_fqdns(ordered_values, max_auto_arpa=999), aa._order_and_unique_fqdns(ordered_values, max_auto_arpa=999),
) )
max_one_value = [(999, 'a.unit.tests.'), (1, 'b.unit.tests.')] max_one_value = [
(999, 3600, 'a.unit.tests.'),
(1, 3600, 'b.unit.tests.'),
]
self.assertEqual( self.assertEqual(
['b.unit.tests.'], [(3600, 'b.unit.tests.')],
aa._order_and_unique_fqdns(max_one_value, max_auto_arpa=1), aa._order_and_unique_fqdns(max_one_value, max_auto_arpa=1),
) )
def test_arpa_inherit_ttl(self):
zone = Zone('unit.tests.', [])
record = Record.new(
zone, 'a1', {'ttl': 32, 'type': 'A', 'value': '1.2.3.4'}
)
zone.add_record(record)
record2 = Record.new(
zone,
'a2',
{
'ttl': 64,
'type': 'A',
'value': '1.2.3.4',
'octodns': {'auto_arpa_priority': 1},
},
)
zone.add_record(record2)
record3 = Record.new(
zone, 'a3', {'ttl': 128, 'type': 'A', 'value': '1.2.3.4'}
)
zone.add_record(record3)
aa = AutoArpa('auto-arpa', 3600, False, 999, True)
aa.process_source_zone(zone, [])
arpa = Zone('3.2.1.in-addr.arpa.', [])
aa.populate(arpa)
(ptr,) = arpa.records
self.assertEqual(64, ptr.ttl)