|
|
@ -9,6 +9,7 @@ from collections import defaultdict |
|
|
from requests import Session |
|
|
from requests import Session |
|
|
from base64 import b64encode |
|
|
from base64 import b64encode |
|
|
from six import string_types |
|
|
from six import string_types |
|
|
|
|
|
from pycountry_convert import country_alpha2_to_continent_code |
|
|
import hashlib |
|
|
import hashlib |
|
|
import hmac |
|
|
import hmac |
|
|
import logging |
|
|
import logging |
|
|
@ -53,7 +54,8 @@ class ConstellixClient(object): |
|
|
self._sess = Session() |
|
|
self._sess = Session() |
|
|
self._sess.headers.update({'x-cnsdns-apiKey': self.api_key}) |
|
|
self._sess.headers.update({'x-cnsdns-apiKey': self.api_key}) |
|
|
self._domains = None |
|
|
self._domains = None |
|
|
self._pools = None |
|
|
|
|
|
|
|
|
self._pools = {'A': None, 'AAAA': None, 'CNAME': None} |
|
|
|
|
|
self._geofilters = None |
|
|
|
|
|
|
|
|
def _current_time(self): |
|
|
def _current_time(self): |
|
|
return str(int(time.time() * 1000)) |
|
|
return str(int(time.time() * 1000)) |
|
|
@ -100,19 +102,29 @@ class ConstellixClient(object): |
|
|
zone_id = self.domains.get(name, False) |
|
|
zone_id = self.domains.get(name, False) |
|
|
if not zone_id: |
|
|
if not zone_id: |
|
|
raise ConstellixClientNotFound() |
|
|
raise ConstellixClientNotFound() |
|
|
path = '/domains/{}'.format(zone_id) |
|
|
|
|
|
|
|
|
path = f'/domains/{zone_id}' |
|
|
return self._request('GET', path).json() |
|
|
return self._request('GET', path).json() |
|
|
|
|
|
|
|
|
def domain_create(self, name): |
|
|
def domain_create(self, name): |
|
|
resp = self._request('POST', '/domains', data={'names': [name]}) |
|
|
resp = self._request('POST', '/domains', data={'names': [name]}) |
|
|
# Add newly created zone to domain cache |
|
|
# Add newly created zone to domain cache |
|
|
self._domains['{}.'.format(name)] = resp.json()[0]['id'] |
|
|
|
|
|
|
|
|
self._domains[f'{name}.'] = resp.json()[0]['id'] |
|
|
|
|
|
|
|
|
|
|
|
def domain_enable_geoip(self, domain_name): |
|
|
|
|
|
domain = self.domain(domain_name) |
|
|
|
|
|
if domain['hasGeoIP'] is False: |
|
|
|
|
|
domain_id = self.domains[domain_name] |
|
|
|
|
|
self._request( |
|
|
|
|
|
'PUT', |
|
|
|
|
|
f'/domains/{domain_id}', |
|
|
|
|
|
data={'hasGeoIP': True} |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
def _absolutize_value(self, value, zone_name): |
|
|
def _absolutize_value(self, value, zone_name): |
|
|
if value == '': |
|
|
if value == '': |
|
|
value = zone_name |
|
|
value = zone_name |
|
|
elif not value.endswith('.'): |
|
|
elif not value.endswith('.'): |
|
|
value = '{}.{}'.format(value, zone_name) |
|
|
|
|
|
|
|
|
value = f'{value}.{zone_name}' |
|
|
|
|
|
|
|
|
return value |
|
|
return value |
|
|
|
|
|
|
|
|
@ -120,7 +132,7 @@ class ConstellixClient(object): |
|
|
zone_id = self.domains.get(zone_name, False) |
|
|
zone_id = self.domains.get(zone_name, False) |
|
|
if not zone_id: |
|
|
if not zone_id: |
|
|
raise ConstellixClientNotFound() |
|
|
raise ConstellixClientNotFound() |
|
|
path = '/domains/{}/records'.format(zone_id) |
|
|
|
|
|
|
|
|
path = f'/domains/{zone_id}/records' |
|
|
|
|
|
|
|
|
resp = self._request('GET', path).json() |
|
|
resp = self._request('GET', path).json() |
|
|
for record in resp: |
|
|
for record in resp: |
|
|
@ -147,7 +159,7 @@ class ConstellixClient(object): |
|
|
record_type = 'ANAME' |
|
|
record_type = 'ANAME' |
|
|
|
|
|
|
|
|
zone_id = self.domains.get(zone_name, False) |
|
|
zone_id = self.domains.get(zone_name, False) |
|
|
path = '/domains/{}/records/{}'.format(zone_id, record_type) |
|
|
|
|
|
|
|
|
path = f'/domains/{zone_id}/records/{record_type}' |
|
|
|
|
|
|
|
|
self._request('POST', path, data=params) |
|
|
self._request('POST', path, data=params) |
|
|
|
|
|
|
|
|
@ -157,18 +169,17 @@ class ConstellixClient(object): |
|
|
record_type = 'ANAME' |
|
|
record_type = 'ANAME' |
|
|
|
|
|
|
|
|
zone_id = self.domains.get(zone_name, False) |
|
|
zone_id = self.domains.get(zone_name, False) |
|
|
path = '/domains/{}/records/{}/{}'.format(zone_id, record_type, |
|
|
|
|
|
record_id) |
|
|
|
|
|
|
|
|
path = f'/domains/{zone_id}/records/{record_type}/{record_id}' |
|
|
self._request('DELETE', path) |
|
|
self._request('DELETE', path) |
|
|
|
|
|
|
|
|
def pools(self, pool_type): |
|
|
def pools(self, pool_type): |
|
|
if self._pools is None: |
|
|
|
|
|
self._pools = {} |
|
|
|
|
|
path = '/pools/{}'.format(pool_type) |
|
|
|
|
|
|
|
|
if self._pools[pool_type] is None: |
|
|
|
|
|
self._pools[pool_type] = {} |
|
|
|
|
|
path = f'/pools/{pool_type}' |
|
|
response = self._request('GET', path).json() |
|
|
response = self._request('GET', path).json() |
|
|
for pool in response: |
|
|
for pool in response: |
|
|
self._pools[pool['id']] = pool |
|
|
|
|
|
return self._pools.values() |
|
|
|
|
|
|
|
|
self._pools[pool_type][pool['id']] = pool |
|
|
|
|
|
return self._pools[pool_type].values() |
|
|
|
|
|
|
|
|
def pool(self, pool_type, pool_name): |
|
|
def pool(self, pool_type, pool_name): |
|
|
pools = self.pools(pool_type) |
|
|
pools = self.pools(pool_type) |
|
|
@ -186,11 +197,11 @@ class ConstellixClient(object): |
|
|
def pool_create(self, data): |
|
|
def pool_create(self, data): |
|
|
path = '/pools/{}'.format(data.get('type')) |
|
|
path = '/pools/{}'.format(data.get('type')) |
|
|
# This returns a list of items, we want the first one |
|
|
# This returns a list of items, we want the first one |
|
|
response = self._request('POST', path, data=data).json()[0] |
|
|
|
|
|
|
|
|
response = self._request('POST', path, data=data).json() |
|
|
|
|
|
|
|
|
# Invalidate our cache |
|
|
|
|
|
self._pools = None |
|
|
|
|
|
return response |
|
|
|
|
|
|
|
|
# Update our cache |
|
|
|
|
|
self._pools[data.get('type')][response[0]['id']] = response[0] |
|
|
|
|
|
return response[0] |
|
|
|
|
|
|
|
|
def pool_update(self, pool_id, data): |
|
|
def pool_update(self, pool_id, data): |
|
|
path = '/pools/{}/{}'.format(data.get('type'), pool_id) |
|
|
path = '/pools/{}/{}'.format(data.get('type'), pool_id) |
|
|
@ -203,6 +214,63 @@ class ConstellixClient(object): |
|
|
raise e |
|
|
raise e |
|
|
return data |
|
|
return data |
|
|
|
|
|
|
|
|
|
|
|
def pool_delete(self, pool_type, pool_id): |
|
|
|
|
|
path = f'/pools/{pool_type}/{pool_id}' |
|
|
|
|
|
self._request('DELETE', path) |
|
|
|
|
|
|
|
|
|
|
|
# Update our cache |
|
|
|
|
|
if self._pools[pool_type] is not None: |
|
|
|
|
|
self._pools[pool_type].pop(pool_id, None) |
|
|
|
|
|
|
|
|
|
|
|
def geofilters(self): |
|
|
|
|
|
if self._geofilters is None: |
|
|
|
|
|
self._geofilters = {} |
|
|
|
|
|
path = '/geoFilters' |
|
|
|
|
|
response = self._request('GET', path).json() |
|
|
|
|
|
for geofilter in response: |
|
|
|
|
|
self._geofilters[geofilter['id']] = geofilter |
|
|
|
|
|
return self._geofilters.values() |
|
|
|
|
|
|
|
|
|
|
|
def geofilter(self, geofilter_name): |
|
|
|
|
|
geofilters = self.geofilters() |
|
|
|
|
|
for geofilter in geofilters: |
|
|
|
|
|
if geofilter['name'] == geofilter_name: |
|
|
|
|
|
return geofilter |
|
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
def geofilter_by_id(self, geofilter_id): |
|
|
|
|
|
geofilters = self.geofilters() |
|
|
|
|
|
for geofilter in geofilters: |
|
|
|
|
|
if geofilter['id'] == geofilter_id: |
|
|
|
|
|
return geofilter |
|
|
|
|
|
|
|
|
|
|
|
def geofilter_create(self, data): |
|
|
|
|
|
path = '/geoFilters' |
|
|
|
|
|
response = self._request('POST', path, data=data).json() |
|
|
|
|
|
|
|
|
|
|
|
# Update our cache |
|
|
|
|
|
self._geofilters[response[0]['id']] = response[0] |
|
|
|
|
|
return response[0] |
|
|
|
|
|
|
|
|
|
|
|
def geofilter_update(self, geofilter_id, data): |
|
|
|
|
|
path = f'/geoFilters/{geofilter_id}' |
|
|
|
|
|
try: |
|
|
|
|
|
self._request('PUT', path, data=data).json() |
|
|
|
|
|
|
|
|
|
|
|
except ConstellixClientBadRequest as e: |
|
|
|
|
|
message = str(e) |
|
|
|
|
|
if not message or "no changes to save" not in message: |
|
|
|
|
|
raise e |
|
|
|
|
|
return data |
|
|
|
|
|
|
|
|
|
|
|
def geofilter_delete(self, geofilter_id): |
|
|
|
|
|
path = f'/geoFilters/{geofilter_id}' |
|
|
|
|
|
self._request('DELETE', path) |
|
|
|
|
|
|
|
|
|
|
|
# Update our cache |
|
|
|
|
|
if self._geofilters is not None: |
|
|
|
|
|
self._geofilters.pop(geofilter_id, None) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConstellixProvider(BaseProvider): |
|
|
class ConstellixProvider(BaseProvider): |
|
|
''' |
|
|
''' |
|
|
@ -225,7 +293,7 @@ class ConstellixProvider(BaseProvider): |
|
|
|
|
|
|
|
|
def __init__(self, id, api_key, secret_key, ratelimit_delay=0.0, |
|
|
def __init__(self, id, api_key, secret_key, ratelimit_delay=0.0, |
|
|
*args, **kwargs): |
|
|
*args, **kwargs): |
|
|
self.log = logging.getLogger('ConstellixProvider[{}]'.format(id)) |
|
|
|
|
|
|
|
|
self.log = logging.getLogger(f'ConstellixProvider[{id}]') |
|
|
self.log.debug('__init__: id=%s, api_key=***, secret_key=***', id) |
|
|
self.log.debug('__init__: id=%s, api_key=***, secret_key=***', id) |
|
|
super(ConstellixProvider, self).__init__(id, *args, **kwargs) |
|
|
super(ConstellixProvider, self).__init__(id, *args, **kwargs) |
|
|
self._client = ConstellixClient(api_key, secret_key, ratelimit_delay) |
|
|
self._client = ConstellixClient(api_key, secret_key, ratelimit_delay) |
|
|
@ -234,39 +302,95 @@ class ConstellixProvider(BaseProvider): |
|
|
def _data_for_multiple(self, _type, records): |
|
|
def _data_for_multiple(self, _type, records): |
|
|
record = records[0] |
|
|
record = records[0] |
|
|
if record['recordOption'] == 'pools': |
|
|
if record['recordOption'] == 'pools': |
|
|
return self._data_for_pool(_type, record) |
|
|
|
|
|
|
|
|
return self._data_for_pool(_type, records) |
|
|
return { |
|
|
return { |
|
|
'ttl': record['ttl'], |
|
|
'ttl': record['ttl'], |
|
|
'type': _type, |
|
|
'type': _type, |
|
|
'values': record['value'] |
|
|
'values': record['value'] |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
def _data_for_pool(self, _type, record): |
|
|
|
|
|
pool_id = record['pools'][0] |
|
|
|
|
|
pool = self._client.pool_by_id(_type, pool_id) |
|
|
|
|
|
pool_name = pool['name'].split(':')[-1] |
|
|
|
|
|
|
|
|
def _data_for_pool(self, _type, records): |
|
|
|
|
|
default_values = [] |
|
|
|
|
|
fallback_pool_name = None |
|
|
pools = {} |
|
|
pools = {} |
|
|
values = [] |
|
|
|
|
|
pools[pool_name] = { |
|
|
|
|
|
'values': [] |
|
|
|
|
|
} |
|
|
|
|
|
for value in pool['values']: |
|
|
|
|
|
pools[pool_name]['values'].append({ |
|
|
|
|
|
'value': value['value'], |
|
|
|
|
|
'weight': value['weight'] |
|
|
|
|
|
}) |
|
|
|
|
|
values.append(value['value']) |
|
|
|
|
|
return { |
|
|
|
|
|
|
|
|
rules = [] |
|
|
|
|
|
|
|
|
|
|
|
for record in records: |
|
|
|
|
|
# fetch record pool data |
|
|
|
|
|
pool_id = record['pools'][0] |
|
|
|
|
|
pool = self._client.pool_by_id(_type, pool_id) |
|
|
|
|
|
|
|
|
|
|
|
geofilter_id = 1 |
|
|
|
|
|
if 'geolocation' in record.keys() \ |
|
|
|
|
|
and record['geolocation'] is not None: |
|
|
|
|
|
# fetch record geofilter data |
|
|
|
|
|
geofilter_id = record['geolocation']['geoipFilter'] |
|
|
|
|
|
geofilter = self._client.geofilter_by_id(geofilter_id) |
|
|
|
|
|
|
|
|
|
|
|
pool_name = pool['name'].split(':')[-1] |
|
|
|
|
|
|
|
|
|
|
|
# fetch default values from the World Default pool |
|
|
|
|
|
if geofilter_id == 1: |
|
|
|
|
|
fallback_pool_name = pool_name |
|
|
|
|
|
for value in pool['values']: |
|
|
|
|
|
default_values.append(value['value']) |
|
|
|
|
|
|
|
|
|
|
|
# populate pools |
|
|
|
|
|
pools[pool_name] = { |
|
|
|
|
|
'fallback': None, |
|
|
|
|
|
'values': [] |
|
|
|
|
|
} |
|
|
|
|
|
for value in pool['values']: |
|
|
|
|
|
pools[pool_name]['values'].append({ |
|
|
|
|
|
'value': value['value'], |
|
|
|
|
|
'weight': value['weight'] |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
# populate rules |
|
|
|
|
|
if geofilter_id == 1: |
|
|
|
|
|
rules.append({'pool': pool_name}) |
|
|
|
|
|
else: |
|
|
|
|
|
geos = [] |
|
|
|
|
|
|
|
|
|
|
|
if 'geoipContinents' in geofilter.keys(): |
|
|
|
|
|
for continent_code in geofilter['geoipContinents']: |
|
|
|
|
|
geos.append(continent_code) |
|
|
|
|
|
|
|
|
|
|
|
if 'geoipCountries' in geofilter.keys(): |
|
|
|
|
|
for country_code in geofilter['geoipCountries']: |
|
|
|
|
|
geos.append('{}-{}'.format( |
|
|
|
|
|
country_alpha2_to_continent_code(country_code), |
|
|
|
|
|
country_code |
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
|
|
|
if 'regions' in geofilter.keys(): |
|
|
|
|
|
for region in geofilter['regions']: |
|
|
|
|
|
geos.append('{}-{}-{}'.format( |
|
|
|
|
|
region['continentCode'], |
|
|
|
|
|
region['countryCode'], |
|
|
|
|
|
region['regionCode'])) |
|
|
|
|
|
|
|
|
|
|
|
rules.append({ |
|
|
|
|
|
'pool': pool_name, |
|
|
|
|
|
'geos': sorted(geos) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
# set fallback pool |
|
|
|
|
|
for pool_name in pools: |
|
|
|
|
|
if pool_name != fallback_pool_name: |
|
|
|
|
|
pools[pool_name]['fallback'] = fallback_pool_name |
|
|
|
|
|
|
|
|
|
|
|
res = { |
|
|
'ttl': record['ttl'], |
|
|
'ttl': record['ttl'], |
|
|
'type': _type, |
|
|
'type': _type, |
|
|
'dynamic': { |
|
|
'dynamic': { |
|
|
'pools': pools, |
|
|
|
|
|
'rules': [{ |
|
|
|
|
|
'pool': pool_name |
|
|
|
|
|
}] |
|
|
|
|
|
|
|
|
'pools': dict( |
|
|
|
|
|
sorted(pools.items(), key=lambda t: t[0])), |
|
|
|
|
|
'rules': sorted(rules, key=lambda t: t['pool']) |
|
|
}, |
|
|
}, |
|
|
'value': values |
|
|
|
|
|
|
|
|
'values': default_values |
|
|
} |
|
|
} |
|
|
|
|
|
return res |
|
|
|
|
|
|
|
|
_data_for_A = _data_for_multiple |
|
|
_data_for_A = _data_for_multiple |
|
|
_data_for_AAAA = _data_for_multiple |
|
|
_data_for_AAAA = _data_for_multiple |
|
|
@ -381,7 +505,7 @@ class ConstellixProvider(BaseProvider): |
|
|
before = len(zone.records) |
|
|
before = len(zone.records) |
|
|
for name, types in values.items(): |
|
|
for name, types in values.items(): |
|
|
for _type, records in types.items(): |
|
|
for _type, records in types.items(): |
|
|
data_for = getattr(self, '_data_for_{}'.format(_type)) |
|
|
|
|
|
|
|
|
data_for = getattr(self, f'_data_for_{_type}') |
|
|
record = Record.new(zone, name, data_for(_type, records), |
|
|
record = Record.new(zone, name, data_for(_type, records), |
|
|
source=self, lenient=lenient) |
|
|
source=self, lenient=lenient) |
|
|
zone.add_record(record, lenient=lenient) |
|
|
zone.add_record(record, lenient=lenient) |
|
|
@ -491,34 +615,70 @@ class ConstellixProvider(BaseProvider): |
|
|
def _handle_pools(self, record): |
|
|
def _handle_pools(self, record): |
|
|
# If we don't have dynamic, then there's no pools |
|
|
# If we don't have dynamic, then there's no pools |
|
|
if not getattr(record, 'dynamic', False): |
|
|
if not getattr(record, 'dynamic', False): |
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
# Get our first entry in the rules that references a pool |
|
|
|
|
|
rules = list(filter( |
|
|
|
|
|
lambda rule: 'pool' in rule.data, |
|
|
|
|
|
record.dynamic.rules |
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
|
|
|
pool_name = rules[0].data.get('pool') |
|
|
|
|
|
|
|
|
|
|
|
pool = record.dynamic.pools.get(pool_name) |
|
|
|
|
|
values = pool.data.get('values') |
|
|
|
|
|
|
|
|
|
|
|
# Make a pool name based on zone, record, type and name |
|
|
|
|
|
pool_name = '{}:{}:{}:{}'.format( |
|
|
|
|
|
record.zone.name, |
|
|
|
|
|
record.name, |
|
|
|
|
|
record._type, |
|
|
|
|
|
pool_name |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
# OK, pool is valid, let's create it or update it |
|
|
|
|
|
return self._create_update_pool( |
|
|
|
|
|
pool_name = pool_name, |
|
|
|
|
|
pool_type = record._type, |
|
|
|
|
|
ttl = record.ttl, |
|
|
|
|
|
values = values |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
|
|
res_pools = [] |
|
|
|
|
|
|
|
|
|
|
|
for i, rule in enumerate(record.dynamic.rules): |
|
|
|
|
|
pool_name = rule.data.get('pool') |
|
|
|
|
|
pool = record.dynamic.pools.get(pool_name) |
|
|
|
|
|
values = pool.data.get('values') |
|
|
|
|
|
|
|
|
|
|
|
# Make a pool name based on zone, record, type and name |
|
|
|
|
|
generated_pool_name = '{}:{}:{}:{}'.format( |
|
|
|
|
|
record.zone.name, |
|
|
|
|
|
record.name, |
|
|
|
|
|
record._type, |
|
|
|
|
|
pool_name |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
# OK, pool is valid, let's create it or update it |
|
|
|
|
|
self.log.debug("Creating pool %s", generated_pool_name) |
|
|
|
|
|
pool_obj = self._create_update_pool( |
|
|
|
|
|
pool_name = generated_pool_name, |
|
|
|
|
|
pool_type = record._type, |
|
|
|
|
|
ttl = record.ttl, |
|
|
|
|
|
values = values |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
# Now will crate GeoFilter for the pool |
|
|
|
|
|
continents = [] |
|
|
|
|
|
countries = [] |
|
|
|
|
|
regions = [] |
|
|
|
|
|
|
|
|
|
|
|
for geo in rule.data.get('geos', []): |
|
|
|
|
|
codes = geo.split('-') |
|
|
|
|
|
n = len(geo) |
|
|
|
|
|
if n == 2: |
|
|
|
|
|
continents.append(geo) |
|
|
|
|
|
elif n == 5: |
|
|
|
|
|
countries.append(codes[1]) |
|
|
|
|
|
else: |
|
|
|
|
|
regions.append({ |
|
|
|
|
|
'continentCode': codes[0], |
|
|
|
|
|
'countryCode': codes[1], |
|
|
|
|
|
'regionCode': codes[2] |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
if len(continents) == 0 and \ |
|
|
|
|
|
len(countries) == 0 and \ |
|
|
|
|
|
len(regions) == 0: |
|
|
|
|
|
pool_obj['geofilter'] = 1 |
|
|
|
|
|
else: |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Creating geofilter %s", |
|
|
|
|
|
generated_pool_name |
|
|
|
|
|
) |
|
|
|
|
|
geofilter_obj = self._create_update_geofilter( |
|
|
|
|
|
generated_pool_name, |
|
|
|
|
|
continents, |
|
|
|
|
|
countries, |
|
|
|
|
|
regions |
|
|
|
|
|
) |
|
|
|
|
|
pool_obj['geofilter'] = geofilter_obj['id'] |
|
|
|
|
|
|
|
|
|
|
|
res_pools.append(pool_obj) |
|
|
|
|
|
return res_pools |
|
|
|
|
|
|
|
|
def _create_update_pool(self, pool_name, pool_type, ttl, values): |
|
|
def _create_update_pool(self, pool_name, pool_type, ttl, values): |
|
|
pool = { |
|
|
pool = { |
|
|
@ -538,29 +698,173 @@ class ConstellixProvider(BaseProvider): |
|
|
updated_pool['id'] = pool_id |
|
|
updated_pool['id'] = pool_id |
|
|
return updated_pool |
|
|
return updated_pool |
|
|
|
|
|
|
|
|
def _apply_Create(self, change): |
|
|
|
|
|
|
|
|
def _create_update_geofilter( |
|
|
|
|
|
self, |
|
|
|
|
|
geofilter_name, |
|
|
|
|
|
continents, |
|
|
|
|
|
countries, |
|
|
|
|
|
regions): |
|
|
|
|
|
geofilter = { |
|
|
|
|
|
'filterRulesLimit': 100, |
|
|
|
|
|
'name': geofilter_name, |
|
|
|
|
|
'geoipContinents': continents, |
|
|
|
|
|
'geoipCountries': countries, |
|
|
|
|
|
'regions': regions |
|
|
|
|
|
} |
|
|
|
|
|
if len(regions) == 0: |
|
|
|
|
|
geofilter.pop('regions', None) |
|
|
|
|
|
|
|
|
|
|
|
existing_geofilter = self._client.geofilter(geofilter_name) |
|
|
|
|
|
if not existing_geofilter: |
|
|
|
|
|
return self._client.geofilter_create(geofilter) |
|
|
|
|
|
|
|
|
|
|
|
geofilter_id = existing_geofilter['id'] |
|
|
|
|
|
updated_geofilter = self._client.geofilter_update( |
|
|
|
|
|
geofilter_id, geofilter) |
|
|
|
|
|
updated_geofilter['id'] = geofilter_id |
|
|
|
|
|
return updated_geofilter |
|
|
|
|
|
|
|
|
|
|
|
def _apply_Create(self, change, domain_name): |
|
|
new = change.new |
|
|
new = change.new |
|
|
params_for = getattr(self, '_params_for_{}'.format(new._type)) |
|
|
params_for = getattr(self, '_params_for_{}'.format(new._type)) |
|
|
pool = self._handle_pools(new) |
|
|
|
|
|
|
|
|
pools = self._handle_pools(new) |
|
|
|
|
|
|
|
|
for params in params_for(new): |
|
|
for params in params_for(new): |
|
|
if pool: |
|
|
|
|
|
params['pools'] = [pool['id']] |
|
|
|
|
|
|
|
|
if len(pools) == 0: |
|
|
|
|
|
self._client.record_create(new.zone.name, new._type, params) |
|
|
|
|
|
elif len(pools) == 1: |
|
|
|
|
|
params['pools'] = [pools[0]['id']] |
|
|
params['recordOption'] = 'pools' |
|
|
params['recordOption'] = 'pools' |
|
|
self._client.record_create(new.zone.name, new._type, params) |
|
|
|
|
|
|
|
|
|
|
|
def _apply_Update(self, change): |
|
|
|
|
|
self._apply_Delete(change) |
|
|
|
|
|
self._apply_Create(change) |
|
|
|
|
|
|
|
|
|
|
|
def _apply_Delete(self, change): |
|
|
|
|
|
|
|
|
params.pop('roundRobin', None) |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Creating record %s %s", |
|
|
|
|
|
new.zone.name, |
|
|
|
|
|
new._type |
|
|
|
|
|
) |
|
|
|
|
|
self._client.record_create( |
|
|
|
|
|
new.zone.name, |
|
|
|
|
|
new._type, |
|
|
|
|
|
params |
|
|
|
|
|
) |
|
|
|
|
|
else: |
|
|
|
|
|
# To use GeoIPFilter feature we need to enable it for domain |
|
|
|
|
|
self.log.debug("Enabling domain %s geo support", domain_name) |
|
|
|
|
|
self._client.domain_enable_geoip(domain_name) |
|
|
|
|
|
|
|
|
|
|
|
# First we need to create World Default (1) Record |
|
|
|
|
|
for pool in pools: |
|
|
|
|
|
if pool['geofilter'] != 1: |
|
|
|
|
|
continue |
|
|
|
|
|
params['pools'] = [pool['id']] |
|
|
|
|
|
params['recordOption'] = 'pools' |
|
|
|
|
|
params['geolocation'] = { |
|
|
|
|
|
'geoipUserRegion': [pool['geofilter']] |
|
|
|
|
|
} |
|
|
|
|
|
params.pop('roundRobin', None) |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Creating record %s %s", |
|
|
|
|
|
new.zone.name, |
|
|
|
|
|
new._type) |
|
|
|
|
|
self._client.record_create( |
|
|
|
|
|
new.zone.name, |
|
|
|
|
|
new._type, |
|
|
|
|
|
params |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
# Now we can create the rest of records |
|
|
|
|
|
for pool in pools: |
|
|
|
|
|
if pool['geofilter'] == 1: |
|
|
|
|
|
continue |
|
|
|
|
|
params['pools'] = [pool['id']] |
|
|
|
|
|
params['recordOption'] = 'pools' |
|
|
|
|
|
params['geolocation'] = { |
|
|
|
|
|
'geoipUserRegion': [pool['geofilter']] |
|
|
|
|
|
} |
|
|
|
|
|
params.pop('roundRobin', None) |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Creating record %s %s", |
|
|
|
|
|
new.zone.name, |
|
|
|
|
|
new._type) |
|
|
|
|
|
self._client.record_create( |
|
|
|
|
|
new.zone.name, |
|
|
|
|
|
new._type, |
|
|
|
|
|
params) |
|
|
|
|
|
|
|
|
|
|
|
def _apply_Update(self, change, domain_name): |
|
|
|
|
|
self._apply_Delete(change, domain_name) |
|
|
|
|
|
self._apply_Create(change, domain_name) |
|
|
|
|
|
|
|
|
|
|
|
def _apply_Delete(self, change, domain_name): |
|
|
existing = change.existing |
|
|
existing = change.existing |
|
|
zone = existing.zone |
|
|
zone = existing.zone |
|
|
|
|
|
|
|
|
|
|
|
# if it is dynamic pools record, we need to delete World Default last |
|
|
|
|
|
world_default_record = None |
|
|
|
|
|
|
|
|
for record in self.zone_records(zone): |
|
|
for record in self.zone_records(zone): |
|
|
if existing.name == record['name'] and \ |
|
|
if existing.name == record['name'] and \ |
|
|
existing._type == record['type']: |
|
|
existing._type == record['type']: |
|
|
self._client.record_delete(zone.name, record['type'], |
|
|
|
|
|
record['id']) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# handle dynamic record |
|
|
|
|
|
if record['recordOption'] == 'pools': |
|
|
|
|
|
if record['geolocation'] is None: |
|
|
|
|
|
world_default_record = record |
|
|
|
|
|
else: |
|
|
|
|
|
if record['geolocation']['geoipFilter'] == 1: |
|
|
|
|
|
world_default_record = record |
|
|
|
|
|
else: |
|
|
|
|
|
# delete record |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Deleting record %s %s", |
|
|
|
|
|
zone.name, |
|
|
|
|
|
record['type']) |
|
|
|
|
|
self._client.record_delete( |
|
|
|
|
|
zone.name, |
|
|
|
|
|
record['type'], |
|
|
|
|
|
record['id']) |
|
|
|
|
|
# delete geofilter |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Deleting geofilter %s", |
|
|
|
|
|
zone.name) |
|
|
|
|
|
self._client.geofilter_delete( |
|
|
|
|
|
record['geolocation']['geoipFilter']) |
|
|
|
|
|
|
|
|
|
|
|
# delete pool |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Deleting pool %s %s", |
|
|
|
|
|
zone.name, |
|
|
|
|
|
record['type']) |
|
|
|
|
|
self._client.pool_delete( |
|
|
|
|
|
record['type'], |
|
|
|
|
|
record['pools'][0]) |
|
|
|
|
|
|
|
|
|
|
|
# for all the rest records |
|
|
|
|
|
else: |
|
|
|
|
|
self._client.record_delete( |
|
|
|
|
|
zone.name, record['type'], record['id']) |
|
|
|
|
|
# delete World Default |
|
|
|
|
|
if world_default_record: |
|
|
|
|
|
# delete record |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Deleting record %s %s", |
|
|
|
|
|
zone.name, |
|
|
|
|
|
world_default_record['type'] |
|
|
|
|
|
) |
|
|
|
|
|
self._client.record_delete( |
|
|
|
|
|
zone.name, |
|
|
|
|
|
world_default_record['type'], |
|
|
|
|
|
world_default_record['id'] |
|
|
|
|
|
) |
|
|
|
|
|
# delete pool |
|
|
|
|
|
self.log.debug( |
|
|
|
|
|
"Deleting pool %s %s", |
|
|
|
|
|
zone.name, |
|
|
|
|
|
world_default_record['type'] |
|
|
|
|
|
) |
|
|
|
|
|
self._client.pool_delete( |
|
|
|
|
|
world_default_record['type'], |
|
|
|
|
|
world_default_record['pools'][0] |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
def _apply(self, plan): |
|
|
def _apply(self, plan): |
|
|
desired = plan.desired |
|
|
desired = plan.desired |
|
|
@ -576,7 +880,9 @@ class ConstellixProvider(BaseProvider): |
|
|
|
|
|
|
|
|
for change in changes: |
|
|
for change in changes: |
|
|
class_name = change.__class__.__name__ |
|
|
class_name = change.__class__.__name__ |
|
|
getattr(self, '_apply_{}'.format(class_name))(change) |
|
|
|
|
|
|
|
|
getattr(self, f'_apply_{class_name}')( |
|
|
|
|
|
change, |
|
|
|
|
|
desired.name) |
|
|
|
|
|
|
|
|
# Clear out the cache if any |
|
|
# Clear out the cache if any |
|
|
self._zone_records.pop(desired.name, None) |
|
|
self._zone_records.pop(desired.name, None) |