diff --git a/binder/exceptions.py b/binder/exceptions.py index f05272d..1d8e420 100644 --- a/binder/exceptions.py +++ b/binder/exceptions.py @@ -17,7 +17,7 @@ class ZoneException(Exception): class RecordException(Exception): - """Thrown when there is an issue dealign with a Record. + """Thrown when there is an issue dealing with a Record. * Adding or deleting. """ diff --git a/binder/forms.py b/binder/forms.py index 53fd31c..42e0f2b 100644 --- a/binder/forms.py +++ b/binder/forms.py @@ -17,7 +17,8 @@ class CustomUnicodeListField(forms.CharField): try: string_list = [str(cur_rr) for cur_rr in eval(value)] except: - raise ValidationError("Error in converting Unicode list to list of Strings: %r" % value) + raise ValidationError("Error in converting Unicode list to list " + "of strings: %r" % value) return string_list @@ -37,7 +38,8 @@ class CustomStringPeriodSuffix(forms.CharField): if new_string[-1] != ".": new_string += "." except: - raise ValidationError("Unable to stick a period on the end of your input: %r" % value) + raise ValidationError("Unable to stick a period on the end of " + "your input: %r" % value) return new_string @@ -50,13 +52,17 @@ class FormAddForwardRecord(forms.Form): record_name = forms.RegexField(max_length=100, regex="^[a-zA-Z0-9-_]+$", required=False) - record_type = forms.ChoiceField(choices=settings.RECORD_TYPE_CHOICES) + record_type = forms.ChoiceField(choices=settings.RECORD_TYPE_CHOICES, + widget=forms.RadioSelect) zone_name = forms.CharField(max_length=100) record_data = forms.GenericIPAddressField() - ttl = forms.ChoiceField(choices=settings.TTL_CHOICES) + ttl = forms.ChoiceField(choices=settings.TTL_CHOICES, + widget=forms.RadioSelect) create_reverse = forms.BooleanField(required=False) key_name = forms.ModelChoiceField(queryset=Key.objects.all(), - required=False) + required=False, + widget=forms.RadioSelect, + empty_label=None) class FormAddReverseRecord(forms.Form): @@ -66,12 +72,16 @@ class FormAddReverseRecord(forms.Form): dns_server = forms.CharField(max_length=100) record_name = forms.IntegerField(min_value=0, max_value=255) record_type = forms.RegexField(regex=r"^PTR$", - error_messages={"invalid": "The only valid choice here is PTR."}) + error_messages={ + "invalid": "The only valid choice here " + "is PTR."}) zone_name = forms.CharField(max_length=100) record_data = CustomStringPeriodSuffix(required=True) ttl = forms.ChoiceField(choices=settings.TTL_CHOICES) key_name = forms.ModelChoiceField(queryset=Key.objects.all(), - required=False) + required=False, + widget=forms.RadioSelect, + empty_label=None) create_reverse = forms.BooleanField(required=False) @@ -83,9 +93,12 @@ class FormAddCnameRecord(forms.Form): originating_record = forms.CharField(max_length=100) cname = forms.RegexField(max_length=100, regex="^[a-zA-Z0-9-_]+$") zone_name = forms.CharField(max_length=256) - ttl = forms.ChoiceField(choices=settings.TTL_CHOICES) + ttl = forms.ChoiceField(choices=settings.TTL_CHOICES, + widget=forms.RadioSelect) key_name = forms.ModelChoiceField(queryset=Key.objects.all(), - required=False) + required=False, + widget=forms.RadioSelect, + empty_label=None) class FormDeleteRecord(forms.Form): @@ -96,4 +109,6 @@ class FormDeleteRecord(forms.Form): zone_name = forms.CharField(max_length=256) rr_list = CustomUnicodeListField() key_name = forms.ModelChoiceField(queryset=Key.objects.all(), - required=False) + required=False, + widget=forms.RadioSelect, + empty_label=None) diff --git a/binder/helpers.py b/binder/helpers.py index 45ea517..8c8114b 100644 --- a/binder/helpers.py +++ b/binder/helpers.py @@ -1,11 +1,13 @@ # Binder Helpers # Standard Imports +import logging import re import socket # 3rd Party import dns.query +import dns.rcode import dns.reversename import dns.tsig import dns.tsigkeyring @@ -13,6 +15,7 @@ import dns.update # App Imports from binder import models +from binder.exceptions import KeyringException, RecordException def add_record(dns_server, zone_name, record_name, record_type, record_data, @@ -89,11 +92,13 @@ def delete_record(dns_server, rr_list, key_name): """Delete a list of DNS records passed as strings in rr_items.""" server = models.BindServer.objects.get(hostname=dns_server) + logger = logging.getLogger('binder.helpers') try: transfer_key = models.Key.objects.get(name=key_name) - except models.Key.DoesNotExist: - keyring = None - algorithm = None + except models.Key.DoesNotExist as exc: + logger.error(exc) + raise KeyringException("The specified TSIG key %s does not exist in " + "binders configuration." % key_name) else: keyring = transfer_key.create_keyring() algorithm = transfer_key.algorithm @@ -107,13 +112,19 @@ def delete_record(dns_server, rr_list, key_name): keyring=keyring, keyalgorithm=algorithm) dns_update.delete(record) - output = send_dns_update(dns_update, - dns_server, - server.dns_port, - key_name) - - delete_response.append({"description": "Delete Record: %s" % current_rr, - "output": output}) + try: + output = send_dns_update(dns_update, + dns_server, + server.dns_port, + key_name) + except (KeyringException, RecordException) as exc: + delete_response.append({"description": exc, + "record": current_rr, + "success": False}) + else: + delete_response.append({"description": output, + "record": current_rr, + "success": True}) return delete_response @@ -123,11 +134,13 @@ def create_update(dns_server, zone_name, record_name, record_type, record_data, """Update/Create DNS record of name and type with passed data and ttl.""" server = models.BindServer.objects.get(hostname=dns_server) + logger = logging.getLogger('binder.helpers') try: transfer_key = models.Key.objects.get(name=key_name) - except models.Key.DoesNotExist: - keyring = None - algorithm = None + except models.Key.DoesNotExist as exc: + logger.error(exc) + raise KeyringException("The specified TSIG key %s does not exist in " + "binders configuration." % key_name) else: keyring = transfer_key.create_keyring() algorithm = transfer_key.algorithm @@ -174,13 +187,21 @@ def send_dns_update(dns_message, dns_server, port, key_name): Returns: String output """ + logger = logging.getLogger('binder.helpers') try: output = dns.query.tcp(dns_message, dns_server, port=port) - except dns.tsig.PeerBadKey: - output = ("DNS server %s is not configured for TSIG key: %s." % - (dns_server, key_name)) - except dns.tsig.PeerBadSignature: - output = ("DNS server %s did like the TSIG signature we sent. Check " - "key %s for correctness." % (dns_server, key_name)) - + except dns.tsig.PeerBadKey as exc: + logger.error(exc) + raise KeyringException("DNS server %s is not configured for TSIG key: %s." % + (dns_server, key_name)) + except dns.tsig.PeerBadSignature as exc: + logger.error(exc) + raise KeyringException("DNS server %s didn't like the TSIG signature " + "we sent. Check key %s for correctness." % + (dns_server, key_name)) + logger.debug(output) + return_code = output.rcode() + if return_code != dns.rcode.NOERROR: + raise RecordException('Error when requesting DNS server %s: %s' % + (dns_server, dns.rcode.to_text(return_code))) return output diff --git a/binder/settings.py b/binder/settings.py index a23da1a..d0ecfaa 100644 --- a/binder/settings.py +++ b/binder/settings.py @@ -1,5 +1,6 @@ # Django settings for binder project. import os +from django.contrib.messages import constants as messages SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) DEBUG = True @@ -88,6 +89,38 @@ INSTALLED_APPS = ( TEST_RUNNER = 'django.test.runner.DiscoverRunner' +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s", + 'datefmt' : "%d/%b/%Y %H:%M:%S" + } + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'verbose' + }, + }, + 'loggers': { + 'django': { + 'handlers': ['console'], + 'propagate': True, + 'level': 'INFO' + }, + 'binder': { + 'handlers': ['console'], + 'level': 'DEBUG' + } + } +} + +MESSAGE_TAGS = { + messages.ERROR: 'danger' +} + TTL_CHOICES = ((300, "5 minutes"), (1800, "30 minutes"), (3600, "1 hour"), diff --git a/binder/templates/base.html b/binder/templates/base.html index d9a6ef6..4d191fa 100644 --- a/binder/templates/base.html +++ b/binder/templates/base.html @@ -10,7 +10,6 @@ {% endblock header %} -
@@ -35,6 +34,8 @@ + +