mirror of
https://github.com/github/octodns.git
synced 2024-05-11 05:55:00 +00:00
Rough draft/expirimentation on dynamic creation
This commit is contained in:
@@ -70,13 +70,54 @@ class Ns1Provider(BaseProvider):
|
||||
class: octodns.provider.ns1.Ns1Provider
|
||||
api_key: env/NS1_API_KEY
|
||||
'''
|
||||
SUPPORTS_GEO = True
|
||||
SUPPORTS_DYNAMIC = False
|
||||
SUPPORTS_GEO = False
|
||||
SUPPORTS_DYNAMIC = True
|
||||
SUPPORTS = set(('A', 'AAAA', 'ALIAS', 'CAA', 'CNAME', 'MX', 'NAPTR',
|
||||
'NS', 'PTR', 'SPF', 'SRV', 'TXT'))
|
||||
|
||||
ZONE_NOT_FOUND_MESSAGE = 'server error: zone not found'
|
||||
|
||||
_DYNAMIC_FILTERS = [{
|
||||
'config': {},
|
||||
'filter': 'up'
|
||||
}, {
|
||||
'config': {},
|
||||
'filter': u'geotarget_regional'
|
||||
}, {
|
||||
'config': {},
|
||||
'filter': u'select_first_region'
|
||||
}, {
|
||||
'config': {
|
||||
'eliminate': u'1'
|
||||
},
|
||||
'filter': 'priority'
|
||||
}, {
|
||||
'config': {},
|
||||
'filter': u'weighted_shuffle'
|
||||
}, {
|
||||
'config': {
|
||||
'N': u'1'
|
||||
},
|
||||
'filter': u'select_first_n'
|
||||
}]
|
||||
_REGION_TO_CONTINENT = {
|
||||
'AFRICA': 'AF',
|
||||
'ASIAPAC': 'AS',
|
||||
'EUROPE': 'EU',
|
||||
'SOUTH-AMERICA': 'SA',
|
||||
'US-CENTRAL': 'NA',
|
||||
'US-EAST': 'NA',
|
||||
'US-WEST': 'NA',
|
||||
}
|
||||
_CONTINENT_TO_REGIONS = {
|
||||
'AF': ('AFRICA',),
|
||||
'AS': ('ASIAPAC',),
|
||||
'EU': ('EUROPE',),
|
||||
'SA': ('SOUTH-AMERICA',),
|
||||
# TODO: what about CA, MX, and all the other NA countries?
|
||||
'NA': ('US-CENTRAL', 'US-EAST', 'US-WEST'),
|
||||
}
|
||||
|
||||
def __init__(self, id, api_key, retry_count=4, *args, **kwargs):
|
||||
self.log = getLogger('Ns1Provider[{}]'.format(id))
|
||||
self.log.debug('__init__: id=%s, api_key=***, retry_count=%d', id,
|
||||
@@ -282,9 +323,124 @@ class Ns1Provider(BaseProvider):
|
||||
len(zone.records) - before, exists)
|
||||
return exists
|
||||
|
||||
def _encode_notes(self, data):
|
||||
return ' '.join(['{}:{}'.format(k, v)
|
||||
for k, v in sorted(data.items())])
|
||||
|
||||
def _params_for_A(self, record):
|
||||
params = {'answers': record.values, 'ttl': record.ttl}
|
||||
if hasattr(record, 'geo'):
|
||||
params = {'ttl': record.ttl}
|
||||
|
||||
if hasattr(record, 'dynamic'):
|
||||
|
||||
pools = record.dynamic.pools
|
||||
|
||||
# Convert rules to regions
|
||||
regions = {}
|
||||
for i, rule in enumerate(record.dynamic.rules):
|
||||
pool_name = rule.data['pool']
|
||||
|
||||
notes = {
|
||||
'rule-order': i,
|
||||
}
|
||||
|
||||
fallback = pools[pool_name].data.get('fallback', None)
|
||||
if fallback:
|
||||
notes['fallback'] = fallback
|
||||
|
||||
country = set()
|
||||
georegion = set()
|
||||
us_state = set()
|
||||
|
||||
for geo in rule.data.get('geos', []):
|
||||
n = len(geo)
|
||||
if n == 8:
|
||||
# US state
|
||||
us_state.add(geo[-2:])
|
||||
elif n == 5:
|
||||
# Country
|
||||
country.add(geo[-2:])
|
||||
else:
|
||||
# Continent
|
||||
georegion.update(self._CONTINENT_TO_REGIONS[geo])
|
||||
|
||||
meta = {
|
||||
'note': self._encode_notes(notes),
|
||||
}
|
||||
if georegion:
|
||||
meta['georegion'] = sorted(georegion)
|
||||
if country:
|
||||
meta['country'] = sorted(country)
|
||||
if us_state:
|
||||
meta['us_state'] = sorted(us_state)
|
||||
|
||||
regions[pool_name] = {
|
||||
'meta': meta,
|
||||
}
|
||||
|
||||
# Build a list of primary values for each pool
|
||||
pool_answers = defaultdict(list)
|
||||
for pool_name, pool in sorted(pools.items()):
|
||||
for value in pool.data['values']:
|
||||
pool_answers[pool_name].append({
|
||||
'answer': [value['value']],
|
||||
'weight': value['weight'],
|
||||
})
|
||||
|
||||
default_answers = [{
|
||||
'answer': [v],
|
||||
'weight': 1,
|
||||
} for v in record.values]
|
||||
|
||||
# Build our list of answers
|
||||
answers = []
|
||||
for pool_name in sorted(pools.keys()):
|
||||
priority = 1
|
||||
|
||||
# Dynamic/health checked
|
||||
current_pool_name = pool_name
|
||||
while current_pool_name:
|
||||
pool = pools[current_pool_name]
|
||||
for answer in pool_answers[current_pool_name]:
|
||||
answer = {
|
||||
'answer': answer['answer'],
|
||||
'meta': {
|
||||
'priority': priority,
|
||||
'note': self._encode_notes({
|
||||
'from': current_pool_name,
|
||||
}),
|
||||
'weight': answer['weight'],
|
||||
},
|
||||
'region': pool_name, # the one we're answering
|
||||
}
|
||||
answers.append(answer)
|
||||
|
||||
current_pool_name = pool.data.get('fallback', None)
|
||||
priority += 1
|
||||
|
||||
# Static/default
|
||||
for answer in default_answers:
|
||||
answer = {
|
||||
'answer': answer['answer'],
|
||||
'meta': {
|
||||
'priority': priority,
|
||||
'note': self._encode_notes({
|
||||
'from': '--default--',
|
||||
}),
|
||||
'weight': 1,
|
||||
},
|
||||
'region': pool_name, # the one we're answering
|
||||
}
|
||||
answers.append(answer)
|
||||
|
||||
params.update({
|
||||
'answers': answers,
|
||||
'filters': self._DYNAMIC_FILTERS,
|
||||
'regions': regions,
|
||||
})
|
||||
|
||||
return params
|
||||
|
||||
elif hasattr(record, 'geo'):
|
||||
# purposefully set non-geo answers to have an empty meta,
|
||||
# so that we know we did this on purpose if/when troubleshooting
|
||||
params['answers'] = [{"answer": [x], "meta": {}}
|
||||
@@ -315,6 +471,9 @@ class Ns1Provider(BaseProvider):
|
||||
{"filter": "select_first_n",
|
||||
"config": {"N": 1}}
|
||||
)
|
||||
else:
|
||||
params['answers'] = record.values
|
||||
|
||||
self.log.debug("params for A: %s", params)
|
||||
return params
|
||||
|
||||
|
Reference in New Issue
Block a user