diff --git a/config-template.txt b/config-template.txt index 82ac92b9dd35aacc5d2914abdfed33e47b1de34e..fb46cffe2d60d55c5538ba8dc58f5e9d1053b241 100644 --- a/config-template.txt +++ b/config-template.txt @@ -14,3 +14,7 @@ ttl = 900 # Production API api = https://dns.api.gandi.net/api/v5/ + +# Number of retries for looking up our own IP address, since +# the service seems to be flaky sometimes. +retries = 3 diff --git a/gandi_ddns.py b/gandi_ddns.py index dcf6bd39e7bb59254d218cf1169db7cb6ba65dbe..170a41f00f20b8bb1c2da0bfab60a4a832fae59d 100755 --- a/gandi_ddns.py +++ b/gandi_ddns.py @@ -5,30 +5,55 @@ import requests import json import ipaddress from datetime import datetime +import time config_file = "config.txt" SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +DEFAULT_RETRIES = 3 -def get_ip(): + +class GandiDdnsError(Exception): + pass + + +def get_ip_inner(): #Get external IP try: - # Could be any service that just gives us a simple raw ASCII IP address (not HTML etc) - r = requests.get('https://api.ipify.org', timeout=3) - except Exception: - print('Failed to retrieve external IP.') - sys.exit(2) + # Could be any service that just gives us a simple raw ASCII IP address (not HTML etc) + r = requests.get('https://api.ipify.org', timeout=3) + except requests.exceptions.RequestException: + raise GandiDdnsError('Failed to retrieve external IP.') if r.status_code != 200: - print(('Failed to retrieve external IP. Server responded with status_code: %d' % r.status_code)) - sys.exit(2) + raise GandiDdnsError( + 'Failed to retrieve external IP.' + ' Server responded with status_code: %d' % r.status_code) ip = r.text.rstrip() # strip \n and any trailing whitespace if not(ipaddress.IPv4Address(ip)): # check if valid IPv4 address - sys.exit(2) + raise GandiDdnsError('Got invalid IP: ' + ip) return ip + +def get_ip(retries): + #Get external IP with retries + + # Start at 5 seconds, double on every retry. + retry_delay_time = 5 + for attempt in range(retries): + try: + return get_ip_inner() + except GandiDdnsError as e: + print('Getting external IP failed: %s' % e) + print('Waiting for %d seconds before trying again' % retry_delay_time) + time.sleep(retry_delay_time) + # Double retry time, cap at 60s. + retry_delay_time = min(60, 2 * retry_delay_time) + print('Exhausted retry attempts') + sys.exit(2) + def read_config(config_path): #Read configuration file cfg = configparser.ConfigParser() @@ -75,7 +100,8 @@ def main(): url = '%sdomains/%s/records/%s/A' % (config.get(section, 'api'), config.get(section, 'domain'), config.get(section, 'a_name')) print(url) #Discover External IP - external_ip = get_ip() + retries = config.get(section, 'retries', fallback=DEFAULT_RETRIES) + external_ip = get_ip(retries) print(('External IP is: %s' % external_ip)) #Prepare record @@ -95,4 +121,4 @@ def main(): update_record(url, headers, payload) if __name__ == "__main__": - main() \ No newline at end of file + main()