From edc3311734b9b4cfbd3468650bbf80321a356083 Mon Sep 17 00:00:00 2001 From: Momcilo Majic Date: Sat, 22 Oct 2016 11:07:22 +0200 Subject: [PATCH 01/10] Added: - adjusted API calls, due to changes made by linode - automatic DOMAINID and RESOURCEID resolution: no need to fish for IDs from web browser - configuration file parsing --- LinodeDynDNS.py | 88 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/LinodeDynDNS.py b/LinodeDynDNS.py index f0187b3..82986f0 100755 --- a/LinodeDynDNS.py +++ b/LinodeDynDNS.py @@ -35,7 +35,8 @@ # https://manager.linode.com/dns/resource/domain.com?id=000000 # Resource ID ^ # -RESOURCE = "000000" +RESOURCE = "nuc" +RESOURCEID = "000000" # # # Find this domain by going to the DNS Manager in Linode and then clicking @@ -43,12 +44,14 @@ # Number should be sitting in parentheses next to domain name. # # -DOMAIN = "000000" +DOMAIN = "de.majic.rs" +#DOMAINID = "000000" # # Your Linode API key. You can generate this by going to your profile in the # Linode manager. It should be fairly long. # KEY = "abcdefghijklmnopqrstuvwxyz" +KEY = "982aRGCfRtDxMWHWRF2v6HVnudEdDWyEb1lU1bZlSF4GQP1QM39ec2l5sT0RukOf" # # The URI of a Web service that returns your IP address as plaintext. You are # welcome to leave this at the default value and use mine. If you want to run @@ -58,7 +61,6 @@ # header("Content-type: text/plain"); # printf("%s", $_SERVER["REMOTE_ADDR"]); # -GETIP = "http://icanhazip.com/" # # If for some reason the API URI changes, or you wish to send requests to a # different URI for debugging reasons, edit this. {0} will be replaced with the @@ -68,7 +70,7 @@ # # Comment or remove this line to indicate that you edited the options above. # -exit("Did you edit the options? vi this file open.") +#exit("Did you edit the options? vi this file open.") # # That's it! # @@ -88,21 +90,24 @@ # If you want to see responses for troubleshooting, set this: # DEBUG = False - +CONFIG_SECTION = 'LINODE' +CONFIG_OPTIONS = ['KEY', 'GETIP', 'RESOURCE', 'DOMAIN'] ##################### # STOP EDITING HERE # try: + import os from json import load from urllib.parse import urlencode from urllib.request import urlretrieve + from configparser import SafeConfigParser except Exception as excp: exit("Couldn't import the standard library. Are you running Python 3?") -def execute(action, parameters): +def execute(action, key, parameters): # Execute a query and return a Python dictionary. - uri = "{0}&action={1}".format(API.format(KEY), action) + uri = "{0}&api_action={1}".format(API.format(key), action) if parameters and len(parameters) > 0: uri = "{0}&{1}".format(uri, urlencode(parameters)) if DEBUG: @@ -120,7 +125,7 @@ def execute(action, parameters): err["ERRORMESSAGE"])) return load(open(file), encoding="utf-8") -def ip(): +def ip(GETIP): if DEBUG: print("-->", GETIP) file, headers = urlretrieve(GETIP) @@ -131,13 +136,72 @@ def ip(): print() return open(file).read().strip() +def load_config(): + # determine the conf file based on path and filename + filename = os.path.join(os.path.dirname(__file__), os.path.basename(__file__)[0:-3] + '.conf' ) + config = SafeConfigParser() + + # exit of config file is not created + if not os.path.exists(filename): + print('Config file {0} was not found.\nExiting...\n'.format(filename)) + + exit(-1) + + config.read(filename) + + # validate the file + if not(CONFIG_SECTION == config.default_section or CONFIG_SECTION in config.sections()): + print('[{0}] section is not defined'.format(CONFIG_SECTION)) + exit(-1) + + # validate config options + for option in CONFIG_OPTIONS: + if not config.has_option(CONFIG_SECTION, option): + print("Option '{0}' is not defined in section [{1}]".format(option, CONFIG_SECTION)) + exit(-1) + + if DEBUG: + print("Configuration is loaded") + return config + def main(): try: - res = execute("domainResourceGet", {"DomainID": DOMAIN, "ResourceID": RESOURCE})["DATA"] + # load configuration file + config = load_config() + key = config.get(CONFIG_SECTION, 'KEY') + # obtain list of all domains + res = execute("domain.list", key, None) + + # determine the DOMAINID of our domain + DOMAINID = None + cfg_domain = config.get(CONFIG_SECTION, 'DOMAIN') + for domain in res['DATA']: + if domain['DOMAIN'] == cfg_domain: + DOMAINID = domain['DOMAINID'] + break + + if not DOMAINID: + raise Exception(("Could not determine the DOMAINID for domain '{0}'".format(cfg_domain))) + + # determine the RESOURCEID of configured RESOURCE + cfg_resource = config.get(CONFIG_SECTION, 'RESOURCE') + RESOURCEID = None + # obtain list of resources within domain + res = execute("domain.resource.list", key, {'DOMAINID': DOMAINID}) + # determine the RESOUCEID of resource + for resource in res['DATA']: + if resource['NAME'] == cfg_resource: + RESOURCEID=resource['RESOURCEID'] + break + + if not RESOURCEID: + raise Exception("Could not determine the RESOURCEID for resource '{0}'".format(cfg_resource)) + + res = execute("domain.resource.list", key, {"DomainID": DOMAINID, "ResourceID": RESOURCEID})["DATA"] res = res[0] # Turn res from a list to a dict if(len(res)) == 0: - raise Exception("No such resource?".format(RESOURCE)) - public = ip() + raise Exception("No such resource?".format(RESOURCEID)) + public = ip(config.get(CONFIG_SECTION, 'GETIP')) if res["TARGET"] != public: old = res["TARGET"] request = { @@ -148,7 +212,7 @@ def main(): "Target": public, "TTL_Sec": res["TTL_SEC"] } - execute("domainResourceSave", request) + execute("domain.resource.update", key, request) print("OK {0} -> {1}".format(old, public)) return 1 else: From 12d3ef9c4f9fb9a04abb75c332f8dfa427e8b2fd Mon Sep 17 00:00:00 2001 From: Momcilo Majic Date: Sat, 22 Oct 2016 11:36:58 +0200 Subject: [PATCH 02/10] Added template config file with basic instructions --- LinodeDynDNS.conf.template | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 LinodeDynDNS.conf.template diff --git a/LinodeDynDNS.conf.template b/LinodeDynDNS.conf.template new file mode 100644 index 0000000..5a8f582 --- /dev/null +++ b/LinodeDynDNS.conf.template @@ -0,0 +1,10 @@ +[LINODE] +# configure URL that returns your external IP. Use of https is preferred +GETIP = https://icanhazip.com/ +# configure resource name +RESOURCE = server +# configure domain +DOMAIN = some.domain.com +# copy/past the API key you obtained from linode. +KEY = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + From 0a5798585d5436c7ab345ec3417b823fa0ab2215 Mon Sep 17 00:00:00 2001 From: momcilo78 Date: Sat, 22 Oct 2016 16:24:14 +0200 Subject: [PATCH 03/10] Updated documentation --- LinodeDynDNS.py | 40 ++++++++-------------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/LinodeDynDNS.py b/LinodeDynDNS.py index 82986f0..d6ce22c 100755 --- a/LinodeDynDNS.py +++ b/LinodeDynDNS.py @@ -19,39 +19,19 @@ # # 2. Save it. # -# 3. Go back and edit the A record you just created. Make a note of the -# ResourceID in the URI of the page while editing the record. +# 3. If you did not already create an API key, please generate it +# +# 4. Copy LinodeDynDNS.conf.template into LinodeDynDNS.conf # -# 4. Edit the four configuration options below, following the directions for -# each. As this is a quick hack, it assumes everything goes right. +# 5. Edit LinodeDynDNS.conf # -# First, the resource ID that contains the 'home' record you created above. If -# the URI while editing that A record looks like this: +# 6. Configure your Linode API key # -# linode.com/members/dns/resource_aud.cfm?DomainID=98765&ResourceID=123456 -# You want 123456. The API key MUST have write access to this resource ID. +# 7. Configure your domain # -# As of lately ( 5/2016 ) the DOMAINID is not in the URI -# https://manager.linode.com/dns/resource/domain.com?id=000000 -# Resource ID ^ +# 8. Configure your resource (server) # -RESOURCE = "nuc" -RESOURCEID = "000000" -# -# -# Find this domain by going to the DNS Manager in Linode and then clicking -# check next to the domain associates with the above resource ID. -# Number should be sitting in parentheses next to domain name. -# -# -DOMAIN = "de.majic.rs" -#DOMAINID = "000000" -# -# Your Linode API key. You can generate this by going to your profile in the -# Linode manager. It should be fairly long. -# -KEY = "abcdefghijklmnopqrstuvwxyz" -KEY = "982aRGCfRtDxMWHWRF2v6HVnudEdDWyEb1lU1bZlSF4GQP1QM39ec2l5sT0RukOf" +# 9. Configure the GETIP # # The URI of a Web service that returns your IP address as plaintext. You are # welcome to leave this at the default value and use mine. If you want to run @@ -68,10 +48,6 @@ # API = "https://api.linode.com/api/?api_key={0}&resultFormat=JSON" # -# Comment or remove this line to indicate that you edited the options above. -# -#exit("Did you edit the options? vi this file open.") -# # That's it! # # Now run dyndns.py manually, or add it to cron, or whatever. You can even have From 3d1474b8837c9b1c133e5a1f5cc6db129f6ac92e Mon Sep 17 00:00:00 2001 From: momcilo78 Date: Sat, 22 Oct 2016 16:36:05 +0200 Subject: [PATCH 04/10] Fixed indentation --- LinodeDynDNS.conf.template | 3 +++ LinodeDynDNS.py | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/LinodeDynDNS.conf.template b/LinodeDynDNS.conf.template index 5a8f582..f2277ad 100644 --- a/LinodeDynDNS.conf.template +++ b/LinodeDynDNS.conf.template @@ -1,10 +1,13 @@ [LINODE] # configure URL that returns your external IP. Use of https is preferred GETIP = https://icanhazip.com/ + # configure resource name RESOURCE = server + # configure domain DOMAIN = some.domain.com + # copy/past the API key you obtained from linode. KEY = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/LinodeDynDNS.py b/LinodeDynDNS.py index d6ce22c..7cb9b6c 100755 --- a/LinodeDynDNS.py +++ b/LinodeDynDNS.py @@ -21,17 +21,17 @@ # # 3. If you did not already create an API key, please generate it # -# 4. Copy LinodeDynDNS.conf.template into LinodeDynDNS.conf +# 4. Copy LinodeDynDNS.conf.template into LinodeDynDNS.conf # -# 5. Edit LinodeDynDNS.conf +# 5. Edit LinodeDynDNS.conf # -# 6. Configure your Linode API key +# 6. Configure your Linode API key # -# 7. Configure your domain +# 7. Configure your domain # -# 8. Configure your resource (server) +# 8. Configure your resource (server) # -# 9. Configure the GETIP +# 9. Configure the GETIP # # The URI of a Web service that returns your IP address as plaintext. You are # welcome to leave this at the default value and use mine. If you want to run From d12fac25dc40510a1b9fa5adb0a45df4306390c5 Mon Sep 17 00:00:00 2001 From: momcilo78 Date: Sat, 22 Oct 2016 17:14:08 +0200 Subject: [PATCH 05/10] Create .travis.yml --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c503fee --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: python +python: + - "3.5" +sctip: ls From 3826a9718627578051f3a33298c467833894289a Mon Sep 17 00:00:00 2001 From: momcilo78 Date: Sat, 22 Oct 2016 17:16:04 +0200 Subject: [PATCH 06/10] Create requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ + From 903c1c938db9d950ff7842bd9de433cfe92fa27b Mon Sep 17 00:00:00 2001 From: momcilo78 Date: Sat, 22 Oct 2016 17:19:37 +0200 Subject: [PATCH 07/10] Update .travis.yml --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c503fee..dd0d311 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python python: - "3.5" -sctip: ls +sctip: + - python -m unittest discover; From ccefd8a04dbfa0b08330119a63bcb85043b323d8 Mon Sep 17 00:00:00 2001 From: momcilo78 Date: Sat, 22 Oct 2016 17:21:09 +0200 Subject: [PATCH 08/10] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dd0d311..c2acf14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: python python: - "3.5" -sctip: +script: - python -m unittest discover; From 4b7f30e3dff771528646244835c3f4af0be8a39b Mon Sep 17 00:00:00 2001 From: momcilo78 Date: Sat, 22 Oct 2016 17:24:02 +0200 Subject: [PATCH 09/10] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c2acf14..24feab3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,4 @@ python: - "3.5" script: - python -m unittest discover; + - python LinodeDynDNS.py From 1444e91a64bca37df9c6a13696b6943994570734 Mon Sep 17 00:00:00 2001 From: momcilo78 Date: Sat, 22 Oct 2016 17:25:43 +0200 Subject: [PATCH 10/10] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 24feab3..202abc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: python python: + - "2.7" - "3.5" script: - python -m unittest discover; - - python LinodeDynDNS.py