diff --git a/README.md b/README.md index 6127234..c2983fd 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ The above command pulled the existing data out of Route53 and placed the results | Provider | Requirements | Record Support | Dynamic | Notes | |--|--|--|--|--| | [AzureProvider](/octodns/provider/azuredns.py) | azure-mgmt-dns | A, AAAA, CAA, CNAME, MX, NS, PTR, SRV, TXT | No | | -| [Akamai](/octodns/provider/fastdns.py) | edgegrid-python | A, AAAA, CNAME, MX, NAPTR, NS, PTR, SPF, SRV, SSHFP, TXT | No | | +| [Akamai](/octodns/provider/edgedns.py) | edgegrid-python | A, AAAA, CNAME, MX, NAPTR, NS, PTR, SPF, SRV, SSHFP, TXT | No | | | [CloudflareProvider](/octodns/provider/cloudflare.py) | | A, AAAA, ALIAS, CAA, CNAME, MX, NS, SPF, SRV, TXT | No | CAA tags restricted | | [ConstellixProvider](/octodns/provider/constellix.py) | | A, AAAA, ALIAS (ANAME), CAA, CNAME, MX, NS, PTR, SPF, SRV, TXT | No | CAA tags restricted | | [DigitalOceanProvider](/octodns/provider/digitalocean.py) | | A, AAAA, CAA, CNAME, MX, NS, TXT, SRV | No | CAA tags restricted | diff --git a/octodns/provider/edgedns.py b/octodns/provider/edgedns.py new file mode 100644 index 0000000..d575bd1 --- /dev/null +++ b/octodns/provider/edgedns.py @@ -0,0 +1,525 @@ +# +# +# + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +from requests import Session +from akamai.edgegrid import EdgeGridAuth +from six.moves.urllib.parse import urljoin +from collections import defaultdict + +from logging import getLogger +from ..record import Record +from .base import BaseProvider + + +class AkamaiClientNotFound(Exception): + + def __init__(self, resp): + message = "404: Resource not found" + super(AkamaiClientNotFound, self).__init__(message) + + +class AkamaiClient(object): + ''' + Client for making calls to Akamai Fast DNS API using Python Requests + + Fast DNS Zone Management API V2, found here: + developer.akamai.com/api/web_performance/fast_dns_zone_management/v2.html + + Info on Python Requests library: + https://2.python-requests.org/en/master/ + + ''' + + def __init__(self, client_secret, host, access_token, client_token): + + self.base = "https://" + host + "/config-dns/v2/" + + sess = Session() + sess.auth = EdgeGridAuth( + client_token=client_token, + client_secret=client_secret, + access_token=access_token + ) + self._sess = sess + + def _request(self, method, path, params=None, data=None, v1=False): + + url = urljoin(self.base, path) + resp = self._sess.request(method, url, params=params, json=data) + + if resp.status_code == 404: + raise AkamaiClientNotFound(resp) + resp.raise_for_status() + + return resp + + def record_create(self, zone, name, record_type, content): + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) + result = self._request('POST', path, data=content) + + return result + + def record_delete(self, zone, name, record_type): + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) + result = self._request('DELETE', path) + + return result + + def record_replace(self, zone, name, record_type, content): + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) + result = self._request('PUT', path, data=content) + + return result + + def zone_get(self, zone): + path = 'zones/{}'.format(zone) + result = self._request('GET', path) + + return result + + def zone_create(self, contractId, params, gid=None): + path = 'zones?contractId={}'.format(contractId) + + if gid is not None: + path += '&gid={}'.format(gid) + + result = self._request('POST', path, data=params) + + return result + + def zone_recordset_get(self, zone, page=None, pageSize=None, search=None, + showAll="true", sortBy="name", types=None): + + params = { + 'page': page, + 'pageSize': pageSize, + 'search': search, + 'showAll': showAll, + 'sortBy': sortBy, + 'types': types + } + + path = 'zones/{}/recordsets'.format(zone) + result = self._request('GET', path, params=params) + + return result + + +class AkamaiProvider(BaseProvider): + + ''' + Akamai Fast DNS Provider + + edgedns.py: + + Example config file with variables: + " + --- + providers: + config: + class: octodns.provider.yaml.YamlProvider + directory: ./config (example path to directory of zone files) + edgedns: + class: octodns.provider.edgedns.AkamaiProvider + client_secret: env/AKAMAI_CLIENT_SECRET + host: env/AKAMAI_HOST + access_token: env/AKAMAI_ACCESS_TOKEN + client_token: env/AKAMAI_CLIENT_TOKEN + contract_id: env/AKAMAI_CONTRACT_ID (optional) + + zones: + example.com.: + sources: + - config + targets: + - edgedns + " + + The first four variables above can be hidden in environment variables + and octoDNS will automatically search for them in the shell. It is + possible to also hard-code into the config file: eg, contract_id. + + The first four values can be found by generating credentials: + https://control.akamai.com/ + Configure > Organization > Manage APIs > New API Client for me + Select appropriate group, and fill relevant fields. + For API Service Name, select DNS-Zone Record Management + and then set appropriate Access level (Read-Write to make changes). + Then select the "New Credential" button to generate values for above + + The contract_id paramater is optional, and only required for creating + a new zone. If the zone being managed already exists in Akamai for the + user in question, then this paramater is not needed. + + ''' + + SUPPORTS_GEO = False + SUPPORTS_DYNAMIC = False + + SUPPORTS = set(('A', 'AAAA', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', + 'SRV', 'SSHFP', 'TXT')) + + def __init__(self, id, client_secret, host, access_token, client_token, + contract_id=None, gid=None, *args, **kwargs): + + self.log = getLogger('AkamaiProvider[{}]'.format(id)) + self.log.debug('__init__: id=%s, ') + super(AkamaiProvider, self).__init__(id, *args, **kwargs) + + self._dns_client = AkamaiClient(client_secret, host, access_token, + client_token) + + self._zone_records = {} + self._contractId = contract_id + self._gid = gid + + def zone_records(self, zone): + """ returns records for a zone, looks for it if not present, or + returns empty [] if can't find a match + """ + if zone.name not in self._zone_records: + try: + name = zone.name[:-1] + response = self._dns_client.zone_recordset_get(name) + self._zone_records[zone.name] = response.json()["recordsets"] + + except (AkamaiClientNotFound, KeyError): + return [] + + return self._zone_records[zone.name] + + def populate(self, zone, target=False, lenient=False): + self.log.debug('populate: name=%s', zone.name) + + values = defaultdict(lambda: defaultdict(list)) + for record in self.zone_records(zone): + + _type = record.get('type') + # Akamai sends down prefix.zonename., while octodns expects prefix + _name = record.get('name').split("." + zone.name[:-1], 1)[0] + if _name == zone.name[:-1]: + _name = '' # root / @ + + if _type not in self.SUPPORTS: + continue + values[_name][_type].append(record) + + before = len(zone.records) + for name, types in values.items(): + for _type, records in types.items(): + data_for = getattr(self, '_data_for_{}'.format(_type)) + record = Record.new(zone, name, data_for(_type, records[0]), + source=self, lenient=lenient) + zone.add_record(record, lenient=lenient) + + exists = zone.name in self._zone_records + found = len(zone.records) - before + self.log.info('populate: found %s records, exists=%s', found, exists) + + return exists + + def _apply(self, plan): + desired = plan.desired + changes = plan.changes + self.log.debug('apply: zone=%s, chnges=%d', desired.name, len(changes)) + + zone_name = desired.name[:-1] + try: + self._dns_client.zone_get(zone_name) + + except AkamaiClientNotFound: + self.log.info("zone not found, creating zone") + params = self._build_zone_config(zone_name) + self._dns_client.zone_create(self._contractId, params, self._gid) + + for change in changes: + class_name = change.__class__.__name__ + getattr(self, '_apply_{}'.format(class_name))(change) + + # Clear out the cache if any + self._zone_records.pop(desired.name, None) + + def _apply_Create(self, change): + + new = change.new + record_type = new._type + + params_for = getattr(self, '_params_for_{}'.format(record_type)) + values = self._get_values(new.data) + rdata = params_for(values) + + zone = new.zone.name[:-1] + name = self._set_full_name(new.name, zone) + + content = { + "name": name, + "type": record_type, + "ttl": new.ttl, + "rdata": rdata + } + + self._dns_client.record_create(zone, name, record_type, content) + + return + + def _apply_Delete(self, change): + + zone = change.existing.zone.name[:-1] + name = self._set_full_name(change.existing.name, zone) + record_type = change.existing._type + + self._dns_client.record_delete(zone, name, record_type) + + return + + def _apply_Update(self, change): + + new = change.new + record_type = new._type + + params_for = getattr(self, '_params_for_{}'.format(record_type)) + values = self._get_values(new.data) + rdata = params_for(values) + + zone = new.zone.name[:-1] + name = self._set_full_name(new.name, zone) + + content = { + "name": name, + "type": record_type, + "ttl": new.ttl, + "rdata": rdata + } + + self._dns_client.record_replace(zone, name, record_type, content) + + return + + def _data_for_multiple(self, _type, records): + + return { + 'ttl': records['ttl'], + 'type': _type, + 'values': [r for r in records['rdata']] + } + + _data_for_A = _data_for_multiple + _data_for_AAAA = _data_for_multiple + _data_for_NS = _data_for_multiple + _data_for_SPF = _data_for_multiple + + def _data_for_CNAME(self, _type, records): + value = records['rdata'][0] + if (value[-1] != '.'): + value = '{}.'.format(value) + + return { + 'ttl': records['ttl'], + 'type': _type, + 'value': value + } + + def _data_for_MX(self, _type, records): + values = [] + for r in records['rdata']: + preference, exchange = r.split(" ", 1) + values.append({ + 'preference': preference, + 'exchange': exchange + }) + return { + 'ttl': records['ttl'], + 'type': _type, + 'values': values + } + + def _data_for_NAPTR(self, _type, records): + values = [] + for r in records['rdata']: + order, preference, flags, service, regexp, repl = r.split(' ', 5) + + values.append({ + 'flags': flags[1:-1], + 'order': order, + 'preference': preference, + 'regexp': regexp[1:-1], + 'replacement': repl, + 'service': service[1:-1] + }) + return { + 'type': _type, + 'ttl': records['ttl'], + 'values': values + } + + def _data_for_PTR(self, _type, records): + + return { + 'ttl': records['ttl'], + 'type': _type, + 'value': records['rdata'][0] + } + + def _data_for_SRV(self, _type, records): + values = [] + for r in records['rdata']: + priority, weight, port, target = r.split(' ', 3) + values.append({ + 'port': port, + 'priority': priority, + 'target': target, + 'weight': weight + }) + + return { + 'type': _type, + 'ttl': records['ttl'], + 'values': values + } + + def _data_for_SSHFP(self, _type, records): + values = [] + for r in records['rdata']: + algorithm, fp_type, fingerprint = r.split(' ', 2) + values.append({ + 'algorithm': algorithm, + 'fingerprint': fingerprint.lower(), + 'fingerprint_type': fp_type + }) + + return { + 'type': _type, + 'ttl': records['ttl'], + 'values': values + } + + def _data_for_TXT(self, _type, records): + values = [] + for r in records['rdata']: + r = r[1:-1] + values.append(r.replace(';', '\\;')) + + return { + 'ttl': records['ttl'], + 'type': _type, + 'values': values + } + + def _params_for_multiple(self, values): + return [r for r in values] + + def _params_for_single(self, values): + return values + + _params_for_A = _params_for_multiple + _params_for_AAAA = _params_for_multiple + _params_for_NS = _params_for_multiple + + _params_for_CNAME = _params_for_single + _params_for_PTR = _params_for_single + + def _params_for_MX(self, values): + rdata = [] + + for r in values: + preference = r['preference'] + exchange = r['exchange'] + + record = '{} {}'.format(preference, exchange) + rdata.append(record) + + return rdata + + def _params_for_NAPTR(self, values): + rdata = [] + + for r in values: + ordr = r['order'] + prf = r['preference'] + flg = "\"" + r['flags'] + "\"" + srvc = "\"" + r['service'] + "\"" + rgx = "\"" + r['regexp'] + "\"" + rpl = r['replacement'] + + record = '{} {} {} {} {} {}'.format(ordr, prf, flg, srvc, rgx, rpl) + rdata.append(record) + + return rdata + + def _params_for_SPF(self, values): + rdata = [] + + for r in values: + txt = "\"" + r.replace('\\;', ';') + "\"" + rdata.append(txt) + + return rdata + + def _params_for_SRV(self, values): + rdata = [] + for r in values: + priority = r['priority'] + weight = r['weight'] + port = r['port'] + target = r['target'] + + record = '{} {} {} {}'.format(priority, weight, port, target) + rdata.append(record) + + return rdata + + def _params_for_SSHFP(self, values): + rdata = [] + for r in values: + algorithm = r['algorithm'] + fp_type = r['fingerprint_type'] + fp = r['fingerprint'] + + record = '{} {} {}'.format(algorithm, fp_type, fp) + rdata.append(record) + + return rdata + + def _params_for_TXT(self, values): + rdata = [] + + for r in values: + txt = "\"" + r.replace('\\;', ';') + "\"" + rdata.append(txt) + + return rdata + + def _build_zone_config(self, zone, _type="primary", comment=None, + masters=[]): + + if self._contractId is None: + raise NameError("contractId not specified to create zone") + + return { + "zone": zone, + "type": _type, + "comment": comment, + "masters": masters + } + + def _get_values(self, data): + + try: + vals = data['values'] + except KeyError: + vals = [data['value']] + + return vals + + def _set_full_name(self, name, zone): + name = name + '.' + zone + + # octodns's name for root is '' + if (name[0] == '.'): + name = name[1:] + + return name diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 8f651f0..6aa842d 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -5,521 +5,12 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals -from requests import Session -from akamai.edgegrid import EdgeGridAuth -from six.moves.urllib.parse import urljoin -from collections import defaultdict - +from .edgedns import AkamaiProvider from logging import getLogger -from ..record import Record -from .base import BaseProvider +# Quell unused warning +AkamaiProvider -class AkamaiClientNotFound(Exception): - - def __init__(self, resp): - message = "404: Resource not found" - super(AkamaiClientNotFound, self).__init__(message) - - -class AkamaiClient(object): - ''' - Client for making calls to Akamai Fast DNS API using Python Requests - - Fast DNS Zone Management API V2, found here: - developer.akamai.com/api/web_performance/fast_dns_zone_management/v2.html - - Info on Python Requests library: - https://2.python-requests.org/en/master/ - - ''' - - def __init__(self, client_secret, host, access_token, client_token): - - self.base = "https://" + host + "/config-dns/v2/" - - sess = Session() - sess.auth = EdgeGridAuth( - client_token=client_token, - client_secret=client_secret, - access_token=access_token - ) - self._sess = sess - - def _request(self, method, path, params=None, data=None, v1=False): - - url = urljoin(self.base, path) - resp = self._sess.request(method, url, params=params, json=data) - - if resp.status_code == 404: - raise AkamaiClientNotFound(resp) - resp.raise_for_status() - - return resp - - def record_create(self, zone, name, record_type, content): - path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('POST', path, data=content) - - return result - - def record_delete(self, zone, name, record_type): - path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('DELETE', path) - - return result - - def record_replace(self, zone, name, record_type, content): - path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('PUT', path, data=content) - - return result - - def zone_get(self, zone): - path = 'zones/{}'.format(zone) - result = self._request('GET', path) - - return result - - def zone_create(self, contractId, params, gid=None): - path = 'zones?contractId={}'.format(contractId) - - if gid is not None: - path += '&gid={}'.format(gid) - - result = self._request('POST', path, data=params) - - return result - - def zone_recordset_get(self, zone, page=None, pageSize=None, search=None, - showAll="true", sortBy="name", types=None): - - params = { - 'page': page, - 'pageSize': pageSize, - 'search': search, - 'showAll': showAll, - 'sortBy': sortBy, - 'types': types - } - - path = 'zones/{}/recordsets'.format(zone) - result = self._request('GET', path, params=params) - - return result - - -class AkamaiProvider(BaseProvider): - - ''' - Akamai Fast DNS Provider - - fastdns.py: - - Example config file with variables: - " - --- - providers: - config: - class: octodns.provider.yaml.YamlProvider - directory: ./config (example path to directory of zone files) - fastdns: - class: octodns.provider.fastdns.AkamaiProvider - client_secret: env/AKAMAI_CLIENT_SECRET - host: env/AKAMAI_HOST - access_token: env/AKAMAI_ACCESS_TOKEN - client_token: env/AKAMAI_CLIENT_TOKEN - contract_id: env/AKAMAI_CONTRACT_ID (optional) - - zones: - example.com.: - sources: - - config - targets: - - fastdns - " - - The first four variables above can be hidden in environment variables - and octoDNS will automatically search for them in the shell. It is - possible to also hard-code into the config file: eg, contract_id. - - The first four values can be found by generating credentials: - https://control.akamai.com/ - Configure > Organization > Manage APIs > New API Client for me - Select appropriate group, and fill relevant fields. - For API Service Name, select DNS-Zone Record Management - and then set appropriate Access level (Read-Write to make changes). - Then select the "New Credential" button to generate values for above - - The contract_id paramater is optional, and only required for creating - a new zone. If the zone being managed already exists in Akamai for the - user in question, then this paramater is not needed. - - ''' - - SUPPORTS_GEO = False - SUPPORTS_DYNAMIC = False - - SUPPORTS = set(('A', 'AAAA', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', - 'SRV', 'SSHFP', 'TXT')) - - def __init__(self, id, client_secret, host, access_token, client_token, - contract_id=None, gid=None, *args, **kwargs): - - self.log = getLogger('AkamaiProvider[{}]'.format(id)) - self.log.debug('__init__: id=%s, ') - super(AkamaiProvider, self).__init__(id, *args, **kwargs) - - self._dns_client = AkamaiClient(client_secret, host, access_token, - client_token) - - self._zone_records = {} - self._contractId = contract_id - self._gid = gid - - def zone_records(self, zone): - """ returns records for a zone, looks for it if not present, or - returns empty [] if can't find a match - """ - if zone.name not in self._zone_records: - try: - name = zone.name[:-1] - response = self._dns_client.zone_recordset_get(name) - self._zone_records[zone.name] = response.json()["recordsets"] - - except (AkamaiClientNotFound, KeyError): - return [] - - return self._zone_records[zone.name] - - def populate(self, zone, target=False, lenient=False): - self.log.debug('populate: name=%s', zone.name) - - values = defaultdict(lambda: defaultdict(list)) - for record in self.zone_records(zone): - - _type = record.get('type') - # Akamai sends down prefix.zonename., while octodns expects prefix - _name = record.get('name').split("." + zone.name[:-1], 1)[0] - if _name == zone.name[:-1]: - _name = '' # root / @ - - if _type not in self.SUPPORTS: - continue - values[_name][_type].append(record) - - before = len(zone.records) - for name, types in values.items(): - for _type, records in types.items(): - data_for = getattr(self, '_data_for_{}'.format(_type)) - record = Record.new(zone, name, data_for(_type, records[0]), - source=self, lenient=lenient) - zone.add_record(record, lenient=lenient) - - exists = zone.name in self._zone_records - found = len(zone.records) - before - self.log.info('populate: found %s records, exists=%s', found, exists) - - return exists - - def _apply(self, plan): - desired = plan.desired - changes = plan.changes - self.log.debug('apply: zone=%s, chnges=%d', desired.name, len(changes)) - - zone_name = desired.name[:-1] - try: - self._dns_client.zone_get(zone_name) - - except AkamaiClientNotFound: - self.log.info("zone not found, creating zone") - params = self._build_zone_config(zone_name) - self._dns_client.zone_create(self._contractId, params, self._gid) - - for change in changes: - class_name = change.__class__.__name__ - getattr(self, '_apply_{}'.format(class_name))(change) - - # Clear out the cache if any - self._zone_records.pop(desired.name, None) - - def _apply_Create(self, change): - - new = change.new - record_type = new._type - - params_for = getattr(self, '_params_for_{}'.format(record_type)) - values = self._get_values(new.data) - rdata = params_for(values) - - zone = new.zone.name[:-1] - name = self._set_full_name(new.name, zone) - - content = { - "name": name, - "type": record_type, - "ttl": new.ttl, - "rdata": rdata - } - - self._dns_client.record_create(zone, name, record_type, content) - - return - - def _apply_Delete(self, change): - - zone = change.existing.zone.name[:-1] - name = self._set_full_name(change.existing.name, zone) - record_type = change.existing._type - - self._dns_client.record_delete(zone, name, record_type) - - return - - def _apply_Update(self, change): - - new = change.new - record_type = new._type - - params_for = getattr(self, '_params_for_{}'.format(record_type)) - values = self._get_values(new.data) - rdata = params_for(values) - - zone = new.zone.name[:-1] - name = self._set_full_name(new.name, zone) - - content = { - "name": name, - "type": record_type, - "ttl": new.ttl, - "rdata": rdata - } - - self._dns_client.record_replace(zone, name, record_type, content) - - return - - def _data_for_multiple(self, _type, records): - - return { - 'ttl': records['ttl'], - 'type': _type, - 'values': [r for r in records['rdata']] - } - - _data_for_A = _data_for_multiple - _data_for_AAAA = _data_for_multiple - _data_for_NS = _data_for_multiple - _data_for_SPF = _data_for_multiple - - def _data_for_CNAME(self, _type, records): - value = records['rdata'][0] - if (value[-1] != '.'): - value = '{}.'.format(value) - - return { - 'ttl': records['ttl'], - 'type': _type, - 'value': value - } - - def _data_for_MX(self, _type, records): - values = [] - for r in records['rdata']: - preference, exchange = r.split(" ", 1) - values.append({ - 'preference': preference, - 'exchange': exchange - }) - return { - 'ttl': records['ttl'], - 'type': _type, - 'values': values - } - - def _data_for_NAPTR(self, _type, records): - values = [] - for r in records['rdata']: - order, preference, flags, service, regexp, repl = r.split(' ', 5) - - values.append({ - 'flags': flags[1:-1], - 'order': order, - 'preference': preference, - 'regexp': regexp[1:-1], - 'replacement': repl, - 'service': service[1:-1] - }) - return { - 'type': _type, - 'ttl': records['ttl'], - 'values': values - } - - def _data_for_PTR(self, _type, records): - - return { - 'ttl': records['ttl'], - 'type': _type, - 'value': records['rdata'][0] - } - - def _data_for_SRV(self, _type, records): - values = [] - for r in records['rdata']: - priority, weight, port, target = r.split(' ', 3) - values.append({ - 'port': port, - 'priority': priority, - 'target': target, - 'weight': weight - }) - - return { - 'type': _type, - 'ttl': records['ttl'], - 'values': values - } - - def _data_for_SSHFP(self, _type, records): - values = [] - for r in records['rdata']: - algorithm, fp_type, fingerprint = r.split(' ', 2) - values.append({ - 'algorithm': algorithm, - 'fingerprint': fingerprint.lower(), - 'fingerprint_type': fp_type - }) - - return { - 'type': _type, - 'ttl': records['ttl'], - 'values': values - } - - def _data_for_TXT(self, _type, records): - values = [] - for r in records['rdata']: - r = r[1:-1] - values.append(r.replace(';', '\\;')) - - return { - 'ttl': records['ttl'], - 'type': _type, - 'values': values - } - - def _params_for_multiple(self, values): - return [r for r in values] - - def _params_for_single(self, values): - return values - - _params_for_A = _params_for_multiple - _params_for_AAAA = _params_for_multiple - _params_for_NS = _params_for_multiple - - _params_for_CNAME = _params_for_single - _params_for_PTR = _params_for_single - - def _params_for_MX(self, values): - rdata = [] - - for r in values: - preference = r['preference'] - exchange = r['exchange'] - - record = '{} {}'.format(preference, exchange) - rdata.append(record) - - return rdata - - def _params_for_NAPTR(self, values): - rdata = [] - - for r in values: - ordr = r['order'] - prf = r['preference'] - flg = "\"" + r['flags'] + "\"" - srvc = "\"" + r['service'] + "\"" - rgx = "\"" + r['regexp'] + "\"" - rpl = r['replacement'] - - record = '{} {} {} {} {} {}'.format(ordr, prf, flg, srvc, rgx, rpl) - rdata.append(record) - - return rdata - - def _params_for_SPF(self, values): - rdata = [] - - for r in values: - txt = "\"" + r.replace('\\;', ';') + "\"" - rdata.append(txt) - - return rdata - - def _params_for_SRV(self, values): - rdata = [] - for r in values: - priority = r['priority'] - weight = r['weight'] - port = r['port'] - target = r['target'] - - record = '{} {} {} {}'.format(priority, weight, port, target) - rdata.append(record) - - return rdata - - def _params_for_SSHFP(self, values): - rdata = [] - for r in values: - algorithm = r['algorithm'] - fp_type = r['fingerprint_type'] - fp = r['fingerprint'] - - record = '{} {} {}'.format(algorithm, fp_type, fp) - rdata.append(record) - - return rdata - - def _params_for_TXT(self, values): - rdata = [] - - for r in values: - txt = "\"" + r.replace('\\;', ';') + "\"" - rdata.append(txt) - - return rdata - - def _build_zone_config(self, zone, _type="primary", comment=None, - masters=[]): - - if self._contractId is None: - raise NameError("contractId not specified to create zone") - - return { - "zone": zone, - "type": _type, - "comment": comment, - "masters": masters - } - - def _get_values(self, data): - - try: - vals = data['values'] - except KeyError: - vals = [data['value']] - - return vals - - def _set_full_name(self, name, zone): - name = name + '.' + zone - - # octodns's name for root is '' - if (name[0] == '.'): - name = name[1:] - - return name +log = getLogger('octodns.provider.fastdns.AkamaiProvider') +log.warn('DEPRECATION NOTICE: AkamaiProvider has been moved to ' + 'octodns.provider.fastdns.AkamaiProvider') diff --git a/tests/fixtures/fastdns-invalid-content.json b/tests/fixtures/edgedns-invalid-content.json similarity index 100% rename from tests/fixtures/fastdns-invalid-content.json rename to tests/fixtures/edgedns-invalid-content.json diff --git a/tests/fixtures/fastdns-records-prev-other.json b/tests/fixtures/edgedns-records-prev-other.json similarity index 100% rename from tests/fixtures/fastdns-records-prev-other.json rename to tests/fixtures/edgedns-records-prev-other.json diff --git a/tests/fixtures/fastdns-records-prev.json b/tests/fixtures/edgedns-records-prev.json similarity index 100% rename from tests/fixtures/fastdns-records-prev.json rename to tests/fixtures/edgedns-records-prev.json diff --git a/tests/fixtures/fastdns-records.json b/tests/fixtures/edgedns-records.json similarity index 100% rename from tests/fixtures/fastdns-records.json rename to tests/fixtures/edgedns-records.json diff --git a/tests/test_octodns_provider_fastdns.py b/tests/test_octodns_provider_edgedns.py similarity index 89% rename from tests/test_octodns_provider_fastdns.py rename to tests/test_octodns_provider_edgedns.py index a8bed74..20a9a07 100644 --- a/tests/test_octodns_provider_fastdns.py +++ b/tests/test_octodns_provider_edgedns.py @@ -13,12 +13,13 @@ from six import text_type from unittest import TestCase from octodns.record import Record -from octodns.provider.fastdns import AkamaiProvider +from octodns.provider.edgedns import AkamaiProvider +from octodns.provider.fastdns import AkamaiProvider as LegacyAkamaiProvider from octodns.provider.yaml import YamlProvider from octodns.zone import Zone -class TestFastdnsProvider(TestCase): +class TestEdgeDnsProvider(TestCase): expected = Zone('unit.tests.', []) source = YamlProvider('test', join(dirname(__file__), 'config')) source.populate(expected) @@ -71,7 +72,7 @@ class TestFastdnsProvider(TestCase): # No diffs == no changes with requests_mock() as mock: - with open('tests/fixtures/fastdns-records.json') as fh: + with open('tests/fixtures/edgedns-records.json') as fh: mock.get(ANY, text=fh.read()) zone = Zone('unit.tests.', []) @@ -95,7 +96,7 @@ class TestFastdnsProvider(TestCase): # tests create update delete through previous state config json with requests_mock() as mock: - with open('tests/fixtures/fastdns-records-prev.json') as fh: + with open('tests/fixtures/edgedns-records-prev.json') as fh: mock.get(ANY, text=fh.read()) plan = provider.plan(self.expected) @@ -108,7 +109,7 @@ class TestFastdnsProvider(TestCase): # Test against a zone that doesn't exist yet with requests_mock() as mock: - with open('tests/fixtures/fastdns-records-prev-other.json') as fh: + with open('tests/fixtures/edgedns-records-prev-other.json') as fh: mock.get(ANY, status_code=404) plan = provider.plan(self.expected) @@ -121,7 +122,7 @@ class TestFastdnsProvider(TestCase): # Test against a zone that doesn't exist yet, but gid not provided with requests_mock() as mock: - with open('tests/fixtures/fastdns-records-prev-other.json') as fh: + with open('tests/fixtures/edgedns-records-prev-other.json') as fh: mock.get(ANY, status_code=404) provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok", "cid") @@ -149,3 +150,9 @@ class TestFastdnsProvider(TestCase): except NameError as e: expected = "contractId not specified to create zone" self.assertEquals(text_type(e), expected) + + +class TestDeprecatedAkamaiProvider(TestCase): + + def test_equivilent(self): + self.assertEquals(LegacyAkamaiProvider, AkamaiProvider)