Fix to pass PEP8 check and tests

This commit is contained in:
rdujardin 2016-08-03 12:51:24 +02:00
parent 50dc519a42
commit f873848e29
15 changed files with 650 additions and 618 deletions

View File

@ -12,6 +12,7 @@ class ZoneAdmin(admin.ModelAdmin):
'soa_name': ['name'], 'soa_name': ['name'],
} }
@admin.register(Record) @admin.register(Record)
class RecordAdmin(admin.ModelAdmin): class RecordAdmin(admin.ModelAdmin):
list_display = ['name', 'zone', 'record_type', 'priority', 'address', 'value'] list_display = ['name', 'zone', 'record_type', 'priority', 'address', 'value']

View File

@ -7,12 +7,14 @@ from dns.models import Zone, Record
# Zones # Zones
# #
class ZoneSerializer(serializers.ModelSerializer): class ZoneSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model=Zone model = Zone
fields = ['id', 'name', 'ttl', 'soa_name', 'soa_contact', 'soa_serial', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'description'] fields = ['id', 'name', 'ttl', 'soa_name', 'soa_contact', 'soa_serial', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'description']
class ZoneNestedSerializer(ZoneSerializer): class ZoneNestedSerializer(ZoneSerializer):
class Meta(ZoneSerializer.Meta): class Meta(ZoneSerializer.Meta):
@ -23,15 +25,17 @@ class ZoneNestedSerializer(ZoneSerializer):
# Records # Records
# #
class RecordSerializer(serializers.ModelSerializer): class RecordSerializer(serializers.ModelSerializer):
zone = ZoneNestedSerializer() zone = ZoneNestedSerializer()
address = IPAddressNestedSerializer() address = IPAddressNestedSerializer()
class Meta: class Meta:
model=Record model = Record
fields = ['id', 'name', 'record_type', 'priority', 'zone', 'address', 'value', 'description'] fields = ['id', 'name', 'record_type', 'priority', 'zone', 'address', 'value', 'description']
class RecordNestedSerializer(RecordSerializer): class RecordNestedSerializer(RecordSerializer):
class Meta(RecordSerializer.Meta): class Meta(RecordSerializer.Meta):

View File

@ -14,6 +14,7 @@ from . import serializers
# Zones # Zones
# #
class ZoneListView(generics.ListAPIView): class ZoneListView(generics.ListAPIView):
""" """
List all zones List all zones
@ -22,6 +23,7 @@ class ZoneListView(generics.ListAPIView):
serializer_class = serializers.ZoneSerializer serializer_class = serializers.ZoneSerializer
filter_class = filters.ZoneFilter filter_class = filters.ZoneFilter
class ZoneDetailView(generics.RetrieveAPIView): class ZoneDetailView(generics.RetrieveAPIView):
""" """
Retrieve a single zone Retrieve a single zone
@ -29,10 +31,12 @@ class ZoneDetailView(generics.RetrieveAPIView):
queryset = Zone.objects.all() queryset = Zone.objects.all()
serializer_class = serializers.ZoneSerializer serializer_class = serializers.ZoneSerializer
# #
# Records # Records
# #
class RecordListView(generics.ListAPIView): class RecordListView(generics.ListAPIView):
""" """
List all records List all records
@ -40,6 +44,7 @@ class RecordListView(generics.ListAPIView):
queryset = Record.objects.all() queryset = Record.objects.all()
serializer_class = serializers.RecordSerializer serializer_class = serializers.RecordSerializer
class RecordDetailView(generics.RetrieveAPIView): class RecordDetailView(generics.RetrieveAPIView):
""" """
Retrieve a single record Retrieve a single record
@ -47,10 +52,12 @@ class RecordDetailView(generics.RetrieveAPIView):
queryset = Record.objects.all() queryset = Record.objects.all()
serializer_class = serializers.RecordSerializer serializer_class = serializers.RecordSerializer
# #
# BIND Exports # BIND Exports
# #
@api_view(['GET']) @api_view(['GET'])
def bind_forward(request): def bind_forward(request):
""" """
@ -59,6 +66,7 @@ def bind_forward(request):
zones_list = export_bind_forward() zones_list = export_bind_forward()
return Response(zones_list) return Response(zones_list)
@api_view(['GET']) @api_view(['GET'])
def bind_reverse(request): def bind_reverse(request):
""" """
@ -66,4 +74,3 @@ def bind_reverse(request):
""" """
zones_list = export_bind_reverse() zones_list = export_bind_reverse()
return Response(zones_list) return Response(zones_list)

View File

@ -3,4 +3,4 @@ from django.apps import AppConfig
class DNSConfig(AppConfig): class DNSConfig(AppConfig):
name = 'dns' name = 'dns'
verbose_name='DNS' verbose_name = 'DNS'

View File

