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

Full support for A/AAAA dynamic records in Azure

This commit is contained in:
Viranch Mehta
2021-07-04 04:55:18 -07:00
parent c5f023dca8
commit 2d21125645
2 changed files with 267 additions and 326 deletions

View File

@@ -301,38 +301,10 @@ def _get_monitor(record):
def _check_valid_dynamic(record):
typ = record._type
dynamic = record.dynamic
if typ in ['A', 'AAAA']:
# A/AAAA records cannot be aliased to Traffic Managers that contain
# other nested Traffic Managers. Due to this limitation, A/AAAA
# dynamic records can do only one of geo-fencing, fallback and
# weighted RR. So let's validate that the record adheres to this
# limitation.
data = dynamic._data()
values = set(record.values)
pools = data['pools'].values()
seen_values = set()
rr = False
fallback = False
for pool in pools:
vals = pool['values']
if len(vals) > 1:
rr = True
pool_values = set(val['value'] for val in vals)
if pool.get('fallback'):
fallback = True
seen_values.update(pool_values)
if values != seen_values:
msg = ('{} {}: All pool values of A/AAAA dynamic records must be '
'included in top-level \'values\'.')
raise AzureException(msg.format(record.fqdn, record._type))
geo = any(r.get('geos') for r in data['rules'])
if [rr, fallback, geo].count(True) > 1:
msg = ('{} {}: A/AAAA dynamic records must use at most one of '
'round-robin, fallback and geo-fencing')
if len(record.values) > 1:
# we don't yet support multi-value defaults
msg = '{} {}: A/AAAA dynamic records can only have a single value'
raise AzureException(msg.format(record.fqdn, record._type))
elif typ != 'CNAME':
# dynamic records of unsupported type
@@ -911,14 +883,6 @@ class AzureProvider(BaseProvider):
active = set()
profiles = self._generate_traffic_managers(record)
# this should not happen with above check, check again here to
# prevent undesired changes
if record._type in ['A', 'AAAA'] and len(profiles) > 1:
msg = ('Unknown error: {} {} needs more than 1 Traffic '
'Managers which is not supported for A/AAAA dynamic '
'records').format(record.fqdn, record._type)
raise AzureException(msg)
for profile in profiles:
name = profile.name
@@ -1170,8 +1134,7 @@ class AzureProvider(BaseProvider):
return traffic_managers
def _sync_traffic_managers(self, record):
desired_profiles = self._generate_traffic_managers(record)
def _sync_traffic_managers(self, desired_profiles):
seen = set()
tm_sync = self._tm_client.profiles.create_or_update
@@ -1230,12 +1193,22 @@ class AzureProvider(BaseProvider):
record = change.new
dynamic = getattr(record, 'dynamic', False)
root_profile = None
endpoints = []
if dynamic:
self._sync_traffic_managers(record)
profiles = self._generate_traffic_managers(record)
root_profile = profiles[-1]
if record._type in ['A', 'AAAA'] and len(profiles) > 1:
# A/AAAA records cannot be aliased to Traffic Managers that
# contain other nested Traffic Managers. To work around this
# limitation, we remove nesting before adding the record, and
# then add the nested endpoints later.
endpoints = root_profile.endpoints
root_profile.endpoints = []
self._sync_traffic_managers(profiles)
profile = self._get_tm_for_dynamic_record(record)
ar = _AzureRecord(self._resource_group, record,
traffic_manager=profile)
traffic_manager=root_profile)
create = self._dns_client.record_sets.create_or_update
create(resource_group_name=ar.resource_group,
@@ -1244,6 +1217,12 @@ class AzureProvider(BaseProvider):
record_type=ar.record_type,
parameters=ar.params)
if endpoints:
# add nested endpoints for A/AAAA dynamic record limitation after
# record creation
root_profile.endpoints = endpoints
self._sync_traffic_managers([root_profile])
self.log.debug('* Success Create: {}'.format(record))
def _apply_Update(self, change):
@@ -1256,19 +1235,35 @@ class AzureProvider(BaseProvider):
'''
existing = change.existing
new = change.new
typ = new._type
existing_is_dynamic = getattr(existing, 'dynamic', False)
new_is_dynamic = getattr(new, 'dynamic', False)
update_record = True
if new_is_dynamic:
active = self._sync_traffic_managers(new)
# only TTL is configured in record, everything else goes inside
# traffic managers, so no need to update if TTL is unchanged
# and existing record is already aliased to its traffic manager
if existing.ttl == new.ttl and existing_is_dynamic:
endpoints = []
profiles = self._generate_traffic_managers(new)
root_profile = profiles[-1]
if typ in ['A', 'AAAA']:
if existing_is_dynamic:
# update to the record is not needed
update_record = False
elif len(profiles) > 1:
# record needs to aliased; remove nested endpoints, we
# will add them at the end
endpoints = root_profile.endpoints
root_profile.endpoints = []
elif existing.ttl == new.ttl and existing_is_dynamic:
# CNAME dynamic records only have TTL in them, everything else
# goes inside the aliased traffic managers; skip update if TTL
# is unchanged and existing record is already aliased to its
# traffic manager
update_record = False
active = self._sync_traffic_managers(profiles)
if update_record:
profile = self._get_tm_for_dynamic_record(new)
ar = _AzureRecord(self._resource_group, new,
@@ -1282,6 +1277,10 @@ class AzureProvider(BaseProvider):
parameters=ar.params)
if new_is_dynamic:
# add any pending nested endpoints
if endpoints:
root_profile.endpoints = endpoints
self._sync_traffic_managers([root_profile])
# let's cleanup unused traffic managers
self._traffic_managers_gc(new, active)
elif existing_is_dynamic: