Viewing / Svedrin / python-django-pdns / pdns/models.py [ raw ] (Rendered using Pygments Lexer: Python)
# -*- coding: utf-8 -*-
import time
from django.conf import settings
from django.db import models
from django.db.models import Q
from django.contrib.auth.models import User
from pdns.conf import settings as pdns_settings
class Supermaster(models.Model):
""" SuperMasters are master servers which are allowed to push domains to us,
even if they are not listed with us as a slave domain yet.
For more info about SuperSlave operation, see:
http://downloads.powerdns.com/documentation/html/generic-mypgsql-backends.html#AEN6061
"""
ip = models.CharField( max_length=25 )
nameserver = models.CharField( 'Name des Nameservers', max_length=255 )
account = models.CharField( 'Accountname', max_length=40, unique=True )
def __unicode__(self):
return self.nameserver
class Meta:
db_table = 'supermasters'
class Domain(models.Model):
""" Registers a domain with the server as a NATIVE, MASTER or SLAVE domain.
In SuperSlave operation, the "account" field will specify which SuperMaster
we received this domain from.
"""
TYPE_CHOICES = (
( "NATIVE", "Nativ: Daten aus DB holen, aber nicht zur Replikation freigeben" ),
( "MASTER", "Master: Wie Nativ, andere Server dürfen replizieren" ),
( "SLAVE", "Slave: Replikation von einem Masterserver" ),
)
name = models.CharField( 'Name der Domain', max_length=255, unique=True )
master = models.CharField( 'Masterserver bei Slaves', max_length=20, blank=True )
last_check = models.DateTimeField( 'Datum der letzten Prüfung auf Änderungen', null=True, blank=True, editable=False )
dtype = models.CharField( 'Typ', db_column="type", max_length=6, choices=TYPE_CHOICES,
default=pdns_settings.DEFAULT_DOMAIN_TYPE )
notified_serial = models.IntegerField( 'Letzte im SOA benutzte Serial', null=True, blank=True, editable=False )
account = models.ForeignKey( Supermaster, db_column="account", null=True, blank=True, to_field="account" )
owner = models.ForeignKey( User, null=True, blank=True )
def __unicode__(self):
return self.name
class Meta:
db_table = 'domains'
soa_record = property(
lambda self: self.record_set.get( rrtype="SOA" ),
doc="The SOA record for this domain."
);
def create_soa_record( self, refresh=None, retry=None, expire=None, default_ttl=None ):
""" Create or change the SOA record for this domain.
Calling this method is NOT necessary to update the serial if you changed
a record, it is ONLY necessary if anything apart from the serial has changed
or no SOA record exists.
"""
if self.owner is not None:
hostmaster = self.owner.email.replace( "@", "." )
else:
hostmaster = pdns_settings.HOSTMASTER_MAILADDR;
if refresh is None: refresh = pdns_settings.DEFAULT_SOA_REFRESH
if retry is None: retry = pdns_settings.DEFAULT_SOA_RETRY
if expire is None: expire = pdns_settings.DEFAULT_SOA_EXPIRE
if default_ttl is None: default_ttl = pdns_settings.DEFAULT_TTL
try:
soa = self.soa_record
except Record.DoesNotExist:
soa = self.record_set.create( name="", rrtype="SOA" )
# The SOA record format is "primary-NS hostmaster serial refresh retry expire default_ttl"
# set serial to 0 to have it auto-assigned by PowerDNS.
soa.content = "%(primns)s %(hostmaster)s 0 %(refresh)d %(retry)d %(expire)d %(ttl)d" % {
'primns': pdns_settings.SERVER_FQDN,
'hostmaster': hostmaster,
'refresh': refresh,
'retry': retry,
'expire': expire,
'ttl': default_ttl,
}
soa.save()
def create_or_update_addr( self, name, address, rrtype="A" ):
""" Make sure `name` is the only RR in this domain that resolves to `address`. """
self.delete_addr( name, address, rrtype )
rec = self.record_set.create( name=name, rrtype=rrtype, content=address )
rec.save()
def delete_addr( self, name, address, rrtype="A" ):
""" Delete all records of the given type with the given name or address. """
self.record_set.filter( ( Q(content=address) | Q(name=name) ) & Q(rrtype=rrtype) ).delete()
def save( self, *args, **kwargs ):
""" Save the Domain, automatically creating a SOA record for new domains. """
makeSoa = ( self.id is None )
models.Model.save( self, *args, **kwargs )
if makeSoa:
self.create_soa_record()
class Record(models.Model):
""" A record for a domain.
For documentation on the fields, see:
http://downloads.powerdns.com/documentation/html/generic-mypgsql-backends.html#AEN5878
"""
TYPE_CHOICES = (
( 'A', 'A: IPv4-Adresse' ),
( 'AAAA', 'AAAA: IPv6-Adresse' ),
( 'CNAME', 'CName: Weiterleitung auf anderen Namen' ),
( 'PTR', 'PTR: Hostname für Reverse-DNS-Eintrag' ),
( 'TXT', 'TXT: Beliebiger Text' ),
( 'SOA', 'SOA: Allgemeine Angaben zur Domain' ),
( 'MX', 'MX: Mailserver' ),
( 'NS', 'NS: Nameserver' ),
( 'SRV', 'SRV: Services' ),
( 'SPF', 'SPF: Sender Permitted From' ),
)
domain = models.ForeignKey( Domain )
name = models.CharField( 'Name', max_length=255, blank=True )
rrtype = models.CharField( 'Klasse', max_length=6, db_column="type", choices=TYPE_CHOICES,
default=pdns_settings.DEFAULT_RECORD_TYPE )
content = models.CharField( 'Eintrag', max_length=255 )
ttl = models.IntegerField( null=True, blank=True, default=pdns_settings.DEFAULT_TTL )
prio = models.IntegerField( null=True, blank=True, default=0 )
change_date = models.IntegerField( editable=False )
def __unicode__(self):
return "%s (%s: %s)" % ( self.name, self.rrtype, self.content )
class Meta:
db_table = 'records'
def get_relative_name( self ):
""" Return the path from the domain name downward. """
if self.name == self.domain.name:
return ''
else:
domlen = len( self.domain.name )
return self.name[:-(domlen+1)]
relname = property( get_relative_name, doc=get_relative_name.__doc__ )
def save( self, *args, **kwargs ):
""" Save the record, making sure the name field contains an FQDN. This will also
update the change_date field to tell PowerDNS that it needs to NOTIFY the domain.
"""
if not self.name.endswith( self.domain.name ):
if self.name:
self.name = "%s.%s" % ( self.name, self.domain.name )
else:
self.name = self.domain.name
self.change_date = time.time()
models.Model.save( self, *args, **kwargs )