@ -8,41 +8,43 @@ from .models import (
) )
from .forms import record_type_choices from .forms import record_type_choices
class ZoneFilter(django_filters.FilterSet): class ZoneFilter(django_filters.FilterSet):
name = django_filters.CharFilter( name = django_filters.CharFilter(
name = 'name', name='name',
lookup_type = 'icontains', lookup_type='icontains',
label = 'Name', label='Name',
) )
class Meta: class Meta:
model = Zone model = Zone
fields = ['name'] fields = ['name']
class RecordFilter(django_filters.FilterSet): class RecordFilter(django_filters.FilterSet):
zone__name = django_filters.ModelMultipleChoiceFilter( zone__name = django_filters.ModelMultipleChoiceFilter(
name = 'zone__name', name='zone__name',
to_field_name = 'name', to_field_name='name',
lookup_type = 'icontains', lookup_type='icontains',
queryset = Zone.objects.all(), queryset=Zone.objects.all(),
label = 'Zone (Name)', label='Zone (Name)',
) )
record_type = django_filters.MultipleChoiceFilter( record_type = django_filters.MultipleChoiceFilter(
name = 'record_type', name='record_type',
label = 'Type', label='Type',
choices = record_type_choices choices=record_type_choices
) )
name = django_filters.CharFilter( name = django_filters.CharFilter(
name = 'name', name='name',
lookup_type = 'icontains', lookup_type='icontains',
label = 'Name', label='Name',
) )
name_or_value_or_ip = django_filters.MethodFilter( name_or_value_or_ip = django_filters.MethodFilter(
name = 'name_or_value_or_ip', name='name_or_value_or_ip',
) )
class Meta: class Meta:
model=Record model = Record
field = ['name', 'record_type', 'value'] field = ['name', 'record_type', 'value']
def filter_name_or_value_or_ip(self, queryset, value): def filter_name_or_value_or_ip(self, queryset, value):

View File

@ -37,5 +37,3 @@ class AddressFormField(forms.Field):
return obj return obj
else: else:
return ip[0] return ip[0]

View File

@ -17,10 +17,11 @@ from .formfields import AddressFormField
# Zones # Zones
# #
class ZoneForm(forms.ModelForm, BootstrapMixin): class ZoneForm(forms.ModelForm, BootstrapMixin):
class Meta: class Meta:
model=Zone model = Zone
fields = ['name', 'ttl', 'soa_name', 'soa_contact', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'description'] fields = ['name', 'ttl', 'soa_name', 'soa_contact', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'description']
labels = { labels = {
'soa_name': 'SOA Name', 'soa_name': 'SOA Name',
@ -41,15 +42,18 @@ class ZoneForm(forms.ModelForm, BootstrapMixin):
'soa_minimum': "Negative result TTL, in seconds", 'soa_minimum': "Negative result TTL, in seconds",
} }
class ZoneFromCSVForm(forms.ModelForm): class ZoneFromCSVForm(forms.ModelForm):
class Meta: class Meta:
model=Zone model = Zone
fields = ['name', 'ttl', 'soa_name', 'soa_contact', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'description'] fields = ['name', 'ttl', 'soa_name', 'soa_contact', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'description']
class ZoneImportForm(BulkImportForm, BootstrapMixin): class ZoneImportForm(BulkImportForm, BootstrapMixin):
csv = CSVDataField(csv_form=ZoneFromCSVForm) csv = CSVDataField(csv_form=ZoneFromCSVForm)
class ZoneBulkEditForm(forms.Form, BootstrapMixin): class ZoneBulkEditForm(forms.Form, BootstrapMixin):
pk = forms.ModelMultipleChoiceField(queryset=Zone.objects.all(), widget=forms.MultipleHiddenInput) pk = forms.ModelMultipleChoiceField(queryset=Zone.objects.all(), widget=forms.MultipleHiddenInput)
name = forms.CharField(max_length=100, required=False, label='Name') name = forms.CharField(max_length=100, required=False, label='Name')
@ -66,6 +70,7 @@ class ZoneBulkEditForm(forms.Form, BootstrapMixin):
class ZoneBulkDeleteForm(ConfirmationForm): class ZoneBulkDeleteForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField(queryset=Zone.objects.all(), widget=forms.MultipleHiddenInput) pk = forms.ModelMultipleChoiceField(queryset=Zone.objects.all(), widget=forms.MultipleHiddenInput)
class ZoneFilterForm(forms.Form, BootstrapMixin): class ZoneFilterForm(forms.Form, BootstrapMixin):
pass pass
@ -73,12 +78,13 @@ class ZoneFilterForm(forms.Form, BootstrapMixin):
# Records # Records
# #
class RecordForm(forms.ModelForm, BootstrapMixin): class RecordForm(forms.ModelForm, BootstrapMixin):
address = AddressFormField(required=False) address = AddressFormField(required=False)
class Meta: class Meta:
model=Record model = Record
fields = ['name', 'record_type', 'priority', 'zone', 'address', 'value', 'description'] fields = ['name', 'record_type', 'priority', 'zone', 'address', 'value', 'description']
labels = { labels = {
'record_type': 'Type', 'record_type': 'Type',
@ -99,20 +105,14 @@ class RecordFromCSVForm(forms.ModelForm):
address = AddressFormField(required=False) address = AddressFormField(required=False)
class Meta: class Meta:
model=Record model = Record
fields = ['zone', 'name', 'record_type', 'priority', 'address', 'value', 'description'] fields = ['zone', 'name', 'record_type', 'priority', 'address', 'value', 'description']
# class RecordBINDImportForm(forms.Form, BootstrapMixin):
# bind = BINDDataField()
# zone_name = CharField(max_length=100, label='Zone')
# slash_v4 = IntegerField()
# def clean(self):
# self.cleaned_data
class RecordImportForm(BulkImportForm, BootstrapMixin): class RecordImportForm(BulkImportForm, BootstrapMixin):
csv = CSVDataField(csv_form=RecordFromCSVForm) csv = CSVDataField(csv_form=RecordFromCSVForm)
class RecordBulkEditForm(forms.Form, BootstrapMixin): class RecordBulkEditForm(forms.Form, BootstrapMixin):
pk = forms.ModelMultipleChoiceField(queryset=Record.objects.all(), widget=forms.MultipleHiddenInput) pk = forms.ModelMultipleChoiceField(queryset=Record.objects.all(), widget=forms.MultipleHiddenInput)
name = forms.CharField(max_length=100, required=False, label='Name') name = forms.CharField(max_length=100, required=False, label='Name')
@ -122,26 +122,29 @@ class RecordBulkEditForm(forms.Form, BootstrapMixin):
address = AddressFormField(required=False) address = AddressFormField(required=False)
value = forms.CharField(max_length=100, required=False) value = forms.CharField(max_length=100, required=False)
class RecordBulkDeleteForm(ConfirmationForm): class RecordBulkDeleteForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField(queryset=Record.objects.all(), widget=forms.MultipleHiddenInput) pk = forms.ModelMultipleChoiceField(queryset=Record.objects.all(), widget=forms.MultipleHiddenInput)
def record_zone_choices(): def record_zone_choices():
zone_choices = Zone.objects.annotate(record_count=Count('records')) zone_choices = Zone.objects.annotate(record_count=Count('records'))
return [(z.name, '{} ({})'.format(z.name, z.record_count)) for z in zone_choices] return [(z.name, '{} ({})'.format(z.name, z.record_count)) for z in zone_choices]
def record_type_choices(): def record_type_choices():
type_choices = {} type_choices = {}
records = Record.objects.all() records = Record.objects.all()
for r in records: for r in records:
if not r.record_type in type_choices: if r.record_type not in type_choices:
type_choices[r.record_type]=1 type_choices[r.record_type] = 1
else: else:
type_choices[r.record_type]+=1 type_choices[r.record_type] += 1
return [(t, '{} ({})'.format(t, count)) for t,count in type_choices.items()] return [(t, '{} ({})'.format(t, count)) for t, count in type_choices.items()]
class RecordFilterForm(forms.Form, BootstrapMixin): class RecordFilterForm(forms.Form, BootstrapMixin):
zone__name = forms.MultipleChoiceField(required=False, choices=record_zone_choices, label='Zone', zone__name = forms.MultipleChoiceField(required=False, choices=record_zone_choices, label='Zone',
widget=forms.SelectMultiple(attrs={'size': 8})) widget=forms.SelectMultiple(attrs={'size': 8}))
record_type = forms.MultipleChoiceField(required=False, choices=record_type_choices, label='Type', record_type = forms.MultipleChoiceField(required=False, choices=record_type_choices, label='Type',
widget=forms.SelectMultiple(attrs={'size': 8})) widget=forms.SelectMultiple(attrs={'size': 8}))

View File

@ -4,7 +4,6 @@ from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
#from ipam.models import IPAddress
from utilities.models import CreatedUpdatedModel from utilities.models import CreatedUpdatedModel
import time import time
@ -14,6 +13,7 @@ from django.dispatch import receiver
import ipam.models import ipam.models
class Zone(CreatedUpdatedModel): class Zone(CreatedUpdatedModel):
""" """
A Zone represents a DNS zone. It contains SOA data but no records, records are represented as Record objects. A Zone represents a DNS zone. It contains SOA data but no records, records are represented as Record objects.
@ -53,25 +53,24 @@ class Zone(CreatedUpdatedModel):
""" """
Each time a record or the zone is modified, the serial is incremented. Each time a record or the zone is modified, the serial is incremented.
""" """
current_date = time.strftime('%Y%m%d',time.localtime()) current_date = time.strftime('%Y%m%d', time.localtime())
if not self.soa_serial: if not self.soa_serial:
self.soa_serial = current_date+'01' self.soa_serial = current_date + '01'
else: else:
serial_date = self.soa_serial[:8] serial_date = self.soa_serial[:8]
serial_num = self.soa_serial[8:] serial_num = self.soa_serial[8:]
if serial_date!=current_date: if serial_date != current_date:
self.soa_serial = current_date+'01' self.soa_serial = current_date + '01'
else: else:
serial_num = int(serial_num) serial_num = int(serial_num)
serial_num += 1 serial_num += 1
if serial_num<10: if serial_num < 10:
self.soa_serial = current_date + '0' + str(serial_num) self.soa_serial = current_date + '0' + str(serial_num)
else: else:
self.soa_serial = current_date + str(serial_num) self.soa_serial = current_date + str(serial_num)
self.set_bind_changed(False) self.set_bind_changed(False)
def to_csv(self): def to_csv(self):
return ','.join([ return ','.join([
self.name, self.name,
@ -86,33 +85,32 @@ class Zone(CreatedUpdatedModel):
self.description, self.description,
]) ])
def to_bind(self,records): def to_bind(self, records):
if self.bind_changed: if self.bind_changed:
self.update_serial() self.update_serial()
bind_records = '' bind_records = ''
for r in records: for r in records:
bind_records += r.to_bind()+'\n' bind_records += r.to_bind() + '\n'
bind_export = '\n'.join([ bind_export = '\n'.join([
'; '+self.name+((' ('+self.description+')') if self.description else ''), '; ' + self.name + ((' (' + self.description + ')') if self.description else ''),
'; gen by netbox ( '+time.strftime('%A %B %d %Y %H:%M:%S',time.localtime())+' ) ', '; gen by netbox ( ' + time.strftime('%A %B %d %Y %H:%M:%S', time.localtime()) + ' ) ',
'', '',
'$TTL '+str(self.ttl), '$TTL ' + str(self.ttl),
self.soa_name.ljust(30)+' IN '+'SOA '+self.soa_contact+' (', self.soa_name.ljust(30) + ' IN ' + 'SOA ' + self.soa_contact + ' (',
' '+self.soa_serial.ljust(30)+' ; serial', ' ' + self.soa_serial.ljust(30) + ' ; serial',
' '+str(self.soa_refresh).ljust(30)+' ; refresh', ' ' + str(self.soa_refresh).ljust(30) + ' ; refresh',
' '+str(self.soa_retry).ljust(30)+' ; retry', ' ' + str(self.soa_retry).ljust(30) + ' ; retry',
' '+str(self.soa_expire).ljust(30)+' ; expire', ' ' + str(self.soa_expire).ljust(30) + ' ; expire',
' '+str(self.soa_minimum).ljust(29)+') ; minimum', ' ' + str(self.soa_minimum).ljust(29) + ') ; minimum',
'', '',
'', '',
'', '',
]) ])
bind_export += '\n'+bind_records bind_export += '\n' + bind_records
bind_export += '\n'+'; end ' bind_export += '\n' + '; end '
return bind_export return bind_export
class Record(CreatedUpdatedModel): class Record(CreatedUpdatedModel):
""" """
A Record represents a DNS record, i.e. a row in a DNS zone. A Record represents a DNS record, i.e. a row in a DNS zone.
@ -156,7 +154,7 @@ class Record(CreatedUpdatedModel):
def to_bind(self): def to_bind(self):
return ''.join([ return ''.join([
(self.name if self.name!='@' else '').ljust(30), (self.name if self.name != '@' else '').ljust(30),
' IN ', ' IN ',
self.record_type.upper().ljust(10), self.record_type.upper().ljust(10),
' ', ' ',
@ -164,17 +162,20 @@ class Record(CreatedUpdatedModel):
' ', ' ',
(str(self.address).split('/')[0] if self.address else self.value).ljust(25), (str(self.address).split('/')[0] if self.address else self.value).ljust(25),
' ', ' ',
' ; '+self.description+' ; gen by netbox ( '+time.strftime('%A %B %d %Y %H:%M:%S',time.localtime())+' ) ' ' ; ' + self.description + ' ; gen by netbox ( ' + time.strftime('%A %B %d %Y %H:%M:%S', time.localtime()) + ' ) '
]) ])
@receiver(pre_delete, sender=Record) @receiver(pre_delete, sender=Record)
def on_record_delete(sender, **kwargs): def on_record_delete(sender, **kwargs):
kwargs['instance'].zone.save() kwargs['instance'].zone.save()
# #
# BIND Exports # BIND Exports
# #
def export_bind_forward(): def export_bind_forward():
zones = Zone.objects.all() zones = Zone.objects.all()
@ -189,6 +190,7 @@ def export_bind_forward():
return zones_list return zones_list
def export_bind_reverse(): def export_bind_reverse():
zones = {} zones = {}
@ -202,7 +204,7 @@ def export_bind_reverse():
zones[zz['id']] = zz['content'] zones[zz['id']] = zz['content']
zones_list = [] zones_list = []
for zid,zc in zones.items(): for zid, zc in zones.items():
zones_list.append({ zones_list.append({
'num': len(zones_list), 'num': len(zones_list),
'id': zid, 'id': zid,
@ -210,5 +212,3 @@ def export_bind_reverse():
}) })
return zones_list return zones_list

View File

@ -10,6 +10,7 @@ from .models import Zone, Record
# Zones # Zones
# #
class ZoneTable(BaseTable): class ZoneTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn('dns:zone', args=[Accessor('pk')], verbose_name='Name') name = tables.LinkColumn('dns:zone', args=[Accessor('pk')], verbose_name='Name')
@ -23,10 +24,12 @@ class ZoneTable(BaseTable):
model = Zone model = Zone
fields = ('pk', 'name', 'ttl', 'soa_name', 'soa_contact', 'soa_serial') fields = ('pk', 'name', 'ttl', 'soa_name', 'soa_contact', 'soa_serial')
# #
# Records # Records
# #
class RecordTable(BaseTable): class RecordTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn('dns:record', args=[Accessor('pk')], verbose_name='Name') name = tables.LinkColumn('dns:record', args=[Accessor('pk')], verbose_name='Name')
@ -37,9 +40,10 @@ class RecordTable(BaseTable):
zone = tables.LinkColumn('dns:zone', args=[Accessor('zone.pk')], verbose_name='Zone') zone = tables.LinkColumn('dns:zone', args=[Accessor('zone.pk')], verbose_name='Zone')
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model=Record model = Record
fields = ('pk', 'name', 'record_type', 'priority', 'address', 'value') fields = ('pk', 'name', 'record_type', 'priority', 'address', 'value')
class RecordBriefTable(BaseTable): class RecordBriefTable(BaseTable):
name = tables.LinkColumn('dns:record', args=[Accessor('pk')], verbose_name='Name') name = tables.LinkColumn('dns:record', args=[Accessor('pk')], verbose_name='Name')
record_type = tables.Column(verbose_name='Type') record_type = tables.Column(verbose_name='Type')
@ -47,9 +51,10 @@ class RecordBriefTable(BaseTable):
zone = tables.LinkColumn('dns:zone', args=[Accessor('zone.pk')], verbose_name='Zone') zone = tables.LinkColumn('dns:zone', args=[Accessor('zone.pk')], verbose_name='Zone')
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model=Record model = Record
fields = ('name', 'record_type', 'priority', 'zone') fields = ('name', 'record_type', 'priority', 'zone')
class RecordZoneTable(BaseTable): class RecordZoneTable(BaseTable):
name = tables.LinkColumn('dns:record', args=[Accessor('pk')], verbose_name='Name') name = tables.LinkColumn('dns:record', args=[Accessor('pk')], verbose_name='Name')
record_type = tables.Column(verbose_name='Type') record_type = tables.Column(verbose_name='Type')
@ -58,6 +63,5 @@ class RecordZoneTable(BaseTable):
value = tables.Column(verbose_name='Value') value = tables.Column(verbose_name='Value')
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model=Record model = Record
fields = ('name', 'record_type', 'priority', 'address', 'value') fields = ('name', 'record_type', 'priority', 'address', 'value')

View File

@ -15,12 +15,15 @@ from . import filters, forms, tables
from .models import Zone, Record, export_bind_forward, export_bind_reverse from .models import Zone, Record, export_bind_forward, export_bind_reverse
from .tables import RecordZoneTable from .tables import RecordZoneTable
import StringIO, zipfile, time import StringIO
import zipfile
import time
# #
# Zones # Zones
# #
class ZoneListView(ObjectListView): class ZoneListView(ObjectListView):
queryset = Zone.objects.annotate(record_count=Count('records')) queryset = Zone.objects.annotate(record_count=Count('records'))
filter = filters.ZoneFilter filter = filters.ZoneFilter
@ -29,6 +32,7 @@ class ZoneListView(ObjectListView):
edit_permissions = ['dns.change_zone', 'dns.delete_zone'] edit_permissions = ['dns.change_zone', 'dns.delete_zone']
template_name = 'dns/zone_list.html' template_name = 'dns/zone_list.html'
def zone(request, pk): def zone(request, pk):
zone = get_object_or_404(Zone.objects.all(), pk=pk) zone = get_object_or_404(Zone.objects.all(), pk=pk)
@ -46,12 +50,14 @@ def zone(request, pk):
'dns_records_table': dns_records_table, 'dns_records_table': dns_records_table,
}) })
class ZoneEditView(PermissionRequiredMixin, ObjectEditView): class ZoneEditView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'dns.change_zone' permission_required = 'dns.change_zone'
model = Zone model = Zone
form_class = forms.ZoneForm form_class = forms.ZoneForm
cancel_url = 'dns:zone_list' cancel_url = 'dns:zone_list'
class ZoneDeleteView(PermissionRequiredMixin, ObjectDeleteView): class ZoneDeleteView(PermissionRequiredMixin, ObjectDeleteView):
permission_required = 'dns.delete_zone' permission_required = 'dns.delete_zone'
model = Zone model = Zone
@ -65,6 +71,7 @@ class ZoneBulkImportView(PermissionRequiredMixin, BulkImportView):
template_name = 'dns/zone_import.html' template_name = 'dns/zone_import.html'
obj_list_url = 'dns:zone_list' obj_list_url = 'dns:zone_list'
class ZoneBulkEditView(PermissionRequiredMixin, BulkEditView): class ZoneBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dns.change_zone' permission_required = 'dns.change_zone'
cls = Zone cls = Zone
@ -91,10 +98,12 @@ class ZoneBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
form = forms.ZoneBulkDeleteForm form = forms.ZoneBulkDeleteForm
default_redirect_url = 'dns:zone_list' default_redirect_url = 'dns:zone_list'
# #
# Records # Records
# #
class RecordListView(ObjectListView): class RecordListView(ObjectListView):
queryset = Record.objects.all() queryset = Record.objects.all()
filter = filters.RecordFilter filter = filters.RecordFilter
@ -103,6 +112,7 @@ class RecordListView(ObjectListView):
edit_permissions = ['dns.change_record', 'dns.delete_record'] edit_permissions = ['dns.change_record', 'dns.delete_record']
template_name = 'dns/record_list.html' template_name = 'dns/record_list.html'
def record(request, pk): def record(request, pk):
record = get_object_or_404(Record.objects.all(), pk=pk) record = get_object_or_404(Record.objects.all(), pk=pk)
@ -113,17 +123,20 @@ def record(request, pk):
'bind_export': bind_export, 'bind_export': bind_export,
}) })
class RecordEditView(PermissionRequiredMixin, ObjectEditView): class RecordEditView(PermissionRequiredMixin, ObjectEditView):
permission_required = 'dns.change_record' permission_required = 'dns.change_record'
model = Record model = Record
form_class = forms.RecordForm form_class = forms.RecordForm
cancel_url = 'dns:record_list' cancel_url = 'dns:record_list'
class RecordDeleteView(PermissionRequiredMixin, ObjectDeleteView): class RecordDeleteView(PermissionRequiredMixin, ObjectDeleteView):
permission_required = 'dns.delete_record' permission_required = 'dns.delete_record'
model = Record model = Record
redirect_url = 'dns:record_list' redirect_url = 'dns:record_list'
class RecordBulkImportView(PermissionRequiredMixin, BulkImportView): class RecordBulkImportView(PermissionRequiredMixin, BulkImportView):
permission_required = 'dns.add_record' permission_required = 'dns.add_record'
form = forms.RecordImportForm form = forms.RecordImportForm
@ -131,6 +144,7 @@ class RecordBulkImportView(PermissionRequiredMixin, BulkImportView):
template_name = 'dns/record_import.html' template_name = 'dns/record_import.html'
obj_list_url = 'dns:record_list' obj_list_url = 'dns:record_list'
class RecordBulkEditView(PermissionRequiredMixin, BulkEditView): class RecordBulkEditView(PermissionRequiredMixin, BulkEditView):
permission_required = 'dns.change_record' permission_required = 'dns.change_record'
cls = Record cls = Record
@ -150,16 +164,19 @@ class RecordBulkEditView(PermissionRequiredMixin, BulkEditView):
rlist[0].save() rlist[0].save()
return rlist.update(**fields_to_update) return rlist.update(**fields_to_update)
class RecordBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class RecordBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dns.delete_record' permission_required = 'dns.delete_record'
cls = Record cls = Record
form = forms.RecordBulkEditForm form = forms.RecordBulkEditForm
default_redirect_url = 'dns:record_list' default_redirect_url = 'dns:record_list'
# #
# BIND Exports # BIND Exports
# #
def bind_export(request, zones_list, context): def bind_export(request, zones_list, context):
download = request.GET.get('download') download = request.GET.get('download')
if download: if download:
@ -169,12 +186,12 @@ def bind_export(request, zones_list, context):
temp = [] temp = []
for z in zones_list: for z in zones_list:
temp.append(StringIO.StringIO()) temp.append(StringIO.StringIO())
temp[len(temp)-1].write(z['content']) temp[len(temp) - 1].write(z['content'])
zfile.writestr(z['id'],str(temp[len(temp)-1].getvalue())) zfile.writestr(z['id'], str(temp[len(temp) - 1].getvalue()))
zfile.close() zfile.close()
response = HttpResponse( response = HttpResponse(
zbuf.getvalue(), zbuf.getvalue(),
content_type = 'application/zip' content_type='application/zip'
) )
response['Content-Disposition'] = 'attachment; filename="netbox_dns_{}_{}.zip"'.format(context, str(int(time.time()))) response['Content-Disposition'] = 'attachment; filename="netbox_dns_{}_{}.zip"'.format(context, str(int(time.time())))
return response return response
@ -193,9 +210,10 @@ def bind_export(request, zones_list, context):
'bind_export_count': len(zones_list), 'bind_export_count': len(zones_list),
}) })
def full_forward(request): def full_forward(request):
return bind_export(request, export_bind_forward(), 'forward') return bind_export(request, export_bind_forward(), 'forward')
def full_reverse(request): def full_reverse(request):
return bind_export(request, export_bind_reverse(), 'reverse') return bind_export(request, export_bind_reverse(), 'reverse')

View File

@ -71,10 +71,10 @@
"soa_name": "", "soa_name": "",
"soa_contact": "", "soa_contact": "",
"soa_serial": "", "soa_serial": "",
"soa_refresh": "", "soa_refresh": null,
"soa_retry": "", "soa_retry": null,
"soa_expire": "", "soa_expire": null,
"soa_minimum": "" "soa_minimum": null
} }
}, },
{ {

View File

@ -411,7 +411,6 @@ class IPAddressFromCSVForm(forms.ModelForm):
model = IPAddress model = IPAddress
fields = ['address', 'vrf', 'tenant', 'ptr', 'device', 'interface_name', 'is_primary', 'description'] fields = ['address', 'vrf', 'tenant', 'ptr', 'device', 'interface_name', 'is_primary', 'description']
def clean(self): def clean(self):
device = self.cleaned_data.get('device') device = self.cleaned_data.get('device')
@ -461,7 +460,6 @@ class IPAddressBulkEditForm(forms.Form, BootstrapMixin):
description = forms.CharField(max_length=100, required=False) description = forms.CharField(max_length=100, required=False)
def ipaddress_family_choices(): def ipaddress_family_choices():
return [('', 'All'), (4, 'IPv4'), (6, 'IPv6')] return [('', 'All'), (4, 'IPv4'), (6, 'IPv6')]

View File

@ -13,7 +13,8 @@ import dns.models
from .fields import IPNetworkField, IPAddressField from .fields import IPNetworkField, IPAddressField
import time, ipaddress import time
import ipaddress
import netaddr import netaddr
@ -245,7 +246,7 @@ class Prefix(CreatedUpdatedModel):
objects = PrefixQuerySet.as_manager() objects = PrefixQuerySet.as_manager()
#Reverse DNS # Reverse DNS
ttl = models.PositiveIntegerField(blank=True, null=True) ttl = models.PositiveIntegerField(blank=True, null=True)
soa_name = models.CharField(max_length=100, blank=True) soa_name = models.CharField(max_length=100, blank=True)
soa_contact = models.CharField(max_length=100, blank=True) soa_contact = models.CharField(max_length=100, blank=True)
@ -296,15 +297,15 @@ class Prefix(CreatedUpdatedModel):
""" """
Each time a record or the zone is modified, the serial is incremented. Each time a record or the zone is modified, the serial is incremented.
""" """
current_date = time.strftime('%Y%m%d',time.localtime()) current_date = time.strftime('%Y%m%d', time.localtime())
if not self.soa_serial: if not self.soa_serial:
self.soa_serial = current_date+'01' self.soa_serial = current_date + '01'
else: else:
serial_date = self.soa_serial[:8] serial_date = self.soa_serial[:8]
serial_num = self.soa_serial[8:] serial_num = self.soa_serial[8:]
if serial_date!=current_date: if serial_date != current_date:
self.soa_serial = current_date+'01' self.soa_serial = current_date + '01'
else: else:
serial_num = int(serial_num) serial_num = int(serial_num)
serial_num += 1 serial_num += 1
@ -346,27 +347,28 @@ class Prefix(CreatedUpdatedModel):
def get_status_class(self): def get_status_class(self):
return STATUS_CHOICE_CLASSES[self.status] return STATUS_CHOICE_CLASSES[self.status]
def to_bind(self,ipaddresses): def to_bind(self, ipaddresses):
if self.bind_changed: if self.bind_changed:
self.update_serial() self.update_serial()
zones = {} zones = {}
def header (zone_id): return '\n'.join([ def header(zone_id):
'; '+zone_id, return '\n'.join([
'; gen from prefix '+str(self.prefix)+' ('+(self.description if self.description else '')+') by netbox ( '+time.strftime('%A %B %d %Y %H:%M:%S',time.localtime())+' ) ', '; ' + zone_id,
'; gen from prefix ' + str(self.prefix) + ' (' + (self.description if self.description else '') + ') by netbox ( ' + time.strftime('%A %B %d %Y %H:%M:%S', time.localtime()) + ' ) ',
'', '',
'$TTL '+str(self.ttl), '$TTL ' + str(self.ttl),
self.soa_name.ljust(30)+' IN '+'SOA '+self.soa_contact+' (', self.soa_name.ljust(30) + ' IN ' + 'SOA ' + self.soa_contact + ' (',
' '+self.soa_serial.ljust(30)+' ; serial', ' ' + self.soa_serial.ljust(30) + ' ; serial',
' '+str(self.soa_refresh).ljust(30)+' ; refresh', ' ' + str(self.soa_refresh).ljust(30) + ' ; refresh',
' '+str(self.soa_retry).ljust(30)+' ; retry', ' ' + str(self.soa_retry).ljust(30) + ' ; retry',
' '+str(self.soa_expire).ljust(30)+' ; expire', ' ' + str(self.soa_expire).ljust(30) + ' ; expire',
' '+str(self.soa_minimum).ljust(29)+') ; minimum', ' ' + str(self.soa_minimum).ljust(29) + ') ; minimum',
'', '',
'', '',
'', '',
'$ORIGIN '+zone_id, '$ORIGIN ' + zone_id,
'', '',
'', '',
]) ])
@ -376,21 +378,20 @@ class Prefix(CreatedUpdatedModel):
pslash = int(str(self.prefix).split('/')[1]) pslash = int(str(self.prefix).split('/')[1])
if pslash > 16: if pslash > 16:
pbytes[3]='0' pbytes[3] = '0'
zslash = 24 zslash = 24
largerPrefix = Prefix.objects.filter(family=4, prefix__net_contains_or_equals=pbytes[0]+'.'+pbytes[1]+'.0.0/16') largerPrefix = Prefix.objects.filter(family=4, prefix__net_contains_or_equals=pbytes[0] + '.' + pbytes[1] + '.0.0/16')
if largerPrefix: if largerPrefix:
pbytes[2]='0' pbytes[2] = '0'
zslash = 16 zslash = 16
else: else:
pbytes[2]='0' pbytes[2] = '0'
zslash = 16 zslash = 16
if pslash > zslash: if pslash > zslash:
pslash = zslash pslash = zslash
p = IPNetwork(unicode('.'.join(pbytes)+'/'+str(pslash))) p = IPNetwork(unicode('.'.join(pbytes) + '/' + str(pslash)))
ipaddresses = IPAddress.objects.filter(family=4) ipaddresses = IPAddress.objects.filter(family=4)
for ip in ipaddresses: for ip in ipaddresses:
@ -402,57 +403,53 @@ class Prefix(CreatedUpdatedModel):
if i in p: if i in p:
if zslash == 24: if zslash == 24:
zone_id = ibytes[2] + '.' + ibytes[1] + '.' + ibytes[0] + '.in-addr.arpa.' zone_id = ibytes[2] + '.' + ibytes[1] + '.' + ibytes[0] + '.in-addr.arpa.'
if not zone_id in zones: if zone_id not in zones:
zones[zone_id] = header(zone_id) zones[zone_id] = header(zone_id)
zones[zone_id] += ibytes[3].ljust(3) + ' IN PTR ' + ip.ptr.ljust(40) + ' ; ' + ip.description.ljust(20) + ' ; gen by netbox ( '+time.strftime('%A %B %d %Y %H:%M:%S',time.localtime())+' ) \n' zones[zone_id] += ibytes[3].ljust(3) + ' IN PTR ' + ip.ptr.ljust(40) + ' ; ' + ip.description.ljust(20) + ' ; gen by netbox ( ' + time.strftime('%A %B %d %Y %H:%M:%S', time.localtime()) + ' ) \n'
else: else:
zone_id = ibytes[1]+'.'+ibytes[0]+'.in-addr.arpa.' zone_id = ibytes[1] + '.' + ibytes[0] + '.in-addr.arpa.'
if not zone_id in zones: if zone_id not in zones:
zones[zone_id] = header(zone_id) zones[zone_id] = header(zone_id)
zones[zone_id] += (ibytes[3]+'.'+ibytes[2]).ljust(7) + ' IN PTR ' + ip.ptr.ljust(40) + ' ; ' + ip.description.ljust(20) + ' ; gen by netbox ( '+time.strftime('%A %B %d %Y %H:%M:%S',time.localtime())+' ) \n' zones[zone_id] += (ibytes[3] + '.' + ibytes[2]).ljust(7) + ' IN PTR ' + ip.ptr.ljust(40) + ' ; ' + ip.description.ljust(20) + ' ; gen by netbox ( ' + time.strftime('%A %B %d %Y %H:%M:%S', time.localtime()) + ' ) \n'
else: else:
pfull = str(ipaddress.IPv6Address(unicode(str(self.prefix).split('/')[0])).exploded) pfull = str(ipaddress.IPv6Address(unicode(str(self.prefix).split('/')[0])).exploded)
pnibbles = pfull.split(':') pnibbles = pfull.split(':')
pdigits = pfull.replace(':','') pdigits = pfull.replace(':', '')
pslash = int(str(self.prefix).split('/')[1]) pslash = int(str(self.prefix).split('/')[1])
zslash = pslash if pslash % 16 == 0 else pslash/16+16 zslash = pslash if pslash % 16 == 0 else pslash / 16 + 16
pnibbles = pnibbles[:zslash/16] + ['0000'] * (8 - zslash/16) pnibbles = pnibbles[:zslash / 16] + ['0000'] * (8 - zslash / 16)
largerPrefix = Prefix.objects.filter(family=6, prefix__net_contains_or_equals=':'.join(pnibbles)+'/'+str(zslash)) largerPrefix = Prefix.objects.filter(family=6, prefix__net_contains_or_equals=':'.join(pnibbles) + '/' + str(zslash))
if largerPrefix: if largerPrefix:
#choper le plus grand
minSlash = 128 minSlash = 128
for pp in largerPrefix: for pp in largerPrefix:
ppslash = int(str(pp.prefix).split('/')[1]) ppslash = int(str(pp.prefix).split('/')[1])
if ppslash < minSlash: if ppslash < minSlash:
minSlash = ppslash minSlash = ppslash
zslash = minSlash zslash = minSlash
pnibbles = pnibbles[:zslash/16] + ['0000'] * (8 - zslash/16) pnibbles = pnibbles[:zslash / 16] + ['0000'] * (8 - zslash / 16)
for ip in ipaddresses: for ip in ipaddresses:
if ip.ptr: if ip.ptr:
ifull = str(ipaddress.IPv6Address(unicode(str(ip.address).split('/')[0])).exploded) ifull = str(ipaddress.IPv6Address(unicode(str(ip.address).split('/')[0])).exploded)
inibbles = ifull.split(':') inibbles = ifull.split(':')
idigits = ifull.replace(':','')[::-1] idigits = ifull.replace(':', '')[::-1]
islash = int(str(ip.address).split('/')[1]) islash = int(str(ip.address).split('/')[1])
pdigitszone = pdigits[:zslash/4][::-1] pdigitszone = pdigits[:zslash / 4][::-1]
zone_id = '.'.join(pdigitszone)+'.ip6.arpa.' zone_id = '.'.join(pdigitszone) + '.ip6.arpa.'
if not zone_id in zones: if zone_id not in zones:
zones[zone_id] = header(zone_id) zones[zone_id] = header(zone_id)
zones[zone_id] += ('.'.join(idigits[:32-zslash/4])).ljust(30)+' IN PTR ' + ip.ptr.ljust(40) + ' ; ' + ip.description.ljust(20) + ' ; gen by netbox ( '+time.strftime('%A %B %d %Y %H:%M:%S',time.localtime())+' ) \n' zones[zone_id] += ('.'.join(idigits[:32 - zslash / 4])).ljust(30) + ' IN PTR ' + ip.ptr.ljust(40) + ' ; ' + ip.description.ljust(20) + ' ; gen by netbox ( ' + time.strftime('%A %B %d %Y %H:%M:%S', time.localtime()) + ' ) \n'
for z in zones: for z in zones:
z += '\n\n; end ' z += '\n\n; end '
ret = [] ret = []
for zid,zc in zones.items(): for zid, zc in zones.items():
ret.append({ ret.append({
'num': len(ret), 'num': len(ret),
'id': zid, 'id': zid,
@ -536,13 +533,13 @@ class IPAddress(CreatedUpdatedModel):
record_name = self.ptr[:-len(zone_name)] record_name = self.ptr[:-len(zone_name)]
if record_name.endswith('.'): if record_name.endswith('.'):
record_name = record_name[:-1] record_name = record_name[:-1]
record_type = 'A' if self.family==4 else 'AAAA' record_type = 'A' if self.family == 4 else 'AAAA'
dns.models.Record.objects.get_or_create( dns.models.Record.objects.get_or_create(
name = record_name, name=record_name,
record_type = record_type, record_type=record_type,
zone = which_zone, zone=which_zone,
address = self address=self
) )
def to_csv(self): def to_csv(self):