api views: reduce code duplication - _update and _delete were almost identical

This commit is contained in:
Thomas Waldmann 2014-11-15 19:10:47 +01:00
parent ae06bb4f73
commit 6ba6628f60

View File

@ -229,10 +229,7 @@ class NicUpdateView(View):
if not ipaddr: # None or '' if not ipaddr: # None or ''
ipaddr = normalize_ip(request.META.get('REMOTE_ADDR')) ipaddr = normalize_ip(request.META.get('REMOTE_ADDR'))
secure = request.is_secure() secure = request.is_secure()
if delete: return _update_or_delete(host, ipaddr, secure, logger=logger, _delete=delete)
return _delete(host, ipaddr, secure, logger=logger)
else:
return _update(host, ipaddr, secure, logger=logger)
class NicDeleteView(NicUpdateView): class NicDeleteView(NicUpdateView):
@ -287,10 +284,7 @@ class AuthorizedNicUpdateView(View):
if not ipaddr: # None or empty string if not ipaddr: # None or empty string
ipaddr = normalize_ip(request.META.get('REMOTE_ADDR')) ipaddr = normalize_ip(request.META.get('REMOTE_ADDR'))
secure = request.is_secure() secure = request.is_secure()
if delete: return _update_or_delete(host, ipaddr, secure, logger=logger, _delete=delete)
return _delete(host, ipaddr, secure, logger=logger)
else:
return _update(host, ipaddr, secure, logger=logger)
class AuthorizedNicDeleteView(AuthorizedNicUpdateView): class AuthorizedNicDeleteView(AuthorizedNicUpdateView):
@ -306,28 +300,30 @@ 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, ipaddr, secure=False, logger=None): def _update_or_delete(host, ipaddr, secure=False, logger=None, _delete=False):
""" """
common code shared by the 2 update views common code shared by the 2 update/delete views
:param host: host object :param host: host object
:param ipaddr: new ip addr (v4 or v6) :param ipaddr: 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
:param _delete: True for delete, False for update
:return: Response object with dyndns2 response :return: Response object with dyndns2 response
""" """
mode = ('update', 'delete')[_delete]
# we are doing abuse / available checks rather late, so the client might # we are doing abuse / available checks rather late, so the client might
# get more specific responses (like 'badagent' or 'notfqdn') by earlier # get more specific responses (like 'badagent' or 'notfqdn') by earlier
# checks. it also avoids some code duplication if done here: # checks. it also avoids some code duplication if done here:
fqdn = host.get_fqdn() fqdn = host.get_fqdn()
if host.abuse or host.abuse_blocked: if host.abuse or host.abuse_blocked:
msg = '%s - received update for host with abuse / abuse_blocked flag set' % (fqdn, ) msg = '%s - received %s for host with abuse / abuse_blocked flag set' % (fqdn, mode, )
logger.warning(msg) logger.warning(msg)
host.register_client_result(msg, fault=False) host.register_client_result(msg, fault=False)
return Response('abuse') return Response('abuse')
if not host.available: if not host.available:
# not available is like it doesn't exist # not available is like it doesn't exist
msg = '%s - received update for unavailable host' % (fqdn, ) msg = '%s - received %s for unavailable host' % (fqdn, mode, )
logger.warning(msg) logger.warning(msg)
host.register_client_result(msg, fault=False) host.register_client_result(msg, fault=False)
return Response('nohost') return Response('nohost')
@ -337,6 +333,7 @@ def _update(host, ipaddr, secure=False, logger=None):
# TODO: reproduce and submit traceback to issue 41 # TODO: reproduce and submit traceback to issue 41
ipaddr = str(ipaddr) ipaddr = str(ipaddr)
kind = check_ip(ipaddr, ('ipv4', 'ipv6')) kind = check_ip(ipaddr, ('ipv4', 'ipv6'))
rdtype = 'A' if kind == 'ipv4' else 'AAAA'
except (ValueError, UnicodeError): except (ValueError, UnicodeError):
# invalid ip address string # invalid ip address string
# 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
@ -346,7 +343,10 @@ def _update(host, ipaddr, secure=False, logger=None):
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)
try: try:
update(fqdn, ipaddr) if _delete:
delete(fqdn, rdtype)
else:
update(fqdn, ipaddr)
except SameIpError: except SameIpError:
msg = '%s - received no-change update, ip: %s tls: %r' % (fqdn, ipaddr, secure) msg = '%s - received no-change update, ip: %s tls: %r' % (fqdn, ipaddr, secure)
logger.warning(msg) logger.warning(msg)
@ -354,119 +354,75 @@ def _update(host, ipaddr, secure=False, logger=None):
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)
msg = '%s - received update that resulted in a dns error [%s], ip: %s tls: %r' % ( msg = '%s - received %s that resulted in a dns error [%s], ip: %s tls: %r' % (
fqdn, msg, ipaddr, secure) fqdn, mode, msg, ipaddr, secure)
logger.error(msg) logger.error(msg)
host.register_server_result(msg, fault=True) host.register_server_result(msg, fault=True)
return Response('dnserr') return Response('dnserr')
else: else:
msg = '%s - received good update -> ip: %s tls: %r' % (fqdn, ipaddr, secure) if _delete:
msg = '%s - received delete for record %s, tls: %r' % (fqdn, rdtype, secure)
else:
msg = '%s - received good update -> ip: %s tls: %r' % (fqdn, ipaddr, secure)
logger.info(msg) logger.info(msg)
host.register_client_result(msg, fault=False) host.register_client_result(msg, fault=False)
# update related hosts if _delete:
for rh in host.relatedhosts.all(): # XXX unclear what to do for "other services" we relay updates to
if rh.available: return Response('deleted %s' % rdtype)
if kind == 'ipv4': else: # update
ifid = rh.interface_id_ipv4.strip() _on_update_success(host, fqdn, kind, ipaddr, secure)
netmask = host.netmask_ipv4 return Response('good %s' % ipaddr)
else: # kind == 'ipv6':
ifid = rh.interface_id_ipv6.strip()
netmask = host.netmask_ipv6 def _on_update_success(host, fqdn, kind, ipaddr, secure):
if not ifid: """after updating the host in dns, do related other updates"""
# ifid can be just left blank if no address record of this type is wanted # update related hosts
continue for rh in host.relatedhosts.all():
if rh.available:
if kind == 'ipv4':
ifid = rh.interface_id_ipv4.strip()
netmask = host.netmask_ipv4
else: # kind == 'ipv6':
ifid = rh.interface_id_ipv6.strip()
netmask = host.netmask_ipv6
if not ifid:
# ifid can be just left blank if no address record of this type is wanted
continue
try:
ifid = IPAddress(ifid)
network = IPNetwork("%s/%d" % (ipaddr, netmask))
rh_ipaddr = str(IPAddress(network.network) + int(ifid))
rh_fqdn = FQDN(rh.name + '.' + fqdn.host, fqdn.domain)
except AddrFormatError as e:
logger.warning("trouble computing address of related host %s [%s]" % (rh, e))
else:
logger.info("updating related host %s -> %s" % (rh_fqdn, rh_ipaddr))
try: try:
ifid = IPAddress(ifid) update(rh_fqdn, rh_ipaddr)
network = IPNetwork("%s/%d" % (ipaddr, netmask)) except SameIpError:
rh_ipaddr = str(IPAddress(network.network) + int(ifid)) msg = '%s - received no-change update, ip: %s tls: %r' % (rh_fqdn, rh_ipaddr, secure)
rh_fqdn = FQDN(rh.name + '.' + fqdn.host, fqdn.domain) logger.warning(msg)
except AddrFormatError as e: host.register_client_result(msg, fault=True)
logger.warning("trouble computing address of related host %s [%s]" % (rh, e)) except (DnsUpdateError, NameServerNotAvailable) as e:
else: msg = str(e)
logger.info("updating related host %s -> %s" % (rh_fqdn, rh_ipaddr)) msg = '%s - received update that resulted in a dns error [%s], ip: %s tls: %r' % (
try: rh_fqdn, msg, rh_ipaddr, secure)
update(rh_fqdn, rh_ipaddr) logger.error(msg)
except SameIpError: host.register_server_result(msg, fault=True)
msg = '%s - received no-change update, ip: %s tls: %r' % (rh_fqdn, rh_ipaddr, secure)
logger.warning(msg)
host.register_client_result(msg, fault=True)
except (DnsUpdateError, NameServerNotAvailable) as e:
msg = str(e)
msg = '%s - received update that resulted in a dns error [%s], ip: %s tls: %r' % (
rh_fqdn, msg, rh_ipaddr, secure)
logger.error(msg)
host.register_server_result(msg, fault=True)
# 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
or or
kind == 'ipv6' and hc.give_ipv6 and hc.service.accept_ipv6): kind == 'ipv6' and hc.give_ipv6 and hc.service.accept_ipv6):
kwargs = dict( kwargs = dict(
name=hc.name, password=hc.password, name=hc.name, password=hc.password,
hostname=hc.hostname, myip=ipaddr, hostname=hc.hostname, myip=ipaddr,
server=hc.service.server, path=hc.service.path, secure=hc.service.secure, server=hc.service.server, path=hc.service.path, secure=hc.service.secure,
) )
try: try:
ddns_client.dyndns2_update(**kwargs) ddns_client.dyndns2_update(**kwargs)
except Exception: except Exception:
# we never want to crash here # we never want to crash here
kwargs.pop('password') kwargs.pop('password')
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)
def _delete(host, ipaddr, secure=False, logger=None):
"""
common code shared by the 2 delete views
:param host: host object
:param ipaddr: ip addr (to determine record type A or AAAA)
:param secure: True if we use TLS/https
:param logger: a logger object
:return: Response object with dyndns2 response
"""
# we are doing abuse / available checks rather late, so the client might
# get more specific responses (like 'badagent' or 'notfqdn') by earlier
# checks. it also avoids some code duplication if done here:
fqdn = host.get_fqdn()
if host.abuse or host.abuse_blocked:
msg = '%s - received delete for host with abuse / abuse_blocked flag set' % (fqdn, )
logger.warning(msg)
host.register_client_result(msg, fault=False)
return Response('abuse')
if not host.available:
# not available is like it doesn't exist
msg = '%s - received delete for unavailable host' % (fqdn, )
logger.warning(msg)
host.register_client_result(msg, fault=False)
return Response('nohost')
try:
# bug in dnspython: crashes if ipaddr is unicode, wants a str!
# https://github.com/rthalley/dnspython/issues/41
# TODO: reproduce and submit traceback to issue 41
ipaddr = str(ipaddr)
kind = check_ip(ipaddr, ('ipv4', 'ipv6'))
except (ValueError, UnicodeError):
# invalid ip address string
# some people manage to even give a non-ascii string instead of an ip addr
msg = '%s - received bad ip address: %r' % (fqdn, ipaddr)
logger.warning(msg)
host.register_client_result(msg, fault=True)
return Response('dnserr') # there should be a better response code for this
host.poke(kind, secure)
try:
rdtype = 'A' if kind == 'ipv4' else 'AAAA'
delete(fqdn, rdtype)
msg = '%s - received delete for record %s, tls: %r' % (fqdn, rdtype, secure)
logger.info(msg)
host.register_client_result(msg, fault=False)
# XXX unclear what to do for "other services" we relay updates to
return Response('deleted %s' % rdtype)
except (DnsUpdateError, NameServerNotAvailable) as e:
msg = str(e)
msg = '%s - received delete for record %s that resulted in a dns error [%s], tls: %r' % (
fqdn, rdtype, msg, secure)
logger.error(msg)
host.register_server_result(msg, fault=True)
return Response('dnserr')