From 7eaf03dec40a809a36b5554e5391f64656827bef Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 30 May 2019 17:33:42 -0700 Subject: [PATCH 01/64] started fast dns --- octodns/provider/fastdns.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 octodns/provider/fastdns.py diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py new file mode 100644 index 0000000..59318cc --- /dev/null +++ b/octodns/provider/fastdns.py @@ -0,0 +1 @@ +## Provider file for Akamai Fast DNS \ No newline at end of file From 95681972b7722e8d880574ba56f7ccbbae45dcf3 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 30 May 2019 18:25:28 -0700 Subject: [PATCH 02/64] starting authentication for akamai fastdns --- octodns/provider/fastdns.py | 55 ++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 59318cc..3504e02 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -1 +1,54 @@ -## Provider file for Akamai Fast DNS \ No newline at end of file +# +# +# + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +## octodns specfic imports: + +import + +import logging +from functools import reduce +from ..record import Record +from .base import BaseProvider + + +class _AkamaiRecord(object): + pass + + + + +class AkamaiProvider(BaseProvider): + pass + + def __init__(self, id, client_secret, host, access_token, client_token, + *args, **kwargs): + + self.log = logging.getLogger('AkamaiProvider[{}]'.format(id)) + self.log.debug('__init__: id=%s, ') + super(AkamaiProvider, self).__init__(id, *args, **kwargs) + + ## generate edgegrid + #### create credential file, and write credentials to it + credFile = open('tempCred.txt', 'w') + + credFile.write('client_secret = ') + credFile.write(str(client_secret)) + credFile.write('\n\n') + + credFile.write('host = %s', str(host)) + credFile.write('\n\n') + + credFile.write('access_token = %s', str(access_token)) + credFile.write('\n\n') + + credFile.write('client_token = %s', str(client_token)) + + credFile.close() + + #### generate edgegrid using tool + + #### delete temp txt file \ No newline at end of file From f7239e30a5766f19b14cffc5dbe79159a6989d37 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 30 May 2019 18:33:03 -0700 Subject: [PATCH 03/64] fixed import bug --- octodns/provider/fastdns.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 3504e02..2a24edc 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -7,8 +7,6 @@ from __future__ import absolute_import, division, print_function, \ ## octodns specfic imports: -import - import logging from functools import reduce from ..record import Record From 99875a128999c5090c9359cfac1d30850ce48a02 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 30 May 2019 18:36:31 -0700 Subject: [PATCH 04/64] fixed base class errors --- octodns/provider/fastdns.py | 7 ++++++- src/octodns | 1 + src/pip-delete-this-directory.txt | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 160000 src/octodns create mode 100644 src/pip-delete-this-directory.txt diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 2a24edc..a9591d4 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -20,8 +20,13 @@ class _AkamaiRecord(object): class AkamaiProvider(BaseProvider): - pass + SUPPORTS_GEO = False + SUPPORTS_DYNAMIC = False + SUPPORTS = set(('A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SRV', + 'TXT')) + + def __init__(self, id, client_secret, host, access_token, client_token, *args, **kwargs): diff --git a/src/octodns b/src/octodns new file mode 160000 index 0000000..b57cbbb --- /dev/null +++ b/src/octodns @@ -0,0 +1 @@ +Subproject commit b57cbbb09230684552f1fa4db5fb53a921486b96 diff --git a/src/pip-delete-this-directory.txt b/src/pip-delete-this-directory.txt new file mode 100644 index 0000000..c8883ea --- /dev/null +++ b/src/pip-delete-this-directory.txt @@ -0,0 +1,5 @@ +This file is placed here by pip to indicate the source was put +here by pip. + +Once this package is successfully installed this source code will be +deleted (unless you remove this file). From 383452809010f423cac9e48f28af5685bc6f2ee4 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 30 May 2019 18:49:41 -0700 Subject: [PATCH 05/64] fixed file writing error --- octodns/provider/fastdns.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index a9591d4..be4df69 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -42,13 +42,13 @@ class AkamaiProvider(BaseProvider): credFile.write(str(client_secret)) credFile.write('\n\n') - credFile.write('host = %s', str(host)) + credFile.write('host = ' + str(host)) credFile.write('\n\n') - credFile.write('access_token = %s', str(access_token)) + credFile.write('access_token = ' + str(access_token)) credFile.write('\n\n') - credFile.write('client_token = %s', str(client_token)) + credFile.write('client_token = ' + str(client_token)) credFile.close() From e7d82ac75d74f1daf2cb9ad76e67f1cab690a4de Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Tue, 4 Jun 2019 12:16:14 -0700 Subject: [PATCH 06/64] sync --- octodns/provider/fastdns.py | 1 + 1 file changed, 1 insertion(+) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index be4df69..439f924 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -51,6 +51,7 @@ class AkamaiProvider(BaseProvider): credFile.write('client_token = ' + str(client_token)) credFile.close() + #### generate edgegrid using tool From 064b30c4d1b9f29eb3f392b33838664d1665a4fc Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 09:29:45 -0700 Subject: [PATCH 07/64] began authentication script --- octodns/provider/fastdns.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 439f924..c955cdf 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -49,10 +49,16 @@ class AkamaiProvider(BaseProvider): credFile.write('\n\n') credFile.write('client_token = ' + str(client_token)) + credFile.write('\n') credFile.close() - + #### generate edgegrid using tool - #### delete temp txt file \ No newline at end of file + #### delete temp txt file + + def populate(self, zone, target=False, lenient=False): + pass + + From 34b618fbee77de96bf1c8bc6afb4fd8762296c6b Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 09:45:27 -0700 Subject: [PATCH 08/64] began authentication script --- octodns/provider/fastdns.py | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index c955cdf..a01f71e 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -6,6 +6,9 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals ## octodns specfic imports: +from os.path import expanduser +import ConfigParser + import logging from functools import reduce @@ -34,8 +37,49 @@ class AkamaiProvider(BaseProvider): self.log.debug('__init__: id=%s, ') super(AkamaiProvider, self).__init__(id, *args, **kwargs) + self._authenticate(self, client_secret, host, access_token, client_token) + + + def _authenticate(self, client_secret, host, access_token, client_token) + ## generate edgegrid + section_name = "dns" + home = expanduser("~") + index = 0 + fields = {} + + ## process original .edgerc file + origConfig = ConfigParser.ConfigParser() + filename = "%s/.edgerc" % home + + # If this is a new file, create it + if not os.path.isfile(filename): + open(filename, 'a+').close() + + origConfig.read(filename) + + + if section_name in origConfig.sections(): + print (">>> Replacing section: %s" % section_name) + + + + sys.stdout.write ("*** OK TO REPLACE section %s? *** [Y|n]:" % section_name) + real_raw_input = vars(__builtins__).get('raw_input',input) + choice = real_raw_input().lower() + if choice == "n": + print ("Not replacing section.") + exit(0) + + replace_section = True + else: + print ("+++ Creating section: %s" % section_name) + replace_section = False + + #### create credential file, and write credentials to it + + credFile = open('tempCred.txt', 'w') credFile.write('client_secret = ') From e1a139d18753ecb7f68acf7c9a051c686fe5332d Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 14:31:26 -0400 Subject: [PATCH 09/64] revamed authentication --- octodns/provider/fastdns.py | 90 +++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index a01f71e..597c68a 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -9,6 +9,8 @@ from __future__ import absolute_import, division, print_function, \ from os.path import expanduser import ConfigParser +# import os ## command line workaround + import logging from functools import reduce @@ -42,65 +44,57 @@ class AkamaiProvider(BaseProvider): def _authenticate(self, client_secret, host, access_token, client_token) - ## generate edgegrid - section_name = "dns" - home = expanduser("~") - index = 0 - fields = {} + # ## generate edgegrid + # section_name = "dns" + # home = expanduser("~") + # index = 0 + # fields = {} - ## process original .edgerc file - origConfig = ConfigParser.ConfigParser() - filename = "%s/.edgerc" % home + # ## process original .edgerc file + # origConfig = ConfigParser.ConfigParser() + # filename = "%s/.edgerc" % home - # If this is a new file, create it - if not os.path.isfile(filename): - open(filename, 'a+').close() + # # If this is a new file, create it + # if not os.path.isfile(filename): + # open(filename, 'a+').close() - origConfig.read(filename) + # origConfig.read(filename) + + # ## credential type already exists, prompt to overwrite + # if section_name in origConfig.sections(): + # print (">>> Replacing section: %s" % section_name) + # sys.stdout.write ("*** OK TO REPLACE section %s? *** [Y|n]:" % section_name) + # real_raw_input = vars(__builtins__).get('raw_input',input) + # choice = real_raw_input().lower() + # if choice == "n": + # print ("Not replacing section.") + # return + # replace_section = True + # else: + # print ("+++ Creating section: %s" % section_name) + # replace_section = False + + # ## open the ~/.edgerc file for writing + # Config = ConfigParser.ConfigParser() + # Config.read(filename) + # confile = open(filename, 'w') - if section_name in origConfig.sections(): - print (">>> Replacing section: %s" % section_name) - - - - sys.stdout.write ("*** OK TO REPLACE section %s? *** [Y|n]:" % section_name) - real_raw_input = vars(__builtins__).get('raw_input',input) - choice = real_raw_input().lower() - if choice == "n": - print ("Not replacing section.") - exit(0) - - replace_section = True - else: - print ("+++ Creating section: %s" % section_name) - replace_section = False + # ## add the new section + #### create credential file, and write credentials to it + with open('~/.edgerc', 'a') as credFile: - credFile = open('tempCred.txt', 'w') + credFile.write('[dns]\n') - credFile.write('client_secret = ') - credFile.write(str(client_secret)) - credFile.write('\n\n') - - credFile.write('host = ' + str(host)) - credFile.write('\n\n') - - credFile.write('access_token = ' + str(access_token)) - credFile.write('\n\n') - - credFile.write('client_token = ' + str(client_token)) - credFile.write('\n') - - credFile.close() - - - #### generate edgegrid using tool - - #### delete temp txt file + credFile.write('client_secret = ' + str(client_secret) + '\n') + credFile.write('host = ' + str(host) + '\n') + credFile.write('access_token = ' + str(access_token) + '\n') + credFile.write('client_token = ' + str(client_token) + '\n') + credFile.close() def populate(self, zone, target=False, lenient=False): pass From d4867c4268c90408e3db49c1f92deaa68a8869d1 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 14:40:41 -0400 Subject: [PATCH 10/64] fixed typo in authernticate --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 597c68a..94c049e 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -42,7 +42,7 @@ class AkamaiProvider(BaseProvider): self._authenticate(self, client_secret, host, access_token, client_token) - def _authenticate(self, client_secret, host, access_token, client_token) + def _authenticate(self, client_secret, host, access_token, client_token): # ## generate edgegrid # section_name = "dns" From f21915308850a7402d75b5e28a6d8647ec4f8321 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 14:49:58 -0400 Subject: [PATCH 11/64] fixed call to auth fxn --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 94c049e..e8fb35c 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -39,7 +39,7 @@ class AkamaiProvider(BaseProvider): self.log.debug('__init__: id=%s, ') super(AkamaiProvider, self).__init__(id, *args, **kwargs) - self._authenticate(self, client_secret, host, access_token, client_token) + self._authenticate(client_secret, host, access_token, client_token) def _authenticate(self, client_secret, host, access_token, client_token): From 3d61baea5060ed1ff75ec14d6b8a2153b8c99780 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 15:00:04 -0400 Subject: [PATCH 12/64] fixed issue with accessing home dir --- octodns/provider/fastdns.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index e8fb35c..1ce91d0 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -86,7 +86,10 @@ class AkamaiProvider(BaseProvider): #### create credential file, and write credentials to it - with open('~/.edgerc', 'a') as credFile: + + home = expanduser("~") + filename = "%s/.edgerc" % home + with open(filename, 'a') as credFile: credFile.write('[dns]\n') From 2ea26273b807b1e4a955133a391512a25cc82d10 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 17:25:16 -0400 Subject: [PATCH 13/64] started populate fxn --- octodns/provider/fastdns.py | 57 +++++++------------------------------ 1 file changed, 10 insertions(+), 47 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 1ce91d0..56e94f8 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -7,9 +7,9 @@ from __future__ import absolute_import, division, print_function, \ ## octodns specfic imports: from os.path import expanduser -import ConfigParser - -# import os ## command line workaround +import requests +from akamai.edgegrid import EdgeGridAuth +from urlparse import urljoin import logging @@ -40,53 +40,12 @@ class AkamaiProvider(BaseProvider): super(AkamaiProvider, self).__init__(id, *args, **kwargs) self._authenticate(client_secret, host, access_token, client_token) + self._zone_records = {} def _authenticate(self, client_secret, host, access_token, client_token): - # ## generate edgegrid - # section_name = "dns" - # home = expanduser("~") - # index = 0 - # fields = {} - - # ## process original .edgerc file - # origConfig = ConfigParser.ConfigParser() - # filename = "%s/.edgerc" % home - - # # If this is a new file, create it - # if not os.path.isfile(filename): - # open(filename, 'a+').close() - - # origConfig.read(filename) - - # ## credential type already exists, prompt to overwrite - # if section_name in origConfig.sections(): - # print (">>> Replacing section: %s" % section_name) - # sys.stdout.write ("*** OK TO REPLACE section %s? *** [Y|n]:" % section_name) - # real_raw_input = vars(__builtins__).get('raw_input',input) - # choice = real_raw_input().lower() - # if choice == "n": - # print ("Not replacing section.") - # return - # replace_section = True - # else: - # print ("+++ Creating section: %s" % section_name) - # replace_section = False - - # ## open the ~/.edgerc file for writing - # Config = ConfigParser.ConfigParser() - # Config.read(filename) - # confile = open(filename, 'w') - - - # ## add the new section - - - - #### create credential file, and write credentials to it - - + ## generate edgegrid home = expanduser("~") filename = "%s/.edgerc" % home with open(filename, 'a') as credFile: @@ -100,6 +59,10 @@ class AkamaiProvider(BaseProvider): credFile.close() def populate(self, zone, target=False, lenient=False): - pass + self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, + target, lenient) + + print ("populate(%s)", zone.name) + From 66136f475f2b41a3e3c3cac1f195dc71fba1e595 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 17:27:42 -0400 Subject: [PATCH 14/64] fixed typo --- octodns/provider/fastdns.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 56e94f8..7cd0593 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -59,10 +59,11 @@ class AkamaiProvider(BaseProvider): credFile.close() def populate(self, zone, target=False, lenient=False): - self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, - target, lenient) + self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, target, lenient) print ("populate(%s)", zone.name) + return + From 4a68f7df259f40e06679512c8b8fcc7c059fc593 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 18:32:46 -0400 Subject: [PATCH 15/64] added dns client class, changed auth method --- octodns/provider/fastdns.py | 39 ++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 7cd0593..fe5602c 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -22,6 +22,29 @@ class _AkamaiRecord(object): pass +class AkamaiClient(object): + + def __init__(self, _client_secret, _host, _access_token, _client_token): + + self.base = "https://" + _host + "/config-dns/v1/zones/" + + sess = requests.Session() + sess.auth = EdgeGridAuth( + client_token=_client_token, + client_secret=_client_secret, + access_token=_access_token + ) + self._sess = sess + + + def getZone(self, name): + path = urljoin(self.base, name) + result = self.sess.get(path) + return result.json() + + + # def _get(self, method, path, params=None, data=None): + # url = '{}{}{}'.format(self.) class AkamaiProvider(BaseProvider): @@ -39,7 +62,12 @@ class AkamaiProvider(BaseProvider): self.log.debug('__init__: id=%s, ') super(AkamaiProvider, self).__init__(id, *args, **kwargs) - self._authenticate(client_secret, host, access_token, client_token) + + self._dns_client = AkamaiClient(client_secret, host, access_token, client_token) + + + + #self._authenticate(client_secret, host, access_token, client_token) self._zone_records = {} @@ -61,9 +89,14 @@ class AkamaiProvider(BaseProvider): def populate(self, zone, target=False, lenient=False): self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, target, lenient) - print ("populate(%s)", zone.name) + print ("populate()", zone.name) - return + result = self._dns_client.getZone() + + print(result) + + return + From 6afab8c90a1033c51809a5d94f39edacaf7286fb Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 18:35:04 -0400 Subject: [PATCH 16/64] fixed typo --- octodns/provider/fastdns.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index fe5602c..afbbfc2 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -90,13 +90,14 @@ class AkamaiProvider(BaseProvider): self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, target, lenient) print ("populate()", zone.name) + - result = self._dns_client.getZone() + result = self._dns_client.getZone(zone.name) print(result) return - + From b385b1ddb5d2a5bc7b4c3eb3e3907c4ef80ef7b4 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 18:36:16 -0400 Subject: [PATCH 17/64] fixed typo --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index afbbfc2..da9d3ee 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -39,7 +39,7 @@ class AkamaiClient(object): def getZone(self, name): path = urljoin(self.base, name) - result = self.sess.get(path) + result = self._sess.get(path) return result.json() From 32f01da506961ad7ab0daf7ff3677aeadcb463fe Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 18:39:24 -0400 Subject: [PATCH 18/64] fixed zone name trailing . --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index da9d3ee..4d0d2a9 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -91,7 +91,7 @@ class AkamaiProvider(BaseProvider): print ("populate()", zone.name) - + zone_name = zone.name[:len(zone_name)-1] result = self._dns_client.getZone(zone.name) print(result) From d2ec707255a1323de8fcaa26e792ea2390f75fa3 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 18:40:30 -0400 Subject: [PATCH 19/64] fixed typo --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 4d0d2a9..b0b3c8e 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -91,7 +91,7 @@ class AkamaiProvider(BaseProvider): print ("populate()", zone.name) - zone_name = zone.name[:len(zone_name)-1] + zone_name = zone.name[:len(zone.name)-1] result = self._dns_client.getZone(zone.name) print(result) From a9fe4a9cf6bba95fbeb951ee2384323a59c263e8 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 18:42:26 -0400 Subject: [PATCH 20/64] fixed typo --- octodns/provider/fastdns.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index b0b3c8e..a58fbe5 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -92,7 +92,8 @@ class AkamaiProvider(BaseProvider): print ("populate()", zone.name) zone_name = zone.name[:len(zone.name)-1] - result = self._dns_client.getZone(zone.name) + + result = self._dns_client.getZone(zone_name) print(result) From 9c43c92339eaa0996fba256a9c92a538d49fac3c Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 18:52:55 -0400 Subject: [PATCH 21/64] json parse --- octodns/provider/fastdns.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index a58fbe5..7f50cb1 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -6,10 +6,10 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals ## octodns specfic imports: -from os.path import expanduser import requests from akamai.edgegrid import EdgeGridAuth from urlparse import urljoin +import json import logging @@ -65,12 +65,13 @@ class AkamaiProvider(BaseProvider): self._dns_client = AkamaiClient(client_secret, host, access_token, client_token) - - #self._authenticate(client_secret, host, access_token, client_token) self._zone_records = {} + + + def _authenticate(self, client_secret, host, access_token, client_token): ## generate edgegrid @@ -95,7 +96,11 @@ class AkamaiProvider(BaseProvider): result = self._dns_client.getZone(zone_name) - print(result) + + print "\n\n" + print json.dumps(result, indent=4, separators=(',', ': ')) + print "\n\n" + return From ec5e7a173ecc90b9a719bcc31a62fad2c6891a0c Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 18:54:19 -0400 Subject: [PATCH 22/64] fixed typo --- octodns/provider/fastdns.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 7f50cb1..caf17ae 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -97,9 +97,9 @@ class AkamaiProvider(BaseProvider): result = self._dns_client.getZone(zone_name) - print "\n\n" - print json.dumps(result, indent=4, separators=(',', ': ')) - print "\n\n" + print ("\n\n") + print (json.dumps(result, indent=4, separators=(',', ': '))) + print ("\n\n") return From c7b39053fe4d4876854d35faeb13ee69960dc39b Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 6 Jun 2019 19:16:37 -0400 Subject: [PATCH 23/64] sync --- octodns/provider/fastdns.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index caf17ae..fc37834 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -43,8 +43,6 @@ class AkamaiClient(object): return result.json() - # def _get(self, method, path, params=None, data=None): - # url = '{}{}{}'.format(self.) class AkamaiProvider(BaseProvider): @@ -90,10 +88,8 @@ class AkamaiProvider(BaseProvider): def populate(self, zone, target=False, lenient=False): self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, target, lenient) - print ("populate()", zone.name) - + zone_name = zone.name[:len(zone.name)-1] - result = self._dns_client.getZone(zone_name) @@ -101,7 +97,6 @@ class AkamaiProvider(BaseProvider): print (json.dumps(result, indent=4, separators=(',', ': '))) print ("\n\n") - return From 4943b72b4cdfcf25d5eee2287ec4ea4752e616f4 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Mon, 10 Jun 2019 07:54:57 -0700 Subject: [PATCH 24/64] sync --- octodns/provider/.vscode/settings.json | 2 ++ octodns/provider/fastdns.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 octodns/provider/.vscode/settings.json diff --git a/octodns/provider/.vscode/settings.json b/octodns/provider/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/octodns/provider/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index fc37834..1405d17 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -40,8 +40,9 @@ class AkamaiClient(object): def getZone(self, name): path = urljoin(self.base, name) result = self._sess.get(path) - return result.json() + return result.json() + # return result @@ -91,9 +92,14 @@ class AkamaiProvider(BaseProvider): zone_name = zone.name[:len(zone.name)-1] result = self._dns_client.getZone(zone_name) - - print ("\n\n") + + print() + print() + + print(type(result)) + #print(result.text) + print (json.dumps(result, indent=4, separators=(',', ': '))) print ("\n\n") From 1aa9424e213dc0e70e6a03164778aae8a723db05 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Mon, 10 Jun 2019 14:23:26 -0700 Subject: [PATCH 25/64] working sync --- src/octodns | 1 - src/pip-delete-this-directory.txt | 5 ----- 2 files changed, 6 deletions(-) delete mode 160000 src/octodns delete mode 100644 src/pip-delete-this-directory.txt diff --git a/src/octodns b/src/octodns deleted file mode 160000 index b57cbbb..0000000 --- a/src/octodns +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b57cbbb09230684552f1fa4db5fb53a921486b96 diff --git a/src/pip-delete-this-directory.txt b/src/pip-delete-this-directory.txt deleted file mode 100644 index c8883ea..0000000 --- a/src/pip-delete-this-directory.txt +++ /dev/null @@ -1,5 +0,0 @@ -This file is placed here by pip to indicate the source was put -here by pip. - -Once this package is successfully installed this source code will be -deleted (unless you remove this file). From b58210328ff2ca7942fd6457ca45ca4c861aeb6f Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Wed, 12 Jun 2019 20:31:11 -0400 Subject: [PATCH 26/64] implemented API v2 - client fxns to request records functionality --- octodns/provider/fastdns.py | 127 +++++++++++++++++++++++++++--------- 1 file changed, 95 insertions(+), 32 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 1405d17..a6d5cae 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -18,6 +18,30 @@ from ..record import Record from .base import BaseProvider +class AkamaiClientException(Exception): + pass + +class AkamaiClientBadRequest(AkamaiClientException): #400 + def __init__(self): + super(AkamaiClientBadRequest, self).__init__('Bad request') + +class AkamaiClientNotAuthorized(AkamaiClientException): #403 + def __init__(self): + super(AkamaiClientNotAuthorized, self).__init__('Forbidden') + +class AkamaiClientNotFound(AkamaiClientException): #404 + def __init__(self): + super(AkamaiClientNotFound, self).__init__('Not found') + +class AkamaiClientNotAcceptable(AkamaiClientException): #406 + def __init__(self): + super(AkamaiClientNotAcceptable, self).__init__('Not acceptable') + +class AkamaiClientGenericExcp(AkamaiClientException): + def __init__(self, num): + super(AkamaiClientGenericExcp, self).__init__('HTTP Error: ' + str(num)) + + class _AkamaiRecord(object): pass @@ -26,7 +50,8 @@ class AkamaiClient(object): def __init__(self, _client_secret, _host, _access_token, _client_token): - self.base = "https://" + _host + "/config-dns/v1/zones/" + self.base = "https://" + _host + "/config-dns/v2/zones/" + self.basev1 = "https://" + _host + "/config-dns/v1/zones/" sess = requests.Session() sess.auth = EdgeGridAuth( @@ -36,22 +61,71 @@ class AkamaiClient(object): ) self._sess = sess + def _request(self, method, path, params=None, data=None): + # url = '{}{}'.format(self.base, path) + url = urljoin(self.base, path) + print(url) + resp = self._sess.request(method, url, params=params, json=data) - def getZone(self, name): - path = urljoin(self.base, name) - result = self._sess.get(path) + if resp.status_code == 400: + raise AkamaiClientBadRequest() + if resp.status_code == 403: + raise AkamaiClientNotAuthorized() + if resp.status_code == 404: + raise AkamaiClientNotFound() + if resp.status_code == 406: + raise AkamaiClientNotAcceptable() + resp.raise_for_status() + return resp + + + # def getZone(self, name): + # path = name + "/recordsets/" + # result = self._request('GET', path, "sortBy=type&types=A") + + # return result.json() + + # def getNames(self, name): + # path = name + "/names/" + # result = self._request('GET', path) + + # return result.json() + + + + def record_get(self, zone, name, record_type): + + path = '/zones/{}/names/{}/types/'.format(zone, name, record_type) + result = self._request('GET', path) + return result.json() - # return result + + def record_create(self, zone, name, record_type, params): + path = '/zones/{}/names/{}/types/'.format(zone, name, record_type) + result = self._request('POST', path, data=params) + + 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, params): + path = '/zones/{}/names/{}/types/'.format(zone, name, record_type) + result = self._request('PUT', path, data=params) + + return result class AkamaiProvider(BaseProvider): SUPPORTS_GEO = False SUPPORTS_DYNAMIC = False - SUPPORTS = set(('A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SRV', - 'TXT')) + SUPPORTS = set(('A', 'AAAA', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', + 'SRV', 'SSHFP', 'TXT')) def __init__(self, id, client_secret, host, access_token, client_token, @@ -64,44 +138,33 @@ class AkamaiProvider(BaseProvider): self._dns_client = AkamaiClient(client_secret, host, access_token, client_token) - #self._authenticate(client_secret, host, access_token, client_token) + self._zone_records = {} - - - def _authenticate(self, client_secret, host, access_token, client_token): - - ## generate edgegrid - home = expanduser("~") - filename = "%s/.edgerc" % home - with open(filename, 'a') as credFile: - - credFile.write('[dns]\n') - - credFile.write('client_secret = ' + str(client_secret) + '\n') - credFile.write('host = ' + str(host) + '\n') - credFile.write('access_token = ' + str(access_token) + '\n') - credFile.write('client_token = ' + str(client_token) + '\n') - credFile.close() - def populate(self, zone, target=False, lenient=False): self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, target, lenient) zone_name = zone.name[:len(zone.name)-1] - result = self._dns_client.getZone(zone_name) + zone = self._dns_client.getZone(zone_name) + names = self._dns_client.getNames(zone_name) - - print() - print() - print(type(result)) + # print(type(result)) #print(result.text) - print (json.dumps(result, indent=4, separators=(',', ': '))) - print ("\n\n") + zoneFile = open('zoneFile.json', 'w') + namesFile = open('namesFile.json', 'w') + zoneFile.write(json.dumps(zone, indent=4, separators=(',', ': '))) + namesFile.write(json.dumps(names, indent=4, separators=(',', ': '))) + zoneFile.close() + namesFile.close() + + + + #print (json.dumps(result, indent=4, separators=(',', ': '))) return From e85fce6fe0751e426222d21a2837e466b0910078 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 13 Jun 2019 13:16:25 -0400 Subject: [PATCH 27/64] added test class to test API calls -- to be removed later --- octodns/provider/fastdns.py | 78 ++++++++++++++++++++++++------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index a6d5cae..cb5123d 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -64,7 +64,23 @@ class AkamaiClient(object): def _request(self, method, path, params=None, data=None): # url = '{}{}'.format(self.base, path) url = urljoin(self.base, path) - print(url) + print(method, url) + + req = requests.Request(method, url, params=params, json=data) + prepped = req.prepare() + + filename = str(method) + ".json" + f = open(filename, 'w') + # f.write(json.dumps(prepped, indent=4, separators=(',', ': '))) + f.write(method + " " + url + "\n") + f.write("headers: \n") + f.write(str(prepped.headers)) + f.write("\nbody: \n") + f.write(str(prepped.body)) + f.close() + + + ''' resp = self._sess.request(method, url, params=params, json=data) if resp.status_code == 400: @@ -78,20 +94,22 @@ class AkamaiClient(object): resp.raise_for_status() return resp + ''' + return prepped + ''' + def getZone(self, name): + path = name + "/recordsets/" + result = self._request('GET', path, "sortBy=type&types=A") - # def getZone(self, name): - # path = name + "/recordsets/" - # result = self._request('GET', path, "sortBy=type&types=A") + return result.json() - # return result.json() - - # def getNames(self, name): - # path = name + "/names/" - # result = self._request('GET', path) - - # return result.json() + def getNames(self, name): + path = name + "/names/" + result = self._request('GET', path) + return result.json() + ''' def record_get(self, zone, name, record_type): @@ -99,6 +117,7 @@ class AkamaiClient(object): path = '/zones/{}/names/{}/types/'.format(zone, name, record_type) result = self._request('GET', path) + return return result.json() def record_create(self, zone, name, record_type, params): @@ -145,29 +164,36 @@ class AkamaiProvider(BaseProvider): def populate(self, zone, target=False, lenient=False): self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, target, lenient) + self._test(zone) + + def _test(self, zone) : + zone_name = zone.name[:len(zone.name)-1] - zone = self._dns_client.getZone(zone_name) - names = self._dns_client.getNames(zone_name) - # print(type(result)) - #print(result.text) - - zoneFile = open('zoneFile.json', 'w') - namesFile = open('namesFile.json', 'w') - zoneFile.write(json.dumps(zone, indent=4, separators=(',', ': '))) - namesFile.write(json.dumps(names, indent=4, separators=(',', ': '))) - zoneFile.close() - namesFile.close() + record_name = "www.exampleRecordName.com" + record_type = "AAAA" + params = { + "name": "www.example.com", + "type": "AAAA", + "ttl": 300, + "rdata": [ + "10.0.0.2", + "10.0.0.3" + ] + } + + self._dns_client.record_get(zone_name, record_name, record_type) + self._dns_client.record_create(zone_name, record_name, record_type, params) + self._dns_client.record_delete(zone_name, record_name, record_type) + self._dns_client.record_replace(zone_name, record_name, record_type, params) + # domainRequest = self._dns_client.getZone(zone_name) + # names = self._dns_client.getNames(zone_name) - #print (json.dumps(result, indent=4, separators=(',', ': '))) return - - - From ea9c90c3bb7ed47ef83a1fdeaf0d51a12177019a Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 13 Jun 2019 18:26:56 -0400 Subject: [PATCH 28/64] major commit: refactored exceptions, implemented testing suite, implm zones get, zone recordset get, record api calls --- octodns/provider/fastdns.py | 311 +++++++++++++++++++++++++++++------- 1 file changed, 251 insertions(+), 60 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index cb5123d..56aa883 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -17,10 +17,28 @@ from functools import reduce from ..record import Record from .base import BaseProvider +TESTING = False class AkamaiClientException(Exception): - pass + + _errorMessages = { + 400: "400: Bad request", + 403: "403: Access is forbidden", + 404: "404: Resource not found", + 405: "405: Method not supported", + 406: "406: Not Acceptable", + 409: "409: Request not allowed due to conflict with current state of resource", + 415: "415: Unsupported media type", + 422: "422: Request body contains an error preventing processing", + 500: "500: Internal server error" + } + def __init__(self, code): + message = self._errorMessages.get(code) + super(AkamaiClientException, self).__init__(message) + + +''' class AkamaiClientBadRequest(AkamaiClientException): #400 def __init__(self): super(AkamaiClientBadRequest, self).__init__('Bad request') @@ -39,8 +57,8 @@ class AkamaiClientNotAcceptable(AkamaiClientException): #406 class AkamaiClientGenericExcp(AkamaiClientException): def __init__(self, num): - super(AkamaiClientGenericExcp, self).__init__('HTTP Error: ' + str(num)) - + super(AkamaiClientGenericExcp, self).__init__('HTTP Error: ' + str(num)) +''' class _AkamaiRecord(object): pass @@ -50,8 +68,8 @@ class AkamaiClient(object): def __init__(self, _client_secret, _host, _access_token, _client_token): - self.base = "https://" + _host + "/config-dns/v2/zones/" - self.basev1 = "https://" + _host + "/config-dns/v1/zones/" + self.base = "https://" + _host + "/config-dns/v2/" + self.basev1 = "https://" + _host + "/config-dns/v1/" sess = requests.Session() sess.auth = EdgeGridAuth( @@ -60,85 +78,126 @@ class AkamaiClient(object): access_token=_access_token ) self._sess = sess - - def _request(self, method, path, params=None, data=None): - # url = '{}{}'.format(self.base, path) - url = urljoin(self.base, path) - print(method, url) - req = requests.Request(method, url, params=params, json=data) - prepped = req.prepare() - filename = str(method) + ".json" + def _writePrepped(self, prepped): + filename = str(prepped.method) + ".json" f = open(filename, 'w') - # f.write(json.dumps(prepped, indent=4, separators=(',', ': '))) - f.write(method + " " + url + "\n") + f.write("headers: \n") f.write(str(prepped.headers)) f.write("\nbody: \n") f.write(str(prepped.body)) + f.write("\nhooks:\n") + f.write(str(prepped.hooks)) + f.write("\nmethod: " + str(prepped.method)) + f.write("\nurl: " + str(prepped.url) + "\n") + f.close() + def _request(self, method, path, params=None, data=None): + + url = urljoin(self.base, path) + + if (TESTING): + print("testing mode") + print(method, url) + req = requests.Request(method, url, params=params, json=data) + prepped = req.prepare() + self._writePrepped(prepped) - ''' resp = self._sess.request(method, url, params=params, json=data) - if resp.status_code == 400: - raise AkamaiClientBadRequest() - if resp.status_code == 403: - raise AkamaiClientNotAuthorized() - if resp.status_code == 404: - raise AkamaiClientNotFound() - if resp.status_code == 406: - raise AkamaiClientNotAcceptable() + + if resp.status_code > 299: + raise AkamaiClientException(resp.status_code) + + # if resp.status_code == 400: + # raise AkamaiClientBadRequest() + # if resp.status_code == 403: + # raise AkamaiClientNotAuthorized() + # if resp.status_code == 404: + # raise AkamaiClientNotFound() + # if resp.status_code == 406: + # raise AkamaiClientNotAcceptable() resp.raise_for_status() + return resp - ''' - return prepped - - ''' - def getZone(self, name): - path = name + "/recordsets/" - result = self._request('GET', path, "sortBy=type&types=A") - - return result.json() - - def getNames(self, name): - path = name + "/names/" - result = self._request('GET', path) - - return result.json() - ''' - + def record_get(self, zone, name, record_type): - path = '/zones/{}/names/{}/types/'.format(zone, name, record_type) + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) result = self._request('GET', path) - return - return result.json() + return result def record_create(self, zone, name, record_type, params): - path = '/zones/{}/names/{}/types/'.format(zone, name, record_type) + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) result = self._request('POST', path, data=params) return result - def record_delete(self, zone, name, record_type): - path = '/zones/{}/names/{}/types/'.format(zone, name, record_type) + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) result = self._request('DELETE', path) + if result.status_code == 204: + print ("successfully deleted ", path) + return result def record_replace(self, zone, name, record_type, params): - path = '/zones/{}/names/{}/types/'.format(zone, name, record_type) + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) result = self._request('PUT', path, data=params) return result + + def zones_get(self, contractIds=None, page=None, pageSize=None, search=None, + showAll="true", sortBy="zone", types=None): + path = 'zones' + + params = { + "contracIds": contractIds, + "page": page, + "pageSize": pageSize, + "search": search, + "showAll": showAll, + "sortBy": sortBy, + "types": types + } + + result = self._request('GET', path, params=params) + + return result + + + def zone_create(self): + pass + + + def zone_recordset_get(self, zone, params=None): + path = 'zones/{}/recordsets'.format(zone) + result = self._request('GET', path, params=params) + + return result + + def zone_recordset_create(self, zone): + pass + + def zone_recordset_replace(self, zone): + pass + + + def master_zone_file_get(self, zone): + path = 'zones/{}/zone-file'.format(zone) + result = self._request('GET', path) + + return result + + class AkamaiProvider(BaseProvider): SUPPORTS_GEO = False @@ -172,28 +231,160 @@ class AkamaiProvider(BaseProvider): zone_name = zone.name[:len(zone.name)-1] - - record_name = "www.exampleRecordName.com" - record_type = "AAAA" + record_name = "octo.basir-test.com" + record_type = "A" params = { - "name": "www.example.com", - "type": "AAAA", + "name": "octo.basir-test.com", + "type": "A", "ttl": 300, "rdata": [ "10.0.0.2", "10.0.0.3" ] } - - self._dns_client.record_get(zone_name, record_name, record_type) - self._dns_client.record_create(zone_name, record_name, record_type, params) - self._dns_client.record_delete(zone_name, record_name, record_type) - self._dns_client.record_replace(zone_name, record_name, record_type, params) + repl_params = { + "name": "octo.basir-test.com", + "type": "A", + "ttl": 300, + "rdata": [ + "99.99.99.99", + "10.0.0.3", + "1.2.3.4" + ] + } - # domainRequest = self._dns_client.getZone(zone_name) - # names = self._dns_client.getNames(zone_name) + print("\n\nRunning test: record get..........\n") + self._test_record_get(zone_name, "test.basir-test.com", record_type) + print("\n\nRunning test: record create..........\n") + self._test_record_create(zone_name, record_name, record_type, params) + print("\n\nRunning test: record replace..........\n") + self._test_record_replace(zone_name, record_name, record_type, repl_params) + print("\n\nRunning test: record delete..........\n") + self._test_record_delete(zone_name, record_name, record_type) + + # print("\n\nRunning test: zones get..........\n") + # self._test_zones_get() + # print("\n\nRunning test: zone recordset get..........\n") + # self._test_zones_recordset_get(zone_name) return + def _test_record_get(self, zone_name, record_name, record_type): + try: + get = self._dns_client.record_get(zone_name, record_name, record_type) + except AkamaiClientException as e: + print ("record get test failed") + print (e.message) + + else: + print("record get test result: ") + print(json.dumps(get.json(), indent=4, separators=(',', ': '))) + + return + + def _test_record_delete(self, zone_name, record_name, record_type): + + try: + delete = self._dns_client.record_delete(zone_name, record_name, record_type) + except AkamaiClientException as e: + print("delete failed") + print(e.message) + return + + + try: + self._dns_client.record_get(zone_name, record_name, record_type) + except AkamaiClientException as e: + print("get on record failed as expected, since record was succesfully deleted") + print ("(Probably Ignore):", e.message) + print ("delete status:", delete.status_code) + else: + print("unexpected condition in test delete") + + return + + def _test_record_create(self, zone_name, record_name, record_type, params): + + try: + create = self._dns_client.record_create(zone_name, record_name, record_type, params) + except AkamaiClientException as e: + print ("create unsuccessful, presumably because it already exists") + print ("(Probably Ignore)", e.message) + else: + print("initial create of", create.json().get("name"), "succesful: ", create.status_code) + + return + + def _test_record_replace(self, zone_name, record_name, record_type, params): + + ## create record to be replaced, if it doesn't already exist + try: + old_params = { + "name": record_name, + "type": record_type, + "ttl": 300, + "rdata": [ + "10.0.0.2", + "10.0.0.3" + ] + } + create = self._dns_client.record_create(zone_name, record_name, record_type, old_params) + except AkamaiClientException as e: + print ("initial create unsuccessful, presumably because it already exists") + print ("(Probably Ignore)", e.message) + else: + print("initial create of record to be replaced", create.json().get("name"), "succesful: ", create.status_code) + + + ## test replace + try: + replace = self._dns_client.record_replace(zone_name, record_name, record_type, params) + except AkamaiClientException as e: + print("replace failed") + print(e.message) + return + else: + try: + record = self._dns_client.record_get(zone_name, record_name, record_type) + except AkamaiClientException as e: + print("retrieval in replacement failed") + print(e.message) + else: + new_data = record.json() + + if (new_data != params): + print("replace failed, records don't match") + print("current data:") + print(new_data) + print("expected data:") + print(params) + + else: + print("replace succesful") + print("replace status:", replace.status_code) + + def _test_zones_get(self): + try: + zonesList = self._dns_client.zones_get() + except AkamaiClientException as e: + print ("zones get test failed") + print (e.message) + + else: + print("zones list: ") + print(json.dumps(zonesList.json(), indent=4, separators=(',', ': '))) + + return + + def _test_zones_recordset_get(self, zone_name): + try: + zoneRecordset = self._dns_client.zone_recordset_get(zone_name) + except AkamaiClientException as e: + print("zone recordset retrieval test failed") + print (e.message) + else: + print("zone recordset: ") + print(json.dumps(zoneRecordset.json(), indent=4, separators=(',', ': '))) + return From 9844f4293a31834c455fd7e00cf999d693e00ee8 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 13 Jun 2019 19:51:56 -0400 Subject: [PATCH 29/64] implemented records(), and zone_records(), fixed sone_recordset_get(), started populate() --- octodns/provider/fastdns.py | 152 +++++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 65 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 56aa883..b94ff61 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -10,14 +10,14 @@ import requests from akamai.edgegrid import EdgeGridAuth from urlparse import urljoin import json - +from collections import defaultdict import logging from functools import reduce from ..record import Record from .base import BaseProvider -TESTING = False +TESTING = True class AkamaiClientException(Exception): @@ -38,28 +38,6 @@ class AkamaiClientException(Exception): super(AkamaiClientException, self).__init__(message) -''' -class AkamaiClientBadRequest(AkamaiClientException): #400 - def __init__(self): - super(AkamaiClientBadRequest, self).__init__('Bad request') - -class AkamaiClientNotAuthorized(AkamaiClientException): #403 - def __init__(self): - super(AkamaiClientNotAuthorized, self).__init__('Forbidden') - -class AkamaiClientNotFound(AkamaiClientException): #404 - def __init__(self): - super(AkamaiClientNotFound, self).__init__('Not found') - -class AkamaiClientNotAcceptable(AkamaiClientException): #406 - def __init__(self): - super(AkamaiClientNotAcceptable, self).__init__('Not acceptable') - -class AkamaiClientGenericExcp(AkamaiClientException): - def __init__(self, num): - super(AkamaiClientGenericExcp, self).__init__('HTTP Error: ' + str(num)) -''' - class _AkamaiRecord(object): pass @@ -95,10 +73,12 @@ class AkamaiClient(object): f.close() - def _request(self, method, path, params=None, data=None): + def _request(self, method, path, params=None, data=None, v1=False): url = urljoin(self.base, path) - + if v1: + url = urljoin(self.basev1, path) + if (TESTING): print("testing mode") print(method, url) @@ -111,15 +91,7 @@ class AkamaiClient(object): if resp.status_code > 299: raise AkamaiClientException(resp.status_code) - - # if resp.status_code == 400: - # raise AkamaiClientBadRequest() - # if resp.status_code == 403: - # raise AkamaiClientNotAuthorized() - # if resp.status_code == 404: - # raise AkamaiClientNotFound() - # if resp.status_code == 406: - # raise AkamaiClientNotAcceptable() + resp.raise_for_status() @@ -154,47 +126,64 @@ class AkamaiClient(object): return result - def zones_get(self, contractIds=None, page=None, pageSize=None, search=None, showAll="true", sortBy="zone", types=None): path = 'zones' params = { - "contracIds": contractIds, - "page": page, - "pageSize": pageSize, - "search": search, - "showAll": showAll, - "sortBy": sortBy, - "types": types + 'contractIds': contractIds, + 'page': page, + 'pageSize': pageSize, + 'search': search, + 'showAll': showAll, + 'sortBy': sortBy, + 'types': types } result = self._request('GET', path, params=params) return result - - def zone_create(self): - pass + def zone_recordset_get(self, zone, page=None, pageSize=30, search=None, + showAll="true", sortBy="name", types=None): - def zone_recordset_get(self, zone, params=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 - def zone_recordset_create(self, zone): - pass - - def zone_recordset_replace(self, zone): - pass + def records(self, zone_name): + + recordset = self.zone_recordset_get(zone_name, showAll="true").json().get("recordsets") + # print (type(recordset)) + print(recordset) + return recordset def master_zone_file_get(self, zone): + path = 'zones/{}/zone-file'.format(zone) - result = self._request('GET', path) - + + try: + result = self._request('GET', path) + + except AkamaiClientException as e: + # not working with API v2, API v1 fallback + path = 'zones/{}'.format(zone) + result = self._request('GET', path, v1=True) + print("Using API v1 fallback") + print("(Probably Ignore)", e.message) + return result @@ -220,10 +209,26 @@ class AkamaiProvider(BaseProvider): self._zone_records = {} + def zone_records(self, zone): + if zone.name not in self._zone_records: + try: + self._zone_records[zone.name] = self._dns_client.records(zone.name[:-1]) + + except AkamaiClientException: + return [] + + return self._zone_records[zone.name] + def populate(self, zone, target=False, lenient=False): self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, target, lenient) - self._test(zone) + + # self._test(zone) + + values = defaultdict(lambda: defaultdict(list)) + + for record in self.zone_records(zone): + pass @@ -254,20 +259,23 @@ class AkamaiProvider(BaseProvider): } - print("\n\nRunning test: record get..........\n") - self._test_record_get(zone_name, "test.basir-test.com", record_type) - print("\n\nRunning test: record create..........\n") - self._test_record_create(zone_name, record_name, record_type, params) - print("\n\nRunning test: record replace..........\n") - self._test_record_replace(zone_name, record_name, record_type, repl_params) - print("\n\nRunning test: record delete..........\n") - self._test_record_delete(zone_name, record_name, record_type) + # print("\n\nRunning test: record get..........\n") + # self._test_record_get(zone_name, "test.basir-test.com", record_type) + # print("\n\nRunning test: record create..........\n") + # self._test_record_create(zone_name, record_name, record_type, params) + # print("\n\nRunning test: record replace..........\n") + # self._test_record_replace(zone_name, record_name, record_type, repl_params) + # print("\n\nRunning test: record delete..........\n") + # self._test_record_delete(zone_name, record_name, record_type) # print("\n\nRunning test: zones get..........\n") # self._test_zones_get() - # print("\n\nRunning test: zone recordset get..........\n") - # self._test_zones_recordset_get(zone_name) + print("\n\nRunning test: zone recordset get..........\n") + self._test_zones_recordset_get(zone_name) + + # print("\n\nRunning test: Master Zone File get..........\n") + # self._test_master_zone_file_get(zone_name) return @@ -388,3 +396,17 @@ class AkamaiProvider(BaseProvider): print("zone recordset: ") print(json.dumps(zoneRecordset.json(), indent=4, separators=(',', ': '))) return + + def _test_master_zone_file_get(self, zone_name): + try: + mzf = self._dns_client.master_zone_file_get(zone_name) + + except AkamaiClientException as e: + print("MZF retrieval test failed") + print (e.message) + + else: + print("Master Zone File:") + print(json.dumps(mzf.json(), indent=4, separators=(',', ': '))) + + return From b8c94386a87be103c6b058c8e6f354639d5afe47 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 13 Jun 2019 20:05:33 -0400 Subject: [PATCH 30/64] thurs sync --- octodns/provider/fastdns.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index b94ff61..de8e7d6 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -166,8 +166,6 @@ class AkamaiClient(object): recordset = self.zone_recordset_get(zone_name, showAll="true").json().get("recordsets") - # print (type(recordset)) - print(recordset) return recordset def master_zone_file_get(self, zone): @@ -228,7 +226,14 @@ class AkamaiProvider(BaseProvider): values = defaultdict(lambda: defaultdict(list)) for record in self.zone_records(zone): - pass + _type=record['type'] + if _type not in self.SUPPORTS: + continue + elif _type == 'TXT' and record['content'].startswith('ALIAS for'): + # ALIAS has a "ride along" TXT record with 'ALIAS for XXXX', + # we're ignoring it + continue + values[record['name']][record['type']].append(record) From 98f7935f9044532ecb5653aa09e872b7c6128d15 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Fri, 14 Jun 2019 19:06:19 -0700 Subject: [PATCH 31/64] major update: progressed on populate(), implemented _data_for_TYPE fxns, but not all are verfied to work as expected yet --- octodns/.vscode/settings.json | 3 + octodns/provider/.vscode/settings.json | 1 + octodns/provider/fastdns.py | 999 ++++++++++++++----------- 3 files changed, 586 insertions(+), 417 deletions(-) create mode 100644 octodns/.vscode/settings.json diff --git a/octodns/.vscode/settings.json b/octodns/.vscode/settings.json new file mode 100644 index 0000000..38e6c1e --- /dev/null +++ b/octodns/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "C:\\Python27\\python.exe" +} \ No newline at end of file diff --git a/octodns/provider/.vscode/settings.json b/octodns/provider/.vscode/settings.json index 7a73a41..88322cc 100644 --- a/octodns/provider/.vscode/settings.json +++ b/octodns/provider/.vscode/settings.json @@ -1,2 +1,3 @@ { + "python.pythonPath": "C:\\Python27\\python.exe" } \ No newline at end of file diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index de8e7d6..bcb1f32 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -1,417 +1,582 @@ -# -# -# - -from __future__ import absolute_import, division, print_function, \ - unicode_literals - -## octodns specfic imports: -import requests -from akamai.edgegrid import EdgeGridAuth -from urlparse import urljoin -import json -from collections import defaultdict - -import logging -from functools import reduce -from ..record import Record -from .base import BaseProvider - -TESTING = True - -class AkamaiClientException(Exception): - - _errorMessages = { - 400: "400: Bad request", - 403: "403: Access is forbidden", - 404: "404: Resource not found", - 405: "405: Method not supported", - 406: "406: Not Acceptable", - 409: "409: Request not allowed due to conflict with current state of resource", - 415: "415: Unsupported media type", - 422: "422: Request body contains an error preventing processing", - 500: "500: Internal server error" - } - - def __init__(self, code): - message = self._errorMessages.get(code) - super(AkamaiClientException, self).__init__(message) - - -class _AkamaiRecord(object): - pass - - -class AkamaiClient(object): - - def __init__(self, _client_secret, _host, _access_token, _client_token): - - self.base = "https://" + _host + "/config-dns/v2/" - self.basev1 = "https://" + _host + "/config-dns/v1/" - - sess = requests.Session() - sess.auth = EdgeGridAuth( - client_token=_client_token, - client_secret=_client_secret, - access_token=_access_token - ) - self._sess = sess - - - def _writePrepped(self, prepped): - filename = str(prepped.method) + ".json" - f = open(filename, 'w') - - f.write("headers: \n") - f.write(str(prepped.headers)) - f.write("\nbody: \n") - f.write(str(prepped.body)) - f.write("\nhooks:\n") - f.write(str(prepped.hooks)) - f.write("\nmethod: " + str(prepped.method)) - f.write("\nurl: " + str(prepped.url) + "\n") - - f.close() - - def _request(self, method, path, params=None, data=None, v1=False): - - url = urljoin(self.base, path) - if v1: - url = urljoin(self.basev1, path) - - if (TESTING): - print("testing mode") - print(method, url) - req = requests.Request(method, url, params=params, json=data) - prepped = req.prepare() - self._writePrepped(prepped) - - resp = self._sess.request(method, url, params=params, json=data) - - - if resp.status_code > 299: - raise AkamaiClientException(resp.status_code) - - resp.raise_for_status() - - - return resp - - - def record_get(self, zone, name, record_type): - - path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('GET', path) - - return result - - def record_create(self, zone, name, record_type, params): - path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('POST', path, data=params) - - return result - - def record_delete(self, zone, name, record_type): - path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('DELETE', path) - - if result.status_code == 204: - print ("successfully deleted ", path) - - return result - - def record_replace(self, zone, name, record_type, params): - path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('PUT', path, data=params) - - return result - - def zones_get(self, contractIds=None, page=None, pageSize=None, search=None, - showAll="true", sortBy="zone", types=None): - path = 'zones' - - params = { - 'contractIds': contractIds, - 'page': page, - 'pageSize': pageSize, - 'search': search, - 'showAll': showAll, - 'sortBy': sortBy, - 'types': types - } - - result = self._request('GET', path, params=params) - - return result - - def zone_recordset_get(self, zone, page=None, pageSize=30, 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 - - def records(self, zone_name): - - recordset = self.zone_recordset_get(zone_name, showAll="true").json().get("recordsets") - - return recordset - - def master_zone_file_get(self, zone): - - path = 'zones/{}/zone-file'.format(zone) - - try: - result = self._request('GET', path) - - except AkamaiClientException as e: - # not working with API v2, API v1 fallback - path = 'zones/{}'.format(zone) - result = self._request('GET', path, v1=True) - print("Using API v1 fallback") - print("(Probably Ignore)", e.message) - - return result - - -class AkamaiProvider(BaseProvider): - - 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, - *args, **kwargs): - - self.log = logging.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 = {} - - - def zone_records(self, zone): - if zone.name not in self._zone_records: - try: - self._zone_records[zone.name] = self._dns_client.records(zone.name[:-1]) - - except AkamaiClientException: - return [] - - return self._zone_records[zone.name] - - - def populate(self, zone, target=False, lenient=False): - self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name, target, lenient) - - # self._test(zone) - - values = defaultdict(lambda: defaultdict(list)) - - for record in self.zone_records(zone): - _type=record['type'] - if _type not in self.SUPPORTS: - continue - elif _type == 'TXT' and record['content'].startswith('ALIAS for'): - # ALIAS has a "ride along" TXT record with 'ALIAS for XXXX', - # we're ignoring it - continue - values[record['name']][record['type']].append(record) - - - - def _test(self, zone) : - - zone_name = zone.name[:len(zone.name)-1] - - record_name = "octo.basir-test.com" - record_type = "A" - params = { - "name": "octo.basir-test.com", - "type": "A", - "ttl": 300, - "rdata": [ - "10.0.0.2", - "10.0.0.3" - ] - } - repl_params = { - "name": "octo.basir-test.com", - "type": "A", - "ttl": 300, - "rdata": [ - "99.99.99.99", - "10.0.0.3", - "1.2.3.4" - ] - } - - - # print("\n\nRunning test: record get..........\n") - # self._test_record_get(zone_name, "test.basir-test.com", record_type) - # print("\n\nRunning test: record create..........\n") - # self._test_record_create(zone_name, record_name, record_type, params) - # print("\n\nRunning test: record replace..........\n") - # self._test_record_replace(zone_name, record_name, record_type, repl_params) - # print("\n\nRunning test: record delete..........\n") - # self._test_record_delete(zone_name, record_name, record_type) - - # print("\n\nRunning test: zones get..........\n") - # self._test_zones_get() - - print("\n\nRunning test: zone recordset get..........\n") - self._test_zones_recordset_get(zone_name) - - # print("\n\nRunning test: Master Zone File get..........\n") - # self._test_master_zone_file_get(zone_name) - - return - - def _test_record_get(self, zone_name, record_name, record_type): - try: - get = self._dns_client.record_get(zone_name, record_name, record_type) - except AkamaiClientException as e: - print ("record get test failed") - print (e.message) - - else: - print("record get test result: ") - print(json.dumps(get.json(), indent=4, separators=(',', ': '))) - - return - - def _test_record_delete(self, zone_name, record_name, record_type): - - try: - delete = self._dns_client.record_delete(zone_name, record_name, record_type) - except AkamaiClientException as e: - print("delete failed") - print(e.message) - return - - - try: - self._dns_client.record_get(zone_name, record_name, record_type) - except AkamaiClientException as e: - print("get on record failed as expected, since record was succesfully deleted") - print ("(Probably Ignore):", e.message) - print ("delete status:", delete.status_code) - else: - print("unexpected condition in test delete") - - return - - def _test_record_create(self, zone_name, record_name, record_type, params): - - try: - create = self._dns_client.record_create(zone_name, record_name, record_type, params) - except AkamaiClientException as e: - print ("create unsuccessful, presumably because it already exists") - print ("(Probably Ignore)", e.message) - else: - print("initial create of", create.json().get("name"), "succesful: ", create.status_code) - - return - - def _test_record_replace(self, zone_name, record_name, record_type, params): - - ## create record to be replaced, if it doesn't already exist - try: - old_params = { - "name": record_name, - "type": record_type, - "ttl": 300, - "rdata": [ - "10.0.0.2", - "10.0.0.3" - ] - } - create = self._dns_client.record_create(zone_name, record_name, record_type, old_params) - except AkamaiClientException as e: - print ("initial create unsuccessful, presumably because it already exists") - print ("(Probably Ignore)", e.message) - else: - print("initial create of record to be replaced", create.json().get("name"), "succesful: ", create.status_code) - - - ## test replace - try: - replace = self._dns_client.record_replace(zone_name, record_name, record_type, params) - except AkamaiClientException as e: - print("replace failed") - print(e.message) - return - else: - try: - record = self._dns_client.record_get(zone_name, record_name, record_type) - except AkamaiClientException as e: - print("retrieval in replacement failed") - print(e.message) - else: - new_data = record.json() - - if (new_data != params): - print("replace failed, records don't match") - print("current data:") - print(new_data) - print("expected data:") - print(params) - - else: - print("replace succesful") - print("replace status:", replace.status_code) - - def _test_zones_get(self): - try: - zonesList = self._dns_client.zones_get() - except AkamaiClientException as e: - print ("zones get test failed") - print (e.message) - - else: - print("zones list: ") - print(json.dumps(zonesList.json(), indent=4, separators=(',', ': '))) - - return - - def _test_zones_recordset_get(self, zone_name): - try: - zoneRecordset = self._dns_client.zone_recordset_get(zone_name) - except AkamaiClientException as e: - print("zone recordset retrieval test failed") - print (e.message) - else: - print("zone recordset: ") - print(json.dumps(zoneRecordset.json(), indent=4, separators=(',', ': '))) - return - - def _test_master_zone_file_get(self, zone_name): - try: - mzf = self._dns_client.master_zone_file_get(zone_name) - - except AkamaiClientException as e: - print("MZF retrieval test failed") - print (e.message) - - else: - print("Master Zone File:") - print(json.dumps(mzf.json(), indent=4, separators=(',', ': '))) - - return +# +# +# + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +## octodns specfic imports: +import requests +from akamai.edgegrid import EdgeGridAuth +from urlparse import urljoin +import json +from collections import defaultdict + +import logging +from ..record import Record +from .base import BaseProvider + +TESTING = True + +class AkamaiClientException(Exception): + + _errorMessages = { + 400: "400: Bad request", + 403: "403: Access is forbidden", + 404: "404: Resource not found", + 405: "405: Method not supported", + 406: "406: Not Acceptable", + 409: "409: Request not allowed due to conflict with current state of resource", + 415: "415: Unsupported media type", + 422: "422: Request body contains an error preventing processing", + 500: "500: Internal server error" + } + + def __init__(self, code): + message = self._errorMessages.get(code) + super(AkamaiClientException, self).__init__(message) + + +class _AkamaiRecord(object): + pass + + +class AkamaiClient(object): + + def __init__(self, _client_secret, _host, _access_token, _client_token): + + self.base = "https://" + _host + "/config-dns/v2/" + self.basev1 = "https://" + _host + "/config-dns/v1/" + + sess = requests.Session() + sess.auth = EdgeGridAuth( + client_token=_client_token, + client_secret=_client_secret, + access_token=_access_token + ) + self._sess = sess + + + def _writePrepped(self, prepped): + filename = str(prepped.method) + ".json" + f = open(filename, 'w') + + f.write("headers: \n") + f.write(str(prepped.headers)) + f.write("\nbody: \n") + f.write(str(prepped.body)) + f.write("\nhooks:\n") + f.write(str(prepped.hooks)) + f.write("\nmethod: " + str(prepped.method)) + f.write("\nurl: " + str(prepped.url) + "\n") + + f.close() + + def _request(self, method, path, params=None, data=None, v1=False): + + url = urljoin(self.base, path) + if v1: + url = urljoin(self.basev1, path) + + if (TESTING): + print("testing mode") + print(method, url) + req = requests.Request(method, url, params=params, json=data) + prepped = req.prepare() + self._writePrepped(prepped) + + resp = self._sess.request(method, url, params=params, json=data) + + + if resp.status_code > 299: + raise AkamaiClientException(resp.status_code) + + resp.raise_for_status() + + + return resp + + + def record_get(self, zone, name, record_type): + + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) + result = self._request('GET', path) + + return result + + def record_create(self, zone, name, record_type, params): + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) + result = self._request('POST', path, data=params) + + return result + + def record_delete(self, zone, name, record_type): + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) + result = self._request('DELETE', path) + + if result.status_code == 204: + print ("successfully deleted ", path) + + return result + + def record_replace(self, zone, name, record_type, params): + path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) + result = self._request('PUT', path, data=params) + + return result + + def zones_get(self, contractIds=None, page=None, pageSize=None, search=None, + showAll="true", sortBy="zone", types=None): + path = 'zones' + + params = { + 'contractIds': contractIds, + 'page': page, + 'pageSize': pageSize, + 'search': search, + 'showAll': showAll, + 'sortBy': sortBy, + 'types': types + } + + result = self._request('GET', path, params=params) + + return result + + def zone_recordset_get(self, zone, page=None, pageSize=30, 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 + + def records(self, zone_name): + + resp = self.zone_recordset_get(zone_name, showAll="true") + recordset = resp.json().get("recordsets") + + return recordset + + def master_zone_file_get(self, zone): + + path = 'zones/{}/zone-file'.format(zone) + + try: + result = self._request('GET', path) + + except AkamaiClientException as e: + # not working with API v2, API v1 fallback + path = 'zones/{}'.format(zone) + result = self._request('GET', path, v1=True) + print("Using API v1 fallback") + print("(Probably Ignore)", e.message) + + return result + + +class AkamaiProvider(BaseProvider): + + SUPPORTS_GEO = False + SUPPORTS_DYNAMIC = False + + SUPPORTS = set(('A', 'AAAA', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', + 'SRV', 'SSHFP', 'TXT')) + # SUPPORTS = set(('NAPTR', 'MX')) + + def __init__(self, id, client_secret, host, access_token, client_token, + *args, **kwargs): + + self.log = logging.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 = {} + + + def zone_records(self, zone): + """ returns records for a zone, finds it if not present, or + returns empty if doesnt exist + """ + if zone.name not in self._zone_records: + try: + name = zone.name[:-1] + self._zone_records[zone.name] = self._dns_client.records(name) + + except AkamaiClientException: + return [] + + + # fileName = "zone_records_for_" + zone.name +".json" + # path = "/mnt/c/Users/bajamil/Desktop/" + fileName + # f = open(path, 'w') + # f.write(json.dumps(self._zone_records[zone.name], indent=4, separators=(',', ': '))) + # f.close() + + return self._zone_records[zone.name] + + + def populate(self, zone, target=False, lenient=False): + self.log.debug('populate: name=%s', zone.name) + # self._test(zone) + + values = defaultdict(lambda: defaultdict(list)) + for record in self.zone_records(zone): + + _type =record.get('type') + _name = record.get('name') + + if _type not in self.SUPPORTS: + continue + + values[_name][_type].append(record) + + + + fileName = "values_for_" + zone.name +".json" + path = "/mnt/c/Users/bajamil/Desktop/" + fileName + f = open(path, 'w') + f.write(json.dumps(values, indent=4, separators=(',', ': '))) + f.close() + + return ### + before = len(zone.records) + for name, types in values.items(): + for _type, records in types.items(): + data_for = getattr(self, '_data_for_{}'.format(_type)) + + data = data_for(_type, records) + + print() + print ("data processed for", name, ":") + print (json.dumps(data, indent=4, separators=(',', ': '))) + print() + + record = Record.new(zone, name, data_for(_type, records), source=self, lenient=lenient) + + + exists = zone.name in self._zone_records + self.log.info('populate: found %s records, exists=%s', + len(zone.records) - before, exists) + + return exists + + + def _data_for_multiple(self, _type, records): + + return { + 'ttl': records[0]['ttl'], + 'type': _type, + 'values': [r['rdata'][0] for r in records] + } + + _data_for_A = _data_for_multiple + _data_for_AAAA = _data_for_multiple + _data_for_SPF = _data_for_multiple + + def _data_for_CNAME(self, _type, records): + record = records[0] + value = record['rdata'][0] + if (value[-1] != "."): + value = '{}.'.format(value) + + return { + 'ttl': records[0]['ttl'], + 'type': _type, + 'value': value + } + + def _data_for_MX(self, _type, records): + values = [] + for record in records: + print ("MX record: ", record) + values.append({ + 'preference': record['priority'], + 'exchange': '{}.'.format(record['rdata'][0]) + }) + return { + 'ttl': records[0]['ttl'], + 'type': _type, + 'values': values + } + + def _data_for_NAPTR(self, _type, records): + print ("NAPTR Record") + print(json.dumps(records, indent=4, separators=(',', ': '))) + values = [] + for record in records: + order, preference, flags, service, regexp, replacement = \ + record['rdata'][0].split(' ', 5) + + values.append({ + 'flags': flags[1:-1], + 'order': order, + 'preference': preference, + 'regexp': regexp[1:-1], + 'replacement': replacement, + 'service': service[1:-1] + }) + return { + 'type': _type, + 'ttl': records[0]['ttl'], + 'values': values + } + + def _data_for_NS(self, _type, records): + values = [] + for record in records: + rdata = record['rdata'] + for content in rdata: + if content[-1] != '.': + content = '{}.'.format(content) + values.append(content) + return { + 'ttl': records[0]['ttl'], + 'type': _type, + 'value': values + } + + def _data_for_PTR(self, _type, records): + return self._data_for_multiple(_type, records[0]) + + # rdata[0]? + def _data_for_SRV(self, _type, records): + values = [] + for record in records: + weight, port, target = record['rdata'].split(', ', 2) + values.append({ + 'port': port, + 'priority': record['priority'], + 'target': '{}.'.format(target), + 'weight': weight + }) + return { + 'type': _type, + 'ttl': records[0]['ttl'], + 'values': values + } + + # rdata[0]? + def _data_for_SSHFP(self, _type, records): + values = [] + algorithm, fp_type, fingerprint = record['rdata'].split(' ', 2) + values.append({ + 'algorithm': algorithm, + 'fingerprint': fingerprint, + 'fingerprint_type': fp_type + }) + + return { + 'type': _type, + 'ttl': records[0]['ttl'], + 'values': values + } + + def _data_for_TXT(self, _type, records): + values = [] + for r in records: + for content in r['rdata']: + values.append(content.replace(';', '\\;')) + + return { + 'ttl': records[0]['ttl'], + 'type': _type, + 'values': values + # 'values': [r['rdata'][0].replace(';', '\\;') for r in records] + } + + + + + def _test(self, zone) : + + zone_name = zone.name[:len(zone.name)-1] + + record_name = "octo.basir-test.com" + record_type = "A" + params = { + "name": "octo.basir-test.com", + "type": "A", + "ttl": 300, + "rdata": [ + "10.0.0.2", + "10.0.0.3" + ] + } + repl_params = { + "name": "octo.basir-test.com", + "type": "A", + "ttl": 300, + "rdata": [ + "99.99.99.99", + "10.0.0.3", + "1.2.3.4" + ] + } + + + # print("\n\nRunning test: record get..........\n") + # self._test_record_get(zone_name, "test.basir-test.com", record_type) + # print("\n\nRunning test: record create..........\n") + # self._test_record_create(zone_name, record_name, record_type, params) + # print("\n\nRunning test: record replace..........\n") + # self._test_record_replace(zone_name, record_name, record_type, repl_params) + # print("\n\nRunning test: record delete..........\n") + # self._test_record_delete(zone_name, record_name, record_type) + + # print("\n\nRunning test: zones get..........\n") + # self._test_zones_get() + + print("\n\nRunning test: zone recordset get..........\n") + self._test_zones_recordset_get(zone_name) + + # print("\n\nRunning test: Master Zone File get..........\n") + # self._test_master_zone_file_get(zone_name) + + return + + def _test_record_get(self, zone_name, record_name, record_type): + try: + get = self._dns_client.record_get(zone_name, record_name, record_type) + except AkamaiClientException as e: + print ("record get test failed") + print (e.message) + + else: + print("record get test result: ") + print(json.dumps(get.json(), indent=4, separators=(',', ': '))) + + return + + def _test_record_delete(self, zone_name, record_name, record_type): + + try: + delete = self._dns_client.record_delete(zone_name, record_name, record_type) + except AkamaiClientException as e: + print("delete failed") + print(e.message) + return + + + try: + self._dns_client.record_get(zone_name, record_name, record_type) + except AkamaiClientException as e: + print("get on record failed as expected, since record was succesfully deleted") + print ("(Probably Ignore):", e.message) + print ("delete status:", delete.status_code) + else: + print("unexpected condition in test delete") + + return + + def _test_record_create(self, zone_name, record_name, record_type, params): + + try: + create = self._dns_client.record_create(zone_name, record_name, record_type, params) + except AkamaiClientException as e: + print ("create unsuccessful, presumably because it already exists") + print ("(Probably Ignore)", e.message) + else: + print("initial create of", create.json().get("name"), "succesful: ", create.status_code) + + return + + def _test_record_replace(self, zone_name, record_name, record_type, params): + + ## create record to be replaced, if it doesn't already exist + try: + old_params = { + "name": record_name, + "type": record_type, + "ttl": 300, + "rdata": [ + "10.0.0.2", + "10.0.0.3" + ] + } + create = self._dns_client.record_create(zone_name, record_name, record_type, old_params) + except AkamaiClientException as e: + print ("initial create unsuccessful, presumably because it already exists") + print ("(Probably Ignore)", e.message) + else: + print("initial create of record to be replaced", create.json().get("name"), "succesful: ", create.status_code) + + + ## test replace + try: + replace = self._dns_client.record_replace(zone_name, record_name, record_type, params) + except AkamaiClientException as e: + print("replace failed") + print(e.message) + return + else: + try: + record = self._dns_client.record_get(zone_name, record_name, record_type) + except AkamaiClientException as e: + print("retrieval in replacement failed") + print(e.message) + else: + new_data = record.json() + + if (new_data != params): + print("replace failed, records don't match") + print("current data:") + print(new_data) + print("expected data:") + print(params) + + else: + print("replace succesful") + print("replace status:", replace.status_code) + + def _test_zones_get(self): + try: + zonesList = self._dns_client.zones_get() + except AkamaiClientException as e: + print ("zones get test failed") + print (e.message) + + else: + print("zones list: ") + print(json.dumps(zonesList.json(), indent=4, separators=(',', ': '))) + + return + + def _test_zones_recordset_get(self, zone_name): + try: + zoneRecordset = self._dns_client.zone_recordset_get(zone_name) + except AkamaiClientException as e: + print("zone recordset retrieval test failed") + print (e.message) + else: + print("zone recordset: ") + print(json.dumps(zoneRecordset.json(), indent=4, separators=(',', ': '))) + return + + def _test_master_zone_file_get(self, zone_name): + try: + mzf = self._dns_client.master_zone_file_get(zone_name) + + except AkamaiClientException as e: + print("MZF retrieval test failed") + print (e.message) + + else: + print("Master Zone File:") + print(json.dumps(mzf.json(), indent=4, separators=(',', ': '))) + + return From 6db6052d28fc10afa861c3b83f9f82160d393b8b Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Mon, 17 Jun 2019 10:24:34 -0700 Subject: [PATCH 32/64] friday sync --- octodns/provider/fastdns.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index bcb1f32..686c81c 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -247,26 +247,24 @@ class AkamaiProvider(BaseProvider): - fileName = "values_for_" + zone.name +".json" - path = "/mnt/c/Users/bajamil/Desktop/" + fileName - f = open(path, 'w') - f.write(json.dumps(values, indent=4, separators=(',', ': '))) - f.close() - return ### before = len(zone.records) for name, types in values.items(): - for _type, records in types.items(): - data_for = getattr(self, '_data_for_{}'.format(_type)) - - data = data_for(_type, records) + # for _type, records in types.items(): + for _type in types.items(): + for rdata in _type['rdata']: - print() - print ("data processed for", name, ":") - print (json.dumps(data, indent=4, separators=(',', ': '))) - print() - record = Record.new(zone, name, data_for(_type, records), source=self, lenient=lenient) + data_for = getattr(self, '_data_for_{}'.format(_type)) + + data = data_for(_type, records, rdata) + + print() + print ("data processed for", name, ":") + print (json.dumps(data, indent=4, separators=(',', ': '))) + print() + + record = Record.new(zone, name, data_for(_type, records), source=self, lenient=lenient) exists = zone.name in self._zone_records From 67f7af58e632345570f8bd2b854dd82c4797905f Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Mon, 17 Jun 2019 10:24:58 -0700 Subject: [PATCH 33/64] friday sync --- octodns/provider/fastdns.py | 1 - 1 file changed, 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 686c81c..0874709 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -235,7 +235,6 @@ class AkamaiProvider(BaseProvider): # self._test(zone) values = defaultdict(lambda: defaultdict(list)) - for record in self.zone_records(zone): _type =record.get('type') _name = record.get('name') From dfd6e8cfdaf051b9e1122f4146c449e967d85a88 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Tue, 18 Jun 2019 14:43:38 -0400 Subject: [PATCH 34/64] Completed populate + helper functions --- .vscode/settings.json | 3 + octodns/provider/fastdns.py | 233 +++++++++++++----------------------- 2 files changed, 84 insertions(+), 152 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..38e6c1e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "C:\\Python27\\python.exe" +} \ No newline at end of file diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 0874709..bdd3b82 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -16,7 +16,6 @@ import logging from ..record import Record from .base import BaseProvider -TESTING = True class AkamaiClientException(Exception): @@ -37,10 +36,6 @@ class AkamaiClientException(Exception): super(AkamaiClientException, self).__init__(message) -class _AkamaiRecord(object): - pass - - class AkamaiClient(object): def __init__(self, _client_secret, _host, _access_token, _client_token): @@ -57,45 +52,20 @@ class AkamaiClient(object): self._sess = sess - def _writePrepped(self, prepped): - filename = str(prepped.method) + ".json" - f = open(filename, 'w') - - f.write("headers: \n") - f.write(str(prepped.headers)) - f.write("\nbody: \n") - f.write(str(prepped.body)) - f.write("\nhooks:\n") - f.write(str(prepped.hooks)) - f.write("\nmethod: " + str(prepped.method)) - f.write("\nurl: " + str(prepped.url) + "\n") - - f.close() - def _request(self, method, path, params=None, data=None, v1=False): url = urljoin(self.base, path) if v1: url = urljoin(self.basev1, path) - if (TESTING): - print("testing mode") - print(method, url) - req = requests.Request(method, url, params=params, json=data) - prepped = req.prepare() - self._writePrepped(prepped) - resp = self._sess.request(method, url, params=params, json=data) - if resp.status_code > 299: raise AkamaiClientException(resp.status_code) - resp.raise_for_status() - return resp - + return resp def record_get(self, zone, name, record_type): @@ -146,7 +116,6 @@ class AkamaiClient(object): def zone_recordset_get(self, zone, page=None, pageSize=30, search=None, showAll="true", sortBy="name", types=None): - params = { 'page': page, 'pageSize': pageSize, @@ -193,8 +162,7 @@ class AkamaiProvider(BaseProvider): SUPPORTS = set(('A', 'AAAA', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', 'SRV', 'SSHFP', 'TXT')) - # SUPPORTS = set(('NAPTR', 'MX')) - + def __init__(self, id, client_secret, host, access_token, client_token, *args, **kwargs): @@ -210,7 +178,7 @@ class AkamaiProvider(BaseProvider): def zone_records(self, zone): """ returns records for a zone, finds it if not present, or - returns empty if doesnt exist + returns empty if can't find a match """ if zone.name not in self._zone_records: try: @@ -220,184 +188,145 @@ class AkamaiProvider(BaseProvider): except AkamaiClientException: return [] - - # fileName = "zone_records_for_" + zone.name +".json" - # path = "/mnt/c/Users/bajamil/Desktop/" + fileName - # f = open(path, 'w') - # f.write(json.dumps(self._zone_records[zone.name], indent=4, separators=(',', ': '))) - # f.close() - return self._zone_records[zone.name] - def populate(self, zone, target=False, lenient=False): self.log.debug('populate: name=%s', zone.name) - # self._test(zone) values = defaultdict(lambda: defaultdict(list)) - + for record in self.zone_records(zone): + _type =record.get('type') - _name = record.get('name') + ## Akamai sends down prefix.zonename., while OctoDNS only 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(): - for _type in types.items(): - for rdata in _type['rdata']: - - - data_for = getattr(self, '_data_for_{}'.format(_type)) - - data = data_for(_type, records, rdata) - - print() - print ("data processed for", name, ":") - print (json.dumps(data, indent=4, separators=(',', ': '))) - print() - - record = Record.new(zone, name, data_for(_type, records), source=self, lenient=lenient) - - - exists = zone.name in self._zone_records - self.log.info('populate: found %s records, exists=%s', - len(zone.records) - before, exists) + 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) - return exists + exists = zone.name in self._zone_records + found = len(zone.records) - before + self.log.info('populate: found %s records, exists=%s', found, exists) - def _data_for_multiple(self, _type, records): + def _data_for_multiple(self, _type, _records): return { - 'ttl': records[0]['ttl'], - 'type': _type, - 'values': [r['rdata'][0] for r in records] - } + '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): - record = records[0] - value = record['rdata'][0] - if (value[-1] != "."): + def _data_for_CNAME(self, _type, _records): + value = _records['rdata'][0] + if (value[-1] != '.') : value = '{}.'.format(value) - + return { - 'ttl': records[0]['ttl'], + 'ttl': _records['ttl'], 'type': _type, 'value': value } - def _data_for_MX(self, _type, records): + def _data_for_MX(self, _type, _records): values = [] - for record in records: - print ("MX record: ", record) + for r in _records['rdata']: + preference, exchange = r.split(" ", 1) values.append({ - 'preference': record['priority'], - 'exchange': '{}.'.format(record['rdata'][0]) - }) + 'preference': preference, + 'exchange' : exchange + }) return { - 'ttl': records[0]['ttl'], + 'ttl': _records['ttl'], 'type': _type, 'values': values } - def _data_for_NAPTR(self, _type, records): - print ("NAPTR Record") - print(json.dumps(records, indent=4, separators=(',', ': '))) + def _data_for_NAPTR(self, _type, _records): values = [] - for record in records: - order, preference, flags, service, regexp, replacement = \ - record['rdata'][0].split(' ', 5) - + 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': replacement, + 'replacement': repl, 'service': service[1:-1] }) return { 'type': _type, - 'ttl': records[0]['ttl'], + 'ttl': _records['ttl'], 'values': values } - - def _data_for_NS(self, _type, records): - values = [] - for record in records: - rdata = record['rdata'] - for content in rdata: - if content[-1] != '.': - content = '{}.'.format(content) - values.append(content) - return { - 'ttl': records[0]['ttl'], - 'type': _type, - 'value': values - } - - def _data_for_PTR(self, _type, records): - return self._data_for_multiple(_type, records[0]) - # rdata[0]? - def _data_for_SRV(self, _type, records): + 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 record in records: - weight, port, target = record['rdata'].split(', ', 2) + for r in _records['rdata']: + priority, weight, port, target = r.split(' ', 3) values.append({ 'port': port, - 'priority': record['priority'], - 'target': '{}.'.format(target), + 'priority': priority, + 'target': target, 'weight': weight }) return { 'type': _type, - 'ttl': records[0]['ttl'], + 'ttl': _records['ttl'], 'values': values } - # rdata[0]? - def _data_for_SSHFP(self, _type, records): + def _data_for_SSHFP(self, _type, _records): values = [] - algorithm, fp_type, fingerprint = record['rdata'].split(' ', 2) - values.append({ - 'algorithm': algorithm, - 'fingerprint': fingerprint, - 'fingerprint_type': fp_type - }) - + for r in _records['rdata']: + algorithm, fp_type, fingerprint = r.split(' ', 2) + values.append({ + 'algorithm': algorithm, + 'fingerprint': fingerprint, + 'fingerprint_type': fp_type + }) return { 'type': _type, - 'ttl': records[0]['ttl'], + 'ttl': _records['ttl'], 'values': values } - def _data_for_TXT(self, _type, records): + def _data_for_TXT(self, _type, _records): values = [] - for r in records: - for content in r['rdata']: - values.append(content.replace(';', '\\;')) + for r in _records['rdata']: + r = r[1:-1] + values.append(r.replace(';', '\\;')) return { - 'ttl': records[0]['ttl'], + 'ttl': _records['ttl'], 'type': _type, 'values': values - # 'values': [r['rdata'][0].replace(';', '\\;') for r in records] } - - def _test(self, zone) : @@ -426,23 +355,23 @@ class AkamaiProvider(BaseProvider): } - # print("\n\nRunning test: record get..........\n") - # self._test_record_get(zone_name, "test.basir-test.com", record_type) - # print("\n\nRunning test: record create..........\n") - # self._test_record_create(zone_name, record_name, record_type, params) - # print("\n\nRunning test: record replace..........\n") - # self._test_record_replace(zone_name, record_name, record_type, repl_params) - # print("\n\nRunning test: record delete..........\n") - # self._test_record_delete(zone_name, record_name, record_type) + print("\n\nRunning test: record get..........\n") + self._test_record_get(zone_name, "test.basir-test.com", record_type) + print("\n\nRunning test: record create..........\n") + self._test_record_create(zone_name, record_name, record_type, params) + print("\n\nRunning test: record replace..........\n") + self._test_record_replace(zone_name, record_name, record_type, repl_params) + print("\n\nRunning test: record delete..........\n") + self._test_record_delete(zone_name, record_name, record_type) - # print("\n\nRunning test: zones get..........\n") - # self._test_zones_get() + print("\n\nRunning test: zones get..........\n") + self._test_zones_get() print("\n\nRunning test: zone recordset get..........\n") self._test_zones_recordset_get(zone_name) - # print("\n\nRunning test: Master Zone File get..........\n") - # self._test_master_zone_file_get(zone_name) + print("\n\nRunning test: Master Zone File get..........\n") + self._test_master_zone_file_get(zone_name) return From c87967a239f748ed72f04232cd0f23ffecc01fe9 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Tue, 18 Jun 2019 18:16:15 -0400 Subject: [PATCH 35/64] tuesday sync --- octodns/provider/fastdns.py | 44 +++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index bdd3b82..99a0b93 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -95,6 +95,23 @@ class AkamaiClient(object): 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 zones_get(self, contractIds=None, page=None, pageSize=None, search=None, showAll="true", sortBy="zone", types=None): path = 'zones' @@ -164,7 +181,7 @@ class AkamaiProvider(BaseProvider): 'SRV', 'SSHFP', 'TXT')) def __init__(self, id, client_secret, host, access_token, client_token, - *args, **kwargs): + contractId=None, gid=None, *args, **kwargs): self.log = logging.getLogger('AkamaiProvider[{}]'.format(id)) self.log.debug('__init__: id=%s, ') @@ -174,7 +191,8 @@ class AkamaiProvider(BaseProvider): client_token) self._zone_records = {} - + self._contractId = contractId + self._gid = gid def zone_records(self, zone): """ returns records for a zone, finds it if not present, or @@ -218,6 +236,7 @@ class AkamaiProvider(BaseProvider): found = len(zone.records) - before self.log.info('populate: found %s records, exists=%s', found, exists) + return exists def _data_for_multiple(self, _type, _records): @@ -328,6 +347,27 @@ class AkamaiProvider(BaseProvider): } + def _apply(self, plan): + desired = plan.desired + changes = plan.changes + self.log.debug('_apply: zone=%s, len(changes)=%d', desired.name, + len(changes)) + + domain_name = desired.name[:-1] + + + try: + self._dns_client.zone_get(domain_name) + except AkamaiClientException: + self.log.debug('_apply: no matching zone, creating domain') + + params = self._build_zone_config(domain_name) + + self._dns_client.zone_create(self._contractId, params, self._gid) + + + def _build_zone_config(self, zone, type="primary", comment=None, + def _test(self, zone) : zone_name = zone.name[:len(zone.name)-1] From c1528e3221e25c4e0d053d93d8bffb71a48d5190 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Wed, 19 Jun 2019 19:21:28 -0400 Subject: [PATCH 36/64] implemented _apply_delete, other misc. fixes --- octodns/provider/fastdns.py | 176 +++++++++++++++++++++++++++++------- 1 file changed, 141 insertions(+), 35 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 99a0b93..6306145 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -21,6 +21,7 @@ class AkamaiClientException(Exception): _errorMessages = { 400: "400: Bad request", + 401: "401: Unauthorized", 403: "403: Access is forbidden", 404: "404: Resource not found", 405: "405: Method not supported", @@ -42,6 +43,7 @@ class AkamaiClient(object): self.base = "https://" + _host + "/config-dns/v2/" self.basev1 = "https://" + _host + "/config-dns/v1/" + self.basehost = "https://" + _host sess = requests.Session() sess.auth = EdgeGridAuth( @@ -61,12 +63,14 @@ class AkamaiClient(object): resp = self._sess.request(method, url, params=params, json=data) if resp.status_code > 299: + # print(resp.status_code) raise AkamaiClientException(resp.status_code) resp.raise_for_status() return resp + def record_get(self, zone, name, record_type): path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) @@ -82,6 +86,7 @@ class AkamaiClient(object): def record_delete(self, zone, name, record_type): path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) + print(path) result = self._request('DELETE', path) if result.status_code == 204: @@ -95,6 +100,7 @@ class AkamaiClient(object): return result + def zone_get(self, zone): path = 'zones/{}'.format(zone) result = self._request('GET', path) @@ -148,7 +154,19 @@ class AkamaiClient(object): return result - def records(self, zone_name): + + def contracts_get(self, gid=None): + path = 'data/contracts' + if gid is not None: + path += '?gid={}'.format(gid) + + # result = self._sess.request('GET', path) + + result = self._request('GET', path) + + return result + + def recordsets_get(self, zone_name): resp = self.zone_recordset_get(zone_name, showAll="true") recordset = resp.json().get("recordsets") @@ -181,7 +199,7 @@ class AkamaiProvider(BaseProvider): 'SRV', 'SSHFP', 'TXT')) def __init__(self, id, client_secret, host, access_token, client_token, - contractId=None, gid=None, *args, **kwargs): + contract_id=None, gid=None, *args, **kwargs): self.log = logging.getLogger('AkamaiProvider[{}]'.format(id)) self.log.debug('__init__: id=%s, ') @@ -191,7 +209,7 @@ class AkamaiProvider(BaseProvider): client_token) self._zone_records = {} - self._contractId = contractId + self._contractId = contract_id self._gid = gid def zone_records(self, zone): @@ -201,7 +219,7 @@ class AkamaiProvider(BaseProvider): if zone.name not in self._zone_records: try: name = zone.name[:-1] - self._zone_records[zone.name] = self._dns_client.records(name) + self._zone_records[zone.name] = self._dns_client.recordsets_get(name) except AkamaiClientException: return [] @@ -238,12 +256,12 @@ class AkamaiProvider(BaseProvider): return exists - def _data_for_multiple(self, _type, _records): + def _data_for_multiple(self, _type, records): return { - 'ttl': _records['ttl'], + 'ttl':records['ttl'], 'type': _type, - 'values': [r for r in _records['rdata']] + 'values': [r for r in records['rdata']] } _data_for_A = _data_for_multiple @@ -251,34 +269,34 @@ class AkamaiProvider(BaseProvider): _data_for_NS = _data_for_multiple _data_for_SPF = _data_for_multiple - def _data_for_CNAME(self, _type, _records): - value = _records['rdata'][0] + def _data_for_CNAME(self, _type, records): + value =records['rdata'][0] if (value[-1] != '.') : value = '{}.'.format(value) return { - 'ttl': _records['ttl'], + 'ttl':records['ttl'], 'type': _type, 'value': value } - def _data_for_MX(self, _type, _records): + def _data_for_MX(self, _type, records): values = [] - for r in _records['rdata']: + for r in records['rdata']: preference, exchange = r.split(" ", 1) values.append({ 'preference': preference, 'exchange' : exchange }) return { - 'ttl': _records['ttl'], + 'ttl':records['ttl'], 'type': _type, 'values': values } - def _data_for_NAPTR(self, _type, _records): + def _data_for_NAPTR(self, _type, records): values = [] - for r in _records['rdata']: + for r in records['rdata']: order, preference, flags, service, regexp, repl = r.split(' ', 5) values.append({ @@ -291,21 +309,21 @@ class AkamaiProvider(BaseProvider): }) return { 'type': _type, - 'ttl': _records['ttl'], + 'ttl':records['ttl'], 'values': values } - def _data_for_PTR(self, _type, _records): + def _data_for_PTR(self, _type, records): return { - 'ttl': _records['ttl'], + 'ttl':records['ttl'], 'type': _type, - 'value' : _records['rdata'][0] + 'value' :records['rdata'][0] } - def _data_for_SRV(self, _type, _records): + def _data_for_SRV(self, _type, records): values = [] - for r in _records['rdata']: + for r in records['rdata']: priority, weight, port, target = r.split(' ', 3) values.append({ 'port': port, @@ -315,13 +333,13 @@ class AkamaiProvider(BaseProvider): }) return { 'type': _type, - 'ttl': _records['ttl'], + 'ttl':records['ttl'], 'values': values } - def _data_for_SSHFP(self, _type, _records): + def _data_for_SSHFP(self, _type, records): values = [] - for r in _records['rdata']: + for r in records['rdata']: algorithm, fp_type, fingerprint = r.split(' ', 2) values.append({ 'algorithm': algorithm, @@ -330,18 +348,18 @@ class AkamaiProvider(BaseProvider): }) return { 'type': _type, - 'ttl': _records['ttl'], + 'ttl': records['ttl'], 'values': values } - def _data_for_TXT(self, _type, _records): + def _data_for_TXT(self, _type, records): values = [] - for r in _records['rdata']: + for r in records['rdata']: r = r[1:-1] values.append(r.replace(';', '\\;')) return { - 'ttl': _records['ttl'], + 'ttl': records['ttl'], 'type': _type, 'values': values } @@ -353,20 +371,108 @@ class AkamaiProvider(BaseProvider): self.log.debug('_apply: zone=%s, len(changes)=%d', desired.name, len(changes)) - domain_name = desired.name[:-1] + zone_name = desired.name[:-1] + print(zone_name) try: - self._dns_client.zone_get(domain_name) - except AkamaiClientException: - self.log.debug('_apply: no matching zone, creating domain') - - params = self._build_zone_config(domain_name) + self._dns_client.zone_get(zone_name) + except AkamaiClientException as e: + + print("zone not found, creating zone") + self.log.debug('_apply: no matching zone, 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__ + print() + print(class_name) + if (class_name == "Delete" ): + print(change.existing.name) + print(change.existing._type) + print(change.existing.data) + elif class_name == "Update": + print(change.existing.name) + print(change.existing._type) + print(change.existing.data) + print("----------------->") + print(change.new.name) + print(change.new._type) + print(change.new.data) + else: + print(change.new.name) + print(change.new._type) + print(change.new.data) + print() + print(change) + print() + 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 + params_for = getattr(self, '_params_for_{}'.format(new._type)) + for params in params_for(new): + pass + # self._dns_client.record_create(new.zone.name[:-1], params) + + def _apply_Delete(self, change): + existing = change.existing + zone = existing.zone.name[:-1] + name = existing.name + '.' + zone + record_type = existing._type + + result = self._dns_client.record_delete(zone, name, record_type) + + return result + + def _build_zone_config(self, zone, _type=None, comment=None, masters=[]): + + if _type is None: + _type="primary" - def _build_zone_config(self, zone, type="primary", comment=None, + if self._contractId is None: + self._set_default_contractId() + + return { + "zone": zone, + "type": _type, + "comment": comment, + "masters": masters + } + + def _set_default_contractId(self): + ''' if no contractId is set, but one is required to create a new zone, + this function will try to retrieve any contracts available to the + user, and use the first one it finds + ''' + + try: + + request = self._dns_client.zones_get(self._gid) + response = request.json() + zones = response['zones'] + + contractId = zones[0]['contractId'] + + + self._contractId = contractId + self.log.info("contractId not specified, using contractId=%s", contractId) + + except KeyError: + self.log.debug("_get_default_contractId: key error") + raise + + except: + self.log.debug("_get_default_contractId: unable to find a contractId") + raise + + return + def _test(self, zone) : From a7fb63623e1eaa0f057bb08d730089f81dea1ab1 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 20 Jun 2019 19:43:40 -0400 Subject: [PATCH 37/64] fixed apply_delete, create, and update. need to impl param_for_ fxns --- octodns/provider/fastdns.py | 159 ++++++++++++++++++++++++++---------- 1 file changed, 115 insertions(+), 44 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 6306145..42bf23b 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -78,9 +78,9 @@ class AkamaiClient(object): return result - def record_create(self, zone, name, record_type, params): + def record_create(self, zone, name, record_type, content): path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('POST', path, data=params) + result = self._request('POST', path, data=content) return result @@ -94,9 +94,9 @@ class AkamaiClient(object): return result - def record_replace(self, zone, name, record_type, params): + def record_replace(self, zone, name, record_type, content): path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('PUT', path, data=params) + result = self._request('PUT', path, data=content) return result @@ -368,67 +368,122 @@ class AkamaiProvider(BaseProvider): def _apply(self, plan): desired = plan.desired changes = plan.changes - self.log.debug('_apply: zone=%s, len(changes)=%d', desired.name, - len(changes)) + self.log.debug('_apply: zone=%s, changes=%d', desired.name, len(changes)) zone_name = desired.name[:-1] - print(zone_name) - - try: self._dns_client.zone_get(zone_name) - except AkamaiClientException as e: - - print("zone not found, creating zone") - self.log.debug('_apply: no matching zone, creating zone') + except AkamaiClientException: + 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__ - print() - print(class_name) - if (class_name == "Delete" ): - print(change.existing.name) - print(change.existing._type) - print(change.existing.data) - elif class_name == "Update": - print(change.existing.name) - print(change.existing._type) - print(change.existing.data) - print("----------------->") - print(change.new.name) - print(change.new._type) - print(change.new.data) - else: - print(change.new.name) - print(change.new._type) - print(change.new.data) - print() - print(change) - print() 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 - params_for = getattr(self, '_params_for_{}'.format(new._type)) - for params in params_for(new): - pass - # self._dns_client.record_create(new.zone.name[:-1], params) + 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 + } + + print() + print ("mock create") + print ("zone=", zone, "name=", name , "record_type=", record_type) + print("content:") + print(json.dumps(content, indent=4, separators=(',', ': '))) + print() + + return + self._dns_client.record_create(zone, name, record_type, content) + + return def _apply_Delete(self, change): - existing = change.existing - zone = existing.zone.name[:-1] - name = existing.name + '.' + zone - record_type = existing._type - result = self._dns_client.record_delete(zone, name, record_type) + 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)) + rdata = params_for(new.data) + + 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 _params_for_multiple(self, values): + + rdata = [r for r in values] + + return rdata + + + _params_for_A = _params_for_multiple + _params_for_AAAA = _params_for_multiple + _params_for_NS = _params_for_multiple + _params_for_SPF = _params_for_multiple + + def _params_for_CNAME(self, values): + pass + + def _params_for_MX(self, values): + pass + + def _params_for_NAPTR(self, values): + pass + + def _params_for_PTR(self, values): + pass + + def _params_for_SRV(self, values): + pass + + def _params_for_SSHFP(self, values): + pass + + def _params_for_TXT(self, values): + pass - return result def _build_zone_config(self, zone, _type=None, comment=None, masters=[]): @@ -473,6 +528,22 @@ class AkamaiProvider(BaseProvider): return + 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 + if (name[0] == '.'): ## octodns's name for root is '' + name = name[1:] + + return name + def _test(self, zone) : From 0eb1f97ae39af8acce6145624a8e01cf801af921 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 20 Jun 2019 20:21:35 -0400 Subject: [PATCH 38/64] started implementing _params_for fxns --- octodns/provider/fastdns.py | 58 +++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 42bf23b..cf19c79 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -396,7 +396,6 @@ class AkamaiProvider(BaseProvider): rdata = params_for(values) zone = new.zone.name[:-1] - name = self._set_full_name(new.name, zone) content = { @@ -434,7 +433,8 @@ class AkamaiProvider(BaseProvider): record_type = new._type params_for = getattr(self, '_params_for_{}'.format(record_type)) - rdata = params_for(new.data) + values = self._get_values(new.data) + rdata = params_for(values) zone = new.zone.name[:-1] name = self._set_full_name(new.name, zone) @@ -446,34 +446,58 @@ class AkamaiProvider(BaseProvider): "rdata" : rdata } - self._dns_client.record_create(zone, name, record_type, content) + self._dns_client.record_replace(zone, name, record_type, content) return def _params_for_multiple(self, values): - - rdata = [r for r in values] - - return rdata + return [r for r in values] + def _params_for_single(self, values): + return values[0] _params_for_A = _params_for_multiple _params_for_AAAA = _params_for_multiple _params_for_NS = _params_for_multiple _params_for_SPF = _params_for_multiple - def _params_for_CNAME(self, values): - pass - def _params_for_MX(self, values): - pass + _params_for_CNAME = _params_for_single + _params_for_PTR = _params_for_single - def _params_for_NAPTR(self, values): - pass - def _params_for_PTR(self, values): - pass + 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: + order = r.order + preference = r.preference + flags = "\"" + r.flags + "\"" + service = "\"" + r.service + "\"" + regexp = "\"" + r.regexp + "\"" + repl = r.replacement + + record = '{} {} {} {} {}'.format(order, preference, flags, service, + regexp, repl) + # record = ' '.join([order, preference, flags, service, regexp, repl]) + rdata.append(record) + + return rdata + def _params_for_SRV(self, values): pass @@ -485,6 +509,7 @@ class AkamaiProvider(BaseProvider): pass + def _build_zone_config(self, zone, _type=None, comment=None, masters=[]): if _type is None: @@ -545,6 +570,9 @@ class AkamaiProvider(BaseProvider): return name + + + def _test(self, zone) : zone_name = zone.name[:len(zone.name)-1] From 3edf6969f8e44711c3c6cc72da91bc31c5049a79 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Fri, 21 Jun 2019 13:48:17 -0400 Subject: [PATCH 39/64] finished implementing applys, MVP reached --- octodns/provider/fastdns.py | 72 +++++++++++++++++++++---------------- ta | 0 targets=[azuredns, | 0 3 files changed, 41 insertions(+), 31 deletions(-) create mode 100644 ta create mode 100644 targets=[azuredns, diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index cf19c79..745fc34 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -26,7 +26,7 @@ class AkamaiClientException(Exception): 404: "404: Resource not found", 405: "405: Method not supported", 406: "406: Not Acceptable", - 409: "409: Request not allowed due to conflict with current state of resource", + 409: "409: Request not allowed due to conflict with current state", 415: "415: Unsupported media type", 422: "422: Request body contains an error preventing processing", 500: "500: Internal server error" @@ -43,7 +43,6 @@ class AkamaiClient(object): self.base = "https://" + _host + "/config-dns/v2/" self.basev1 = "https://" + _host + "/config-dns/v1/" - self.basehost = "https://" + _host sess = requests.Session() sess.auth = EdgeGridAuth( @@ -63,11 +62,9 @@ class AkamaiClient(object): resp = self._sess.request(method, url, params=params, json=data) if resp.status_code > 299: - # print(resp.status_code) raise AkamaiClientException(resp.status_code) resp.raise_for_status() - return resp @@ -160,8 +157,6 @@ class AkamaiClient(object): if gid is not None: path += '?gid={}'.format(gid) - # result = self._sess.request('GET', path) - result = self._request('GET', path) return result @@ -405,14 +400,6 @@ class AkamaiProvider(BaseProvider): "rdata" : rdata } - print() - print ("mock create") - print ("zone=", zone, "name=", name , "record_type=", record_type) - print("content:") - print(json.dumps(content, indent=4, separators=(',', ': '))) - print() - - return self._dns_client.record_create(zone, name, record_type, content) return @@ -455,24 +442,23 @@ class AkamaiProvider(BaseProvider): return [r for r in values] def _params_for_single(self, values): - return values[0] + return values _params_for_A = _params_for_multiple _params_for_AAAA = _params_for_multiple _params_for_NS = _params_for_multiple _params_for_SPF = _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 + print(json.dumps(r, indent=4, separators=(',', ': '))) + preference = r['preference'] + exchange = r['exchange'] record = '{} {}'.format(preference, exchange) @@ -484,29 +470,53 @@ class AkamaiProvider(BaseProvider): rdata = [] for r in values: - order = r.order - preference = r.preference - flags = "\"" + r.flags + "\"" - service = "\"" + r.service + "\"" - regexp = "\"" + r.regexp + "\"" - repl = r.replacement + order = r['order'] + preference = r['preference'] + flags = "\"" + r['flags'] + "\"" + service = "\"" + r['service'] + "\"" + regexp = "\"" + r['regexp'] + "\"" + repl = r['replacement'] - record = '{} {} {} {} {}'.format(order, preference, flags, service, + record = '{} {} {} {} {} {}'.format(order, preference, flags, service, regexp, repl) # record = ' '.join([order, preference, flags, service, regexp, repl]) rdata.append(record) return rdata - def _params_for_SRV(self, values): - pass + 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): - pass + 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): - pass + rdata = [] + + for r in values: + txt = "\"" + r.replace('\\;', ';') + "\"" + rdata.append(txt) + + return rdata @@ -558,7 +568,7 @@ class AkamaiProvider(BaseProvider): try: vals = data['values'] except KeyError: - vals = data['value'] + vals = [data['value']] return vals diff --git a/ta b/ta new file mode 100644 index 0000000..e69de29 diff --git a/targets=[azuredns, b/targets=[azuredns, new file mode 100644 index 0000000..e69de29 From 8417a4475a8fa7d80a202a0e6a469654bb612731 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Fri, 21 Jun 2019 15:04:27 -0400 Subject: [PATCH 40/64] cleaned up code --- octodns/provider/fastdns.py | 537 +++++++++++------------------------- 1 file changed, 162 insertions(+), 375 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 745fc34..4d6f1cd 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -5,11 +5,9 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals -## octodns specfic imports: import requests from akamai.edgegrid import EdgeGridAuth from urlparse import urljoin -import json from collections import defaultdict import logging @@ -32,17 +30,30 @@ class AkamaiClientException(Exception): 500: "500: Internal server error" } - def __init__(self, code): - message = self._errorMessages.get(code) - super(AkamaiClientException, self).__init__(message) + def __init__(self, resp): + try: + message = self._errorMessages.get(resp.status_code) + super(AkamaiClientException, self).__init__(message) + + except: + resp.raise_for_status() 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/" - self.basev1 = "https://" + _host + "/config-dns/v1/" sess = requests.Session() sess.auth = EdgeGridAuth( @@ -56,25 +67,14 @@ class AkamaiClient(object): def _request(self, method, path, params=None, data=None, v1=False): url = urljoin(self.base, path) - if v1: - url = urljoin(self.basev1, path) - resp = self._sess.request(method, url, params=params, json=data) if resp.status_code > 299: - raise AkamaiClientException(resp.status_code) - resp.raise_for_status() + raise AkamaiClientException(resp) return resp - def record_get(self, zone, name, record_type): - - path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - result = self._request('GET', path) - - return result - 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) @@ -83,12 +83,8 @@ class AkamaiClient(object): def record_delete(self, zone, name, record_type): path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) - print(path) result = self._request('DELETE', path) - if result.status_code == 204: - print ("successfully deleted ", path) - return result def record_replace(self, zone, name, record_type, content): @@ -115,24 +111,6 @@ class AkamaiClient(object): return result - def zones_get(self, contractIds=None, page=None, pageSize=None, search=None, - showAll="true", sortBy="zone", types=None): - path = 'zones' - - params = { - 'contractIds': contractIds, - 'page': page, - 'pageSize': pageSize, - 'search': search, - 'showAll': showAll, - 'sortBy': sortBy, - 'types': types - } - - result = self._request('GET', path, params=params) - - return result - def zone_recordset_get(self, zone, page=None, pageSize=30, search=None, showAll="true", sortBy="name", types=None): @@ -151,16 +129,6 @@ class AkamaiClient(object): return result - - def contracts_get(self, gid=None): - path = 'data/contracts' - if gid is not None: - path += '?gid={}'.format(gid) - - result = self._request('GET', path) - - return result - def recordsets_get(self, zone_name): resp = self.zone_recordset_get(zone_name, showAll="true") @@ -168,25 +136,56 @@ class AkamaiClient(object): return recordset - def master_zone_file_get(self, zone): - - path = 'zones/{}/zone-file'.format(zone) - - try: - result = self._request('GET', path) - - except AkamaiClientException as e: - # not working with API v2, API v1 fallback - path = 'zones/{}'.format(zone) - result = self._request('GET', path, v1=True) - print("Using API v1 fallback") - print("(Probably Ignore)", e.message) - - 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 @@ -208,8 +207,8 @@ class AkamaiProvider(BaseProvider): self._gid = gid def zone_records(self, zone): - """ returns records for a zone, finds it if not present, or - returns empty if can't find a match + """ 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: @@ -228,7 +227,7 @@ class AkamaiProvider(BaseProvider): for record in self.zone_records(zone): _type =record.get('type') - ## Akamai sends down prefix.zonename., while OctoDNS only expects prefix + ## 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 / @ @@ -251,6 +250,85 @@ class AkamaiProvider(BaseProvider): return exists + + def _apply(self, plan): + desired = plan.desired + changes = plan.changes + self.log.debug('apply: zone=%s, changes=%d', desired.name, len(changes)) + + zone_name = desired.name[:-1] + try: + self._dns_client.zone_get(zone_name) + + except AkamaiClientException: + 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 { @@ -326,6 +404,7 @@ class AkamaiProvider(BaseProvider): 'target': target, 'weight': weight }) + return { 'type': _type, 'ttl':records['ttl'], @@ -341,6 +420,7 @@ class AkamaiProvider(BaseProvider): 'fingerprint': fingerprint, 'fingerprint_type': fp_type }) + return { 'type': _type, 'ttl': records['ttl'], @@ -358,86 +438,8 @@ class AkamaiProvider(BaseProvider): 'type': _type, 'values': values } - - - def _apply(self, plan): - desired = plan.desired - changes = plan.changes - self.log.debug('_apply: zone=%s, changes=%d', desired.name, len(changes)) - - zone_name = desired.name[:-1] - try: - self._dns_client.zone_get(zone_name) - - except AkamaiClientException: - 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 _params_for_multiple(self, values): return [r for r in values] @@ -456,12 +458,10 @@ class AkamaiProvider(BaseProvider): rdata = [] for r in values: - print(json.dumps(r, indent=4, separators=(',', ': '))) preference = r['preference'] exchange = r['exchange'] record = '{} {}'.format(preference, exchange) - rdata.append(record) return rdata @@ -470,16 +470,14 @@ class AkamaiProvider(BaseProvider): rdata = [] for r in values: - order = r['order'] - preference = r['preference'] - flags = "\"" + r['flags'] + "\"" - service = "\"" + r['service'] + "\"" - regexp = "\"" + r['regexp'] + "\"" - repl = r['replacement'] + ordr = r['order'] + pref = r['preference'] + flg = "\"" + r['flags'] + "\"" + srvc = "\"" + r['service'] + "\"" + rgx = "\"" + r['regexp'] + "\"" + rpl = r['replacement'] - record = '{} {} {} {} {} {}'.format(order, preference, flags, service, - regexp, repl) - # record = ' '.join([order, preference, flags, service, regexp, repl]) + record = '{} {} {} {} {} {}'.format(ordr, pref, flg, srvc, rgx, rpl) rdata.append(record) return rdata @@ -518,15 +516,13 @@ class AkamaiProvider(BaseProvider): return rdata - - def _build_zone_config(self, zone, _type=None, comment=None, masters=[]): if _type is None: _type="primary" if self._contractId is None: - self._set_default_contractId() + raise NameError("contractId not specified to create zone") return { "zone": zone, @@ -535,34 +531,6 @@ class AkamaiProvider(BaseProvider): "masters": masters } - def _set_default_contractId(self): - ''' if no contractId is set, but one is required to create a new zone, - this function will try to retrieve any contracts available to the - user, and use the first one it finds - ''' - - try: - - request = self._dns_client.zones_get(self._gid) - response = request.json() - zones = response['zones'] - - contractId = zones[0]['contractId'] - - - self._contractId = contractId - self.log.info("contractId not specified, using contractId=%s", contractId) - - except KeyError: - self.log.debug("_get_default_contractId: key error") - raise - - except: - self.log.debug("_get_default_contractId: unable to find a contractId") - raise - - return - def _get_values(self, data): try: @@ -574,190 +542,9 @@ class AkamaiProvider(BaseProvider): def _set_full_name(self, name, zone): name = name + '.' + zone - if (name[0] == '.'): ## octodns's name for root is '' + + ## octodns's name for root is '' + if (name[0] == '.'): name = name[1:] return name - - - - - - def _test(self, zone) : - - zone_name = zone.name[:len(zone.name)-1] - - record_name = "octo.basir-test.com" - record_type = "A" - params = { - "name": "octo.basir-test.com", - "type": "A", - "ttl": 300, - "rdata": [ - "10.0.0.2", - "10.0.0.3" - ] - } - repl_params = { - "name": "octo.basir-test.com", - "type": "A", - "ttl": 300, - "rdata": [ - "99.99.99.99", - "10.0.0.3", - "1.2.3.4" - ] - } - - - print("\n\nRunning test: record get..........\n") - self._test_record_get(zone_name, "test.basir-test.com", record_type) - print("\n\nRunning test: record create..........\n") - self._test_record_create(zone_name, record_name, record_type, params) - print("\n\nRunning test: record replace..........\n") - self._test_record_replace(zone_name, record_name, record_type, repl_params) - print("\n\nRunning test: record delete..........\n") - self._test_record_delete(zone_name, record_name, record_type) - - print("\n\nRunning test: zones get..........\n") - self._test_zones_get() - - print("\n\nRunning test: zone recordset get..........\n") - self._test_zones_recordset_get(zone_name) - - print("\n\nRunning test: Master Zone File get..........\n") - self._test_master_zone_file_get(zone_name) - - return - - def _test_record_get(self, zone_name, record_name, record_type): - try: - get = self._dns_client.record_get(zone_name, record_name, record_type) - except AkamaiClientException as e: - print ("record get test failed") - print (e.message) - - else: - print("record get test result: ") - print(json.dumps(get.json(), indent=4, separators=(',', ': '))) - - return - - def _test_record_delete(self, zone_name, record_name, record_type): - - try: - delete = self._dns_client.record_delete(zone_name, record_name, record_type) - except AkamaiClientException as e: - print("delete failed") - print(e.message) - return - - - try: - self._dns_client.record_get(zone_name, record_name, record_type) - except AkamaiClientException as e: - print("get on record failed as expected, since record was succesfully deleted") - print ("(Probably Ignore):", e.message) - print ("delete status:", delete.status_code) - else: - print("unexpected condition in test delete") - - return - - def _test_record_create(self, zone_name, record_name, record_type, params): - - try: - create = self._dns_client.record_create(zone_name, record_name, record_type, params) - except AkamaiClientException as e: - print ("create unsuccessful, presumably because it already exists") - print ("(Probably Ignore)", e.message) - else: - print("initial create of", create.json().get("name"), "succesful: ", create.status_code) - - return - - def _test_record_replace(self, zone_name, record_name, record_type, params): - - ## create record to be replaced, if it doesn't already exist - try: - old_params = { - "name": record_name, - "type": record_type, - "ttl": 300, - "rdata": [ - "10.0.0.2", - "10.0.0.3" - ] - } - create = self._dns_client.record_create(zone_name, record_name, record_type, old_params) - except AkamaiClientException as e: - print ("initial create unsuccessful, presumably because it already exists") - print ("(Probably Ignore)", e.message) - else: - print("initial create of record to be replaced", create.json().get("name"), "succesful: ", create.status_code) - - - ## test replace - try: - replace = self._dns_client.record_replace(zone_name, record_name, record_type, params) - except AkamaiClientException as e: - print("replace failed") - print(e.message) - return - else: - try: - record = self._dns_client.record_get(zone_name, record_name, record_type) - except AkamaiClientException as e: - print("retrieval in replacement failed") - print(e.message) - else: - new_data = record.json() - - if (new_data != params): - print("replace failed, records don't match") - print("current data:") - print(new_data) - print("expected data:") - print(params) - - else: - print("replace succesful") - print("replace status:", replace.status_code) - - def _test_zones_get(self): - try: - zonesList = self._dns_client.zones_get() - except AkamaiClientException as e: - print ("zones get test failed") - print (e.message) - - else: - print("zones list: ") - print(json.dumps(zonesList.json(), indent=4, separators=(',', ': '))) - - return - - def _test_zones_recordset_get(self, zone_name): - try: - zoneRecordset = self._dns_client.zone_recordset_get(zone_name) - except AkamaiClientException as e: - print("zone recordset retrieval test failed") - print (e.message) - else: - print("zone recordset: ") - print(json.dumps(zoneRecordset.json(), indent=4, separators=(',', ': '))) - return - - def _test_master_zone_file_get(self, zone_name): - try: - mzf = self._dns_client.master_zone_file_get(zone_name) - - except AkamaiClientException as e: - print("MZF retrieval test failed") - print (e.message) - - else: - print("Master Zone File:") - print(json.dumps(mzf.json(), indent=4, separators=(',', ': '))) - - return From 173d41aa9930346e8d92e1521e89ab3cf94cf795 Mon Sep 17 00:00:00 2001 From: basirjamil Date: Fri, 21 Jun 2019 15:06:14 -0400 Subject: [PATCH 41/64] Delete targets=[azuredns, --- targets=[azuredns, | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 targets=[azuredns, diff --git a/targets=[azuredns, b/targets=[azuredns, deleted file mode 100644 index e69de29..0000000 From c7251444c82d1562ac1d280f3e300cb337b3717f Mon Sep 17 00:00:00 2001 From: basirjamil Date: Fri, 21 Jun 2019 15:07:14 -0400 Subject: [PATCH 42/64] Delete ta --- ta | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ta diff --git a/ta b/ta deleted file mode 100644 index e69de29..0000000 From 2397030cf6d2e7c8d1698b1a66e888a3f34eef4d Mon Sep 17 00:00:00 2001 From: basirjamil Date: Fri, 21 Jun 2019 15:07:24 -0400 Subject: [PATCH 43/64] Delete settings.json --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 38e6c1e..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "python.pythonPath": "C:\\Python27\\python.exe" -} \ No newline at end of file From 6f4e60ac141fd53eb29f112f2b82238a5610d8de Mon Sep 17 00:00:00 2001 From: basirjamil Date: Fri, 21 Jun 2019 15:08:11 -0400 Subject: [PATCH 44/64] Delete settings.json --- octodns/.vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 octodns/.vscode/settings.json diff --git a/octodns/.vscode/settings.json b/octodns/.vscode/settings.json deleted file mode 100644 index 38e6c1e..0000000 --- a/octodns/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "python.pythonPath": "C:\\Python27\\python.exe" -} \ No newline at end of file From 70a1627757cb6eafd32596a3a35acd7626df68c5 Mon Sep 17 00:00:00 2001 From: basirjamil Date: Fri, 21 Jun 2019 15:08:43 -0400 Subject: [PATCH 45/64] Delete settings.json --- octodns/provider/.vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 octodns/provider/.vscode/settings.json diff --git a/octodns/provider/.vscode/settings.json b/octodns/provider/.vscode/settings.json deleted file mode 100644 index 88322cc..0000000 --- a/octodns/provider/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "python.pythonPath": "C:\\Python27\\python.exe" -} \ No newline at end of file From 5b143e422c2edbbb73d220baf428415b432698de Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Fri, 21 Jun 2019 17:26:54 -0400 Subject: [PATCH 46/64] cleanup - fixed lint issues --- octodns/provider/fastdns.py | 178 +++++++++++++++++------------------- 1 file changed, 85 insertions(+), 93 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 4d6f1cd..26c4351 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -12,13 +12,13 @@ from collections import defaultdict import logging from ..record import Record -from .base import BaseProvider +from .base import BaseProvider class AkamaiClientException(Exception): - + _errorMessages = { - 400: "400: Bad request", + 400: "400: Bad request", 401: "401: Unauthorized", 403: "403: Access is forbidden", 404: "404: Resource not found", @@ -35,15 +35,15 @@ class AkamaiClientException(Exception): message = self._errorMessages.get(resp.status_code) super(AkamaiClientException, self).__init__(message) - except: + except: resp.raise_for_status() class AkamaiClient(object): - ''' + ''' Client for making calls to Akamai Fast DNS API using Python Requests - Fast DNS Zone Management API V2, found here: + 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: @@ -63,9 +63,8 @@ class AkamaiClient(object): ) 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) @@ -73,7 +72,6 @@ class AkamaiClient(object): raise AkamaiClientException(resp) return resp - def record_create(self, zone, name, record_type, content): path = 'zones/{}/names/{}/types/{}'.format(zone, name, record_type) @@ -84,7 +82,7 @@ class AkamaiClient(object): 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): @@ -93,7 +91,6 @@ class AkamaiClient(object): return result - def zone_get(self, zone): path = 'zones/{}'.format(zone) result = self._request('GET', path) @@ -105,17 +102,16 @@ class AkamaiClient(object): 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=30, search=None, - showAll="true", sortBy="name", types=None): + showAll="true", sortBy="name", types=None): params = { - 'page': page, + 'page': page, 'pageSize': pageSize, 'search': search, 'showAll': showAll, @@ -126,11 +122,10 @@ class AkamaiClient(object): path = 'zones/{}/recordsets'.format(zone) result = self._request('GET', path, params=params) - return result def recordsets_get(self, zone_name): - + resp = self.zone_recordset_get(zone_name, showAll="true") recordset = resp.json().get("recordsets") @@ -170,8 +165,8 @@ class AkamaiProvider(BaseProvider): 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: + + 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. @@ -179,41 +174,41 @@ class AkamaiProvider(BaseProvider): 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. + 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', + 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): - + def __init__(self, id, client_secret, host, access_token, client_token, + contract_id=None, gid=None, *args, **kwargs): + self.log = logging.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._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 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] - self._zone_records[zone.name] = self._dns_client.recordsets_get(name) + self._zone_records[zone.name] = \ + self._dns_client.recordsets_get(name) except AkamaiClientException: return [] @@ -225,12 +220,12 @@ class AkamaiProvider(BaseProvider): 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 / @ + + _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 @@ -240,21 +235,20 @@ class AkamaiProvider(BaseProvider): 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) + 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) + 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, changes=%d', desired.name, len(changes)) + self.log.debug('apply: zone=%s, chnges=%d', desired.name, len(changes)) zone_name = desired.name[:-1] try: @@ -287,13 +281,13 @@ class AkamaiProvider(BaseProvider): content = { "name": name, "type": record_type, - "ttl" : new.ttl, - "rdata" : rdata + "ttl": new.ttl, + "rdata": rdata } self._dns_client.record_create(zone, name, record_type, content) - return + return def _apply_Delete(self, change): @@ -303,10 +297,10 @@ class AkamaiProvider(BaseProvider): self._dns_client.record_delete(zone, name, record_type) - return + return def _apply_Update(self, change): - + new = change.new record_type = new._type @@ -320,35 +314,34 @@ class AkamaiProvider(BaseProvider): content = { "name": name, "type": record_type, - "ttl" : new.ttl, - "rdata" : rdata + "ttl": new.ttl, + "rdata": rdata } self._dns_client.record_replace(zone, name, record_type, content) - return - + return def _data_for_multiple(self, _type, records): - + return { - 'ttl':records['ttl'], - 'type': _type, + '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] != '.') : + def _data_for_CNAME(self, _type, records): + value = records['rdata'][0] + if (value[-1] != '.'): value = '{}.'.format(value) return { - 'ttl':records['ttl'], + 'ttl': records['ttl'], 'type': _type, 'value': value } @@ -359,10 +352,10 @@ class AkamaiProvider(BaseProvider): preference, exchange = r.split(" ", 1) values.append({ 'preference': preference, - 'exchange' : exchange - }) + 'exchange': exchange + }) return { - 'ttl':records['ttl'], + 'ttl': records['ttl'], 'type': _type, 'values': values } @@ -371,29 +364,29 @@ class AkamaiProvider(BaseProvider): 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, + 'replacement': repl, 'service': service[1:-1] }) return { 'type': _type, - 'ttl':records['ttl'], + 'ttl': records['ttl'], 'values': values } - + def _data_for_PTR(self, _type, records): return { - 'ttl':records['ttl'], + 'ttl': records['ttl'], 'type': _type, - 'value' :records['rdata'][0] - } - + 'value': records['rdata'][0] + } + def _data_for_SRV(self, _type, records): values = [] for r in records['rdata']: @@ -407,7 +400,7 @@ class AkamaiProvider(BaseProvider): return { 'type': _type, - 'ttl':records['ttl'], + 'ttl': records['ttl'], 'values': values } @@ -420,11 +413,11 @@ class AkamaiProvider(BaseProvider): 'fingerprint': fingerprint, 'fingerprint_type': fp_type }) - + return { 'type': _type, 'ttl': records['ttl'], - 'values': values + 'values': values } def _data_for_TXT(self, _type, records): @@ -438,8 +431,7 @@ class AkamaiProvider(BaseProvider): 'type': _type, 'values': values } - - + def _params_for_multiple(self, values): return [r for r in values] @@ -453,7 +445,7 @@ class AkamaiProvider(BaseProvider): _params_for_CNAME = _params_for_single _params_for_PTR = _params_for_single - + def _params_for_MX(self, values): rdata = [] @@ -471,15 +463,15 @@ class AkamaiProvider(BaseProvider): for r in values: ordr = r['order'] - pref = r['preference'] + prf = r['preference'] flg = "\"" + r['flags'] + "\"" srvc = "\"" + r['service'] + "\"" rgx = "\"" + r['regexp'] + "\"" rpl = r['replacement'] - - record = '{} {} {} {} {} {}'.format(ordr, pref, flg, srvc, rgx, rpl) + + record = '{} {} {} {} {} {}'.format(ordr, prf, flg, srvc, rgx, rpl) rdata.append(record) - + return rdata def _params_for_SRV(self, values): @@ -494,7 +486,7 @@ class AkamaiProvider(BaseProvider): rdata.append(record) return rdata - + def _params_for_SSHFP(self, values): rdata = [] for r in values: @@ -506,10 +498,10 @@ class AkamaiProvider(BaseProvider): rdata.append(record) return rdata - + def _params_for_TXT(self, values): rdata = [] - + for r in values: txt = "\"" + r.replace('\\;', ';') + "\"" rdata.append(txt) @@ -518,11 +510,11 @@ class AkamaiProvider(BaseProvider): def _build_zone_config(self, zone, _type=None, comment=None, masters=[]): - if _type is None: - _type="primary" - + if _type is None: + _type = "primary" + if self._contractId is None: - raise NameError("contractId not specified to create zone") + raise NameError("contractId not specified to create zone") return { "zone": zone, @@ -537,14 +529,14 @@ class AkamaiProvider(BaseProvider): 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] == '.'): + # octodns's name for root is '' + if (name[0] == '.'): name = name[1:] return name From 550f5b14a391d741e223ba0ef64e74cb18b6f6a4 Mon Sep 17 00:00:00 2001 From: Basir Date: Wed, 26 Jun 2019 16:19:52 -0400 Subject: [PATCH 47/64] started fastdns testing --- tests/test_octodns_provider_fastdns.py | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/test_octodns_provider_fastdns.py diff --git a/tests/test_octodns_provider_fastdns.py b/tests/test_octodns_provider_fastdns.py new file mode 100644 index 0000000..baf28ea --- /dev/null +++ b/tests/test_octodns_provider_fastdns.py @@ -0,0 +1,70 @@ +# +# +# + +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +from mock import Mock, call +from os.path import dirname, join +from requests import HTTPError +from requests_mock import ANY, mock as requests_mock +from unittest import TestCase + +from octodns.record import Record +from octodns.provider.fastdns import AkamaiProvider, AkamaiClientException +from octodns.provider.yaml import YamlProvider +from octodns.zone import Zone + + +class TestFastdnsProvider(TestCase): + expected = Zone('unit.tests.', []) + source = YamlProvider('test', join(dirname(__file__), 'config')) + source.populate(expected) + + def test_populate(self): + provider = AkamaiProvider("test", "client_secret", "host", "access_token", + "client_token") + + # Bad Auth + with requests_mock() as mock: + mock.get(ANY, status_code=401, + text='{"message": "Authentication failed"}') + + with self.assertRaises(Exception) as ctx: + zone = Zone('unit.tests.', []) + provider.populate(zone) + self.assertEquals("401: Unauthorized", ctx.exception.message) + + # general error + with requests_mock() as mock: + mock.get(ANY, status_code=502, text='Things caught fire') + + with self.assertRaises(HTTPError) as ctx: + zone = Zone('unit.tests.', []) + provider.populate(zone) + self.assertEquals(502, ctx.exception.response.status_code) + + # Non-existant zone doesn't populate anything + with requests_mock() as mock: + mock.get(ANY, status_code=404, + text='{"message": "Domain `foo.bar` not found"}') + + zone = Zone('unit.tests.', []) + provider.populate(zone) + self.assertEquals(set(), zone.records) + + # # No diffs == no changes + # with requests_mock() as mock: + # base = 'https://api.dnsimple.com/v2/42/zones/unit.tests/' \ + # 'records?page=' + # with open('tests/fixtures/dnsimple-page-1.json') as fh: + # mock.get('{}{}'.format(base, 1), text=fh.read()) + # with open('tests/fixtures/dnsimple-page-2.json') as fh: + # mock.get('{}{}'.format(base, 2), text=fh.read()) + + # zone = Zone('unit.tests.', []) + # provider.populate(zone) + # self.assertEquals(16, len(zone.records)) + # changes = self.expected.changes(zone, provider) + # self.assertEquals(0, len(changes)) \ No newline at end of file From ea3b84091ea4f4760d3a165bec24ac293cdea092 Mon Sep 17 00:00:00 2001 From: Basir Date: Wed, 26 Jun 2019 16:25:57 -0400 Subject: [PATCH 48/64] encoding issue --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 26c4351..7ad5988 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -170,7 +170,7 @@ class AkamaiProvider(BaseProvider): 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" + 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 From 5729358e3703fc8231b6ac819184967578d7a193 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Tue, 16 Jul 2019 19:27:24 +0000 Subject: [PATCH 49/64] removed redundant recordsets_get fxn from AkamaiClient --- octodns/provider/fastdns.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 7ad5988..90323b2 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -124,14 +124,6 @@ class AkamaiClient(object): return result - def recordsets_get(self, zone_name): - - resp = self.zone_recordset_get(zone_name, showAll="true") - recordset = resp.json().get("recordsets") - - return recordset - - class AkamaiProvider(BaseProvider): ''' @@ -207,8 +199,9 @@ class AkamaiProvider(BaseProvider): if zone.name not in self._zone_records: try: name = zone.name[:-1] - self._zone_records[zone.name] = \ - self._dns_client.recordsets_get(name) + resp = self._dns_client.zone_recordset_get(name, showAll="true") + resp = resp.json().get("recordsets") + self._zone_records[zone.name] = resp except AkamaiClientException: return [] From 3e3bfe5cf5d691bed7023f100a366acbba7eaf42 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Wed, 17 Jul 2019 23:34:58 +0000 Subject: [PATCH 50/64] fastdns provider fixes: - fixed exception handling in AkamaiClientException - fixed zone_records and zone_recordset fxns in AkamaiProvider and AkamaiClient - refactored _build_zone_config in AkamaiProvider --- octodns/provider/fastdns.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 90323b2..3dd52e7 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -19,7 +19,6 @@ class AkamaiClientException(Exception): _errorMessages = { 400: "400: Bad request", - 401: "401: Unauthorized", 403: "403: Access is forbidden", 404: "404: Resource not found", 405: "405: Method not supported", @@ -32,7 +31,7 @@ class AkamaiClientException(Exception): def __init__(self, resp): try: - message = self._errorMessages.get(resp.status_code) + message = self._errorMessages[resp.status_code] super(AkamaiClientException, self).__init__(message) except: @@ -107,7 +106,7 @@ class AkamaiClient(object): return result - def zone_recordset_get(self, zone, page=None, pageSize=30, search=None, + def zone_recordset_get(self, zone, page=None, pageSize=None, search=None, showAll="true", sortBy="name", types=None): params = { @@ -124,6 +123,7 @@ class AkamaiClient(object): return result + class AkamaiProvider(BaseProvider): ''' @@ -162,7 +162,7 @@ class AkamaiProvider(BaseProvider): 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 + 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 @@ -199,9 +199,11 @@ class AkamaiProvider(BaseProvider): if zone.name not in self._zone_records: try: name = zone.name[:-1] - resp = self._dns_client.zone_recordset_get(name, showAll="true") - resp = resp.json().get("recordsets") - self._zone_records[zone.name] = resp + response = self._dns_client.zone_recordset_get(name) + self._zone_records[zone.name] = response.json()["recordsets"] + + except KeyError: + return [] except AkamaiClientException: return [] @@ -501,10 +503,8 @@ class AkamaiProvider(BaseProvider): return rdata - def _build_zone_config(self, zone, _type=None, comment=None, masters=[]): - - if _type is None: - _type = "primary" + def _build_zone_config(self, zone, _type="primary", comment=None, + masters=[]): if self._contractId is None: raise NameError("contractId not specified to create zone") From 41c59861440400524307aa78e5ec3e684b26d34c Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Wed, 17 Jul 2019 23:43:56 +0000 Subject: [PATCH 51/64] Fastdns provider: - added test cases for testin populate() - added mock request responses in tests/fixtures/ - updated requirements.txt to require edgegrid-python --- requirements.txt | 1 + tests/fixtures/fastdns-invalid-content.json | 35 +++++ tests/fixtures/fastdns-records.json | 157 ++++++++++++++++++++ tests/test_octodns_provider_fastdns.py | 58 +++++--- 4 files changed, 231 insertions(+), 20 deletions(-) create mode 100644 tests/fixtures/fastdns-invalid-content.json create mode 100644 tests/fixtures/fastdns-records.json diff --git a/requirements.txt b/requirements.txt index d100c96..75dc1df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ botocore==1.10.5 dnspython==1.15.0 docutils==0.14 dyn==1.8.1 +edgegrid-python==1.1.1 futures==3.2.0 google-cloud-core==0.28.1 google-cloud-dns==0.29.0 diff --git a/tests/fixtures/fastdns-invalid-content.json b/tests/fixtures/fastdns-invalid-content.json new file mode 100644 index 0000000..8932f66 --- /dev/null +++ b/tests/fixtures/fastdns-invalid-content.json @@ -0,0 +1,35 @@ +{ + "recordsets": [ + { + "rdata": [ + "", + "12 20 foo-2.unit.tests." + ], + "type": "SRV", + "name": "_srv._tcp.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "", + "1 1" + ], + "type": "SSHFP", + "name": "unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "", + "100 \"U\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" ." + ], + "type": "NAPTR", + "name": "naptr.unit.tests", + "ttl": 600 + } + ], + "metadata": { + "totalElements": 3, + "showAll": true + } +} \ No newline at end of file diff --git a/tests/fixtures/fastdns-records.json b/tests/fixtures/fastdns-records.json new file mode 100644 index 0000000..4693eb1 --- /dev/null +++ b/tests/fixtures/fastdns-records.json @@ -0,0 +1,157 @@ +{ + "recordsets": [ + { + "rdata": [ + "10 20 30 foo-1.unit.tests.", + "12 20 30 foo-2.unit.tests." + ], + "type": "SRV", + "name": "_srv._tcp.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "2601:644:500:e210:62f8:1dff:feb8:947a" + ], + "type": "AAAA", + "name": "aaaa.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "ns1.akam.net.", + "ns2.akam.net.", + "ns3.akam.net.", + "ns4.akam.net." + ], + "type": "NS", + "name": "unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "1.2.3.4", + "1.2.3.5" + ], + "type": "A", + "name": "unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300" + ], + "type": "SOA", + "name": "unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49", + "1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73" + ], + "type": "SSHFP", + "name": "unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "unit.tests." + ], + "type": "CNAME", + "name": "cname.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "unit.tests." + ], + "type": "CNAME", + "name": "excluded.unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "unit.tests." + ], + "type": "CNAME", + "name": "included.unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "10 smtp-4.unit.tests.", + "20 smtp-2.unit.tests.", + "30 smtp-3.unit.tests.", + "40 smtp-1.unit.tests." + ], + "type": "MX", + "name": "mx.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" .", + "100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" ." + ], + "type": "NAPTR", + "name": "naptr.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "foo.bar.com." + ], + "type": "PTR", + "name": "ptr.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "\"v=spf1 ip4:192.168.0.1/16-all\"" + ], + "type": "SPF", + "name": "spf.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "ns1.unit.tests.", + "ns2.unit.tests." + ], + "type": "NS", + "name": "under.unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "\"Bah bah black sheep\"", + "\"have you any wool.\"", + "\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\"" + ], + "type": "TXT", + "name": "txt.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "2.2.3.6" + ], + "type": "A", + "name": "www.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "2.2.3.6" + ], + "type": "A", + "name": "www.sub.unit.tests", + "ttl": 300 + } + ], + "metadata": { + "totalElements": 16, + "showAll": true + } +} \ No newline at end of file diff --git a/tests/test_octodns_provider_fastdns.py b/tests/test_octodns_provider_fastdns.py index baf28ea..8c841fe 100644 --- a/tests/test_octodns_provider_fastdns.py +++ b/tests/test_octodns_provider_fastdns.py @@ -22,19 +22,32 @@ class TestFastdnsProvider(TestCase): source = YamlProvider('test', join(dirname(__file__), 'config')) source.populate(expected) + # Our test suite differs a bit, add our NS and remove the simple one + expected.add_record(Record.new(expected, 'under', { + 'ttl': 3600, + 'type': 'NS', + 'values': [ + 'ns1.unit.tests.', + 'ns2.unit.tests.', + ] + })) + for record in list(expected.records): + if record.name == 'sub' and record._type == 'NS': + expected._remove_record(record) + break + def test_populate(self): - provider = AkamaiProvider("test", "client_secret", "host", "access_token", - "client_token") - + provider = AkamaiProvider("test", "secret", "akam.com", "atok", "ctok") + # Bad Auth with requests_mock() as mock: - mock.get(ANY, status_code=401, - text='{"message": "Authentication failed"}') + mock.get(ANY, status_code=401, text='{"message": "Unauthorized"}') with self.assertRaises(Exception) as ctx: zone = Zone('unit.tests.', []) provider.populate(zone) - self.assertEquals("401: Unauthorized", ctx.exception.message) + + self.assertEquals(401, ctx.exception.response.status_code) # general error with requests_mock() as mock: @@ -45,7 +58,7 @@ class TestFastdnsProvider(TestCase): provider.populate(zone) self.assertEquals(502, ctx.exception.response.status_code) - # Non-existant zone doesn't populate anything + # Non-existant zone doesn't populate anything with requests_mock() as mock: mock.get(ANY, status_code=404, text='{"message": "Domain `foo.bar` not found"}') @@ -54,17 +67,22 @@ class TestFastdnsProvider(TestCase): provider.populate(zone) self.assertEquals(set(), zone.records) - # # No diffs == no changes - # with requests_mock() as mock: - # base = 'https://api.dnsimple.com/v2/42/zones/unit.tests/' \ - # 'records?page=' - # with open('tests/fixtures/dnsimple-page-1.json') as fh: - # mock.get('{}{}'.format(base, 1), text=fh.read()) - # with open('tests/fixtures/dnsimple-page-2.json') as fh: - # mock.get('{}{}'.format(base, 2), text=fh.read()) + # No diffs == no changes + with requests_mock() as mock: - # zone = Zone('unit.tests.', []) - # provider.populate(zone) - # self.assertEquals(16, len(zone.records)) - # changes = self.expected.changes(zone, provider) - # self.assertEquals(0, len(changes)) \ No newline at end of file + with open('tests/fixtures/fastdns-records.json') as fh: + mock.get(ANY, text=fh.read()) + + zone = Zone('unit.tests.', []) + provider.populate(zone) + self.assertEquals(16, len(zone.records)) + changes = self.expected.changes(zone, provider) + self.assertEquals(0, len(changes)) + + # 2nd populate makes no network calls/all from cache + again = Zone('unit.tests.', []) + provider.populate(again) + self.assertEquals(16, len(again.records)) + + # bust the cache + del provider._zone_records[zone.name] \ No newline at end of file From 852d607715078c682c1c1bb74a365a430279acc7 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 18 Jul 2019 01:07:42 +0000 Subject: [PATCH 52/64] fastdns: - added test cases for test_apply, testing for basic create, update, delete records - added previous state json file in test/fixtures to do the above --- tests/fixtures/fastdns-records-prev.json | 166 +++++++++++++++++++++++ tests/test_octodns_provider_fastdns.py | 22 ++- 2 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/fastdns-records-prev.json diff --git a/tests/fixtures/fastdns-records-prev.json b/tests/fixtures/fastdns-records-prev.json new file mode 100644 index 0000000..04ce274 --- /dev/null +++ b/tests/fixtures/fastdns-records-prev.json @@ -0,0 +1,166 @@ +{ + "recordsets": [ + { + "rdata": [ + "10 20 30 foo-1.unit.tests.", + "12 20 30 foo-2.unit.tests." + ], + "type": "SRV", + "name": "_srv._tcp.old.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "10 20 30 foo-1.unit.tests.", + "12 20 30 foo-2.unit.tests." + ], + "type": "SRV", + "name": "_srv._tcp.old.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "2601:644:500:e210:62f8:1dff:feb8:9471" + ], + "type": "AAAA", + "name": "aaaa.old.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "ns1.akam.net.", + "ns2.akam.net.", + "ns3.akam.net.", + "ns4.akam.net." + ], + "type": "NS", + "name": "old.unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "1.2.3.4", + "1.2.3.5" + ], + "type": "A", + "name": "old.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300" + ], + "type": "SOA", + "name": "unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49", + "1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73" + ], + "type": "SSHFP", + "name": "old.unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "unit.tests." + ], + "type": "CNAME", + "name": "old.cname.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "unit.tests." + ], + "type": "CNAME", + "name": "excluded.old.unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "unit.tests." + ], + "type": "CNAME", + "name": "included.old.unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "10 smtp-4.unit.tests.", + "20 smtp-2.unit.tests.", + "30 smtp-3.unit.tests.", + "40 smtp-1.unit.tests." + ], + "type": "MX", + "name": "mx.old.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" .", + "100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" ." + ], + "type": "NAPTR", + "name": "naptr.old.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "foo.bar.com." + ], + "type": "PTR", + "name": "ptr.old.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "\"v=spf1 ip4:192.168.0.1/16-all\"" + ], + "type": "SPF", + "name": "spf.old.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "ns1.unit.tests.", + "ns2.unit.tests." + ], + "type": "NS", + "name": "under.old.unit.tests", + "ttl": 3600 + }, + { + "rdata": [ + "\"Bah bah black sheep\"", + "\"have you any wool.\"", + "\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\"" + ], + "type": "TXT", + "name": "txt.old.unit.tests", + "ttl": 600 + }, + { + "rdata": [ + "2.2.3.7" + ], + "type": "A", + "name": "www.unit.tests", + "ttl": 300 + }, + { + "rdata": [ + "2.2.3.6" + ], + "type": "A", + "name": "www.sub.old.unit.tests", + "ttl": 300 + } + ], + "metadata": { + "totalElements": 16, + "showAll": true + } +} \ No newline at end of file diff --git a/tests/test_octodns_provider_fastdns.py b/tests/test_octodns_provider_fastdns.py index 8c841fe..4df36c0 100644 --- a/tests/test_octodns_provider_fastdns.py +++ b/tests/test_octodns_provider_fastdns.py @@ -5,14 +5,14 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals -from mock import Mock, call +# from mock import Mock, call from os.path import dirname, join from requests import HTTPError from requests_mock import ANY, mock as requests_mock from unittest import TestCase from octodns.record import Record -from octodns.provider.fastdns import AkamaiProvider, AkamaiClientException +from octodns.provider.fastdns import AkamaiProvider from octodns.provider.yaml import YamlProvider from octodns.zone import Zone @@ -85,4 +85,20 @@ class TestFastdnsProvider(TestCase): self.assertEquals(16, len(again.records)) # bust the cache - del provider._zone_records[zone.name] \ No newline at end of file + del provider._zone_records[zone.name] + + def test_apply(self): + provider = AkamaiProvider("test", "secret", "akam.com", "atok", "ctok") + + with requests_mock() as mock: + + with open('tests/fixtures/fastdns-records-prev.json') as fh: + mock.get(ANY, text=fh.read()) + + plan = provider.plan(self.expected) + mock.post(ANY, status_code=201) + mock.put(ANY, status_code=200) + mock.delete(ANY, status_code=204) + + changes = provider.apply(plan) + self.assertEquals(29, changes) From 1596add89f54c32af3e8c005f53507b3d0f39489 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 18 Jul 2019 20:46:05 +0000 Subject: [PATCH 53/64] fastdns provider: - added unit tests for apply functions - added another json config file for this - 100% code coverage reached --- .../fixtures/fastdns-records-prev-other.json | 166 ++++++++++++++++++ tests/test_octodns_provider_fastdns.py | 48 ++++- 2 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/fastdns-records-prev-other.json diff --git a/tests/fixtures/fastdns-records-prev-other.json b/tests/fixtures/fastdns-records-prev-other.json new file mode 100644 index 0000000..acae3ec --- /dev/null +++ b/tests/fixtures/fastdns-records-prev-other.json @@ -0,0 +1,166 @@ +{ + "recordsets": [ + { + "rdata": [ + "10 20 30 foo-1.other.tests.", + "12 20 30 foo-2.other.tests." + ], + "type": "SRV", + "name": "_srv._tcp.old.other.tests", + "ttl": 600 + }, + { + "rdata": [ + "10 20 30 foo-1.other.tests.", + "12 20 30 foo-2.other.tests." + ], + "type": "SRV", + "name": "_srv._tcp.old.other.tests", + "ttl": 600 + }, + { + "rdata": [ + "2601:644:500:e210:62f8:1dff:feb8:9471" + ], + "type": "AAAA", + "name": "aaaa.old.other.tests", + "ttl": 600 + }, + { + "rdata": [ + "ns1.akam.net.", + "ns2.akam.net.", + "ns3.akam.net.", + "ns4.akam.net." + ], + "type": "NS", + "name": "old.other.tests", + "ttl": 3600 + }, + { + "rdata": [ + "1.2.3.4", + "1.2.3.5" + ], + "type": "A", + "name": "old.other.tests", + "ttl": 300 + }, + { + "rdata": [ + "ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300" + ], + "type": "SOA", + "name": "other.tests", + "ttl": 3600 + }, + { + "rdata": [ + "1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49", + "1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73" + ], + "type": "SSHFP", + "name": "old.other.tests", + "ttl": 3600 + }, + { + "rdata": [ + "other.tests." + ], + "type": "CNAME", + "name": "old.cname.other.tests", + "ttl": 300 + }, + { + "rdata": [ + "other.tests." + ], + "type": "CNAME", + "name": "excluded.old.other.tests", + "ttl": 3600 + }, + { + "rdata": [ + "other.tests." + ], + "type": "CNAME", + "name": "included.old.other.tests", + "ttl": 3600 + }, + { + "rdata": [ + "10 smtp-4.other.tests.", + "20 smtp-2.other.tests.", + "30 smtp-3.other.tests.", + "40 smtp-1.other.tests." + ], + "type": "MX", + "name": "mx.old.other.tests", + "ttl": 300 + }, + { + "rdata": [ + "10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" .", + "100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" ." + ], + "type": "NAPTR", + "name": "naptr.old.other.tests", + "ttl": 600 + }, + { + "rdata": [ + "foo.bar.com." + ], + "type": "PTR", + "name": "ptr.old.other.tests", + "ttl": 300 + }, + { + "rdata": [ + "\"v=spf1 ip4:192.168.0.1/16-all\"" + ], + "type": "SPF", + "name": "spf.old.other.tests", + "ttl": 600 + }, + { + "rdata": [ + "ns1.other.tests.", + "ns2.other.tests." + ], + "type": "NS", + "name": "under.old.other.tests", + "ttl": 3600 + }, + { + "rdata": [ + "\"Bah bah black sheep\"", + "\"have you any wool.\"", + "\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\"" + ], + "type": "TXT", + "name": "txt.old.other.tests", + "ttl": 600 + }, + { + "rdata": [ + "2.2.3.7" + ], + "type": "A", + "name": "www.other.tests", + "ttl": 300 + }, + { + "rdata": [ + "2.2.3.6" + ], + "type": "A", + "name": "www.sub.old.other.tests", + "ttl": 300 + } + ], + "metadata": { + "totalElements": 16, + "showAll": true + } +} \ No newline at end of file diff --git a/tests/test_octodns_provider_fastdns.py b/tests/test_octodns_provider_fastdns.py index 4df36c0..5f503c7 100644 --- a/tests/test_octodns_provider_fastdns.py +++ b/tests/test_octodns_provider_fastdns.py @@ -88,8 +88,10 @@ class TestFastdnsProvider(TestCase): del provider._zone_records[zone.name] def test_apply(self): - provider = AkamaiProvider("test", "secret", "akam.com", "atok", "ctok") + provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok", + "cid", "gid") + # tests create update delete through previous state config json with requests_mock() as mock: with open('tests/fixtures/fastdns-records-prev.json') as fh: @@ -102,3 +104,47 @@ class TestFastdnsProvider(TestCase): changes = provider.apply(plan) self.assertEquals(29, changes) + + # 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: + mock.get(ANY, status_code=404) + + plan = provider.plan(self.expected) + mock.post(ANY, status_code=201) + mock.put(ANY, status_code=200) + mock.delete(ANY, status_code=204) + + changes = provider.apply(plan) + self.assertEquals(14, changes) + + # 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: + mock.get(ANY, status_code=404) + provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok", + "cid") + plan = provider.plan(self.expected) + mock.post(ANY, status_code=201) + mock.put(ANY, status_code=200) + mock.delete(ANY, status_code=204) + + changes = provider.apply(plan) + self.assertEquals(14, changes) + + # Test against a zone that doesn't exist, but cid not provided + + with requests_mock() as mock: + mock.get(ANY, status_code=404) + + provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok") + plan = provider.plan(self.expected) + mock.post(ANY, status_code=201) + mock.put(ANY, status_code=200) + mock.delete(ANY, status_code=204) + + try: + changes = provider.apply(plan) + except NameError as e: + expected = "contractId not specified to create zone" + self.assertEquals(e.message, expected) From e3f6c4c711b314bc5ea0616327f4d3c4629bf758 Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 18 Jul 2019 21:07:14 +0000 Subject: [PATCH 54/64] fastdns: changed sshfp to convert text to lowercase, since akamai changes to caps --- octodns/provider/fastdns.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 3dd52e7..e4a3c66 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -202,10 +202,7 @@ class AkamaiProvider(BaseProvider): response = self._dns_client.zone_recordset_get(name) self._zone_records[zone.name] = response.json()["recordsets"] - except KeyError: - return [] - - except AkamaiClientException: + except (AkamaiClientException, KeyError): return [] return self._zone_records[zone.name] @@ -405,7 +402,7 @@ class AkamaiProvider(BaseProvider): algorithm, fp_type, fingerprint = r.split(' ', 2) values.append({ 'algorithm': algorithm, - 'fingerprint': fingerprint, + 'fingerprint': fingerprint.lower(), 'fingerprint_type': fp_type }) From 5a741eb6007fed26d79d14288f7b95294d850a1b Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Thu, 18 Jul 2019 21:16:34 +0000 Subject: [PATCH 55/64] fastdns: fixed typo in test fixture --- tests/fixtures/fastdns-records-prev.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fixtures/fastdns-records-prev.json b/tests/fixtures/fastdns-records-prev.json index 04ce274..b07c63f 100644 --- a/tests/fixtures/fastdns-records-prev.json +++ b/tests/fixtures/fastdns-records-prev.json @@ -65,7 +65,7 @@ }, { "rdata": [ - "unit.tests." + "unit.tests" ], "type": "CNAME", "name": "old.cname.unit.tests", From 79dd35f7a74d50c854e460f8dd11a3222f7fb384 Mon Sep 17 00:00:00 2001 From: Basir Date: Thu, 18 Jul 2019 17:56:18 -0400 Subject: [PATCH 56/64] fastdns: fixed an issue with handling spaces in SPF records --- octodns/provider/fastdns.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index e4a3c66..f6d3bd3 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -433,7 +433,6 @@ class AkamaiProvider(BaseProvider): _params_for_A = _params_for_multiple _params_for_AAAA = _params_for_multiple _params_for_NS = _params_for_multiple - _params_for_SPF = _params_for_multiple _params_for_CNAME = _params_for_single _params_for_PTR = _params_for_single @@ -466,6 +465,15 @@ class AkamaiProvider(BaseProvider): 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: From 76e799873e51fa78138a85efd194ec35594ce3ef Mon Sep 17 00:00:00 2001 From: basirjamil Date: Fri, 19 Jul 2019 14:32:51 -0400 Subject: [PATCH 57/64] Update octodns/provider/fastdns.py Co-Authored-By: Ross McFarland --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index f6d3bd3..0bc0aa2 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals -import requests +from requests import Session from akamai.edgegrid import EdgeGridAuth from urlparse import urljoin from collections import defaultdict From d53eb7ad8e2400fe0266802617cf82ee76391305 Mon Sep 17 00:00:00 2001 From: basirjamil Date: Fri, 19 Jul 2019 14:32:58 -0400 Subject: [PATCH 58/64] Update octodns/provider/fastdns.py Co-Authored-By: Ross McFarland --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 0bc0aa2..45d6aa1 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -10,7 +10,7 @@ from akamai.edgegrid import EdgeGridAuth from urlparse import urljoin from collections import defaultdict -import logging +from logging import getLogger from ..record import Record from .base import BaseProvider From 91e7ac6d36c22f5570896296f1fac9632c78fa1d Mon Sep 17 00:00:00 2001 From: basirjamil Date: Fri, 19 Jul 2019 14:33:23 -0400 Subject: [PATCH 59/64] Update octodns/provider/fastdns.py Co-Authored-By: Ross McFarland --- octodns/provider/fastdns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 45d6aa1..d0d2009 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -181,7 +181,7 @@ class AkamaiProvider(BaseProvider): def __init__(self, id, client_secret, host, access_token, client_token, contract_id=None, gid=None, *args, **kwargs): - self.log = logging.getLogger('AkamaiProvider[{}]'.format(id)) + self.log = getLogger('AkamaiProvider[{}]'.format(id)) self.log.debug('__init__: id=%s, ') super(AkamaiProvider, self).__init__(id, *args, **kwargs) From 41a5d78728ffc90d90f89d1ad966c4a84c3fbebe Mon Sep 17 00:00:00 2001 From: Basir Jamil Date: Fri, 19 Jul 2019 20:53:34 +0000 Subject: [PATCH 60/64] fastdns: refactored how exceptions are handled --- octodns/provider/fastdns.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index d0d2009..fb199d3 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -18,25 +18,18 @@ from .base import BaseProvider class AkamaiClientException(Exception): _errorMessages = { - 400: "400: Bad request", - 403: "403: Access is forbidden", - 404: "404: Resource not found", - 405: "405: Method not supported", - 406: "406: Not Acceptable", - 409: "409: Request not allowed due to conflict with current state", - 415: "415: Unsupported media type", - 422: "422: Request body contains an error preventing processing", - 500: "500: Internal server error" + 404: "404: Resource not found" } def __init__(self, resp): try: message = self._errorMessages[resp.status_code] - super(AkamaiClientException, self).__init__(message) - except: + except KeyError: resp.raise_for_status() + super(AkamaiClientException, self).__init__(message) + class AkamaiClient(object): ''' @@ -54,7 +47,7 @@ class AkamaiClient(object): self.base = "https://" + _host + "/config-dns/v2/" - sess = requests.Session() + sess = Session() sess.auth = EdgeGridAuth( client_token=_client_token, client_secret=_client_secret, From a85d4e25eda59d59ebc320b1a6597ac9d2848f1a Mon Sep 17 00:00:00 2001 From: Basir Date: Mon, 29 Jul 2019 13:03:23 -0400 Subject: [PATCH 61/64] fastdns: refactored AkamaiClientNotFound exception class --- octodns/provider/fastdns.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index fb199d3..bf19953 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -15,19 +15,10 @@ from ..record import Record from .base import BaseProvider -class AkamaiClientException(Exception): - - _errorMessages = { - 404: "404: Resource not found" - } +class AkamaiClientNotFound(Exception): def __init__(self, resp): - try: - message = self._errorMessages[resp.status_code] - - except KeyError: - resp.raise_for_status() - + message = "404: Resource not found" super(AkamaiClientException, self).__init__(message) From 93e1e0939d5cb3d8218bb11cc3fd62cfda9f83ca Mon Sep 17 00:00:00 2001 From: Basir Date: Mon, 29 Jul 2019 13:07:04 -0400 Subject: [PATCH 62/64] fastdns: fixed how exceptions are handled in _request --- octodns/provider/fastdns.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index bf19953..6e9a00d 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -51,8 +51,9 @@ class AkamaiClient(object): url = urljoin(self.base, path) resp = self._sess.request(method, url, params=params, json=data) - if resp.status_code > 299: - raise AkamaiClientException(resp) + if resp.status_code == 404: + raise AkamaiClientNotFound(resp) + resp.raise_for_status() return resp From 55210ebb05cd4e9077fa324dd2619de83e6cb046 Mon Sep 17 00:00:00 2001 From: Basir Date: Mon, 29 Jul 2019 13:09:18 -0400 Subject: [PATCH 63/64] fastdns: fixed naming in AkamaiClient init --- octodns/provider/fastdns.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 6e9a00d..ab80882 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -34,15 +34,15 @@ class AkamaiClient(object): ''' - def __init__(self, _client_secret, _host, _access_token, _client_token): + def __init__(self, client_secret, host, access_token, client_token): - self.base = "https://" + _host + "/config-dns/v2/" + self.base = "https://" + host + "/config-dns/v2/" sess = Session() sess.auth = EdgeGridAuth( - client_token=_client_token, - client_secret=_client_secret, - access_token=_access_token + client_token=client_token, + client_secret=client_secret, + access_token=access_token ) self._sess = sess From b2bd91a998aa5a3e7c046d9a32eea3b505071109 Mon Sep 17 00:00:00 2001 From: Basir Date: Mon, 29 Jul 2019 13:25:26 -0400 Subject: [PATCH 64/64] fastdns: resolved naming mismatch for exception class --- octodns/provider/fastdns.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index ab80882..f851303 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -19,7 +19,7 @@ class AkamaiClientNotFound(Exception): def __init__(self, resp): message = "404: Resource not found" - super(AkamaiClientException, self).__init__(message) + super(AkamaiClientNotFound, self).__init__(message) class AkamaiClient(object): @@ -187,7 +187,7 @@ class AkamaiProvider(BaseProvider): response = self._dns_client.zone_recordset_get(name) self._zone_records[zone.name] = response.json()["recordsets"] - except (AkamaiClientException, KeyError): + except (AkamaiClientNotFound, KeyError): return [] return self._zone_records[zone.name] @@ -231,7 +231,7 @@ class AkamaiProvider(BaseProvider): try: self._dns_client.zone_get(zone_name) - except AkamaiClientException: + 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)