179 lines
6.8 KiB
Python
179 lines
6.8 KiB
Python
#!/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")
|
|
|