updated force command, texts, error handling of requests and http code,
updated readme.
This commit is contained in:
parent
a2adf9ecab
commit
e0866f3f86
2
.settings/org.eclipse.core.resources.prefs
Normal file
2
.settings/org.eclipse.core.resources.prefs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding//src/gandi_live_dns.py=utf-8
|
76
README.md
76
README.md
|
@ -6,12 +6,12 @@ This is a simple dynamic DNS updater for the
|
||||||
the zone file for a subdomain of a domain to point at the external IPv4
|
the zone file for a subdomain of a domain to point at the external IPv4
|
||||||
address of the computer it has been run from.
|
address of the computer it has been run from.
|
||||||
|
|
||||||
It has been developed and tested on Debian 8 Jessie GNU/Linux using Python 2.7.
|
It has been developed on Debian 8 Jessie and tested on Debian 9 Sretch GNU/Linux using Python 2.7.
|
||||||
|
|
||||||
With the new v5 Website, Gandi has also launched a
|
With the new v5 Website, Gandi has also launched a
|
||||||
new REST API which makes it easier to communicate via bash/curl or python/requests.
|
new REST API which makes it easier to communicate via bash/curl or python/requests.
|
||||||
|
|
||||||
### Walkthrough
|
### Goal
|
||||||
|
|
||||||
You want your homeserver to be always available at `dynamic.mydomain.tld`.
|
You want your homeserver to be always available at `dynamic.mydomain.tld`.
|
||||||
|
|
||||||
|
@ -20,32 +20,84 @@ First, you must apply for an API key with Gandi. Visit
|
||||||
https://account.gandi.net/en/ and apply for (at least) the production API
|
https://account.gandi.net/en/ and apply for (at least) the production API
|
||||||
key by following their directions.
|
key by following their directions.
|
||||||
|
|
||||||
#### A Record Setup
|
#### A DNS Record
|
||||||
Create the DNS A Records in the GANDI Webinterface which you want to update if your IP changes.
|
Create the DNS A Records in the GANDI Webinterface which you want to update if your IP changes.
|
||||||
|
|
||||||
|
### Debian Package Requirements
|
||||||
|
|
||||||
|
`apt-get update && apt-get upgrade && apt-get install unzip python-requests python-args python-simplejson`
|
||||||
|
|
||||||
#### Git Clone or Download the Script
|
#### Git Clone or Download the Script
|
||||||
Download the Script from [GitHub](https://github.com/cavebeat/gandi_live_dns/archive/master.zip)
|
Download the Script from [GitHub](https://github.com/cavebeat/gandi_live_dns/archive/master.zip) and unzip it.
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
`git clone https://github.com/cavebeat/gandi_live_dns.git`
|
`git clone https://github.com/cavebeat/gandi_live_dns.git`
|
||||||
|
|
||||||
#### Script Configuration
|
#### Script Configuration
|
||||||
Then you'd need to configure the script in the src directory.
|
Then you'd need to configure the script in the src directory.
|
||||||
|
Copy `example.config.py` to `config.py`, and put it in the same directory as the script.
|
||||||
|
|
||||||
Copy `example.config.py` to `config.py`, and put it in the same directory
|
Edit the config file to fit your needs.
|
||||||
as the script.
|
|
||||||
|
##### api_secret
|
||||||
|
Start by retrieving your API Key from the "Security" section in new [Gandi Account admin panel](https://account.gandi.net/) to be able to make authenticated requests to the API.
|
||||||
|
api_secret = '---my_secret_API_KEY----'
|
||||||
|
|
||||||
|
##### api_endpoint
|
||||||
|
Gandiv5 LiveDNS API Location
|
||||||
|
http://doc.livedns.gandi.net/#api-endpoint
|
||||||
|
'''
|
||||||
|
api_endpoint = 'https://dns.beta.gandi.net/api/v5'
|
||||||
|
|
||||||
|
##### domain
|
||||||
|
Your domain with the subdomains to be updated
|
||||||
|
domain = 'mydomain.tld'
|
||||||
|
|
||||||
|
##### subdomains
|
||||||
|
All subdomains which should be updated. They get created if they do not yet exist.
|
||||||
|
subdomains = ["subdomain1", "subdomain2", "subdomain3"]
|
||||||
|
|
||||||
|
The first domain is used to find out the actual IP in the Zone Records.
|
||||||
|
|
||||||
#### Run the script
|
#### Run the script
|
||||||
|
And run the script:
|
||||||
|
`
|
||||||
|
root@dyndns:~/gandi_live_dns-master/src# ./gandi_live_dns.py
|
||||||
|
Checking dynamic IP: 127.0.0.1
|
||||||
|
Checking IP from DNS Record subdomain1: 127.0.0.1
|
||||||
|
IP Address Match - no further action
|
||||||
|
`
|
||||||
|
|
||||||
|
If your IP has changed, it will be detected and the update will be triggered.
|
||||||
|
|
||||||
Make the script executeable.
|
|
||||||
|
|
||||||
`
|
`
|
||||||
$ cd gandi_live_dns/src
|
root@dyndns:~/gandi_live_dns-master/src# ./gandi_live_dns.py
|
||||||
$ chmod +x gandi_live_dns.py
|
Checking dynamic IP: 127.0.0.2
|
||||||
|
Checking IP from DNS Record subdomain1: 127.0.0.1
|
||||||
|
IP Address Mismatch - going to update the DNS Records for the subdomains with new IP 127.0.0.2
|
||||||
|
Status Code: 201 , DNS Record Created , IP updated for subdomain1
|
||||||
|
Status Code: 201 , DNS Record Created , IP updated for subdomain2
|
||||||
|
Status Code: 201 , DNS Record Created , IP updated for subdomain3
|
||||||
`
|
`
|
||||||
And run the script
|
|
||||||
|
#### Command Line Arguments
|
||||||
|
|
||||||
`
|
`
|
||||||
$ ./gandi_live_dns.py
|
root@dyndns:~/gandi_live_dns-master/src# ./gandi_live_dns.py -h
|
||||||
|
usage: gandi_live_dns.py [-h] [-f]
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
-f, --force force an update/create
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
|
The force option runs the script, even when no IP change has been detected.
|
||||||
|
It will update all subdomains and even create them if they are missing in the
|
||||||
|
Zone File/Zone UUID. This can be used if additional/new subdomains get appended to the conig file.
|
||||||
|
|
||||||
|
|
||||||
This DynDNS updater is inspired by https://github.com/jasontbradshaw/gandi-dyndns which worked very well
|
This DynDNS updater is inspired by https://github.com/jasontbradshaw/gandi-dyndns which worked very well
|
||||||
with the classic DNS from Gandiv4 Website and their XML-RPC API.
|
with the classic DNS from Gandiv4 Website and their XML-RPC API.
|
||||||
|
|
BIN
src/config.pyc
BIN
src/config.pyc
Binary file not shown.
|
@ -18,7 +18,7 @@ Gandiv5 LiveDNS API Location
|
||||||
http://doc.livedns.gandi.net/#api-endpoint
|
http://doc.livedns.gandi.net/#api-endpoint
|
||||||
https://dns.beta.gandi.net/api/v5/
|
https://dns.beta.gandi.net/api/v5/
|
||||||
'''
|
'''
|
||||||
api = 'https://dns.beta.gandi.net/api/v5'
|
api_endpoint = 'https://dns.beta.gandi.net/api/v5'
|
||||||
|
|
||||||
#your domain with the subdomains in the zone file/UUID
|
#your domain with the subdomains in the zone file/UUID
|
||||||
domain = 'mydomain.tld'
|
domain = 'mydomain.tld'
|
||||||
|
|
|
@ -32,23 +32,36 @@ def get_uuid():
|
||||||
GET /domains/<DOMAIN>:
|
GET /domains/<DOMAIN>:
|
||||||
|
|
||||||
'''
|
'''
|
||||||
url = config.api + '/domains/' + config.domain
|
url = config.api_endpoint + '/domains/' + config.domain
|
||||||
u = requests.get(url, headers={"X-Api-Key":config.api_secret})
|
u = requests.get(url, headers={"X-Api-Key":config.api_secret})
|
||||||
json_object = json.loads(u._content)
|
json_object = json.loads(u._content)
|
||||||
return json_object['zone_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']
|
||||||
|
exit()
|
||||||
|
|
||||||
def get_dnsip(uuid):
|
def get_dnsip(uuid):
|
||||||
''' find out IP from first Subdomain DNS-Record
|
''' find out IP from first Subdomain DNS-Record
|
||||||
List all records with name "NAME" and type "TYPE" in the zone UUID
|
List all records with name "NAME" and type "TYPE" in the zone UUID
|
||||||
GET /zones/<UUID>/records/<NAME>/<TYPE>:
|
GET /zones/<UUID>/records/<NAME>/<TYPE>:
|
||||||
|
|
||||||
|
The first subdomain from config.subdomain will be used to get
|
||||||
|
the actual DNS Record IP
|
||||||
'''
|
'''
|
||||||
|
|
||||||
url = config.api + '/zones/' + uuid + '/records/' + config.subdomains[0] + '/A'
|
url = config.api_endpoint+ '/zones/' + uuid + '/records/' + config.subdomains[0] + '/A'
|
||||||
headers = {"X-Api-Key":config.api_secret}
|
headers = {"X-Api-Key":config.api_secret}
|
||||||
u = requests.get(url, headers=headers)
|
u = requests.get(url, headers=headers)
|
||||||
json_object = json.loads(u._content)
|
if u.status_code == 200:
|
||||||
print 'Checking IP from DNS Record' , config.subdomains[0], ' : ', json_object['rrset_values'][0].encode('ascii','ignore').strip('\n')
|
json_object = json.loads(u._content)
|
||||||
return json_object['rrset_values'][0].encode('ascii','ignore').strip('\n')
|
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')
|
||||||
|
else:
|
||||||
|
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):
|
def update_records(uuid, dynIP, subdomain):
|
||||||
''' update DNS Records for Subdomains
|
''' update DNS Records for Subdomains
|
||||||
|
@ -60,21 +73,26 @@ def update_records(uuid, dynIP, subdomain):
|
||||||
"rrset_values": ["<VALUE>"]}' \
|
"rrset_values": ["<VALUE>"]}' \
|
||||||
https://dns.beta.gandi.net/api/v5/zones/<UUID>/records/<NAME>/<TYPE>
|
https://dns.beta.gandi.net/api/v5/zones/<UUID>/records/<NAME>/<TYPE>
|
||||||
'''
|
'''
|
||||||
url = config.api + '/zones/' + uuid + '/records/' + subdomain + '/A'
|
url = config.api_endpoint+ '/zones/' + uuid + '/records/' + subdomain + '/A'
|
||||||
payload = {"rrset_ttl": config.ttl, "rrset_values": [dynIP]}
|
payload = {"rrset_ttl": config.ttl, "rrset_values": [dynIP]}
|
||||||
headers = {"Content-Type": "application/json", "X-Api-Key":config.api_secret}
|
headers = {"Content-Type": "application/json", "X-Api-Key":config.api_secret}
|
||||||
record_update = requests.put(url, data=json.dumps(payload), headers=headers)
|
u = requests.put(url, data=json.dumps(payload), headers=headers)
|
||||||
json_object = json.loads(record_update._content)
|
json_object = json.loads(u._content)
|
||||||
print 'Status Code: ', record_update.status_code, ', ', json_object['message'], ', IP updated for', subdomain
|
|
||||||
return True
|
if u.status_code == 201:
|
||||||
|
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']
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main(force_update, verbosity):
|
def main(force_update, verbosity):
|
||||||
|
|
||||||
if force_update:
|
|
||||||
print "force update turned on"
|
|
||||||
if verbosity:
|
if verbosity:
|
||||||
print "verbosity turned on"
|
print "verbosity turned on - not implemented by now"
|
||||||
|
|
||||||
|
|
||||||
#get zone ID from Account
|
#get zone ID from Account
|
||||||
|
@ -85,21 +103,21 @@ def main(force_update, verbosity):
|
||||||
dnsIP = get_dnsip(uuid)
|
dnsIP = get_dnsip(uuid)
|
||||||
|
|
||||||
if force_update:
|
if force_update:
|
||||||
print "Going to update the DNS Records for the subdomains"
|
print "Going to update/create the DNS Records for the subdomains"
|
||||||
for sub in config.subdomains:
|
for sub in config.subdomains:
|
||||||
update_records(uuid, dynIP, sub)
|
update_records(uuid, dynIP, sub)
|
||||||
else:
|
else:
|
||||||
if dynIP == dnsIP:
|
if dynIP == dnsIP:
|
||||||
print "IP Address Match - no further action"
|
print "IP Address Match - no further action"
|
||||||
else:
|
else:
|
||||||
print "IP Address Mismatch - going to update the DNS Records for the subdomains"
|
print "IP Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIP
|
||||||
for sub in config.subdomains:
|
for sub in config.subdomains:
|
||||||
update_records(uuid, dynIP, sub)
|
update_records(uuid, dynIP, sub)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('-v', '--verbose', help="increase output verbosity", action="store_true")
|
parser.add_argument('-v', '--verbose', help="increase output verbosity", action="store_true")
|
||||||
parser.add_argument('-f', '--force', help="force an update", action="store_true")
|
parser.add_argument('-f', '--force', help="force an update/create", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue