From 49ae3aba325fa8d9ab3d1c8494e7b9fc6a9fc784 Mon Sep 17 00:00:00 2001 From: pj Date: Sat, 27 Feb 2021 00:10:11 +0100 Subject: [PATCH] commit code --- ovh-dns-updater.py | 179 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 ovh-dns-updater.py diff --git a/ovh-dns-updater.py b/ovh-dns-updater.py new file mode 100644 index 0000000..06d182e --- /dev/null +++ b/ovh-dns-updater.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + + +import json +import ovh +import sys +import requests, time + +''' +API credentials +can be provided in variety of ways: +(see https://github.com/ovh/python-ovh#configuration) +-explicitly here : +''' +#client = ovh.Client( +# endpoint = "ovh-eu", +# application_key = "XXXXXXXXXXXXXXXX", +# application_secret = "YYYYYYYYYYYYYYYY", +# consumer_key = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" +# ) +''' +-or in the ENVIRONMENT +-or in a ovh.conf file +in which cases we can call with no argument (the ovh module gets the credentials on its own): +''' +client = ovh.Client() + +default_ttl = 600 # seconds +# ttl = how long will a DNS server cache the value before checking it at the Registrar. Longer value yields faster name resolution most of the time, but less frequent updates + +# list of hosts (=subdomain.domain.tld) to update, each a dictionnary with at least "domain" and "subdomain" defined +hosts = [ + { + "domain": "mydomain.tld", # Required + "subdomain": "www", # Required. Explicit subdomain or empty string "" (for @) or "*" for wildcard + #"ipv6": any_value_except_False # Optional : maintain corresponding record, when possible + "ipv4": False, #explicitly disable modifiying ipv4 (A) records, even if public IPV4 exists (a possibly erroneous record would be left as-is) + #"ttl": 60 # optional : if 'ttl' in specified in host, overrides the global default value + }, + { + "domain": "otherdomain.tld", + "subdomain": "" + # 'ipv4' and 'ipv6' are not listed : automatically maintain any/both records, according to availability + } + ] + +checkDNS_interval_hrs = 12.1 # when the saved IP addresses are old, check the DNS record, even if the addresses did not change +#save last known address in local file. +current_ip_file = "/tmp/current_ip.json" + + +def get_current_ip(v = 4): + url = 'https://api6.ipify.org' if v == 6 else 'https://api.ipify.org' + try : + r = requests.get(url, timeout=5.0) + except requests.exceptions.RequestException as e: + print("failed getting ipv{} address because {} occurred".format(v, str(e))) + return False + return r.text if r.status_code == requests.codes.ok else False + +def update_record(domain, subdomain, new_ip, _ttl = 600): + """ + Update the (A or AAAA) record with the provided IP + """ + + typ = 'AAAA' if ":" in new_ip else 'A' + print("checking record {} for {}.{}".format(typ,subdomain,domain)) + path = "/domain/zone/{}/record".format(domain) + result = client.get(path, + fieldType = typ, + subDomain = subdomain + ) + + if len(result) != 1: + print("### creating NEW record {} for {}.{}".format(typ,subdomain,domain)) + result = client.post(path, + fieldType = typ, + subDomain = subdomain, + target = new_ip, + ttl = _ttl + ) + client.post('/domain/zone/{}/refresh'.format(domain)) + result = client.get(path, + fieldType=typ, + subDomain=subdomain + ) + record_id = result[0] + else : + # record exists + record_id = result[0] + path = "/domain/zone/{}/record/{}".format(domain,record_id) + result = client.get(path) + oldip = result['target'] + print('record exists, with ip :',oldip) + if oldip == new_ip : + print('nothing to do') + return + else : + print('updating to ', new_ip) + result = client.put(path, + subDomain = subdomain, + target = new_ip, + ttl = _ttl + ) + client.post('/domain/zone/{}/refresh'.format(domain)) + #checking changes + result = client.get("/domain/zone/{}/record/{}".format(domain,record_id)) + if new_ip != result['target'] : + raise Exception("Error updating {}.{} with {}".format(subdomain,domain,new_ip)) + + +def delete_record(domain, subdomain, typ): + """ + if it exists, delete an A or AAAA record + (because the corresponding IP is not available) + """ + print("checking record {} for {}.{}".format(typ,subdomain,domain)) + result = client.get("/domain/zone/{}/record".format(domain), + fieldType = typ, + subDomain = subdomain + ) + if len(result) == 1: + # record exists, delete it + record_id = result[0] + print("### deleting record {} for {}.{}".format(typ,subdomain,domain)) + client.delete("/domain/zone/{}/record/{}".format(domain,record_id)) + client.post('/domain/zone/{}/refresh'.format(domain)) + + +#reload saved values +try: + with open(current_ip_file, 'r') as f: + old_time, old_ipv4, old_ipv6 = json.load(f) +except IOError: + print("No old ips recorded") + +current_ipv4 = get_current_ip(4) +current_ipv6 = get_current_ip(6) +print('current ips: {} ; {}'.format(current_ipv4, current_ipv6)) + +if current_ipv4 or current_ipv6 : #we could get at least one address + try : + need_update = (old_ipv4 != current_ipv4) or (old_ipv6 != current_ipv6) or ((old_time - time.time()) > 3600.0 * checkDNS_interval_hrs) + except : #old values do not exist, we must check the records + need_update = True + if need_update : + try : + for host in hosts : + domain = host["domain"] + subdomain = host ["subdomain"] + if ('ipv4' not in host) or (host['ipv4'] != False) : + if current_ipv4 : + ttl = default_ttl if ('ttl' not in host) else host['ttl'] + update_record(domain, subdomain, current_ipv4, _ttl = ttl) + else : + delete_record(domain, subdomain, 'A') + else : + print("Not touching A record for {}.{}, as instructed".format(subdomain, domain)) + if ('ipv6' not in host) or (host['ipv6'] != False) : + if current_ipv6 : + ttl = default_ttl if ('ttl' not in host) else host['ttl'] + update_record(domain, subdomain, current_ipv6, _ttl = ttl) + else : + delete_record(domain, subdomain, 'AAAA') + else : + print("Not touching AAAA record for {}.{}, as instructed".format(subdomain, domain)) + print ("changed , saving new ips: ",current_ipv4, " ", current_ipv6) + #all hosts records have been updated without errors, save current addresses + with open(current_ip_file, 'w') as f: + json.dump([time.time(), current_ipv4, current_ipv6],f) + except Exception as e: #some error occured, + print("error updating records :", str(e)) + pass + else : + print("do nothing") +else : + print("cannot get IPs. Network down? Doing nothing") +