From be3a124fbca4e882ddb11d77d07b51934fd3b340 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Sep 2013 17:02:43 +0200 Subject: [PATCH] implement intelligent updater (only sends ns update if IP really changed) --- nsupdate/main/_tests/test_dnstools.py | 18 +++++++++++++++- nsupdate/main/dnstools.py | 30 ++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/nsupdate/main/_tests/test_dnstools.py b/nsupdate/main/_tests/test_dnstools.py index 04e26a3..c128ded 100644 --- a/nsupdate/main/_tests/test_dnstools.py +++ b/nsupdate/main/_tests/test_dnstools.py @@ -4,13 +4,29 @@ Tests for dnstools module. import pytest -from ..dnstools import (query_ns, parse_name, update_ns, +from ..dnstools import (update, query_ns, parse_name, update_ns, SameIpError, BASEDOMAIN, NONEXISTING_HOST, WWW_HOST, WWW_IPV4_HOST, WWW_IPV4_IP, WWW_IPV6_HOST, WWW_IPV6_IP, ) from dns.resolver import NXDOMAIN +class TestIntelligentUpdater(object): + def test_double_update(self): + host, ip = 'test0.' + BASEDOMAIN, '1.2.3.4' + # make sure the host is not there + try: + update_ns(host, 'A', action='del') + except NXDOMAIN: + # it is ok if it was never there + pass + # first update with this IP, should work without issue: + update(host, ip) + with pytest.raises(SameIpError): + # trying to update again with same IP should raise + update(host, ip) + + class TestQuery(object): def test_queries_ok(self): assert query_ns(WWW_IPV4_HOST, 'A') == WWW_IPV4_IP # v4 ONLY diff --git a/nsupdate/main/dnstools.py b/nsupdate/main/dnstools.py index 885850e..1cf0450 100644 --- a/nsupdate/main/dnstools.py +++ b/nsupdate/main/dnstools.py @@ -1,8 +1,10 @@ -""" +s""" Misc. DNS related code: query, dynamic update, etc. """ from django.conf import settings + +import dns.inet import dns.name import dns.resolver import dns.query @@ -25,6 +27,32 @@ UPDATE_ALGO = dns.tsig.HMAC_SHA512 UPDATE_KEY = settings.UPDATE_KEY +class SameIpError(ValueError): + """ + raised if an IP address is already present in DNS and and update was + requested, but is not needed. + """ + + +def update(fqdn, ipaddr, ttl=60): + af = dns.inet.af_for_address(ipaddr) + rdtype = 'A' if af == dns.inet.AF_INET else 'AAAA' + try: + current_ipaddr = query_ns(fqdn, rdtype) + # check if ip really changed + ok = ipaddr != current_ipaddr + except dns.resolver.NXDOMAIN: + # no dns entry yet, ok + ok = True + if ok: + # only send an update if the ip really changed as the update + # causes write I/O on the nameserver and also traffic to the + # dns slaves (they get a notify if we update the zone). + update_ns(fqdn, rdtype, ipaddr, action='upd', ttl=ttl) + else: + raise SameIpError + + def query_ns(qname, rdtype): """ query a dns name from our master server