From 36344d267b807668c9b6aba4edb785f93938a402 Mon Sep 17 00:00:00 2001 From: David Ervideira Date: Wed, 13 Dec 2017 23:20:16 +0000 Subject: [PATCH 1/5] Update .gitignore - With standarts from github gitignores repo - Add .vscode/ - Move to root repo folder --- .gitignore | 114 +++++++++++++++++++++++++++++++++++++++++- .vscode/settings.json | 3 ++ src/.gitignore | 2 - 3 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 src/.gitignore diff --git a/.gitignore b/.gitignore index 6156f13..debf005 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,112 @@ -src/config.py -src/config.pyc +## From https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Custom + +config.py +test.py +.vscode/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..fe71598 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.linting.pylintEnabled": false +} \ No newline at end of file diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index 3abc68b..0000000 --- a/src/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/config.py -/test.py From 09df90924bef407f0633e488a8d97b6f9298dd3e Mon Sep 17 00:00:00 2001 From: David Ervideira Date: Wed, 13 Dec 2017 23:39:24 +0000 Subject: [PATCH 2/5] Python 3 support - Python 3 is regarded as the future of the language so it _should_ be prefered - Cleaner usage of requests: changed json.loads(u._content) to the more clean usage u.json() --- src/gandi-live-dns.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/gandi-live-dns.py b/src/gandi-live-dns.py index c5ad256..3a2ee15 100755 --- a/src/gandi-live-dns.py +++ b/src/gandi-live-dns.py @@ -22,8 +22,8 @@ def get_dynip(ifconfig_provider): similar to curl ifconfig.me/ip, see example.config.py for details to ifconfig providers ''' r = requests.get(ifconfig_provider) - print 'Checking dynamic IP: ' , r._content.strip('\n') - return r.content.strip('\n') + print('Checking dynamic IP: ' , r.text.strip('\n')) + return r.text.strip('\n') def get_uuid(): ''' @@ -34,12 +34,12 @@ def get_uuid(): ''' url = config.api_endpoint + '/domains/' + config.domain u = requests.get(url, headers={"X-Api-Key":config.api_secret}) - json_object = json.loads(u._content) + json_object = u.json() if u.status_code == 200: return json_object['zone_uuid'] else: - print 'Error: HTTP Status Code ', u.status_code, 'when trying to get Zone UUID' - print json_object['message'] + print('Error: HTTP Status Code ', u.status_code, 'when trying to get Zone UUID') + print(json_object['message']) exit() def get_dnsip(uuid): @@ -55,12 +55,12 @@ def get_dnsip(uuid): headers = {"X-Api-Key":config.api_secret} u = requests.get(url, headers=headers) if u.status_code == 200: - json_object = json.loads(u._content) - print 'Checking IP from DNS Record' , config.subdomains[0], ':', json_object['rrset_values'][0].encode('ascii','ignore').strip('\n') - return json_object['rrset_values'][0].encode('ascii','ignore').strip('\n') + json_object = u.json() + print('Checking IP from DNS Record' , config.subdomains[0], ':', json_object['rrset_values'][0].strip('\n')) + return json_object['rrset_values'][0].strip('\n') else: - print 'Error: HTTP Status Code ', u.status_code, 'when trying to get IP from subdomain', config.subdomains[0] - print json_object['message'] + print('Error: HTTP Status Code ', u.status_code, 'when trying to get IP from subdomain', config.subdomains[0]) + print(json_object['message']) exit() def update_records(uuid, dynIP, subdomain): @@ -77,14 +77,14 @@ def update_records(uuid, dynIP, subdomain): payload = {"rrset_ttl": config.ttl, "rrset_values": [dynIP]} headers = {"Content-Type": "application/json", "X-Api-Key":config.api_secret} u = requests.put(url, data=json.dumps(payload), headers=headers) - json_object = json.loads(u._content) + json_object = u.json() if u.status_code == 201: - print 'Status Code:', u.status_code, ',', json_object['message'], ', IP updated for', subdomain + print('Status Code:', u.status_code, ',', json_object['message'], ', IP updated for', subdomain) return True else: - print 'Error: HTTP Status Code ', u.status_code, 'when trying to update IP from subdomain', subdomain - print json_object['message'] + print('Error: HTTP Status Code ', u.status_code, 'when trying to update IP from subdomain', subdomain) + print(json_object['message']) exit() @@ -92,7 +92,7 @@ def update_records(uuid, dynIP, subdomain): def main(force_update, verbosity): if verbosity: - print "verbosity turned on - not implemented by now" + print("verbosity turned on - not implemented by now") #get zone ID from Account @@ -103,14 +103,14 @@ def main(force_update, verbosity): dnsIP = get_dnsip(uuid) if force_update: - print "Going to update/create the DNS Records for the subdomains" + print("Going to update/create the DNS Records for the subdomains") for sub in config.subdomains: update_records(uuid, dynIP, sub) else: if dynIP == dnsIP: - print "IP Address Match - no further action" + print("IP Address Match - no further action") else: - print "IP Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIP + print("IP Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIP) for sub in config.subdomains: update_records(uuid, dynIP, sub) From 65f08836b7f9e6f8668932da3190f93f5716527b Mon Sep 17 00:00:00 2001 From: David Ervideira Date: Thu, 14 Dec 2017 00:07:14 +0000 Subject: [PATCH 3/5] Fix _ifconfig_ url in config example - https://ifconfig.co does not give the ip only. https://ifconfig.co/ip does. --- src/example.config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/example.config.py b/src/example.config.py index 039c895..7ac37fc 100644 --- a/src/example.config.py +++ b/src/example.config.py @@ -36,7 +36,7 @@ + e.g. -+ https://ifconfig.co ++ https://ifconfig.co/ip + http://ifconfig.me/ip + http://whatismyip.akamai.com/ + http://ipinfo.io/ip From 152757bf3f11d93588024a6ded99db34059a0f53 Mon Sep 17 00:00:00 2001 From: David Ervideira Date: Thu, 14 Dec 2017 00:45:32 +0000 Subject: [PATCH 4/5] Added propper logging to console and a file - Logger can be configured in the config file - Added an working example config in the example file --- src/example.config.py | 39 +++++++++++++++++++++++++++++++++++++++ src/gandi-live-dns.py | 38 ++++++++++++++++++-------------------- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/example.config.py b/src/example.config.py index 7ac37fc..d54914d 100644 --- a/src/example.config.py +++ b/src/example.config.py @@ -43,3 +43,42 @@ + many more ... ''' ifconfig = 'choose_from_above_or_run_your_own' + + +''' +Sample logging config +This is optmized for *nix +Permission are needed to write the log file +''' +log_config = { + 'version': 1, + 'formatters': { + 'detailed': { + 'class': 'logging.Formatter', + 'format': '%(asctime)s %(name)-15s %(levelname)-8s %(processName)-10s %(message)s' + }, + 'simple': { + 'class': 'logging.Formatter', + 'format': '%(message)s' + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'simple', + }, + 'file': { + 'class': 'logging.handlers.TimedRotatingFileHandler', + 'filename': '/var/log/gandi-live-dns.log', + 'when': 'W6', + 'backupCount': '3', + 'formatter': 'detailed', + }, + }, + 'loggers': { + 'gandi-live-dns': { + 'level': 'DEBUG', + 'handlers': ['console', 'file'] + }, + }, + } diff --git a/src/gandi-live-dns.py b/src/gandi-live-dns.py index 3a2ee15..c5e1cf8 100755 --- a/src/gandi-live-dns.py +++ b/src/gandi-live-dns.py @@ -12,17 +12,22 @@ http://doc.livedns.gandi.net/#api-endpoint -> https://dns.beta.gandi.net/api/v5/ ''' -import requests, json +import json +import logging +import logging.config +import requests import config import argparse +logging.config.dictConfig(config.log_config) +log = logging.getLogger('gandi-live-dns') def get_dynip(ifconfig_provider): ''' find out own IPv4 at home <-- this is the dynamic IP which changes more or less frequently similar to curl ifconfig.me/ip, see example.config.py for details to ifconfig providers ''' r = requests.get(ifconfig_provider) - print('Checking dynamic IP: ' , r.text.strip('\n')) + log.info('Checking dynamic IP: {}'.format(r.text.strip('\n'))) return r.text.strip('\n') def get_uuid(): @@ -38,8 +43,8 @@ def get_uuid(): if u.status_code == 200: return json_object['zone_uuid'] else: - print('Error: HTTP Status Code ', u.status_code, 'when trying to get Zone UUID') - print(json_object['message']) + log.error('Error: HTTP Status Code {} when trying to get Zone UUID'.format(u.status_code)) + log.error('{}'.format(json_object['message'])) exit() def get_dnsip(uuid): @@ -56,11 +61,11 @@ def get_dnsip(uuid): u = requests.get(url, headers=headers) if u.status_code == 200: json_object = u.json() - print('Checking IP from DNS Record' , config.subdomains[0], ':', json_object['rrset_values'][0].strip('\n')) + log.info('Checking IP from DNS Record {} : {}'.format(config.subdomains[0], json_object['rrset_values'][0].strip('\n'))) return json_object['rrset_values'][0].strip('\n') else: - print('Error: HTTP Status Code ', u.status_code, 'when trying to get IP from subdomain', config.subdomains[0]) - print(json_object['message']) + log.error('Error: HTTP Status Code {} when trying to get IP from subdomain {}'.format(u.status_code, config.subdomains[0])) + log.error('{}'.format(json_object['message'])) exit() def update_records(uuid, dynIP, subdomain): @@ -80,11 +85,11 @@ def update_records(uuid, dynIP, subdomain): json_object = u.json() if u.status_code == 201: - print('Status Code:', u.status_code, ',', json_object['message'], ', IP updated for', subdomain) + log.info('Status Code: {}, {}, IP updated for {}'.format(u.status_code, json_object['message'], subdomain)) return True else: - print('Error: HTTP Status Code ', u.status_code, 'when trying to update IP from subdomain', subdomain) - print(json_object['message']) + log.error('Error: HTTP Status Code {} when trying to update IP from subdomain {}'.format(u.status_code, subdomain)) + log.error('{}'.format(json_object['message'])) exit() @@ -103,14 +108,14 @@ def main(force_update, verbosity): dnsIP = get_dnsip(uuid) if force_update: - print("Going to update/create the DNS Records for the subdomains") + log.info("Going to update/create the DNS Records for the subdomains") for sub in config.subdomains: update_records(uuid, dynIP, sub) else: if dynIP == dnsIP: - print("IP Address Match - no further action") + log.info("IP Address Match - no further action") else: - print("IP Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIP) + log.info("IP Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIP) for sub in config.subdomains: update_records(uuid, dynIP, sub) @@ -119,12 +124,5 @@ def main(force_update, verbosity): parser.add_argument('-v', '--verbose', help="increase output verbosity", action="store_true") parser.add_argument('-f', '--force', help="force an update/create", action="store_true") args = parser.parse_args() - - main(args.force, args.verbose) - - - - - \ No newline at end of file From 825900b5e53e257b36aea6ace182b0316975a1ab Mon Sep 17 00:00:00 2001 From: David Ervideira Date: Thu, 14 Dec 2017 00:58:31 +0000 Subject: [PATCH 5/5] Added verbosity function - Added verbosity function using the debug level in logger --- src/gandi-live-dns.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/gandi-live-dns.py b/src/gandi-live-dns.py index c5e1cf8..13c2ad4 100755 --- a/src/gandi-live-dns.py +++ b/src/gandi-live-dns.py @@ -26,6 +26,7 @@ def get_dynip(ifconfig_provider): ''' find out own IPv4 at home <-- this is the dynamic IP which changes more or less frequently similar to curl ifconfig.me/ip, see example.config.py for details to ifconfig providers ''' + log.debug('Using ifconfig_provider {}'.format(ifconfig_provider)) r = requests.get(ifconfig_provider) log.info('Checking dynamic IP: {}'.format(r.text.strip('\n'))) return r.text.strip('\n') @@ -38,6 +39,7 @@ def get_uuid(): ''' url = config.api_endpoint + '/domains/' + config.domain + log.debug('Using url {}'.format(url)) u = requests.get(url, headers={"X-Api-Key":config.api_secret}) json_object = u.json() if u.status_code == 200: @@ -57,7 +59,9 @@ def get_dnsip(uuid): ''' url = config.api_endpoint+ '/zones/' + uuid + '/records/' + config.subdomains[0] + '/A' + log.debug('Using url {}'.format(url)) headers = {"X-Api-Key":config.api_secret} + log.debug('Using headers {}'.format(headers)) u = requests.get(url, headers=headers) if u.status_code == 200: json_object = u.json() @@ -79,8 +83,11 @@ def update_records(uuid, dynIP, subdomain): https://dns.beta.gandi.net/api/v5/zones//records// ''' url = config.api_endpoint+ '/zones/' + uuid + '/records/' + subdomain + '/A' + log.debug('Using url {}'.format(url)) payload = {"rrset_ttl": config.ttl, "rrset_values": [dynIP]} + log.debug('Using payload {}'.format(payload)) headers = {"Content-Type": "application/json", "X-Api-Key":config.api_secret} + log.debug('Using headers {}'.format(headers)) u = requests.put(url, data=json.dumps(payload), headers=headers) json_object = u.json() @@ -97,19 +104,22 @@ def update_records(uuid, dynIP, subdomain): def main(force_update, verbosity): if verbosity: - print("verbosity turned on - not implemented by now") + log.setLevel(logging.DEBUG) + log.debug('Verbosity On') - #get zone ID from Account uuid = get_uuid() + log.debug('uuid: {}'.format(uuid)) #compare dynIP and DNS IP dynIP = get_dynip(config.ifconfig) dnsIP = get_dnsip(uuid) + log.debug('dynIP: {}, dnsIP: {}'.format(dynIP, dnsIP)) if force_update: log.info("Going to update/create the DNS Records for the subdomains") for sub in config.subdomains: + log.debug('Forcing update on {}'.format(sub)) update_records(uuid, dynIP, sub) else: if dynIP == dnsIP: @@ -117,6 +127,7 @@ def main(force_update, verbosity): else: log.info("IP Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIP) for sub in config.subdomains: + log.debug('Updating on {}'.format(sub)) update_records(uuid, dynIP, sub) if __name__ == "__main__":