more cleanup, adding docstrings, bit more exception handling.
This commit is contained in:
parent
94fa3cbb43
commit
fc971391b9
|
@ -1,4 +1,6 @@
|
||||||
from binder import keyutils
|
from binder import keyutils, exceptions
|
||||||
|
# TODO: Start using exceptions here, force a record/add/delete on
|
||||||
|
# an unresponsive Bind server.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import dns.query
|
import dns.query
|
||||||
|
@ -15,10 +17,9 @@ def add_forward_record(dns_server, zone_name, record_name, record_type, record_d
|
||||||
|
|
||||||
dns_update = dns.update.Update(zone_name, keyring = keyring)
|
dns_update = dns.update.Update(zone_name, keyring = keyring)
|
||||||
dns_update.replace(record_name, ttl, record_type, record_data)
|
dns_update.replace(record_name, ttl, record_type, record_data)
|
||||||
|
output = dns.query.tcp(dns_update, dns_server)
|
||||||
|
|
||||||
response = dns.query.tcp(dns_update, dns_server)
|
return output
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
def add_reverse_record(dns_server, zone_name, record_name, record_data, ttl, keyring):
|
def add_reverse_record(dns_server, zone_name, record_name, record_data, ttl, keyring):
|
||||||
""" Given passed arguments, add/update a reverse PTR record."""
|
""" Given passed arguments, add/update a reverse PTR record."""
|
||||||
|
@ -66,10 +67,10 @@ def add_record(form_data):
|
||||||
def add_cname_record(dns_server, zone_name, originating_record, cname, ttl, key_name):
|
def add_cname_record(dns_server, zone_name, originating_record, cname, ttl, key_name):
|
||||||
"""Add a Cname record."""
|
"""Add a Cname record."""
|
||||||
|
|
||||||
if key_name is None:
|
if key_name == "None":
|
||||||
keyring = None
|
keyring = None
|
||||||
else:
|
else:
|
||||||
this_key = models.Key.objects.get(name=str(key_name))
|
this_key = models.Key.objects.get(name=key_name)
|
||||||
keyring = keyutils.create_keyring(this_key.name, this_key.data)
|
keyring = keyutils.create_keyring(this_key.name, this_key.data)
|
||||||
|
|
||||||
update = dns.update.Update(zone_name, keyring = keyring)
|
update = dns.update.Update(zone_name, keyring = keyring)
|
||||||
|
@ -98,6 +99,7 @@ def delete_record(form_data, rr_items):
|
||||||
dns_update = dns.update.Update(domain, keyring = keyring)
|
dns_update = dns.update.Update(domain, keyring = keyring)
|
||||||
dns_update.delete(record)
|
dns_update.delete(record)
|
||||||
output = dns.query.tcp(dns_update, dns_server)
|
output = dns.query.tcp(dns_update, dns_server)
|
||||||
delete_response.append({ "description" : "Delete record %s" % current_rr_item, "output" : output })
|
delete_response.append({ "description" : "Delete record %s" % current_rr_item,
|
||||||
|
"output" : output })
|
||||||
|
|
||||||
return delete_response
|
return delete_response
|
||||||
|
|
|
@ -12,6 +12,6 @@ def create_keyring(key_name, key_data):
|
||||||
|
|
||||||
keyring = dns.tsigkeyring.from_text({
|
keyring = dns.tsigkeyring.from_text({
|
||||||
key_name : key_data
|
key_name : key_data
|
||||||
})
|
})
|
||||||
|
|
||||||
return keyring
|
return keyring
|
||||||
|
|
|
@ -1,27 +1,38 @@
|
||||||
from BeautifulSoup import BeautifulStoneSoup as BS
|
from BeautifulSoup import BeautifulStoneSoup as BS
|
||||||
from django.db import models
|
|
||||||
from binder import exceptions
|
from binder import exceptions
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
import dns.query
|
import dns.query
|
||||||
import dns.zone
|
|
||||||
import dns.tsig
|
import dns.tsig
|
||||||
|
import dns.zone
|
||||||
import keyutils
|
import keyutils
|
||||||
import re
|
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
TSIG_ALGORITHMS = (('hmac-md5', 'MD5'),('hmac-sha1', 'SHA1'),('hmac-224', 'SHA224'),('hmac-sha256', 'SHA256'),('hmac-sha384', 'SHA384'),('hmac-sha512', 'SHA512'))
|
TSIG_ALGORITHMS = (('hmac-md5', 'MD5'),
|
||||||
|
('hmac-sha1', 'SHA1'),
|
||||||
|
('hmac-sha256', 'SHA256'),
|
||||||
|
('hmac-sha384', 'SHA384'),
|
||||||
|
('hmac-sha512', 'SHA512'))
|
||||||
|
|
||||||
class Key(models.Model):
|
class Key(models.Model):
|
||||||
name = models.CharField(max_length=50)
|
""" Class to store and reference TSIG keys.
|
||||||
data = models.CharField(max_length=150)
|
|
||||||
algorithm = models.CharField(max_length=200, choices=TSIG_ALGORITHMS)
|
TODO: Should/Can we encrypt these DNS keys in the DB?
|
||||||
|
"""
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
data = models.CharField(max_length=255)
|
||||||
|
algorithm = models.CharField(max_length=255, choices=TSIG_ALGORITHMS)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class BindServer(models.Model):
|
class BindServer(models.Model):
|
||||||
hostname = models.CharField(max_length=100)
|
""" Class to store DNS servers and attributes for referencing their
|
||||||
|
statistics ports. Also reference FK for TSIG transfer keys,
|
||||||
|
if required.
|
||||||
|
"""
|
||||||
|
hostname = models.CharField(max_length=255)
|
||||||
statistics_port = models.IntegerField()
|
statistics_port = models.IntegerField()
|
||||||
default_transfer_key = models.ForeignKey(Key, null=True, blank=True)
|
default_transfer_key = models.ForeignKey(Key, null=True, blank=True)
|
||||||
|
|
||||||
|
@ -29,12 +40,16 @@ class BindServer(models.Model):
|
||||||
return self.hostname
|
return self.hostname
|
||||||
|
|
||||||
def list_zones(self):
|
def list_zones(self):
|
||||||
""" Take a DNS server, and list the DNS zones it provides resolution for. """
|
""" List the DNS zones and attributes.
|
||||||
# I should take the dns_hostname here, get the object from the DB,
|
|
||||||
# and use the status port attribute for the urllib2 query.
|
TODO: Parse these XML more intelligently. Grab the view name. Any other data available?
|
||||||
myreq = urllib2.Request("http://%s:%s" % (self.hostname, self.statistics_port))
|
|
||||||
|
Returns:
|
||||||
|
List of Dicts { String zone_name, String zone_serial }
|
||||||
|
"""
|
||||||
|
zone_req = urllib2.Request("http://%s:%s" % (self.hostname, self.statistics_port))
|
||||||
try:
|
try:
|
||||||
http_request = urllib2.urlopen(myreq)
|
http_request = urllib2.urlopen(zone_req)
|
||||||
except urllib2.URLError, err:
|
except urllib2.URLError, err:
|
||||||
raise exceptions.ZoneException(err)
|
raise exceptions.ZoneException(err)
|
||||||
|
|
||||||
|
@ -42,7 +57,7 @@ class BindServer(models.Model):
|
||||||
xmloutput = http_request.read()
|
xmloutput = http_request.read()
|
||||||
mysoup = BS(xmloutput)
|
mysoup = BS(xmloutput)
|
||||||
zones = mysoup.findAll('zone')
|
zones = mysoup.findAll('zone')
|
||||||
for current_zone in zones: # Interate over found zones
|
for current_zone in zones:
|
||||||
zone_name = current_zone.find("name").string.split("/IN")[0]
|
zone_name = current_zone.find("name").string.split("/IN")[0]
|
||||||
zone_serial = current_zone.find("serial").string
|
zone_serial = current_zone.find("serial").string
|
||||||
zone_class = current_zone.find("rdataclass").string
|
zone_class = current_zone.find("rdataclass").string
|
||||||
|
@ -51,9 +66,18 @@ class BindServer(models.Model):
|
||||||
|
|
||||||
return return_array
|
return return_array
|
||||||
|
|
||||||
def list_zone_records(self, zone):
|
def list_zone_records(self, zone_name):
|
||||||
"""Given a zone, produce an array of dicts containing
|
""" List all records in a specific zone.
|
||||||
each RR record and its attributes."""
|
|
||||||
|
TODO: Print out current_record in the loop and see if we can parse this more programatically,
|
||||||
|
rather than just splitting on space. What is the difference between class and type?
|
||||||
|
Arguments:
|
||||||
|
String zone_name: Name of the zone
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of Dicts { String rr_name, String rr_ttl, String rr_class, String rr_type, String rr_data }
|
||||||
|
"""
|
||||||
|
|
||||||
if self.default_transfer_key:
|
if self.default_transfer_key:
|
||||||
keyring = keyutils.create_keyring(self.default_transfer_key.name,
|
keyring = keyutils.create_keyring(self.default_transfer_key.name,
|
||||||
self.default_transfer_key.data)
|
self.default_transfer_key.data)
|
||||||
|
@ -61,7 +85,7 @@ class BindServer(models.Model):
|
||||||
keyring = None
|
keyring = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
zone = dns.zone.from_xfr(dns.query.xfr(self.hostname, zone, keyring=keyring))
|
zone = dns.zone.from_xfr(dns.query.xfr(self.hostname, zone_name, keyring=keyring))
|
||||||
except dns.exception.FormError, err:
|
except dns.exception.FormError, err:
|
||||||
raise exceptions.TransferException("There was an error attempting to list zone records.")
|
raise exceptions.TransferException("There was an error attempting to list zone records.")
|
||||||
except dns.tsig.PeerBadKey:
|
except dns.tsig.PeerBadKey:
|
||||||
|
@ -72,11 +96,16 @@ class BindServer(models.Model):
|
||||||
record_array = []
|
record_array = []
|
||||||
for current_name in names:
|
for current_name in names:
|
||||||
current_record = zone[current_name].to_text(current_name)
|
current_record = zone[current_name].to_text(current_name)
|
||||||
for split_record in current_record.split("\n"): # Split the records on the newline
|
for split_record in current_record.split("\n"):
|
||||||
record_array.append({'rr_name' : split_record.split(" ")[0],
|
current_record = split_record.split(" ")
|
||||||
'rr_ttl' : split_record.split(" ")[1],
|
rr_dict = {}
|
||||||
'rr_class' : split_record.split(" ")[2],
|
rr_dict["rr_name"] = current_record[0]
|
||||||
'rr_type' : split_record.split(" ")[3],
|
rr_dict["rr_ttl"] = current_record[1]
|
||||||
'rr_data' : split_record.split(" ")[4]})
|
rr_dict["rr_class"] = current_record[2]
|
||||||
|
rr_dict["rr_type"] = current_record[3]
|
||||||
|
rr_dict["rr_data"] = current_record[4]
|
||||||
|
|
||||||
|
record_array.append(rr_dict)
|
||||||
|
|
||||||
return record_array
|
return record_array
|
||||||
|
|
||||||
|
|
110
binder/views.py
110
binder/views.py
|
@ -1,6 +1,5 @@
|
||||||
from django.shortcuts import redirect, render
|
|
||||||
|
|
||||||
from binder import exceptions, forms, helpers, models
|
from binder import exceptions, forms, helpers, models
|
||||||
|
from django.shortcuts import redirect, render
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
@ -8,12 +7,12 @@ RE_UNICODEARRAY = re.compile(r"u'(.*?)'")
|
||||||
|
|
||||||
def home_index(request):
|
def home_index(request):
|
||||||
""" List the main index page for Binder. """
|
""" List the main index page for Binder. """
|
||||||
return render(request, 'index.htm')
|
return render(request, "index.htm")
|
||||||
|
|
||||||
def view_server_list(request):
|
def view_server_list(request):
|
||||||
""" List the DNS servers configured in the Django DB. """
|
""" List the DNS servers configured in the database. """
|
||||||
server_list = models.BindServer.objects.all().order_by('hostname')
|
server_list = models.BindServer.objects.all().order_by("hostname")
|
||||||
return render(request, 'bcommon/list_servers.htm',
|
return render(request, "bcommon/list_servers.htm",
|
||||||
{ "server_list" : server_list})
|
{ "server_list" : server_list})
|
||||||
|
|
||||||
def view_server_zones(request, dns_server):
|
def view_server_zones(request, dns_server):
|
||||||
|
@ -24,35 +23,35 @@ def view_server_zones(request, dns_server):
|
||||||
this_server = models.BindServer.objects.get(hostname=dns_server)
|
this_server = models.BindServer.objects.get(hostname=dns_server)
|
||||||
zone_array = this_server.list_zones()
|
zone_array = this_server.list_zones()
|
||||||
except models.BindServer.DoesNotExist, err:
|
except models.BindServer.DoesNotExist, err:
|
||||||
errors = "Configured server does not exist: %s" % dns_server
|
errors = "There is no configured server by that name: %s" % dns_server
|
||||||
except exceptions.ZoneException, err:
|
except exceptions.ZoneException, err:
|
||||||
errors = "Unable to list server zones. Error: %s" % err
|
errors = "Unable to list server zones. Error: %s" % err
|
||||||
|
|
||||||
return render(request, 'bcommon/list_server_zones.htm',
|
return render(request, "bcommon/list_server_zones.htm",
|
||||||
{ "errors" : errors,
|
{ "errors" : errors,
|
||||||
"dns_server" : dns_server,
|
"dns_server" : dns_server,
|
||||||
"zone_array" : zone_array})
|
"zone_array" : zone_array})
|
||||||
|
|
||||||
def view_zone_records(request, dns_server, zone_name):
|
def view_zone_records(request, dns_server, zone_name):
|
||||||
""" Display the list of records for a a particular zone."""
|
""" Display the list of records for a particular zone. """
|
||||||
|
# Have we tried here to transfer for a zone that does not exist?
|
||||||
|
# Try with a transfer key and without.
|
||||||
try:
|
try:
|
||||||
this_server = models.BindServer.objects.get(hostname=dns_server)
|
this_server = models.BindServer.objects.get(hostname=dns_server)
|
||||||
zone_array = this_server.list_zone_records(zone_name)
|
zone_array = this_server.list_zone_records(zone_name)
|
||||||
except exceptions.TransferException, err:
|
except exceptions.TransferException, err:
|
||||||
return render(request, 'bcommon/list_zone.htm',
|
return render(request, "bcommon/list_zone.htm",
|
||||||
{ 'errors' : err,
|
{ "errors" : err,
|
||||||
'zone_name' : zone_name})
|
"zone_name" : zone_name})
|
||||||
|
|
||||||
return render(request, 'bcommon/list_zone.htm',
|
return render(request, "bcommon/list_zone.htm",
|
||||||
{ 'zone_array' : zone_array,
|
{ "zone_array" : zone_array,
|
||||||
'dns_server' : dns_server,
|
"dns_server" : dns_server,
|
||||||
'zone_name' : zone_name})
|
"zone_name" : zone_name})
|
||||||
|
|
||||||
def view_add_record(request, dns_server, zone_name):
|
def view_add_record(request, dns_server, zone_name):
|
||||||
""" View to provide form to add a DNS record. """
|
""" View to provide form to add a DNS record. """
|
||||||
print "keys: %r" % models.Key.objects.all()
|
return render(request, "bcommon/add_record_form.htm",
|
||||||
form = forms.FormAddRecord()
|
|
||||||
return render(request, 'bcommon/add_record_form.htm',
|
|
||||||
{ "dns_server" : dns_server,
|
{ "dns_server" : dns_server,
|
||||||
"zone_name" : zone_name,
|
"zone_name" : zone_name,
|
||||||
"tsig_keys" : models.Key.objects.all() })
|
"tsig_keys" : models.Key.objects.all() })
|
||||||
|
@ -61,21 +60,22 @@ def view_add_record_result(request):
|
||||||
""" Process the input given to add a DNS record. """
|
""" Process the input given to add a DNS record. """
|
||||||
errors = ""
|
errors = ""
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return redirect('/')
|
return redirect("/")
|
||||||
|
|
||||||
form = forms.FormAddRecord(request.POST)
|
form = forms.FormAddRecord(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
cd = form.cleaned_data
|
form_cleaned = form.cleaned_data
|
||||||
try:
|
try:
|
||||||
add_record_response = helpers.add_record(cd)
|
add_record_response = helpers.add_record(form_cleaned)
|
||||||
except exceptions.RecordException, err:
|
except exceptions.RecordException, err:
|
||||||
|
# TODO: Start using this exception.
|
||||||
errors = err
|
errors = err
|
||||||
|
|
||||||
return render(request, 'bcommon/response_result.htm',
|
return render(request, "bcommon/response_result.htm",
|
||||||
{ "errors" : errors,
|
{ "errors" : errors,
|
||||||
"response" : add_record_response })
|
"response" : add_record_response })
|
||||||
|
|
||||||
return render(request, 'bcommon/add_record_form.htm',
|
return render(request, "bcommon/add_record_form.htm",
|
||||||
{ "dns_server" : request.POST["dns_server"],
|
{ "dns_server" : request.POST["dns_server"],
|
||||||
"zone_name" : request.POST["zone_name"],
|
"zone_name" : request.POST["zone_name"],
|
||||||
"form_errors" : form.errors,
|
"form_errors" : form.errors,
|
||||||
|
@ -92,25 +92,22 @@ def view_add_cname_record(request, dns_server, zone_name, record_name):
|
||||||
def view_add_cname_result(request):
|
def view_add_cname_result(request):
|
||||||
""" Process input on the CNAME form and provide a response."""
|
""" Process input on the CNAME form and provide a response."""
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return redirect('/')
|
return redirect("/")
|
||||||
|
|
||||||
form = forms.FormAddCnameRecord(request.POST)
|
|
||||||
errors = ""
|
errors = ""
|
||||||
|
form = forms.FormAddCnameRecord(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
cd = form.cleaned_data
|
cd = form.cleaned_data
|
||||||
try:
|
add_cname_response = helpers.add_cname_record(
|
||||||
add_cname_response = helpers.add_cname_record(
|
str(cd["dns_server"]),
|
||||||
str(cd["dns_server"]),
|
str(cd["zone_name"]),
|
||||||
str(cd["zone_name"]),
|
str(cd["originating_record"]),
|
||||||
str(cd["originating_record"]),
|
str(cd["cname"]),
|
||||||
str(cd["cname"]),
|
str(cd["ttl"]),
|
||||||
str(cd["ttl"]),
|
str(cd["key_name"]))
|
||||||
str(cd["key_name"]))
|
|
||||||
except:
|
|
||||||
print "hit exception in view_add_cname_result"
|
|
||||||
|
|
||||||
return render(request, 'bcommon/response_result.htm',
|
return render(request, "bcommon/response_result.htm",
|
||||||
{'response' : add_cname_response })
|
{"response" : add_cname_response })
|
||||||
|
|
||||||
return render(request, "bcommon/add_cname_record_form.htm",
|
return render(request, "bcommon/add_cname_record_form.htm",
|
||||||
{ "dns_server" : request.POST["dns_server"],
|
{ "dns_server" : request.POST["dns_server"],
|
||||||
|
@ -123,40 +120,37 @@ def view_add_cname_result(request):
|
||||||
|
|
||||||
|
|
||||||
def view_delete_record(request):
|
def view_delete_record(request):
|
||||||
"""Provide the initial form for deleting records."""
|
""" Provide the initial form for deleting records. """
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
# Return home. You shouldn't trying to directly acces
|
# Return home. You shouldn't trying to directly acces
|
||||||
# the url for deleting records.
|
# the url for deleting records.
|
||||||
return redirect('/')
|
return redirect("/")
|
||||||
|
|
||||||
dns_server = request.POST['dns_server']
|
dns_server = request.POST["dns_server"]
|
||||||
zone_name = request.POST['zone_name']
|
zone_name = request.POST["zone_name"]
|
||||||
rr_array = request.POST.getlist('rr_array')
|
rr_array = request.POST.getlist("rr_array")
|
||||||
|
|
||||||
return render(request, 'bcommon/delete_record_initial.htm',
|
return render(request, "bcommon/delete_record_initial.htm",
|
||||||
{ 'dns_server' : dns_server,
|
{ "dns_server" : dns_server,
|
||||||
'zone_name' : zone_name,
|
"zone_name" : zone_name,
|
||||||
'rr_array' : rr_array,
|
"rr_array" : rr_array,
|
||||||
'tsig_keys' : models.Key.objects.all() })
|
"tsig_keys" : models.Key.objects.all() })
|
||||||
|
|
||||||
|
|
||||||
def view_delete_result(request):
|
def view_delete_result(request):
|
||||||
""" View that deletes records and returns the response."""
|
""" View that deletes records and returns the response. """
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
# Return home. You shouldn't trying to directly access
|
# Return home. You shouldn't trying to directly access
|
||||||
# the url for deleting records.
|
# the url for deleting records.
|
||||||
return redirect('/')
|
return redirect("/")
|
||||||
|
|
||||||
# What seems like an ugly hack to get around the fact
|
# What seems like an ugly hack to get around the fact
|
||||||
# that the array comes back as Unicode values.
|
# that the array comes back as Unicode values.
|
||||||
rr_unicode_array = request.POST.getlist('rr_array')[0]
|
# TODO: Can we make this cleaner?
|
||||||
|
rr_unicode_array = request.POST.getlist("rr_array")[0]
|
||||||
rr_items = RE_UNICODEARRAY.findall(rr_unicode_array)
|
rr_items = RE_UNICODEARRAY.findall(rr_unicode_array)
|
||||||
|
|
||||||
try:
|
delete_result = helpers.delete_record(request.POST, rr_items)
|
||||||
delete_result = helpers.delete_record(request.POST, rr_items)
|
|
||||||
except exceptions.RecordException, err:
|
|
||||||
return render(request, 'bcommon/response_result.htm.htm',
|
|
||||||
{ "errors" : err })
|
|
||||||
|
|
||||||
return render(request, 'bcommon/response_result.htm',
|
return render(request, "bcommon/response_result.htm",
|
||||||
{ 'response' : delete_result })
|
{ "response" : delete_result })
|
||||||
|
|
Loading…
Reference in New Issue