Commit aa5cedc3 authored by Maël Kervella's avatar Maël Kervella

Ajoute un model SOA pour les données SOA d'une extension

parent e404f59b
......@@ -27,8 +27,8 @@ from django.contrib import admin
from reversion.admin import VersionAdmin
from .models import IpType, Machine, MachineType, Domain, IpList, Interface
from .models import Extension, Mx, Ns, Vlan, Text, Nas, Service, OuverturePort
from .models import OuverturePortList
from .models import Extension, SOA, Mx, Ns, Vlan, Text, Nas, Service
from .models import OuverturePort, OuverturePortList
class MachineAdmin(VersionAdmin):
......@@ -51,6 +51,10 @@ class ExtensionAdmin(VersionAdmin):
pass
class SOAAdmin(VersionAdmin):
pass
class MxAdmin(VersionAdmin):
pass
......@@ -95,6 +99,7 @@ admin.site.register(Machine, MachineAdmin)
admin.site.register(MachineType, MachineTypeAdmin)
admin.site.register(IpType, IpTypeAdmin)
admin.site.register(Extension, ExtensionAdmin)
admin.site.register(SOA, SOAAdmin)
admin.site.register(Mx, MxAdmin)
admin.site.register(Ns, NsAdmin)
admin.site.register(Text, TextAdmin)
......
......@@ -45,6 +45,7 @@ from .models import (
IpList,
MachineType,
Extension,
SOA,
Mx,
Text,
Ns,
......@@ -274,6 +275,7 @@ class ExtensionForm(ModelForm):
self.fields['name'].label = 'Extension à ajouter'
self.fields['origin'].label = 'Enregistrement A origin'
self.fields['origin_v6'].label = 'Enregistrement AAAA origin'
self.fields['soa'].label = 'En-tête SOA à utiliser'
class DelExtensionForm(Form):
......@@ -285,6 +287,26 @@ class DelExtensionForm(Form):
)
class SOAForm(ModelForm):
"""Ajout et edition d'un SOA"""
class Meta:
model = SOA
fields = '__all__'
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(SOAForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelSOAForm(Form):
"""Suppression d'un ou plusieurs SOA"""
soa = forms.ModelMultipleChoiceField(
queryset=SOA.objects.all(),
label="SOA actuels",
widget=forms.CheckboxSelectMultiple
)
class MxForm(ModelForm):
"""Ajout et edition d'un MX"""
class Meta:
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-19 22:40
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import machines.models
class Migration(migrations.Migration):
dependencies = [
('machines', '0062_extension_origin_v6'),
]
operations = [
migrations.CreateModel(
name='SOA',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='SOA', max_length=255)),
('mail', models.EmailField(default='postmaster@example.com', help_text='Email du contact pour la zone', max_length=254)),
('refresh', models.PositiveIntegerField(default=86400, help_text='Secondes avant que les DNS secondaires doivent demander le serial du DNS primaire pour détecter une modification')),
('retry', models.PositiveIntegerField(default=7200, help_text='Secondes avant que les DNS secondaires fassent une nouvelle demande de serial en cas de timeout du DNS primaire')),
('expire', models.PositiveIntegerField(default=3600000, help_text='Secondes après lesquelles les DNS secondaires arrêtent de de répondre aux requêtes en cas de timeout du DNS primaire')),
('ttl', models.PositiveIntegerField(default=172800, help_text='Time To Live')),
],
),
migrations.AddField(
model_name='extension',
name='soa',
field=models.ForeignKey(default=machines.models.SOA.new_default_soa, on_delete=django.db.models.deletion.CASCADE, to='machines.SOA'),
),
]
......@@ -234,6 +234,79 @@ class Nas(models.Model):
return self.name
class SOA(models.Model):
"""
Un enregistrement SOA associé à une extension
Les valeurs par défault viennent des recommandations RIPE :
https://www.ripe.net/publications/docs/ripe-203
"""
PRETTY_NAME = "Enregistrement SOA"
name = models.CharField(max_length=255, default='SOA')
mail = models.EmailField(
default='postmaster@example.com',
help_text='Email du contact pour la zone'
)
refresh = models.PositiveIntegerField(
default=86400, # 24 hours
help_text='Secondes avant que les DNS secondaires doivent demander le\
serial du DNS primaire pour détecter une modification'
)
retry = models.PositiveIntegerField(
default=7200, # 2 hours
help_text='Secondes avant que les DNS secondaires fassent une nouvelle\
demande de serial en cas de timeout du DNS primaire'
)
expire = models.PositiveIntegerField(
default=3600000, # 1000 hours
help_text='Secondes après lesquelles les DNS secondaires arrêtent de\
de répondre aux requêtes en cas de timeout du DNS primaire'
)
ttl = models.PositiveIntegerField(
default=172800, # 2 days
help_text='Time To Live'
)
def __str__(self):
return str(self.name)
@cached_property
def dns_soa_param(self):
"""
Renvoie la partie de l'enregistrement SOA correspondant aux champs :
<refresh> ; refresh
<retry> ; retry
<expire> ; expire
<ttl> ; TTL
"""
return (
' {refresh};refresh\n'
' {retry};retry\n'
' {expire};expire\n'
' {ttl};TTL'
).format(
refresh=self.refresh,
retry=self.retry,
expire=self.expire,
ttl=self.expire
)
@cached_property
def dns_soa_mail(self):
""" Renvoie le mail dans l'enregistrement SOA """
mail_fields = str(self.mail).split('@')
return mail_fields[0].replace('.', '\\.') + '.' +mail_fields[1]
@classmethod
def new_default_soa(cls):
""" Fonction pour créer un SOA par défaut, utile pour les nouvelles
extensions .
/!\ Ne jamais supprimer ou renommer cette fonction car elle est
utilisée dans les migrations de la BDD. """
return cls.objects.get_or_create(name="SOA to edit", mail="postmaser@example.com")[0].pk
class Extension(models.Model):
""" Extension dns type example.org. Précise si tout le monde peut
l'utiliser, associé à un origin (ip d'origine)"""
......@@ -252,6 +325,11 @@ class Extension(models.Model):
null=True,
blank=True
)
soa = models.ForeignKey(
'SOA',
on_delete=models.CASCADE,
default=SOA.new_default_soa
)
@cached_property
def dns_entry(self):
......@@ -307,7 +385,7 @@ class Ns(models.Model):
class Text(models.Model):
""" Un enregistrement TXT associé à une extension"""
PRETTY_NAME = "Enregistrement text"
PRETTY_NAME = "Enregistrement TXT"
zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
field1 = models.CharField(max_length=255)
......@@ -790,6 +868,18 @@ def extension_post_selete(sender, **kwargs):
regen('dns')
@receiver(post_save, sender=SOA)
def soa_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un SOA"""
regen('dns')
@receiver(post_delete, sender=SOA)
def soa_post_delete(sender, **kwargs):
"""Regeneration dns après suppresson d'un SOA"""
regen('dns')
@receiver(post_save, sender=Mx)
def mx_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un MX"""
......
......@@ -26,19 +26,25 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<thead>
<tr>
<th>Extension</th>
<th>Autorisation infra pour utiliser l'extension</th>
<th>Droit infra pour utiliser ?</th>
<th>Enregistrement SOA</th>
<th>Enregistrement A origin</th>
{% if ipv6_enabled %}
<th>Enregistrement AAAA origin</th>
{% endif %}
<th></th>
</tr>
</thead>
{% for extension in extension_list %}
<tr>
<td>{{ extension.name }}</td>
<td>{{ extension.need_infra }}</td>
<td>{{ extension.need_infra }}</td>
<td>{{ extension.soa}}</td>
<td>{{ extension.origin }}</td>
<td>{{ extension.origin_v6 }}</td>
<td class="text-right">
{% if ipv6_enabled %}
<td>{{ extension.origin_v6 }}</td>
{% endif %}
<td class="text-right">
{% if is_infra %}
{% include 'buttons/edit.html' with href='machines:edit-extension' id=extension.id %}
{% endif %}
......
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
<table class="table table-striped">
<thead>
<tr>
<th>Nom</th>
<th>Mail</th>
<th>Refresh</th>
<th>Retry</th>
<th>Expire</th>
<th>TTL</th>
<th></th>
<th></th>
</tr>
</thead>
{% for soa in soa_list %}
<tr>
<td>{{ soa.name }}</td>
<td>{{ soa.mail }}</td>
<td>{{ soa.refresh }}</td>
<td>{{ soa.retry }}</td>
<td>{{ soa.expire }}</td>
<td>{{ soa.ttl }}</td>
<td class="text-right">
{% if is_infra %}
{% include 'buttons/edit.html' with href='machines:edit-soa' id=soa.id %}
{% endif %}
{% include 'buttons/history.html' with href='machines:history' name='soa' id=soa.id %}
</td>
</tr>
{% endfor %}
</table>
......@@ -35,6 +35,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %}
{% include "machines/aff_extension.html" with extension_list=extension_list %}
<h2>Liste des enregistrements SOA</h2>
{% if is_infra %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-soa' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement SOA</a>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-soa' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement SOA</a>
{% endif %}
{% include "machines/aff_soa.html" with soa_list=soa_list %}
<h2>Liste des enregistrements MX</h2>
{% if is_infra %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-mx' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement MX</a>
......
......@@ -100,6 +100,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<h3>Extension</h3>
{% massive_bootstrap_form extensionform 'origin' %}
{% endif %}
{% if soaform %}
<h3>Enregistrement SOA</h3>
{% bootstrap_form soaform %}
{% endif %}
{% if mxform %}
<h3>Enregistrement MX</h3>
{% massive_bootstrap_form mxform 'name' %}
......
......@@ -44,6 +44,9 @@ urlpatterns = [
url(r'^add_extension/$', views.add_extension, name='add-extension'),
url(r'^edit_extension/(?P<extensionid>[0-9]+)$', views.edit_extension, name='edit-extension'),
url(r'^del_extension/$', views.del_extension, name='del-extension'),
url(r'^add_soa/$', views.add_soa, name='add-soa'),
url(r'^edit_soa/(?P<soaid>[0-9]+)$', views.edit_soa, name='edit-soa'),
url(r'^del_soa/$', views.del_soa, name='del-soa'),
url(r'^add_mx/$', views.add_mx, name='add-mx'),
url(r'^edit_mx/(?P<mxid>[0-9]+)$', views.edit_mx, name='edit-mx'),
url(r'^del_mx/$', views.del_mx, name='del-mx'),
......@@ -74,6 +77,7 @@ urlpatterns = [
url(r'^history/(?P<object>interface)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>machinetype)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>extension)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>soa)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>mx)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>ns)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>txt)/(?P<id>[0-9]+)$', views.history, name='history'),
......
......@@ -77,6 +77,8 @@ from .forms import (
DomainForm,
AliasForm,
DelAliasForm,
SOAForm,
DelSOAForm,
NsForm,
DelNsForm,
TxtForm,
......@@ -98,6 +100,7 @@ from .models import (
IpList,
MachineType,
Extension,
SOA,
Mx,
Ns,
Domain,
......@@ -519,6 +522,54 @@ def del_extension(request):
return redirect("/machines/index_extension")
return form({'extensionform': extension}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def add_soa(request):
soa = SOAForm(request.POST or None)
if soa.is_valid():
with transaction.atomic(), reversion.create_revision():
soa.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "Cet enregistrement SOA a été ajouté")
return redirect("/machines/index_extension")
return form({'soaform': soa}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def edit_soa(request, soaid):
try:
soa_instance = SOA.objects.get(pk=soaid)
except SOA.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect("/machines/index_extension/")
soa = SOAForm(request.POST or None, instance=soa_instance)
if soa.is_valid():
with transaction.atomic(), reversion.create_revision():
soa.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in soa.changed_data))
messages.success(request, "SOA modifié")
return redirect("/machines/index_extension/")
return form({'soaform': soa}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def del_soa(request):
soa = DelSOAForm(request.POST or None)
if soa.is_valid():
soa_dels = soa.cleaned_data['soa']
for soa_del in soa_dels:
try:
with transaction.atomic(), reversion.create_revision():
soa_del.delete()
reversion.set_user(request.user)
messages.success(request, "Le SOA a été supprimée")
except ProtectedError:
messages.error(request, "Erreur le SOA suivant %s ne peut être supprimé" % soa_del)
return redirect("/machines/index_extension")
return form({'soaform': soa}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def add_mx(request):
......@@ -925,11 +976,12 @@ def index_nas(request):
@login_required
@permission_required('cableur')
def index_extension(request):
extension_list = Extension.objects.select_related('origin').order_by('name')
extension_list = Extension.objects.select_related('origin').select_related('soa').order_by('name')
soa_list = SOA.objects.order_by('name')
mx_list = Mx.objects.order_by('zone').select_related('zone').select_related('name__extension')
ns_list = Ns.objects.order_by('zone').select_related('zone').select_related('ns__extension')
text_list = Text.objects.all().select_related('zone')
return render(request, 'machines/index_extension.html', {'extension_list':extension_list, 'mx_list': mx_list, 'ns_list': ns_list, 'text_list' : text_list})
return render(request, 'machines/index_extension.html', {'extension_list':extension_list, 'soa_list': soa_list, 'mx_list': mx_list, 'ns_list': ns_list, 'text_list' : text_list})
@login_required
def index_alias(request, interfaceid):
......@@ -998,6 +1050,12 @@ def history(request, object, id):
except Extension.DoesNotExist:
messages.error(request, "Extension inexistante")
return redirect("/machines/")
elif object == 'soa' and request.user.has_perms(('cableur',)):
try:
object_instance = SOA.objects.get(pk=id)
except SOA.DoesNotExist:
messages.error(request, "SOA inexistant")
return redirect("/machines/")
elif object == 'mx' and request.user.has_perms(('cableur',)):
try:
object_instance = Mx.objects.get(pk=id)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment