use FQDN typed params in api, remove unused parse_name (which was problematic anyway)
This commit is contained in:
parent
f13d985f4a
commit
cf2c46e612
@ -32,7 +32,7 @@ SECURE = False # TLS/SNI support on python 2.x sucks :(
|
|||||||
|
|
||||||
from django.utils.translation import activate
|
from django.utils.translation import activate
|
||||||
|
|
||||||
from nsupdate.main.dnstools import update_ns
|
from nsupdate.main.dnstools import update_ns, FQDN
|
||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture(scope="function")
|
@pytest.yield_fixture(scope="function")
|
||||||
@ -43,13 +43,14 @@ def ddns_hostname():
|
|||||||
"""
|
"""
|
||||||
hostname = "test%d" % randint(1000000000, 2000000000)
|
hostname = "test%d" % randint(1000000000, 2000000000)
|
||||||
yield hostname
|
yield hostname
|
||||||
update_ns(hostname + '.' + TESTDOMAIN, 'A', action='del')
|
fqdn = FQDN(hostname, TESTDOMAIN)
|
||||||
update_ns(hostname + '.' + TESTDOMAIN, 'AAAA', action='del')
|
update_ns(fqdn, 'A', action='del')
|
||||||
|
update_ns(fqdn, 'AAAA', action='del')
|
||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture(scope="function")
|
@pytest.yield_fixture(scope="function")
|
||||||
def ddns_fqdn(ddns_hostname):
|
def ddns_fqdn(ddns_hostname):
|
||||||
yield ddns_hostname + '.' + TESTDOMAIN
|
yield FQDN(ddns_hostname, TESTDOMAIN)
|
||||||
|
|
||||||
|
|
||||||
# Note: fixture must be "function" scope (default), see https://github.com/pelme/pytest_django/issues/33
|
# Note: fixture must be "function" scope (default), see https://github.com/pelme/pytest_django/issues/33
|
||||||
|
@ -4,7 +4,7 @@ Tests for api package.
|
|||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from nsupdate.main.dnstools import query_ns
|
from nsupdate.main.dnstools import query_ns, FQDN
|
||||||
from nsupdate.main.models import Domain
|
from nsupdate.main.models import Domain
|
||||||
|
|
||||||
from conftest import TESTDOMAIN, TEST_HOST, TEST_HOST2, TEST_SECRET, TEST_SECRET2
|
from conftest import TESTDOMAIN, TEST_HOST, TEST_HOST2, TEST_SECRET, TEST_SECRET2
|
||||||
@ -13,7 +13,7 @@ USERNAME = 'test'
|
|||||||
PASSWORD = 'pass'
|
PASSWORD = 'pass'
|
||||||
|
|
||||||
BASEDOMAIN = "nsupdate.info"
|
BASEDOMAIN = "nsupdate.info"
|
||||||
HOSTNAME = 'nsupdate-ddns-client-unittest.' + BASEDOMAIN
|
TEST_HOST_OTHER = FQDN('nsupdate-ddns-client-unittest', BASEDOMAIN)
|
||||||
|
|
||||||
|
|
||||||
def test_myip(client):
|
def test_myip(client):
|
||||||
@ -124,14 +124,14 @@ def test_nic_update_authorized_update_other_services(client):
|
|||||||
# must be good (was different IP)
|
# must be good (was different IP)
|
||||||
assert response.content == b'good 1.2.3.4'
|
assert response.content == b'good 1.2.3.4'
|
||||||
# now check if it updated the other service also:
|
# now check if it updated the other service also:
|
||||||
assert query_ns(HOSTNAME, 'A') == '1.2.3.4'
|
assert query_ns(TEST_HOST_OTHER, 'A') == '1.2.3.4'
|
||||||
response = client.get(reverse('nic_update') + '?myip=2.3.4.5',
|
response = client.get(reverse('nic_update') + '?myip=2.3.4.5',
|
||||||
HTTP_AUTHORIZATION=make_basic_auth_header(TEST_HOST, TEST_SECRET))
|
HTTP_AUTHORIZATION=make_basic_auth_header(TEST_HOST, TEST_SECRET))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
# must be good (was different IP)
|
# must be good (was different IP)
|
||||||
assert response.content == b'good 2.3.4.5'
|
assert response.content == b'good 2.3.4.5'
|
||||||
# now check if it updated the other service also:
|
# now check if it updated the other service also:
|
||||||
assert query_ns(HOSTNAME, 'A') == '2.3.4.5'
|
assert query_ns(TEST_HOST_OTHER, 'A') == '2.3.4.5'
|
||||||
|
|
||||||
|
|
||||||
def test_nic_update_authorized_badagent(client, settings):
|
def test_nic_update_authorized_badagent(client, settings):
|
||||||
|
@ -224,9 +224,9 @@ class NicUpdateView(View):
|
|||||||
ipaddr = request.META.get('REMOTE_ADDR')
|
ipaddr = request.META.get('REMOTE_ADDR')
|
||||||
secure = request.is_secure()
|
secure = request.is_secure()
|
||||||
if delete:
|
if delete:
|
||||||
return _delete(host, hostname, ipaddr, secure, logger=logger)
|
return _delete(host, ipaddr, secure, logger=logger)
|
||||||
else:
|
else:
|
||||||
return _update(host, hostname, ipaddr, secure, logger=logger)
|
return _update(host, ipaddr, secure, logger=logger)
|
||||||
|
|
||||||
|
|
||||||
class NicDeleteView(NicUpdateView):
|
class NicDeleteView(NicUpdateView):
|
||||||
@ -282,9 +282,9 @@ class AuthorizedNicUpdateView(View):
|
|||||||
ipaddr = request.META.get('REMOTE_ADDR')
|
ipaddr = request.META.get('REMOTE_ADDR')
|
||||||
secure = request.is_secure()
|
secure = request.is_secure()
|
||||||
if delete:
|
if delete:
|
||||||
return _delete(host, hostname, ipaddr, secure, logger=logger)
|
return _delete(host, ipaddr, secure, logger=logger)
|
||||||
else:
|
else:
|
||||||
return _update(host, hostname, ipaddr, secure, logger=logger)
|
return _update(host, ipaddr, secure, logger=logger)
|
||||||
|
|
||||||
|
|
||||||
class AuthorizedNicDeleteView(AuthorizedNicUpdateView):
|
class AuthorizedNicDeleteView(AuthorizedNicUpdateView):
|
||||||
@ -300,12 +300,11 @@ class AuthorizedNicDeleteView(AuthorizedNicUpdateView):
|
|||||||
return super(AuthorizedNicDeleteView, self).get(request, logger=logger, delete=delete)
|
return super(AuthorizedNicDeleteView, self).get(request, logger=logger, delete=delete)
|
||||||
|
|
||||||
|
|
||||||
def _update(host, hostname, ipaddr, secure=False, logger=None):
|
def _update(host, ipaddr, secure=False, logger=None):
|
||||||
"""
|
"""
|
||||||
common code shared by the 2 update views
|
common code shared by the 2 update views
|
||||||
|
|
||||||
:param host: host object
|
:param host: host object
|
||||||
:param hostname: hostname (fqdn)
|
|
||||||
:param ipaddr: new ip addr (v4 or v6)
|
:param ipaddr: new ip addr (v4 or v6)
|
||||||
:param secure: True if we use TLS/https
|
:param secure: True if we use TLS/https
|
||||||
:param logger: a logger object
|
:param logger: a logger object
|
||||||
@ -330,9 +329,10 @@ def _update(host, hostname, ipaddr, secure=False, logger=None):
|
|||||||
# some people manage to even give a non-ascii string instead of an ip addr
|
# some people manage to even give a non-ascii string instead of an ip addr
|
||||||
return Response('dnserr') # there should be a better response code for this
|
return Response('dnserr') # there should be a better response code for this
|
||||||
host.poke(kind, secure)
|
host.poke(kind, secure)
|
||||||
|
fqdn = host.get_fqdn()
|
||||||
try:
|
try:
|
||||||
update(hostname, ipaddr)
|
update(fqdn, ipaddr)
|
||||||
logger.info('%s - received good update -> ip: %s tls: %r' % (hostname, ipaddr, secure))
|
logger.info('%s - received good update -> ip: %s tls: %r' % (fqdn, ipaddr, secure))
|
||||||
# now check if there are other services we shall relay updates to:
|
# now check if there are other services we shall relay updates to:
|
||||||
for hc in host.serviceupdaterhostconfigs.all():
|
for hc in host.serviceupdaterhostconfigs.all():
|
||||||
if (kind == 'ipv4' and hc.give_ipv4 and hc.service.accept_ipv4
|
if (kind == 'ipv4' and hc.give_ipv4 and hc.service.accept_ipv4
|
||||||
@ -351,23 +351,22 @@ def _update(host, hostname, ipaddr, secure=False, logger=None):
|
|||||||
logger.exception("the dyndns2 updater raised an exception [%r]" % kwargs)
|
logger.exception("the dyndns2 updater raised an exception [%r]" % kwargs)
|
||||||
return Response('good %s' % ipaddr)
|
return Response('good %s' % ipaddr)
|
||||||
except SameIpError:
|
except SameIpError:
|
||||||
logger.warning('%s - received no-change update, ip: %s tls: %r' % (hostname, ipaddr, secure))
|
logger.warning('%s - received no-change update, ip: %s tls: %r' % (fqdn, ipaddr, secure))
|
||||||
host.register_client_fault()
|
host.register_client_fault()
|
||||||
return Response('nochg %s' % ipaddr)
|
return Response('nochg %s' % ipaddr)
|
||||||
except (DnsUpdateError, NameServerNotAvailable) as e:
|
except (DnsUpdateError, NameServerNotAvailable) as e:
|
||||||
msg = str(e)
|
msg = str(e)
|
||||||
logger.error('%s - received update that resulted in a dns error [%s], ip: %s tls: %r' % (
|
logger.error('%s - received update that resulted in a dns error [%s], ip: %s tls: %r' % (
|
||||||
hostname, msg, ipaddr, secure))
|
fqdn, msg, ipaddr, secure))
|
||||||
host.register_server_fault()
|
host.register_server_fault()
|
||||||
return Response('dnserr')
|
return Response('dnserr')
|
||||||
|
|
||||||
|
|
||||||
def _delete(host, hostname, ipaddr, secure=False, logger=None):
|
def _delete(host, ipaddr, secure=False, logger=None):
|
||||||
"""
|
"""
|
||||||
common code shared by the 2 delete views
|
common code shared by the 2 delete views
|
||||||
|
|
||||||
:param host: host object
|
:param host: host object
|
||||||
:param hostname: hostname (fqdn)
|
|
||||||
:param ipaddr: ip addr (to determine record type A or AAAA)
|
:param ipaddr: ip addr (to determine record type A or AAAA)
|
||||||
:param secure: True if we use TLS/https
|
:param secure: True if we use TLS/https
|
||||||
:param logger: a logger object
|
:param logger: a logger object
|
||||||
@ -392,15 +391,16 @@ def _delete(host, hostname, ipaddr, secure=False, logger=None):
|
|||||||
# some people manage to even give a non-ascii string instead of an ip addr
|
# some people manage to even give a non-ascii string instead of an ip addr
|
||||||
return Response('dnserr') # there should be a better response code for this
|
return Response('dnserr') # there should be a better response code for this
|
||||||
host.poke(kind, secure)
|
host.poke(kind, secure)
|
||||||
|
fqdn = host.get_fqdn()
|
||||||
try:
|
try:
|
||||||
rdtype = 'A' if kind == 'ipv4' else 'AAAA'
|
rdtype = 'A' if kind == 'ipv4' else 'AAAA'
|
||||||
delete(hostname, rdtype)
|
delete(fqdn, rdtype)
|
||||||
logger.info('%s - received delete for record %s, tls: %r' % (hostname, rdtype, secure))
|
logger.info('%s - received delete for record %s, tls: %r' % (fqdn, rdtype, secure))
|
||||||
# XXX unclear what to do for "other services" we relay updates to
|
# XXX unclear what to do for "other services" we relay updates to
|
||||||
return Response('deleted %s' % rdtype)
|
return Response('deleted %s' % rdtype)
|
||||||
except (DnsUpdateError, NameServerNotAvailable) as e:
|
except (DnsUpdateError, NameServerNotAvailable) as e:
|
||||||
msg = str(e)
|
msg = str(e)
|
||||||
logger.error('%s - received delete for record %s that resulted in a dns error [%s], tls: %r' % (
|
logger.error('%s - received delete for record %s that resulted in a dns error [%s], tls: %r' % (
|
||||||
hostname, rdtype, msg, secure))
|
fqdn, rdtype, msg, secure))
|
||||||
host.register_server_fault()
|
host.register_server_fault()
|
||||||
return Response('dnserr')
|
return Response('dnserr')
|
||||||
|
@ -10,12 +10,12 @@ pytestmark = pytest.mark.django_db
|
|||||||
|
|
||||||
from dns.resolver import NXDOMAIN, NoAnswer
|
from dns.resolver import NXDOMAIN, NoAnswer
|
||||||
|
|
||||||
from ..dnstools import (add, delete, update, query_ns, rev_lookup, parse_name, update_ns,
|
from ..dnstools import (add, delete, update, query_ns, rev_lookup, update_ns,
|
||||||
SameIpError, DnsUpdateError, FQDN)
|
SameIpError, DnsUpdateError, FQDN)
|
||||||
|
|
||||||
# see also conftest.py
|
# see also conftest.py
|
||||||
BASEDOMAIN = 'nsupdate.info'
|
BASEDOMAIN = 'nsupdate.info'
|
||||||
INVALID_HOST = 'test999.' + BASEDOMAIN # this can't get updated
|
INVALID_HOST = FQDN('test999', BASEDOMAIN) # this can't get updated
|
||||||
|
|
||||||
|
|
||||||
class TestFQDN(object):
|
class TestFQDN(object):
|
||||||
@ -128,23 +128,6 @@ class TestReverseLookup(object):
|
|||||||
|
|
||||||
|
|
||||||
class TestUpdate(object):
|
class TestUpdate(object):
|
||||||
def test_parse1(self):
|
|
||||||
host, domain = 'test', BASEDOMAIN
|
|
||||||
origin, relname = parse_name(host + '.' + domain)
|
|
||||||
assert str(origin) == domain + '.'
|
|
||||||
assert str(relname) == host
|
|
||||||
|
|
||||||
def test_parse2(self):
|
|
||||||
host, domain = 'prefix.test', BASEDOMAIN
|
|
||||||
origin, relname = parse_name(host + '.' + domain)
|
|
||||||
assert str(origin) == domain + '.'
|
|
||||||
assert str(relname) == 'prefix.test'
|
|
||||||
|
|
||||||
def test_parse_with_origin(self):
|
|
||||||
origin, relname = parse_name('foo.bar.baz.org', 'bar.baz.org')
|
|
||||||
assert str(origin) == 'bar.baz.org' + '.'
|
|
||||||
assert str(relname) == 'foo'
|
|
||||||
|
|
||||||
def test_add_del_v4(self, ddns_fqdn):
|
def test_add_del_v4(self, ddns_fqdn):
|
||||||
host, ip = ddns_fqdn, '1.1.1.1'
|
host, ip = ddns_fqdn, '1.1.1.1'
|
||||||
remove_records(host)
|
remove_records(host)
|
||||||
|
@ -98,22 +98,22 @@ def check_ip(ipaddr, keys=('ipv4', 'ipv6')):
|
|||||||
return keys[af == dns.inet.AF_INET6]
|
return keys[af == dns.inet.AF_INET6]
|
||||||
|
|
||||||
|
|
||||||
def add(fqdn, ipaddr, ttl=60, origin=None):
|
def add(fqdn, ipaddr, ttl=60):
|
||||||
"""
|
"""
|
||||||
intelligent dns adder - first does a lookup on the master server to find
|
intelligent dns adder - first does a lookup on the master server to find
|
||||||
the current ip and only sends an 'add' if there is no such entry.
|
the current ip and only sends an 'add' if there is no such entry.
|
||||||
otherwise send an 'upd' if the if we have a different ip.
|
otherwise send an 'upd' if the if we have a different ip.
|
||||||
|
|
||||||
:param fqdn: fully qualified domain name (str)
|
:param fqdn: fully qualified domain name (FQDN)
|
||||||
:param ipaddr: new ip address
|
:param ipaddr: new ip address
|
||||||
:param ttl: time to live, default 60s (int)
|
:param ttl: time to live, default 60s (int)
|
||||||
:param origin: origin zone (optional, str)
|
|
||||||
:raises: SameIpError if new and old IP is the same
|
:raises: SameIpError if new and old IP is the same
|
||||||
:raises: ValueError if ipaddr is no valid ip address string
|
:raises: ValueError if ipaddr is no valid ip address string
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(fqdn, FQDN)
|
||||||
rdtype = check_ip(ipaddr, keys=('A', 'AAAA'))
|
rdtype = check_ip(ipaddr, keys=('A', 'AAAA'))
|
||||||
try:
|
try:
|
||||||
current_ipaddr = query_ns(fqdn, rdtype, origin=origin)
|
current_ipaddr = query_ns(fqdn, rdtype)
|
||||||
# check if ip really changed
|
# check if ip really changed
|
||||||
ok = ipaddr != current_ipaddr
|
ok = ipaddr != current_ipaddr
|
||||||
action = 'upd'
|
action = 'upd'
|
||||||
@ -125,43 +125,43 @@ def add(fqdn, ipaddr, ttl=60, origin=None):
|
|||||||
# only send an add/update if the ip really changed as the update
|
# only send an add/update if the ip really changed as the update
|
||||||
# causes write I/O on the nameserver and also traffic to the
|
# causes write I/O on the nameserver and also traffic to the
|
||||||
# dns slaves (they get a notify if we update the zone).
|
# dns slaves (they get a notify if we update the zone).
|
||||||
update_ns(fqdn, rdtype, ipaddr, action=action, ttl=ttl, origin=origin)
|
update_ns(fqdn, rdtype, ipaddr, action=action, ttl=ttl)
|
||||||
else:
|
else:
|
||||||
raise SameIpError
|
raise SameIpError
|
||||||
|
|
||||||
|
|
||||||
def delete(fqdn, rdtype=None, origin=None):
|
def delete(fqdn, rdtype=None):
|
||||||
"""
|
"""
|
||||||
dns deleter
|
dns deleter
|
||||||
|
|
||||||
:param fqdn: fully qualified domain name (str)
|
:param fqdn: fully qualified domain name (FQDN)
|
||||||
:param rdtype: 'A', 'AAAA' or None (deletes 'A' and 'AAAA')
|
:param rdtype: 'A', 'AAAA' or None (deletes 'A' and 'AAAA')
|
||||||
:param origin: origin zone (optional, str)
|
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(fqdn, FQDN)
|
||||||
if rdtype is not None:
|
if rdtype is not None:
|
||||||
assert rdtype in ['A', 'AAAA', ]
|
assert rdtype in ['A', 'AAAA', ]
|
||||||
rdtypes = [rdtype, ]
|
rdtypes = [rdtype, ]
|
||||||
else:
|
else:
|
||||||
rdtypes = ['A', 'AAAA']
|
rdtypes = ['A', 'AAAA']
|
||||||
for rdtype in rdtypes:
|
for rdtype in rdtypes:
|
||||||
update_ns(fqdn, rdtype, action='del', origin=origin)
|
update_ns(fqdn, rdtype, action='del')
|
||||||
|
|
||||||
|
|
||||||
def update(fqdn, ipaddr, ttl=60, origin=None):
|
def update(fqdn, ipaddr, ttl=60):
|
||||||
"""
|
"""
|
||||||
intelligent dns updater - first does a lookup on the master server to find
|
intelligent dns updater - first does a lookup on the master server to find
|
||||||
the current ip and only sends a dynamic update if we have a different ip.
|
the current ip and only sends a dynamic update if we have a different ip.
|
||||||
|
|
||||||
:param fqdn: fully qualified domain name (str)
|
:param fqdn: fully qualified domain name (FQDN)
|
||||||
:param ipaddr: new ip address
|
:param ipaddr: new ip address
|
||||||
:param ttl: time to live, default 60s (int)
|
:param ttl: time to live, default 60s (int)
|
||||||
:param origin: origin zone (optional, str)
|
|
||||||
:raises: SameIpError if new and old IP is the same
|
:raises: SameIpError if new and old IP is the same
|
||||||
:raises: ValueError if ipaddr is no valid ip address string
|
:raises: ValueError if ipaddr is no valid ip address string
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(fqdn, FQDN)
|
||||||
rdtype = check_ip(ipaddr, keys=('A', 'AAAA'))
|
rdtype = check_ip(ipaddr, keys=('A', 'AAAA'))
|
||||||
try:
|
try:
|
||||||
current_ipaddr = query_ns(fqdn, rdtype, origin=origin)
|
current_ipaddr = query_ns(fqdn, rdtype)
|
||||||
# check if ip really changed
|
# check if ip really changed
|
||||||
ok = ipaddr != current_ipaddr
|
ok = ipaddr != current_ipaddr
|
||||||
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||||
@ -171,27 +171,23 @@ def update(fqdn, ipaddr, ttl=60, origin=None):
|
|||||||
# only send an update if the ip really changed as the update
|
# only send an update if the ip really changed as the update
|
||||||
# causes write I/O on the nameserver and also traffic to the
|
# causes write I/O on the nameserver and also traffic to the
|
||||||
# dns slaves (they get a notify if we update the zone).
|
# dns slaves (they get a notify if we update the zone).
|
||||||
update_ns(fqdn, rdtype, ipaddr, action='upd', ttl=ttl, origin=origin)
|
update_ns(fqdn, rdtype, ipaddr, action='upd', ttl=ttl)
|
||||||
else:
|
else:
|
||||||
raise SameIpError
|
raise SameIpError
|
||||||
|
|
||||||
|
|
||||||
def query_ns(qname, rdtype, origin=None):
|
def query_ns(fqdn, rdtype):
|
||||||
"""
|
"""
|
||||||
query a dns name from our master server
|
query a dns name from our master server
|
||||||
|
|
||||||
:param qname: the query name
|
:param fqdn: fqdn to query the name server for
|
||||||
:type qname: dns.name.Name object or str
|
:type fqdn: dnstools.FQDN
|
||||||
:param rdtype: the query type
|
:param rdtype: the query type
|
||||||
:type rdtype: int or str
|
:type rdtype: int or str
|
||||||
:param origin: origin zone
|
|
||||||
:type origin: str or None
|
|
||||||
:return: IP (as str)
|
:return: IP (as str)
|
||||||
:raises: see dns.resolver.Resolver.query
|
:raises: see dns.resolver.Resolver.query
|
||||||
"""
|
"""
|
||||||
origin, name = parse_name(qname, origin)
|
assert isinstance(fqdn, FQDN)
|
||||||
fqdn = name + origin
|
|
||||||
assert fqdn.is_absolute()
|
|
||||||
nameserver, origin = get_ns_info(fqdn)[0:2]
|
nameserver, origin = get_ns_info(fqdn)[0:2]
|
||||||
resolver = dns.resolver.Resolver(configure=False)
|
resolver = dns.resolver.Resolver(configure=False)
|
||||||
# we do not configure it from resolv.conf, but patch in the values we
|
# we do not configure it from resolv.conf, but patch in the values we
|
||||||
@ -204,13 +200,13 @@ def query_ns(qname, rdtype, origin=None):
|
|||||||
# (used if flags = None is given). Thus, we explicitly give flags (all off):
|
# (used if flags = None is given). Thus, we explicitly give flags (all off):
|
||||||
resolver.flags = 0
|
resolver.flags = 0
|
||||||
try:
|
try:
|
||||||
answer = resolver.query(fqdn, rdtype)
|
answer = resolver.query(str(fqdn), rdtype)
|
||||||
ip = str(list(answer)[0])
|
ip = str(list(answer)[0])
|
||||||
logger.debug("query: %s answer: %s" % (fqdn.to_text(), ip))
|
logger.debug("query: %s answer: %s" % (fqdn, ip))
|
||||||
return ip
|
return ip
|
||||||
except (dns.resolver.Timeout, dns.resolver.NoNameservers): # socket.error also?
|
except (dns.resolver.Timeout, dns.resolver.NoNameservers): # socket.error also?
|
||||||
logger.warning("timeout when querying for name '%s' in zone '%s' with rdtype '%s'." % (
|
logger.warning("timeout when querying for name '%s' in zone '%s' with rdtype '%s'." % (
|
||||||
name, origin, rdtype))
|
fqdn.host, origin, rdtype))
|
||||||
set_ns_availability(origin, False)
|
set_ns_availability(origin, False)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@ -233,53 +229,27 @@ def rev_lookup(ipaddr):
|
|||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
def parse_name(fqdn, origin=None):
|
def get_ns_info(fqdn):
|
||||||
"""
|
"""
|
||||||
Parse a fully qualified domain name into a relative name
|
Get the master nameserver for fqdn, the key secret needed to update the zone and the key algorithm used.
|
||||||
and a origin zone. Please note that the origin return value will
|
|
||||||
have a trailing dot.
|
|
||||||
|
|
||||||
:param fqdn: fully qualified domain name (str)
|
|
||||||
:param origin: origin zone (optional, str)
|
|
||||||
:return: origin, relative name (both dns.name.Name)
|
|
||||||
"""
|
|
||||||
fqdn = dns.name.from_text(fqdn)
|
|
||||||
if origin is None:
|
|
||||||
origin = dns.resolver.zone_for_name(fqdn)
|
|
||||||
rel_name = fqdn.relativize(origin)
|
|
||||||
else:
|
|
||||||
origin = dns.name.from_text(origin)
|
|
||||||
rel_name = fqdn - origin
|
|
||||||
return origin, rel_name
|
|
||||||
|
|
||||||
|
|
||||||
def get_ns_info(fqdn, origin=None):
|
|
||||||
"""
|
|
||||||
Get the master nameserver for the <origin> zone, the key secret needed
|
|
||||||
to update the zone and the key algorithm used.
|
|
||||||
|
|
||||||
:param fqdn: the fully qualified hostname we are dealing with (str)
|
:param fqdn: the fully qualified hostname we are dealing with (str)
|
||||||
:param origin: zone we are dealing with, must be with trailing dot (default:autodetect) (str)
|
|
||||||
:return: master nameserver, origin, domain, update keyname, update secret, update algo
|
:return: master nameserver, origin, domain, update keyname, update secret, update algo
|
||||||
:raises: NameServerNotAvailable if ns was flagged unavailable in the db
|
:raises: NameServerNotAvailable if ns was flagged unavailable in the db
|
||||||
"""
|
"""
|
||||||
fqdn_str = str(fqdn)
|
assert isinstance(fqdn, FQDN)
|
||||||
origin, name = parse_name(fqdn_str, origin)
|
|
||||||
origin_str = str(origin)
|
|
||||||
from .models import Domain
|
from .models import Domain
|
||||||
try:
|
try:
|
||||||
# first we check if we have an entry for the fqdn
|
# first we check if we have an entry for the fqdn
|
||||||
# single-host update secret use case
|
# single-host update secret use case
|
||||||
# XXX we need 2 DB accesses for the usual case just to support this rare case
|
# XXX we need 2 DB accesses for the usual case just to support this rare case
|
||||||
domain = fqdn_str.rstrip('.')
|
domain = str(fqdn)
|
||||||
d = Domain.objects.get(domain=domain)
|
d = Domain.objects.get(domain=domain)
|
||||||
keyname = fqdn_str
|
|
||||||
except Domain.DoesNotExist:
|
except Domain.DoesNotExist:
|
||||||
# now check the base zone, the usual case
|
# now check the base zone, the usual case
|
||||||
# zone update secret use case
|
# zone update secret use case
|
||||||
domain = origin_str.rstrip('.')
|
domain = fqdn.domain
|
||||||
d = Domain.objects.get(domain=domain)
|
d = Domain.objects.get(domain=domain)
|
||||||
keyname = origin_str
|
|
||||||
if not d.available:
|
if not d.available:
|
||||||
if d.last_update + timedelta(seconds=UNAVAILABLE_RETRY) > now():
|
if d.last_update + timedelta(seconds=UNAVAILABLE_RETRY) > now():
|
||||||
# if there are troubles with a nameserver, we set available=False
|
# if there are troubles with a nameserver, we set available=False
|
||||||
@ -290,24 +260,24 @@ def get_ns_info(fqdn, origin=None):
|
|||||||
# retry timeout is over, set it available again
|
# retry timeout is over, set it available again
|
||||||
set_ns_availability(domain, True)
|
set_ns_availability(domain, True)
|
||||||
algorithm = getattr(dns.tsig, d.nameserver_update_algorithm)
|
algorithm = getattr(dns.tsig, d.nameserver_update_algorithm)
|
||||||
return d.nameserver_ip, origin, domain, name, keyname, d.nameserver_update_secret, algorithm
|
return d.nameserver_ip, fqdn.domain, domain, fqdn.host, domain, d.nameserver_update_secret, algorithm
|
||||||
|
|
||||||
|
|
||||||
def update_ns(fqdn, rdtype='A', ipaddr=None, origin=None, action='upd', ttl=60):
|
def update_ns(fqdn, rdtype='A', ipaddr=None, action='upd', ttl=60):
|
||||||
"""
|
"""
|
||||||
update the master server
|
update the master server
|
||||||
|
|
||||||
:param fqdn: the fully qualified domain name to update (str)
|
:param fqdn: the fully qualified domain name to update (FQDN)
|
||||||
:param rdtype: the record type (default: 'A') (str)
|
:param rdtype: the record type (default: 'A') (str)
|
||||||
:param ipaddr: ip address (v4 or v6), if needed (str)
|
:param ipaddr: ip address (v4 or v6), if needed (str)
|
||||||
:param origin: the origin zone to update (default; autodetect) (str)
|
|
||||||
:param action: 'add', 'del' or 'upd'
|
:param action: 'add', 'del' or 'upd'
|
||||||
:param ttl: time to live for the added/updated resource, default 60s (int)
|
:param ttl: time to live for the added/updated resource, default 60s (int)
|
||||||
:return: dns response
|
:return: dns response
|
||||||
:raises: DnsUpdateError, Timeout
|
:raises: DnsUpdateError, Timeout
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(fqdn, FQDN)
|
||||||
assert action in ['add', 'del', 'upd', ]
|
assert action in ['add', 'del', 'upd', ]
|
||||||
nameserver, origin, domain, name, keyname, key, algo = get_ns_info(fqdn, origin)
|
nameserver, origin, domain, name, keyname, key, algo = get_ns_info(fqdn)
|
||||||
upd = dns.update.Update(origin,
|
upd = dns.update.Update(origin,
|
||||||
keyring=dns.tsigkeyring.from_text({keyname: key}),
|
keyring=dns.tsigkeyring.from_text({keyname: key}),
|
||||||
keyalgorithm=algo)
|
keyalgorithm=algo)
|
||||||
|
@ -181,7 +181,7 @@ class Host(models.Model):
|
|||||||
index_together = (('subdomain', 'domain'), )
|
index_together = (('subdomain', 'domain'), )
|
||||||
|
|
||||||
def get_fqdn(self):
|
def get_fqdn(self):
|
||||||
return '%s.%s' % (self.subdomain, self.domain.domain)
|
return dnstools.FQDN(self.subdomain, self.domain.domain)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_fqdn(cls, fqdn, **kwargs):
|
def get_by_fqdn(cls, fqdn, **kwargs):
|
||||||
@ -202,7 +202,7 @@ class Host(models.Model):
|
|||||||
def get_ip(self, kind):
|
def get_ip(self, kind):
|
||||||
record = 'A' if kind == 'ipv4' else 'AAAA'
|
record = 'A' if kind == 'ipv4' else 'AAAA'
|
||||||
try:
|
try:
|
||||||
return dnstools.query_ns(self.get_fqdn(), record, origin=self.domain.domain)
|
return dnstools.query_ns(self.get_fqdn(), record)
|
||||||
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||||
return 'none'
|
return 'none'
|
||||||
except (dns.resolver.NoNameservers, dns.resolver.Timeout, dnstools.NameServerNotAvailable):
|
except (dns.resolver.NoNameservers, dns.resolver.Timeout, dnstools.NameServerNotAvailable):
|
||||||
@ -250,7 +250,7 @@ class Host(models.Model):
|
|||||||
def pre_delete_host(sender, **kwargs):
|
def pre_delete_host(sender, **kwargs):
|
||||||
obj = kwargs['instance']
|
obj = kwargs['instance']
|
||||||
try:
|
try:
|
||||||
dnstools.delete(obj.get_fqdn(), origin=obj.domain.domain)
|
dnstools.delete(obj.get_fqdn())
|
||||||
except (dnstools.Timeout, dnstools.NameServerNotAvailable):
|
except (dnstools.Timeout, dnstools.NameServerNotAvailable):
|
||||||
# well, we tried to clean up, but we didn't reach the nameserver
|
# well, we tried to clean up, but we didn't reach the nameserver
|
||||||
pass
|
pass
|
||||||
|
@ -194,7 +194,7 @@ class OverviewView(CreateView):
|
|||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
self.object = form.save(commit=False)
|
self.object = form.save(commit=False)
|
||||||
try:
|
try:
|
||||||
dnstools.add(self.object.get_fqdn(), self.request.META['REMOTE_ADDR'], origin=self.object.domain.domain)
|
dnstools.add(self.object.get_fqdn(), self.request.META['REMOTE_ADDR'])
|
||||||
except dnstools.Timeout:
|
except dnstools.Timeout:
|
||||||
success, level, msg = False, messages.ERROR, 'Timeout - communicating to name server failed.'
|
success, level, msg = False, messages.ERROR, 'Timeout - communicating to name server failed.'
|
||||||
except dnstools.NameServerNotAvailable:
|
except dnstools.NameServerNotAvailable:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user